xref: /netbsd-src/external/ibm-public/postfix/dist/src/tls/tls_dh.c (revision aef5eb5f59cdfe8314f1b5f78ac04eb144e44010)
1 /*	$NetBSD: tls_dh.c,v 1.4 2022/10/08 16:12:50 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)
13 /*	const char *path;
14 /*
15 /*	void	tls_auto_eecdh_curves(ctx, configured)
16 /*	SSL_CTX	*ctx;
17 /*	char	*configured;
18 /*
19 /*	void	tls_tmp_dh(ctx, useauto)
20 /*	SSL_CTX *ctx;
21 /*	int	useauto;
22 /* DESCRIPTION
23 /*	This module maintains parameters for Diffie-Hellman key generation.
24 /*
25 /*	tls_tmp_dh() returns the configured or compiled-in FFDHE
26 /*	group parameters.
27 /*
28 /*	tls_set_dh_from_file() overrides compiled-in DH parameters
29 /*	with those specified in the named files. The file format
30 /*	is as expected by the PEM_read_DHparams() routine.
31 /*
32 /*	tls_auto_eecdh_curves() enables negotiation of the most preferred curve
33 /*	among the curves specified by the "configured" argument.  The useauto
34 /*	argument enables OpenSSL-builtin group selection in preference to our
35 /*	own compiled-in group.  This may interoperate better with overly strict
36 /*	peers that accept only "standard" groups (bogus threat model).
37 /* DIAGNOSTICS
38 /*	In case of error, tls_set_dh_from_file() logs a warning and
39 /*	ignores the request.
40 /* LICENSE
41 /* .ad
42 /* .fi
43 /*	This software is free. You can do with it whatever you want.
44 /*	The original author kindly requests that you acknowledge
45 /*	the use of his software.
46 /* AUTHOR(S)
47 /*	Originally written by:
48 /*	Lutz Jaenicke
49 /*	BTU Cottbus
50 /*	Allgemeine Elektrotechnik
51 /*	Universitaetsplatz 3-4
52 /*	D-03044 Cottbus, Germany
53 /*
54 /*	Updated by:
55 /*	Wietse Venema
56 /*	IBM T.J. Watson Research
57 /*	P.O. Box 704
58 /*	Yorktown Heights, NY 10598, USA
59 /*--*/
60 
61 /* System library. */
62 
63 #include <sys_defs.h>
64 
65 #ifdef USE_TLS
66 #include <stdio.h>
67 
68 /* Utility library. */
69 
70 #include <msg.h>
71 #include <mymalloc.h>
72 #include <stringops.h>
73 
74  /*
75   * Global library
76   */
77 #include <mail_params.h>
78 
79 /* TLS library. */
80 
81 #define TLS_INTERNAL
82 #include <tls.h>
83 #include <openssl/dh.h>
84 #ifndef OPENSSL_NO_ECDH
85 #include <openssl/ec.h>
86 #endif
87 #if OPENSSL_VERSION_PREREQ(3,0)
88 #include <openssl/decoder.h>
89 #endif
90 
91 /* Application-specific. */
92 
93  /*
94   * Compiled-in FFDHE (finite-field ephemeral Diffie-Hellman) parameters.
95   * Used when no parameters are explicitly loaded from a site-specific file.
96   *
97   * With OpenSSL 3.0 and later when no explicit parameter file is specified by
98   * the administrator (or the setting is "auto"), we delegate group selection
99   * to OpenSSL via SSL_CTX_set_dh_auto(3).
100   *
101   * Using an ASN.1 DER encoding avoids the need to explicitly manipulate the
102   * internal representation of DH parameter objects.
103   *
104   * The FFDHE group is now 2048-bit, as 1024 bits is increasingly considered to
105   * weak by clients.  When greater security is required, use EECDH.
106   */
107 
108  /*-
109   * Generated via:
110   *   $ openssl dhparam -2 -outform DER 2048 2>/dev/null |
111   *     hexdump -ve '/1 "0x%02x, "' | fmt -73
112   * TODO: generate at compile-time. But that is no good for the majority of
113   * sites that install pre-compiled binaries, and breaks reproducible builds.
114   * Instead, generate at installation time and use main.cf configuration.
115   */
116 static unsigned char builtin_der[] = {
117     0x30, 0x82, 0x01, 0x08, 0x02, 0x82, 0x01, 0x01, 0x00, 0xec, 0x02, 0x7b,
118     0x74, 0xc6, 0xd4, 0xb4, 0x89, 0x68, 0xfd, 0xbc, 0xe0, 0x82, 0xae, 0xd6,
119     0xf1, 0x4d, 0x93, 0xaa, 0x47, 0x07, 0x84, 0x3d, 0x86, 0xf8, 0x47, 0xf7,
120     0xdf, 0x08, 0x7b, 0xca, 0x04, 0xa4, 0x72, 0xec, 0x11, 0xe2, 0x38, 0x43,
121     0xb7, 0x94, 0xab, 0xaf, 0xe2, 0x85, 0x59, 0x43, 0x4e, 0x71, 0x85, 0xfe,
122     0x52, 0x0c, 0xe0, 0x1c, 0xb6, 0xc7, 0xb0, 0x1b, 0x06, 0xb3, 0x4d, 0x1b,
123     0x4f, 0xf6, 0x4b, 0x45, 0xbd, 0x1d, 0xb8, 0xe4, 0xa4, 0x48, 0x09, 0x28,
124     0x19, 0xd7, 0xce, 0xb1, 0xe5, 0x9a, 0xc4, 0x94, 0x55, 0xde, 0x4d, 0x86,
125     0x0f, 0x4c, 0x5e, 0x25, 0x51, 0x6c, 0x96, 0xca, 0xfa, 0xe3, 0x01, 0x69,
126     0x82, 0x6c, 0x8f, 0xf5, 0xe7, 0x0e, 0xb7, 0x8e, 0x52, 0xf1, 0xcf, 0x0b,
127     0x67, 0x10, 0xd0, 0xb3, 0x77, 0x79, 0xa4, 0xc1, 0xd0, 0x0f, 0x3f, 0xf5,
128     0x5c, 0x35, 0xf9, 0x46, 0xd2, 0xc7, 0xfb, 0x97, 0x6d, 0xd5, 0xbe, 0xe4,
129     0x8b, 0x5a, 0xf2, 0x88, 0xfa, 0x47, 0xdc, 0xc2, 0x4a, 0x4d, 0x69, 0xd3,
130     0x2a, 0xdf, 0x55, 0x6c, 0x5f, 0x71, 0x11, 0x1e, 0x87, 0x03, 0x68, 0xe1,
131     0xf4, 0x21, 0x06, 0x63, 0xd9, 0x65, 0xd4, 0x0c, 0x4d, 0xa7, 0x1f, 0x15,
132     0x53, 0x3a, 0x50, 0x1a, 0xf5, 0x9b, 0x50, 0x35, 0xe0, 0x16, 0xa1, 0xd7,
133     0xe6, 0xbf, 0xd7, 0xd9, 0xd9, 0x53, 0xe5, 0x8b, 0xf8, 0x7b, 0x45, 0x46,
134     0xb6, 0xac, 0x50, 0x16, 0x46, 0x42, 0xca, 0x76, 0x38, 0x4b, 0x8e, 0x83,
135     0xc6, 0x73, 0x13, 0x9c, 0x03, 0xd1, 0x7a, 0x3d, 0x8d, 0x99, 0x34, 0x10,
136     0x79, 0x67, 0x21, 0x23, 0xf9, 0x6f, 0x48, 0x9a, 0xa6, 0xde, 0xbf, 0x7f,
137     0x9c, 0x16, 0x53, 0xff, 0xf7, 0x20, 0x96, 0xeb, 0x34, 0xcb, 0x5b, 0x85,
138     0x2b, 0x7c, 0x98, 0x00, 0x23, 0x47, 0xce, 0xc2, 0x58, 0x12, 0x86, 0x2c,
139     0x57, 0x02, 0x01, 0x02,
140 };
141 
142 #if OPENSSL_VERSION_PREREQ(3,0)
143 
144 /* ------------------------------------- 3.0 API */
145 
146 static EVP_PKEY *dhp = 0;
147 
148 /* load_builtin - load compile-time FFDHE group */
149 
150 static void load_builtin(void)
151 {
152     EVP_PKEY *tmp = 0;
153     OSSL_DECODER_CTX *d;
154     const unsigned char *endp = builtin_der;
155     size_t  dlen = sizeof(builtin_der);
156 
157     d = OSSL_DECODER_CTX_new_for_pkey(&tmp, "DER", NULL, "DH",
158 				      OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
159 				      NULL, NULL);
160     /* Check decode succeeds and consumes all data (final dlen == 0) */
161     if (d && OSSL_DECODER_from_data(d, &endp, &dlen) && tmp && !dlen) {
162 	dhp = tmp;
163     } else {
164 	EVP_PKEY_free(tmp);
165 	msg_warn("error loading compiled-in DH parameters");
166 	tls_print_errors();
167     }
168     OSSL_DECODER_CTX_free(d);
169 }
170 
171 /* tls_set_dh_from_file - set Diffie-Hellman parameters from file */
172 
173 void    tls_set_dh_from_file(const char *path)
174 {
175     FILE   *fp;
176     EVP_PKEY *tmp = 0;
177     OSSL_DECODER_CTX *d;
178 
179     /*
180      * This function is the first to set the DH parameters, but free any
181      * prior value just in case the call sequence changes some day.
182      */
183     if (dhp) {
184 	EVP_PKEY_free(dhp);
185 	dhp = 0;
186     }
187     if (strcmp(path, "auto") == 0)
188 	return;
189 
190     if ((fp = fopen(path, "r")) == 0) {
191 	msg_warn("error opening DH parameter file \"%s\": %m"
192 		 " -- using compiled-in defaults", path);
193 	return;
194     }
195     d = OSSL_DECODER_CTX_new_for_pkey(&tmp, "PEM", NULL, "DH",
196 				      OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
197 				      NULL, NULL);
198     if (!d || !OSSL_DECODER_from_fp(d, fp) || !tmp) {
199 	msg_warn("error decoding DH parameters from file \"%s\""
200 		 " -- using compiled-in defaults", path);
201 	tls_print_errors();
202     } else {
203 	dhp = tmp;
204     }
205     OSSL_DECODER_CTX_free(d);
206     (void) fclose(fp);
207 }
208 
209 /* tls_tmp_dh - configure FFDHE group */
210 
211 void    tls_tmp_dh(SSL_CTX *ctx, int useauto)
212 {
213     if (!dhp && !useauto)
214 	load_builtin();
215     if (!ctx)
216 	return;
217     if (dhp) {
218 	EVP_PKEY *tmp = EVP_PKEY_dup(dhp);
219 
220 	if (tmp && SSL_CTX_set0_tmp_dh_pkey(ctx, tmp) > 0)
221 	    return;
222 	EVP_PKEY_free(tmp);
223 	msg_warn("error configuring explicit DH parameters");
224 	tls_print_errors();
225     } else {
226 	if (SSL_CTX_set_dh_auto(ctx, 1) > 0)
227 	    return;
228 	msg_warn("error configuring auto DH parameters");
229 	tls_print_errors();
230     }
231 }
232 
233 #else					/* OPENSSL_VERSION_PREREQ(3,0) */
234 
235 /* ------------------------------------- 1.1.1 API */
236 
237 static DH *dhp = 0;
238 
239 static void load_builtin(void)
240 {
241     DH     *tmp = 0;
242     const unsigned char *endp = builtin_der;
243 
244     if (d2i_DHparams(&tmp, &endp, sizeof(builtin_der))
245 	&& sizeof(builtin_der) == endp - builtin_der) {
246 	dhp = tmp;
247     } else {
248 	DH_free(tmp);
249 	msg_warn("error loading compiled-in DH parameters");
250 	tls_print_errors();
251     }
252 }
253 
254 /* tls_set_dh_from_file - set Diffie-Hellman parameters from file */
255 
256 void    tls_set_dh_from_file(const char *path)
257 {
258     FILE   *fp;
259 
260     /*
261      * This function is the first to set the DH parameters, but free any
262      * prior value just in case the call sequence changes some day.
263      */
264     if (dhp) {
265 	DH_free(dhp);
266 	dhp = 0;
267     }
268 
269     /*
270      * Forwards compatibility, support "auto" by using the builtin group when
271      * OpenSSL is < 3.0 and does not support automatic FFDHE group selection.
272      */
273     if (strcmp(path, "auto") == 0)
274 	return;
275 
276     if ((fp = fopen(path, "r")) == 0) {
277 	msg_warn("cannot load DH parameters from file %s: %m"
278 		 " -- using compiled-in defaults", path);
279 	return;
280     }
281     if ((dhp = PEM_read_DHparams(fp, 0, 0, 0)) == 0) {
282 	msg_warn("cannot load DH parameters from file %s"
283 		 " -- using compiled-in defaults", path);
284 	tls_print_errors();
285     }
286     (void) fclose(fp);
287 }
288 
289 /* tls_tmp_dh - configure FFDHE group */
290 
291 void    tls_tmp_dh(SSL_CTX *ctx, int useauto)
292 {
293     if (!dhp)
294 	load_builtin();
295     if (!ctx || !dhp || SSL_CTX_set_tmp_dh(ctx, dhp) > 0)
296 	return;
297     msg_warn("error configuring explicit DH parameters");
298     tls_print_errors();
299 }
300 
301 #endif					/* OPENSSL_VERSION_PREREQ(3,0) */
302 
303 /* ------------------------------------- Common API */
304 
305 void    tls_auto_eecdh_curves(SSL_CTX *ctx, const char *configured)
306 {
307 #ifndef OPENSSL_NO_ECDH
308     SSL_CTX *tmpctx;
309     int    *nids;
310     int     space = 5;
311     int     n = 0;
312     int     unknown = 0;
313     char   *save;
314     char   *curves;
315     char   *curve;
316 
317     if ((tmpctx = SSL_CTX_new(TLS_method())) == 0) {
318 	msg_warn("cannot allocate temp SSL_CTX, using default ECDHE curves");
319 	tls_print_errors();
320 	return;
321     }
322     nids = mymalloc(space * sizeof(int));
323     curves = save = mystrdup(configured);
324 #define RETURN do { \
325 	myfree(save); \
326 	myfree(nids); \
327 	SSL_CTX_free(tmpctx); \
328 	return; \
329     } while (0)
330 
331     while ((curve = mystrtok(&curves, CHARS_COMMA_SP)) != 0) {
332 	int     nid = EC_curve_nist2nid(curve);
333 
334 	if (nid == NID_undef)
335 	    nid = OBJ_sn2nid(curve);
336 	if (nid == NID_undef)
337 	    nid = OBJ_ln2nid(curve);
338 	if (nid == NID_undef) {
339 	    msg_warn("ignoring unknown ECDHE curve \"%s\"",
340 		     curve);
341 	    continue;
342 	}
343 
344 	/*
345 	 * Validate the NID by trying it as the sole EC curve for a
346 	 * throw-away SSL context.  Silently skip unsupported code points.
347 	 * This way, we can list X25519 and X448 as soon as the nids are
348 	 * assigned, and before the supporting code is implemented.  They'll
349 	 * be silently skipped when not yet supported.
350 	 */
351 	if (SSL_CTX_set1_curves(tmpctx, &nid, 1) <= 0) {
352 	    ++unknown;
353 	    continue;
354 	}
355 	if (++n > space) {
356 	    space *= 2;
357 	    nids = myrealloc(nids, space * sizeof(int));
358 	}
359 	nids[n - 1] = nid;
360     }
361 
362     if (n == 0) {
363 	if (unknown > 0)
364 	    msg_warn("none of the configured ECDHE curves are supported");
365 	RETURN;
366     }
367     if (SSL_CTX_set1_curves(ctx, nids, n) <= 0) {
368 	msg_warn("failed to configure ECDHE curves");
369 	tls_print_errors();
370 	RETURN;
371     }
372     RETURN;
373 #endif
374 }
375 
376 #ifdef TEST
377 
378 int     main(int unused_argc, char **unused_argv)
379 {
380     tls_tmp_dh(0, 0);
381     return (dhp == 0);
382 }
383 
384 #endif
385 
386 #endif
387