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