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