Skip to content

Commit 0b70b61

Browse files
support certificate verification (#6)
* Certificate verification support: - Add support for trust certificate file and directory. - Config option to set trust certificate file and directory. - Config option to enable/disable certificate and hostname verification. - Config option to enable/disable self signed certificate. - Fix SSL context and object release on failure. * Update man page with new config options. * Update TLS document with new config options.
1 parent 1ccd773 commit 0b70b61

3 files changed

Lines changed: 202 additions & 20 deletions

File tree

TLS

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,17 @@ Set this to yes to make ssmtp identify itself with a certificate.
2626
TLSCert=<file>
2727
Specify which certificate file should be used.
2828

29+
TLS_CA_File=<file>
30+
Optional file of trusted certificates for validating the server.
2931

30-
TODO:
31-
* Check server certificate for changes and notify about it.
32-
* Diffrent Certificate and Key file?
32+
TLS_CA_Dir=<file>
33+
Optional directory of trusted certificates for validating the server.
3334

35+
VerifyPeer=YES/NO
36+
Set this to yes to make ssmtp to verify peer(server) certificate.
3437

38+
VerifyPeerName=YES/NO
39+
Set this to yes to make ssmtp to verify peer(server) hostname.
40+
41+
AllowSelfSigned=YES/NO
42+
Set this to yes to make ssmtp to allow self signed certificates.

ssmtp.c

Lines changed: 172 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <openssl/pem.h>
3232
#include <openssl/ssl.h>
3333
#include <openssl/err.h>
34+
#include <openssl/x509v3.h>
3435
#endif
3536
#ifdef MD5AUTH
3637
#include "md5auth/hmac_md5.h"
@@ -52,6 +53,9 @@ bool_t use_tls = False; /* Use SSL to transfer mail to HUB */
5253
bool_t use_starttls = False; /* SSL only after STARTTLS (RFC2487) */
5354
bool_t use_cert = False; /* Use a certificate to transfer SSL mail */
5455
bool_t use_oldauth = False; /* use old AUTH LOGIN username style */
56+
bool_t verify_peer = True; /* Verify peer certificate. */
57+
bool_t verify_peer_name = True; /* Verify hostname in the certificate match server connected */
58+
bool_t allow_self_signed = True; /* Accept self signed certificates. */
5559

5660
#define ARPADATE_LENGTH 32 /* Current date in RFC format */
5761
char arpadate[ARPADATE_LENGTH];
@@ -68,6 +72,9 @@ char *gecos;
6872
char *prog = NULL;
6973
char *root = NULL;
7074
char *tls_cert = "/etc/ssl/certs/ssmtp.pem"; /* Default Certificate */
75+
char *tls_key = "/etc/ssl/certs/ssmtp.pem"; /* Default Private Key */
76+
char *tls_ca_file = NULL; /* Trusted Certificate file */
77+
char *tls_ca_dir = NULL; /* Trusted Certificate directory */
7178
char *uad = NULL;
7279
char *config_file = NULL; /* alternate configuration file */
7380

@@ -1014,6 +1021,45 @@ bool_t read_config()
10141021
"Set UseSTARTTLS=\"%s\"\n", use_tls ? "True" : "False");
10151022
}
10161023
}
1024+
else if(strcasecmp(p, "VerifyPeer") == 0) {
1025+
if(strcasecmp(q, "YES") == 0) {
1026+
verify_peer = True;
1027+
}
1028+
else {
1029+
verify_peer = False;
1030+
}
1031+
1032+
if(log_level > 0) {
1033+
log_event(LOG_INFO,
1034+
"Set VerifyPeer=\"%s\"\n", verify_peer ? "True" : "False");
1035+
}
1036+
}
1037+
else if(strcasecmp(p, "VerifyPeerName") == 0) {
1038+
if(strcasecmp(q, "YES") == 0) {
1039+
verify_peer_name = True;
1040+
}
1041+
else {
1042+
verify_peer_name = False;
1043+
}
1044+
1045+
if(log_level > 0) {
1046+
log_event(LOG_INFO,
1047+
"Set VerifyPeerName=\"%s\"\n", verify_peer_name ? "True" : "False");
1048+
}
1049+
}
1050+
else if(strcasecmp(p, "AllowSelfSigned") == 0) {
1051+
if(strcasecmp(q, "YES") == 0) {
1052+
allow_self_signed = True;
1053+
}
1054+
else {
1055+
allow_self_signed = False;
1056+
}
1057+
1058+
if(log_level > 0) {
1059+
log_event(LOG_INFO,
1060+
"Set AllowSelfSigned=\"%s\"\n", allow_self_signed ? "True" : "False");
1061+
}
1062+
}
10171063
else if(strcasecmp(p, "UseTLSCert") == 0) {
10181064
if(strcasecmp(q, "YES") == 0) {
10191065
use_cert = True;
@@ -1037,6 +1083,33 @@ bool_t read_config()
10371083
log_event(LOG_INFO, "Set TLSCert=\"%s\"\n", tls_cert);
10381084
}
10391085
}
1086+
else if(strcasecmp(p, "TLSKey") == 0) {
1087+
if((tls_key = strdup(q)) == (char *)NULL) {
1088+
die("parse_config() -- strdup() failed");
1089+
}
1090+
1091+
if(log_level > 0) {
1092+
log_event(LOG_INFO, "Set TLSKey=\"%s\"\n", tls_key);
1093+
}
1094+
}
1095+
else if(strcasecmp(p, "TLS_CA_File") == 0) {
1096+
if((tls_ca_file = strdup(q)) == (char *)NULL) {
1097+
die("parse_config() -- strdup() failed");
1098+
}
1099+
1100+
if(log_level > 0) {
1101+
log_event(LOG_INFO, "Set TLS_CA_File=\"%s\"\n", tls_ca_file);
1102+
}
1103+
}
1104+
else if(strcasecmp(p, "TLS_CA_Dir") == 0) {
1105+
if((tls_ca_dir = strdup(q)) == (char *)NULL) {
1106+
die("parse_config() -- strdup() failed");
1107+
}
1108+
1109+
if(log_level > 0) {
1110+
log_event(LOG_INFO, "Set TLS_CA_Dir=\"%s\"\n", tls_ca_dir);
1111+
}
1112+
}
10401113
#endif
10411114
/* Command-line overrides these */
10421115
else if(strcasecmp(p, "AuthUser") == 0 && !auth_user) {
@@ -1129,29 +1202,66 @@ int smtp_open(char *host, int port)
11291202

11301203
SSL_load_error_strings();
11311204
SSLeay_add_ssl_algorithms();
1132-
meth=SSLv23_client_method();
1205+
meth=TLS_client_method();
11331206
ctx = SSL_CTX_new(meth);
11341207
if(!ctx) {
11351208
log_event(LOG_ERR, "No SSL support initiated\n");
1209+
SSL_CTX_free(ctx);
11361210
return(-1);
11371211
}
11381212

11391213
if(use_cert == True) {
11401214
if(SSL_CTX_use_certificate_chain_file(ctx, tls_cert) <= 0) {
11411215
perror("Use certfile");
1216+
SSL_CTX_free(ctx);
11421217
return(-1);
11431218
}
11441219

1145-
if(SSL_CTX_use_PrivateKey_file(ctx, tls_cert, SSL_FILETYPE_PEM) <= 0) {
1220+
if(SSL_CTX_use_PrivateKey_file(ctx, tls_key, SSL_FILETYPE_PEM) <= 0) {
11461221
perror("Use PrivateKey");
1222+
SSL_CTX_free(ctx);
11471223
return(-1);
11481224
}
11491225

11501226
if(!SSL_CTX_check_private_key(ctx)) {
11511227
log_event(LOG_ERR, "Private key does not match the certificate public key\n");
1228+
SSL_CTX_free(ctx);
11521229
return(-1);
11531230
}
11541231
}
1232+
1233+
if (verify_peer == True) {
1234+
long lerr;
1235+
unsigned long ulerr;
1236+
1237+
if(log_level > 0) {
1238+
log_event(LOG_INFO, "verifying peer");
1239+
}
1240+
if (tls_ca_file != NULL || tls_ca_dir != NULL) {
1241+
if(!SSL_CTX_load_verify_locations(ctx, tls_ca_file, tls_ca_dir)) {
1242+
ulerr = ERR_get_error();
1243+
log_event(LOG_ERR, "Error setting verify location: %s",
1244+
ERR_reason_error_string(ulerr));
1245+
SSL_CTX_free(ctx);
1246+
return(-1);
1247+
}
1248+
}
1249+
else {
1250+
if (SSL_CTX_set_default_verify_paths(ctx) != 1) {
1251+
ulerr = ERR_get_error();
1252+
log_event(LOG_ERR, "Error setting default verify location: %s",
1253+
ERR_reason_error_string(ulerr));
1254+
SSL_CTX_free(ctx);
1255+
return(-1);
1256+
}
1257+
if(log_level > 0) {
1258+
log_event(LOG_INFO, "set default verify path");
1259+
}
1260+
}
1261+
if (allow_self_signed == False) {
1262+
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
1263+
}
1264+
}
11551265
#endif
11561266

11571267
#ifdef INET6
@@ -1163,7 +1273,7 @@ int smtp_open(char *host, int port)
11631273
/* Check we can reach the host */
11641274
if (getaddrinfo(host, servname, &hints, &ai0)) {
11651275
log_event(LOG_ERR, "Unable to locate %s", host);
1166-
return(-1);
1276+
goto tcp_err;
11671277
}
11681278

11691279
for (ai = ai0; ai; ai = ai->ai_next) {
@@ -1183,31 +1293,31 @@ int smtp_open(char *host, int port)
11831293
if(s < 0) {
11841294
log_event (LOG_ERR,
11851295
"Unable to connect to \"%s\" port %d.\n", host, port);
1186-
1187-
return(-1);
1296+
goto tcp_err;
11881297
}
1298+
goto tcp_ok;
11891299
#else
11901300
/* Check we can reach the host */
11911301
if((hent = gethostbyname(host)) == (struct hostent *)NULL) {
11921302
log_event(LOG_ERR, "Unable to locate %s", host);
1193-
return(-1);
1303+
goto tcp_err;
11941304
}
11951305

11961306
if(hent->h_length > sizeof(hent->h_addr)) {
11971307
log_event(LOG_ERR, "Buffer overflow in gethostbyname()");
1198-
return(-1);
1308+
goto tcp_err;
11991309
}
12001310

12011311
/* Create a socket for the connection */
12021312
if((s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
12031313
log_event(LOG_ERR, "Unable to create a socket");
1204-
return(-1);
1314+
goto tcp_err;
12051315
}
12061316

12071317
for (i = 0; ; ++i) {
12081318
if (!hent->h_addr_list[i]) {
12091319
log_event(LOG_ERR, "Unable to connect to %s:%d", host, port);
1210-
return(-1);
1320+
goto tcp_err;
12111321
}
12121322

12131323
/* This SHOULD already be in Network Byte Order from gethostbyname() */
@@ -1220,10 +1330,17 @@ int smtp_open(char *host, int port)
12201330
continue;
12211331
break;
12221332
}
1333+
goto tcp_ok;
12231334
#endif
1224-
1335+
tcp_err:
1336+
#ifdef HAVE_SSL
1337+
SSL_CTX_free(ctx);
1338+
#endif
1339+
return(-1);
1340+
tcp_ok:
12251341
#ifdef HAVE_SSL
12261342
if(use_tls == True) {
1343+
long status;
12271344
log_event(LOG_INFO, "Creating SSL connection to host");
12281345

12291346
if (use_starttls == True)
@@ -1237,6 +1354,7 @@ int smtp_open(char *host, int port)
12371354
smtp_write(s, "STARTTLS"); /* assume STARTTLS regardless */
12381355
if (!smtp_okay(s, buf)) {
12391356
log_event(LOG_ERR, "STARTTLS not working");
1357+
SSL_CTX_free(ctx);
12401358
return(-1);
12411359
}
12421360
}
@@ -1248,6 +1366,7 @@ int smtp_open(char *host, int port)
12481366
else
12491367
{
12501368
log_event(LOG_ERR, "Invalid response SMTP Server (STARTTLS)");
1369+
SSL_CTX_free(ctx);
12511370
return(-1);
12521371
}
12531372
use_tls=True; /* now continue as normal for SSL */
@@ -1256,13 +1375,27 @@ int smtp_open(char *host, int port)
12561375
ssl = SSL_new(ctx);
12571376
if(!ssl) {
12581377
log_event(LOG_ERR, "SSL not working");
1378+
SSL_free(ssl);
1379+
SSL_CTX_free(ctx);
12591380
return(-1);
12601381
}
1261-
SSL_set_fd(ssl, s);
12621382

1383+
if (verify_peer == True && verify_peer_name == True) {
1384+
SSL_set_hostflags(ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
1385+
if (!SSL_set1_host(ssl, host)) {
1386+
log_event(LOG_ERR, "Unable to set peer host name");
1387+
SSL_free(ssl);
1388+
SSL_CTX_free(ctx);
1389+
return(-1);
1390+
}
1391+
}
1392+
1393+
SSL_set_fd(ssl, s);
12631394
err = SSL_connect(ssl);
12641395
if(err < 0) {
12651396
perror("SSL_connect");
1397+
SSL_free(ssl);
1398+
SSL_CTX_free(ctx);
12661399
return(-1);
12671400
}
12681401

@@ -1273,11 +1406,37 @@ int smtp_open(char *host, int port)
12731406

12741407
server_cert = SSL_get_peer_certificate(ssl);
12751408
if(!server_cert) {
1409+
SSL_free(ssl);
1410+
SSL_CTX_free(ctx);
12761411
return(-1);
12771412
}
1278-
X509_free(server_cert);
12791413

1280-
/* TODO: Check server cert if changed! */
1414+
if (verify_peer == True) {
1415+
status = SSL_get_verify_result(ssl);
1416+
if(log_level > 0) {
1417+
log_event(LOG_INFO,"%d %s", status, X509_verify_cert_error_string(status) );
1418+
}
1419+
if (status != X509_V_OK) {
1420+
if (allow_self_signed == True
1421+
&& status != X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
1422+
&& status != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
1423+
&& status != X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) {
1424+
log_event(LOG_ERR, "%d %s", status, X509_verify_cert_error_string(status));
1425+
X509_free(server_cert);
1426+
SSL_free(ssl);
1427+
SSL_CTX_free(ctx);
1428+
return(-1);
1429+
}
1430+
else if (allow_self_signed == False) {
1431+
log_event(LOG_ERR, "%d %s", status, X509_verify_cert_error_string(status));
1432+
X509_free(server_cert);
1433+
SSL_free(ssl);
1434+
SSL_CTX_free(ctx);
1435+
return(-1);
1436+
}
1437+
}
1438+
}
1439+
X509_free(server_cert);
12811440
}
12821441
#endif
12831442

ssmtp.conf.5

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
.\"/* Copyright 2004 Reuben Thomas
1+
.\"/* Copyright 2004 Reuben Thomas, 2026 Syed Shahrukh Hussain
22
.\" * All rights reserved
33
.\" *
44
.\" This man page is distributed under the GNU General Public License
55
.\" version 2, or at your option, any later version. There is no warranty.
66
.\"
7-
.Dd October 7, 2004
7+
.Dd Feb 19, 2026
88
.Dt SSMTP.CONF 5
99
.Os
1010
.Sh NAME
@@ -53,6 +53,22 @@ See RFC 2487.
5353
.Pp
5454
.It Cm TLSCert
5555
The file name of an RSA certificate to use for TLS, if required.
56+
.It Cm TLSKey
57+
The file name of an RSA key to use for TLS, if required.
58+
.It Cm TLS_CA_File
59+
A file of trusted certificates for validating the server, if required.
60+
.Pp
61+
.It Cm TLS_CA_Dir
62+
A directory of trusted certificates for validating the server, if required.
63+
.Pp
64+
.It VerifyPeer
65+
Specifies whether ssmtp to verify peer(server) certificate.
66+
.Pp
67+
.It VerifyPeerName
68+
Specifies whether ssmtp to verify peer(server) hostname.
69+
.Pp
70+
.It AllowSelfSigned
71+
Specifies whether ssmtp to allow self signed certificates.
5672
.Pp
5773
.It Cm AuthUser
5874
The user name to use for SMTP AUTH.
@@ -77,6 +93,5 @@ Contains configuration data for
7793
.Sh SEE ALSO
7894
.Xr ssmtp 8
7995
.Sh AUTHORS
80-
Matt Ryan (mryan@debian.org), Hugo Haas (hugo@debian.org), Christoph Lameter (clameter@debian.org)
81-
and Dave Collier-Brown (davecb@hobbes.ss.org).
96+
Matt Ryan (mryan@debian.org), Hugo Haas (hugo@debian.org), Christoph Lameter (clameter@debian.org), Dave Collier-Brown (davecb@hobbes.ss.org) and Syed Shahrukh Hussain (syed.shahrukh@ossrevival.org).
8297
Reuben Thomas (rrt@sc3d.org) wrote the man page.

0 commit comments

Comments
 (0)