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