1 /** 2 * OpenSSL support. 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <ae@cy.md> 12 */ 13 14 /** 15 This module selects which OpenSSL version to target depending on 16 what version of D bindings are available. The "openssl" Deimos 17 package version 1.x targets OpenSSL 1.0, and version 2.x targets 18 OpenSSL 1.1. 19 20 If you use ae with Dub, you can specify the version of the OpenSSL 21 D bindings in your project's dub.sdl. The ae:openssl subpackage 22 also has configurations which indicate the library file names to 23 link against. 24 25 Thus, to target OpenSSL 1.0, you can use: 26 27 --- 28 dependency "ae:openssl" version="..." 29 dependency "openssl" version="~>1.0" 30 subConfiguration "ae:openssl" "lib-explicit-1.0" 31 --- 32 33 And, to target OpenSSL 1.1: 34 35 --- 36 dependency "ae:openssl" version="..." 37 dependency "openssl" version="~>2.0" 38 subConfiguration "ae:openssl" "lib-implicit-1.1" 39 --- 40 */ 41 42 module ae.net.ssl.openssl; 43 44 import core.stdc.stdint; 45 46 import std.conv : to; 47 import std.exception : enforce, errnoEnforce; 48 import std.functional; 49 import std.socket; 50 import std.string; 51 52 //import deimos.openssl.rand; 53 import deimos.openssl.ssl; 54 import deimos.openssl.err; 55 import deimos.openssl.x509_vfy; 56 import deimos.openssl.x509v3; 57 58 import ae.net.asockets; 59 import ae.net.ssl; 60 import ae.utils.exception : CaughtException; 61 import ae.utils.meta : enumLength; 62 import ae.utils.text; 63 64 debug(OPENSSL) import std.stdio : stderr; 65 debug(OPENSSL_DATA) import std.stdio : stderr; 66 67 // *************************************************************************** 68 69 /// Are the current Deimos OpenSSL bindings 1.1 or newer? 70 static if (is(typeof(OPENSSL_MAKE_VERSION))) 71 enum isOpenSSL11 = OPENSSL_VERSION_NUMBER >= OPENSSL_MAKE_VERSION(1, 1, 0, 0); 72 else 73 enum isOpenSSL11 = false; 74 75 /// `mixin` this in your program to link to OpenSSL. 76 mixin template SSLUseLib() 77 { 78 static if (ae.net.ssl.openssl.isOpenSSL11) 79 { 80 pragma(lib, "ssl"); 81 pragma(lib, "crypto"); 82 } 83 else 84 { 85 version(Win64) 86 { 87 pragma(lib, "ssleay32"); 88 pragma(lib, "libeay32"); 89 } 90 else 91 { 92 pragma(lib, "ssl"); 93 version(Windows) 94 { pragma(lib, "eay"); } 95 else 96 { pragma(lib, "crypto"); } 97 } 98 } 99 } 100 101 // Patch up incomplete Deimos bindings. 102 103 private 104 { 105 enum TLS1_3_VERSION = 0x0304; 106 enum SSL_CTRL_SET_MIN_PROTO_VERSION = 123; 107 enum SSL_CTRL_SET_MAX_PROTO_VERSION = 124; 108 long SSL_CTX_set_min_proto_version(SSL_CTX* ctx, int version_) { return SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, version_, null); } 109 long SSL_CTX_set_max_proto_version(SSL_CTX* ctx, int version_) { return SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, version_, null); } 110 111 static if (isOpenSSL11) 112 { 113 alias SSLv23_client_method = TLS_client_method; 114 alias SSLv23_server_method = TLS_server_method; 115 void SSL_load_error_strings() {} 116 struct OPENSSL_INIT_SETTINGS; 117 extern(C) void OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings) nothrow; 118 void SSL_library_init() { OPENSSL_init_ssl(0, null); } 119 void OpenSSL_add_all_algorithms() { SSL_library_init(); } 120 extern(C) BIGNUM *BN_get_rfc3526_prime_1536(BIGNUM *bn) nothrow; 121 alias get_rfc3526_prime_1536 = BN_get_rfc3526_prime_1536; 122 extern(C) BIGNUM *BN_get_rfc3526_prime_2048(BIGNUM *bn) nothrow; 123 alias get_rfc3526_prime_2048 = BN_get_rfc3526_prime_2048; 124 extern(C) BIGNUM *BN_get_rfc3526_prime_3072(BIGNUM *bn) nothrow; 125 alias get_rfc3526_prime_3072 = BN_get_rfc3526_prime_3072; 126 extern(C) BIGNUM *BN_get_rfc3526_prime_4096(BIGNUM *bn) nothrow; 127 alias get_rfc3526_prime_4096 = BN_get_rfc3526_prime_4096; 128 extern(C) BIGNUM *BN_get_rfc3526_prime_6144(BIGNUM *bn) nothrow; 129 alias get_rfc3526_prime_6144 = BN_get_rfc3526_prime_6144; 130 extern(C) BIGNUM *BN_get_rfc3526_prime_8192(BIGNUM *bn) nothrow; 131 alias get_rfc3526_prime_8192 = BN_get_rfc3526_prime_8192; 132 extern(C) int SSL_in_init(const SSL *s) nothrow; 133 extern(C) int SSL_CTX_set_ciphersuites(SSL_CTX* ctx, const(char)* str); 134 } 135 else 136 { 137 extern(C) void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param, uint flags) nothrow; 138 extern(C) X509_VERIFY_PARAM *SSL_get0_param(SSL *ssl) nothrow; 139 enum X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS = 0x4; 140 extern(C) int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param, const char *name, size_t namelen) nothrow; 141 } 142 143 alias SSL_CTX_set_tlsext_servername_callback_fun = extern(C) int function(SSL *s, int *al, void *arg); 144 alias SSL_CTX_callback_ctrl_fun = extern (C) void function(); 145 auto SSL_CTX_set_tlsext_servername_callback(SSL_CTX* ctx, SSL_CTX_set_tlsext_servername_callback_fun cb) { 146 return SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, cast(SSL_CTX_callback_ctrl_fun)cb); 147 } 148 } 149 150 // *************************************************************************** 151 152 shared static this() 153 { 154 SSL_load_error_strings(); 155 SSL_library_init(); 156 OpenSSL_add_all_algorithms(); 157 } 158 159 // *************************************************************************** 160 161 /// `SSLProvider` implementation. 162 class OpenSSLProvider : SSLProvider 163 { 164 override SSLContext createContext(SSLContext.Kind kind) 165 { 166 return new OpenSSLContext(kind); 167 } /// 168 169 override SSLAdapter createAdapter(SSLContext context, IConnection next) 170 { 171 auto ctx = cast(OpenSSLContext)context; 172 assert(ctx, "Not an OpenSSLContext"); 173 return new OpenSSLAdapter(ctx, next); 174 } /// 175 } 176 177 /// `SSLContext` implementation. 178 class OpenSSLContext : SSLContext 179 { 180 SSL_CTX* sslCtx; /// The C OpenSSL context object. 181 Kind kind; /// Client or server. 182 Verify verify; /// 183 184 const(ubyte)[] psk; /// PSK (Pre-Shared Key) configuration. 185 string pskID; /// ditto 186 187 this(Kind kind) 188 { 189 this.kind = kind; 190 191 const(SSL_METHOD)* method; 192 193 final switch (kind) 194 { 195 case Kind.client: 196 method = SSLv23_client_method().sslEnforce(); 197 break; 198 case Kind.server: 199 method = SSLv23_server_method().sslEnforce(); 200 break; 201 } 202 sslCtx = SSL_CTX_new(method).sslEnforce(); 203 setCipherList(["ALL", "!MEDIUM", "!LOW", "!aNULL", "!eNULL", "!SSLv2", "!DH", "!TLSv1"]); 204 205 SSL_CTX_set_default_verify_paths(sslCtx); 206 207 if (kind == Kind.server) 208 { 209 SSL_CTX_set_tlsext_servername_callback(sslCtx, &servernameCallback); 210 SSL_CTX_set_tlsext_servername_arg(sslCtx, cast(void*)this); 211 } 212 } /// 213 214 /// OpenSSL uses different APIs to specify the cipher list for 215 /// TLSv1.2 and below and to specify the ciphersuites for TLSv1.3. 216 /// When calling `setCipherList`, use this value to delimit them: 217 /// values before `cipherListTLS13Delimiter` will be specified via 218 /// SSL_CTX_set_cipher_list (for TLSv1.2 and older), and those 219 /// after `cipherListTLS13Delimiter` will be specified via 220 /// `SSL_CTX_set_ciphersuites` (for TLSv1.3). 221 static immutable cipherListTLS13Delimiter = "\0ae-net-ssl-openssl-cipher-list-tls-1.3-delimiter"; 222 223 override void setCipherList(string[] ciphers) 224 { 225 assert(ciphers.length, "Empty cipher list"); 226 import std.algorithm.searching : findSplit; 227 auto parts = ciphers.findSplit((&cipherListTLS13Delimiter)[0..1]); 228 auto oldCiphers = parts[0]; 229 auto newCiphers = parts[2]; 230 if (oldCiphers.length) 231 SSL_CTX_set_cipher_list(sslCtx, oldCiphers.join(":").toStringz()).sslEnforce(); 232 if (newCiphers.length) 233 { 234 static if (isOpenSSL11) 235 SSL_CTX_set_ciphersuites(sslCtx, newCiphers.join(":").toStringz()).sslEnforce(); 236 else 237 assert(false, "Not built against OpenSSL version with TLSv1.3 support."); 238 } 239 } /// `SSLContext` method implementation. 240 241 override void enableDH(int bits) 242 { 243 typeof(&get_rfc3526_prime_2048) func; 244 245 switch (bits) 246 { 247 case 1536: func = &get_rfc3526_prime_1536; break; 248 case 2048: func = &get_rfc3526_prime_2048; break; 249 case 3072: func = &get_rfc3526_prime_3072; break; 250 case 4096: func = &get_rfc3526_prime_4096; break; 251 case 6144: func = &get_rfc3526_prime_6144; break; 252 case 8192: func = &get_rfc3526_prime_8192; break; 253 default: assert(false, "No RFC3526 prime available for %d bits".format(bits)); 254 } 255 256 DH* dh; 257 scope(exit) DH_free(dh); 258 259 dh = DH_new().sslEnforce(); 260 dh.p = func(null).sslEnforce(); 261 ubyte gen = 2; 262 dh.g = BN_bin2bn(&gen, gen.sizeof, null); 263 SSL_CTX_set_tmp_dh(sslCtx, dh).sslEnforce(); 264 } /// ditto 265 266 override void enableECDH() 267 { 268 auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1).sslEnforce(); 269 scope(exit) EC_KEY_free(ecdh); 270 SSL_CTX_set_tmp_ecdh(sslCtx, ecdh).sslEnforce(); 271 } /// ditto 272 273 override void setCertificate(string path) 274 { 275 SSL_CTX_use_certificate_chain_file(sslCtx, toStringz(path)) 276 .sslEnforce("Failed to load certificate file " ~ path); 277 } /// ditto 278 279 override void setPrivateKey(string path) 280 { 281 SSL_CTX_use_PrivateKey_file(sslCtx, toStringz(path), SSL_FILETYPE_PEM) 282 .sslEnforce("Failed to load private key file " ~ path); 283 } /// ditto 284 285 override void setPreSharedKey(string id, const(ubyte)[] key) 286 { 287 pskID = id; 288 psk = key; 289 290 final switch (kind) 291 { 292 case Kind.client: SSL_CTX_set_psk_client_callback(sslCtx, psk ? &pskClientCallback : null); break; 293 case Kind.server: SSL_CTX_set_psk_server_callback(sslCtx, psk ? &pskServerCallback : null); break; 294 } 295 } /// ditto 296 297 extern (C) private static uint pskClientCallback( 298 SSL* ssl, const(char)* hint, 299 char* identity, uint max_identity_len, ubyte* psk, 300 uint max_psk_len) 301 { 302 debug(OPENSSL) stderr.writeln("pskClientCallback! hint=", hint); 303 304 auto self = cast(OpenSSLAdapter)SSL_get_ex_data(ssl, 0); 305 if (self.context.pskID.length + 1 > max_identity_len || 306 self.context.psk.length > max_psk_len) 307 { 308 debug(OPENSSL) stderr.writeln("PSK or PSK ID too long"); 309 return 0; 310 } 311 312 identity[0 .. self.context.pskID.length] = self.context.pskID[]; 313 identity[ self.context.pskID.length] = 0; 314 psk[0 .. self.context.psk.length] = self.context.psk[]; 315 return cast(uint)self.context.psk.length; 316 } 317 318 extern (C) private static uint pskServerCallback( 319 SSL* ssl, const(char)* identity, 320 ubyte* psk, uint max_psk_len) 321 { 322 auto self = cast(OpenSSLAdapter)SSL_get_ex_data(ssl, 0); 323 auto identityStr = fromStringz(identity); 324 if (identityStr != self.context.pskID) 325 { 326 debug(OPENSSL) stderr.writefln("PSK ID mismatch: expected %s, got %s", 327 self.context.pskID, identityStr); 328 return 0; 329 } 330 if (self.context.psk.length > max_psk_len) 331 { 332 debug(OPENSSL) stderr.writeln("PSK too long"); 333 return 0; 334 } 335 psk[0 .. self.context.psk.length] = self.context.psk[]; 336 return cast(uint)self.context.psk.length; 337 } 338 339 extern (C) private static int servernameCallback(SSL* ssl, int* ad, void* arg) 340 { 341 auto context = cast(OpenSSLContext)arg; 342 auto adapter = cast(OpenSSLAdapter)SSL_get_ex_data(ssl, 0); 343 344 auto sniName = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); 345 if (sniName) 346 { 347 adapter.sniHostname = fromStringz(sniName).idup; 348 debug(OPENSSL) stderr.writeln("SNI hostname: ", context.sniHostname); 349 } 350 else 351 { 352 adapter.sniHostname = null; 353 debug(OPENSSL) stderr.writeln("No SNI hostname provided."); 354 } 355 356 return SSL_TLSEXT_ERR_OK; 357 } 358 359 override void setPeerVerify(Verify verify) 360 { 361 static const int[enumLength!Verify] modes = 362 [ 363 SSL_VERIFY_NONE, 364 SSL_VERIFY_PEER, 365 SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 366 ]; 367 SSL_CTX_set_verify(sslCtx, modes[verify], null); 368 this.verify = verify; 369 } /// ditto 370 371 override void setPeerRootCertificate(string path) 372 { 373 auto szPath = toStringz(path); 374 SSL_CTX_load_verify_locations(sslCtx, szPath, null).sslEnforce(); 375 376 if (kind == Kind.server) 377 { 378 auto list = SSL_load_client_CA_file(szPath).sslEnforce(); 379 SSL_CTX_set_client_CA_list(sslCtx, list); 380 } 381 } /// ditto 382 383 override void setFlags(int flags) 384 { 385 SSL_CTX_set_options(sslCtx, flags).sslEnforce(); 386 } /// ditto 387 388 private static immutable int[enumLength!SSLVersion] sslVersions = [ 389 0, 390 SSL3_VERSION, 391 TLS1_VERSION, 392 TLS1_1_VERSION, 393 TLS1_2_VERSION, 394 TLS1_3_VERSION, 395 ]; 396 397 override void setMinimumVersion(SSLVersion v) 398 { 399 SSL_CTX_set_min_proto_version(sslCtx, sslVersions[v]).sslEnforce(); 400 } /// ditto 401 402 override void setMaximumVersion(SSLVersion v) 403 { 404 SSL_CTX_set_max_proto_version(sslCtx, sslVersions[v]).sslEnforce(); 405 } /// ditto 406 } 407 408 static this() 409 { 410 ssl = new OpenSSLProvider(); 411 } 412 413 // *************************************************************************** 414 415 /// `SSLAdapter` implementation. 416 class OpenSSLAdapter : SSLAdapter 417 { 418 SSL* sslHandle; /// The C OpenSSL connection object. 419 OpenSSLContext context; /// 420 ConnectionState connectionState; /// 421 const(char)* hostname; /// 422 private string sniHostname; 423 424 this(OpenSSLContext context, IConnection next) 425 { 426 this.context = context; 427 super(next); 428 429 sslHandle = sslEnforce(SSL_new(context.sslCtx)); 430 SSL_set_ex_data(sslHandle, 0, cast(void*)this).sslEnforce(); 431 SSL_set_bio(sslHandle, r.bio, w.bio); 432 433 if (next.state == ConnectionState.connected) 434 initialize(); 435 } /// 436 437 override void onConnect() 438 { 439 debug(OPENSSL) stderr.writefln("OpenSSL: * Transport is connected"); 440 initialize(); 441 } /// `SSLAdapter` method implementation. 442 443 override void onReadData(Data data) 444 { 445 debug(OPENSSL_DATA) stderr.writefln("OpenSSL: { Got %d incoming bytes from network", data.length); 446 447 if (next.state == ConnectionState.disconnecting) 448 { 449 return; 450 } 451 452 assert(r.data.length == 0, "Would clobber data"); 453 data.enter((contents) { r.set(contents); }); 454 455 try 456 { 457 // We must buffer all cleartext data and send it off in a 458 // single `super.onReadData` call. It cannot be split up 459 // into multiple calls, because the `readDataHandler` may 460 // be set to null in the middle of our loop. 461 Data clearText; 462 463 while (true) 464 { 465 static ubyte[4096] buf; 466 debug(OPENSSL_DATA) auto oldLength = r.data.length; 467 auto result = SSL_read(sslHandle, buf.ptr, buf.length); 468 debug(OPENSSL_DATA) stderr.writefln("OpenSSL: < SSL_read ate %d bytes and spat out %d bytes", oldLength - r.data.length, result); 469 if (result > 0) 470 { 471 updateState(); 472 clearText ~= buf[0..result]; 473 } 474 else 475 { 476 sslError(result, "SSL_read"); 477 updateState(); 478 break; 479 } 480 } 481 enforce(r.data.length == 0, "SSL did not consume all read data"); 482 if (clearText.length) 483 super.onReadData(clearText); 484 } 485 catch (CaughtException e) 486 { 487 debug(OPENSSL) stderr.writeln("Error while %s and processing incoming data: %s".format(next.state, e.msg)); 488 if (next.state != ConnectionState.disconnecting && next.state != ConnectionState.disconnected) 489 disconnect(e.msg, DisconnectType.error); 490 else 491 throw e; 492 } 493 } /// `SSLAdapter` method implementation. 494 495 override void send(scope Data[] data, int priority = DEFAULT_PRIORITY) 496 { 497 assert(state == ConnectionState.connected, "Attempting to send to a non-connected socket"); 498 while (data.length) 499 { 500 auto datum = data[0]; 501 data = data[1 .. $]; 502 if (!datum.length) 503 continue; 504 505 debug(OPENSSL_DATA) stderr.writefln("OpenSSL: > Got %d outgoing bytes from program", datum.length); 506 507 debug(OPENSSL_DATA) auto oldLength = w.data.length; 508 int result; 509 datum.enter((scope contents) { 510 result = SSL_write(sslHandle, contents.ptr, contents.length.to!int); 511 }); 512 debug(OPENSSL_DATA) stderr.writefln("OpenSSL: SSL_write ate %d bytes and spat out %d bytes", datum.length, w.data.length - oldLength); 513 if (result > 0) 514 { 515 // "SSL_write() will only return with success, when the 516 // complete contents of buf of length num has been written." 517 } 518 else 519 { 520 sslError(result, "SSL_write"); 521 break; 522 } 523 } 524 updateState(); 525 } /// ditto 526 527 override @property ConnectionState state() 528 { 529 if (next.state == ConnectionState.connecting) 530 return next.state; 531 return connectionState; 532 } /// ditto 533 534 override void disconnect(string reason, DisconnectType type) 535 { 536 debug(OPENSSL) stderr.writefln("OpenSSL: disconnect called ('%s')", reason); 537 if (!SSL_in_init(sslHandle)) 538 { 539 debug(OPENSSL) stderr.writefln("OpenSSL: Calling SSL_shutdown"); 540 SSL_shutdown(sslHandle); 541 connectionState = ConnectionState.disconnecting; 542 updateState(); 543 } 544 else 545 debug(OPENSSL) stderr.writefln("OpenSSL: In init, not calling SSL_shutdown"); 546 debug(OPENSSL) stderr.writefln("OpenSSL: SSL_shutdown done, flushing"); 547 debug(OPENSSL) stderr.writefln("OpenSSL: SSL_shutdown output flushed"); 548 super.disconnect(reason, type); 549 } /// ditto 550 551 override void onDisconnect(string reason, DisconnectType type) 552 { 553 debug(OPENSSL) stderr.writefln("OpenSSL: onDisconnect ('%s'), calling SSL_free", reason); 554 r.clear(); 555 w.clear(); 556 SSL_free(sslHandle); 557 sslHandle = null; 558 r = MemoryBIO.init; // Was owned by sslHandle, destroyed by SSL_free 559 w = MemoryBIO.init; // ditto 560 connectionState = ConnectionState.disconnected; 561 debug(OPENSSL) stderr.writeln("OpenSSL: onDisconnect: SSL_free called, calling super.onDisconnect"); 562 super.onDisconnect(reason, type); 563 debug(OPENSSL) stderr.writeln("OpenSSL: onDisconnect finished"); 564 } /// ditto 565 566 override void setHostName(string hostname, ushort port = 0, string service = null) 567 { 568 this.hostname = cast(char*)hostname.toStringz(); 569 SSL_set_tlsext_host_name(sslHandle, cast(char*)this.hostname); 570 } /// ditto 571 572 override OpenSSLCertificate getHostCertificate() 573 { 574 return new OpenSSLCertificate(SSL_get_certificate(sslHandle).sslEnforce()); 575 } /// ditto 576 577 override OpenSSLCertificate getPeerCertificate() 578 { 579 return new OpenSSLCertificate(SSL_get_peer_certificate(sslHandle).sslEnforce()); 580 } /// ditto 581 582 override string getSNIHostname() 583 { 584 return sniHostname; 585 } 586 587 protected: 588 MemoryBIO r; // BIO for incoming ciphertext 589 MemoryBIO w; // BIO for outgoing ciphertext 590 591 private final void initialize() 592 { 593 final switch (context.kind) 594 { 595 case OpenSSLContext.Kind.client: SSL_connect(sslHandle).sslEnforce(); break; 596 case OpenSSLContext.Kind.server: SSL_accept (sslHandle).sslEnforce(); break; 597 } 598 connectionState = ConnectionState.connecting; 599 updateState(); 600 601 if (context.verify && hostname && context.kind == OpenSSLContext.Kind.client) 602 { 603 static if (!isOpenSSL11) 604 { 605 import core.stdc.string : strlen; 606 X509_VERIFY_PARAM* param = SSL_get0_param(sslHandle); 607 X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); 608 X509_VERIFY_PARAM_set1_host(param, hostname, strlen(hostname)).sslEnforce("X509_VERIFY_PARAM_set1_host"); 609 } 610 else 611 { 612 SSL_set_hostflags(sslHandle, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); 613 SSL_set1_host(sslHandle, hostname).sslEnforce("SSL_set1_host"); 614 } 615 } 616 } 617 618 protected final void updateState() 619 { 620 // Flush any accumulated outgoing ciphertext to the network 621 if (w.data.length) 622 { 623 debug(OPENSSL_DATA) stderr.writefln("OpenSSL: } Flushing %d outgoing bytes from OpenSSL to network", w.data.length); 624 next.send(Data(w.data)); 625 w.clear(); 626 } 627 628 // Has the handshake been completed? 629 if (connectionState == ConnectionState.connecting && SSL_is_init_finished(sslHandle)) 630 { 631 connectionState = ConnectionState.connected; 632 if (context.verify) 633 try 634 if (!SSL_get_peer_certificate(sslHandle)) 635 enforce(context.verify != SSLContext.Verify.require, "No SSL peer certificate was presented"); 636 else 637 { 638 auto result = SSL_get_verify_result(sslHandle); 639 enforce(result == X509_V_OK, 640 "SSL peer verification failed with error " ~ result.to!string); 641 } 642 catch (Exception e) 643 { 644 disconnect(e.msg, DisconnectType.error); 645 return; 646 } 647 super.onConnect(); 648 } 649 } 650 651 alias send = SSLAdapter.send; 652 653 void sslError(int ret, string msg) 654 { 655 auto err = SSL_get_error(sslHandle, ret); 656 debug(OPENSSL) stderr.writefln("OpenSSL: SSL error ('%s', ret %d): %s", msg, ret, err); 657 switch (err) 658 { 659 case SSL_ERROR_WANT_READ: 660 case SSL_ERROR_ZERO_RETURN: 661 return; 662 case SSL_ERROR_SYSCALL: 663 errnoEnforce(false, msg ~ " failed"); 664 assert(false); 665 default: 666 sslEnforce(false, "%s failed - error code %s".format(msg, err)); 667 } 668 } 669 } 670 671 /// `SSLCertificate` implementation. 672 class OpenSSLCertificate : SSLCertificate 673 { 674 X509* x509; /// The C OpenSSL certificate object. 675 676 this(X509* x509) 677 { 678 this.x509 = x509; 679 } /// 680 681 override string getSubjectName() 682 { 683 char[256] buf; 684 X509_NAME_oneline(X509_get_subject_name(x509), buf.ptr, buf.length); 685 buf[$-1] = 0; 686 return buf.ptr.to!string(); 687 } /// `SSLCertificate` method implementation. 688 } 689 690 // *************************************************************************** 691 692 /// TODO: replace with custom BIO which hooks into IConnection 693 struct MemoryBIO 694 { 695 @disable this(this); 696 697 this(const(ubyte)[] data) 698 { 699 bio_ = BIO_new_mem_buf(cast(void*)data.ptr, data.length.to!int); 700 } /// 701 702 void set(scope const(void)[] data) 703 { 704 BUF_MEM *bptr = BUF_MEM_new(); 705 if (data.length) 706 { 707 BUF_MEM_grow(bptr, data.length); 708 bptr.data[0..bptr.length] = cast(char[])data; 709 } 710 BIO_set_mem_buf(bio, bptr, BIO_CLOSE); 711 } /// 712 713 void clear() { set(null); } /// 714 715 @property BIO* bio() 716 { 717 if (!bio_) 718 { 719 bio_ = sslEnforce(BIO_new(BIO_s_mem())); 720 BIO_set_close(bio_, BIO_CLOSE); 721 } 722 return bio_; 723 } /// 724 725 const(ubyte)[] data() 726 { 727 BUF_MEM *bptr; 728 BIO_get_mem_ptr(bio, &bptr); 729 return (cast(ubyte*)bptr.data)[0..bptr.length]; 730 } /// 731 732 private: 733 BIO* bio_; 734 } 735 736 /// Convert an OpenSSL error into a thrown D exception. 737 T sslEnforce(T)(T v, string message = null) 738 { 739 if (v) 740 return v; 741 742 { 743 MemoryBIO m; 744 ERR_print_errors(m.bio); 745 string msg = (cast(char[])m.data).idup; 746 747 if (message) 748 msg = message ~ ": " ~ msg; 749 750 throw new Exception(msg); 751 } 752 } 753 754 // *************************************************************************** 755 756 debug(ae_unittest) unittest 757 { 758 auto p = new OpenSSLProvider; 759 auto sc = p.createContext(SSLContext.Kind.server); 760 // Use PSK to avoid needing a certificate 761 sc.setPreSharedKey("test", "hunter2".representation); 762 auto s = new TcpServer; 763 s.handleAccept = (TcpConnection c) 764 { 765 auto a = p.createAdapter(sc, c); 766 a.handleReadData = (Data d) { 767 import std.ascii : toUpper; 768 foreach (ref char c; cast(char[])d.mcontents) 769 c = toUpper(c); 770 a.send(d); 771 }; 772 s.close(); // One connection 773 }; 774 auto port = s.listen(0, "127.0.0.1"); 775 auto cc = p.createContext(SSLContext.Kind.client); 776 cc.setPeerVerify(SSLContext.Verify.none); 777 cc.setPreSharedKey("test", "hunter2".representation); 778 auto c = new TcpConnection; 779 auto a = p.createAdapter(cc, c); 780 a.handleConnect = { a.send(Data("hello")); }; 781 bool ok; 782 a.handleReadData = (Data d) { 783 assert(cast(string)d.contents == "HELLO"); 784 ok = true; 785 a.disconnect(); 786 }; 787 c.connect("127.0.0.1", port); 788 socketManager.loop(); 789 assert(ok); 790 } 791 792 debug (ae_unittest) import ae.net.ssl.test; 793 debug(ae_unittest) unittest { testSSL(new OpenSSLProvider); }