2015-12-02 21:49:51 +01:00
|
|
|
/*
|
|
|
|
* smtp.c, SJ
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <strings.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <syslog.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <openssl/ssl.h>
|
|
|
|
#include <openssl/err.h>
|
|
|
|
#include <piler.h>
|
2016-04-05 21:10:09 +02:00
|
|
|
#include <smtp.h>
|
2015-12-02 21:49:51 +01:00
|
|
|
|
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
void process_command_ehlo_lhlo(struct session_ctx *sctx, int *protocol_state, char *resp, int resplen){
|
2015-12-02 21:49:51 +01:00
|
|
|
char tmpbuf[MAXBUFSIZE];
|
2016-08-17 21:47:19 +02:00
|
|
|
char extensions[SMALLBUFSIZE];
|
|
|
|
|
|
|
|
memset(extensions, 0, sizeof(extensions));
|
2015-12-02 21:49:51 +01:00
|
|
|
|
|
|
|
if(*protocol_state == SMTP_STATE_INIT) *protocol_state = SMTP_STATE_HELO;
|
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
if(sctx->sdata->tls == 0) snprintf(extensions, sizeof(extensions)-1, "%s", sctx->data->starttls);
|
2016-08-21 09:30:09 +02:00
|
|
|
if(sctx->cfg->enable_chunking == 1) strncat(extensions, SMTP_EXTENSION_CHUNKING, sizeof(extensions)-strlen(extensions)-2);
|
2016-08-17 21:47:19 +02:00
|
|
|
|
2016-08-21 09:30:09 +02:00
|
|
|
snprintf(tmpbuf, sizeof(tmpbuf)-1, SMTP_RESP_250_EXTENSIONS, sctx->cfg->hostid, extensions);
|
2015-12-02 21:49:51 +01:00
|
|
|
|
2016-01-02 08:16:38 +01:00
|
|
|
strncat(resp, tmpbuf, resplen-strlen(resp));
|
2015-12-02 21:49:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
void process_command_starttls(struct session_ctx *sctx, int *protocol_state, int *starttls, char *resp, int resplen){
|
2015-12-02 21:49:51 +01:00
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: starttls request from client", sctx->sdata->ttmpfile);
|
2015-12-02 21:49:51 +01:00
|
|
|
|
2016-08-21 09:30:09 +02:00
|
|
|
if(sctx->data->ctx){
|
|
|
|
sctx->data->ssl = SSL_new(sctx->data->ctx);
|
|
|
|
if(sctx->data->ssl){
|
2015-12-02 21:49:51 +01:00
|
|
|
|
2016-08-21 09:30:09 +02:00
|
|
|
SSL_set_options(sctx->data->ssl, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3);
|
2015-12-02 21:49:51 +01:00
|
|
|
|
2016-08-21 09:30:09 +02:00
|
|
|
if(SSL_set_fd(sctx->data->ssl, sctx->new_sd) == 1){
|
2015-12-02 21:49:51 +01:00
|
|
|
strncat(resp, SMTP_RESP_220_READY_TO_START_TLS, resplen);
|
|
|
|
*starttls = 1;
|
|
|
|
*protocol_state = SMTP_STATE_INIT;
|
|
|
|
return;
|
2016-08-31 12:35:31 +02:00
|
|
|
} syslog(LOG_PRIORITY, "%s: SSL_set_fd() failed", sctx->sdata->ttmpfile);
|
|
|
|
} syslog(LOG_PRIORITY, "%s: SSL_new() failed", sctx->sdata->ttmpfile);
|
|
|
|
} syslog(LOG_PRIORITY, "%s: SSL ctx is null!", sctx->sdata->ttmpfile);
|
2015-12-02 21:49:51 +01:00
|
|
|
|
|
|
|
strncat(resp, SMTP_RESP_454_ERR_TLS_TEMP_ERROR, resplen);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
void process_command_mail_from(struct session_ctx *sctx, int *protocol_state, char *buf, char *resp, int resplen){
|
2015-12-02 21:49:51 +01:00
|
|
|
|
2016-08-23 07:31:42 +02:00
|
|
|
if(*protocol_state != SMTP_STATE_HELO && *protocol_state != SMTP_STATE_PERIOD && *protocol_state != SMTP_STATE_BDAT){
|
2015-12-02 21:49:51 +01:00
|
|
|
strncat(resp, SMTP_RESP_503_ERR, resplen);
|
|
|
|
}
|
|
|
|
else {
|
2016-08-31 18:07:23 +02:00
|
|
|
if(*protocol_state == SMTP_STATE_PERIOD || *protocol_state == SMTP_STATE_BDAT){
|
2016-08-31 12:35:31 +02:00
|
|
|
if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: initiated new transaction", sctx->sdata->ttmpfile);
|
2015-12-02 21:49:51 +01:00
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
unlink(sctx->sdata->ttmpfile);
|
|
|
|
unlink(sctx->sdata->tmpframe);
|
2015-12-02 21:49:51 +01:00
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
init_session_data(sctx->sdata, sctx->cfg);
|
2015-12-02 21:49:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
*protocol_state = SMTP_STATE_MAIL_FROM;
|
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
snprintf(sctx->sdata->mailfrom, SMALLBUFSIZE-1, "%s\r\n", buf);
|
2015-12-02 21:49:51 +01:00
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
memset(sctx->sdata->fromemail, 0, SMALLBUFSIZE);
|
|
|
|
extractEmail(sctx->sdata->mailfrom, sctx->sdata->fromemail);
|
2015-12-02 21:49:51 +01:00
|
|
|
|
|
|
|
strncat(resp, SMTP_RESP_250_OK, strlen(SMTP_RESP_250_OK));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
void process_command_rcpt_to(struct session_ctx *sctx, int *protocol_state, char *buf, char *resp, int resplen){
|
2015-12-02 21:49:51 +01:00
|
|
|
|
|
|
|
if(*protocol_state == SMTP_STATE_MAIL_FROM || *protocol_state == SMTP_STATE_RCPT_TO){
|
|
|
|
|
|
|
|
if(strlen(buf) > SMALLBUFSIZE/2){
|
|
|
|
strncat(resp, SMTP_RESP_550_ERR_TOO_LONG_RCPT_TO, resplen);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
if(sctx->sdata->num_of_rcpt_to < MAX_RCPT_TO-1){
|
2016-08-31 17:09:33 +02:00
|
|
|
extractEmail(buf, sctx->sdata->rcptto[sctx->sdata->num_of_rcpt_to]);
|
2015-12-02 21:49:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
*protocol_state = SMTP_STATE_RCPT_TO;
|
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
if(sctx->sdata->num_of_rcpt_to < MAX_RCPT_TO-1) sctx->sdata->num_of_rcpt_to++;
|
2015-12-02 21:49:51 +01:00
|
|
|
|
|
|
|
strncat(resp, SMTP_RESP_250_OK, resplen);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
strncat(resp, SMTP_RESP_503_ERR, resplen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
void process_command_data(struct session_ctx *sctx, int *protocol_state, char *resp, int resplen){
|
2015-12-02 21:49:51 +01:00
|
|
|
|
|
|
|
if(*protocol_state != SMTP_STATE_RCPT_TO){
|
|
|
|
strncat(resp, SMTP_RESP_503_ERR, resplen);
|
|
|
|
}
|
|
|
|
else {
|
2016-08-31 12:35:31 +02:00
|
|
|
sctx->sdata->fd = open(sctx->sdata->filename, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
|
|
|
|
if(sctx->sdata->fd == -1){
|
|
|
|
syslog(LOG_PRIORITY, "%s: %s", ERR_OPEN_TMP_FILE, sctx->sdata->ttmpfile);
|
2015-12-02 21:49:51 +01:00
|
|
|
strncat(resp, SMTP_RESP_451_ERR, resplen);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
*protocol_state = SMTP_STATE_DATA;
|
|
|
|
strncat(resp, SMTP_RESP_354_DATA_OK, resplen-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-21 21:15:48 +02:00
|
|
|
void process_command_bdat(struct session_ctx *sctx, int *protocol_state, char *buf, char *resp, int resplen){
|
2016-08-19 22:33:47 +02:00
|
|
|
int n, expected_bdat_len;
|
|
|
|
char puf[MAXBUFSIZE];
|
|
|
|
|
2016-08-19 23:27:24 +02:00
|
|
|
if(*protocol_state != SMTP_STATE_RCPT_TO){
|
2016-08-19 22:33:47 +02:00
|
|
|
strncat(resp, SMTP_RESP_503_ERR, resplen);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sctx->bdat_rounds = 0;
|
|
|
|
sctx->bdat_last_round = 0;
|
|
|
|
|
|
|
|
while(sctx->bdat_last_round != 1){
|
|
|
|
sctx->bdat_rounds++;
|
|
|
|
expected_bdat_len = 0;
|
|
|
|
|
|
|
|
if(sctx->bdat_rounds == 1){
|
2016-08-21 21:15:48 +02:00
|
|
|
expected_bdat_len = extract_bdat_command(sctx, buf);
|
2016-08-19 22:33:47 +02:00
|
|
|
|
2016-08-21 21:15:48 +02:00
|
|
|
sctx->sdata->fd = open(sctx->sdata->filename, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
|
|
|
|
if(sctx->sdata->fd == -1){
|
|
|
|
syslog(LOG_PRIORITY, "%s: %s", ERR_OPEN_TMP_FILE, sctx->sdata->ttmpfile);
|
2016-08-19 22:33:47 +02:00
|
|
|
strncat(resp, SMTP_RESP_451_ERR, resplen);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
*protocol_state = SMTP_STATE_BDAT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(sctx->bdat_last_round != 1){
|
2016-08-21 21:15:48 +02:00
|
|
|
if((n = recvtimeoutssl(sctx->new_sd, &puf[0], sizeof(puf), TIMEOUT, sctx->sdata->tls, sctx->data->ssl)) > 0){
|
|
|
|
expected_bdat_len = extract_bdat_command(sctx, puf);
|
2016-08-19 23:27:24 +02:00
|
|
|
if(expected_bdat_len <= 0 && sctx->bdat_rounds > 0) sctx->bdat_rounds--;
|
2016-08-19 22:33:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-21 21:15:48 +02:00
|
|
|
if(expected_bdat_len > 0) sctx->sdata->tot_len += read_bdat_data(sctx, expected_bdat_len);
|
2016-08-19 22:33:47 +02:00
|
|
|
}
|
|
|
|
|
2016-08-21 21:15:48 +02:00
|
|
|
fsync(sctx->sdata->fd);
|
|
|
|
close(sctx->sdata->fd);
|
2016-08-19 22:33:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-21 21:15:48 +02:00
|
|
|
int extract_bdat_command(struct session_ctx *sctx, char *buf){
|
2016-08-19 22:33:47 +02:00
|
|
|
int expected_bdat_len=0;
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
// determine if this is the last BDAT command
|
|
|
|
|
2016-08-19 23:27:24 +02:00
|
|
|
p = strcasestr(buf, " LAST");
|
2016-08-19 22:33:47 +02:00
|
|
|
if(p){
|
|
|
|
sctx->bdat_last_round = 1;
|
2016-08-21 21:15:48 +02:00
|
|
|
syslog(LOG_INFO, "%s: BDAT LAST", sctx->sdata->ttmpfile);
|
2016-08-19 22:33:47 +02:00
|
|
|
*p = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
// determine the size to be read
|
|
|
|
|
|
|
|
p = strchr(buf, ' ');
|
|
|
|
if(p){
|
|
|
|
expected_bdat_len = atoi(p);
|
2016-08-21 21:15:48 +02:00
|
|
|
if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_INFO, "%s: BDAT len=%d", sctx->sdata->ttmpfile, expected_bdat_len);
|
2016-08-19 22:33:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if(!p || expected_bdat_len <= 0){
|
2016-08-21 21:15:48 +02:00
|
|
|
syslog(LOG_INFO, "%s: malformed BDAT command", sctx->sdata->ttmpfile);
|
2016-08-19 22:33:47 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return expected_bdat_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-21 21:15:48 +02:00
|
|
|
int read_bdat_data(struct session_ctx *sctx, int expected_bdat_len){
|
2016-08-19 22:33:47 +02:00
|
|
|
int n, read_bdat_len=0, written_bdat_len=0;
|
|
|
|
char puf[MAXBUFSIZE];
|
|
|
|
|
|
|
|
while(read_bdat_len < expected_bdat_len){
|
2016-08-21 21:15:48 +02:00
|
|
|
if((n = recvtimeoutssl(sctx->new_sd, &puf[0], sizeof(puf), TIMEOUT, sctx->sdata->tls, sctx->data->ssl)) > 0){
|
2016-08-19 22:33:47 +02:00
|
|
|
read_bdat_len += n;
|
2016-08-21 21:15:48 +02:00
|
|
|
written_bdat_len += write(sctx->sdata->fd, puf, n);
|
2016-08-19 22:33:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-21 21:15:48 +02:00
|
|
|
if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_INFO, "%s: wrote %d bytes of BDAT data", sctx->sdata->ttmpfile, written_bdat_len);
|
2016-08-19 23:27:24 +02:00
|
|
|
|
2016-08-19 22:33:47 +02:00
|
|
|
return written_bdat_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
void process_command_quit(struct session_ctx *sctx, int *protocol_state, char *resp, int resplen){
|
2015-12-02 21:49:51 +01:00
|
|
|
char tmpbuf[MAXBUFSIZE];
|
|
|
|
|
|
|
|
*protocol_state = SMTP_STATE_FINISHED;
|
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
snprintf(tmpbuf, sizeof(tmpbuf)-1, SMTP_RESP_221_GOODBYE, sctx->cfg->hostid);
|
2015-12-02 21:49:51 +01:00
|
|
|
strncat(resp, tmpbuf, resplen);
|
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
unlink(sctx->sdata->ttmpfile);
|
|
|
|
unlink(sctx->sdata->tmpframe);
|
2015-12-02 21:49:51 +01:00
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: removed", sctx->sdata->ttmpfile);
|
2015-12-02 21:49:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
void process_command_reset(struct session_ctx *sctx, int *protocol_state, char *resp, int resplen){
|
2015-12-02 21:49:51 +01:00
|
|
|
|
|
|
|
strncat(resp, SMTP_RESP_250_OK, resplen);
|
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: removed", sctx->sdata->ttmpfile);
|
2015-12-02 21:49:51 +01:00
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
unlink(sctx->sdata->ttmpfile);
|
|
|
|
unlink(sctx->sdata->tmpframe);
|
2015-12-02 21:49:51 +01:00
|
|
|
|
2016-08-31 12:35:31 +02:00
|
|
|
init_session_data(sctx->sdata, sctx->cfg);
|
2015-12-02 21:49:51 +01:00
|
|
|
|
|
|
|
*protocol_state = SMTP_STATE_HELO;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-21 21:15:48 +02:00
|
|
|
void send_buffered_response(struct session_ctx *sctx, int starttls, char *resp){
|
2015-12-02 21:49:51 +01:00
|
|
|
int rc;
|
|
|
|
char ssl_error[SMALLBUFSIZE];
|
|
|
|
|
2016-08-21 21:15:48 +02:00
|
|
|
write1(sctx->new_sd, resp, strlen(resp), sctx->sdata->tls, sctx->data->ssl);
|
2015-12-02 21:49:51 +01:00
|
|
|
|
2016-08-21 21:15:48 +02:00
|
|
|
if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sctx->sdata->ttmpfile, resp);
|
2015-12-02 21:49:51 +01:00
|
|
|
memset(resp, 0, MAXBUFSIZE);
|
|
|
|
|
2016-08-21 21:15:48 +02:00
|
|
|
if(starttls == 1 && sctx->sdata->tls == 0){
|
2015-12-02 21:49:51 +01:00
|
|
|
|
2016-08-21 21:15:48 +02:00
|
|
|
if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: waiting for ssl handshake", sctx->sdata->ttmpfile);
|
2015-12-02 21:49:51 +01:00
|
|
|
|
2016-08-21 09:30:09 +02:00
|
|
|
rc = SSL_accept(sctx->data->ssl);
|
2015-12-02 21:49:51 +01:00
|
|
|
|
2016-08-21 21:15:48 +02:00
|
|
|
if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: SSL_accept() finished", sctx->sdata->ttmpfile);
|
2015-12-02 21:49:51 +01:00
|
|
|
|
|
|
|
if(rc == 1){
|
2016-08-21 21:15:48 +02:00
|
|
|
sctx->sdata->tls = 1;
|
2015-12-02 21:49:51 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
ERR_error_string_n(ERR_get_error(), ssl_error, SMALLBUFSIZE);
|
2016-08-21 21:15:48 +02:00
|
|
|
syslog(LOG_PRIORITY, "%s: SSL_accept() failed, rc=%d, errorcode: %d, error text: %s\n", sctx->sdata->ttmpfile, rc, SSL_get_error(sctx->data->ssl, rc), ssl_error);
|
2015-12-02 21:49:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|