summaryrefslogtreecommitdiffstats
path: root/qmail-smtpd.c
diff options
context:
space:
mode:
authormanuel <manuel@mausz.at>2023-08-08 23:37:39 +0200
committermanuel <manuel@mausz.at>2023-08-08 23:37:39 +0200
commit2ae3dd41c6d46d80574c59eec9689c16093edf2b (patch)
tree716a3cab8fffbf19db48e95b0c4d187c17b09d11 /qmail-smtpd.c
parent718ae19d5571f8d0132575b5aea4684dc5f40054 (diff)
downloadqmail-2ae3dd41c6d46d80574c59eec9689c16093edf2b.tar.gz
qmail-2ae3dd41c6d46d80574c59eec9689c16093edf2b.tar.bz2
qmail-2ae3dd41c6d46d80574c59eec9689c16093edf2b.zip
Make qmail openssl 3.0 compatible
- remove support for loading custom DH params from pem. use opensslconf if really required - remove support for loading custom ec group from params - reimplement DANE support using openssl DANE functions
Diffstat (limited to 'qmail-smtpd.c')
-rw-r--r--qmail-smtpd.c213
1 files changed, 22 insertions, 191 deletions
diff --git a/qmail-smtpd.c b/qmail-smtpd.c
index 5ea23dc..0d3b16d 100644
--- a/qmail-smtpd.c
+++ b/qmail-smtpd.c
@@ -67,8 +67,6 @@ static const stralloc *client_get_session_id();
67 67
68#ifdef TLS 68#ifdef TLS
69# include <sys/stat.h> 69# include <sys/stat.h>
70# include <openssl/bn.h>
71# include <openssl/dh.h>
72# include "tls.h" 70# include "tls.h"
73# include "ssl_timeoutio.h" 71# include "ssl_timeoutio.h"
74 72
@@ -208,7 +206,7 @@ int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\
208void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); } 206void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); }
209void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); do_hard_errors(); } 207void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); do_hard_errors(); }
210int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); do_hard_errors(); return -1; } 208int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); do_hard_errors(); return -1; }
211int err_noauth2() { out("503 auth not available (#5.3.3)\r\n"); do_hard_errors(); } 209int err_noauth2() { out("503 auth not available (#5.3.3)\r\n"); do_hard_errors(); return -1; }
212int err_authabrt() { out("501 auth exchange canceled (#5.0.0)\r\n"); return -1; } 210int err_authabrt() { out("501 auth exchange canceled (#5.0.0)\r\n"); return -1; }
213int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; } 211int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; }
214int err_wantstarttls() { out("530 Must issue a STARTTLS command first (#5.7.0)\r\n"); return -1; }; 212int err_wantstarttls() { out("530 Must issue a STARTTLS command first (#5.7.0)\r\n"); return -1; };
@@ -386,7 +384,7 @@ char *arg;
386 if (!stralloc_copys(&addr,"")) die_nomem(); 384 if (!stralloc_copys(&addr,"")) die_nomem();
387 flagesc = 0; 385 flagesc = 0;
388 flagquoted = 0; 386 flagquoted = 0;
389 for (i = 0;ch = arg[i];++i) { /* copy arg to addr, stripping quotes */ 387 for (i = 0; (ch = arg[i]); ++i) { /* copy arg to addr, stripping quotes */
390 if (flagesc) { 388 if (flagesc) {
391 if (!stralloc_append(&addr,&ch)) die_nomem(); 389 if (!stralloc_append(&addr,&ch)) die_nomem();
392 flagesc = 0; 390 flagesc = 0;
@@ -485,7 +483,7 @@ int bmcheck(which) int which;
485 } 483 }
486 if ((negate) && (x == 0)) { 484 if ((negate) && (x == 0)) {
487 if (!stralloc_copyb(&matchedregex,bmb.s + j - 1,(i - j + 1))) die_nomem(); 485 if (!stralloc_copyb(&matchedregex,bmb.s + j - 1,(i - j + 1))) die_nomem();
488 if (!stralloc_0(&matchedregex)) die_nomem(); 486 if (!stralloc_0(&matchedregex)) die_nomem();
489 return 1; 487 return 1;
490 } 488 }
491 if (!(negate) && (x > 0)) { 489 if (!(negate) && (x > 0)) {
@@ -582,8 +580,6 @@ void mailfrom_auth(arg,len)
582char *arg; 580char *arg;
583int len; 581int len;
584{ 582{
585 int j;
586
587 if (!stralloc_copys(&fuser,"")) die_nomem(); 583 if (!stralloc_copys(&fuser,"")) die_nomem();
588 if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); } 584 if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); }
589 else 585 else
@@ -610,7 +606,7 @@ void mailfrom_parms(arg) char *arg;
610 int len; 606 int len;
611 607
612 len = str_len(arg); 608 len = str_len(arg);
613 if (!stralloc_copys(&mfparms,"")) die_nomem; 609 if (!stralloc_copys(&mfparms,"")) die_nomem();
614 i = byte_chr(arg,len,'>'); 610 i = byte_chr(arg,len,'>');
615 if (i > 4 && i < len) { 611 if (i > 4 && i < len) {
616 while (len) { 612 while (len) {
@@ -622,10 +618,10 @@ void mailfrom_parms(arg) char *arg;
622 if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; } 618 if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; }
623 /* we do not support this */ 619 /* we do not support this */
624 //if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5); 620 //if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5);
625 if (!stralloc_copys(&mfparms,"")) die_nomem; 621 if (!stralloc_copys(&mfparms,"")) die_nomem();
626 } 622 }
627 else 623 else
628 if (!stralloc_catb(&mfparms,arg,1)) die_nomem; 624 if (!stralloc_catb(&mfparms,arg,1)) die_nomem();
629 } 625 }
630 } 626 }
631} 627}
@@ -1094,17 +1090,17 @@ int authenticate(void)
1094 if (authout.len > 0) { 1090 if (authout.len > 0) {
1095 len = authout.len; 1091 len = authout.len;
1096 arg = authout.s; 1092 arg = authout.s;
1097 if (!stralloc_copys(&authparams, "")) die_nomem; 1093 if (!stralloc_copys(&authparams, "")) die_nomem();
1098 while (len) { 1094 while (len) {
1099 if (*arg == '\0') { 1095 if (*arg == '\0') {
1100 if (case_starts(authparams.s, "USER=") && (authparams.len - 5) > 0) { 1096 if (case_starts(authparams.s, "USER=") && (authparams.len - 5) > 0) {
1101 if (!stralloc_copyb(&user, authparams.s + 5, authparams.len - 5)) die_nomem(); 1097 if (!stralloc_copyb(&user, authparams.s + 5, authparams.len - 5)) die_nomem();
1102 if (!stralloc_0(&user)) die_nomem(); 1098 if (!stralloc_0(&user)) die_nomem();
1103 } 1099 }
1104 if (!stralloc_copys(&authparams, "")) die_nomem; 1100 if (!stralloc_copys(&authparams, "")) die_nomem();
1105 } 1101 }
1106 else 1102 else
1107 if (!stralloc_catb(&authparams, arg, 1)) die_nomem; 1103 if (!stralloc_catb(&authparams, arg, 1)) die_nomem();
1108 arg++; 1104 arg++;
1109 len--; 1105 len--;
1110 } 1106 }
@@ -1130,19 +1126,19 @@ int auth_login(arg) char *arg;
1130 if (tls_before_auth && !ssl) return err_wantstarttls(); 1126 if (tls_before_auth && !ssl) return err_wantstarttls();
1131#endif 1127#endif
1132 if (*arg) { 1128 if (*arg) {
1133 if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input(); 1129 if ((r = b64decode(arg,str_len(arg),&user)) == 1) return err_input();
1134 } 1130 }
1135 else { 1131 else {
1136 out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */ 1132 out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */
1137 if (authgetl() < 0) return -1; 1133 if (authgetl() < 0) return -1;
1138 if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input(); 1134 if ((r = b64decode(authin.s,authin.len,&user)) == 1) return err_input();
1139 } 1135 }
1140 if (r == -1) die_nomem(); 1136 if (r == -1) die_nomem();
1141 1137
1142 out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */ 1138 out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */
1143 1139
1144 if (authgetl() < 0) return -1; 1140 if (authgetl() < 0) return -1;
1145 if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input(); 1141 if ((r = b64decode(authin.s,authin.len,&pass)) == 1) return err_input();
1146 if (r == -1) die_nomem(); 1142 if (r == -1) die_nomem();
1147 1143
1148 if (!user.len || !pass.len) return err_input(); 1144 if (!user.len || !pass.len) return err_input();
@@ -1157,12 +1153,12 @@ int auth_plain(arg) char *arg;
1157 if (tls_before_auth && !ssl) return err_wantstarttls(); 1153 if (tls_before_auth && !ssl) return err_wantstarttls();
1158#endif 1154#endif
1159 if (*arg) { 1155 if (*arg) {
1160 if (r = b64decode(arg,str_len(arg),&resp) == 1) return err_input(); 1156 if ((r = b64decode(arg,str_len(arg),&resp)) == 1) return err_input();
1161 } 1157 }
1162 else { 1158 else {
1163 out("334 \r\n"); flush(); 1159 out("334 \r\n"); flush();
1164 if (authgetl() < 0) return -1; 1160 if (authgetl() < 0) return -1;
1165 if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input(); 1161 if ((r = b64decode(authin.s,authin.len,&resp)) == 1) return err_input();
1166 } 1162 }
1167 if (r == -1 || !stralloc_0(&resp)) die_nomem(); 1163 if (r == -1 || !stralloc_0(&resp)) die_nomem();
1168 while (resp.s[id]) id++; /* "authorize-id\0userid\0passwd\0" */ 1164 while (resp.s[id]) id++; /* "authorize-id\0userid\0passwd\0" */
@@ -1297,140 +1293,6 @@ void smtp_tls(char *arg)
1297 else tls_init(); 1293 else tls_init();
1298} 1294}
1299 1295
1300/*
1301 * Grab well-defined DH parameters from OpenSSL, see the get_rfc*
1302 * functions in <openssl/bn.h> for all available primes.
1303 */
1304#if OPENSSL_VERSION_NUMBER < 0x10100005L
1305static int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
1306{
1307 /* q is optional */
1308 if (p == NULL || g == NULL)
1309 return 0;
1310 BN_free(dh->p);
1311 BN_free(dh->q);
1312 BN_free(dh->g);
1313 dh->p = p;
1314 dh->q = q;
1315 dh->g = g;
1316
1317 if (q != NULL)
1318 dh->length = BN_num_bits(q);
1319
1320 return 1;
1321}
1322#endif
1323
1324static DH *make_dh_params(BIGNUM *(*prime)(BIGNUM *))
1325{
1326 BIGNUM *p, *g;
1327 DH *dh = DH_new();
1328 if (!dh)
1329 return NULL;
1330
1331 p = prime(NULL);
1332 g = BN_new();
1333 if (g != NULL)
1334 BN_set_word(g, 2);
1335 if (!p || !g || !DH_set0_pqg(dh, p, NULL, g)) {
1336 DH_free(dh);
1337 BN_free(p);
1338 BN_free(g);
1339 return NULL;
1340 }
1341 return dh;
1342}
1343
1344/* Storage and initialization for DH parameters. */
1345static struct dhparam {
1346 BIGNUM *(*const prime)(BIGNUM *); /* function to generate... */
1347 DH *dh; /* ...this, used for keys.... */
1348 const unsigned int min; /* ...of length >= this. */
1349} dhparams[] = {
1350 { get_rfc3526_prime_8192, NULL, 6145 },
1351 { get_rfc3526_prime_6144, NULL, 4097 },
1352 { get_rfc3526_prime_4096, NULL, 3073 },
1353 { get_rfc3526_prime_3072, NULL, 2049 },
1354 { get_rfc3526_prime_2048, NULL, 1025 },
1355 { get_rfc2409_prime_1024, NULL, 0 }
1356};
1357
1358/* Hand out the same DH structure though once generated as we leak
1359 * memory otherwise and freeing the structure up after use would be
1360 * hard to track and in fact is not needed at all as it is safe to
1361 * use the same parameters over and over again security wise (in
1362 * contrast to the keys itself) and code safe as the returned structure
1363 * is duplicated by OpenSSL anyway. Hence no modification happens
1364 * to our copy. */
1365static DH *tmp_dh_cb(SSL *ssl, int export, int keylen)
1366{
1367 EVP_PKEY *pkey = SSL_get_privatekey(ssl);
1368 int type = pkey ? EVP_PKEY_base_id(pkey) : EVP_PKEY_NONE;
1369 unsigned n;
1370
1371 /*
1372 * OpenSSL will call us with either keylen == 512 or keylen == 1024
1373 * (see the definition of SSL_EXPORT_PKEYLENGTH in ssl_locl.h).
1374 * Adjust the DH parameter length according to the size of the
1375 * RSA/DSA private key used for the current connection, and always
1376 * use at least 1024-bit parameters.
1377 * Note: This may cause interoperability issues with implementations
1378 * which limit their DH support to 1024 bit - e.g. Java 7 and earlier.
1379 * In this case, SSLCertificateFile can be used to specify fixed
1380 * 1024-bit DH parameters (with the effect that OpenSSL skips this
1381 * callback).
1382 */
1383 if (type == EVP_PKEY_RSA || type == EVP_PKEY_DSA)
1384 keylen = EVP_PKEY_bits(pkey);
1385
1386 for (n = 0; n < sizeof(dhparams)/sizeof(dhparams[0]); n++) {
1387 if (keylen >= dhparams[n].min) {
1388 if (dhparams[n].dh == NULL)
1389 dhparams[n].dh = make_dh_params(dhparams[n].prime);
1390 return dhparams[n].dh;
1391 }
1392 }
1393
1394 return NULL; /* impossible to reach. */
1395}
1396
1397static DH *ssl_dh_GetParamFromFile(const char *file)
1398{
1399 DH *dh = NULL;
1400 BIO *bio;
1401
1402 if ((bio = BIO_new_file(file, "r")) == NULL)
1403 return NULL;
1404 dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
1405 BIO_free(bio);
1406 if (!dh)
1407 (void)ERR_get_error();
1408 return dh;
1409}
1410
1411#ifdef HAVE_ECC
1412static EC_GROUP *ssl_ec_GetParamFromFile(const char *file)
1413{
1414 EC_GROUP *group = NULL;
1415 BIO *bio;
1416
1417 if ((bio = BIO_new_file(file, "r")) == NULL)
1418 return NULL;
1419 group = PEM_read_bio_ECPKParameters(bio, NULL, NULL, NULL);
1420 BIO_free(bio);
1421 if (!group)
1422 (void)ERR_get_error();
1423 return group;
1424}
1425
1426#if !defined(SSL_set_ecdh_auto)
1427static EC_KEY *tmp_ecdh_cb(SSL *ssl, int export, int keylen)
1428{
1429 return EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
1430}
1431#endif
1432#endif
1433
1434/* don't want to fail handshake if cert isn't verifiable */ 1296/* don't want to fail handshake if cert isn't verifiable */
1435int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } 1297int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; }
1436 1298
@@ -1455,15 +1317,7 @@ void tls_init()
1455 SSL_CTX *ctx; 1317 SSL_CTX *ctx;
1456 const char *ciphers; 1318 const char *ciphers;
1457 stralloc saciphers = {0}; 1319 stralloc saciphers = {0};
1458 X509_STORE *store;
1459 X509_LOOKUP *lookup;
1460 const char *servercert, *servercert2; 1320 const char *servercert, *servercert2;
1461 DH *dhparams;
1462#ifdef HAVE_ECC
1463 EC_GROUP *ecparams;
1464 int nid;
1465 EC_KEY *eckey = NULL;
1466#endif
1467 struct stat st; 1321 struct stat st;
1468 1322
1469 /* if set, use servercert selected through SMTP_SERVERCERT env var */ 1323 /* if set, use servercert selected through SMTP_SERVERCERT env var */
@@ -1475,7 +1329,7 @@ void tls_init()
1475 SSL_library_init(); 1329 SSL_library_init();
1476 1330
1477 /* a new SSL context with the bare minimum of options */ 1331 /* a new SSL context with the bare minimum of options */
1478 ctx = SSL_CTX_new(SSLv23_server_method()); 1332 ctx = SSL_CTX_new(TLS_server_method());
1479 if (!ctx) { tls_err("unable to initialize ctx"); return; } 1333 if (!ctx) { tls_err("unable to initialize ctx"); return; }
1480 int min_ssl_version = (*childargs) ? TLS1_2_VERSION : TLS1_VERSION; 1334 int min_ssl_version = (*childargs) ? TLS1_2_VERSION : TLS1_VERSION;
1481 SSL_CTX_set_min_proto_version(ctx, min_ssl_version); 1335 SSL_CTX_set_min_proto_version(ctx, min_ssl_version);
@@ -1490,13 +1344,13 @@ void tls_init()
1490 1344
1491 /* this will also check whether public and private keys match */ 1345 /* this will also check whether public and private keys match */
1492 if (!SSL_CTX_use_PrivateKey_file(ctx, servercert, SSL_FILETYPE_PEM)) 1346 if (!SSL_CTX_use_PrivateKey_file(ctx, servercert, SSL_FILETYPE_PEM))
1493 { SSL_free(myssl); tls_err("no valid private key"); return; } 1347 { SSL_CTX_free(ctx); tls_err("no valid private key"); return; }
1494 1348
1495 if (stat(servercert2, &st) == 0) { 1349 if (stat(servercert2, &st) == 0) {
1496 if (!SSL_CTX_use_certificate_chain_file(ctx, servercert2)) 1350 if (!SSL_CTX_use_certificate_chain_file(ctx, servercert2))
1497 { SSL_CTX_free(ctx); tls_err("missing alternate certificate"); return; } 1351 { SSL_CTX_free(ctx); tls_err("invalid alternate certificate"); return; }
1498 if (!SSL_CTX_use_PrivateKey_file(ctx, servercert2, SSL_FILETYPE_PEM)) 1352 if (!SSL_CTX_use_PrivateKey_file(ctx, servercert2, SSL_FILETYPE_PEM))
1499 { SSL_free(myssl); tls_err("no valid alternate private key"); return; } 1353 { SSL_CTX_free(ctx); tls_err("no valid alternate private key"); return; }
1500 } 1354 }
1501 1355
1502 /* a new SSL object, with the rest added to it directly to avoid copying */ 1356 /* a new SSL object, with the rest added to it directly to avoid copying */
@@ -1519,39 +1373,17 @@ void tls_init()
1519 SSL_set_cipher_list(myssl, ciphers); 1373 SSL_set_cipher_list(myssl, ciphers);
1520 alloc_free(saciphers.s); 1374 alloc_free(saciphers.s);
1521 1375
1522 //TODO: we shouldn't use hardcoded DH: see https://weakdh.org/ 1376 SSL_set_dh_auto(myssl, 1);
1523 SSL_set_tmp_dh_callback(myssl, tmp_dh_cb); 1377 SSL_set_ecdh_auto(myssl, 1);
1524
1525 /* try to read DH parameters from certificate */
1526 if ((dhparams = ssl_dh_GetParamFromFile(servercert)))
1527 SSL_set_tmp_dh(myssl, dhparams);
1528
1529#ifdef HAVE_ECC
1530 /* similarly, try to read the ECDH curve name from certificate... */
1531 if ((ecparams = ssl_ec_GetParamFromFile(servercert)) &&
1532 (nid = EC_GROUP_get_curve_name(ecparams)) &&
1533 (eckey = EC_KEY_new_by_curve_name(nid))) {
1534 SSL_set_tmp_ecdh(myssl, eckey);
1535 }
1536 else {
1537#if defined(SSL_set_ecdh_auto)
1538 /* ...otherwise, enable auto curve selection (OpenSSL 1.0.2 and later) */
1539 SSL_set_ecdh_auto(myssl, 1);
1540#else
1541 /* or set callback, which will configure NIST P-256 */
1542 SSL_set_tmp_ecdh_callback(myssl, tmp_ecdh_cb);
1543#endif
1544 }
1545 EC_KEY_free(eckey);
1546#endif
1547 1378
1548#if OPENSSL_VERSION_NUMBER >= 0x10100005L
1549 stralloc opensslconf = {0}; 1379 stralloc opensslconf = {0};
1550 if (control_readfile(&opensslconf, "control/opensslconf", 0) == -1) 1380 if (control_readfile(&opensslconf, "control/opensslconf", 0) == -1)
1551 { SSL_free(myssl); die_control(); } 1381 { SSL_free(myssl); die_control(); }
1552 if (opensslconf.len) { 1382 if (opensslconf.len) {
1553 SSL_CONF_CTX *cctx = SSL_CONF_CTX_new(); 1383 SSL_CONF_CTX *cctx = SSL_CONF_CTX_new();
1554 SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE); 1384 SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE);
1385 /* client + server so we can share one single file */
1386 SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT);
1555 SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SERVER); 1387 SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SERVER);
1556 SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CERTIFICATE); 1388 SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CERTIFICATE);
1557 SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SHOW_ERRORS); 1389 SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SHOW_ERRORS);
@@ -1579,7 +1411,6 @@ void tls_init()
1579 1411
1580 (void)SSL_CONF_CTX_finish(cctx); 1412 (void)SSL_CONF_CTX_finish(cctx);
1581 } 1413 }
1582#endif
1583 1414
1584 SSL_set_rfd(myssl, ssl_rfd = substdio_fileno(&ssin)); 1415 SSL_set_rfd(myssl, ssl_rfd = substdio_fileno(&ssin));
1585 SSL_set_wfd(myssl, ssl_wfd = substdio_fileno(&ssout)); 1416 SSL_set_wfd(myssl, ssl_wfd = substdio_fileno(&ssout));