summaryrefslogtreecommitdiffstats
path: root/qmail-smtpd.c
diff options
context:
space:
mode:
Diffstat (limited to 'qmail-smtpd.c')
-rw-r--r--qmail-smtpd.c177
1 files changed, 80 insertions, 97 deletions
diff --git a/qmail-smtpd.c b/qmail-smtpd.c
index 18795bc..f40a4c5 100644
--- a/qmail-smtpd.c
+++ b/qmail-smtpd.c
@@ -71,7 +71,6 @@ char *relayclient;
71# define SERVERCERT "control/servercert.pem" 71# define SERVERCERT "control/servercert.pem"
72 72
73void tls_init(); 73void tls_init();
74int tls_verify();
75void tls_nogateway(); 74void tls_nogateway();
76int ssl_rfd = -1, ssl_wfd = -1; /* SSL_get_Xfd() are broken */ 75int ssl_rfd = -1, ssl_wfd = -1; /* SSL_get_Xfd() are broken */
77stralloc proto = {0}; 76stralloc proto = {0};
@@ -508,9 +507,6 @@ int addrallowed()
508 int r; 507 int r;
509 r = rcpthosts(addr.s,str_len(addr.s)); 508 r = rcpthosts(addr.s,str_len(addr.s));
510 if (r == -1) die_control(); 509 if (r == -1) die_control();
511#ifdef TLS
512 if (r == 0) if (tls_verify()) r = -2;
513#endif
514 return r; 510 return r;
515} 511}
516 512
@@ -1261,19 +1257,44 @@ void smtp_tls(char *arg)
1261 * Grab well-defined DH parameters from OpenSSL, see the get_rfc* 1257 * Grab well-defined DH parameters from OpenSSL, see the get_rfc*
1262 * functions in <openssl/bn.h> for all available primes. 1258 * functions in <openssl/bn.h> for all available primes.
1263 */ 1259 */
1264static DH *make_dh_params(BIGNUM *(*prime)(BIGNUM *), const char *gen) 1260#if OPENSSL_VERSION_NUMBER < 0x10100005L
1261static int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
1265{ 1262{
1266 DH *dh = DH_new(); 1263 /* q is optional */
1267 if (!dh) 1264 if (p == NULL || g == NULL)
1268 return NULL; 1265 return 0;
1269 1266 BN_free(dh->p);
1270 dh->p = prime(NULL); 1267 BN_free(dh->q);
1271 BN_dec2bn(&dh->g, gen); 1268 BN_free(dh->g);
1272 if (!dh->p || !dh->g) { 1269 dh->p = p;
1273 DH_free(dh); 1270 dh->q = q;
1274 return NULL; 1271 dh->g = g;
1275 } 1272
1276 return dh; 1273 if (q != NULL)
1274 dh->length = BN_num_bits(q);
1275
1276 return 1;
1277}
1278#endif
1279
1280static DH *make_dh_params(BIGNUM *(*prime)(BIGNUM *))
1281{
1282 BIGNUM *p, *g;
1283 DH *dh = DH_new();
1284 if (!dh)
1285 return NULL;
1286
1287 p = prime(NULL);
1288 g = BN_new();
1289 if (g != NULL)
1290 BN_set_word(g, 2);
1291 if (!p || !g || !DH_set0_pqg(dh, p, NULL, g)) {
1292 DH_free(dh);
1293 BN_free(p);
1294 BN_free(g);
1295 return NULL;
1296 }
1297 return dh;
1277} 1298}
1278 1299
1279/* Storage and initialization for DH parameters. */ 1300/* Storage and initialization for DH parameters. */
@@ -1300,7 +1321,7 @@ static struct dhparam {
1300static DH *tmp_dh_cb(SSL *ssl, int export, int keylen) 1321static DH *tmp_dh_cb(SSL *ssl, int export, int keylen)
1301{ 1322{
1302 EVP_PKEY *pkey = SSL_get_privatekey(ssl); 1323 EVP_PKEY *pkey = SSL_get_privatekey(ssl);
1303 int type = pkey ? EVP_PKEY_type(pkey->type) : EVP_PKEY_NONE; 1324 int type = pkey ? EVP_PKEY_base_id(pkey) : EVP_PKEY_NONE;
1304 unsigned n; 1325 unsigned n;
1305 1326
1306 /* 1327 /*
@@ -1321,7 +1342,7 @@ static DH *tmp_dh_cb(SSL *ssl, int export, int keylen)
1321 for (n = 0; n < sizeof(dhparams)/sizeof(dhparams[0]); n++) { 1342 for (n = 0; n < sizeof(dhparams)/sizeof(dhparams[0]); n++) {
1322 if (keylen >= dhparams[n].min) { 1343 if (keylen >= dhparams[n].min) {
1323 if (dhparams[n].dh == NULL) 1344 if (dhparams[n].dh == NULL)
1324 dhparams[n].dh = make_dh_params(dhparams[n].prime, "2"); 1345 dhparams[n].dh = make_dh_params(dhparams[n].prime);
1325 return dhparams[n].dh; 1346 return dhparams[n].dh;
1326 } 1347 }
1327 } 1348 }
@@ -1338,6 +1359,8 @@ static DH *ssl_dh_GetParamFromFile(const char *file)
1338 return NULL; 1359 return NULL;
1339 dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); 1360 dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
1340 BIO_free(bio); 1361 BIO_free(bio);
1362 if (!dh)
1363 (void)ERR_get_error();
1341 return dh; 1364 return dh;
1342} 1365}
1343 1366
@@ -1351,6 +1374,8 @@ static EC_GROUP *ssl_ec_GetParamFromFile(const char *file)
1351 return NULL; 1374 return NULL;
1352 group = PEM_read_bio_ECPKParameters(bio, NULL, NULL, NULL); 1375 group = PEM_read_bio_ECPKParameters(bio, NULL, NULL, NULL);
1353 BIO_free(bio); 1376 BIO_free(bio);
1377 if (!group)
1378 (void)ERR_get_error();
1354 return group; 1379 return group;
1355} 1380}
1356 1381
@@ -1378,85 +1403,6 @@ void tls_out(const char *s1, const char *s2)
1378} 1403}
1379void tls_err(const char *s) { tls_out(s, ssl_error()); if (smtps) die_read(); } 1404void tls_err(const char *s) { tls_out(s, ssl_error()); if (smtps) die_read(); }
1380 1405
1381int tls_verify()
1382{
1383 stralloc clients = {0};
1384 struct constmap mapclients;
1385
1386 if (!ssl || relayclient || ssl_verified) return 0;
1387 ssl_verified = 1; /* don't do this twice */
1388
1389 /* request client cert to see if it can be verified by one of our CAs
1390 * and the associated email address matches an entry in tlsclients */
1391 switch (control_readfile(&clients, "control/tlsclients", 0))
1392 {
1393 case 1:
1394 if (constmap_init(&mapclients, clients.s, clients.len, 0)) {
1395 /* if CLIENTCA contains all the standard root certificates, a
1396 * 0.9.6b client might fail with SSL_R_EXCESSIVE_MESSAGE_SIZE;
1397 * it is probably due to 0.9.6b supporting only 8k key exchange
1398 * data while the 0.9.6c release increases that limit to 100k */
1399 STACK_OF(X509_NAME) *sk = SSL_load_client_CA_file(CLIENTCA);
1400 if (sk) {
1401 SSL_set_client_CA_list(ssl, sk);
1402 SSL_set_verify(ssl, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, NULL);
1403 break;
1404 }
1405 constmap_free(&mapclients);
1406 }
1407 case 0: alloc_free(clients.s); return 0;
1408 case -1: die_control();
1409 }
1410
1411 if (ssl_timeoutrehandshake(timeout, ssl_rfd, ssl_wfd, ssl) <= 0) {
1412 const char *err = ssl_error_str();
1413 tls_out("rehandshake failed", err); die_read();
1414 }
1415
1416 do { /* one iteration */
1417 X509 *peercert;
1418 X509_NAME *subj;
1419 stralloc email = {0};
1420
1421 int n = SSL_get_verify_result(ssl);
1422 if (n != X509_V_OK)
1423 { ssl_verify_err = X509_verify_cert_error_string(n); break; }
1424 peercert = SSL_get_peer_certificate(ssl);
1425 if (!peercert) break;
1426
1427 subj = X509_get_subject_name(peercert);
1428 n = X509_NAME_get_index_by_NID(subj, NID_pkcs9_emailAddress, -1);
1429 if (n >= 0) {
1430 const ASN1_STRING *s = X509_NAME_get_entry(subj, n)->value;
1431 if (s) { email.len = s->length; email.s = s->data; }
1432 }
1433
1434 if (email.len <= 0)
1435 ssl_verify_err = "contains no email address";
1436 else if (!constmap(&mapclients, email.s, email.len))
1437 ssl_verify_err = "email address not in my list of tlsclients";
1438 else {
1439 /* add the cert email to the proto if it helped allow relaying */
1440 --proto.len;
1441 if (!stralloc_cats(&proto, "\n (cert ") /* continuation line */
1442 || !stralloc_catb(&proto, email.s, email.len)
1443 || !stralloc_cats(&proto, ")")
1444 || !stralloc_0(&proto)) die_nomem();
1445 relayclient = "";
1446 protocol = proto.s;
1447 }
1448
1449 X509_free(peercert);
1450 } while (0);
1451 constmap_free(&mapclients); alloc_free(clients.s);
1452
1453 /* we are not going to need this anymore: free the memory */
1454 SSL_set_client_CA_list(ssl, NULL);
1455 SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
1456
1457 return relayclient ? 1 : 0;
1458}
1459
1460void tls_init() 1406void tls_init()
1461{ 1407{
1462 SSL *myssl; 1408 SSL *myssl;
@@ -1525,6 +1471,7 @@ void tls_init()
1525 SSL_set_cipher_list(myssl, ciphers); 1471 SSL_set_cipher_list(myssl, ciphers);
1526 alloc_free(saciphers.s); 1472 alloc_free(saciphers.s);
1527 1473
1474 //TODO: we shouldn't use hardcoded DH: see https://weakdh.org/
1528 SSL_set_tmp_dh_callback(myssl, tmp_dh_cb); 1475 SSL_set_tmp_dh_callback(myssl, tmp_dh_cb);
1529 1476
1530 /* try to read DH parameters from certificate */ 1477 /* try to read DH parameters from certificate */
@@ -1550,6 +1497,42 @@ void tls_init()
1550 EC_KEY_free(eckey); 1497 EC_KEY_free(eckey);
1551#endif 1498#endif
1552 1499
1500#if OPENSSL_VERSION_NUMBER >= 0x10100005L
1501 stralloc opensslconf = {0};
1502 if (control_readfile(&opensslconf, "control/opensslconf", 0) == -1)
1503 { SSL_free(myssl); die_control(); }
1504 if (opensslconf.len) {
1505 SSL_CONF_CTX *cctx = SSL_CONF_CTX_new();
1506 SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE);
1507 SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SERVER);
1508 SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CERTIFICATE);
1509 SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SHOW_ERRORS);
1510 SSL_CONF_CTX_set_ssl(cctx, myssl);
1511
1512 int i, j, next = 0;
1513 char *cmd, * arg;
1514 for (i = 0; i < opensslconf.len; i += next) {
1515 cmd = opensslconf.s + i;
1516 next = str_len(cmd) + 1;
1517
1518 j = str_chr(cmd, ' ');
1519 arg = cmd + j;
1520 while (*arg == ' ') ++arg;
1521 cmd[j] = 0;
1522
1523 if (SSL_CONF_cmd(cctx, cmd, arg) <= 0) {
1524 enew(); eout("opensslconf \""); eout(cmd); eout(" "); eout(arg);
1525 eout("\" failed: "); eout(ssl_error()); eout("\n"); eflush();
1526 tls_out("OpenSSL", "unable to initialize ssl");
1527 SSL_free(myssl);
1528 die_read();
1529 }
1530 }
1531
1532 (void)SSL_CONF_CTX_finish(cctx);
1533 }
1534#endif
1535
1553 SSL_set_rfd(myssl, ssl_rfd = substdio_fileno(&ssin)); 1536 SSL_set_rfd(myssl, ssl_rfd = substdio_fileno(&ssin));
1554 SSL_set_wfd(myssl, ssl_wfd = substdio_fileno(&ssout)); 1537 SSL_set_wfd(myssl, ssl_wfd = substdio_fileno(&ssout));
1555 1538