1 /**
2 	TLS stream implementation
3 
4 	TLSStream can be used to implement TLS communication on top of a TCP connection. The
5 	TLSContextKind of an TLSStream determines if the TLS tunnel is established actively (client) or
6 	passively (server).
7 
8 	Copyright: © 2012-2014 Sönke Ludwig
9 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
10 	Authors: Sönke Ludwig
11 */
12 module vibe.stream.tls;
13 
14 import vibe.core.log;
15 import vibe.core.net;
16 import vibe.core.path : NativePath;
17 import vibe.core.stream;
18 import vibe.core.sync;
19 
20 import vibe.utils.dictionarylist;
21 import vibe.internal.interfaceproxy;
22 
23 import std.algorithm;
24 import std.array;
25 import std.conv;
26 import std.exception;
27 import std.string;
28 
29 import core.stdc.string : strlen;
30 import core.sync.mutex;
31 import core.thread;
32 
33 version (VibeNoSSL) {}
34 else version(Have_openssl) version = OpenSSL;
35 else version(Have_botan) version = Botan;
36 
37 
38 /// A simple TLS client
39 unittest {
40 	import vibe.core.net;
41 	import vibe.stream.tls;
42 
43 	void sendTLSMessage()
44 	{
45 		auto conn = connectTCP("127.0.0.1", 1234);
46 		auto sslctx = createTLSContext(TLSContextKind.client);
47 		auto stream = createTLSStream(conn, sslctx);
48 		stream.write("Hello, World!");
49 		stream.finalize();
50 		conn.close();
51 	}
52 }
53 
54 /// Corresponding server
55 unittest {
56 	import vibe.core.log;
57 	import vibe.core.net;
58 	import vibe.stream.operations;
59 	import vibe.stream.tls;
60 
61 	void listenForTLS()
62 	{
63 		auto sslctx = createTLSContext(TLSContextKind.server);
64 		sslctx.useCertificateChainFile("server.crt");
65 		sslctx.usePrivateKeyFile("server.key");
66 		listenTCP(1234, delegate void(TCPConnection conn) nothrow {
67 			try {
68 				auto stream = createTLSStream(conn, sslctx);
69 				logInfo("Got message: %s", stream.readAllUTF8());
70 				stream.finalize();
71 			} catch (Exception e) {
72 				logInfo("Failed to receive encrypted message");
73 			}
74 		});
75 	}
76 }
77 
78 
79 /**************************************************************************************************/
80 /* Public functions                                                                               */
81 /**************************************************************************************************/
82 @safe:
83 
84 /** Creates a new context of the given kind.
85 
86 	Params:
87 		kind = Specifies if the context is going to be used on the client
88 			or on the server end of the TLS tunnel
89 		ver = The TLS protocol used for negotiating the tunnel
90 */
91 TLSContext createTLSContext(TLSContextKind kind, TLSVersion ver = TLSVersion.any)
92 @trusted {
93 	version (OpenSSL) {
94 		static TLSContext createOpenSSLContext(TLSContextKind kind, TLSVersion ver) @safe {
95 			import vibe.stream.openssl;
96 			return new OpenSSLContext(kind, ver);
97 		}
98 		if (!gs_sslContextFactory)
99 			setTLSContextFactory(&createOpenSSLContext);
100 	} else version(Botan) {
101 		static TLSContext createBotanContext(TLSContextKind kind, TLSVersion ver) @safe {
102 			import vibe.stream.botan;
103 			return new BotanTLSContext(kind);
104 		}
105 		if (!gs_sslContextFactory)
106 			setTLSContextFactory(&createBotanContext);
107 	}
108 	assert(gs_sslContextFactory !is null, "No TLS context factory registered. Compile in botan or openssl dependencies, or call setTLSContextFactory first.");
109 	return gs_sslContextFactory(kind, ver);
110 }
111 
112 /** Constructs a new TLS tunnel and infers the stream state from the TLSContextKind.
113 
114 	Depending on the TLSContextKind of ctx, the tunnel will try to establish an TLS
115 	tunnel by either passively accepting or by actively connecting.
116 
117 	Params:
118 		underlying = The base stream which is used for the TLS tunnel
119 		ctx = TLS context used for initiating the tunnel
120 		peer_name = DNS name of the remote peer, used for certificate validation
121 		peer_address = IP address of the remote peer, used for certificate validation
122 */
123 TLSStream createTLSStream(Stream)(Stream underlying, TLSContext ctx, string peer_name = null, NetworkAddress peer_address = NetworkAddress.init)
124 	if (isStream!Stream)
125 {
126 	auto stream_state = ctx.kind == TLSContextKind.client ? TLSStreamState.connecting : TLSStreamState.accepting;
127 	return createTLSStream(underlying, ctx, stream_state, peer_name, peer_address);
128 }
129 
130 /** Constructs a new TLS tunnel, allowing to override the stream state.
131 
132 	This constructor allows to specify a custom tunnel state, which can
133 	be useful when a tunnel has already been established by other means.
134 
135 	Params:
136 		underlying = The base stream which is used for the TLS tunnel
137 		ctx = TLS context used for initiating the tunnel
138 		state = The manually specified tunnel state
139 		peer_name = DNS name of the remote peer, used for certificate validation
140 		peer_address = IP address of the remote peer, used for certificate validation
141 */
142 TLSStream createTLSStream(Stream)(Stream underlying, TLSContext ctx, TLSStreamState state, string peer_name = null, NetworkAddress peer_address = NetworkAddress.init)
143 	if (isStream!Stream)
144 {
145 	return ctx.createStream(interfaceProxy!(.Stream)(underlying), state, peer_name, peer_address);
146 }
147 
148 /**
149 	Constructs a new TLS stream using manual memory allocator.
150 */
151 auto createTLSStreamFL(Stream)(Stream underlying, TLSContext ctx, TLSStreamState state, string peer_name = null, NetworkAddress peer_address = NetworkAddress.init)
152 	if (isStream!Stream)
153 {
154 	// This function has an auto return type to avoid the import of the TLS
155 	// implementation headers.  When client code uses this function the compiler
156 	// will have to semantically analyse it and subsequently will import the TLS
157 	// implementation headers.
158 	version (OpenSSL) {
159 		import vibe.internal.freelistref;
160 		import vibe.stream.openssl;
161 		static assert(AllocSize!TLSStream > 0);
162 		return FreeListRef!OpenSSLStream(interfaceProxy!(.Stream)(underlying), cast(OpenSSLContext)ctx,
163 										 state, peer_name, peer_address);
164 	} else version (Botan) {
165 		import vibe.internal.freelistref;
166 		import vibe.stream.botan;
167 		return FreeListRef!BotanTLSStream(interfaceProxy!(.Stream)(underlying), cast(BotanTLSContext) ctx, state, peer_name, peer_address);
168 	} else assert(false, "No TLS support compiled in (VibeNoTLS)");
169 }
170 
171 void setTLSContextFactory(TLSContext function(TLSContextKind, TLSVersion) @safe factory)
172 {
173 	() @trusted { gs_sslContextFactory = factory; } ();
174 }
175 
176 
177 /**************************************************************************************************/
178 /* Public types                                                                                   */
179 /**************************************************************************************************/
180 
181 /**
182 	Creates an TLS tunnel within an existing stream.
183 
184 	Note: Be sure to call finalize before finalizing/closing the outer stream so that the TLS
185 		tunnel is properly closed first.
186 */
187 interface TLSStream : Stream {
188 	@safe:
189 
190 	@property TLSCertificateInformation peerCertificate();
191 
192 	//-/ The host name reported through SNI
193 	//@property string hostName() const;
194 
195 	/** The ALPN that has been negotiated for this connection.
196 
197 		See_also: $(WEB https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation)
198 	*/
199 	@property string alpn() const;
200 }
201 
202 enum TLSStreamState {
203 	connecting,
204 	accepting,
205 	connected
206 }
207 
208 
209 /**
210 	Encapsulates the configuration for an TLS tunnel.
211 
212 	Note that when creating an TLSContext with TLSContextKind.client, the
213 	peerValidationMode will be set to TLSPeerValidationMode.trustedCert,
214 	but no trusted certificate authorities are added by default. Use
215 	useTrustedCertificateFile to add those.
216 */
217 interface TLSContext {
218 	@safe:
219 
220 	/// The kind of TLS context (client/server)
221 	@property TLSContextKind kind() const;
222 
223 	/** Specifies the validation level of remote peers.
224 
225 		The default mode for TLSContextKind.client is
226 		TLSPeerValidationMode.trustedCert and the default for
227 		TLSContextKind.server is TLSPeerValidationMode.none.
228 	*/
229 	@property void peerValidationMode(TLSPeerValidationMode mode);
230 	/// ditto
231 	@property TLSPeerValidationMode peerValidationMode() const;
232 
233 	/** The maximum length of an accepted certificate chain.
234 
235 		Any certificate chain longer than this will result in the TLS
236 		negitiation failing.
237 
238 		The default value is 9.
239 	*/
240 	@property void maxCertChainLength(int val);
241 	/// ditto
242 	@property int maxCertChainLength() const;
243 
244 	/** An optional user callback for peer validation.
245 
246 		This callback will be called for each peer and each certificate of
247 		its certificate chain to allow overriding the validation decision
248 		based on the selected peerValidationMode (e.g. to allow invalid
249 		certificates or to reject valid ones). This is mainly useful for
250 		presenting the user with a dialog in case of untrusted or mismatching
251 		certificates.
252 	*/
253 	@property void peerValidationCallback(TLSPeerValidationCallback callback);
254 	/// ditto
255 	@property inout(TLSPeerValidationCallback) peerValidationCallback() inout;
256 
257 	/** The callback used to associcate host names with TLS certificates/contexts.
258 
259 		This property is only used for kind $(D TLSContextKind.serverSNI).
260 	*/
261 	@property void sniCallback(TLSServerNameCallback callback);
262 	/// ditto
263 	@property inout(TLSServerNameCallback) sniCallback() inout;
264 
265 	/// Callback function invoked to choose alpn (client side)
266 	@property void alpnCallback(TLSALPNCallback alpn_chooser);
267 	/// ditto
268 	@property TLSALPNCallback alpnCallback() const;
269 
270 	/// Setter method invoked to offer ALPN (server side)
271 	void setClientALPN(string[] alpn);
272 
273 	/** Creates a new stream associated to this context.
274 	*/
275 	TLSStream createStream(InterfaceProxy!Stream underlying, TLSStreamState state, string peer_name = null, NetworkAddress peer_address = NetworkAddress.init);
276 
277 	/** Set the list of cipher specifications to use for TLS tunnels.
278 
279 		The list must be a colon separated list of cipher
280 		specifications as accepted by OpenSSL. Calling this function
281 		without argument will restore the default.
282 
283 		See_also: $(LINK https://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT)
284 	*/
285 	void setCipherList(string list = null);
286 
287 	/** Set params to use for DH cipher.
288 	 *
289 	 * By default the 2048-bit prime from RFC 3526 is used.
290 	 *
291 	 * Params:
292 	 * pem_file = Path to a PEM file containing the DH parameters. Calling
293 	 *    this function without argument will restore the default.
294 	 */
295 	void setDHParams(string pem_file=null);
296 
297 	/** Set the elliptic curve to use for ECDH cipher.
298 	 *
299 	 * By default a curve is either chosen automatically or  prime256v1 is used.
300 	 *
301 	 * Params:
302 	 * curve = The short name of the elliptic curve to use. Calling this
303 	 *    function without argument will restore the default.
304 	 *
305 	 */
306 	void setECDHCurve(string curve=null);
307 
308 	/// Sets a certificate file to use for authenticating to the remote peer
309 	void useCertificateChainFile(string path);
310 	/// ditto
311 	final void useCertificateChainFile(NativePath path) { useCertificateChainFile(path.toString()); }
312 
313 	/// Sets the private key to use for authenticating to the remote peer based
314 	/// on the configured certificate chain file.
315 	void usePrivateKeyFile(string path);
316 	/// ditto
317 	final void usePrivateKeyFile(NativePath path) { usePrivateKeyFile(path.toString()); }
318 
319 	/** Sets the list of trusted certificates for verifying peer certificates.
320 
321 		If this is a server context, this also entails that the given
322 		certificates are advertised to connecting clients during handshake.
323 
324 		On Linux, the system's root certificate authority list is usually
325 		found at "/etc/ssl/certs/ca-certificates.crt",
326 		"/etc/pki/tls/certs/ca-bundle.crt", or "/etc/ssl/ca-bundle.pem".
327 	*/
328 	void useTrustedCertificateFile(string path);
329 }
330 
331 enum TLSContextKind {
332 	client,     /// Client context (active connector)
333 	server,     /// Server context (passive connector)
334 	serverSNI,  /// Server context with multiple certificate support (SNI)
335 }
336 
337 enum TLSVersion {
338 	any, /// Accept TLSv1.0 and greater
339 	ssl3, /// Accept only SSLv3 (not supported anymore)
340 	tls1, /// Accept only TLSv1.0
341 	tls1_1, /// Accept only TLSv1.1
342 	tls1_2, /// Accept only TLSv1.2
343 	dtls1, /// Use DTLSv1.0
344 
345 	ssl23 = any /// Deprecated compatibility alias
346 }
347 
348 
349 /** Specifies how rigorously TLS peer certificates are validated.
350 
351 	The individual options can be combined using a bitwise "or". Usually it is
352 	recommended to use $(D trustedCert) for full validation.
353 */
354 enum TLSPeerValidationMode {
355 	/** Accept any peer regardless if and which certificate is presented.
356 
357 		This mode is generally discouraged and should only be used with
358 		a custom validation callback set to do the verification.
359 	*/
360 	none = 0,
361 
362 	/** Require the peer to always present a certificate.
363 
364 		Note that this option alone does not verify the certificate at all. It
365 		can be used together with the "check" options, or by using a custom
366 		validation callback to actually validate certificates.
367 	*/
368 	requireCert = 1<<0,
369 
370 	/** Check the certificate for basic validity.
371 
372 		This verifies the validity of the certificate chain and some other
373 		general properties, such as expiration time. It doesn't verify
374 		either the peer name or the trust state of the certificate.
375 	*/
376 	checkCert = 1<<1,
377 
378 	/** Validate the actual peer name/address against the certificate.
379 
380 		Compares the name/address of the connected peer, as passed to
381 		$(D createTLSStream) to the list of patterns present in the
382 		certificate, if any. If no match is found, the connection is
383 		rejected.
384 	*/
385 	checkPeer = 1<<2,
386 
387 	/** Requires that the certificate or any parent certificate is trusted.
388 
389 		Searches list of trusted certificates for a match of the certificate
390 		chain. If no match is found, the connection is rejected.
391 
392 		See_also: $(D useTrustedCertificateFile)
393 	*/
394 	checkTrust = 1<<3,
395 
396 	/** Require a valid certificate matching the peer name.
397 
398 		In this mode, the certificate is validated for general consistency and
399 		possible expiration, and the peer name is checked to see if the
400 		certificate actually applies.
401 
402 		However, the certificate chain is not matched against the system's
403 		pool of trusted certificate authorities, so a custom validation
404 		callback is still needed to get a secure validation process.
405 
406 		This option is a combination $(D requireCert), $(D checkCert) and
407 		$(D checkPeer).
408 	*/
409 	validCert = requireCert | checkCert | checkPeer,
410 
411 	/** Require a valid and trusted certificate (strongly recommended).
412 
413 		Checks the certificate and peer name for validity and requires that
414 		the certificate chain originates from a trusted CA (based on the
415 		registered pool of certificate authorities).
416 
417 		This option is a combination $(D validCert) and $(D checkTrust).
418 
419 		See_also: $(D useTrustedCertificateFile)
420 	*/
421 	trustedCert = validCert | checkTrust,
422 }
423 
424 /** Certificate information  */
425 struct TLSCertificateInformation {
426 
427 	/** Information about the certificate's subject name.
428 
429 		Maps fields to their values. For example, typical fields on a
430 		certificate will be 'commonName', 'countryName', 'emailAddress', etc.
431 	*/
432 	DictionaryList!(string, false, 8) subjectName;
433 
434 	/** Vendor specific representation of the peer certificate.
435 
436 		This field is only set if the functionality is supported and if the
437 		peer certificate is a X509 certificate.
438 
439 		For the OpenSSL driver, this will point to an `X509` struct. Note
440 		that the life time of the object is limited to the life time of the
441 		TLS stream.
442 	*/
443 	void* _x509;
444 }
445 
446 struct TLSPeerValidationData {
447 	char[] certName;
448 	string errorString;
449 	// certificate chain
450 	// public key
451 	// public key fingerprint
452 }
453 
454 alias TLSPeerValidationCallback = bool delegate(scope TLSPeerValidationData data);
455 
456 alias TLSServerNameCallback = TLSContext delegate(string hostname);
457 alias TLSALPNCallback = string delegate(string[] alpn_choices);
458 
459 private {
460 	__gshared TLSContext function(TLSContextKind, TLSVersion) gs_sslContextFactory;
461 }