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