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