postscreen_smtpd.c [plain text]
#include <sys_defs.h>
#include <string.h>
#include <ctype.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif
#include <msg.h>
#include <stringops.h>
#include <mymalloc.h>
#include <iostuff.h>
#include <vstring.h>
#include <mail_params.h>
#include <mail_proto.h>
#include <is_header.h>
#include <string_list.h>
#include <maps.h>
#include <ehlo_mask.h>
#include <lex_822.h>
#include <tls.h>
#include <postscreen.h>
#define PSC_SMTPD_HAVE_PUSH_BACK(state) (0)
#define PSC_SMTPD_PUSH_BACK_CHAR(state, ch) \
vstream_ungetc((state)->smtp_client_stream, (ch))
#define PSC_SMTPD_NEXT_CHAR(state) \
VSTREAM_GETC((state)->smtp_client_stream)
#define PSC_SMTPD_BUFFER_EMPTY(state) \
(!PSC_SMTPD_HAVE_PUSH_BACK(state) \
&& vstream_peek((state)->smtp_client_stream) <= 0)
#define PSC_SMTPD_PEEK_DATA(state) \
vstream_peek_data((state)->smtp_client_stream)
#define PSC_SMTPD_PEEK_LEN(state) \
vstream_peek((state)->smtp_client_stream)
static char *psc_smtpd_greeting;
static char *psc_smtpd_helo_reply;
static char *psc_smtpd_ehlo_reply_plain;
static char *psc_smtpd_ehlo_reply_tls;
static char *psc_smtpd_timeout_reply;
static char *psc_smtpd_421_reply;
static void psc_smtpd_time_event(int, char *);
static void psc_smtpd_read_event(int, char *);
#define PSC_RESUME_SMTP_CMD_EVENTS(state) do { \
PSC_READ_EVENT_REQUEST2(vstream_fileno((state)->smtp_client_stream), \
psc_smtpd_read_event, psc_smtpd_time_event, \
(char *) (state), PSC_EFF_CMD_TIME_LIMIT); \
if (!PSC_SMTPD_BUFFER_EMPTY(state)) \
psc_smtpd_read_event(EVENT_READ, (char *) state); \
} while (0)
#define PSC_SUSPEND_SMTP_CMD_EVENTS(state) \
PSC_CLEAR_EVENT_REQUEST(vstream_fileno((state)->smtp_client_stream), \
psc_smtpd_time_event, (char *) (state));
#define PSC_SMTPD_NEXT_TOKEN(ptr) mystrtok(&(ptr), " ")
static MAPS *psc_ehlo_discard_maps;
static int psc_ehlo_discard_mask;
static DICT *psc_cmd_filter;
#define PSC_CLEAR_EVENT_DROP_SESSION_STATE(state, event, reply) do { \
PSC_CLEAR_EVENT_REQUEST(vstream_fileno((state)->smtp_client_stream), \
(event), (char *) (state)); \
PSC_DROP_SESSION_STATE((state), (reply)); \
} while (0);
#define PSC_CLEAR_EVENT_HANGUP(state, event) do { \
PSC_CLEAR_EVENT_REQUEST(vstream_fileno((state)->smtp_client_stream), \
(event), (char *) (state)); \
psc_hangup_event(state); \
} while (0);
static int psc_helo_cmd(PSC_STATE *state, char *args)
{
char *helo_name = PSC_SMTPD_NEXT_TOKEN(args);
if (helo_name == 0)
return (PSC_SEND_REPLY(state, "501 Syntax: HELO hostname\r\n"));
PSC_STRING_UPDATE(state->helo_name, helo_name);
PSC_STRING_RESET(state->sender);
return (PSC_SEND_REPLY(state, psc_smtpd_helo_reply));
}
static void psc_smtpd_format_ehlo_reply(VSTRING *buf, int discard_mask
)
{
const char *myname = "psc_smtpd_format_ehlo_reply";
int saved_len = 0;
if (msg_verbose)
msg_info("%s: discard_mask %s", myname, str_ehlo_mask(discard_mask));
#define PSC_EHLO_APPEND(save, buf, fmt) do { \
(save) = LEN(buf); \
vstring_sprintf_append((buf), (fmt)); \
} while (0)
#define PSC_EHLO_APPEND1(save, buf, fmt, arg1) do { \
(save) = LEN(buf); \
vstring_sprintf_append((buf), (fmt), (arg1)); \
} while (0)
vstring_sprintf(psc_temp, "250-%s\r\n", var_myhostname);
if ((discard_mask & EHLO_MASK_SIZE) == 0) {
if (var_message_limit)
PSC_EHLO_APPEND1(saved_len, psc_temp, "250-SIZE %lu\r\n",
(unsigned long) var_message_limit);
else
PSC_EHLO_APPEND(saved_len, psc_temp, "250-SIZE\r\n");
}
if ((discard_mask & EHLO_MASK_VRFY) == 0 && var_disable_vrfy_cmd == 0)
PSC_EHLO_APPEND(saved_len, psc_temp, "250-VRFY\r\n");
if ((discard_mask & EHLO_MASK_ETRN) == 0)
PSC_EHLO_APPEND(saved_len, psc_temp, "250-ETRN\r\n");
if ((discard_mask & EHLO_MASK_STARTTLS) == 0 && var_psc_use_tls)
PSC_EHLO_APPEND(saved_len, psc_temp, "250-STARTTLS\r\n");
#ifdef TODO_SASL_AUTH
if ((discard_mask & EHLO_MASK_AUTH) == 0 && sasl_mechanism_list
&& (!var_psc_tls_auth_only || (discard_mask & EHLO_MASK_STARTTLS))) {
PSC_EHLO_APPEND1(saved_len, psc_temp, "AUTH %s", sasl_mechanism_list);
if (var_broken_auth_clients)
PSC_EHLO_APPEND1(saved_len, psc_temp, "AUTH=%s", sasl_mechanism_list);
}
#endif
if ((discard_mask & EHLO_MASK_ENHANCEDSTATUSCODES) == 0)
PSC_EHLO_APPEND(saved_len, psc_temp, "250-ENHANCEDSTATUSCODES\r\n");
if ((discard_mask & EHLO_MASK_8BITMIME) == 0)
PSC_EHLO_APPEND(saved_len, psc_temp, "250-8BITMIME\r\n");
if ((discard_mask & EHLO_MASK_DSN) == 0)
PSC_EHLO_APPEND(saved_len, psc_temp, "250-DSN\r\n");
STR(psc_temp)[saved_len + 3] = ' ';
}
static int psc_ehlo_cmd(PSC_STATE *state, char *args)
{
char *helo_name = PSC_SMTPD_NEXT_TOKEN(args);
const char *ehlo_words;
int discard_mask;
char *reply;
if (helo_name == 0)
return (PSC_SEND_REPLY(state, "501 Syntax: EHLO hostname\r\n"));
PSC_STRING_UPDATE(state->helo_name, helo_name);
PSC_STRING_RESET(state->sender);
state->protocol = MAIL_PROTO_ESMTP;
if (psc_ehlo_discard_maps != 0
&& (ehlo_words = psc_maps_find(psc_ehlo_discard_maps,
state->smtp_client_addr, 0)) != 0
&& (discard_mask = ehlo_mask(ehlo_words)) != psc_ehlo_discard_mask) {
if (discard_mask && !(discard_mask & EHLO_MASK_SILENT))
msg_info("[%s]%s: discarding EHLO keywords: %s",
PSC_CLIENT_ADDR_PORT(state), str_ehlo_mask(discard_mask));
if (state->flags & PSC_STATE_FLAG_USING_TLS)
discard_mask |= EHLO_MASK_STARTTLS;
psc_smtpd_format_ehlo_reply(psc_temp, discard_mask);
reply = STR(psc_temp);
state->ehlo_discard_mask = discard_mask;
} else if (psc_ehlo_discard_maps && psc_ehlo_discard_maps->error) {
msg_fatal("%s lookup error for %s",
psc_ehlo_discard_maps->title, state->smtp_client_addr);
} else if (state->flags & PSC_STATE_FLAG_USING_TLS) {
reply = psc_smtpd_ehlo_reply_tls;
state->ehlo_discard_mask = psc_ehlo_discard_mask | EHLO_MASK_STARTTLS;
} else {
reply = psc_smtpd_ehlo_reply_plain;
state->ehlo_discard_mask = psc_ehlo_discard_mask;
}
return (PSC_SEND_REPLY(state, reply));
}
static void psc_starttls_resume(int unused_event, char *context)
{
const char *myname = "psc_starttls_resume";
PSC_STATE *state = (PSC_STATE *) context;
if (state->flags & PSC_STATE_FLAG_USING_TLS) {
PSC_STRING_RESET(state->helo_name);
PSC_STRING_RESET(state->sender);
#ifdef TODO_SASL_AUTH
#endif
}
PSC_RESUME_SMTP_CMD_EVENTS(state);
}
static int psc_starttls_cmd(PSC_STATE *state, char *args)
{
const char *myname = "psc_starttls_cmd";
if (PSC_SMTPD_NEXT_TOKEN(args) != 0)
return (PSC_SEND_REPLY(state, "501 Syntax: EHLO hostname\r\n"));
if (state->flags & PSC_STATE_FLAG_USING_TLS)
return (PSC_SEND_REPLY(state,
"554 5.5.1 Error: TLS already active\r\n"));
if (var_psc_use_tls == 0 || (state->ehlo_discard_mask & EHLO_MASK_STARTTLS))
return (PSC_SEND_REPLY(state,
"502 5.5.1 Error: command not implemented\r\n"));
PSC_SUSPEND_SMTP_CMD_EVENTS(state);
psc_starttls_open(state, psc_starttls_resume);
return (0);
}
static char *psc_extract_addr(VSTRING *result, const char *string)
{
const unsigned char *cp = (const unsigned char *) string;
char *addr;
char *colon;
int stop_at;
int inquote = 0;
while (*cp && *cp == ' ')
cp++;
if (*cp == '<') {
cp++;
stop_at = '>';
} else {
stop_at = ' ';
}
VSTRING_RESET(result);
for ( ; *cp; cp++) {
if (!inquote && *cp == stop_at)
break;
if (*cp == '"') {
inquote = !inquote;
} else {
if (*cp == '\\' && *++cp == 0)
break;
VSTRING_ADDCH(result, *cp);
}
}
VSTRING_TERMINATE(result);
addr = STR(result);
if (*addr == '@' && (colon = strchr(addr, ':')) != 0)
addr = colon + 1;
return (addr);
}
static int psc_mail_cmd(PSC_STATE *state, char *args)
{
char *colon;
char *addr;
if (var_psc_helo_required && state->helo_name == 0)
return (PSC_SEND_REPLY(state,
"503 5.5.1 Error: send HELO/EHLO first\r\n"));
if (state->sender != 0)
return (PSC_SEND_REPLY(state,
"503 5.5.1 Error: nested MAIL command\r\n"));
if (args == 0 || (colon = strchr(args, ':')) == 0)
return (PSC_SEND_REPLY(state,
"501 5.5.4 Syntax: MAIL FROM:<address>\r\n"));
if ((addr = psc_extract_addr(psc_temp, colon + 1)) == 0)
return (PSC_SEND_REPLY(state,
"501 5.1.7 Bad sender address syntax\r\n"));
PSC_STRING_UPDATE(state->sender, addr);
return (PSC_SEND_REPLY(state, "250 2.1.0 Ok\r\n"));
}
static char *psc_soften_reply(const char *reply)
{
static VSTRING *buf = 0;
if (buf == 0)
buf = vstring_alloc(100);
vstring_strcpy(buf, reply);
if (reply[0] == '5')
STR(buf)[0] = '4';
if (reply[4] == '5')
STR(buf)[4] = '4';
return (STR(buf));
}
static int psc_rcpt_cmd(PSC_STATE *state, char *args)
{
char *colon;
char *addr;
if (state->sender == 0)
return (PSC_SEND_REPLY(state,
"503 5.5.1 Error: need MAIL command\r\n"));
if (args == 0 || (colon = strchr(args, ':')) == 0)
return (PSC_SEND_REPLY(state,
"501 5.5.4 Syntax: RCPT TO:<address>\r\n"));
if ((addr = psc_extract_addr(psc_temp, colon + 1)) == 0)
return (PSC_SEND_REPLY(state,
"501 5.1.3 Bad recipient address syntax\r\n"));
msg_info("NOQUEUE: reject: RCPT from [%s]:%s: %.*s; "
"from=<%s>, to=<%s>, proto=%s, helo=<%s>",
PSC_CLIENT_ADDR_PORT(state),
(int) strlen(state->rcpt_reply) - 2,
var_soft_bounce == 0 ? state->rcpt_reply :
psc_soften_reply(state->rcpt_reply),
state->sender, addr, state->protocol,
state->helo_name ? state->helo_name : "");
return (PSC_SEND_REPLY(state, state->rcpt_reply));
}
static int psc_data_cmd(PSC_STATE *state, char *args)
{
if (PSC_SMTPD_NEXT_TOKEN(args) != 0)
return (PSC_SEND_REPLY(state,
"501 5.5.4 Syntax: DATA\r\n"));
if (state->sender == 0)
return (PSC_SEND_REPLY(state,
"503 5.5.1 Error: need RCPT command\r\n"));
return (PSC_SEND_REPLY(state,
"554 5.5.1 Error: no valid recipients\r\n"));
}
static int psc_rset_cmd(PSC_STATE *state, char *unused_args)
{
PSC_STRING_RESET(state->sender);
return (PSC_SEND_REPLY(state, "250 2.0.0 Ok\r\n"));
}
static int psc_noop_cmd(PSC_STATE *state, char *unused_args)
{
return (PSC_SEND_REPLY(state, "250 2.0.0 Ok\r\n"));
}
static int psc_vrfy_cmd(PSC_STATE *state, char *args)
{
if (PSC_SMTPD_NEXT_TOKEN(args) == 0)
return (PSC_SEND_REPLY(state,
"501 5.5.4 Syntax: VRFY address\r\n"));
if (var_psc_disable_vrfy)
return (PSC_SEND_REPLY(state,
"502 5.5.1 VRFY command is disabled\r\n"));
return (PSC_SEND_REPLY(state, state->rcpt_reply));
}
static int psc_etrn_cmd(PSC_STATE *state, char *args)
{
if (var_psc_helo_required && state->helo_name == 0)
return (PSC_SEND_REPLY(state,
"503 5.5.1 Error: send HELO/EHLO first\r\n"));
if (PSC_SMTPD_NEXT_TOKEN(args) == 0)
return (PSC_SEND_REPLY(state,
"500 Syntax: ETRN domain\r\n"));
return (PSC_SEND_REPLY(state, "458 Unable to queue messages\r\n"));
}
static int psc_quit_cmd(PSC_STATE *state, char *unused_args)
{
const char *myname = "psc_quit_cmd";
PSC_CLEAR_EVENT_DROP_SESSION_STATE(state, psc_smtpd_time_event,
"221 2.0.0 Bye\r\n");
return (0);
}
static void psc_smtpd_time_event(int event, char *context)
{
const char *myname = "psc_smtpd_time_event";
PSC_STATE *state = (PSC_STATE *) context;
if (msg_verbose > 1)
msg_info("%s: sq=%d cq=%d event %d on smtp socket %d from [%s]:%s flags=%s",
myname, psc_post_queue_length, psc_check_queue_length,
event, vstream_fileno(state->smtp_client_stream),
state->smtp_client_addr, state->smtp_client_port,
psc_print_state_flags(state->flags, myname));
msg_info("COMMAND TIME LIMIT from [%s]:%s", PSC_CLIENT_ADDR_PORT(state));
PSC_CLEAR_EVENT_DROP_SESSION_STATE(state, psc_smtpd_time_event,
psc_smtpd_timeout_reply);
}
typedef struct {
const char *name;
int (*action) (PSC_STATE *, char *);
int flags;
} PSC_SMTPD_COMMAND;
#define PSC_SMTPD_CMD_FLAG_NONE (0)
#define PSC_SMTPD_CMD_FLAG_ENABLE (1<<0)
#define PSC_SMTPD_CMD_FLAG_DESTROY (1<<1)
#define PSC_SMTPD_CMD_FLAG_PRE_TLS (1<<2)
#define PSC_SMTPD_CMD_FLAG_SUSPEND (1<<3)
static const PSC_SMTPD_COMMAND command_table[] = {
"HELO", psc_helo_cmd, PSC_SMTPD_CMD_FLAG_ENABLE | PSC_SMTPD_CMD_FLAG_PRE_TLS,
"EHLO", psc_ehlo_cmd, PSC_SMTPD_CMD_FLAG_ENABLE | PSC_SMTPD_CMD_FLAG_PRE_TLS,
"STARTTLS", psc_starttls_cmd, PSC_SMTPD_CMD_FLAG_ENABLE | PSC_SMTPD_CMD_FLAG_PRE_TLS | PSC_SMTPD_CMD_FLAG_SUSPEND,
"XCLIENT", psc_noop_cmd, PSC_SMTPD_CMD_FLAG_NONE,
"XFORWARD", psc_noop_cmd, PSC_SMTPD_CMD_FLAG_NONE,
"AUTH", psc_noop_cmd, PSC_SMTPD_CMD_FLAG_NONE,
"MAIL", psc_mail_cmd, PSC_SMTPD_CMD_FLAG_ENABLE,
"RCPT", psc_rcpt_cmd, PSC_SMTPD_CMD_FLAG_ENABLE,
"DATA", psc_data_cmd, PSC_SMTPD_CMD_FLAG_ENABLE,
"RSET", psc_rset_cmd, PSC_SMTPD_CMD_FLAG_ENABLE,
"NOOP", psc_noop_cmd, PSC_SMTPD_CMD_FLAG_ENABLE | PSC_SMTPD_CMD_FLAG_PRE_TLS,
"VRFY", psc_vrfy_cmd, PSC_SMTPD_CMD_FLAG_ENABLE,
"ETRN", psc_etrn_cmd, PSC_SMTPD_CMD_FLAG_ENABLE,
"QUIT", psc_quit_cmd, PSC_SMTPD_CMD_FLAG_ENABLE | PSC_SMTPD_CMD_FLAG_DESTROY | PSC_SMTPD_CMD_FLAG_PRE_TLS,
0,
};
static void psc_smtpd_read_event(int event, char *context)
{
const char *myname = "psc_smtpd_read_event";
PSC_STATE *state = (PSC_STATE *) context;
int ch;
struct cmd_trans {
int state;
int want;
int next_state;
};
#define PSC_SMTPD_CMD_ST_ANY 0
#define PSC_SMTPD_CMD_ST_CR 1
#define PSC_SMTPD_CMD_ST_CR_LF 2
static const struct cmd_trans cmd_trans[] = {
PSC_SMTPD_CMD_ST_ANY, '\r', PSC_SMTPD_CMD_ST_CR,
PSC_SMTPD_CMD_ST_CR, '\n', PSC_SMTPD_CMD_ST_CR_LF,
0, 0, 0,
};
const struct cmd_trans *transp;
char *cmd_buffer_ptr;
char *command;
const PSC_SMTPD_COMMAND *cmdp;
int write_stat;
if (msg_verbose > 1)
msg_info("%s: sq=%d cq=%d event %d on smtp socket %d from [%s]:%s flags=%s",
myname, psc_post_queue_length, psc_check_queue_length,
event, vstream_fileno(state->smtp_client_stream),
state->smtp_client_addr, state->smtp_client_port,
psc_print_state_flags(state->flags, myname));
for (;;) {
for (;;) {
if ((ch = PSC_SMTPD_NEXT_CHAR(state)) == VSTREAM_EOF) {
PSC_CLEAR_EVENT_HANGUP(state, psc_smtpd_time_event);
return;
}
if (state->read_state == PSC_SMTPD_CMD_ST_ANY
&& VSTRING_LEN(state->cmd_buffer) >= var_line_limit) {
msg_info("COMMAND LENGTH LIMIT from [%s]:%s",
PSC_CLIENT_ADDR_PORT(state));
PSC_CLEAR_EVENT_DROP_SESSION_STATE(state, psc_smtpd_time_event,
psc_smtpd_421_reply);
return;
}
VSTRING_ADDCH(state->cmd_buffer, ch);
for (transp = cmd_trans; transp->state != state->read_state; transp++)
if (transp->want == 0)
msg_panic("%s: command_read: unknown state: %d",
myname, state->read_state);
if (ch == transp->want)
state->read_state = transp->next_state;
else if (ch == cmd_trans[0].want)
state->read_state = cmd_trans[0].next_state;
else
state->read_state = PSC_SMTPD_CMD_ST_ANY;
if (state->read_state == PSC_SMTPD_CMD_ST_CR_LF) {
vstring_truncate(state->cmd_buffer,
VSTRING_LEN(state->cmd_buffer) - 2);
break;
}
if (ch == '\n') {
if ((state->flags & PSC_STATE_MASK_BARLF_TODO_SKIP)
== PSC_STATE_FLAG_BARLF_TODO) {
msg_info("BARE NEWLINE from [%s]:%s",
PSC_CLIENT_ADDR_PORT(state));
PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_BARLF_FAIL);
PSC_UNPASS_SESSION_STATE(state, PSC_STATE_FLAG_BARLF_PASS);
state->barlf_stamp = PSC_TIME_STAMP_DISABLED;
PSC_SKIP_SESSION_STATE(state, "bare newline test",
PSC_STATE_FLAG_BARLF_SKIP);
switch (psc_barlf_action) {
case PSC_ACT_DROP:
PSC_CLEAR_EVENT_DROP_SESSION_STATE(state,
psc_smtpd_time_event,
"521 5.5.1 Protocol error\r\n");
return;
case PSC_ACT_ENFORCE:
PSC_ENFORCE_SESSION_STATE(state,
"550 5.5.1 Protocol error\r\n");
break;
case PSC_ACT_IGNORE:
PSC_UNFAIL_SESSION_STATE(state,
PSC_STATE_FLAG_BARLF_FAIL);
PSC_PASS_SESSION_STATE(state, "bare newline test",
PSC_STATE_FLAG_BARLF_PASS);
state->barlf_stamp = event_time() + psc_min_ttl;
break;
default:
msg_panic("%s: unknown bare_newline action value %d",
myname, psc_barlf_action);
}
}
vstring_truncate(state->cmd_buffer,
VSTRING_LEN(state->cmd_buffer) - 1);
break;
}
if (PSC_SMTPD_BUFFER_EMPTY(state))
return;
}
VSTRING_TERMINATE(state->cmd_buffer);
if (psc_cmd_filter != 0) {
const char *cp;
for (cp = STR(state->cmd_buffer); *cp && IS_SPACE_TAB(*cp); cp++)
;
if ((cp = psc_dict_get(psc_cmd_filter, cp)) != 0) {
msg_info("[%s]:%s: replacing command \"%.100s\" with \"%.100s\"",
state->smtp_client_addr, state->smtp_client_port,
STR(state->cmd_buffer), cp);
vstring_strcpy(state->cmd_buffer, cp);
} else if (psc_cmd_filter->error != 0) {
msg_fatal("%s:%s lookup error for \"%.100s\"",
psc_cmd_filter->type, psc_cmd_filter->name, cp);
}
}
state->read_state = PSC_SMTPD_CMD_ST_ANY;
VSTRING_RESET(state->cmd_buffer);
cmd_buffer_ptr = STR(state->cmd_buffer);
if (msg_verbose)
msg_info("< [%s]:%s: %s", state->smtp_client_addr,
state->smtp_client_port, cmd_buffer_ptr);
if ((command = PSC_SMTPD_NEXT_TOKEN(cmd_buffer_ptr)) == 0)
command = "";
for (cmdp = command_table; cmdp->name != 0; cmdp++)
if (strcasecmp(command, cmdp->name) == 0)
break;
if ((state->flags & PSC_STATE_FLAG_SMTPD_X21)
&& cmdp->action != psc_quit_cmd) {
PSC_CLEAR_EVENT_DROP_SESSION_STATE(state, psc_smtpd_time_event,
state->final_reply);
return;
}
if ((state->flags & PSC_STATE_MASK_NSMTP_TODO_SKIP)
== PSC_STATE_FLAG_NSMTP_TODO && cmdp->name == 0
&& (is_header(command)
|| (*var_psc_forbid_cmds
&& string_list_match(psc_forbid_cmds, command)))) {
printable(command, '?');
msg_info("NON-SMTP COMMAND from [%s]:%s %.100s %.100s",
PSC_CLIENT_ADDR_PORT(state), command, cmd_buffer_ptr);
PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_NSMTP_FAIL);
PSC_UNPASS_SESSION_STATE(state, PSC_STATE_FLAG_NSMTP_PASS);
state->nsmtp_stamp = PSC_TIME_STAMP_DISABLED;
PSC_SKIP_SESSION_STATE(state, "non-smtp test",
PSC_STATE_FLAG_NSMTP_SKIP);
switch (psc_nsmtp_action) {
case PSC_ACT_DROP:
PSC_CLEAR_EVENT_DROP_SESSION_STATE(state,
psc_smtpd_time_event,
"521 5.7.0 Error: I can break rules, too. Goodbye.\r\n");
return;
case PSC_ACT_ENFORCE:
PSC_ENFORCE_SESSION_STATE(state,
"550 5.5.1 Protocol error\r\n");
break;
case PSC_ACT_IGNORE:
PSC_UNFAIL_SESSION_STATE(state,
PSC_STATE_FLAG_NSMTP_FAIL);
PSC_PASS_SESSION_STATE(state, "non-smtp test",
PSC_STATE_FLAG_NSMTP_PASS);
state->nsmtp_stamp = event_time() + psc_min_ttl;
break;
default:
msg_panic("%s: unknown non_smtp_command action value %d",
myname, psc_nsmtp_action);
}
}
if ((state->flags & PSC_STATE_MASK_PIPEL_TODO_SKIP)
== PSC_STATE_FLAG_PIPEL_TODO && !PSC_SMTPD_BUFFER_EMPTY(state)) {
printable(command, '?');
escape(psc_temp, PSC_SMTPD_PEEK_DATA(state),
PSC_SMTPD_PEEK_LEN(state) < 100 ?
PSC_SMTPD_PEEK_LEN(state) : 100);
msg_info("COMMAND PIPELINING from [%s]:%s after %.100s: %s",
PSC_CLIENT_ADDR_PORT(state), command, STR(psc_temp));
PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_PIPEL_FAIL);
PSC_UNPASS_SESSION_STATE(state, PSC_STATE_FLAG_PIPEL_PASS);
state->pipel_stamp = PSC_TIME_STAMP_DISABLED;
PSC_SKIP_SESSION_STATE(state, "pipelining test",
PSC_STATE_FLAG_PIPEL_SKIP);
switch (psc_pipel_action) {
case PSC_ACT_DROP:
PSC_CLEAR_EVENT_DROP_SESSION_STATE(state,
psc_smtpd_time_event,
"521 5.5.1 Protocol error\r\n");
return;
case PSC_ACT_ENFORCE:
PSC_ENFORCE_SESSION_STATE(state,
"550 5.5.1 Protocol error\r\n");
break;
case PSC_ACT_IGNORE:
PSC_UNFAIL_SESSION_STATE(state,
PSC_STATE_FLAG_PIPEL_FAIL);
PSC_PASS_SESSION_STATE(state, "pipelining test",
PSC_STATE_FLAG_PIPEL_PASS);
state->pipel_stamp = event_time() + psc_min_ttl;
break;
default:
msg_panic("%s: unknown pipelining action value %d",
myname, psc_pipel_action);
}
}
if (cmdp->action == psc_rcpt_cmd) {
if ((state->flags & PSC_STATE_MASK_BARLF_TODO_PASS_FAIL)
== PSC_STATE_FLAG_BARLF_TODO) {
PSC_PASS_SESSION_STATE(state, "bare newline test",
PSC_STATE_FLAG_BARLF_PASS);
state->barlf_stamp = event_time() + var_psc_barlf_ttl;
}
if ((state->flags & PSC_STATE_MASK_NSMTP_TODO_PASS_FAIL)
== PSC_STATE_FLAG_NSMTP_TODO) {
PSC_PASS_SESSION_STATE(state, "non-smtp test",
PSC_STATE_FLAG_NSMTP_PASS);
state->nsmtp_stamp = event_time() + var_psc_nsmtp_ttl;
}
if ((state->flags & PSC_STATE_MASK_PIPEL_TODO_PASS_FAIL)
== PSC_STATE_FLAG_PIPEL_TODO) {
PSC_PASS_SESSION_STATE(state, "pipelining test",
PSC_STATE_FLAG_PIPEL_PASS);
state->pipel_stamp = event_time() + var_psc_pipel_ttl;
}
}
if (++state->command_count > var_psc_cmd_count
&& cmdp->action != psc_quit_cmd) {
msg_info("COMMAND COUNT LIMIT from [%s]:%s",
PSC_CLIENT_ADDR_PORT(state));
PSC_CLEAR_EVENT_DROP_SESSION_STATE(state, psc_smtpd_time_event,
psc_smtpd_421_reply);
return;
}
if (cmdp->name == 0 || (cmdp->flags & PSC_SMTPD_CMD_FLAG_ENABLE) == 0) {
write_stat = PSC_SEND_REPLY(state,
"502 5.5.2 Error: command not recognized\r\n");
} else if (var_psc_enforce_tls
&& (state->flags & PSC_STATE_FLAG_USING_TLS) == 0
&& (cmdp->flags & PSC_SMTPD_CMD_FLAG_PRE_TLS) == 0) {
write_stat = PSC_SEND_REPLY(state,
"530 5.7.0 Must issue a STARTTLS command first\r\n");
} else {
write_stat = cmdp->action(state, cmd_buffer_ptr);
if (cmdp->flags & PSC_SMTPD_CMD_FLAG_DESTROY)
return;
}
if (write_stat < 0) {
PSC_CLEAR_EVENT_HANGUP(state, psc_smtpd_time_event);
return;
}
if (cmdp->flags & PSC_SMTPD_CMD_FLAG_SUSPEND)
return;
event_request_timer(psc_smtpd_time_event, (char *) state,
PSC_EFF_CMD_TIME_LIMIT);
if (PSC_SMTPD_BUFFER_EMPTY(state))
return;
}
}
void psc_smtpd_tests(PSC_STATE *state)
{
static char *myname = "psc_smtpd_tests";
PSC_BEGIN_TESTS(state, "tests after SMTP handshake");
state->cmd_buffer = vstring_alloc(100);
state->read_state = PSC_SMTPD_CMD_ST_ANY;
if ((state->flags & PSC_STATE_FLAG_SMTPD_X21) == 0) {
state->flags |= PSC_STATE_MASK_SMTPD_TODO;
} else {
state->flags &= ~PSC_STATE_MASK_SMTPD_TODO;
}
if ((state->flags & PSC_STATE_FLAG_PREGR_FAIL) == 0
&& PSC_SEND_REPLY(state, psc_smtpd_greeting) != 0) {
psc_hangup_event(state);
return;
}
PSC_READ_EVENT_REQUEST2(vstream_fileno(state->smtp_client_stream),
psc_smtpd_read_event, psc_smtpd_time_event,
(char *) state, PSC_EFF_CMD_TIME_LIMIT);
}
void psc_smtpd_init(void)
{
vstring_sprintf(psc_temp, "220 %s\r\n", var_smtpd_banner);
psc_smtpd_greeting = mystrdup(STR(psc_temp));
vstring_sprintf(psc_temp, "250 %s\r\n", var_myhostname);
psc_smtpd_helo_reply = mystrdup(STR(psc_temp));
if (*var_psc_tls_level) {
switch (tls_level_lookup(var_psc_tls_level)) {
default:
msg_fatal("Invalid TLS level \"%s\"", var_psc_tls_level);
break;
case TLS_LEV_SECURE:
case TLS_LEV_VERIFY:
case TLS_LEV_FPRINT:
msg_warn("%s: unsupported TLS level \"%s\", using \"encrypt\"",
VAR_PSC_TLS_LEVEL, var_psc_tls_level);
case TLS_LEV_ENCRYPT:
var_psc_enforce_tls = var_psc_use_tls = 1;
break;
case TLS_LEV_MAY:
var_psc_enforce_tls = 0;
var_psc_use_tls = 1;
break;
case TLS_LEV_NONE:
var_psc_enforce_tls = var_psc_use_tls = 0;
break;
}
}
var_psc_use_tls = var_psc_use_tls || var_psc_enforce_tls;
#ifdef TODO_SASL_AUTH
var_psc_tls_auth_only = var_psc_tls_auth_only || var_psc_enforce_tls;
#endif
psc_smtpd_format_ehlo_reply(psc_temp, psc_ehlo_discard_mask);
psc_smtpd_ehlo_reply_plain = mystrdup(STR(psc_temp));
psc_smtpd_format_ehlo_reply(psc_temp,
psc_ehlo_discard_mask | EHLO_MASK_STARTTLS);
psc_smtpd_ehlo_reply_tls = mystrdup(STR(psc_temp));
vstring_sprintf(psc_temp, "421 4.4.2 %s Error: timeout exceeded\r\n",
var_myhostname);
psc_smtpd_timeout_reply = mystrdup(STR(psc_temp));
vstring_sprintf(psc_temp, "421 %s Service unavailable - try again later\r\n",
var_myhostname);
psc_smtpd_421_reply = mystrdup(STR(psc_temp));
if (*var_psc_rej_footer)
psc_expand_init();
}
void psc_smtpd_pre_jail_init(void)
{
if (*var_psc_ehlo_dis_maps)
psc_ehlo_discard_maps = maps_create(VAR_PSC_EHLO_DIS_MAPS,
var_psc_ehlo_dis_maps,
DICT_FLAG_LOCK);
psc_ehlo_discard_mask = ehlo_mask(var_psc_ehlo_dis_words);
if (*var_psc_cmd_filter)
psc_cmd_filter = dict_open(var_psc_cmd_filter, O_RDONLY,
DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
}