added starttls support to piler daemon

This commit is contained in:
SJ 2012-10-28 20:36:46 +01:00
parent d7f860c15b
commit dbbebbe5bf
13 changed files with 236 additions and 13 deletions

22
configure vendored
View File

@ -675,6 +675,7 @@ enable_option_checking
enable_static_build
enable_clamd
enable_memcached
enable_starttls
with_piler_user
'
ac_precious_vars='build_alias
@ -1298,6 +1299,7 @@ Optional Features:
--enable-static-build build statically linked executables (default: dynamically linked)
--enable-clamd build clamd antivirus support
--enable-memcached build memcached support
--enable-starttls build starttls support
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@ -3414,6 +3416,7 @@ have_mysql="no"
have_tre="no"
have_zip="no"
have_zlib="no"
have_starttls="no"
pdftotext="no"
catdoc="no"
@ -3485,6 +3488,16 @@ fi
# Check whether --enable-starttls was given.
if test "${enable_starttls+set}" = set; then :
enableval=$enable_starttls; have_starttls=$enableval
else
have_starttls="no"
fi
for ac_header in math.h
do :
@ -4339,6 +4352,15 @@ _ACEOF
antispam_libs="$antispam_libs -lzip"
fi
if test "$have_starttls" = "yes"; then
echo "starttls support: yes"
cat >>confdefs.h <<_ACEOF
#define HAVE_STARTTLS 1
_ACEOF
fi
echo
if test "$have_clamd" = "yes"; then

View File

@ -40,6 +40,7 @@ have_mysql="no"
have_tre="no"
have_zip="no"
have_zlib="no"
have_starttls="no"
pdftotext="no"
catdoc="no"
@ -99,6 +100,11 @@ AC_ARG_ENABLE(memcached,
[ --enable-memcached build memcached support], want_memcached=$enableval, want_memcached="no")
AC_ARG_ENABLE(starttls,
[ --enable-starttls build starttls support], have_starttls=$enableval, have_starttls="no")
dnl math library
AC_CHECK_HEADERS(math.h, have_math=yes, have_math=no)
@ -278,6 +284,11 @@ if test "$have_zip" = "yes"; then
antispam_libs="$antispam_libs -lzip"
fi
if test "$have_starttls" = "yes"; then
echo "starttls support: yes"
AC_DEFINE_UNQUOTED(HAVE_STARTTLS, 1, [starttls support])
fi
echo
if test "$have_clamd" = "yes"; then

View File

@ -47,6 +47,21 @@ backlog=20
workdir=/var/piler/tmp
;
; starttls stuff
;
; whether to enable (1) or disable (0) starttls support
tls_enable=0
; PEM file containing both the certificate and the private key.
; Make sure to create this file (and secure it with chmod 600 /usr/local/etc/piler.pem)
; before turning on starttls support!
pemfile=
; cipher list to use, see 'man SSL_CTX_set_cipher_list' for more details
cipher_list=HIGH:MEDIUM
; piler's own header to indicate previously archived messages
piler_header_field=X-piler: piler already archived this email

View File

@ -17,3 +17,4 @@
#undef HAVE_UNRTF
#undef HAVE_ZIP
#undef HAVE_STARTTLS

View File

@ -61,6 +61,7 @@ struct _parse_rule config_parse_rules[] =
{ "archive_emails_not_having_message_id", "integer", (void*) int_parser, offsetof(struct __config, archive_emails_not_having_message_id), "0", sizeof(int)},
{ "backlog", "integer", (void*) int_parser, offsetof(struct __config, backlog), "20", sizeof(int)},
{ "cipher_list", "string", (void*) string_parser, offsetof(struct __config, cipher_list), "HIGH:MEDIUM", MAXVAL-1},
{ "clamd_addr", "string", (void*) string_parser, offsetof(struct __config, clamd_addr), "", MAXVAL-1},
{ "clamd_port", "integer", (void*) int_parser, offsetof(struct __config, clamd_port), "0", sizeof(int)},
{ "clamd_socket", "string", (void*) string_parser, offsetof(struct __config, clamd_socket), CLAMD_SOCKET, MAXVAL-1},
@ -85,11 +86,13 @@ struct _parse_rule config_parse_rules[] =
{ "mysqldb", "string", (void*) string_parser, offsetof(struct __config, mysqldb), "piler", MAXVAL-1},
{ "mysql_connect_timeout", "integer", (void*) int_parser, offsetof(struct __config, mysql_connect_timeout), "2", sizeof(int)},
{ "number_of_worker_processes", "integer", (void*) int_parser, offsetof(struct __config, number_of_worker_processes), "10", sizeof(int)},
{ "pemfile", "string", (void*) string_parser, offsetof(struct __config, pemfile), "", MAXVAL-1},
{ "pidfile", "string", (void*) string_parser, offsetof(struct __config, pidfile), PIDFILE, MAXVAL-1},
{ "piler_header_field", "string", (void*) string_parser, offsetof(struct __config, piler_header_field), "", MAXVAL-1},
{ "queuedir", "string", (void*) string_parser, offsetof(struct __config, queuedir), QUEUE_DIR, MAXVAL-1},
{ "session_timeout", "integer", (void*) int_parser, offsetof(struct __config, session_timeout), "420", sizeof(int)},
{ "spam_header_line", "string", (void*) string_parser, offsetof(struct __config, spam_header_line), "", MAXVAL-1},
{ "tls_enable", "integer", (void*) int_parser, offsetof(struct __config, tls_enable), "0", sizeof(int)},
{ "update_counters_to_memcached", "integer", (void*) int_parser, offsetof(struct __config, update_counters_to_memcached), "0", sizeof(int)},
{ "username", "string", (void*) string_parser, offsetof(struct __config, username), "piler", MAXVAL-1},
{ "use_antivirus", "integer", (void*) int_parser, offsetof(struct __config, use_antivirus), "1", sizeof(int)},

View File

@ -20,6 +20,10 @@ struct __config {
int clamd_port;
char clamd_socket[MAXVAL];
int tls_enable;
char pemfile[MAXVAL];
char cipher_list[MAXVAL];
int use_antivirus;
char memcached_servers[MAXVAL];

View File

@ -13,7 +13,7 @@
#define VERSION "0.1.21"
#define BUILD 719
#define BUILD 722
#define HOSTID "mailarchiver"

View File

@ -178,6 +178,7 @@ struct session_data {
char attachments[SMALLBUFSIZE];
char internal_sender, internal_recipient, external_recipient;
int direction;
int tls;
int spam_message;
int fd, hdr_len, tot_len, num_of_rcpt_to, rav;
int need_scan;
@ -239,6 +240,8 @@ struct memcached_server {
struct __data {
int folder;
char recursive_folder_names;
char starttls[TINYBUFSIZE];
#ifdef HAVE_TRE
struct rule *archiving_rules;
struct rule *retention_rules;

View File

@ -218,7 +218,6 @@ int connect_to_imap_server(int sd, int *seq, char *imapserver, char *username, c
unsigned long host=0;
struct sockaddr_in remote_addr;
X509* server_cert;
SSL_METHOD *meth;
char *str;
@ -237,11 +236,10 @@ int connect_to_imap_server(int sd, int *seq, char *imapserver, char *username, c
if(use_ssl == 1){
SSLeay_add_ssl_algorithms();
meth = SSLv3_client_method();
SSL_library_init();
SSL_load_error_strings();
data->ctx = SSL_CTX_new(meth);
data->ctx = SSL_CTX_new(SSLv3_client_method());
CHK_NULL(data->ctx, "internal SSL error");
data->ssl = SSL_new(data->ctx);
@ -314,6 +312,7 @@ void close_connection(int sd, struct __data *data, int use_ssl){
SSL_shutdown(data->ssl);
SSL_free(data->ssl);
SSL_CTX_free(data->ctx);
ERR_free_strings();
}
}

View File

@ -322,12 +322,67 @@ int write1(int sd, char *buf, int use_ssl, SSL *ssl){
}
int ssl_want_retry(SSL *ssl, int ret, int timeout){
int i;
fd_set fds;
struct timeval tv;
int sock;
// something went wrong. I'll retry, die quietly, or complain
i = SSL_get_error(ssl, ret);
if(i == SSL_ERROR_NONE)
return 1;
tv.tv_sec = timeout/1000;
tv.tv_usec = 0;
FD_ZERO(&fds);
switch(i){
case SSL_ERROR_WANT_READ: // pause until the socket is readable
sock = SSL_get_rfd(ssl);
FD_SET(sock, &fds);
i = select(sock+1, &fds, 0, 0, &tv);
break;
case SSL_ERROR_WANT_WRITE: // pause until the socket is writeable
sock = SSL_get_wfd(ssl);
FD_SET(sock, &fds);
i = select(sock+1, 0, &fds, 0, &tv);
break;
case SSL_ERROR_ZERO_RETURN: // the sock closed, just return quietly
i = 0;
break;
default: // ERROR - unexpected error code
i = -1;
break;
};
return i;
}
int ssl_read_timeout(SSL *ssl, void *buf, int len, int timeout){
int i;
while(1){
i = SSL_read(ssl, (char*)buf, len);
if(i > 0) break;
i = ssl_want_retry(ssl, i, timeout);
if(i <= 0) break;
}
return i;
}
int recvtimeoutssl(int s, char *buf, int len, int timeout, int use_ssl, SSL *ssl){
memset(buf, 0, len);
if(use_ssl == 1){
return SSL_read(ssl, buf, len-1);
return ssl_read_timeout(ssl, buf, len-1, timeout);
}
else {
return recvtimeout(s, buf, len-1, timeout);

View File

@ -21,6 +21,8 @@
#include <unistd.h>
#include <locale.h>
#include <errno.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <piler.h>
@ -247,6 +249,13 @@ void clean_exit(){
unlink(cfg.pidfile);
#ifdef HAVE_STARTTLS
if(data.ctx){
SSL_CTX_free(data.ctx);
ERR_free_strings();
}
#endif
exit(1);
}
@ -257,6 +266,27 @@ void fatal(char *s){
}
#ifdef HAVE_STARTTLS
int init_ssl(){
SSL_library_init();
SSL_load_error_strings();
data.ctx = SSL_CTX_new(SSLv23_server_method());
if(data.ctx == NULL){ syslog(LOG_PRIORITY, "SSL_CTX_new() failed"); return ERR; }
if(SSL_CTX_set_cipher_list(data.ctx, cfg.cipher_list) == 0){ syslog(LOG_PRIORITY, "failed to set cipher list: '%s'", cfg.cipher_list); return ERR; }
if(SSL_CTX_use_PrivateKey_file(data.ctx, cfg.pemfile, SSL_FILETYPE_PEM) != 1){ syslog(LOG_PRIORITY, "cannot load private key from %s", cfg.pemfile); return ERR; }
if(SSL_CTX_use_certificate_file(data.ctx, cfg.pemfile, SSL_FILETYPE_PEM) != 1){ syslog(LOG_PRIORITY, "cannot load certificate from %s", cfg.pemfile); return ERR; }
return OK;
}
#endif
void initialise_configuration(){
struct session_data sdata;
@ -293,6 +323,14 @@ void initialise_configuration(){
data.archiving_rules = NULL;
data.retention_rules = NULL;
memset(data.starttls, 0, TINYBUFSIZE);
#ifdef HAVE_STARTTLS
if(cfg.tls_enable > 0 && data.ctx == NULL && init_ssl() == OK){
snprintf(data.starttls, TINYBUFSIZE-1, "250-STARTTLS\r\n");
}
#endif
mysql_init(&(sdata.mysql));
mysql_options(&(sdata.mysql), MYSQL_OPT_CONNECT_TIMEOUT, (const char*)&cfg.mysql_connect_timeout);
if(mysql_real_connect(&(sdata.mysql), cfg.mysqlhost, cfg.mysqluser, cfg.mysqlpwd, cfg.mysqldb, cfg.mysqlport, cfg.mysqlsocket, 0) == 0){
@ -347,6 +385,8 @@ int main(int argc, char **argv){
data.recursive_folder_names = 0;
data.archiving_rules = NULL;
data.retention_rules = NULL;
data.ctx = NULL;
data.ssl = NULL;
initialise_configuration();

View File

@ -14,6 +14,8 @@
#include <signal.h>
#include <syslog.h>
#include <time.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <piler.h>
@ -31,10 +33,15 @@ int handle_smtp_session(int new_sd, struct __data *data, struct __config *cfg){
struct timezone tz;
struct timeval tv1, tv2;
#ifdef HAVE_STARTTLS
int starttls = 0;
char ssl_error[SMALLBUFSIZE];
#endif
state = SMTP_STATE_INIT;
init_session_data(&sdata);
sdata.tls = 0;
bzero(&counters, sizeof(counters));
@ -70,7 +77,7 @@ int handle_smtp_session(int new_sd, struct __data *data, struct __config *cfg){
send(new_sd, buf, strlen(buf), 0);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, buf);
while((n = recvtimeout(new_sd, puf, MAXBUFSIZE, TIMEOUT)) > 0){
while((n = recvtimeoutssl(new_sd, puf, MAXBUFSIZE, TIMEOUT, sdata.tls, data->ssl)) > 0){
pos = 0;
/* accept mail data */
@ -120,7 +127,8 @@ int handle_smtp_session(int new_sd, struct __data *data, struct __config *cfg){
for(i=0; i<sdata.num_of_rcpt_to; i++){
#endif
send(new_sd, SMTP_RESP_421_ERR_WRITE_FAILED, strlen(SMTP_RESP_421_ERR_WRITE_FAILED), 0);
//send(new_sd, SMTP_RESP_421_ERR_WRITE_FAILED, strlen(SMTP_RESP_421_ERR_WRITE_FAILED), 0);
write1(new_sd, SMTP_RESP_421_ERR_WRITE_FAILED, sdata.tls, data->ssl);
#ifdef HAVE_LMTP
}
@ -213,7 +221,8 @@ int handle_smtp_session(int new_sd, struct __data *data, struct __config *cfg){
if(inj == ERR) snprintf(sdata.acceptbuf, SMALLBUFSIZE-1, "451 %s <%s>\r\n", sdata.ttmpfile, rctptoemail);
send(new_sd, sdata.acceptbuf, strlen(sdata.acceptbuf), 0);
//send(new_sd, sdata.acceptbuf, strlen(sdata.acceptbuf), 0);
write1(new_sd, sdata.acceptbuf, sdata.tls, data->ssl);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, sdata.acceptbuf);
@ -288,7 +297,9 @@ AFTER_PERIOD:
if(strncasecmp(buf, SMTP_CMD_EHLO, strlen(SMTP_CMD_EHLO)) == 0 || strncasecmp(buf, LMTP_CMD_LHLO, strlen(LMTP_CMD_LHLO)) == 0){
if(state == SMTP_STATE_INIT) state = SMTP_STATE_HELO;
snprintf(buf, MAXBUFSIZE-1, SMTP_RESP_250_EXTENSIONS, cfg->hostid);
if(sdata.tls == 0) snprintf(buf, MAXBUFSIZE-1, SMTP_RESP_250_EXTENSIONS, cfg->hostid, data->starttls);
else snprintf(buf, MAXBUFSIZE-1, SMTP_RESP_250_EXTENSIONS, cfg->hostid, "");
strncat(resp, buf, MAXBUFSIZE-1);
continue;
@ -306,6 +317,29 @@ AFTER_PERIOD:
}
#ifdef HAVE_STARTTLS
if(cfg->tls_enable > 0 && strncasecmp(buf, SMTP_CMD_STARTTLS, strlen(SMTP_CMD_STARTTLS)) == 0 && strlen(data->starttls) > 4 && sdata.tls == 0){
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: starttls request from client", sdata.ttmpfile);
if(data->ctx){
data->ssl = SSL_new(data->ctx);
if(data->ssl){
if(SSL_set_fd(data->ssl, new_sd) == 1){
strncat(resp, SMTP_RESP_220_READY_TO_START_TLS, MAXBUFSIZE-1);
starttls = 1;
state = SMTP_STATE_INIT;
continue;
} syslog(LOG_PRIORITY, "%s: SSL_set_fd() failed", sdata.ttmpfile);
} syslog(LOG_PRIORITY, "%s: SSL_new() failed", sdata.ttmpfile);
} syslog(LOG_PRIORITY, "%s: SSL ctx is null!", sdata.ttmpfile);
strncat(resp, SMTP_RESP_454_ERR_TLS_TEMP_ERROR, MAXBUFSIZE-1);
continue;
}
#endif
if(strncasecmp(buf, SMTP_CMD_MAIL_FROM, strlen(SMTP_CMD_MAIL_FROM)) == 0){
@ -433,9 +467,32 @@ AFTER_PERIOD:
/* now we can send our buffered response */
if(strlen(resp) > 0){
send(new_sd, resp, strlen(resp), 0);
//send(new_sd, resp, strlen(resp), 0);
write1(new_sd, resp, sdata.tls, data->ssl);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, resp);
memset(resp, 0, MAXBUFSIZE);
#ifdef HAVE_STARTTLS
if(starttls == 1 && sdata.tls == 0){
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: waiting for ssl handshake", sdata.ttmpfile);
rc = SSL_accept(data->ssl);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: SSL_accept() finished", sdata.ttmpfile);
if(rc == 1){
sdata.tls = 1;
}
else {
ERR_error_string_n(ERR_get_error(), ssl_error, SMALLBUFSIZE);
syslog(LOG_PRIORITY, "%s: SSL_accept() failed, rc=%d, errorcode: %d, error text: %s\n", sdata.ttmpfile, rc, SSL_get_error(data->ssl, rc), ssl_error);
}
}
#endif
}
if(state == SMTP_STATE_FINISHED){
@ -451,7 +508,9 @@ AFTER_PERIOD:
if(state < SMTP_STATE_QUIT && inj == ERR){
snprintf(buf, MAXBUFSIZE-1, SMTP_RESP_421_ERR, cfg->hostid);
send(new_sd, buf, strlen(buf), 0);
//send(new_sd, buf, strlen(buf), 0);
write1(new_sd, buf, sdata.tls, data->ssl);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, buf);
if(sdata.fd != -1){
@ -475,6 +534,13 @@ QUITTING:
mysql_close(&(sdata.mysql));
#endif
#ifdef HAVE_STARTTLS
if(sdata.tls == 1){
SSL_shutdown(data->ssl);
SSL_free(data->ssl);
}
#endif
if(cfg->verbosity >= _LOG_INFO) syslog(LOG_PRIORITY, "processed %llu messages", counters.c_rcvd);
return (int)counters.c_rcvd;

View File

@ -26,13 +26,15 @@
#define SMTP_CMD_NOOP "NOOP"
#define SMTP_CMD_XFORWARD "XFORWARD"
#define SMTP_CMD_XCLIENT "XCLIENT"
#define SMTP_CMD_STARTTLS "STARTTLS"
// SMTP responses
#define SMTP_RESP_220_BANNER "220 %s ESMTP\r\n"
#define SMTP_RESP_220_READY_TO_START_TLS "220 Ready to start TLS\r\n"
#define SMTP_RESP_221_GOODBYE "221 %s Goodbye\r\n"
#define SMTP_RESP_250_OK "250 Ok\r\n"
#define SMTP_RESP_250_EXTENSIONS "250-%s\r\n250-PIPELINING\r\n250-SIZE\r\n250 8BITMIME\r\n"
#define SMTP_RESP_250_EXTENSIONS "250-%s\r\n250-PIPELINING\r\n%s250-SIZE\r\n250 8BITMIME\r\n"
#define SMTP_RESP_354_DATA_OK "354 Send mail data; end it with <CRLF>.<CRLF>\r\n"
@ -41,9 +43,11 @@
#define SMTP_RESP_421_ERR_WRITE_FAILED "421 writing queue file failed\r\n"
#define SMTP_RESP_450_ERR_CMD_NOT_IMPLEMENTED "450 command not implemented\r\n"
#define SMTP_RESP_451_ERR "451 Error in processing, try again later\r\n"
#define SMTP_RESP_454_ERR_TLS_TEMP_ERROR "454 TLS not available currently\r\n"
#define SMTP_RESP_502_ERR "502 Command not implemented\r\n"
#define SMTP_RESP_503_ERR "503 Bad command sequence\r\n"
#define SMTP_RESP_530_ERR_MUST_ISSUE_STARTTLS_FIRST "530 MUST issue STARTTLS command first\r\n"
#define SMTP_RESP_550_ERR "550 Access denied.\r\n"
#define SMTP_RESP_550_ERR_PREF "550 Access denied."
#define SMTP_RESP_550_INVALID_RECIPIENT "550 Unknown recipient\r\n"