From 2865b20e43c865c9a06d084b3c26b500ddb48faf Mon Sep 17 00:00:00 2001 From: Fabian Peter Hammerle Date: Fri, 27 Dec 2019 00:29:32 +0100 Subject: [PATCH 1/2] added option to verify server's cert fingerprint https://github.com/corecode/dma/pull/71 https://github.com/corecode/dma/issues/10 --- conf.c | 17 ++++++++++++++++- crypto.c | 28 ++++++++++++++++++++++++++++ dma.8 | 3 +++ dma.c | 1 + dma.conf | 4 ++++ dma.h | 1 + 6 files changed, 53 insertions(+), 1 deletion(-) diff --git a/conf.c b/conf.c index 3e3c7d3..c4eb247 100644 --- a/conf.c +++ b/conf.c @@ -218,7 +218,22 @@ parse_conf(const char *config_path) config.masquerade_user = user; } else if (strcmp(word, "STARTTLS") == 0 && data == NULL) config.features |= STARTTLS; - else if (strcmp(word, "OPPORTUNISTIC_TLS") == 0 && data == NULL) + else if (strcmp(word, "FINGERPRINT") == 0) { + if (strlen(data) != SHA256_DIGEST_LENGTH * 2) { + errlogx(EX_CONFIG, "invalid sha256 fingerprint length"); + } + unsigned char *fingerprint = malloc(SHA256_DIGEST_LENGTH); + if (fingerprint == NULL) { + errlogx(EX_CONFIG, "fingerprint allocation failed"); + } + for (unsigned int i = 0; i < SHA256_DIGEST_LENGTH; i++) { + if(sscanf(data + 2 * i, "%02hhx", &fingerprint[i]) != 1) { + errlogx(EX_CONFIG, "failed to read fingerprint"); + } + } + free(data); + config.fingerprint = fingerprint; + } else if (strcmp(word, "OPPORTUNISTIC_TLS") == 0 && data == NULL) config.features |= TLS_OPP; else if (strcmp(word, "SECURETRANSFER") == 0 && data == NULL) config.features |= SECURETRANS; diff --git a/crypto.c b/crypto.c index f9bc360..c37345d 100644 --- a/crypto.c +++ b/crypto.c @@ -78,6 +78,29 @@ init_cert_file(SSL_CTX *ctx, const char *path) return (0); } +int +verify_server_fingerprint(const X509 *cert) +{ + unsigned char fingerprint[EVP_MAX_MD_SIZE] = {0}; + unsigned int fingerprint_len = 0; + if(!X509_digest(cert, EVP_sha256(), fingerprint, &fingerprint_len)) { + syslog(LOG_WARNING, "failed to load fingerprint of server's certicate: %s", + ssl_errstr()); + return (1); + } + if(fingerprint_len != SHA256_DIGEST_LENGTH) { + syslog(LOG_WARNING, "sha256 fingerprint has unexpected length of %d bytes", + fingerprint_len); + return (1); + } + if(memcmp(fingerprint, config.fingerprint, SHA256_DIGEST_LENGTH) != 0) { + syslog(LOG_WARNING, "fingerprints do not match"); + return (1); + } + syslog(LOG_DEBUG, "verified server's fingerprint"); + return (0); +} + int smtp_init_crypto(int fd, int feature, struct smtp_features* features) { @@ -173,6 +196,11 @@ smtp_init_crypto(int fd, int feature, struct smtp_features* features) if (cert == NULL) { syslog(LOG_WARNING, "remote delivery deferred: Peer did not provide certificate: %s", ssl_errstr()); + return (1); + } + if(config.fingerprint != NULL && verify_server_fingerprint(cert)) { + X509_free(cert); + return (1); } X509_free(cert); diff --git a/dma.8 b/dma.8 index d29c85b..ea18292 100644 --- a/dma.8 +++ b/dma.8 @@ -226,6 +226,9 @@ Uncomment if you want TLS/SSL secured transfer. Uncomment if you want to use STARTTLS. Only useful together with .Sq SECURETRANS . +.It Ic FINGERPRINT Xo +Pin the server certificate by specifying its SHA256 fingerprint. +Only makes sense if you use a smarthost. .It Ic OPPORTUNISTIC_TLS Xo (boolean, default=commented) .Xc diff --git a/dma.c b/dma.c index 4006bce..d62bf2c 100644 --- a/dma.c +++ b/dma.c @@ -85,6 +85,7 @@ struct config config = { .mailname = NULL, .masquerade_host = NULL, .masquerade_user = NULL, + .fingerprint = NULL, }; diff --git a/dma.conf b/dma.conf index 6d5a51c..fa95fc1 100644 --- a/dma.conf +++ b/dma.conf @@ -25,6 +25,10 @@ # SECURETRANSFER) #STARTTLS +# Pin the server certificate by specifying its SHA256 fingerprint. +# Only makes sense if you use a smarthost. +#FINGERPRINT 1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF + # Uncomment if you have specified STARTTLS above and it should be allowed # to fail ("opportunistic TLS", use an encrypted connection when available # but allow an unencrypted one to servers that do not support it) diff --git a/dma.h b/dma.h index ed0d0fc..5841362 100644 --- a/dma.h +++ b/dma.h @@ -138,6 +138,7 @@ struct config { const char *mailname; const char *masquerade_host; const char *masquerade_user; + const unsigned char *fingerprint; /* XXX does not belong into config */ SSL *ssl; From ff520537486a32f1946b8076f17c7cdd1cd0b5d4 Mon Sep 17 00:00:00 2001 From: Simon Schubert <2@0x2c.org> Date: Sun, 29 Dec 2019 14:44:11 +0100 Subject: [PATCH 2/2] rephrase messages without possessive --- crypto.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto.c b/crypto.c index c37345d..e2d621b 100644 --- a/crypto.c +++ b/crypto.c @@ -84,7 +84,7 @@ verify_server_fingerprint(const X509 *cert) unsigned char fingerprint[EVP_MAX_MD_SIZE] = {0}; unsigned int fingerprint_len = 0; if(!X509_digest(cert, EVP_sha256(), fingerprint, &fingerprint_len)) { - syslog(LOG_WARNING, "failed to load fingerprint of server's certicate: %s", + syslog(LOG_WARNING, "failed to load fingerprint of server certicate: %s", ssl_errstr()); return (1); } @@ -97,7 +97,7 @@ verify_server_fingerprint(const X509 *cert) syslog(LOG_WARNING, "fingerprints do not match"); return (1); } - syslog(LOG_DEBUG, "verified server's fingerprint"); + syslog(LOG_DEBUG, "successfully verified server certificate fingerprint"); return (0); }