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 initialize(); 406 } /// `SSLAdapter` method implementation. 407 408 override void onReadData(Data data) 409 { 410 debug(OPENSSL_DATA) stderr.writefln("OpenSSL: { Got %d incoming bytes from network", data.length); 411 412 if (next.state == ConnectionState.disconnecting) 413 { 414 return; 415 } 416 417 assert(r.data.length == 0, "Would clobber data"); 418 r.set(data.contents); 419 420 try 421 { 422 // We must buffer all cleartext data and send it off in a 423 // single `super.onReadData` call. It cannot be split up 424 // into multiple calls, because the `readDataHandler` may 425 // be set to null in the middle of our loop. 426 Data clearText; 427 428 while (true) 429 { 430 static ubyte[4096] buf; 431 debug(OPENSSL_DATA) auto oldLength = r.data.length; 432 auto result = SSL_read(sslHandle, buf.ptr, buf.length); 433 debug(OPENSSL_DATA) stderr.writefln("OpenSSL: < SSL_read ate %d bytes and spat out %d bytes", oldLength - r.data.length, result); 434 if (result > 0) 435 { 436 updateState(); 437 clearText ~= buf[0..result]; 438 } 439 else 440 { 441 sslError(result, "SSL_read"); 442 updateState(); 443 break; 444 } 445 } 446 enforce(r.data.length == 0, "SSL did not consume all read data"); 447 super.onReadData(clearText); 448 } 449 catch (CaughtException e) 450 { 451 debug(OPENSSL) stderr.writeln("Error while %s and processing incoming data: %s".format(next.state, e.msg)); 452 if (next.state != ConnectionState.disconnecting && next.state != ConnectionState.disconnected) 453 disconnect(e.msg, DisconnectType.error); 454 else 455 throw e; 456 } 457 } /// `SSLAdapter` method implementation. 458 459 override void send(scope Data[] data, int priority = DEFAULT_PRIORITY) 460 { 461 assert(state == ConnectionState.connected, "Attempting to send to a non-connected socket"); 462 while (data.length) 463 { 464 auto datum = data[0]; 465 data = data[1 .. $]; 466 if (!datum.length) 467 continue; 468 469 debug(OPENSSL_DATA) stderr.writefln("OpenSSL: > Got %d outgoing bytes from program", datum.length); 470 471 debug(OPENSSL_DATA) auto oldLength = w.data.length; 472 auto result = SSL_write(sslHandle, datum.ptr, datum.length.to!int); 473 debug(OPENSSL_DATA) stderr.writefln("OpenSSL: SSL_write ate %d bytes and spat out %d bytes", datum.length, w.data.length - oldLength); 474 if (result > 0) 475 { 476 // "SSL_write() will only return with success, when the 477 // complete contents of buf of length num has been written." 478 } 479 else 480 { 481 sslError(result, "SSL_write"); 482 break; 483 } 484 } 485 updateState(); 486 } /// ditto 487 488 override @property ConnectionState state() 489 { 490 return connectionState; 491 } /// ditto 492 493 override void disconnect(string reason, DisconnectType type) 494 { 495 debug(OPENSSL) stderr.writefln("OpenSSL: disconnect called ('%s')", reason); 496 if (!SSL_in_init(sslHandle)) 497 { 498 debug(OPENSSL) stderr.writefln("OpenSSL: Calling SSL_shutdown"); 499 SSL_shutdown(sslHandle); 500 connectionState = ConnectionState.disconnecting; 501 updateState(); 502 } 503 else 504 debug(OPENSSL) stderr.writefln("OpenSSL: In init, not calling SSL_shutdown"); 505 debug(OPENSSL) stderr.writefln("OpenSSL: SSL_shutdown done, flushing"); 506 debug(OPENSSL) stderr.writefln("OpenSSL: SSL_shutdown output flushed"); 507 super.disconnect(reason, type); 508 } /// ditto 509 510 override void onDisconnect(string reason, DisconnectType type) 511 { 512 debug(OPENSSL) stderr.writefln("OpenSSL: onDisconnect ('%s'), calling SSL_free", reason); 513 r.clear(); 514 w.clear(); 515 SSL_free(sslHandle); 516 sslHandle = null; 517 r = MemoryBIO.init; // Was owned by sslHandle, destroyed by SSL_free 518 w = MemoryBIO.init; // ditto 519 connectionState = ConnectionState.disconnected; 520 debug(OPENSSL) stderr.writeln("OpenSSL: onDisconnect: SSL_free called, calling super.onDisconnect"); 521 super.onDisconnect(reason, type); 522 debug(OPENSSL) stderr.writeln("OpenSSL: onDisconnect finished"); 523 } /// ditto 524 525 override void setHostName(string hostname, ushort port = 0, string service = null) 526 { 527 this.hostname = cast(char*)hostname.toStringz(); 528 SSL_set_tlsext_host_name(sslHandle, cast(char*)this.hostname); 529 } /// ditto 530 531 override OpenSSLCertificate getHostCertificate() 532 { 533 return new OpenSSLCertificate(SSL_get_certificate(sslHandle).sslEnforce()); 534 } /// ditto 535 536 override OpenSSLCertificate getPeerCertificate() 537 { 538 return new OpenSSLCertificate(SSL_get_peer_certificate(sslHandle).sslEnforce()); 539 } /// ditto 540 541 protected: 542 MemoryBIO r; // BIO for incoming ciphertext 543 MemoryBIO w; // BIO for outgoing ciphertext 544 545 private final void initialize() 546 { 547 final switch (context.kind) 548 { 549 case OpenSSLContext.Kind.client: SSL_connect(sslHandle).sslEnforce(); break; 550 case OpenSSLContext.Kind.server: SSL_accept (sslHandle).sslEnforce(); break; 551 } 552 connectionState = ConnectionState.connecting; 553 updateState(); 554 555 if (context.verify && hostname && context.kind == OpenSSLContext.Kind.client) 556 { 557 static if (!isOpenSSL11) 558 { 559 import core.stdc.string : strlen; 560 X509_VERIFY_PARAM* param = SSL_get0_param(sslHandle); 561 X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); 562 X509_VERIFY_PARAM_set1_host(param, hostname, strlen(hostname)).sslEnforce("X509_VERIFY_PARAM_set1_host"); 563 } 564 else 565 { 566 SSL_set_hostflags(sslHandle, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); 567 SSL_set1_host(sslHandle, hostname).sslEnforce("SSL_set1_host"); 568 } 569 } 570 } 571 572 protected final void updateState() 573 { 574 // Flush any accumulated outgoing ciphertext to the network 575 if (w.data.length) 576 { 577 debug(OPENSSL_DATA) stderr.writefln("OpenSSL: } Flushing %d outgoing bytes from OpenSSL to network", w.data.length); 578 next.send(Data(w.data)); 579 w.clear(); 580 } 581 582 // Has the handshake been completed? 583 if (connectionState == ConnectionState.connecting && SSL_is_init_finished(sslHandle)) 584 { 585 connectionState = ConnectionState.connected; 586 if (context.verify) 587 try 588 if (!SSL_get_peer_certificate(sslHandle)) 589 enforce(context.verify != SSLContext.Verify.require, "No SSL peer certificate was presented"); 590 else 591 { 592 auto result = SSL_get_verify_result(sslHandle); 593 enforce(result == X509_V_OK, 594 "SSL peer verification failed with error " ~ result.to!string); 595 } 596 catch (Exception e) 597 { 598 disconnect(e.msg, DisconnectType.error); 599 return; 600 } 601 super.onConnect(); 602 } 603 } 604 605 alias send = SSLAdapter.send; 606 607 void sslError(int ret, string msg) 608 { 609 auto err = SSL_get_error(sslHandle, ret); 610 debug(OPENSSL) stderr.writefln("OpenSSL: SSL error ('%s', ret %d): %s", msg, ret, err); 611 switch (err) 612 { 613 case SSL_ERROR_WANT_READ: 614 case SSL_ERROR_ZERO_RETURN: 615 return; 616 case SSL_ERROR_SYSCALL: 617 errnoEnforce(false, msg ~ " failed"); 618 assert(false); 619 default: 620 sslEnforce(false, "%s failed - error code %s".format(msg, err)); 621 } 622 } 623 } 624 625 /// `SSLCertificate` implementation. 626 class OpenSSLCertificate : SSLCertificate 627 { 628 X509* x509; /// The C OpenSSL certificate object. 629 630 this(X509* x509) 631 { 632 this.x509 = x509; 633 } /// 634 635 override string getSubjectName() 636 { 637 char[256] buf; 638 X509_NAME_oneline(X509_get_subject_name(x509), buf.ptr, buf.length); 639 buf[$-1] = 0; 640 return buf.ptr.to!string(); 641 } /// `SSLCertificate` method implementation. 642 } 643 644 // *************************************************************************** 645 646 /// TODO: replace with custom BIO which hooks into IConnection 647 struct MemoryBIO 648 { 649 @disable this(this); 650 651 this(const(void)[] data) 652 { 653 bio_ = BIO_new_mem_buf(cast(void*)data.ptr, data.length.to!int); 654 } /// 655 656 void set(const(void)[] data) 657 { 658 BUF_MEM *bptr = BUF_MEM_new(); 659 if (data.length) 660 { 661 BUF_MEM_grow(bptr, data.length); 662 bptr.data[0..bptr.length] = cast(char[])data; 663 } 664 BIO_set_mem_buf(bio, bptr, BIO_CLOSE); 665 } /// 666 667 void clear() { set(null); } /// 668 669 @property BIO* bio() 670 { 671 if (!bio_) 672 { 673 bio_ = sslEnforce(BIO_new(BIO_s_mem())); 674 BIO_set_close(bio_, BIO_CLOSE); 675 } 676 return bio_; 677 } /// 678 679 const(void)[] data() 680 { 681 BUF_MEM *bptr; 682 BIO_get_mem_ptr(bio, &bptr); 683 return bptr.data[0..bptr.length]; 684 } /// 685 686 private: 687 BIO* bio_; 688 } 689 690 /// Convert an OpenSSL error into a thrown D exception. 691 T sslEnforce(T)(T v, string message = null) 692 { 693 if (v) 694 return v; 695 696 { 697 MemoryBIO m; 698 ERR_print_errors(m.bio); 699 string msg = (cast(char[])m.data).idup; 700 701 if (message) 702 msg = message ~ ": " ~ msg; 703 704 throw new Exception(msg); 705 } 706 } 707 708 // *************************************************************************** 709 710 version (unittest) import ae.net.ssl.test; 711 unittest { testSSL(new OpenSSLProvider); }