xref: /netbsd-src/external/ibm-public/postfix/dist/src/tls/tls_dh.c (revision 4d342c046e3288fb5a1edcd33cfec48c41c80664)
1 /*	$NetBSD: tls_dh.c,v 1.3 2020/03/18 19:05:21 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	tls_dh
6 /* SUMMARY
7 /*	Diffie-Hellman parameter support
8 /* SYNOPSIS
9 /*	#define TLS_INTERNAL
10 /*	#include <tls.h>
11 /*
12 /*	void	tls_set_dh_from_file(path, bits)
13 /*	const char *path;
14 /*	int	bits;
15 /*
16 /*	void	tls_auto_eecdh_curves(ctx, configured)
17 /*	SSL_CTX	*ctx;
18 /*	char	*configured;
19 /*
20 /*	void	tls_set_eecdh_curve(server_ctx, grade)
21 /*	SSL_CTX	*server_ctx;
22 /*	const char *grade;
23 /*
24 /*	DH	*tls_tmp_dh_cb(ssl, export, keylength)
25 /*	SSL	*ssl; /* unused */
26 /*	int	export;
27 /*	int	keylength;
28 /* DESCRIPTION
29 /*	This module maintains parameters for Diffie-Hellman key generation.
30 /*
31 /*	tls_tmp_dh_cb() is a call-back routine for the
32 /*	SSL_CTX_set_tmp_dh_callback() function.
33 /*
34 /*	tls_set_dh_from_file() overrides compiled-in DH parameters
35 /*	with those specified in the named files. The file format
36 /*	is as expected by the PEM_read_DHparams() routine. The
37 /*	"bits" argument must be 512 or 1024.
38 /*
39 /*	tls_auto_eecdh_curves() enables negotiation of the most preferred curve
40 /*	among the curves specified by the "configured" argument.
41 /*
42 /*	tls_set_eecdh_curve() enables ephemeral Elliptic-Curve DH
43 /*	key exchange algorithms by instantiating in the server SSL
44 /*	context a suitable curve (corresponding to the specified
45 /*	EECDH security grade) from the set of named curves in RFC
46 /*	4492 Section 5.1.1. Errors generate warnings, but do not
47 /*	disable TLS, rather we continue without EECDH. A zero
48 /*	result indicates that the grade is invalid or the corresponding
49 /*	curve could not be used.  The "auto" grade enables multiple
50 /*	curves, with the actual curve chosen as the most preferred
51 /*	among those supported by both the server and the client.
52 /* DIAGNOSTICS
53 /*	In case of error, tls_set_dh_from_file() logs a warning and
54 /*	ignores the request.
55 /* LICENSE
56 /* .ad
57 /* .fi
58 /*	This software is free. You can do with it whatever you want.
59 /*	The original author kindly requests that you acknowledge
60 /*	the use of his software.
61 /* AUTHOR(S)
62 /*	Originally written by:
63 /*	Lutz Jaenicke
64 /*	BTU Cottbus
65 /*	Allgemeine Elektrotechnik
66 /*	Universitaetsplatz 3-4
67 /*	D-03044 Cottbus, Germany
68 /*
69 /*	Updated by:
70 /*	Wietse Venema
71 /*	IBM T.J. Watson Research
72 /*	P.O. Box 704
73 /*	Yorktown Heights, NY 10598, USA
74 /*--*/
75 
76 /* System library. */
77 
78 #include <sys_defs.h>
79 
80 #ifdef USE_TLS
81 #include <stdio.h>
82 
83 /* Utility library. */
84 
85 #include <msg.h>
86 #include <mymalloc.h>
87 #include <stringops.h>
88 
89  /*
90   * Global library
91   */
92 #include <mail_params.h>
93 
94 /* TLS library. */
95 
96 #define TLS_INTERNAL
97 #include <tls.h>
98 #include <openssl/dh.h>
99 #ifndef OPENSSL_NO_ECDH
100 #include <openssl/ec.h>
101 #endif
102 
103 /* Application-specific. */
104 
105  /*
106   * Compiled-in DH parameters.  Used when no parameters are explicitly loaded
107   * from a site-specific file.  Using an ASN.1 DER encoding avoids the need
108   * to explicitly manipulate the internal representation of DH parameter
109   * objects.
110   *
111   * 512-bit parameters are used for export ciphers, and 2048-bit parameters are
112   * used for non-export ciphers.  The non-export group is now 2048-bit, as
113   * 1024 bits is increasingly considered to weak by clients.  When greater
114   * security is required, use EECDH.
115   */
116 
117  /*-
118   * Generated via:
119   *   $ openssl dhparam -2 -outform DER 512 2>/dev/null |
120   *     hexdump -ve '/1 "0x%02x, "' | fmt
121   * TODO: generate at compile-time. But that is no good for the majority of
122   * sites that install pre-compiled binaries, and breaks reproducible builds.
123   * Instead, generate at installation time and use main.cf configuration.
124   */
125 static unsigned char dh512_der[] = {
126     0x30, 0x46, 0x02, 0x41, 0x00, 0xd8, 0xbf, 0x11, 0xd6, 0x41, 0x2a, 0x7a,
127     0x9c, 0x78, 0xb2, 0xaa, 0x41, 0x23, 0x0a, 0xdc, 0xcf, 0xb7, 0x19, 0xc5,
128     0x16, 0x4c, 0xcb, 0x4a, 0xd0, 0xd2, 0x1f, 0x1f, 0x70, 0x24, 0x86, 0x6f,
129     0x51, 0x52, 0xc6, 0x5b, 0x28, 0xbb, 0x82, 0xe1, 0x24, 0x91, 0x3d, 0x4d,
130     0x95, 0x56, 0xf8, 0x0b, 0x2c, 0xe0, 0x36, 0x67, 0x88, 0x64, 0x15, 0x1f,
131     0x45, 0xd5, 0xb8, 0x0a, 0x00, 0x03, 0x76, 0x32, 0x0b, 0x02, 0x01, 0x02,
132 };
133 
134  /*-
135   * Generated via:
136   *   $ openssl dhparam -2 -outform DER 2048 2>/dev/null |
137   *     hexdump -ve '/1 "0x%02x, "' | fmt
138   * TODO: generate at compile-time. But that is no good for the majority of
139   * sites that install pre-compiled binaries, and breaks reproducible builds.
140   * Instead, generate at installation time and use main.cf configuration.
141   */
142 static unsigned char dh2048_der[] = {
143     0x30, 0x82, 0x01, 0x08, 0x02, 0x82, 0x01, 0x01, 0x00, 0xbf, 0x28, 0x1b,
144     0x68, 0x69, 0x90, 0x2f, 0x37, 0x9f, 0x5a, 0x50, 0x23, 0x73, 0x2c, 0x11,
145     0xf2, 0xac, 0x7c, 0x3e, 0x58, 0xb9, 0x23, 0x3e, 0x02, 0x07, 0x4d, 0xba,
146     0xd9, 0x2c, 0xc1, 0x9e, 0xf9, 0xc4, 0x2f, 0xbc, 0x8d, 0x86, 0x4b, 0x2a,
147     0x87, 0x86, 0x93, 0x32, 0x0f, 0x72, 0x40, 0xfe, 0x7e, 0xa2, 0xc1, 0x32,
148     0xf0, 0x65, 0x9c, 0xc3, 0x19, 0x25, 0x2d, 0xeb, 0x6a, 0x49, 0x94, 0x79,
149     0x2d, 0xa1, 0xbe, 0x05, 0x26, 0xac, 0x8d, 0x69, 0xdc, 0x2e, 0x7e, 0xb5,
150     0xfd, 0x3c, 0x2b, 0x7d, 0x43, 0x22, 0x53, 0xf6, 0x1e, 0x04, 0x45, 0xd7,
151     0x53, 0x84, 0xfd, 0x6b, 0x12, 0x72, 0x47, 0x04, 0xaf, 0xa4, 0xac, 0x4b,
152     0x55, 0xb6, 0x79, 0x42, 0x40, 0x88, 0x54, 0x48, 0xd5, 0x4d, 0x3a, 0xb2,
153     0xbf, 0x6c, 0x26, 0x95, 0x29, 0xdd, 0x8b, 0x9e, 0xed, 0xb8, 0x60, 0x8e,
154     0xb5, 0x35, 0xb6, 0x22, 0x44, 0x1f, 0xfb, 0x56, 0x74, 0xfe, 0xf0, 0x2c,
155     0xe6, 0x0c, 0x22, 0xc9, 0x35, 0xb3, 0x1b, 0x96, 0xbb, 0x0a, 0x5a, 0xc3,
156     0x09, 0xa0, 0xcc, 0xa5, 0x40, 0x90, 0x0f, 0x59, 0xa2, 0x89, 0x69, 0x2a,
157     0x69, 0x79, 0xe4, 0xd3, 0x24, 0xc6, 0x8c, 0xda, 0xbc, 0x98, 0x3a, 0x5b,
158     0x16, 0xae, 0x63, 0x6c, 0x0b, 0x43, 0x4f, 0xf3, 0x2e, 0xc8, 0xa9, 0x6b,
159     0x58, 0x6a, 0xa9, 0x8e, 0x64, 0x09, 0x3d, 0x88, 0x44, 0x4f, 0x97, 0x2c,
160     0x1d, 0x98, 0xb0, 0xa9, 0xc0, 0xb6, 0x8d, 0x19, 0x37, 0x1f, 0xb7, 0xc9,
161     0x86, 0xa8, 0xdc, 0x37, 0x4d, 0x64, 0x27, 0xf3, 0xf5, 0x2b, 0x7b, 0x6b,
162     0x76, 0x84, 0x3f, 0xc1, 0x23, 0x97, 0x2d, 0x71, 0xf7, 0xb6, 0xc2, 0x35,
163     0x28, 0x10, 0x96, 0xd6, 0x69, 0x0c, 0x2e, 0x1f, 0x9f, 0xdf, 0x82, 0x81,
164     0x57, 0x57, 0x39, 0xa5, 0xf2, 0x81, 0x29, 0x57, 0xf9, 0x2f, 0xd0, 0x03,
165     0xab, 0x02, 0x01, 0x02,
166 };
167 
168  /*
169   * Cached results.
170   */
171 static DH *dh_1024 = 0;
172 static DH *dh_512 = 0;
173 
174 /* tls_set_dh_from_file - set Diffie-Hellman parameters from file */
175 
176 void    tls_set_dh_from_file(const char *path, int bits)
177 {
178     FILE   *paramfile;
179     DH    **dhPtr;
180 
181     switch (bits) {
182     case 512:
183 	dhPtr = &dh_512;
184 	break;
185     case 1024:
186 	dhPtr = &dh_1024;
187 	break;
188     default:
189 	msg_panic("Invalid DH parameters size %d, file %s", bits, path);
190     }
191 
192     /*
193      * This function is the first to set the DH parameters, but free any
194      * prior value just in case the call sequence changes some day.
195      */
196     if (*dhPtr) {
197 	DH_free(*dhPtr);
198 	*dhPtr = 0;
199     }
200     if ((paramfile = fopen(path, "r")) != 0) {
201 	if ((*dhPtr = PEM_read_DHparams(paramfile, 0, 0, 0)) == 0) {
202 	    msg_warn("cannot load %d-bit DH parameters from file %s"
203 		     " -- using compiled-in defaults", bits, path);
204 	    tls_print_errors();
205 	}
206 	(void) fclose(paramfile);		/* 200411 */
207     } else {
208 	msg_warn("cannot load %d-bit DH parameters from file %s: %m"
209 		 " -- using compiled-in defaults", bits, path);
210     }
211 }
212 
213 /* tls_get_dh - get compiled-in DH parameters */
214 
215 static DH *tls_get_dh(const unsigned char *p, size_t plen)
216 {
217     const unsigned char *endp = p;
218     DH     *dh = 0;
219 
220     if (d2i_DHparams(&dh, &endp, plen) && plen == endp - p)
221 	return (dh);
222 
223     msg_warn("cannot load compiled-in DH parameters");
224     if (dh)
225 	DH_free(dh);
226     return (0);
227 }
228 
229 /* tls_tmp_dh_cb - call-back for Diffie-Hellman parameters */
230 
231 DH     *tls_tmp_dh_cb(SSL *unused_ssl, int export, int keylength)
232 {
233     DH     *dh_tmp;
234 
235     if (export && keylength == 512) {		/* 40-bit export cipher */
236 	if (dh_512 == 0)
237 	    dh_512 = tls_get_dh(dh512_der, sizeof(dh512_der));
238 	dh_tmp = dh_512;
239     } else {					/* ADH, DHE-RSA or DSA */
240 	if (dh_1024 == 0)
241 	    dh_1024 = tls_get_dh(dh2048_der, sizeof(dh2048_der));
242 	dh_tmp = dh_1024;
243     }
244     return (dh_tmp);
245 }
246 
247 void    tls_auto_eecdh_curves(SSL_CTX *ctx, const char *configured)
248 {
249 #ifndef OPENSSL_NO_ECDH
250     SSL_CTX *tmpctx;
251     int    *nids;
252     int     space = 5;
253     int     n = 0;
254     int     unknown = 0;
255     char   *save;
256     char   *curves;
257     char   *curve;
258 
259     if ((tmpctx = SSL_CTX_new(TLS_method())) == 0) {
260 	msg_warn("cannot allocate temp SSL_CTX, using default ECDHE curves");
261 	tls_print_errors();
262 	return;
263     }
264     nids = mymalloc(space * sizeof(int));
265     curves = save = mystrdup(configured);
266 #define RETURN do { \
267 	myfree(save); \
268 	myfree(nids); \
269 	SSL_CTX_free(tmpctx); \
270 	return; \
271     } while (0)
272 
273     while ((curve = mystrtok(&curves, CHARS_COMMA_SP)) != 0) {
274 	int     nid = EC_curve_nist2nid(curve);
275 
276 	if (nid == NID_undef)
277 	    nid = OBJ_sn2nid(curve);
278 	if (nid == NID_undef)
279 	    nid = OBJ_ln2nid(curve);
280 	if (nid == NID_undef) {
281 	    msg_warn("ignoring unknown ECDHE curve \"%s\"",
282 		     curve);
283 	    continue;
284 	}
285 
286 	/*
287 	 * Validate the NID by trying it as the sole EC curve for a
288 	 * throw-away SSL context.  Silently skip unsupported code points.
289 	 * This way, we can list X25519 and X448 as soon as the nids are
290 	 * assigned, and before the supporting code is implemented.  They'll
291 	 * be silently skipped when not yet supported.
292 	 */
293 	if (SSL_CTX_set1_curves(tmpctx, &nid, 1) <= 0) {
294 	    ++unknown;
295 	    continue;
296 	}
297 	if (++n > space) {
298 	    space *= 2;
299 	    nids = myrealloc(nids, space * sizeof(int));
300 	}
301 	nids[n - 1] = nid;
302     }
303 
304     if (n == 0) {
305 	if (unknown > 0)
306 	    msg_warn("none of the configured ECDHE curves are supported");
307 	RETURN;
308     }
309     if (SSL_CTX_set1_curves(ctx, nids, n) <= 0) {
310 	msg_warn("failed to configure ECDHE curves");
311 	tls_print_errors();
312 	RETURN;
313     }
314 
315     /*
316      * This is a NOP in OpenSSL 1.1.0 and later, where curves are always
317      * auto-negotiated.
318      */
319 #if OPENSSL_VERSION_NUMBER < 0x10100000UL
320     if (SSL_CTX_set_ecdh_auto(ctx, 1) <= 0) {
321 	msg_warn("failed to enable automatic ECDHE curve selection");
322 	tls_print_errors();
323 	RETURN;
324     }
325 #endif
326     RETURN;
327 #endif
328 }
329 
330 #define TLS_EECDH_INVALID	0
331 #define TLS_EECDH_NONE		1
332 #define TLS_EECDH_STRONG	2
333 #define TLS_EECDH_ULTRA		3
334 #define TLS_EECDH_AUTO		4
335 
336 void    tls_set_eecdh_curve(SSL_CTX *server_ctx, const char *grade)
337 {
338 #ifndef OPENSSL_NO_ECDH
339     int     g;
340     static NAME_CODE eecdh_table[] = {
341 	"none", TLS_EECDH_NONE,
342 	"strong", TLS_EECDH_STRONG,
343 	"ultra", TLS_EECDH_ULTRA,
344 	"auto", TLS_EECDH_AUTO,
345 	0, TLS_EECDH_INVALID,
346     };
347 
348     switch (g = name_code(eecdh_table, NAME_CODE_FLAG_NONE, grade)) {
349     default:
350 	msg_panic("Invalid eecdh grade code: %d", g);
351     case TLS_EECDH_INVALID:
352 	msg_warn("Invalid TLS eecdh grade \"%s\": EECDH disabled", grade);
353 	return;
354     case TLS_EECDH_STRONG:
355 	tls_auto_eecdh_curves(server_ctx, var_tls_eecdh_strong);
356 	return;
357     case TLS_EECDH_ULTRA:
358 	tls_auto_eecdh_curves(server_ctx, var_tls_eecdh_ultra);
359 	return;
360     case TLS_EECDH_NONE:
361 
362 	/*
363 	 * Pretend "none" is "auto", the former is no longer supported or
364 	 * wise
365 	 */
366 	msg_warn("The \"none\" eecdh grade is no longer supported, "
367 		 "using \"auto\" instead");
368     case TLS_EECDH_AUTO:
369 	tls_auto_eecdh_curves(server_ctx, var_tls_eecdh_auto);
370 	return;
371     }
372 #endif
373     return;
374 }
375 
376 #ifdef TEST
377 
378 int     main(int unused_argc, char **unused_argv)
379 {
380     tls_tmp_dh_cb(0, 1, 512);
381     tls_tmp_dh_cb(0, 1, 1024);
382     tls_tmp_dh_cb(0, 1, 2048);
383     tls_tmp_dh_cb(0, 0, 512);
384     return (0);
385 }
386 
387 #endif
388 
389 #endif
390