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); }