| 1 |
#include "OpenSSL.hh" |
| 2 |
|
| 3 |
#include <array> |
| 4 |
#include <bit> |
| 5 |
#include <cstdint> |
| 6 |
#include <cstdio> |
| 7 |
#include <mutex> |
| 8 |
#include <string> |
| 9 |
#include <utility> |
| 10 |
#include <vector> |
| 11 |
|
| 12 |
#ifdef _WIN32 |
| 13 |
#include <windows.h> |
| 14 |
#include <wincrypt.h> |
| 15 |
#else |
| 16 |
#include <dlfcn.h> |
| 17 |
#endif |
| 18 |
|
| 19 |
namespace openmsx::OpenSSL { |
| 20 |
|
| 21 |
|
| 22 |
|
| 23 |
|
| 24 |
struct SslCtx; |
| 25 |
struct X509; |
| 26 |
struct X509VerifyParam; |
| 27 |
struct SslMethod; |
| 28 |
struct X509StoreCtx; |
| 29 |
struct SslInitSettings; |
| 30 |
|
| 31 |
|
| 32 |
|
| 33 |
|
| 34 |
|
| 35 |
struct OpenSSLApi { |
| 36 |
|
| 37 |
int (*init)(unsigned long long opts, const SslInitSettings* settings) = nullptr; |
| 38 |
|
| 39 |
const SslMethod* (*tlsClientMethod)() = nullptr; |
| 40 |
SslCtx* (*ctxNew)(const SslMethod* method) = nullptr; |
| 41 |
int (*ctxSetDefaultVerifyPaths)(SslCtx* ctx) = nullptr; |
| 42 |
int (*ctxLoadVerifyLocations)(SslCtx* ctx, const char* caFile, |
| 43 |
const char* caPath) = nullptr; |
| 44 |
|
| 45 |
Ssl* (*newSession)(SslCtx* ctx) = nullptr; |
| 46 |
void (*freeSession)(Ssl* ssl) = nullptr; |
| 47 |
int (*setFd)(Ssl* ssl, int fd) = nullptr; |
| 48 |
void (*setVerify)(Ssl* ssl, int mode, void (*)(int, X509StoreCtx*)) = nullptr; |
| 49 |
int (*connect)(Ssl* ssl) = nullptr; |
| 50 |
long (*ctrl)(Ssl* ssl, int cmd, long larg, const char* parg) = nullptr; |
| 51 |
X509VerifyParam* (*get0Param)(const Ssl* ssl) = nullptr; |
| 52 |
int (*verifyParamSet1Host)(X509VerifyParam* param, const char* name, |
| 53 |
size_t namelen) = nullptr; |
| 54 |
|
| 55 |
int (*read)(Ssl* ssl, uint8_t* buf, int num) = nullptr; |
| 56 |
int (*write)(Ssl* ssl, const uint8_t* buf, int num) = nullptr; |
| 57 |
int (*pending)(const Ssl* ssl) = nullptr; |
| 58 |
int (*shutdown)(Ssl* ssl) = nullptr; |
| 59 |
|
| 60 |
int (*getError)(const Ssl* ssl, int ret) = nullptr; |
| 61 |
long (*getVerifyResult)(const Ssl* ssl) = nullptr; |
| 62 |
const char* (*verifyCertErrorString)(long code) = nullptr; |
| 63 |
X509* (*get1PeerCertificate)(const Ssl* ssl) = nullptr; |
| 64 |
void (*freeX509)(X509* x) = nullptr; |
| 65 |
unsigned long (*errGetError)() = nullptr; |
| 66 |
void (*errErrorStringN)(unsigned long e, char* buf, size_t len) = nullptr; |
| 67 |
const char* (*version)(int type) = nullptr; |
| 68 |
}; |
| 69 |
|
| 70 |
namespace { |
| 71 |
|
| 72 |
|
| 73 |
|
| 74 |
|
| 75 |
|
| 76 |
#ifdef _WIN32 |
| 77 |
using DllHandle = HMODULE; |
| 78 |
DllHandle openSslLib(const char* name) |
| 79 |
{ |
| 80 |
return LoadLibraryA(name); |
| 81 |
} |
| 82 |
#else |
| 83 |
using DllHandle = void*; |
| 84 |
DllHandle openSslLib(const char* name) |
| 85 |
{ |
| 86 |
return dlopen(name, RTLD_NOW | RTLD_LOCAL); |
| 87 |
} |
| 88 |
#endif |
| 89 |
|
| 90 |
#ifdef _WIN32 |
| 91 |
static constexpr std::array libraryCandidates = { |
| 92 |
"libssl-4-x64.dll", "libssl-4.dll", |
| 93 |
"libssl-3-x64.dll", "libssl-3.dll", |
| 94 |
"libssl-1_1-x64.dll", "libssl-1_1.dll", |
| 95 |
"libssl.dll" |
| 96 |
}; |
| 97 |
#elif defined(__APPLE__) |
| 98 |
static constexpr std::array libraryCandidates = { |
| 99 |
|
| 100 |
"/opt/homebrew/opt/openssl@3/lib/libssl.3.dylib", |
| 101 |
"/opt/homebrew/opt/[email protected]/lib/libssl.1.1.dylib", |
| 102 |
"/usr/local/opt/openssl@3/lib/libssl.3.dylib", |
| 103 |
"/usr/local/opt/[email protected]/lib/libssl.1.1.dylib", |
| 104 |
"/usr/local/lib/libssl.dylib", |
| 105 |
"/opt/homebrew/lib/libssl.dylib", |
| 106 |
|
| 107 |
"libssl.3.dylib", "libssl.1.1.dylib", "libssl.dylib" |
| 108 |
}; |
| 109 |
#else |
| 110 |
static constexpr std::array libraryCandidates = { |
| 111 |
|
| 112 |
"libssl.so.3", "libssl.so.1.1", "libssl.so" |
| 113 |
}; |
| 114 |
#endif |
| 115 |
|
| 116 |
|
| 117 |
|
| 118 |
|
| 119 |
template<typename T> |
| 120 |
T resolveSymbol(DllHandle lib, const char* name) |
| 121 |
{ |
| 122 |
#ifdef _WIN32 |
| 123 |
return std::bit_cast<T>(GetProcAddress(lib, name)); |
| 124 |
#else |
| 125 |
return std::bit_cast<T>(dlsym(lib, name)); |
| 126 |
#endif |
| 127 |
} |
| 128 |
|
| 129 |
|
| 130 |
|
| 131 |
|
| 132 |
|
| 133 |
#ifdef _WIN32 |
| 134 |
|
| 135 |
|
| 136 |
|
| 137 |
bool loadWindowsTrustStore(const OpenSSLApi& api, SslCtx* ctx) |
| 138 |
{ |
| 139 |
|
| 140 |
HCERTSTORE hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, 0, |
| 141 |
CERT_SYSTEM_STORE_CURRENT_USER, |
| 142 |
"ROOT"); |
| 143 |
if (!hStore) return false; |
| 144 |
|
| 145 |
std::array<char, MAX_PATH> tmpPath; |
| 146 |
std::array<char, MAX_PATH> tmpFile; |
| 147 |
if (!GetTempPathA(MAX_PATH, tmpPath.data())) { |
| 148 |
CertCloseStore(hStore, 0); |
| 149 |
return false; |
| 150 |
} |
| 151 |
|
| 152 |
if (!GetTempFileNameA(tmpPath.data(), "omsx", 0, tmpFile.data())) { |
| 153 |
CertCloseStore(hStore, 0); |
| 154 |
return false; |
| 155 |
} |
| 156 |
|
| 157 |
bool ok = false; |
| 158 |
if (FILE* f = fopen(tmpFile.data(), "wb"); f) { |
| 159 |
PCCERT_CONTEXT pCert = nullptr; |
| 160 |
while ((pCert = CertEnumCertificatesInStore(hStore, pCert)) != nullptr) { |
| 161 |
DWORD len = 0; |
| 162 |
if (!CryptBinaryToStringA(pCert->pbCertEncoded, |
| 163 |
pCert->cbCertEncoded, |
| 164 |
CRYPT_STRING_BASE64HEADER, |
| 165 |
nullptr, &len) || len == 0) { |
| 166 |
continue; |
| 167 |
} |
| 168 |
std::vector<char> buf(len); |
| 169 |
if (CryptBinaryToStringA(pCert->pbCertEncoded, |
| 170 |
pCert->cbCertEncoded, |
| 171 |
CRYPT_STRING_BASE64HEADER, |
| 172 |
buf.data(), &len)) { |
| 173 |
fwrite(buf.data(), 1, len, f); |
| 174 |
} |
| 175 |
} |
| 176 |
fclose(f); |
| 177 |
ok = api.ctxLoadVerifyLocations(ctx, tmpFile.data(), nullptr) == 1; |
| 178 |
} |
| 179 |
CertCloseStore(hStore, 0); |
| 180 |
DeleteFileA(tmpFile.data()); |
| 181 |
return ok; |
| 182 |
} |
| 183 |
#endif |
| 184 |
|
| 185 |
|
| 186 |
|
| 187 |
void setupTrustStore(const OpenSSLApi& api, SslCtx* ctx) |
| 188 |
{ |
| 189 |
#ifdef _WIN32 |
| 190 |
|
| 191 |
loadWindowsTrustStore(api, ctx); |
| 192 |
#else |
| 193 |
|
| 194 |
|
| 195 |
|
| 196 |
|
| 197 |
|
| 198 |
api.ctxLoadVerifyLocations(ctx, "/etc/ssl/cert.pem", nullptr); |
| 199 |
#endif |
| 200 |
|
| 201 |
|
| 202 |
|
| 203 |
api.ctxSetDefaultVerifyPaths(ctx); |
| 204 |
} |
| 205 |
|
| 206 |
|
| 207 |
SslCtx* getSharedContext(const OpenSSLApi& api) |
| 208 |
{ |
| 209 |
static std::once_flag flag; |
| 210 |
static SslCtx* ctx = nullptr; |
| 211 |
std::call_once(flag, [&api] { |
| 212 |
|
| 213 |
ctx = api.ctxNew(api.tlsClientMethod()); |
| 214 |
if (ctx) { |
| 215 |
setupTrustStore(api, ctx); |
| 216 |
} |
| 217 |
}); |
| 218 |
return ctx; |
| 219 |
} |
| 220 |
|
| 221 |
|
| 222 |
|
| 223 |
|
| 224 |
|
| 225 |
static constexpr unsigned long OPENSSL_INIT_LOAD_SSL_STRINGS = 0x00200000UL; |
| 226 |
static constexpr unsigned long OPENSSL_INIT_LOAD_CRYPTO_STRINGS = 0x00000001UL; |
| 227 |
|
| 228 |
template<typename T> |
| 229 |
T resolve(DllHandle lib, const char* name) |
| 230 |
{ |
| 231 |
return resolveSymbol<T>(lib, name); |
| 232 |
} |
| 233 |
|
| 234 |
|
| 235 |
|
| 236 |
|
| 237 |
template<typename T> |
| 238 |
T resolveAny(DllHandle lib, DllHandle crypto, const char* name) |
| 239 |
{ |
| 240 |
if (auto p = resolveSymbol<T>(lib, name)) { |
| 241 |
return p; |
| 242 |
} |
| 243 |
if (crypto) { |
| 244 |
if (auto p = resolveSymbol<T>(crypto, name)) { |
| 245 |
return p; |
| 246 |
} |
| 247 |
} |
| 248 |
return nullptr; |
| 249 |
} |
| 250 |
|
| 251 |
|
| 252 |
std::string cryptoNameFor(std::string n) |
| 253 |
{ |
| 254 |
if (auto pos = n.find("libssl"); pos != std::string::npos) { |
| 255 |
n.replace(pos, 6, "libcrypto"); |
| 256 |
} |
| 257 |
return n; |
| 258 |
} |
| 259 |
|
| 260 |
bool openLibrary(DllHandle& sslLib, DllHandle& cryptoLib) |
| 261 |
{ |
| 262 |
for (const char* name : libraryCandidates) { |
| 263 |
if (auto h = openSslLib(name)) { |
| 264 |
sslLib = h; |
| 265 |
|
| 266 |
|
| 267 |
|
| 268 |
|
| 269 |
std::string cryptoName = cryptoNameFor(name); |
| 270 |
if (auto ch = openSslLib(cryptoName.c_str())) { |
| 271 |
cryptoLib = ch; |
| 272 |
} |
| 273 |
return true; |
| 274 |
} |
| 275 |
} |
| 276 |
return false; |
| 277 |
} |
| 278 |
|
| 279 |
bool resolveSymbols(OpenSSLApi& api, DllHandle sslLib, DllHandle cryptoLib) |
| 280 |
{ |
| 281 |
|
| 282 |
|
| 283 |
|
| 284 |
|
| 285 |
|
| 286 |
api.init = resolve<decltype(api.init)>(sslLib, "OPENSSL_init_ssl"); |
| 287 |
if (!api.init) return false; |
| 288 |
api.tlsClientMethod = resolve<decltype(api.tlsClientMethod)>(sslLib, "TLS_client_method"); |
| 289 |
if (!api.tlsClientMethod) return false; |
| 290 |
api.ctxNew = resolve<decltype(api.ctxNew)>(sslLib, "SSL_CTX_new"); |
| 291 |
if (!api.ctxNew) return false; |
| 292 |
api.ctxSetDefaultVerifyPaths = resolve<decltype(api.ctxSetDefaultVerifyPaths)>(sslLib, "SSL_CTX_set_default_verify_paths"); |
| 293 |
if (!api.ctxSetDefaultVerifyPaths) return false; |
| 294 |
api.ctxLoadVerifyLocations = resolve<decltype(api.ctxLoadVerifyLocations)>(sslLib, "SSL_CTX_load_verify_locations"); |
| 295 |
if (!api.ctxLoadVerifyLocations) return false; |
| 296 |
api.newSession = resolve<decltype(api.newSession)>(sslLib, "SSL_new"); |
| 297 |
if (!api.newSession) return false; |
| 298 |
api.freeSession = resolve<decltype(api.freeSession)>(sslLib, "SSL_free"); |
| 299 |
if (!api.freeSession) return false; |
| 300 |
api.setFd = resolve<decltype(api.setFd)>(sslLib, "SSL_set_fd"); |
| 301 |
if (!api.setFd) return false; |
| 302 |
api.setVerify = resolve<decltype(api.setVerify)>(sslLib, "SSL_set_verify"); |
| 303 |
if (!api.setVerify) return false; |
| 304 |
api.connect = resolve<decltype(api.connect)>(sslLib, "SSL_connect"); |
| 305 |
if (!api.connect) return false; |
| 306 |
api.ctrl = resolve<decltype(api.ctrl)>(sslLib, "SSL_ctrl"); |
| 307 |
if (!api.ctrl) return false; |
| 308 |
api.get0Param = resolve<decltype(api.get0Param)>(sslLib, "SSL_get0_param"); |
| 309 |
if (!api.get0Param) return false; |
| 310 |
api.verifyParamSet1Host = resolveAny<decltype(api.verifyParamSet1Host)>(sslLib, cryptoLib, "X509_VERIFY_PARAM_set1_host"); |
| 311 |
if (!api.verifyParamSet1Host) return false; |
| 312 |
api.read = resolve<decltype(api.read)>(sslLib, "SSL_read"); |
| 313 |
if (!api.read) return false; |
| 314 |
api.write = resolve<decltype(api.write)>(sslLib, "SSL_write"); |
| 315 |
if (!api.write) return false; |
| 316 |
api.pending = resolve<decltype(api.pending)>(sslLib, "SSL_pending"); |
| 317 |
if (!api.pending) return false; |
| 318 |
api.shutdown = resolve<decltype(api.shutdown)>(sslLib, "SSL_shutdown"); |
| 319 |
if (!api.shutdown) return false; |
| 320 |
api.getError = resolve<decltype(api.getError)>(sslLib, "SSL_get_error"); |
| 321 |
if (!api.getError) return false; |
| 322 |
api.getVerifyResult = resolve<decltype(api.getVerifyResult)>(sslLib, "SSL_get_verify_result"); |
| 323 |
if (!api.getVerifyResult) return false; |
| 324 |
api.verifyCertErrorString = resolveAny<decltype(api.verifyCertErrorString)>(sslLib, cryptoLib, "X509_verify_cert_error_string"); |
| 325 |
if (!api.verifyCertErrorString) return false; |
| 326 |
api.get1PeerCertificate = resolve<decltype(api.get1PeerCertificate)>(sslLib, "SSL_get1_peer_certificate"); |
| 327 |
if (!api.get1PeerCertificate) return false; |
| 328 |
api.freeX509 = resolveAny<decltype(api.freeX509)>(sslLib, cryptoLib, "X509_free"); |
| 329 |
if (!api.freeX509) return false; |
| 330 |
api.errGetError = resolveAny<decltype(api.errGetError)>(sslLib, cryptoLib, "ERR_get_error"); |
| 331 |
if (!api.errGetError) return false; |
| 332 |
api.errErrorStringN = resolveAny<decltype(api.errErrorStringN)>(sslLib, cryptoLib, "ERR_error_string_n"); |
| 333 |
if (!api.errErrorStringN) return false; |
| 334 |
|
| 335 |
|
| 336 |
|
| 337 |
api.version = resolveAny<decltype(api.version)>(sslLib, cryptoLib, "OPENSSL_version"); |
| 338 |
if (!api.version) { |
| 339 |
|
| 340 |
api.version = resolveAny<decltype(api.version)>(sslLib, cryptoLib, "OpenSSL_version"); |
| 341 |
} |
| 342 |
if (!api.version) { |
| 343 |
api.version = resolveAny<decltype(api.version)>(sslLib, cryptoLib, "SSLeay_version"); |
| 344 |
} |
| 345 |
|
| 346 |
|
| 347 |
if (api.init(OPENSSL_INIT_LOAD_SSL_STRINGS | |
| 348 |
OPENSSL_INIT_LOAD_CRYPTO_STRINGS, nullptr) != 1) { |
| 349 |
return false; |
| 350 |
} |
| 351 |
return true; |
| 352 |
} |
| 353 |
|
| 354 |
static constexpr int SSL_VERIFY_NONE = 0x00; |
| 355 |
static constexpr int SSL_VERIFY_PEER = 0x01; |
| 356 |
static constexpr int SSL_ERROR_WANT_READ = 2; |
| 357 |
static constexpr int SSL_ERROR_WANT_WRITE = 3; |
| 358 |
static constexpr int SSL_ERROR_ZERO_RETURN = 6; |
| 359 |
static constexpr int SSL_CTRL_SET_TLSEXT_HOSTNAME = 55; |
| 360 |
static constexpr long TLSEXT_NAMETYPE_HOST_NAME = 0; |
| 361 |
static constexpr int OPENSSL_VERSION = 0; |
| 362 |
|
| 363 |
|
| 364 |
static constexpr long X509_V_OK = 0; |
| 365 |
static constexpr long X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT = 2; |
| 366 |
static constexpr long X509_V_ERR_CERT_NOT_YET_VALID = 9; |
| 367 |
static constexpr long X509_V_ERR_CERT_HAS_EXPIRED = 10; |
| 368 |
static constexpr long X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT = 18; |
| 369 |
static constexpr long X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN = 19; |
| 370 |
static constexpr long X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY = 20; |
| 371 |
static constexpr long X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE = 21; |
| 372 |
static constexpr long X509_V_ERR_CERT_REVOKED = 23; |
| 373 |
static constexpr long X509_V_ERR_INVALID_CA = 24; |
| 374 |
static constexpr long X509_V_ERR_CERT_UNTRUSTED = 27; |
| 375 |
static constexpr long X509_V_ERR_CERT_REJECTED = 28; |
| 376 |
static constexpr long X509_V_ERR_HOSTNAME_MISMATCH = 62; |
| 377 |
|
| 378 |
} |
| 379 |
|
| 380 |
|
| 381 |
|
| 382 |
|
| 383 |
|
| 384 |
LibHandle::LibHandle() |
| 385 |
: api(std::make_unique<OpenSSLApi>()) |
| 386 |
{ |
| 387 |
} |
| 388 |
|
| 389 |
LibHandle::~LibHandle() = default; |
| 390 |
|
| 391 |
LibHandle* load() |
| 392 |
{ |
| 393 |
static LibHandle singleton; |
| 394 |
static std::once_flag flag; |
| 395 |
std::call_once(flag, [] { |
| 396 |
|
| 397 |
|
| 398 |
DllHandle sslLib = nullptr; |
| 399 |
DllHandle cryptoLib = nullptr; |
| 400 |
if (openLibrary(sslLib, cryptoLib)) { |
| 401 |
singleton.loaded = resolveSymbols(*singleton.api, sslLib, cryptoLib); |
| 402 |
singleton.libHandle = sslLib; |
| 403 |
singleton.cryptoHandle = cryptoLib; |
| 404 |
} |
| 405 |
}); |
| 406 |
return singleton.loaded ? &singleton : nullptr; |
| 407 |
} |
| 408 |
|
| 409 |
SessionHandle::SessionHandle(Ssl* ssl_, const OpenSSLApi* api_) |
| 410 |
: ssl(ssl_) |
| 411 |
, api(api_) |
| 412 |
{ |
| 413 |
} |
| 414 |
|
| 415 |
SessionHandle::SessionHandle(SessionHandle&& other) noexcept |
| 416 |
: ssl(std::exchange(other.ssl, nullptr)) |
| 417 |
, api(other.api) |
| 418 |
{ |
| 419 |
} |
| 420 |
|
| 421 |
SessionHandle& SessionHandle::operator=(SessionHandle&& other) noexcept |
| 422 |
{ |
| 423 |
if (this != &other) { |
| 424 |
release(); |
| 425 |
ssl = std::exchange(other.ssl, nullptr); |
| 426 |
api = other.api; |
| 427 |
} |
| 428 |
return *this; |
| 429 |
} |
| 430 |
|
| 431 |
SessionHandle::~SessionHandle() |
| 432 |
{ |
| 433 |
release(); |
| 434 |
} |
| 435 |
|
| 436 |
void SessionHandle::release() noexcept |
| 437 |
{ |
| 438 |
if (!ssl) return; |
| 439 |
|
| 440 |
api->shutdown(ssl); |
| 441 |
api->freeSession(ssl); |
| 442 |
ssl = nullptr; |
| 443 |
} |
| 444 |
|
| 445 |
HandshakeResult SessionHandle::handshake() const |
| 446 |
{ |
| 447 |
int r = api->connect(ssl); |
| 448 |
if (r == 1) return HandshakeType::Done; |
| 449 |
int e = api->getError(ssl, r); |
| 450 |
if (e == SSL_ERROR_WANT_READ) return HandshakeType::NeedRead; |
| 451 |
if (e == SSL_ERROR_WANT_WRITE) return HandshakeType::NeedWrite; |
| 452 |
|
| 453 |
|
| 454 |
return std::unexpected(ErrorCode{api->errGetError()}); |
| 455 |
} |
| 456 |
|
| 457 |
int SessionHandle::verifyResult() const |
| 458 |
{ |
| 459 |
long vr = api->getVerifyResult(ssl); |
| 460 |
if (vr == X509_V_OK) return 0; |
| 461 |
|
| 462 |
|
| 463 |
X509* peer = api->get1PeerCertificate(ssl); |
| 464 |
if (!peer) { |
| 465 |
return 9; |
| 466 |
} |
| 467 |
api->freeX509(peer); |
| 468 |
|
| 469 |
|
| 470 |
switch (vr) { |
| 471 |
case X509_V_ERR_HOSTNAME_MISMATCH: |
| 472 |
return 11; |
| 473 |
case X509_V_ERR_CERT_HAS_EXPIRED: |
| 474 |
case X509_V_ERR_CERT_NOT_YET_VALID: |
| 475 |
return 12; |
| 476 |
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: |
| 477 |
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: |
| 478 |
return 13; |
| 479 |
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: |
| 480 |
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: |
| 481 |
case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: |
| 482 |
case X509_V_ERR_CERT_UNTRUSTED: |
| 483 |
case X509_V_ERR_CERT_REJECTED: |
| 484 |
return 14; |
| 485 |
case X509_V_ERR_CERT_REVOKED: |
| 486 |
return 15; |
| 487 |
case X509_V_ERR_INVALID_CA: |
| 488 |
return 16; |
| 489 |
default: |
| 490 |
return 10; |
| 491 |
} |
| 492 |
} |
| 493 |
|
| 494 |
std::string SessionHandle::verifyErrorDescription() const |
| 495 |
{ |
| 496 |
long vr = api->getVerifyResult(ssl); |
| 497 |
if (vr == X509_V_OK) return ""; |
| 498 |
return api->verifyCertErrorString(vr); |
| 499 |
} |
| 500 |
|
| 501 |
IoReturn SessionHandle::read(std::span<uint8_t> buf) const |
| 502 |
{ |
| 503 |
int r = api->read(ssl, buf.data(), static_cast<int>(buf.size())); |
| 504 |
if (r > 0) return static_cast<size_t>(r); |
| 505 |
int e = api->getError(ssl, r); |
| 506 |
if (e == SSL_ERROR_WANT_READ || e == SSL_ERROR_WANT_WRITE) { |
| 507 |
return std::unexpected(IoError{IoResult::WouldBlock}); |
| 508 |
} |
| 509 |
if (e == SSL_ERROR_ZERO_RETURN) { |
| 510 |
return std::unexpected(IoError{IoResult::Closed}); |
| 511 |
} |
| 512 |
|
| 513 |
|
| 514 |
return std::unexpected(IoError{std::unexpected(ErrorCode{api->errGetError()})}); |
| 515 |
} |
| 516 |
|
| 517 |
IoReturn SessionHandle::write(std::span<const uint8_t> buf) const |
| 518 |
{ |
| 519 |
int r = api->write(ssl, buf.data(), static_cast<int>(buf.size())); |
| 520 |
if (r > 0) return static_cast<size_t>(r); |
| 521 |
int e = api->getError(ssl, r); |
| 522 |
if (e == SSL_ERROR_WANT_READ || e == SSL_ERROR_WANT_WRITE) { |
| 523 |
return std::unexpected(IoError{IoResult::WouldBlock}); |
| 524 |
} |
| 525 |
if (e == SSL_ERROR_ZERO_RETURN) { |
| 526 |
return std::unexpected(IoError{IoResult::Closed}); |
| 527 |
} |
| 528 |
|
| 529 |
|
| 530 |
return std::unexpected(IoError{std::unexpected(ErrorCode{api->errGetError()})}); |
| 531 |
} |
| 532 |
|
| 533 |
size_t SessionHandle::pending() const |
| 534 |
{ |
| 535 |
return static_cast<size_t>(api->pending(ssl)); |
| 536 |
} |
| 537 |
|
| 538 |
std::optional<SessionHandle> LibHandle::createClientSession( |
| 539 |
bool verify, zstring_view hostname, int fd) const |
| 540 |
{ |
| 541 |
const OpenSSLApi& sslApi = *this->api; |
| 542 |
SslCtx* ctx = getSharedContext(sslApi); |
| 543 |
if (!ctx) return std::nullopt; |
| 544 |
Ssl* ssl = sslApi.newSession(ctx); |
| 545 |
if (!ssl) return std::nullopt; |
| 546 |
if (sslApi.setFd(ssl, fd) != 1) { |
| 547 |
sslApi.freeSession(ssl); |
| 548 |
return std::nullopt; |
| 549 |
} |
| 550 |
sslApi.setVerify(ssl, verify ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, nullptr); |
| 551 |
|
| 552 |
|
| 553 |
|
| 554 |
if (!hostname.empty()) { |
| 555 |
sslApi.ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, |
| 556 |
TLSEXT_NAMETYPE_HOST_NAME, |
| 557 |
hostname.data()); |
| 558 |
if (verify) { |
| 559 |
sslApi.verifyParamSet1Host(sslApi.get0Param(ssl), hostname.data(), |
| 560 |
hostname.size()); |
| 561 |
} |
| 562 |
} |
| 563 |
return SessionHandle(ssl, &sslApi); |
| 564 |
} |
| 565 |
|
| 566 |
zstring_view LibHandle::version() const |
| 567 |
{ |
| 568 |
const OpenSSLApi& sslApi = *this->api; |
| 569 |
return sslApi.version ? sslApi.version(OPENSSL_VERSION) : ""; |
| 570 |
} |
| 571 |
|
| 572 |
std::string to_string(ErrorCode ec) |
| 573 |
{ |
| 574 |
|
| 575 |
|
| 576 |
|
| 577 |
|
| 578 |
if (const auto* lib = load(); lib && ec.value != 0) { |
| 579 |
std::array<char, 256> buf; |
| 580 |
lib->api->errErrorStringN(ec.value, buf.data(), buf.size()); |
| 581 |
return buf.data(); |
| 582 |
} |
| 583 |
return "OpenSSL error"; |
| 584 |
} |
| 585 |
|
| 586 |
} |