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