1 /** 2 OpenSSL based SSL/TLS stream implementation 3 4 Copyright: © 2012-2014 Sönke Ludwig 5 License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 6 Authors: Sönke Ludwig 7 */ 8 module vibe.stream.openssl; 9 version(Have_openssl): 10 import vibe.core.log; 11 import vibe.core.net; 12 import vibe.core.stream; 13 import vibe.core.sync; 14 import vibe.stream.tls; 15 import vibe.internal.interfaceproxy : InterfaceProxy; 16 17 import std.algorithm; 18 import std.array; 19 import std.conv; 20 import std.exception; 21 import std.socket; 22 import std.string; 23 24 import core.stdc.string : strlen; 25 import core.sync.mutex; 26 import core.thread; 27 28 /**************************************************************************************************/ 29 /* Public types */ 30 /**************************************************************************************************/ 31 32 import deimos.openssl.bio; 33 import deimos.openssl.err; 34 import deimos.openssl.opensslv; 35 import deimos.openssl.rand; 36 import deimos.openssl.ssl; 37 import deimos.openssl.stack; 38 import deimos.openssl.x509v3; 39 40 version (VibePragmaLib) { 41 pragma(lib, "ssl"); 42 version (Windows) pragma(lib, "eay"); 43 } 44 45 version(VibeForceALPN) enum alpn_forced = true; 46 else enum alpn_forced = false; 47 enum haveALPN = OPENSSL_VERSION_NUMBER >= 0x10200000 || alpn_forced; 48 49 // openssl/1.1.0 hack: provides a 1.0.x API in terms of the 1.1.x API 50 static if (OPENSSL_VERSION_AT_LEAST(1, 1, 0)) { 51 extern(C) const(SSL_METHOD)* TLS_client_method(); 52 alias SSLv23_client_method = TLS_client_method; 53 54 extern(C) const(SSL_METHOD)* TLS_server_method(); 55 alias SSLv23_server_method = TLS_server_method; 56 57 // #define SSL_get_ex_new_index(l, p, newf, dupf, freef) \ 58 // CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL, l, p, newf, dupf, freef) 59 60 extern(C) int CRYPTO_get_ex_new_index(int class_index, c_long argl, void *argp, 61 CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, 62 CRYPTO_EX_free *free_func); 63 64 int SSL_get_ex_new_index(c_long argl, void *argp, 65 CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, 66 CRYPTO_EX_free *free_func) { 67 // # define CRYPTO_EX_INDEX_SSL 0 68 return CRYPTO_get_ex_new_index(0, argl, argp, new_func, dup_func, 69 free_func); 70 } 71 72 extern(C) BIGNUM* BN_get_rfc3526_prime_2048(BIGNUM *bn); 73 74 alias get_rfc3526_prime_2048 = BN_get_rfc3526_prime_2048; 75 76 // # define sk_num OPENSSL_sk_num 77 static if (!is(typeof(OPENSSL_sk_num))) 78 { 79 extern(C) int OPENSSL_sk_num(const void *); 80 extern(C) int sk_num(const(_STACK)* p) { return OPENSSL_sk_num(p); } 81 } 82 83 // # define sk_value OPENSSL_sk_value 84 static if (!is(typeof(OPENSSL_sk_value))) 85 { 86 extern(C) void *OPENSSL_sk_value(const void *, int); 87 extern(C) void* sk_value(const(_STACK)* p, int i) { return OPENSSL_sk_value(p, i); } 88 } 89 90 static if (!is(typeof(OPENSSL_sk_free))) 91 { 92 // Version v1.x.x of the bindings don't have this, 93 // but it's been available since v1.1.0 94 private extern(C) void *OPENSSL_sk_free(const void *); 95 } 96 97 private enum SSL_CTRL_SET_MIN_PROTO_VERSION = 123; 98 private enum SSL_CTRL_SET_MAX_PROTO_VERSION = 124; 99 100 private int SSL_CTX_set_min_proto_version(ssl_ctx_st* ctx, int ver) { 101 return cast(int) SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, ver, null); 102 } 103 104 private int SSL_CTX_set_max_proto_version(ssl_ctx_st* ctx, int ver) { 105 return cast(int) SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, ver, null); 106 } 107 108 private int SSL_set_min_proto_version(ssl_st* s, int ver) { 109 return cast(int) SSL_ctrl(s, SSL_CTRL_SET_MIN_PROTO_VERSION, ver, null); 110 } 111 112 extern(C) nothrow { 113 void BIO_set_init(BIO* bio, int init_) @trusted; 114 int BIO_get_init(BIO* bio) @trusted; 115 void BIO_set_data(BIO* bio, void* ptr) @trusted; 116 void* BIO_get_data(BIO* bio) @trusted; 117 void BIO_set_shutdown(BIO* bio, int shut) @trusted; 118 int BIO_get_shutdown(BIO* bio) @trusted; 119 void BIO_clear_flags(BIO* b, int flags) @trusted; 120 int BIO_test_flags(BIO* b, int flags) @trusted; 121 void BIO_set_flags(BIO* b, int flags) @trusted; 122 123 alias BIOMethWriteCallback = int function(BIO*, const(char)*, int); 124 alias BIOMethReadCallback = int function(BIO*, const(char)*, int); 125 alias BIOMethCtrlCallback = c_long function(BIO*, int, c_long, void*); 126 alias BIOMethCreateCallback = int function(BIO*); 127 alias BIOMethDestroyCallback = int function(BIO*); 128 129 int BIO_get_new_index(); 130 BIO_METHOD* BIO_meth_new(int type, const(char)* name); 131 void BIO_meth_free(BIO_METHOD* biom); 132 int BIO_meth_set_write(BIO_METHOD* biom, BIOMethWriteCallback cb); 133 int BIO_meth_set_read(BIO_METHOD* biom, BIOMethReadCallback cb); 134 int BIO_meth_set_ctrl(BIO_METHOD* biom, BIOMethCtrlCallback cb); 135 int BIO_meth_set_create(BIO_METHOD* biom, BIOMethCreateCallback cb); 136 int BIO_meth_set_destroy(BIO_METHOD* biom, BIOMethDestroyCallback cb); 137 } 138 139 static if (OPENSSL_VERSION_AT_LEAST(3, 0, 0)) { 140 extern (C) nothrow { 141 X509 *SSL_get1_peer_certificate(const SSL *ssl); 142 void ERR_new(); 143 void ERR_set_debug(const char *file, int line, const char *func); 144 void ERR_set_error(int lib, int reason, const char *fmt, ...); 145 } 146 147 alias SSL_get_peer_certificate = SSL_get1_peer_certificate; 148 } 149 } else { 150 private void BIO_set_init(BIO* b, int init_) @safe nothrow { 151 b.init_ = 1; 152 } 153 private int BIO_get_init(BIO* b) @safe nothrow { 154 return b.init_; 155 } 156 private void BIO_set_data(BIO* b, void* ptr) @safe nothrow { 157 b.ptr = ptr; 158 } 159 private void* BIO_get_data(BIO* b) @safe nothrow { 160 return b.ptr; 161 } 162 private void BIO_set_shutdown(BIO* b, int shut) @safe nothrow { 163 b.shutdown = shut; 164 } 165 private int BIO_get_shutdown(BIO* b) @safe nothrow { 166 return b.shutdown; 167 } 168 private void BIO_clear_flags(BIO *b, int flags) @safe nothrow { 169 b.flags &= ~flags; 170 } 171 private int BIO_test_flags(BIO *b, int flags) @safe nothrow { 172 return (b.flags & flags); 173 } 174 private void BIO_set_flags(BIO *b, int flags) @safe nothrow { 175 b.flags |= flags; 176 } 177 178 // OpenSSL 1.1 renamed `sk_*` to OpenSSL_sk_*` 179 private alias OPENSSL_sk_free = sk_free; 180 181 // Temporary hack: Deimos OpenSSL v3.0.1 is missing bindings for OpenSSL v1.0.x 182 // Until it's updated, we have duplicates here, see: 183 // https://github.com/vibe-d/vibe.d/pull/2658 184 // https://github.com/vibe-d/vibe.d/pull/2661 185 extern(C) const(SSL_METHOD)* SSLv23_client_method(); 186 extern(C) const(SSL_METHOD)* SSLv23_server_method(); 187 188 extern(C) int CRYPTO_num_locks(); 189 extern(C) void CRYPTO_set_locking_callback( 190 void function(int mode, int type, const(char)* file, int line) func); 191 } 192 193 // Copied from https://github.com/D-Programming-Deimos/openssl/pull/69 194 // Remove once we are depending on >= v3.0.2 195 static if (OPENSSL_VERSION_AT_LEAST(3, 0, 0)) 196 { 197 // The argument type for `SSL_[CTX_][gs]et_options was changed between 1.1.1 198 // and 3.0.0, from `c_long` to `uint64_t`. See below commit. 199 // https://github.com/openssl/openssl/commit/56bd17830f2d5855b533d923d4e0649d3ed61d11 200 201 extern(C) nothrow { 202 ulong SSL_CTX_get_options(const SSL_CTX* ctx); 203 ulong SSL_get_options(const SSL* ssl); 204 ulong SSL_CTX_clear_options(SSL_CTX* ctx, ulong op); 205 ulong SSL_clear_options(SSL* ssl, ulong op); 206 ulong SSL_CTX_set_options(SSL_CTX* ctx, ulong op); 207 ulong SSL_set_options(SSL* ssl, ulong op); 208 } 209 } 210 else static if (OPENSSL_VERSION_AT_LEAST(1, 1, 0)) 211 { 212 // Note: Despite the manuals listing the return type (as well as parameter) 213 // as 'long', the `.h` was `unsigned long`. 214 215 extern(C) nothrow { 216 c_ulong SSL_CTX_get_options(const SSL_CTX* ctx); 217 c_ulong SSL_get_options(const SSL* ssl); 218 c_ulong SSL_CTX_clear_options(SSL_CTX* ctx, c_ulong op); 219 c_ulong SSL_clear_options(SSL* ssl, c_ulong op); 220 c_ulong SSL_CTX_set_options(SSL_CTX* ctx, c_ulong op); 221 c_ulong SSL_set_options(SSL* ssl, c_ulong op); 222 } 223 } 224 else 225 { 226 // Before v1.1.0, those were macros. See below commit. 227 // https://github.com/openssl/openssl/commit/8106cb8b6d706079cbcabd4631f05e4526a316e1 228 229 extern(C) nothrow { 230 c_ulong SSL_CTX_set_options()(SSL_CTX* ctx, c_ulong op) { 231 return SSL_CTX_ctrl(ctx, SSL_CTRL_OPTIONS, op, null); 232 } 233 c_ulong SSL_CTX_clear_options()(SSL_CTX* ctx, c_ulong op) { 234 return SSL_CTX_ctrl(ctx, SSL_CTRL_CLEAR_OPTIONS, op, null); 235 } 236 c_ulong SSL_CTX_get_options()(SSL_CTX* ctx) { 237 return SSL_CTX_ctrl(ctx, SSL_CTRL_OPTIONS, 0, null); 238 } 239 c_ulong SSL_set_options()(SSL* ssl,op) { 240 return SSL_ctrl(ssl, SSL_CTRL_OPTIONS, op, null); 241 } 242 c_ulong SSL_clear_options()(SSL* ssl, c_long op) { 243 return SSL_ctrl(ssl, SSL_CTRL_CLEAR_OPTIONS, op, null); 244 } 245 c_ulong SSL_get_options()(SSL* ssl) { 246 return SSL_ctrl(ssl, SSL_CTRL_OPTIONS, 0, null); 247 } 248 } 249 250 // The need for calling `CRYPTO_set_id_callback` / `CRYPTO_set_locking_callback` 251 // was removed in OpenSSL 1.1.0, which are the only users of those callbacks 252 // and mutexes. 253 private __gshared InterruptibleTaskMutex[] g_cryptoMutexes; 254 255 private extern(C) c_ulong onCryptoGetThreadID() nothrow @safe 256 { 257 try { 258 return cast(c_ulong)(cast(size_t)() @trusted { return cast(void*)Thread.getThis(); } () * 0x35d2c57); 259 } catch (Exception e) { 260 logWarn("OpenSSL: failed to get current thread ID: %s", e.msg); 261 return 0; 262 } 263 } 264 265 private extern(C) void onCryptoLock(int mode, int n, const(char)* file, int line) nothrow @safe 266 { 267 try { 268 enforce(n >= 0 && n < () @trusted { return g_cryptoMutexes; } ().length, "Mutex index out of range."); 269 auto mutex = () @trusted { return g_cryptoMutexes[n]; } (); 270 assert(mutex !is null); 271 if (mode & CRYPTO_LOCK) mutex.lock(); 272 else mutex.unlock(); 273 } catch (Exception e) { 274 logWarn("OpenSSL: failed to lock/unlock mutex: %s", e.msg); 275 } 276 } 277 } 278 279 // Deimos had an incorrect translation for this define prior to 2.0.2+1.1.0h 280 // See https://github.com/D-Programming-Deimos/openssl/issues/63#issuecomment-840266138 281 static if (!is(typeof(GEN_DNS))) 282 { 283 private enum GEN_DNS = GENERAL_NAME.GEN_DNS; 284 private enum GEN_IPADD = GENERAL_NAME.GEN_IPADD; 285 } 286 287 private int SSL_set_tlsext_host_name(ssl_st* s, const(char)* c) @trusted { 288 return cast(int) SSL_ctrl(s, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, cast(void*)c); 289 } 290 291 /** 292 Creates an SSL/TLS tunnel within an existing stream. 293 294 Note: Be sure to call finalize before finalizing/closing the outer stream so that the SSL 295 tunnel is properly closed first. 296 */ 297 final class OpenSSLStream : TLSStream { 298 @safe: 299 300 private { 301 InterfaceProxy!Stream m_stream; 302 TLSContext m_tlsCtx; 303 TLSStreamState m_state; 304 SSLState m_tls; 305 BIO* m_bio; 306 ubyte[64] m_peekBuffer; 307 TLSCertificateInformation m_peerCertificateInfo; 308 X509* m_peerCertificate; 309 } 310 311 this(InterfaceProxy!Stream underlying, OpenSSLContext ctx, TLSStreamState state, string peer_name = null, NetworkAddress peer_address = NetworkAddress.init, string[] alpn = null) 312 { 313 // sanity check to distinguish any error that might have slipped 314 // somewhere else from errors generated here 315 validateSSLErrors(); 316 317 m_stream = underlying; 318 m_state = state; 319 m_tlsCtx = ctx; 320 m_tls = ctx.createClientCtx(); 321 scope (failure) { 322 () @trusted { SSL_free(m_tls); } (); 323 m_tls = null; 324 } 325 326 static if (OPENSSL_VERSION_AT_LEAST(1, 1, 0)) { 327 if (!s_bio_methods) initBioMethods(); 328 329 m_bio = () @trusted { return BIO_new(s_bio_methods); } (); 330 } else 331 m_bio = () @trusted { return BIO_new(&s_bio_methods); } (); 332 enforce(m_bio !is null, "SSL failed: failed to create BIO structure."); 333 BIO_set_init(m_bio, 1); 334 BIO_set_data(m_bio, () @trusted { return cast(void*)this; } ()); // lifetime is shorter than this, so no GC.addRange needed. 335 BIO_set_shutdown(m_bio, 0); 336 337 () @trusted { SSL_set_bio(m_tls, m_bio, m_bio); } (); 338 339 if (state != TLSStreamState.connected) { 340 OpenSSLContext.VerifyData vdata; 341 vdata.verifyDepth = ctx.maxCertChainLength; 342 vdata.validationMode = ctx.peerValidationMode; 343 vdata.callback = ctx.peerValidationCallback; 344 vdata.peerName = peer_name; 345 vdata.peerAddress = peer_address; 346 checkSSLRet(() @trusted { return SSL_set_ex_data(m_tls, gs_verifyDataIndex, &vdata); } (), "Setting SSL user data"); 347 scope (exit) () @trusted { SSL_set_ex_data(m_tls, gs_verifyDataIndex, null); } (); 348 349 final switch (state) { 350 case TLSStreamState.accepting: 351 //SSL_set_accept_state(m_tls); 352 checkSSLRet(() @trusted { return SSL_accept(m_tls); } (), "Accepting SSL tunnel"); 353 break; 354 case TLSStreamState.connecting: 355 // a client stream can override the default ALPN setting for this context 356 if (alpn.length) setClientALPN(alpn); 357 if (peer_name.length) 358 SSL_set_tlsext_host_name(m_tls, peer_name.toStringz); 359 //SSL_set_connect_state(m_tls); 360 validateSSLErrors(); 361 checkSSLRet(() @trusted { return SSL_connect(m_tls); } (), "Connecting TLS tunnel"); 362 break; 363 case TLSStreamState.connected: 364 break; 365 } 366 367 // ensure that the SSL tunnel gets terminated when an error happens during verification 368 scope (failure) () @trusted { SSL_shutdown(m_tls); } (); 369 370 m_peerCertificate = () @trusted { return SSL_get_peer_certificate(m_tls); } (); 371 if (m_peerCertificate) { 372 readPeerCertInfo(); 373 auto result = () @trusted { return SSL_get_verify_result(m_tls); } (); 374 if (result == X509_V_OK && (ctx.peerValidationMode & TLSPeerValidationMode.checkPeer)) { 375 if (!verifyCertName(m_peerCertificate, GEN_DNS, vdata.peerName)) { 376 version(Windows) import core.sys.windows.winsock2; 377 else import core.sys.posix.netinet.in_; 378 379 logDiagnostic("TLS peer name '%s' couldn't be verified, trying IP address.", vdata.peerName); 380 char* addr; 381 int addrlen; 382 switch (vdata.peerAddress.family) { 383 default: break; 384 case AF_INET: 385 addr = cast(char*)&vdata.peerAddress.sockAddrInet4.sin_addr; 386 addrlen = vdata.peerAddress.sockAddrInet4.sin_addr.sizeof; 387 break; 388 case AF_INET6: 389 addr = cast(char*)&vdata.peerAddress.sockAddrInet6.sin6_addr; 390 addrlen = vdata.peerAddress.sockAddrInet6.sin6_addr.sizeof; 391 break; 392 } 393 394 if (!verifyCertName(m_peerCertificate, GEN_IPADD, () @trusted { return addr[0 .. addrlen]; } ())) { 395 logDiagnostic("Error validating TLS peer address"); 396 result = X509_V_ERR_APPLICATION_VERIFICATION; 397 } 398 } 399 } 400 401 enforce(result == X509_V_OK, "Peer failed the certificate validation: "~to!string(result)); 402 } //else enforce(ctx.verifyMode < requireCert); 403 } 404 } 405 406 /** Read certificate info into the clientInformation field */ 407 private void readPeerCertInfo() 408 { 409 X509_NAME* name = () @trusted { return X509_get_subject_name(m_peerCertificate); } (); 410 411 int c = () @trusted { return X509_NAME_entry_count(name); } (); 412 foreach (i; 0 .. c) { 413 X509_NAME_ENTRY *e = () @trusted { return X509_NAME_get_entry(name, i); } (); 414 ASN1_OBJECT *obj = () @trusted { return X509_NAME_ENTRY_get_object(e); } (); 415 ASN1_STRING *val = () @trusted { return X509_NAME_ENTRY_get_data(e); } (); 416 417 auto longName = () @trusted { return OBJ_nid2ln(OBJ_obj2nid(obj)).to!string; } (); 418 auto valStr = () @trusted { return cast(string)val.data[0 .. val.length]; } (); // FIXME: .idup? 419 420 m_peerCertificateInfo.subjectName.addField(longName, valStr); 421 } 422 m_peerCertificateInfo._x509 = m_peerCertificate; 423 } 424 425 ~this() 426 { 427 if (m_peerCertificate) () @trusted { X509_free(m_peerCertificate); } (); 428 if (m_tls) () @trusted { SSL_free(m_tls); } (); 429 } 430 431 @property bool empty() 432 { 433 return leastSize() == 0; 434 } 435 436 @property ulong leastSize() 437 { 438 if(m_tls == null) return 0; 439 440 auto ret = () @trusted { return SSL_peek(m_tls, m_peekBuffer.ptr, 1); } (); 441 if (ret != 0) // zero means the connection got closed 442 checkSSLRet(ret, "Peeking TLS stream"); 443 return () @trusted { return SSL_pending(m_tls); } (); 444 } 445 446 @property bool dataAvailableForRead() 447 { 448 return () @trusted { return SSL_pending(m_tls); } () > 0 || m_stream.dataAvailableForRead; 449 } 450 451 const(ubyte)[] peek() 452 { 453 auto ret = checkSSLRet(() @trusted { return SSL_peek(m_tls, m_peekBuffer.ptr, m_peekBuffer.length); } (), "Peeking TLS stream"); 454 return ret > 0 ? m_peekBuffer[0 .. ret] : null; 455 } 456 457 size_t read(scope ubyte[] dst, IOMode mode) 458 { 459 size_t nbytes = 0; 460 if(m_tls == null) 461 throw new Exception("Reading from closed stream"); 462 463 while (dst.length > 0) { 464 int readlen = min(dst.length, int.max); 465 auto ret = checkSSLRet(() @trusted { return SSL_read(m_tls, dst.ptr, readlen); } (), "Reading from TLS stream"); 466 //logTrace("SSL read %d/%d", ret, dst.length); 467 dst = dst[ret .. $]; 468 nbytes += ret; 469 470 if (mode == IOMode.immediate || mode == IOMode.once) 471 break; 472 } 473 474 return nbytes; 475 } 476 477 alias read = Stream.read; 478 479 size_t write(in ubyte[] bytes_, IOMode mode) 480 { 481 const(ubyte)[] bytes = bytes_; 482 483 size_t nbytes = 0; 484 485 while (bytes.length > 0) { 486 int writelen = min(bytes.length, int.max); 487 auto ret = checkSSLRet(() @trusted { return SSL_write(m_tls, bytes.ptr, writelen); } (), "Writing to TLS stream"); 488 //logTrace("SSL write %s", cast(string)bytes[0 .. ret]); 489 bytes = bytes[ret .. $]; 490 nbytes += ret; 491 492 if (mode == IOMode.immediate || mode == IOMode.once) 493 break; 494 } 495 496 return nbytes; 497 } 498 499 alias write = Stream.write; 500 501 void flush() 502 { 503 m_stream.flush(); 504 } 505 506 void finalize() 507 { 508 if( !m_tls ) return; 509 logTrace("OpenSSLStream finalize"); 510 511 () @trusted { 512 auto ret = SSL_shutdown(m_tls); 513 if (ret != 0) checkSSLRet(ret, "SSL_shutdown"); 514 SSL_free(m_tls); 515 ERR_clear_error(); 516 } (); 517 518 m_tls = null; 519 m_stream = InterfaceProxy!Stream.init; 520 } 521 522 private void validateSSLErrors() 523 @safe { 524 auto err = () @trusted { return ERR_get_error(); } (); 525 if (err != SSL_ERROR_NONE) { 526 throw new Exception("OpenSSL error occured previously: " ~ processSSLError(err)); 527 } 528 } 529 530 private int checkSSLRet(int ret, string what) 531 @safe { 532 if (ret > 0) return ret; 533 534 auto err = () @trusted { return SSL_get_error(m_tls, ret); } (); 535 string desc = processSSLError(err, what); 536 537 enforce(ret != 0, format("%s was unsuccessful with ret 0", what)); 538 enforce(ret >= 0, format("%s returned an error: %s", what, desc)); 539 return ret; 540 } 541 542 private string processSSLError(c_ulong err, string what = "OpenSSL") 543 @safe { 544 string desc; 545 switch (err) { 546 default: desc = format("Unknown error (%s)", err); break; 547 case SSL_ERROR_NONE: desc = "No error"; break; 548 case SSL_ERROR_ZERO_RETURN: desc = "SSL/TLS tunnel closed"; break; 549 case SSL_ERROR_WANT_READ: desc = "Need to block for read"; break; 550 case SSL_ERROR_WANT_WRITE: desc = "Need to block for write"; break; 551 case SSL_ERROR_WANT_CONNECT: desc = "Need to block for connect"; break; 552 case SSL_ERROR_WANT_ACCEPT: desc = "Need to block for accept"; break; 553 case SSL_ERROR_WANT_X509_LOOKUP: desc = "Need to block for certificate lookup"; break; 554 case SSL_ERROR_SYSCALL: 555 version (linux) { 556 import core.sys.linux.errno : errno; 557 import core.stdc.string : strerror; 558 559 desc = format("non-recoverable socket I/O error: %s (%s)", errno, (() @trusted => strerror(errno).to!string)()); 560 } else { 561 desc = "non-recoverable socket I/O error"; 562 } 563 break; 564 case SSL_ERROR_SSL: 565 throwSSL(what); 566 assert(false); 567 } 568 569 const(char)* file = null, data = null; 570 int line; 571 int flags; 572 c_ulong eret; 573 char[120] ebuf; 574 while( (eret = () @trusted { return ERR_get_error_line_data(&file, &line, &data, &flags); } ()) != 0 ){ 575 () @trusted { ERR_error_string(eret, ebuf.ptr); } (); 576 logDebug("%s error at %s:%d: %s (%s)", what, 577 () @trusted { return to!string(file); } (), line, 578 () @trusted { return to!string(ebuf.ptr); } (), 579 flags & ERR_TXT_STRING ? () @trusted { return to!string(data); } () : "-"); 580 } 581 582 return desc; 583 } 584 585 @property TLSCertificateInformation peerCertificate() 586 { 587 return m_peerCertificateInfo; 588 } 589 590 @property X509* peerCertificateX509() 591 { 592 return m_peerCertificate; 593 } 594 595 @property string alpn() 596 const { 597 static if (!haveALPN) assert(false, "OpenSSL support not compiled with ALPN enabled. Use VibeForceALPN."); 598 else { 599 // modified since C functions expects a NULL pointer 600 const(ubyte)* data = null; 601 uint datalen; 602 string ret; 603 604 () @trusted { 605 SSL_get0_alpn_selected(m_tls, &data, &datalen); 606 ret = cast(string)data[0 .. datalen].idup; 607 } (); 608 logDebug("alpn selected: ", ret); 609 return ret; 610 } 611 } 612 613 /// Invoked by client to offer alpn 614 private void setClientALPN(string[] alpn_list) 615 { 616 logDebug("SetClientALPN: ", alpn_list); 617 import vibe.internal.allocator : dispose, makeArray, vibeThreadAllocator; 618 ubyte[] alpn; 619 size_t len; 620 foreach (string alpn_val; alpn_list) 621 len += alpn_val.length + 1; 622 alpn = () @trusted { return vibeThreadAllocator.makeArray!ubyte(len); } (); 623 624 size_t i; 625 foreach (string alpn_val; alpn_list) 626 { 627 alpn[i++] = cast(ubyte)alpn_val.length; 628 alpn[i .. i+alpn_val.length] = cast(immutable(ubyte)[])alpn_val; 629 i += alpn_val.length; 630 } 631 assert(i == len); 632 633 634 () @trusted { 635 static if (haveALPN) 636 SSL_set_alpn_protos(m_tls, cast(const char*) alpn.ptr, cast(uint) len); 637 vibeThreadAllocator.dispose(alpn); 638 } (); 639 } 640 } 641 642 private int enforceSSL(int ret, string message) 643 @safe { 644 if (ret > 0) return ret; 645 throwSSL(message); 646 assert(false); 647 } 648 649 private void throwSSL(string message) 650 @safe { 651 c_ulong eret; 652 const(char)* file = null, data = null; 653 int line; 654 int flags; 655 string estr; 656 char[120] ebuf = 0; 657 658 while ((eret = () @trusted { return ERR_get_error_line_data(&file, &line, &data, &flags); } ()) != 0) { 659 () @trusted { ERR_error_string_n(eret, ebuf.ptr, ebuf.length); } (); 660 estr = () @trusted { return ebuf.ptr.to!string; } (); 661 // throw the last error code as an exception 662 logDebug("OpenSSL error at %s:%d: %s (%s)", 663 () @trusted { return file.to!string; } (), line, estr, 664 flags & ERR_TXT_STRING ? () @trusted { return to!string(data); } () : "-"); 665 if (!() @trusted { return ERR_peek_error(); } ()) break; 666 } 667 668 throw new Exception(format("%s: %s (%s)", message, estr, eret)); 669 } 670 671 672 /** 673 Encapsulates the configuration for an SSL tunnel. 674 675 Note that when creating an SSLContext with SSLContextKind.client, the 676 peerValidationMode will be set to SSLPeerValidationMode.trustedCert, 677 but no trusted certificate authorities are added by default. Use 678 useTrustedCertificateFile to add those. 679 */ 680 final class OpenSSLContext : TLSContext { 681 @safe: 682 683 private { 684 TLSContextKind m_kind; 685 TLSVersion m_version; 686 ssl_ctx_st* m_ctx; 687 TLSPeerValidationCallback m_peerValidationCallback; 688 TLSPeerValidationMode m_validationMode; 689 int m_verifyDepth; 690 TLSServerNameCallback m_sniCallback; 691 TLSALPNCallback m_alpnCallback; 692 } 693 694 695 this(TLSContextKind kind, TLSVersion ver = TLSVersion.any) 696 { 697 m_kind = kind; 698 m_version = ver; 699 700 const(SSL_METHOD)* method; 701 c_ulong veroptions = SSL_OP_NO_SSLv2; 702 c_ulong options = SSL_OP_NO_COMPRESSION; 703 static if (OPENSSL_VERSION_BEFORE(1, 1, 0)) 704 options |= SSL_OP_SINGLE_DH_USE|SSL_OP_SINGLE_ECDH_USE; // There are always enabled in OpenSSL 1.1.0. 705 int minver = TLS1_VERSION; 706 int maxver = TLS1_2_VERSION; 707 708 () @trusted { 709 final switch (kind) { 710 case TLSContextKind.client: 711 final switch (ver) { 712 case TLSVersion.any: method = SSLv23_client_method(); veroptions |= SSL_OP_NO_SSLv3; break; 713 case TLSVersion.ssl3: throw new Exception("SSLv3 is not supported anymore"); 714 case TLSVersion.tls1: method = SSLv23_client_method(); veroptions |= SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2; maxver = TLS1_VERSION; break; 715 case TLSVersion.tls1_1: method = SSLv23_client_method(); veroptions |= SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_2; minver = TLS1_1_VERSION; maxver = TLS1_1_VERSION; break; 716 case TLSVersion.tls1_2: method = SSLv23_client_method(); veroptions |= SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1; minver = TLS1_2_VERSION; break; 717 case TLSVersion.dtls1: method = DTLSv1_client_method(); minver = DTLS1_VERSION; maxver = DTLS1_VERSION; break; 718 } 719 break; 720 case TLSContextKind.server: 721 case TLSContextKind.serverSNI: 722 final switch (ver) { 723 case TLSVersion.any: method = SSLv23_server_method(); veroptions |= SSL_OP_NO_SSLv3; break; 724 case TLSVersion.ssl3: throw new Exception("SSLv3 is not supported anymore"); 725 case TLSVersion.tls1: method = SSLv23_server_method(); veroptions |= SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2; maxver = TLS1_VERSION; break; 726 case TLSVersion.tls1_1: method = SSLv23_server_method(); veroptions |= SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_2; minver = TLS1_1_VERSION; maxver = TLS1_1_VERSION; break; 727 case TLSVersion.tls1_2: method = SSLv23_server_method(); veroptions |= SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1; minver = TLS1_2_VERSION; break; 728 case TLSVersion.dtls1: method = DTLSv1_server_method(); minver = DTLS1_VERSION; maxver = DTLS1_VERSION; break; 729 } 730 options |= SSL_OP_CIPHER_SERVER_PREFERENCE; 731 break; 732 } 733 } (); 734 735 m_ctx = () @trusted { return SSL_CTX_new(method); } (); 736 if (!m_ctx) { 737 enforceSSL(0, "Failed to create SSL context"); 738 assert(false); 739 } 740 741 static if (OPENSSL_VERSION_AT_LEAST(1, 1, 0)) { 742 () @trusted { return SSL_CTX_set_min_proto_version(m_ctx, minver); }() 743 .enforceSSL("Failed setting minimum protocol version"); 744 () @trusted { return SSL_CTX_set_max_proto_version(m_ctx, maxver); }() 745 .enforceSSL("Failed setting maximum protocol version"); 746 } else { 747 options |= veroptions; 748 } 749 750 auto newopts = () @trusted { return SSL_CTX_set_options(m_ctx, options); }(); 751 if ((newopts & options) != options) 752 logDiagnostic("Not all SSL options applied: passed 0x%08x vs applied 0x%08x", options, newopts); 753 754 if (kind == TLSContextKind.server) { 755 setDHParams(); 756 setECDHCurve(); 757 guessSessionIDContext(); 758 } 759 760 setCipherList(); 761 762 maxCertChainLength = 9; 763 if (kind == TLSContextKind.client) peerValidationMode = TLSPeerValidationMode.trustedCert; 764 else peerValidationMode = TLSPeerValidationMode.none; 765 766 // while it would be nice to use the system's certificate store, this 767 // seems to be difficult to get right across all systems. The most 768 // popular alternative is to use Mozilla's certificate store and 769 // distribute it along with the library (e.g. in source code form. 770 771 /*version (Posix) { 772 enforce(SSL_CTX_load_verify_locations(m_ctx, null, "/etc/ssl/certs"), 773 "Failed to load system certificate store."); 774 } 775 776 version (Windows) { 777 auto store = CertOpenSystemStore(null, "ROOT"); 778 enforce(store !is null, "Failed to load system certificate store."); 779 scope (exit) CertCloseStore(store, 0); 780 781 PCCERT_CONTEXT ctx; 782 while((ctx = CertEnumCertificatesInStore(store, ctx)) !is null) { 783 X509* x509cert; 784 auto buffer = ctx.pbCertEncoded; 785 auto len = ctx.cbCertEncoded; 786 if (ctx.dwCertEncodingType & X509_ASN_ENCODING) { 787 x509cert = d2i_X509(null, &buffer, len); 788 X509_STORE_add_cert(SSL_CTX_get_cert_store(m_ctx), x509cert); 789 } 790 } 791 }*/ 792 } 793 794 ~this() 795 { 796 () @trusted { SSL_CTX_free(m_ctx); } (); 797 m_ctx = null; 798 } 799 800 801 /// The kind of SSL context (client/server) 802 @property TLSContextKind kind() const { return m_kind; } 803 804 /// Callback function invoked by server to choose alpn 805 @property void alpnCallback(TLSALPNCallback alpn_chooser) 806 { 807 logDebug("Choosing ALPN callback"); 808 m_alpnCallback = alpn_chooser; 809 static if (haveALPN) { 810 logDebug("Call select cb"); 811 () @trusted { 812 SSL_CTX_set_alpn_select_cb(m_ctx, &chooser, cast(void*)this); 813 } (); 814 } 815 } 816 817 /// Get the current ALPN callback function 818 @property TLSALPNCallback alpnCallback() const { return m_alpnCallback; } 819 820 /// Invoked by client to offer alpn 821 void setClientALPN(string[] alpn_list) 822 { 823 static if (!haveALPN) assert(false, "OpenSSL support not compiled with ALPN enabled. Use VibeForceALPN."); 824 else { 825 import vibe.internal.memory_legacy : allocArray, freeArray, manualAllocator; 826 ubyte[] alpn; 827 size_t len; 828 foreach (string alpn_value; alpn_list) 829 len += alpn_value.length + 1; 830 () @trusted { 831 alpn = allocArray!ubyte(manualAllocator(), len); 832 } (); 833 834 size_t i; 835 foreach (string alpn_value; alpn_list) 836 { 837 () @trusted { 838 alpn[i++] = cast(ubyte)alpn_value.length; 839 alpn[i .. i+alpn_value.length] = cast(ubyte[])alpn_value; 840 } (); 841 842 i += alpn_value.length; 843 } 844 assert(i == len); 845 846 () @trusted { 847 SSL_CTX_set_alpn_protos(m_ctx, cast(const char*) alpn.ptr, cast(uint) len); 848 freeArray(manualAllocator(), alpn); 849 } (); 850 851 } 852 } 853 854 /** Specifies the validation level of remote peers. 855 856 The default mode for TLSContextKind.client is 857 TLSPeerValidationMode.trustedCert and the default for 858 TLSContextKind.server is TLSPeerValidationMode.none. 859 */ 860 @property void peerValidationMode(TLSPeerValidationMode mode) 861 { 862 m_validationMode = mode; 863 864 int sslmode; 865 866 with (TLSPeerValidationMode) { 867 if (mode == none) sslmode = SSL_VERIFY_NONE; 868 else { 869 sslmode |= SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; 870 if (mode & requireCert) sslmode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; 871 } 872 } 873 874 () @trusted { SSL_CTX_set_verify(m_ctx, sslmode, &verify_callback); } (); 875 } 876 /// ditto 877 @property TLSPeerValidationMode peerValidationMode() const { return m_validationMode; } 878 879 880 /** The maximum length of an accepted certificate chain. 881 882 Any certificate chain longer than this will result in the SSL/TLS 883 negitiation failing. 884 885 The default value is 9. 886 */ 887 @property void maxCertChainLength(int val) 888 { 889 m_verifyDepth = val; 890 // + 1 to let the validation callback handle the error 891 () @trusted { SSL_CTX_set_verify_depth(m_ctx, val + 1); } (); 892 } 893 894 /// ditto 895 @property int maxCertChainLength() const { return m_verifyDepth; } 896 897 /** An optional user callback for peer validation. 898 899 This callback will be called for each peer and each certificate of 900 its certificate chain to allow overriding the validation decision 901 based on the selected peerValidationMode (e.g. to allow invalid 902 certificates or to reject valid ones). This is mainly useful for 903 presenting the user with a dialog in case of untrusted or mismatching 904 certificates. 905 */ 906 @property void peerValidationCallback(TLSPeerValidationCallback callback) { m_peerValidationCallback = callback; } 907 /// ditto 908 @property inout(TLSPeerValidationCallback) peerValidationCallback() inout { return m_peerValidationCallback; } 909 910 @property void sniCallback(TLSServerNameCallback callback) 911 { 912 m_sniCallback = callback; 913 if (m_kind == TLSContextKind.serverSNI) { 914 () @trusted { 915 SSL_CTX_callback_ctrl(m_ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, cast(OSSLCallback)&onContextForServerName); 916 SSL_CTX_ctrl(m_ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG, 0, cast(void*)this); 917 } (); 918 } 919 } 920 @property inout(TLSServerNameCallback) sniCallback() inout { return m_sniCallback; } 921 922 private extern(C) alias OSSLCallback = void function(); 923 private static extern(C) int onContextForServerName(SSL *s, int *ad, void *arg) 924 { 925 auto ctx = () @trusted { return cast(OpenSSLContext)arg; } (); 926 auto servername = () @trusted { return SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); } (); 927 if (!servername) return SSL_TLSEXT_ERR_NOACK; 928 auto newctx = cast(OpenSSLContext)ctx.m_sniCallback(() @trusted { return servername.to!string; } ()); 929 if (!newctx) return SSL_TLSEXT_ERR_NOACK; 930 () @trusted { SSL_set_SSL_CTX(s, newctx.m_ctx); } (); 931 return SSL_TLSEXT_ERR_OK; 932 } 933 934 OpenSSLStream createStream(InterfaceProxy!Stream underlying, TLSStreamState state, string peer_name = null, NetworkAddress peer_address = NetworkAddress.init) 935 { 936 return new OpenSSLStream(underlying, this, state, peer_name, peer_address); 937 } 938 939 /** Set the list of cipher specifications to use for SSL/TLS tunnels. 940 941 The list must be a colon separated list of cipher 942 specifications as accepted by OpenSSL. Calling this function 943 without argument will restore the default. 944 945 The default is derived from $(LINK https://wiki.mozilla.org/Security/Server_Side_TLS), 946 using the "intermediate" list for TLSv1.2+ server contexts or using the 947 "old compatibility" list otherwise. 948 949 See_also: $(LINK https://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT) 950 */ 951 void setCipherList(string list = null) 952 @trusted 953 { 954 if (list is null) { 955 if (m_kind == TLSContextKind.server && m_version == TLSVersion.tls1_2) { 956 list = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:" 957 ~ "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:" 958 ~ "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:" 959 ~ "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:" 960 ~ "DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"; 961 } else { 962 list = 963 "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:" 964 ~ "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:" 965 ~ "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:" 966 ~ "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:" 967 ~ "DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:" 968 ~ "ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:" 969 ~ "ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:" 970 ~ "ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:" 971 ~ "DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:" 972 ~ "AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA"; 973 } 974 } 975 976 SSL_CTX_set_cipher_list(m_ctx, toStringz(list)) 977 .enforceSSL("Setting cipher list"); 978 } 979 980 /** Make up a context ID to assign to the SSL context. 981 982 This is required when doing client cert authentication, otherwise many 983 connections will go aborted as the client tries to revive a session 984 that it used to have on another machine. 985 986 The session ID context should be unique within a pool of servers. 987 Currently, this is achieved by taking the hostname. 988 */ 989 private void guessSessionIDContext() 990 @trusted 991 { 992 string contextID = Socket.hostName; 993 SSL_CTX_set_session_id_context(m_ctx, cast(ubyte*)contextID.toStringz(), cast(uint)contextID.length); 994 } 995 996 /** Set params to use for DH cipher. 997 * 998 * By default the 2048-bit prime from RFC 3526 is used. 999 * 1000 * Params: 1001 * pem_file = Path to a PEM file containing the DH parameters. Calling 1002 * this function without argument will restore the default. 1003 */ 1004 void setDHParams(string pem_file=null) 1005 @trusted { 1006 DH* dh; 1007 scope(exit) DH_free(dh); 1008 1009 if (pem_file is null) { 1010 dh = enforce(DH_new(), "Unable to create DH structure."); 1011 dh.p = get_rfc3526_prime_2048(null); 1012 ubyte dh_generator = 2; 1013 dh.g = BN_bin2bn(&dh_generator, dh_generator.sizeof, null); 1014 } else { 1015 import core.stdc.stdio : fclose, fopen; 1016 1017 auto f = enforce(fopen(toStringz(pem_file), "r"), "Failed to load dhparams file "~pem_file); 1018 scope(exit) fclose(f); 1019 dh = enforce(PEM_read_DHparams(f, null, null, null), "Failed to read dhparams file "~pem_file); 1020 } 1021 1022 SSL_CTX_set_tmp_dh(m_ctx, dh); 1023 } 1024 1025 /** Set the elliptic curve to use for ECDH cipher. 1026 * 1027 * By default a curve is either chosen automatically or prime256v1 is used. 1028 * 1029 * Params: 1030 * curve = The short name of the elliptic curve to use. Calling this 1031 * function without argument will restore the default. 1032 * 1033 */ 1034 void setECDHCurve(string curve = null) 1035 @trusted { 1036 // `SSL_CTX_set_ecdh_auto` are no longer available in v1.1.0, 1037 // as it is always enabled by default. 1038 // https://github.com/openssl/openssl/issues/1437 1039 // https://github.com/openssl/openssl/commit/2ecb9f2d18614fb7b7b42830a358b7163ed43221 1040 static if (OPENSSL_VERSION_NUMBER >= 0x10200000 && OPENSSL_VERSION_NUMBER < OPENSSL_MAKE_VERSION(1, 1, 0, 0)) { 1041 // use automatic ecdh curve selection by default 1042 if (curve is null) { 1043 SSL_CTX_set_ecdh_auto(m_ctx, true); 1044 return; 1045 } 1046 // but disable it when an explicit curve is given 1047 SSL_CTX_set_ecdh_auto(m_ctx, false); 1048 } 1049 1050 int nid; 1051 if (curve is null) 1052 nid = NID_X9_62_prime256v1; 1053 else 1054 nid = enforce(OBJ_sn2nid(toStringz(curve)), "Unknown ECDH curve '"~curve~"'."); 1055 1056 auto ecdh = enforce(EC_KEY_new_by_curve_name(nid), "Unable to create ECDH curve."); 1057 SSL_CTX_set_tmp_ecdh(m_ctx, ecdh); 1058 EC_KEY_free(ecdh); 1059 } 1060 1061 /// Sets a certificate file to use for authenticating to the remote peer 1062 void useCertificateChainFile(string path) 1063 { 1064 enforce(() @trusted { return SSL_CTX_use_certificate_chain_file(m_ctx, toStringz(path)); } (), "Failed to load certificate file " ~ path); 1065 } 1066 1067 /// Sets the private key to use for authenticating to the remote peer based 1068 /// on the configured certificate chain file. 1069 void usePrivateKeyFile(string path) 1070 { 1071 enforce(() @trusted { return SSL_CTX_use_PrivateKey_file(m_ctx, toStringz(path), SSL_FILETYPE_PEM); } (), "Failed to load private key file " ~ path); 1072 } 1073 1074 /** Sets the list of trusted certificates for verifying peer certificates. 1075 1076 If this is a server context, this also entails that the given 1077 certificates are advertised to connecting clients during handshake. 1078 1079 On Linux, the system's root certificate authority list is usually 1080 found at "/etc/ssl/certs/ca-certificates.crt", 1081 "/etc/pki/tls/certs/ca-bundle.crt", or "/etc/ssl/ca-bundle.pem". 1082 */ 1083 void useTrustedCertificateFile(string path) 1084 @trusted { 1085 immutable cPath = toStringz(path); 1086 enforce(SSL_CTX_load_verify_locations(m_ctx, cPath, null), 1087 "Failed to load trusted certificate file " ~ path); 1088 1089 if (m_kind == TLSContextKind.server) { 1090 auto certNames = enforce(SSL_load_client_CA_file(cPath), 1091 "Failed to load client CA name list from file " ~ path); 1092 SSL_CTX_set_client_CA_list(m_ctx, certNames); 1093 } 1094 } 1095 1096 private SSLState createClientCtx() 1097 { 1098 SSLState ret = () @trusted { return SSL_new(m_ctx); } (); 1099 if (!ret) { 1100 enforceSSL(0, "Failed to create SSL context"); 1101 assert(false); 1102 } 1103 return ret; 1104 } 1105 1106 private static struct VerifyData { 1107 int verifyDepth; 1108 TLSPeerValidationMode validationMode; 1109 TLSPeerValidationCallback callback; 1110 string peerName; 1111 NetworkAddress peerAddress; 1112 } 1113 1114 private static extern(C) nothrow 1115 int verify_callback(int valid, X509_STORE_CTX* ctx) 1116 @trusted { 1117 X509* err_cert = X509_STORE_CTX_get_current_cert(ctx); 1118 int err = X509_STORE_CTX_get_error(ctx); 1119 int depth = X509_STORE_CTX_get_error_depth(ctx); 1120 1121 SSL* ssl = cast(SSL*)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); 1122 VerifyData* vdata = cast(VerifyData*)SSL_get_ex_data(ssl, gs_verifyDataIndex); 1123 1124 char[1024] buf; 1125 X509_NAME_oneline(X509_get_subject_name(err_cert), buf.ptr, 256); 1126 buf[$-1] = 0; 1127 1128 try { 1129 logDebug("validate callback for %s", buf.ptr.to!string); 1130 1131 if (depth > vdata.verifyDepth) { 1132 logDiagnostic("SSL cert chain too long: %s vs. %s", depth, vdata.verifyDepth); 1133 valid = false; 1134 err = X509_V_ERR_CERT_CHAIN_TOO_LONG; 1135 } 1136 1137 if (err != X509_V_OK) 1138 logDebug("SSL cert initial error: %s", X509_verify_cert_error_string(err).to!string); 1139 1140 if (!valid) { 1141 switch (err) { 1142 default: break; 1143 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: 1144 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: 1145 case X509_V_ERR_CERT_UNTRUSTED: 1146 assert(err_cert !is null); 1147 X509_NAME_oneline(X509_get_issuer_name(err_cert), buf.ptr, buf.length); 1148 buf[$-1] = 0; 1149 logDebug("SSL cert not trusted or unknown issuer: %s", buf.ptr.to!string); 1150 if (!(vdata.validationMode & TLSPeerValidationMode.checkTrust)) { 1151 valid = true; 1152 err = X509_V_OK; 1153 } 1154 break; 1155 } 1156 } 1157 1158 if (!(vdata.validationMode & TLSPeerValidationMode.checkCert)) { 1159 valid = true; 1160 err = X509_V_OK; 1161 } 1162 1163 if (vdata.callback) { 1164 TLSPeerValidationData pvdata; 1165 // ... 1166 if (!valid) { 1167 if (vdata.callback(pvdata)) { 1168 valid = true; 1169 err = X509_V_OK; 1170 } 1171 } else { 1172 if (!vdata.callback(pvdata)) { 1173 logDebug("SSL application verification failed"); 1174 valid = false; 1175 err = X509_V_ERR_APPLICATION_VERIFICATION; 1176 } 1177 } 1178 } 1179 } catch (Exception e) { 1180 logWarn("SSL verification failed due to exception: %s", e.msg); 1181 err = X509_V_ERR_APPLICATION_VERIFICATION; 1182 valid = false; 1183 } 1184 1185 X509_STORE_CTX_set_error(ctx, err); 1186 1187 logDebug("SSL validation result: %s (%s)", valid, err); 1188 1189 return valid; 1190 } 1191 } 1192 1193 alias SSLState = ssl_st*; 1194 1195 /**************************************************************************************************/ 1196 /* Private functions */ 1197 /**************************************************************************************************/ 1198 1199 private { 1200 __gshared int gs_verifyDataIndex; 1201 } 1202 1203 shared static this() 1204 { 1205 static if (OPENSSL_VERSION_BEFORE(1, 1, 0)) 1206 { 1207 logDebug("Initializing OpenSSL..."); 1208 // Not required as of OpenSSL 1.1.0, see: 1209 // https://wiki.openssl.org/index.php/Library_Initialization 1210 SSL_load_error_strings(); 1211 SSL_library_init(); 1212 1213 g_cryptoMutexes.length = CRYPTO_num_locks(); 1214 // TODO: investigate if a normal Mutex is enough - not sure if BIO is called in a locked state 1215 foreach (i; 0 .. g_cryptoMutexes.length) 1216 g_cryptoMutexes[i] = new InterruptibleTaskMutex; 1217 foreach (ref m; g_cryptoMutexes) { 1218 assert(m !is null); 1219 } 1220 1221 // Those two were removed in v1.1.0, see: 1222 // https://github.com/openssl/openssl/issues/1260 1223 CRYPTO_set_id_callback(&onCryptoGetThreadID); 1224 CRYPTO_set_locking_callback(&onCryptoLock); 1225 logDebug("... done."); 1226 } 1227 1228 enforce(RAND_poll(), "Fatal: failed to initialize random number generator entropy (RAND_poll)."); 1229 gs_verifyDataIndex = SSL_get_ex_new_index(0, cast(void*)"VerifyData".ptr, null, null, null); 1230 } 1231 1232 private bool verifyCertName(X509* cert, int field, in char[] value, bool allow_wildcards = true) 1233 @trusted { 1234 bool delegate(in char[]) @safe str_match; 1235 1236 bool check_value(ASN1_STRING* str, int type) { 1237 if (!str.data || !str.length) return false; 1238 1239 if (type > 0) { 1240 if (type != str.type) return 0; 1241 auto strstr = cast(string)str.data[0 .. str.length]; 1242 return type == V_ASN1_IA5STRING ? str_match(strstr) : strstr == value; 1243 } 1244 1245 char* utfstr; 1246 auto utflen = ASN1_STRING_to_UTF8(&utfstr, str); 1247 enforce (utflen >= 0, "Error converting ASN1 string to UTF-8."); 1248 scope (exit) OPENSSL_free(utfstr); 1249 return str_match(utfstr[0 .. utflen]); 1250 } 1251 1252 int cnid; 1253 int alt_type; 1254 final switch (field) { 1255 case GEN_DNS: 1256 cnid = NID_commonName; 1257 alt_type = V_ASN1_IA5STRING; 1258 str_match = allow_wildcards ? (in s) => matchWildcard(value, s) : (in s) => s.icmp(value) == 0; 1259 break; 1260 case GEN_IPADD: 1261 cnid = 0; 1262 alt_type = V_ASN1_OCTET_STRING; 1263 str_match = (in s) => s == value; 1264 break; 1265 } 1266 1267 if (auto gens = cast(STACK_OF!GENERAL_NAME*)X509_get_ext_d2i(cert, NID_subject_alt_name, null, null)) { 1268 // Somehow Deimos' bindings don't allow us to call `sk_GENERAL_NAMES_free`, 1269 // as it takes a `stack_st_GENERAL_NAME*` which in C is just what 1270 // `STACK_OF(GENERAL_NAME)` aliases to, but not in D (STACK_OF is a template). 1271 // Since under the hood all stack APIs are untyped, just use `OPENSSL_sk_free` 1272 // directly, see: https://man.openbsd.org/OPENSSL_sk_new.3 1273 scope(exit) OPENSSL_sk_free(cast(_STACK*) gens); 1274 1275 foreach (i; 0 .. sk_GENERAL_NAME_num(gens)) { 1276 auto gen = sk_GENERAL_NAME_value(gens, i); 1277 if (gen.type != field) continue; 1278 ASN1_STRING *cstr = field == GEN_DNS ? gen.d.dNSName : gen.d.iPAddress; 1279 if (check_value(cstr, alt_type)) return true; 1280 } 1281 if (!cnid) return false; 1282 } 1283 1284 X509_NAME* name = X509_get_subject_name(cert); 1285 int i = -1; 1286 while ((i = X509_NAME_get_index_by_NID(name, cnid, i)) >= 0) { 1287 X509_NAME_ENTRY* ne = X509_NAME_get_entry(name, i); 1288 ASN1_STRING* str = X509_NAME_ENTRY_get_data(ne); 1289 if (check_value(str, -1)) return true; 1290 } 1291 1292 return false; 1293 } 1294 1295 private bool matchWildcard(const(char)[] str, const(char)[] pattern) 1296 @safe { 1297 auto strparts = str.split("."); 1298 auto patternparts = pattern.split("."); 1299 if (strparts.length != patternparts.length) return false; 1300 1301 bool isValidChar(dchar ch) { 1302 if (ch >= '0' && ch <= '9') return true; 1303 if (ch >= 'a' && ch <= 'z') return true; 1304 if (ch >= 'A' && ch <= 'Z') return true; 1305 if (ch == '-' || ch == '.') return true; 1306 return false; 1307 } 1308 1309 if (!pattern.all!(c => isValidChar(c) || c == '*') || !str.all!(c => isValidChar(c))) 1310 return false; 1311 1312 foreach (i; 0 .. strparts.length) { 1313 import std.regex; 1314 auto p = patternparts[i]; 1315 auto s = strparts[i]; 1316 if (!p.length || !s.length) return false; 1317 auto rex = "^" ~ std.array.replace(p, "*", "[^.]*") ~ "$"; 1318 if (!match(s, rex)) return false; 1319 } 1320 return true; 1321 } 1322 1323 unittest { 1324 assert(matchWildcard("www.example.org", "*.example.org")); 1325 assert(matchWildcard("www.example.org", "*w.example.org")); 1326 assert(matchWildcard("www.example.org", "w*w.example.org")); 1327 assert(matchWildcard("www.example.org", "*w*.example.org")); 1328 assert(matchWildcard("test.abc.example.org", "test.*.example.org")); 1329 assert(!matchWildcard("test.abc.example.org", "abc.example.org")); 1330 assert(!matchWildcard("test.abc.example.org", ".abc.example.org")); 1331 assert(!matchWildcard("abc.example.org", "a.example.org")); 1332 assert(!matchWildcard("abc.example.org", "bc.example.org")); 1333 assert(!matchWildcard("abcdexample.org", "abc.example.org")); 1334 } 1335 1336 1337 private nothrow @safe extern(C) 1338 { 1339 import core.stdc.config; 1340 1341 1342 int chooser(SSL* ssl, const(char)** output, ubyte* outlen, const(char) *input_, uint inlen, void* arg) { 1343 const(char)[] input = () @trusted { return input_[0 .. inlen]; } (); 1344 1345 OpenSSLContext ctx = () @trusted { return cast(OpenSSLContext) arg; } (); 1346 import vibe.utils.array : AllocAppender, AppenderResetMode; 1347 size_t i; 1348 size_t len; 1349 Appender!(string[]) alpn_list; 1350 while (i < inlen) 1351 { 1352 len = cast(size_t) input[i]; 1353 ++i; 1354 auto proto = input[i .. i+len]; 1355 i += len; 1356 () @trusted { alpn_list ~= cast(string)proto; } (); 1357 } 1358 1359 string alpn; 1360 1361 try { alpn = ctx.m_alpnCallback(alpn_list.data); } catch (Exception e) { } 1362 if (alpn) { 1363 i = 0; 1364 while (i < inlen) 1365 { 1366 len = input[i]; 1367 ++i; 1368 auto proto = input[i .. i+len]; 1369 i += len; 1370 if (proto == alpn) { 1371 *output = &proto[0]; 1372 *outlen = cast(ubyte) proto.length; 1373 } 1374 } 1375 } 1376 1377 if (!output) { 1378 logError("None of the proposed ALPN were selected: %s / falling back on HTTP/1.1", input); 1379 enum hdr = "http/1.1"; 1380 *output = &hdr[0]; 1381 *outlen = cast(ubyte)hdr.length; 1382 } 1383 1384 return 0; 1385 } 1386 1387 int onBioNew(BIO *b) nothrow 1388 { 1389 BIO_set_init(b, 0); 1390 //b.num = -1; 1391 BIO_set_data(b, null); 1392 BIO_clear_flags(b, ~0); 1393 return 1; 1394 } 1395 1396 int onBioFree(BIO *b) 1397 { 1398 if( !b ) return 0; 1399 if(BIO_get_shutdown(b)){ 1400 //if( b.init && b.ptr ) b.ptr.stream.free(); 1401 BIO_set_init(b, 0); 1402 BIO_clear_flags(b, ~0); 1403 BIO_set_data(b, null); 1404 } 1405 return 1; 1406 } 1407 1408 int onBioRead(BIO *b, const(char)* outb, int outlen) 1409 { 1410 auto stream = () @trusted { return cast(OpenSSLStream)BIO_get_data(b); } (); 1411 1412 try { 1413 outlen = min(outlen, stream.m_stream.leastSize); 1414 stream.m_stream.read(() @trusted { return cast(ubyte[])outb[0 .. outlen]; } ()); 1415 } catch (Exception e) { 1416 setSSLError("Error reading from underlying stream", e.msg); 1417 return -1; 1418 } 1419 return outlen; 1420 } 1421 1422 int onBioWrite(BIO *b, const(char) *inb, int inlen) 1423 { 1424 auto stream = () @trusted { return cast(OpenSSLStream)BIO_get_data(b); } (); 1425 try { 1426 stream.m_stream.write(() @trusted { return inb[0 .. inlen]; } ()); 1427 } catch (Exception e) { 1428 setSSLError("Error writing to underlying stream", e.msg); 1429 return -1; 1430 } 1431 return inlen; 1432 } 1433 1434 c_long onBioCtrl(BIO *b, int cmd, c_long num, void *ptr) 1435 { 1436 auto stream = () @trusted { return cast(OpenSSLStream)BIO_get_data(b); } (); 1437 c_long ret = 1; 1438 1439 switch(cmd){ 1440 case BIO_CTRL_GET_CLOSE: ret = BIO_get_shutdown(b); break; 1441 case BIO_CTRL_SET_CLOSE: 1442 logTrace("SSL set close %d", num); 1443 BIO_set_shutdown(b, cast(int)num); 1444 break; 1445 case BIO_CTRL_PENDING: 1446 try { 1447 auto sz = stream.m_stream.leastSize; // FIXME: .peek.length should be sufficient here 1448 return sz <= c_long.max ? cast(c_long)sz : c_long.max; 1449 } catch( Exception e ){ 1450 setSSLError("Error reading from underlying stream", e.msg); 1451 return -1; 1452 } 1453 case BIO_CTRL_WPENDING: return 0; 1454 case BIO_CTRL_DUP: 1455 case BIO_CTRL_FLUSH: 1456 ret = 1; 1457 break; 1458 default: 1459 ret = 0; 1460 break; 1461 } 1462 return ret; 1463 } 1464 1465 int onBioPuts(BIO *b, const(char) *s) 1466 { 1467 return onBioWrite(b, s, cast(int)() @trusted { return strlen(s); } ()); 1468 } 1469 } 1470 1471 private void setSSLError(string msg, string submsg, int line = __LINE__, string file = __FILE__) 1472 @trusted nothrow { 1473 import std.string : toStringz; 1474 static if (is(typeof(ERR_new))) { 1475 ERR_new(); 1476 ERR_set_debug(file.toStringz, line, ""); 1477 ERR_set_error(ERR_LIB_USER, 1, null); 1478 } else { 1479 ERR_put_error(ERR_LIB_USER, 0, 1, file.toStringz, line); 1480 } 1481 ERR_add_error_data(3, msg.toStringz, ": ".ptr, submsg.toStringz); 1482 } 1483 1484 static if (OPENSSL_VERSION_AT_LEAST(1, 1, 0)) { 1485 private BIO_METHOD* s_bio_methods; 1486 1487 private void initBioMethods() 1488 @trusted { 1489 s_bio_methods = BIO_meth_new(BIO_get_new_index(), "SslStream"); 1490 1491 BIO_meth_set_write(s_bio_methods, &onBioWrite); 1492 BIO_meth_set_read(s_bio_methods, &onBioRead); 1493 BIO_meth_set_ctrl(s_bio_methods, &onBioCtrl); 1494 BIO_meth_set_create(s_bio_methods, &onBioNew); 1495 BIO_meth_set_destroy(s_bio_methods, &onBioFree); 1496 } 1497 } else { 1498 // OpenSSL 1.1.0 made BIO opaque, this is for older versions 1499 //https://github.com/openssl/openssl/commit/a146ae55ba479a5c7aa2a6afba1b2b93102a152c 1500 private BIO_METHOD s_bio_methods = { 1501 57, "SslStream", 1502 &onBioWrite, 1503 &onBioRead, 1504 &onBioPuts, 1505 null, // &onBioGets 1506 &onBioCtrl, 1507 &onBioNew, 1508 &onBioFree, 1509 null, // &onBioCallbackCtrl 1510 }; 1511 } 1512 1513 private nothrow extern(C): 1514 static if (haveALPN) { 1515 alias ALPNCallback = int function(SSL *ssl, const(char) **output, ubyte* outlen, const(char) *input, uint inlen, void *arg); 1516 void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, ALPNCallback cb, void *arg); 1517 int SSL_set_alpn_protos(SSL *ssl, const char *data, uint len); 1518 int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const char* protos, uint protos_len); 1519 void SSL_get0_alpn_selected(const SSL *ssl, const ubyte** data, uint *len); 1520 } 1521 const(ssl_method_st)* TLSv1_2_server_method();