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