diff options
-rw-r--r-- | common/smtppass.c | 125 | ||||
-rw-r--r-- | common/smtppass.h | 6 | ||||
-rw-r--r-- | common/sppriv.h | 5 | ||||
-rw-r--r-- | src/proxsmtpd.c | 10 |
4 files changed, 126 insertions, 20 deletions
diff --git a/common/smtppass.c b/common/smtppass.c index 7fcafd0..0a85f4e 100644 --- a/common/smtppass.c +++ b/common/smtppass.c @@ -180,6 +180,9 @@ spthread_t; #define CFG_USER "User" #define CFG_PIDFILE "PidFile" #define CFG_XCLIENT "XClient" +#define CFG_SKIP "Skip" + +#define VAL_AUTHENTICATED "authenticated" /* ----------------------------------------------------------------------- * DEFAULT SETTINGS @@ -216,6 +219,7 @@ static int parse_config_file(const char* configfile); static char* parse_address(char* line); static char* parse_xforward(char* line, const char* part); static const char* get_successful_rsp(const char* line, int* cont); +static const char* get_continue_rsp(const char* line); static void do_server_noop(spctx_t* ctx); /* Used externally in some cases */ @@ -885,6 +889,37 @@ static int make_connections(spctx_t* ctx, int client) * SMTP HANDLING */ +static int data_passthru(spctx_t* ctx) +{ + int r, count = 0; + const char* data; + + while((r = sp_read_data(ctx, &data)) != 0) + { + if(r < 0) + return -1; /* Message already printed */ + + count += r; + + if(spio_write_data_raw(ctx, &(ctx->server), (unsigned char*)data, r) == -1) + return -1; + + } + + sp_messagex(ctx, LOG_DEBUG, "skipped %d data bytes", count); + return count; +} + +static int should_skip_processing(spctx_t* ctx) +{ + if(ctx->authenticated && (g_state.skip & SKIP_AUTHENTICATED)) + { + sp_messagex(ctx, LOG_DEBUG, "skipping data processing because client is authenticated"); + return 1; + } + return 0; +} + static int smtp_passthru(spctx_t* ctx) { char* t; @@ -896,6 +931,7 @@ static int smtp_passthru(spctx_t* ctx) int first_rsp = 1; /* The first 220 response from server to be filtered */ int filter_host = 0; /* Next response is 250 hostname, which we change */ int auth_started = 0; /* Started performing authentication */ + int data_mode = 0; /* Skip processing of data */ /* XCLIENT is for use in access control */ int xclient_sup = 0; /* Is XCLIENT supported? */ @@ -939,6 +975,9 @@ static int smtp_passthru(spctx_t* ctx) /* Only valid after EHLO or HELO commands */ filter_host = 0; + /* Only valid after a DATA command (when should skip) */ + data_mode = 0; + /* * At this point we may want to send our XCLIENT. This is a per * connection command. @@ -962,26 +1001,30 @@ static int smtp_passthru(spctx_t* ctx) /* Handle the DATA section via our AV checker */ if(is_first_word(C_LINE, DATA_CMD, KL(DATA_CMD))) { - /* Send back the intermediate response to the client */ - if(spio_write_data(ctx, &(ctx->client), SMTP_DATAINTERMED) == -1) - RETURN(-1); + if(should_skip_processing (ctx)) + { + data_mode = 1; + } + else + { + /* + * Now go into scan mode. This also handles the eventual + * sending of the data to the server, making the av check + * transparent + */ - /* - * Now go into scan mode. This also handles the eventual - * sending of the data to the server, making the av check - * transparent - */ - if(cb_check_data(ctx) == -1) - RETURN(-1); + if(cb_check_data(ctx) == -1) + RETURN(-1); - /* Print the log out for this email */ - sp_messagex(ctx, LOG_INFO, "%s", ctx->logline); + /* Print the log out for this email */ + sp_messagex(ctx, LOG_INFO, "%s", ctx->logline); - /* Done with that email */ - cleanup_context(ctx); + /* Done with that email */ + cleanup_context(ctx); - /* Command handled */ - continue; + /* Command handled */ + continue; + } } /* @@ -1043,7 +1086,6 @@ static int smtp_passthru(spctx_t* ctx) else if(is_first_word(C_LINE, AUTH_CMD, KL(AUTH_CMD))) { - sp_messagex(ctx, LOG_DEBUG, "Tracking authentication"); auth_started = 1; } @@ -1094,6 +1136,16 @@ static int smtp_passthru(spctx_t* ctx) } } + /* + * If we're preparing for data mode, and didn't get a satisfactory + * repsonse from server then cancel it. + */ + if(data_mode && !get_continue_rsp(S_LINE)) + { + sp_messagex(ctx, LOG_DEBUG, "server did not accept data"); + data_mode = 0; + } + if((p = get_successful_rsp(S_LINE, &cont)) != NULL) { /* @@ -1236,9 +1288,18 @@ static int smtp_passthru(spctx_t* ctx) } } + /* Send the line to the client */ if(spio_write_data(ctx, &(ctx->client), S_LINE) == -1) RETURN(-1); + /* Should we enter data passthru mode? */ + if(data_mode) + { + data_mode = 0; + if(data_passthru(ctx) < 0) + RETURN(-1); + } + continue; } } @@ -1315,6 +1376,20 @@ static char* parse_xforward(char* line, const char* part) return t; } +static const char* get_continue_rsp(const char* line) +{ + /* + * We check for '3XX xxx' type replies + */ + + line = trim_start(line); + + if(line[0] == '3' && isdigit(line[1]) && isdigit(line[2]) && line[3] == ' ') + return line + 4; + + return NULL; +} + static const char* get_successful_rsp(const char* line, int* cont) { /* @@ -1368,6 +1443,12 @@ void sp_add_log(spctx_t* ctx, char* prefix, char* line) trim_end(t); } +int sp_start_data(spctx_t* ctx) +{ + /* Send back the intermediate response to the client */ + return spio_write_data(ctx, &(ctx->client), SMTP_DATAINTERMED); +} + int sp_read_data(spctx_t* ctx, const char** data) { int r; @@ -2095,6 +2176,16 @@ int sp_parse_option(const char* name, const char* value) ret = 1; } + else if(strcasecmp(CFG_SKIP, name) == 0) + { + if(strcasecmp(value, VAL_AUTHENTICATED) == 0) + g_state.skip |= SKIP_AUTHENTICATED; + else + errx(2, "invalid value for " CFG_SKIP + " (must specify '" VAL_AUTHENTICATED "')"); + return 1; + } + /* Always pass through to program */ if(cb_parse_option(name, value) == 1) ret = 1; diff --git a/common/smtppass.h b/common/smtppass.h index 0fbe458..7f1892e 100644 --- a/common/smtppass.h +++ b/common/smtppass.h @@ -191,6 +191,12 @@ int sp_parse_option(const char* name, const char* option); void sp_add_log(spctx_t* ctx, char* prefix, char* line); /* + * Tells client to start sending data. Sends appropriate + * response code. + */ +int sp_start_data(spctx_t* ctx); + +/* * Reads a line of DATA from client. Or less than a line if * line is longer than LINE_LENGTH. No trimming or anything * is done on the read line. This will end automatically diff --git a/common/sppriv.h b/common/sppriv.h index 777025f..883097d 100644 --- a/common/sppriv.h +++ b/common/sppriv.h @@ -41,6 +41,10 @@ #include "smtppass.h" +enum { + SKIP_AUTHENTICATED = 0x01, +}; + typedef struct spstate { /* Settings ------------------------------- */ @@ -54,6 +58,7 @@ typedef struct spstate const char* user; /* User to run as */ const char* pidfile; /* The pid file for daemon */ const char* header; /* A header to include in the email */ + int skip; /* Various types of email to skip processing */ struct sockaddr_any outaddr; /* The outgoing address */ const char* outname; diff --git a/src/proxsmtpd.c b/src/proxsmtpd.c index 407bcaf..9402641 100644 --- a/src/proxsmtpd.c +++ b/src/proxsmtpd.c @@ -229,12 +229,16 @@ int cb_check_data(spctx_t* ctx) if(g_pxstate.filter_type == FILTER_REJECT) { - if(sp_cache_data(ctx) < 0 || - sp_fail_data(ctx, g_pxstate.reject) < 0) + if(sp_fail_data(ctx, g_pxstate.reject) < 0) return -1; /* Message already printed */ return 0; } - else if(!g_pxstate.command) + + /* Tell client to start sending data */ + if(sp_start_data (ctx) < 0) + return -1; /* Message already printed */ + + if(!g_pxstate.command) { sp_messagex(ctx, LOG_WARNING, "no filter command specified. passing message through"); |