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