#include <sys_defs.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <signal.h>
#include <stddef.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif
#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
#include <vstream.h>
#include <vstring_vstream.h>
#include <stringops.h>
#include <events.h>
#include <smtp_stream.h>
#include <valid_hostname.h>
#include <dict.h>
#include <watchdog.h>
#include <iostuff.h>
#include <split_at.h>
#include <name_code.h>
#include <inet_proto.h>
#ifdef __APPLE_OS_X_SERVER__
#include <dtrace-postfix.h>
#endif
#include <mail_params.h>
#include <mail_version.h>
#include <record.h>
#include <rec_type.h>
#include <mail_proto.h>
#include <cleanup_user.h>
#include <mail_date.h>
#include <mail_conf.h>
#include <off_cvt.h>
#include <debug_peer.h>
#include <mail_error.h>
#include <flush_clnt.h>
#include <mail_stream.h>
#include <mail_queue.h>
#include <tok822.h>
#include <verp_sender.h>
#include <string_list.h>
#include <quote_822_local.h>
#include <lex_822.h>
#include <namadr_list.h>
#include <input_transp.h>
#include <is_header.h>
#include <anvil_clnt.h>
#include <flush_clnt.h>
#include <ehlo_mask.h>
#include <maps.h>
#include <valid_mailhost_addr.h>
#include <dsn_mask.h>
#include <xtext.h>
#include <tls_proxy.h>
#include <verify_sender_addr.h>
#include <mail_server.h>
#include <milter.h>
#include <smtpd_token.h>
#include <smtpd.h>
#include <smtpd_check.h>
#include <smtpd_chat.h>
#include <smtpd_sasl_proto.h>
#include <smtpd_sasl_glue.h>
#include <smtpd_proxy.h>
#include <smtpd_milter.h>
#include <smtpd_expand.h>
#if defined(USE_SASL_AUTH) && defined(USE_TLS)
#include <smtpd_imap.h>
#endif
#include <smtpd_binary.h>
int var_smtpd_rcpt_limit;
int var_smtpd_tmout;
int var_smtpd_soft_erlim;
int var_smtpd_hard_erlim;
int var_queue_minfree;
char *var_smtpd_banner;
char *var_notify_classes;
char *var_client_checks;
char *var_helo_checks;
char *var_mail_checks;
char *var_rcpt_checks;
char *var_etrn_checks;
char *var_data_checks;
char *var_eod_checks;
int var_unk_client_code;
int var_bad_name_code;
int var_unk_name_code;
int var_unk_addr_code;
int var_relay_code;
int var_maps_rbl_code;
int var_map_reject_code;
int var_map_defer_code;
char *var_maps_rbl_domains;
char *var_rbl_reply_maps;
int var_helo_required;
int var_reject_code;
int var_defer_code;
int var_smtpd_err_sleep;
int var_non_fqdn_code;
char *var_error_rcpt;
int var_smtpd_delay_reject;
char *var_rest_classes;
int var_strict_rfc821_env;
bool var_disable_vrfy_cmd;
char *var_canonical_maps;
char *var_rcpt_canon_maps;
char *var_virt_alias_maps;
char *var_virt_mailbox_maps;
char *var_alias_maps;
char *var_local_rcpt_maps;
bool var_allow_untrust_route;
int var_smtpd_junk_cmd_limit;
int var_smtpd_rcpt_overlim;
bool var_smtpd_sasl_enable;
bool var_smtpd_sasl_auth_hdr;
char *var_smtpd_sasl_opts;
char *var_smtpd_sasl_path;
char *var_cyrus_conf_path;
char *var_smtpd_sasl_realm;
char *var_smtpd_sasl_exceptions_networks;
char *var_smtpd_sasl_type;
char *var_filter_xport;
bool var_broken_auth_clients;
char *var_perm_mx_networks;
char *var_smtpd_snd_auth_maps;
char *var_smtpd_noop_cmds;
char *var_smtpd_null_key;
int var_smtpd_hist_thrsh;
char *var_smtpd_exp_filter;
char *var_def_rbl_reply;
int var_unv_from_rcode;
int var_unv_rcpt_rcode;
int var_unv_from_dcode;
int var_unv_rcpt_dcode;
char *var_unv_from_why;
char *var_unv_rcpt_why;
int var_mul_rcpt_code;
char *var_relay_rcpt_maps;
int var_local_rcpt_code;
int var_virt_alias_code;
int var_virt_mailbox_code;
int var_relay_rcpt_code;
char *var_verp_clients;
int var_show_unk_rcpt_table;
int var_verify_poll_count;
int var_verify_poll_delay;
char *var_smtpd_proxy_filt;
int var_smtpd_proxy_tmout;
char *var_smtpd_proxy_ehlo;
char *var_smtpd_proxy_opts;
char *var_input_transp;
int var_smtpd_policy_tmout;
int var_smtpd_policy_idle;
int var_smtpd_policy_ttl;
char *var_xclient_hosts;
char *var_xforward_hosts;
bool var_smtpd_rej_unl_from;
bool var_smtpd_rej_unl_rcpt;
char *var_smtpd_forbid_cmds;
int var_smtpd_crate_limit;
int var_smtpd_cconn_limit;
int var_smtpd_cmail_limit;
int var_smtpd_crcpt_limit;
int var_smtpd_cntls_limit;
char *var_smtpd_hoggers;
char *var_local_rwr_clients;
char *var_smtpd_ehlo_dis_words;
char *var_smtpd_ehlo_dis_maps;
char *var_smtpd_tls_level;
bool var_smtpd_use_tls;
bool var_smtpd_enforce_tls;
bool var_smtpd_tls_wrappermode;
bool var_smtpd_tls_auth_only;
char *var_smtpd_cmd_filter;
char *var_smtpd_rej_footer;
#ifdef USE_TLS
char *var_smtpd_relay_ccerts;
char *var_smtpd_sasl_tls_opts;
int var_smtpd_starttls_tmout;
char *var_smtpd_tls_CAfile;
char *var_smtpd_tls_CApath;
bool var_smtpd_tls_ask_ccert;
int var_smtpd_tls_ccert_vd;
char *var_smtpd_tls_cert_file;
char *var_smtpd_tls_mand_ciph;
char *var_smtpd_tls_excl_ciph;
char *var_smtpd_tls_mand_excl;
char *var_smtpd_tls_dcert_file;
char *var_smtpd_tls_dh1024_param_file;
char *var_smtpd_tls_dh512_param_file;
char *var_smtpd_tls_dkey_file;
char *var_smtpd_tls_key_file;
char *var_smtpd_tls_loglevel;
char *var_smtpd_tls_mand_proto;
bool var_smtpd_tls_received_header;
bool var_smtpd_tls_req_ccert;
int var_smtpd_tls_scache_timeout;
bool var_smtpd_tls_set_sessid;
char *var_smtpd_tls_fpt_dgst;
char *var_smtpd_tls_ciph;
char *var_smtpd_tls_proto;
char *var_smtpd_tls_eecdh;
char *var_smtpd_tls_eccert_file;
char *var_smtpd_tls_eckey_file;
#endif
bool var_smtpd_peername_lookup;
int var_plaintext_code;
bool var_smtpd_delay_open;
char *var_smtpd_milters;
int var_milt_conn_time;
int var_milt_cmd_time;
int var_milt_msg_time;
char *var_milt_protocol;
char *var_milt_def_action;
char *var_milt_daemon_name;
char *var_milt_v;
char *var_milt_conn_macros;
char *var_milt_helo_macros;
char *var_milt_mail_macros;
char *var_milt_rcpt_macros;
char *var_milt_data_macros;
char *var_milt_eoh_macros;
char *var_milt_eod_macros;
char *var_milt_unk_macros;
bool var_smtpd_client_port_log;
char *var_stress;
char *var_reject_tmpf_act;
char *var_unk_name_tf_act;
char *var_unk_addr_tf_act;
char *var_unv_rcpt_tf_act;
char *var_unv_from_tf_act;
bool var_smtpd_rec_deadline;
int smtpd_proxy_opts;
#ifdef USE_TLSPROXY
char *var_tlsproxy_service;
#endif
#ifdef __APPLE_OS_X_SERVER__
bool var_smtpd_use_pw_server;
int smtpd_pw_server_sasl_opts;
char *var_smtpd_pw_server_opts;
#endif
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
static MAPS *ehlo_discard_maps;
#define VERP_CMD "XVERP"
#define VERP_CMD_LEN 5
static NAMADR_LIST *verp_clients;
static NAMADR_LIST *xclient_hosts;
static int xclient_allowed;
static NAMADR_LIST *xforward_hosts;
static int xforward_allowed;
ANVIL_CLNT *anvil_clnt;
static NAMADR_LIST *hogger_list;
int smtpd_input_transp_mask;
static void helo_reset(SMTPD_STATE *);
static void mail_reset(SMTPD_STATE *);
static void rcpt_reset(SMTPD_STATE *);
static void tls_reset(SMTPD_STATE *);
static void chat_reset(SMTPD_STATE *, int);
#define NEUTER_CHARACTERS " <>()\\\";@"
#define REASON_TIMEOUT "timeout"
#define REASON_LOST_CONNECTION "lost connection"
#define REASON_ERROR_LIMIT "too many errors"
MILTERS *smtpd_milters;
#ifdef USE_TLS
static TLS_APPL_STATE *smtpd_tls_ctx;
static int ask_client_cert;
#endif
static DICT *smtpd_cmd_filter;
#ifdef USE_SASL_AUTH
static NAMADR_LIST *sasl_exceptions_networks;
static int sasl_client_exception(SMTPD_STATE *state)
{
int match;
if (sasl_exceptions_networks == 0)
return (0);
if ((match = namadr_list_match(sasl_exceptions_networks,
state->name, state->addr)) == 0)
match = sasl_exceptions_networks->error;
if (msg_verbose)
msg_info("sasl_exceptions: %s, match=%d",
state->namaddr, match);
return (match);
}
#endif
static const char *smtpd_whatsup(SMTPD_STATE *state)
{
static VSTRING *buf = 0;
if (buf == 0)
buf = vstring_alloc(100);
else
VSTRING_RESET(buf);
if (state->sender)
vstring_sprintf_append(buf, " from=<%s>", state->sender);
if (state->recipient)
vstring_sprintf_append(buf, " to=<%s>", state->recipient);
if (state->protocol)
vstring_sprintf_append(buf, " proto=%s", state->protocol);
if (state->helo_name)
vstring_sprintf_append(buf, " helo=<%s>", state->helo_name);
return (STR(buf));
}
static void collapse_args(int argc, SMTPD_TOKEN *argv)
{
int i;
for (i = 1; i < argc; i++) {
vstring_strcat(argv[0].vstrval, " ");
vstring_strcat(argv[0].vstrval, argv[i].strval);
}
argv[0].strval = STR(argv[0].vstrval);
}
static const char *check_milter_reply(SMTPD_STATE *state, const char *reply)
{
const char *queue_id = state->queue_id ? state->queue_id : "NOQUEUE";
const char *action;
const char *text;
#define MILTER_SKIP_FLAGS (CLEANUP_FLAG_DISCARD)
switch (reply[0]) {
case 'H':
state->saved_flags |= CLEANUP_FLAG_HOLD;
action = "milter-hold";
reply = 0;
text = "milter triggers HOLD action";
break;
case 'D':
state->saved_flags |= CLEANUP_FLAG_DISCARD;
action = "milter-discard";
reply = 0;
text = "milter triggers DISCARD action";
break;
case 'S':
state->error_mask |= MAIL_ERROR_POLICY;
action = "milter-reject";
reply = "421 4.7.0 Server closing connection";
text = 0;
break;
case '4':
case '5':
state->error_mask |= MAIL_ERROR_POLICY;
action = "milter-reject";
text = 0;
break;
default:
state->error_mask |= MAIL_ERROR_SOFTWARE;
action = "reject";
reply = "421 4.3.5 Server configuration error";
text = 0;
break;
}
msg_info("%s: %s: %s from %s: %s;%s", queue_id, action, state->where,
state->namaddr, reply ? reply : text, smtpd_whatsup(state));
return (reply);
}
static int helo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
const char *err;
if (argc < 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Syntax: HELO hostname");
return (-1);
}
if (argc > 2)
collapse_args(argc - 1, argv + 1);
if (SMTPD_STAND_ALONE(state) == 0
&& var_smtpd_delay_reject == 0
&& (err = smtpd_check_helo(state, argv[1].strval)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
#define PUSH_STRING(old, curr, new) { char *old = (curr); (curr) = (new);
#define POP_STRING(old, curr) (curr) = old; }
if (smtpd_milters != 0
&& SMTPD_STAND_ALONE(state) == 0
&& (state->saved_flags & MILTER_SKIP_FLAGS) == 0
&& (err = milter_helo_event(smtpd_milters, argv[1].strval, 0)) != 0) {
PUSH_STRING(saved_helo, state->helo_name, argv[1].strval);
err = check_milter_reply(state, err);
POP_STRING(saved_helo, state->helo_name);
if (err != 0 && strncmp(err, "421", 3) == 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
}
if (state->helo_name != 0)
helo_reset(state);
chat_reset(state, var_smtpd_hist_thrsh);
mail_reset(state);
rcpt_reset(state);
state->helo_name = mystrdup(printable(argv[1].strval, '?'));
neuter(state->helo_name, NEUTER_CHARACTERS, '?');
if (strcasecmp(state->protocol, MAIL_PROTO_ESMTP) != 0
&& strcasecmp(state->protocol, MAIL_PROTO_SMTP) != 0) {
myfree(state->protocol);
state->protocol = mystrdup(MAIL_PROTO_SMTP);
}
smtpd_chat_reply(state, "250 %s", var_myhostname);
return (0);
}
static NORETURN cant_announce_feature(SMTPD_STATE *state, const char *feature)
{
msg_warn("don't know if EHLO feature %s should be announced to %s",
feature, state->namaddr);
vstream_longjmp(state->client, SMTP_ERR_DATA);
}
static NORETURN cant_permit_command(SMTPD_STATE *state, const char *command)
{
msg_warn("don't know if command %s should be allowed from %s",
command, state->namaddr);
vstream_longjmp(state->client, SMTP_ERR_DATA);
}
static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
const char *err;
int discard_mask;
char **cpp;
if (argc < 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Syntax: EHLO hostname");
return (-1);
}
if (argc > 2)
collapse_args(argc - 1, argv + 1);
if (SMTPD_STAND_ALONE(state) == 0
&& var_smtpd_delay_reject == 0
&& (err = smtpd_check_helo(state, argv[1].strval)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
err = 0;
if (smtpd_milters != 0
&& SMTPD_STAND_ALONE(state) == 0
&& (state->saved_flags & MILTER_SKIP_FLAGS) == 0
&& (err = milter_helo_event(smtpd_milters, argv[1].strval, 1)) != 0) {
PUSH_STRING(saved_helo, state->helo_name, argv[1].strval);
err = check_milter_reply(state, err);
POP_STRING(saved_helo, state->helo_name);
if (err != 0 && strncmp(err, "421", 3) == 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
}
if (state->helo_name != 0)
helo_reset(state);
chat_reset(state, var_smtpd_hist_thrsh);
mail_reset(state);
rcpt_reset(state);
state->helo_name = mystrdup(printable(argv[1].strval, '?'));
neuter(state->helo_name, NEUTER_CHARACTERS, '?');
if (strcasecmp(state->protocol, MAIL_PROTO_ESMTP) != 0) {
myfree(state->protocol);
state->protocol = mystrdup(MAIL_PROTO_ESMTP);
}
#define EHLO_APPEND(state, cmd) \
do { \
vstring_sprintf((state)->ehlo_buf, (cmd)); \
argv_add((state)->ehlo_argv, STR((state)->ehlo_buf), (char *) 0); \
} while (0)
#define EHLO_APPEND1(state, cmd, arg) \
do { \
vstring_sprintf((state)->ehlo_buf, (cmd), (arg)); \
argv_add((state)->ehlo_argv, STR((state)->ehlo_buf), (char *) 0); \
} while (0)
discard_mask = state->ehlo_discard_mask;
if (err != 0 && err[0] == '5')
discard_mask |= ~EHLO_MASK_ENHANCEDSTATUSCODES;
if ((discard_mask & EHLO_MASK_ENHANCEDSTATUSCODES) == 0)
if (discard_mask && !(discard_mask & EHLO_MASK_SILENT))
msg_info("discarding EHLO keywords: %s", str_ehlo_mask(discard_mask));
if (ehlo_discard_maps && ehlo_discard_maps->error) {
msg_warn("don't know what EHLO features to announce to %s",
state->namaddr);
vstream_longjmp(state->client, SMTP_ERR_DATA);
}
if (state->ehlo_argv == 0) {
state->ehlo_argv = argv_alloc(10);
state->ehlo_buf = vstring_alloc(10);
} else
argv_truncate(state->ehlo_argv, 0);
EHLO_APPEND1(state, "%s", var_myhostname);
if ((discard_mask & EHLO_MASK_PIPELINING) == 0)
EHLO_APPEND(state, "PIPELINING");
if ((discard_mask & EHLO_MASK_SIZE) == 0) {
if (var_message_limit)
EHLO_APPEND1(state, "SIZE %lu",
(unsigned long) var_message_limit);
else
EHLO_APPEND(state, "SIZE");
}
if ((discard_mask & EHLO_MASK_VRFY) == 0)
if (var_disable_vrfy_cmd == 0)
EHLO_APPEND(state, SMTPD_CMD_VRFY);
if ((discard_mask & EHLO_MASK_ETRN) == 0)
EHLO_APPEND(state, SMTPD_CMD_ETRN);
#ifdef USE_TLS
if ((discard_mask & EHLO_MASK_STARTTLS) == 0)
if (var_smtpd_use_tls && (!state->tls_context))
EHLO_APPEND(state, SMTPD_CMD_STARTTLS);
#endif
#ifdef __APPLE_OS_X_SERVER__
#define XCLIENT_LOGIN_KLUDGE " " XCLIENT_LOGIN
if ((discard_mask & EHLO_MASK_AUTH) == 0) {
if (smtpd_sasl_is_active(state) && !sasl_client_exception(state)) {
if ( var_smtpd_use_pw_server ) {
if ( smtpd_pw_server_sasl_opts ) {
char mechanism_list[256];
mechanism_list[0] = '\0';
if ( smtpd_pw_server_sasl_opts & PW_SERVER_LOGIN )
strlcpy( mechanism_list, " LOGIN", sizeof mechanism_list );
if ( smtpd_pw_server_sasl_opts & PW_SERVER_PLAIN )
strlcat( mechanism_list, " PLAIN", sizeof mechanism_list );
if ( smtpd_pw_server_sasl_opts & PW_SERVER_CRAM_MD5 )
strlcat( mechanism_list, " CRAM-MD5", sizeof mechanism_list );
if ( smtpd_pw_server_sasl_opts & PW_SERVER_DIGEST_MD5 )
strlcat( mechanism_list, " DIGEST-MD5", sizeof mechanism_list );
if ( smtpd_pw_server_sasl_opts & PW_SERVER_GSSAPI )
strlcat( mechanism_list, " GSSAPI", sizeof mechanism_list );
EHLO_APPEND1( state, "AUTH%s", mechanism_list );
if ( var_broken_auth_clients )
EHLO_APPEND1(state, "AUTH=%s", (char *)&mechanism_list[1] );
}
} else {
EHLO_APPEND1(state, "AUTH %s", state->sasl_mechanism_list);
if (var_broken_auth_clients)
EHLO_APPEND1(state, "AUTH=%s", state->sasl_mechanism_list);
}
}
}
#else
#ifdef USE_SASL_AUTH
#ifndef AUTH_CMD
#define AUTH_CMD "AUTH"
#endif
if ((discard_mask & EHLO_MASK_AUTH) == 0) {
if (smtpd_sasl_is_active(state) && !sasl_client_exception(state)) {
EHLO_APPEND1(state, "AUTH %s", state->sasl_mechanism_list);
if (var_broken_auth_clients)
EHLO_APPEND1(state, "AUTH=%s", state->sasl_mechanism_list);
} else if (sasl_exceptions_networks && sasl_exceptions_networks->error)
cant_announce_feature(state, AUTH_CMD);
}
#define XCLIENT_LOGIN_KLUDGE " " XCLIENT_LOGIN
#else
#define XCLIENT_LOGIN_KLUDGE ""
#endif
#endif
if ((discard_mask & EHLO_MASK_VERP) == 0) {
if (namadr_list_match(verp_clients, state->name, state->addr))
EHLO_APPEND(state, VERP_CMD);
else if (verp_clients && verp_clients->error)
cant_announce_feature(state, VERP_CMD);
}
if ((discard_mask & EHLO_MASK_XCLIENT) == 0) {
if (xclient_allowed)
EHLO_APPEND(state, XCLIENT_CMD
" " XCLIENT_NAME " " XCLIENT_ADDR
" " XCLIENT_PROTO " " XCLIENT_HELO
" " XCLIENT_REVERSE_NAME " " XCLIENT_PORT
XCLIENT_LOGIN_KLUDGE);
else if (xclient_hosts && xclient_hosts->error)
cant_announce_feature(state, XCLIENT_CMD);
}
if ((discard_mask & EHLO_MASK_XFORWARD) == 0) {
if (xforward_allowed)
EHLO_APPEND(state, XFORWARD_CMD
" " XFORWARD_NAME " " XFORWARD_ADDR
" " XFORWARD_PROTO " " XFORWARD_HELO
" " XFORWARD_DOMAIN " " XFORWARD_PORT
" " XFORWARD_IDENT);
else if (xforward_hosts && xforward_hosts->error)
cant_announce_feature(state, XFORWARD_CMD);
}
if ((discard_mask & EHLO_MASK_ENHANCEDSTATUSCODES) == 0)
EHLO_APPEND(state, "ENHANCEDSTATUSCODES");
if ((discard_mask & EHLO_MASK_8BITMIME) == 0)
EHLO_APPEND(state, "8BITMIME");
if ((discard_mask & EHLO_MASK_DSN) == 0)
EHLO_APPEND(state, "DSN");
#ifdef __APPLE_OS_X_SERVER__
if (!USE_SMTPD_PROXY(state)) {
if ((discard_mask & EHLO_MASK_BINARYMIME) == 0)
EHLO_APPEND(state, "BINARYMIME");
if ((discard_mask & EHLO_MASK_CHUNKING) == 0)
EHLO_APPEND(state, "CHUNKING");
#if defined(USE_SASL_AUTH) && defined(USE_TLS)
if ((discard_mask & EHLO_MASK_BURL) == 0 && imap_allowed(state))
EHLO_APPEND(state, state->sasl_username != NULL &&
*state->sasl_username != '\0' ? "BURL imap" : "BURL");
#endif
}
#endif
for (cpp = state->ehlo_argv->argv; *cpp; cpp++)
smtpd_chat_reply(state, "250%c%s", cpp[1] ? '-' : ' ', *cpp);
argv_free(state->ehlo_argv);
state->ehlo_argv = 0;
vstring_free(state->ehlo_buf);
state->ehlo_buf = 0;
return (0);
}
static void helo_reset(SMTPD_STATE *state)
{
if (state->helo_name) {
myfree(state->helo_name);
state->helo_name = 0;
if (SMTPD_STAND_ALONE(state) == 0 && smtpd_milters != 0)
milter_abort(smtpd_milters);
}
if (state->ehlo_argv) {
argv_free(state->ehlo_argv);
state->ehlo_argv = 0;
}
if (state->ehlo_buf) {
vstring_free(state->ehlo_buf);
state->ehlo_buf = 0;
}
}
static int mail_open_stream(SMTPD_STATE *state)
{
if (state->proxy_mail) {
if (smtpd_proxy_create(state, smtpd_proxy_opts, var_smtpd_proxy_filt,
var_smtpd_proxy_tmout, var_smtpd_proxy_ehlo,
state->proxy_mail) != 0) {
smtpd_chat_reply(state, "%s", STR(state->proxy->buffer));
smtpd_proxy_free(state);
return (-1);
}
}
else if (SMTPD_STAND_ALONE(state) == 0) {
int cleanup_flags;
cleanup_flags = input_transp_cleanup(CLEANUP_FLAG_MASK_EXTERNAL,
smtpd_input_transp_mask)
| CLEANUP_FLAG_SMTP_REPLY;
state->dest = mail_stream_service(MAIL_CLASS_PUBLIC,
var_cleanup_service);
if (state->dest == 0
|| attr_print(state->dest->stream, ATTR_FLAG_NONE,
ATTR_TYPE_INT, MAIL_ATTR_FLAGS, cleanup_flags,
ATTR_TYPE_END) != 0)
msg_fatal("unable to connect to the %s %s service",
MAIL_CLASS_PUBLIC, var_cleanup_service);
}
else {
char *postdrop_command;
postdrop_command = concatenate(var_command_dir, "/postdrop",
msg_verbose ? " -v" : (char *) 0, (char *) 0);
state->dest = mail_stream_command(postdrop_command);
if (state->dest == 0)
msg_fatal("unable to execute %s", postdrop_command);
myfree(postdrop_command);
}
if (state->dest) {
state->cleanup = state->dest->stream;
state->queue_id = mystrdup(state->dest->id);
if (SMTPD_STAND_ALONE(state) == 0) {
if (smtpd_milters != 0
&& (state->saved_flags & MILTER_SKIP_FLAGS) == 0)
(void) milter_dummy(smtpd_milters, state->cleanup);
rec_fprintf(state->cleanup, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT,
REC_TYPE_TIME_ARG(state->arrival_time));
if (*var_filter_xport)
rec_fprintf(state->cleanup, REC_TYPE_FILT, "%s", var_filter_xport);
if (FORWARD_IDENT(state))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_LOG_IDENT, FORWARD_IDENT(state));
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_RWR_CONTEXT, FORWARD_DOMAIN(state));
#ifdef USE_SASL_AUTH
if (state->sasl_method)
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_SASL_METHOD, state->sasl_method);
if (state->sasl_username)
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_SASL_USERNAME, state->sasl_username);
if (state->sasl_sender)
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_SASL_SENDER, state->sasl_sender);
#endif
if (state->dsn_envid)
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_DSN_ENVID, state->dsn_envid);
if (state->dsn_ret)
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%d",
MAIL_ATTR_DSN_RET, state->dsn_ret);
}
rec_fputs(state->cleanup, REC_TYPE_FROM, state->sender);
if (state->encoding != 0)
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_ENCODING, state->encoding);
if (SMTPD_STAND_ALONE(state) == 0) {
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_LOG_CLIENT_NAME, FORWARD_NAME(state));
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_LOG_CLIENT_ADDR, FORWARD_ADDR(state));
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_LOG_CLIENT_PORT, FORWARD_PORT(state));
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_LOG_ORIGIN, FORWARD_NAMADDR(state));
if (FORWARD_HELO(state))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_LOG_HELO_NAME, FORWARD_HELO(state));
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_LOG_PROTO_NAME, FORWARD_PROTO(state));
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_ACT_CLIENT_NAME, state->name);
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_ACT_REVERSE_CLIENT_NAME, state->reverse_name);
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_ACT_CLIENT_ADDR, state->addr);
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_ACT_CLIENT_PORT, state->port);
if (state->helo_name)
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_ACT_HELO_NAME, state->helo_name);
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_ACT_PROTO_NAME, state->protocol);
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%u",
MAIL_ATTR_ACT_CLIENT_AF, state->addr_family);
}
if (state->verp_delims)
rec_fputs(state->cleanup, REC_TYPE_VERP, state->verp_delims);
}
#ifdef USE_SASL_AUTH
if (state->sasl_username)
smtpd_sasl_mail_log(state);
else
#endif
#define PRINT_OR_NULL(cond, str) \
((cond) ? (str) : "")
#define PRINT2_OR_NULL(cond, name, value) \
PRINT_OR_NULL((cond), (name)), PRINT_OR_NULL((cond), (value))
msg_info("%s: client=%s%s%s%s%s",
(state->queue_id ? state->queue_id : "NOQUEUE"),
state->namaddr,
PRINT2_OR_NULL(HAVE_FORWARDED_IDENT(state),
", orig_queue_id=", FORWARD_IDENT(state)),
PRINT2_OR_NULL(HAVE_FORWARDED_CLIENT_ATTR(state),
", orig_client=", FORWARD_NAMADDR(state)));
#ifdef __APPLE_OS_X_SERVER__
if (POSTFIX_SMTP_RECEIVE_ENABLED())
POSTFIX_SMTP_RECEIVE(state);
#endif
return (0);
}
static int extract_addr(SMTPD_STATE *state, SMTPD_TOKEN *arg,
int allow_empty_addr, int strict_rfc821)
{
const char *myname = "extract_addr";
TOK822 *tree;
TOK822 *tp;
TOK822 *addr = 0;
int naddr;
int non_addr;
int err = 0;
char *junk = 0;
char *text;
char *colon;
#define PERMIT_EMPTY_ADDR 1
#define REJECT_EMPTY_ADDR 0
if (msg_verbose)
msg_info("%s: input: %s", myname, STR(arg->vstrval));
if (STR(arg->vstrval)[0] == '<'
&& STR(arg->vstrval)[LEN(arg->vstrval) - 1] == '>') {
junk = text = mystrndup(STR(arg->vstrval) + 1, LEN(arg->vstrval) - 2);
} else
text = STR(arg->vstrval);
if (*text == '@' && (colon = strchr(text, ':')) != 0)
text = colon + 1;
tree = tok822_parse(text);
if (junk)
myfree(junk);
for (naddr = non_addr = 0, tp = tree; tp != 0; tp = tp->next) {
if (tp->type == TOK822_ADDR) {
addr = tp;
naddr += 1;
} else if (tp->type == '<' || tp->type == '>') {
;
} else {
non_addr += 1;
}
}
if (naddr > 1
|| (strict_rfc821 && (non_addr || *STR(arg->vstrval) != '<'))) {
msg_warn("Illegal address syntax from %s in %s command: %s",
state->namaddr, state->where,
printable(STR(arg->vstrval), '?'));
err = 1;
}
if (addr)
tok822_internalize(state->addr_buf, addr->head, TOK822_STR_DEFL);
else
vstring_strcpy(state->addr_buf, "");
if (err == 0)
if ((STR(state->addr_buf)[0] == 0 && !allow_empty_addr)
|| (strict_rfc821 && STR(state->addr_buf)[0] == '@')
|| (SMTPD_STAND_ALONE(state) == 0
&& smtpd_check_addr(STR(state->addr_buf)) != 0)) {
msg_warn("Illegal address syntax from %s in %s command: %s",
state->namaddr, state->where,
printable(STR(arg->vstrval), '?'));
err = 1;
}
tok822_free_tree(tree);
if (msg_verbose)
msg_info("%s: in: %s, result: %s",
myname, STR(arg->vstrval), STR(state->addr_buf));
return (err);
}
static const char **milter_argv(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
int n;
ssize_t len = argc + 1;
if (state->milter_argc < len) {
if (state->milter_argc > 0)
state->milter_argv = (const char **)
myrealloc((char *) state->milter_argv,
sizeof(const char *) * len);
else
state->milter_argv = (const char **)
mymalloc(sizeof(const char *) * len);
state->milter_argc = len;
}
for (n = 0; n < argc; n++)
state->milter_argv[n] = argv[n].strval;
state->milter_argv[n] = 0;
return (state->milter_argv);
}
static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
const char *err;
int narg;
char *arg;
char *verp_delims = 0;
int rate;
int dsn_envid = 0;
state->encoding = 0;
state->dsn_ret = 0;
if (var_helo_required && state->helo_name == 0) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "503 5.5.1 Error: send HELO/EHLO first");
return (-1);
}
#define IN_MAIL_TRANSACTION(state) ((state)->sender != 0)
if (IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 5.5.1 Error: nested MAIL command");
return (-1);
}
if (argc < 3
|| strcasecmp(argv[1].strval, "from:") != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Syntax: MAIL FROM:<address>");
return (-1);
}
if (SMTPD_STAND_ALONE(state) == 0
&& !xclient_allowed
&& anvil_clnt
&& var_smtpd_cmail_limit > 0
&& !namadr_list_match(hogger_list, state->name, state->addr)
&& anvil_clnt_mail(anvil_clnt, state->service, state->addr,
&rate) == ANVIL_STAT_OK
&& rate > var_smtpd_cmail_limit) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "450 4.7.1 Error: too much mail from %s",
state->addr);
msg_warn("Message delivery request rate limit exceeded: %d from %s for service %s",
rate, state->namaddr, state->service);
return (-1);
}
if (argv[2].tokval == SMTPD_TOK_ERROR) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.1.7 Bad sender address syntax");
return (-1);
}
if (extract_addr(state, argv + 2, PERMIT_EMPTY_ADDR, var_strict_rfc821_env) != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.1.7 Bad sender address syntax");
return (-1);
}
for (narg = 3; narg < argc; narg++) {
arg = argv[narg].strval;
#ifdef __APPLE_OS_X_SERVER__
if (strcasecmp(arg, "BODY=BINARYMIME") == 0 &&
!USE_SMTPD_PROXY(state)) {
state->encoding = MAIL_ATTR_ENC_7BIT;
state->chunking |= SMTPD_CHUNKING_BINARYMIME;
} else
#endif
if (strcasecmp(arg, "BODY=8BITMIME") == 0) {
state->encoding = MAIL_ATTR_ENC_8BIT;
} else if (strcasecmp(arg, "BODY=7BIT") == 0) {
state->encoding = MAIL_ATTR_ENC_7BIT;
} else if (strncasecmp(arg, "SIZE=", 5) == 0) {
if (!alldig(arg + 5)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Bad message size syntax");
return (-1);
}
if ((state->msg_size = off_cvt_string(arg + 5)) < 0) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "552 5.3.4 Message size exceeds file system imposed limit");
return (-1);
}
#ifdef USE_SASL_AUTH
} else if (strncasecmp(arg, "AUTH=", 5) == 0) {
if ((err = smtpd_sasl_mail_opt(state, arg + 5)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
#endif
} else if (namadr_list_match(verp_clients, state->name, state->addr)
&& strncasecmp(arg, VERP_CMD, VERP_CMD_LEN) == 0
&& (arg[VERP_CMD_LEN] == '=' || arg[VERP_CMD_LEN] == 0)) {
if (arg[VERP_CMD_LEN] == 0) {
verp_delims = var_verp_delims;
} else {
verp_delims = arg + VERP_CMD_LEN + 1;
if (verp_delims_verify(verp_delims) != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state,
"501 5.5.4 Error: %s needs two characters from %s",
VERP_CMD, var_verp_filter);
return (-1);
}
}
} else if (strncasecmp(arg, "RET=", 4) == 0) {
if (state->ehlo_discard_mask & EHLO_MASK_DSN) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.7.1 DSN support is disabled");
return (-1);
}
if (state->dsn_ret
|| (state->dsn_ret = dsn_ret_code(arg + 4)) == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state,
"501 5.5.4 Bad RET parameter syntax");
return (-1);
}
} else if (strncasecmp(arg, "ENVID=", 6) == 0) {
if (state->ehlo_discard_mask & EHLO_MASK_DSN) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.7.1 DSN support is disabled");
return (-1);
}
if (dsn_envid
|| xtext_unquote(state->dsn_buf, arg + 6) == 0
|| !allprint(STR(state->dsn_buf))) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Bad ENVID parameter syntax");
return (-1);
}
dsn_envid = 1;
} else {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "555 5.5.4 Unsupported option: %s", arg);
return (-1);
}
}
if ((err = smtpd_check_size(state, state->msg_size)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
if (verp_delims && STR(state->addr_buf)[0] == 0) {
smtpd_chat_reply(state, "503 5.5.4 Error: %s requires non-null sender",
VERP_CMD);
return (-1);
}
if (SMTPD_STAND_ALONE(state) == 0
&& var_smtpd_delay_reject == 0
&& (err = smtpd_check_mail(state, STR(state->addr_buf))) != 0) {
mail_reset(state);
smtpd_chat_reply(state, "%s", err);
return (-1);
}
if (smtpd_milters != 0
&& SMTPD_STAND_ALONE(state) == 0
&& (state->saved_flags & MILTER_SKIP_FLAGS) == 0) {
PUSH_STRING(saved_sender, state->sender, STR(state->addr_buf));
err = milter_mail_event(smtpd_milters,
milter_argv(state, argc - 2, argv + 2));
if (err != 0) {
err = check_milter_reply(state, err);
}
POP_STRING(saved_sender, state->sender);
if (err != 0) {
mail_reset(state);
smtpd_chat_reply(state, "%s", err);
return (-1);
}
}
if (SMTPD_STAND_ALONE(state) == 0) {
err = smtpd_check_rewrite(state);
if (err != 0) {
mail_reset(state);
smtpd_chat_reply(state, "%s", err);
return (-1);
}
}
if (!USE_SMTPD_PROXY(state)
|| (smtpd_proxy_opts & SMTPD_PROXY_FLAG_SPEED_ADJUST)) {
if (SMTPD_STAND_ALONE(state) == 0
&& (err = smtpd_check_queue(state)) != 0) {
mail_reset(state);
smtpd_chat_reply(state, "%s", err);
return (-1);
}
}
GETTIMEOFDAY(&state->arrival_time);
state->sender = mystrdup(STR(state->addr_buf));
vstring_sprintf(state->instance, "%x.%lx.%lx.%x",
var_pid, (unsigned long) state->arrival_time.tv_sec,
(unsigned long) state->arrival_time.tv_usec, state->seqno++);
if (verp_delims)
state->verp_delims = mystrdup(verp_delims);
if (dsn_envid)
state->dsn_envid = mystrdup(STR(state->dsn_buf));
if (USE_SMTPD_PROXY(state))
state->proxy_mail = mystrdup(STR(state->buffer));
if (var_smtpd_delay_open == 0 && mail_open_stream(state) < 0) {
mail_reset(state);
return (-1);
}
smtpd_chat_reply(state, "250 2.1.0 Ok");
return (0);
}
static void mail_reset(SMTPD_STATE *state)
{
state->chunking = 0;
if (state->chunking_context) {
binary_filter_destroy(state->chunking_context);
state->chunking_context = 0;
}
state->msg_size = 0;
state->act_size = 0;
state->flags &= SMTPD_MASK_MAIL_KEEP;
if (state->cleanup != 0) {
mail_stream_cleanup(state->dest);
state->dest = 0;
state->cleanup = 0;
}
state->err = 0;
if (state->queue_id != 0) {
myfree(state->queue_id);
state->queue_id = 0;
}
if (state->sender) {
if (SMTPD_STAND_ALONE(state) == 0 && smtpd_milters != 0)
milter_abort(smtpd_milters);
myfree(state->sender);
state->sender = 0;
}
if (state->verp_delims) {
myfree(state->verp_delims);
state->verp_delims = 0;
}
if (state->proxy_mail) {
myfree(state->proxy_mail);
state->proxy_mail = 0;
}
if (state->saved_filter) {
myfree(state->saved_filter);
state->saved_filter = 0;
}
if (state->saved_redirect) {
myfree(state->saved_redirect);
state->saved_redirect = 0;
}
if (state->saved_bcc) {
myfree(state->saved_bcc);
state->saved_bcc = 0;
}
state->saved_flags = 0;
#ifdef DELAY_ACTION
state->saved_delay = 0;
#endif
#ifdef USE_SASL_AUTH
if (state->sasl_sender)
smtpd_sasl_mail_reset(state);
#endif
state->discard = 0;
VSTRING_RESET(state->instance);
VSTRING_TERMINATE(state->instance);
if (state->proxy)
smtpd_proxy_free(state);
if (state->xforward.flags)
smtpd_xforward_reset(state);
if (state->prepend)
state->prepend = argv_free(state->prepend);
if (state->dsn_envid) {
myfree(state->dsn_envid);
state->dsn_envid = 0;
}
if (state->milter_argv) {
myfree((char *) state->milter_argv);
state->milter_argv = 0;
state->milter_argc = 0;
}
}
static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
SMTPD_PROXY *proxy;
const char *err;
int narg;
char *arg;
int rate;
const char *dsn_orcpt_addr = 0;
ssize_t dsn_orcpt_addr_len = 0;
const char *dsn_orcpt_type = 0;
int dsn_notify = 0;
const char *coded_addr;
const char *milter_err;
if (!IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 5.5.1 Error: need MAIL command");
return (-1);
}
if (argc < 3
|| strcasecmp(argv[1].strval, "to:") != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Syntax: RCPT TO:<address>");
return (-1);
}
if (SMTPD_STAND_ALONE(state) == 0
&& !xclient_allowed
&& anvil_clnt
&& var_smtpd_crcpt_limit > 0
&& !namadr_list_match(hogger_list, state->name, state->addr)
&& anvil_clnt_rcpt(anvil_clnt, state->service, state->addr,
&rate) == ANVIL_STAT_OK
&& rate > var_smtpd_crcpt_limit) {
state->error_mask |= MAIL_ERROR_POLICY;
msg_warn("Recipient address rate limit exceeded: %d from %s for service %s",
rate, state->namaddr, state->service);
smtpd_chat_reply(state, "450 4.7.1 Error: too many recipients from %s",
state->addr);
return (-1);
}
if (argv[2].tokval == SMTPD_TOK_ERROR) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.1.3 Bad recipient address syntax");
return (-1);
}
if (extract_addr(state, argv + 2, REJECT_EMPTY_ADDR, var_strict_rfc821_env) != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.1.3 Bad recipient address syntax");
return (-1);
}
for (narg = 3; narg < argc; narg++) {
arg = argv[narg].strval;
if (strncasecmp(arg, "NOTIFY=", 7) == 0) {
if (state->ehlo_discard_mask & EHLO_MASK_DSN) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.7.1 DSN support is disabled");
return (-1);
}
if (dsn_notify || (dsn_notify = dsn_notify_mask(arg + 7)) == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state,
"501 5.5.4 Error: Bad NOTIFY parameter syntax");
return (-1);
}
} else if (strncasecmp(arg, "ORCPT=", 6) == 0) {
if (state->ehlo_discard_mask & EHLO_MASK_DSN) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.7.1 DSN support is disabled");
return (-1);
}
vstring_strcpy(state->dsn_orcpt_buf, arg + 6);
if (dsn_orcpt_addr
|| (coded_addr = split_at(STR(state->dsn_orcpt_buf), ';')) == 0
|| xtext_unquote(state->dsn_buf, coded_addr) == 0
|| *(dsn_orcpt_type = STR(state->dsn_orcpt_buf)) == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state,
"501 5.5.4 Error: Bad ORCPT parameter syntax");
return (-1);
}
dsn_orcpt_addr = STR(state->dsn_buf);
dsn_orcpt_addr_len = LEN(state->dsn_buf);
} else {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "555 5.5.4 Unsupported option: %s", arg);
return (-1);
}
}
if (var_smtpd_rcpt_limit && state->rcpt_count >= var_smtpd_rcpt_limit) {
smtpd_chat_reply(state, "452 4.5.3 Error: too many recipients");
if (state->rcpt_overshoot++ < var_smtpd_rcpt_overlim)
return (0);
state->error_mask |= MAIL_ERROR_POLICY;
return (-1);
}
if (SMTPD_STAND_ALONE(state) == 0) {
const char *verify_sender;
verify_sender = valid_verify_sender_addr(STR(state->addr_buf));
if (verify_sender != 0) {
vstring_strcpy(state->addr_buf, verify_sender);
err = 0;
} else {
err = smtpd_check_rcpt(state, STR(state->addr_buf));
}
if (smtpd_milters != 0
&& (state->saved_flags & MILTER_SKIP_FLAGS) == 0) {
PUSH_STRING(saved_rcpt, state->recipient, STR(state->addr_buf));
state->milter_reject_text = err;
milter_err = milter_rcpt_event(smtpd_milters,
err == 0 ? MILTER_FLAG_NONE :
MILTER_FLAG_WANT_RCPT_REJ,
milter_argv(state, argc - 2, argv + 2));
if (err == 0 && milter_err != 0) {
err = check_milter_reply(state, milter_err);
}
POP_STRING(saved_rcpt, state->recipient);
}
if (err != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
}
if (state->proxy == 0 && state->cleanup == 0 && mail_open_stream(state) < 0)
return (-1);
proxy = state->proxy;
if (proxy != 0 && proxy->cmd(state, SMTPD_PROX_WANT_OK,
"%s", STR(state->buffer)) != 0) {
smtpd_chat_reply(state, "%s", STR(proxy->buffer));
return (-1);
}
state->rcpt_count++;
if (state->recipient == 0)
state->recipient = mystrdup(STR(state->addr_buf));
if (state->cleanup) {
if (dsn_orcpt_addr == 0) {
dsn_orcpt_type = "rfc822";
dsn_orcpt_addr = argv[2].strval;
dsn_orcpt_addr_len = strlen(argv[2].strval);
if (dsn_orcpt_addr[0] == '<'
&& dsn_orcpt_addr[dsn_orcpt_addr_len - 1] == '>') {
dsn_orcpt_addr += 1;
dsn_orcpt_addr_len -= 2;
}
}
if (dsn_notify)
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%d",
MAIL_ATTR_DSN_NOTIFY, dsn_notify);
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s;%.*s",
MAIL_ATTR_DSN_ORCPT, dsn_orcpt_type,
(int) dsn_orcpt_addr_len, dsn_orcpt_addr);
rec_fputs(state->cleanup, REC_TYPE_RCPT, STR(state->addr_buf));
vstream_fflush(state->cleanup);
}
smtpd_chat_reply(state, "250 2.1.5 Ok");
return (0);
}
static void rcpt_reset(SMTPD_STATE *state)
{
if (state->recipient) {
myfree(state->recipient);
state->recipient = 0;
}
state->rcpt_count = 0;
state->rcpt_overshoot = 0;
}
#if 0
static VSTRING *rfc2047_comment_encode(const char *str, const char *charset)
{
VSTRING *buf = vstring_alloc(30);
const unsigned char *cp;
int ch;
#define ESPECIALS "()<>@,;:\"/[]?.="
#define QSPECIALS "_" ESPECIALS
#define CSPECIALS "\\\"()"
for (cp = (unsigned char *) str; ; ++cp) {
if ((ch = *cp) == 0) {
vstring_sprintf(buf, "\"%s\"", str);
return (buf);
}
if (!ISPRINT(ch) || strchr(CSPECIALS, ch))
break;
}
vstring_sprintf(buf, "=?%s?Q?=%02X", charset, '"');
for (cp = (unsigned char *) str; (ch = *cp) != 0; ++cp) {
if (!ISPRINT(ch) || strchr(QSPECIALS CSPECIALS, ch)) {
vstring_sprintf_append(buf, "=%02X", ch);
} else if (ch == ' ') {
VSTRING_ADDCH(buf, '_');
} else {
VSTRING_ADDCH(buf, ch);
}
}
vstring_sprintf_append(buf, "=%02X?=", '"');
return (buf);
}
#endif
static void comment_sanitize(VSTRING *comment_string)
{
unsigned char *cp;
int ch;
int pc;
for (pc = 0, cp = (unsigned char *) STR(comment_string); (ch = *cp) != 0; cp++) {
if (!ISASCII(ch) || !ISPRINT(ch) || ch == '\\') {
*cp = '?';
} else if (ch == '(') {
pc++;
} else if (ch == ')') {
if (pc > 0)
pc--;
else
*cp = '?';
}
}
while (pc-- > 0)
VSTRING_ADDCH(comment_string, ')');
VSTRING_TERMINATE(comment_string);
}
#ifdef __APPLE_OS_X_SERVER__
static int data_common(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv, bool burl, off_t bdat_size)
#else
static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
#endif
{
SMTPD_PROXY *proxy;
const char *err;
char *start;
int len;
int curr_rec_type;
int prev_rec_type;
int first = 1;
VSTRING *why = 0;
int saved_err;
int (*out_record) (VSTREAM *, int, const char *, ssize_t);
int (*out_fprintf) (VSTREAM *, int, const char *,...);
VSTREAM *out_stream;
int out_error;
char **cpp;
const CLEANUP_STAT_DETAIL *detail;
const char *rfc3848_sess;
const char *rfc3848_auth;
#ifdef __APPLE_OS_X_SERVER__
VSTREAM *in_stream;
#if defined(USE_SASL_AUTH) && defined(USE_TLS)
int status;
const char *url;
#endif
int (*in_get)(VSTRING *, VSTREAM *, ssize_t, int);
#endif
#ifdef USE_TLS
VSTRING *peer_CN;
VSTRING *issuer_CN;
#endif
#ifdef USE_SASL_AUTH
VSTRING *username;
#endif
if (state->rcpt_count == 0) {
if (!IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 5.5.1 Error: need RCPT command");
} else {
smtpd_chat_reply(state, "554 5.5.1 Error: no valid recipients");
}
return (-1);
}
#ifdef __APPLE_OS_X_SERVER__
if (state->chunking & SMTPD_CHUNKING)
first = (state->chunking & SMTPD_CHUNKING_NONZERO) == 0;
else
#endif
if (argc != 1) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Syntax: DATA");
return (-1);
}
#ifdef __APPLE_OS_X_SERVER__
if ((state->chunking & SMTPD_CHUNKING_CONT) == 0) {
#endif
if (SMTPD_STAND_ALONE(state) == 0 && (err = smtpd_check_data(state)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
if (smtpd_milters != 0
&& SMTPD_STAND_ALONE(state) == 0
&& (state->saved_flags & MILTER_SKIP_FLAGS) == 0
&& (err = milter_data_event(smtpd_milters)) != 0
&& (err = check_milter_reply(state, err)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
proxy = state->proxy;
if (proxy != 0 && proxy->cmd(state, SMTPD_PROX_WANT_MORE,
"%s", STR(state->buffer)) != 0) {
smtpd_chat_reply(state, "%s", STR(proxy->buffer));
return (-1);
}
#ifdef __APPLE_OS_X_SERVER__
} else
proxy = state->proxy;
#endif
if (proxy) {
out_stream = proxy->stream;
out_record = proxy->rec_put;
out_fprintf = proxy->rec_fprintf;
out_error = CLEANUP_STAT_PROXY;
} else {
out_stream = state->cleanup;
out_record = rec_put;
out_fprintf = rec_fprintf;
out_error = CLEANUP_STAT_WRITE;
}
#ifdef __APPLE_OS_X_SERVER__
if ((state->chunking & SMTPD_CHUNKING_CONT) == 0) {
#endif
if (state->cleanup) {
if (SMTPD_STAND_ALONE(state) == 0) {
if (smtpd_milters != 0
&& (state->saved_flags & MILTER_SKIP_FLAGS) == 0)
(void) milter_send(smtpd_milters, state->cleanup);
if (state->saved_flags)
rec_fprintf(state->cleanup, REC_TYPE_FLGS, "%d",
state->saved_flags);
}
rec_fputs(state->cleanup, REC_TYPE_MESG, "");
}
if (state->prepend)
for (cpp = state->prepend->argv; *cpp; cpp++)
out_fprintf(out_stream, REC_TYPE_NORM, "%s", *cpp);
if (!proxy || state->xforward.flags == 0) {
out_fprintf(out_stream, REC_TYPE_NORM,
"Received: from %s (%s [%s])",
state->helo_name ? state->helo_name : state->name,
state->name, state->rfc_addr);
#define VSTRING_STRDUP(s) vstring_strcpy(vstring_alloc(strlen(s) + 1), (s))
#ifdef USE_TLS
if (var_smtpd_tls_received_header && state->tls_context) {
out_fprintf(out_stream, REC_TYPE_NORM,
"\t(using %s with cipher %s (%d/%d bits))",
state->tls_context->protocol,
state->tls_context->cipher_name,
state->tls_context->cipher_usebits,
state->tls_context->cipher_algbits);
if (TLS_CERT_IS_PRESENT(state->tls_context)) {
peer_CN = VSTRING_STRDUP(state->tls_context->peer_CN);
comment_sanitize(peer_CN);
issuer_CN = VSTRING_STRDUP(state->tls_context->issuer_CN ?
state->tls_context->issuer_CN : "");
comment_sanitize(issuer_CN);
out_fprintf(out_stream, REC_TYPE_NORM,
"\t(Client CN \"%s\", Issuer \"%s\" (%s))",
STR(peer_CN), STR(issuer_CN),
TLS_CERT_IS_TRUSTED(state->tls_context) ?
"verified OK" : "not verified");
vstring_free(issuer_CN);
vstring_free(peer_CN);
} else if (var_smtpd_tls_ask_ccert)
out_fprintf(out_stream, REC_TYPE_NORM,
"\t(Client did not present a certificate)");
else
out_fprintf(out_stream, REC_TYPE_NORM,
"\t(No client certificate requested)");
}
if (state->tls_context != 0
&& strcmp(state->protocol, MAIL_PROTO_ESMTP) == 0)
rfc3848_sess = "S";
else
#endif
rfc3848_sess = "";
#ifdef USE_SASL_AUTH
if (var_smtpd_sasl_auth_hdr && state->sasl_username) {
username = VSTRING_STRDUP(state->sasl_username);
comment_sanitize(username);
out_fprintf(out_stream, REC_TYPE_NORM,
"\t(Authenticated sender: %s)", STR(username));
vstring_free(username);
}
if (state->sasl_username
&& strcmp(state->protocol, MAIL_PROTO_ESMTP) == 0)
rfc3848_auth = "A";
else
#endif
rfc3848_auth = "";
if (state->rcpt_count == 1 && state->recipient) {
out_fprintf(out_stream, REC_TYPE_NORM,
state->cleanup ? "\tby %s (%s) with %s%s%s id %s" :
"\tby %s (%s) with %s%s%s",
var_myhostname, var_mail_name,
state->protocol, rfc3848_sess,
rfc3848_auth, state->queue_id);
quote_822_local(state->buffer, state->recipient);
out_fprintf(out_stream, REC_TYPE_NORM,
"\tfor <%s>; %s", STR(state->buffer),
mail_date(state->arrival_time.tv_sec));
} else {
out_fprintf(out_stream, REC_TYPE_NORM,
state->cleanup ? "\tby %s (%s) with %s%s%s id %s;" :
"\tby %s (%s) with %s%s%s;",
var_myhostname, var_mail_name,
state->protocol, rfc3848_sess,
rfc3848_auth, state->queue_id);
out_fprintf(out_stream, REC_TYPE_NORM,
"\t%s", mail_date(state->arrival_time.tv_sec));
}
#ifdef RECEIVED_ENVELOPE_FROM
quote_822_local(state->buffer, state->sender);
out_fprintf(out_stream, REC_TYPE_NORM,
"\t(envelope-from %s)", STR(state->buffer));
#endif
}
#ifdef __APPLE_OS_X_SERVER__
}
in_stream = state->client;
in_get = smtp_get;
#if defined(USE_SASL_AUTH) && defined(USE_TLS)
if (burl) {
url = argv[1].strval;
len = strlen(url);
if (len >= 2 && url[0] == '"' && url[len - 1] == '"')
url = mystrndup(url + 1, len - 2);
in_stream = imap_open(state, url);
in_get = smtp_get_to_eof;
if (url != argv[1].strval) {
myfree((char *) url);
url = NULL;
}
if (in_stream == NULL) {
chat_reset(state, var_smtpd_hist_thrsh);
mail_reset(state);
rcpt_reset(state);
return -1;
}
status = vstream_setjmp(in_stream);
if (status != 0) {
imap_close(in_stream);
in_stream = NULL;
}
switch (status) {
case 0:
case SMTP_ERR_NONE:
break;
case SMTP_ERR_EOF:
smtpd_chat_reply(state, "454 4.6.6 EOF from IMAP server");
vstream_longjmp(state->client, SMTP_ERR_QUIET);
break;
case SMTP_ERR_TIME:
smtpd_chat_reply(state, "454 4.6.6 Timeout from IMAP server");
vstream_longjmp(state->client, SMTP_ERR_QUIET);
break;
case SMTP_ERR_QUIET:
vstream_longjmp(state->client, SMTP_ERR_QUIET);
break;
default:
msg_panic("data_common: unknown error %d", status);
break;
}
} else
#endif
if (state->chunking & SMTPD_CHUNKING) {
vstream_limit_init(in_stream, bdat_size);
in_get = smtp_get_to_eof;
} else
#endif
smtpd_chat_reply(state, "354 End data with <CR><LF>.<CR><LF>");
state->where = SMTPD_AFTER_DATA;
#ifdef __APPLE_OS_X_SERVER__
if (state->chunking & SMTPD_CHUNKING_BINARYMIME) {
in_get = smtp_get_binary_to_eof;
if (state->chunking_context == NULL)
state->chunking_context = binary_filter_create();
binary_filter_start(state->chunking_context, out_stream, out_record);
out_record = binary_filter_rec_put;
}
#endif
#ifdef __APPLE_OS_X_SERVER__
for (prev_rec_type = 0; in_stream != NULL; prev_rec_type = curr_rec_type) {
if (in_get(state->buffer, in_stream, var_line_limit,
SMTP_GET_FLAG_NONE) == '\n')
#else
for (prev_rec_type = 0; ; prev_rec_type = curr_rec_type) {
if (smtp_get(state->buffer, state->client, var_line_limit,
SMTP_GET_FLAG_NONE) == '\n')
#endif
curr_rec_type = REC_TYPE_NORM;
else
curr_rec_type = REC_TYPE_CONT;
start = vstring_str(state->buffer);
len = VSTRING_LEN(state->buffer);
if (first) {
if (strncmp(start + strspn(start, ">"), "From ", 5) == 0) {
out_fprintf(out_stream, curr_rec_type,
"X-Mailbox-Line: %s", start);
continue;
}
first = 0;
if (len > 0 && IS_SPACE_TAB(start[0]))
out_record(out_stream, REC_TYPE_NORM, "", 0);
#ifdef __APPLE_OS_X_SERVER__
if ((state->chunking & SMTPD_CHUNKING) &&
(len > 0 || curr_rec_type == REC_TYPE_NORM)) {
state->chunking |= SMTPD_CHUNKING_NONZERO;
}
#endif
}
#ifdef __APPLE_OS_X_SERVER__
if ((state->chunking & SMTPD_CHUNKING) == 0)
#endif
if (prev_rec_type != REC_TYPE_CONT && *start == '.'
&& (proxy == 0 ? (++start, --len) == 0 : len == 1))
break;
#ifdef __APPLE_OS_X_SERVER__
if ((state->chunking & SMTPD_CHUNKING) == 0 || burl || bdat_size > 0)
#endif
if (state->err == CLEANUP_STAT_OK) {
if (var_message_limit > 0 && var_message_limit - state->act_size < len + 2) {
state->err = CLEANUP_STAT_SIZE;
msg_warn("%s: queue file size limit exceeded",
state->queue_id ? state->queue_id : "NOQUEUE");
} else {
state->act_size += len + 2;
if (out_record(out_stream, curr_rec_type, start, len) < 0)
state->err = out_error;
}
}
#ifdef __APPLE_OS_X_SERVER__
#if defined(USE_SASL_AUTH) && defined(USE_TLS)
if (burl) {
if (imap_isdone(in_stream)) {
imap_close(in_stream);
in_stream = NULL;
if (state->chunking & SMTPD_CHUNKING_BINARYMIME) {
if (state->chunking & SMTPD_CHUNKING_LAST) {
if (binary_filter_flush(state->chunking_context,
out_stream) < 0)
state->err = out_error;
}
binary_filter_stop(state->chunking_context, out_stream);
}
out_stream = NULL;
out_record = NULL;
out_fprintf = NULL;
if ((state->chunking & SMTPD_CHUNKING_LAST) == 0 &&
state->err == CLEANUP_STAT_OK) {
smtpd_chat_reply(state, "250 2.5.0 BURL OK");
return 0;
}
break;
} else if (vstream_feof(in_stream))
vstream_longjmp(in_stream, SMTP_ERR_EOF);
}
#endif
if (!burl && (state->chunking & SMTPD_CHUNKING)) {
if (vstream_limit_reached(in_stream)) {
vstream_limit_deinit(in_stream);
vstream_clearerr(in_stream);
if (state->chunking & SMTPD_CHUNKING_BINARYMIME) {
if (state->chunking & SMTPD_CHUNKING_LAST) {
if (binary_filter_flush(state->chunking_context,
out_stream) < 0)
state->err = out_error;
}
binary_filter_stop(state->chunking_context, out_stream);
}
out_stream = NULL;
out_record = NULL;
out_fprintf = NULL;
if ((state->chunking & SMTPD_CHUNKING_LAST) == 0 &&
state->err == CLEANUP_STAT_OK) {
smtpd_chat_reply(state, "250 2.5.0 BDAT OK");
return 0;
}
break;
} else if (vstream_feof(in_stream)) {
vstream_limit_deinit(in_stream);
vstream_longjmp(in_stream, SMTP_ERR_EOF);
}
}
#endif
}
state->where = SMTPD_AFTER_DOT;
if (state->err == CLEANUP_STAT_OK
&& SMTPD_STAND_ALONE(state) == 0
&& (err = smtpd_check_eod(state)) != 0) {
smtpd_chat_reply(state, "%s", err);
if (proxy) {
smtpd_proxy_close(state);
} else {
mail_stream_cleanup(state->dest);
state->dest = 0;
state->cleanup = 0;
}
return (-1);
}
if (proxy) {
if (state->err == CLEANUP_STAT_OK) {
(void) proxy->cmd(state, SMTPD_PROX_WANT_ANY, ".");
if (state->err == CLEANUP_STAT_OK &&
*STR(proxy->buffer) != '2')
state->err = CLEANUP_STAT_CONT;
}
}
else {
if (state->err == CLEANUP_STAT_OK) {
rec_fputs(state->cleanup, REC_TYPE_XTRA, "");
if (state->saved_filter)
rec_fprintf(state->cleanup, REC_TYPE_FILT, "%s",
state->saved_filter);
if (state->saved_redirect)
rec_fprintf(state->cleanup, REC_TYPE_RDR, "%s",
state->saved_redirect);
if (state->saved_bcc) {
rec_fprintf(state->cleanup, REC_TYPE_RCPT, "%s",
state->saved_bcc);
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%d",
MAIL_ATTR_DSN_NOTIFY, DSN_NOTIFY_NEVER);
}
if (state->saved_flags)
rec_fprintf(state->cleanup, REC_TYPE_FLGS, "%d",
state->saved_flags);
#ifdef DELAY_ACTION
if (state->saved_delay)
rec_fprintf(state->cleanup, REC_TYPE_DELAY, "%d",
state->saved_delay);
#endif
if (vstream_ferror(state->cleanup))
state->err = CLEANUP_STAT_WRITE;
}
if (state->err == CLEANUP_STAT_OK)
if (rec_fputs(state->cleanup, REC_TYPE_END, "") < 0
|| vstream_fflush(state->cleanup))
state->err = CLEANUP_STAT_WRITE;
if (state->err == 0) {
why = vstring_alloc(10);
state->err = mail_stream_finish(state->dest, why);
printable(STR(why), ' ');
} else
mail_stream_cleanup(state->dest);
state->dest = 0;
state->cleanup = 0;
}
if (smtpd_milters != 0 && (state->err & CLEANUP_STAT_WRITE) != 0)
state->access_denied = mystrdup("421 4.3.0 Mail system error");
#define IS_SMTP_REJECT(s) \
(((s)[0] == '4' || (s)[0] == '5') \
&& ISDIGIT((s)[1]) && ISDIGIT((s)[2]) \
&& ((s)[3] == '\0' || (s)[3] == ' ' || (s)[3] == '-'))
if (state->err == CLEANUP_STAT_OK) {
state->error_count = 0;
state->error_mask = 0;
state->junk_cmds = 0;
if (proxy)
smtpd_chat_reply(state, "%s", STR(proxy->buffer));
else
smtpd_chat_reply(state,
"250 2.0.0 Ok: queued as %s", state->queue_id);
} else if (why && IS_SMTP_REJECT(STR(why))) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "%s", STR(why));
} else if ((state->err & CLEANUP_STAT_DEFER) != 0) {
state->error_mask |= MAIL_ERROR_POLICY;
detail = cleanup_stat_detail(CLEANUP_STAT_DEFER);
if (why && LEN(why) > 0) {
smtpd_chat_reply(state, "%d %s", detail->smtp, STR(why));
} else {
smtpd_chat_reply(state, "%d %s Error: %s",
detail->smtp, detail->dsn, detail->text);
}
} else if ((state->err & CLEANUP_STAT_BAD) != 0) {
state->error_mask |= MAIL_ERROR_SOFTWARE;
detail = cleanup_stat_detail(CLEANUP_STAT_BAD);
smtpd_chat_reply(state, "%d %s Error: internal error %d",
detail->smtp, detail->dsn, state->err);
} else if ((state->err & CLEANUP_STAT_SIZE) != 0) {
state->error_mask |= MAIL_ERROR_BOUNCE;
detail = cleanup_stat_detail(CLEANUP_STAT_SIZE);
smtpd_chat_reply(state, "%d %s Error: %s",
detail->smtp, detail->dsn, detail->text);
} else if ((state->err & CLEANUP_STAT_HOPS) != 0) {
state->error_mask |= MAIL_ERROR_BOUNCE;
detail = cleanup_stat_detail(CLEANUP_STAT_HOPS);
smtpd_chat_reply(state, "%d %s Error: %s",
detail->smtp, detail->dsn, detail->text);
} else if ((state->err & CLEANUP_STAT_CONT) != 0) {
state->error_mask |= MAIL_ERROR_POLICY;
detail = cleanup_stat_detail(CLEANUP_STAT_CONT);
if (proxy) {
smtpd_chat_reply(state, "%s", STR(proxy->buffer));
} else if (why && LEN(why) > 0) {
smtpd_chat_reply(state, "%d %s", detail->smtp, STR(why));
} else {
smtpd_chat_reply(state, "%d %s Error: %s",
detail->smtp, detail->dsn, detail->text);
}
} else if ((state->err & CLEANUP_STAT_WRITE) != 0) {
state->error_mask |= MAIL_ERROR_RESOURCE;
detail = cleanup_stat_detail(CLEANUP_STAT_WRITE);
smtpd_chat_reply(state, "%d %s Error: %s",
detail->smtp, detail->dsn, detail->text);
} else if ((state->err & CLEANUP_STAT_PROXY) != 0) {
state->error_mask |= MAIL_ERROR_SOFTWARE;
smtpd_chat_reply(state, "%s", STR(proxy->buffer));
} else {
state->error_mask |= MAIL_ERROR_SOFTWARE;
detail = cleanup_stat_detail(CLEANUP_STAT_BAD);
smtpd_chat_reply(state, "%d %s Error: internal error %d",
detail->smtp, detail->dsn, state->err);
}
if (proxy)
msg_info("proxy-%s: %s: %s;%s",
(state->err == CLEANUP_STAT_OK) ? "accept" : "reject",
state->where, STR(proxy->buffer), smtpd_whatsup(state));
saved_err = state->err;
chat_reset(state, var_smtpd_hist_thrsh);
mail_reset(state);
rcpt_reset(state);
if (why)
vstring_free(why);
return (saved_err);
}
#ifdef __APPLE_OS_X_SERVER__
static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
if (state->chunking != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 5.5.1 Can't mix BURL/BDAT/BODY=BINARYMIME and DATA");
return (-1);
}
return data_common(state, argc, argv, 0, 0);
}
static int chunking_common(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv,
bool burl, bool last, off_t bdat_size)
{
int old_chunking = state->chunking;
int chunking = SMTPD_CHUNKING;
int ret;
if (last)
chunking |= SMTPD_CHUNKING_LAST;
state->chunking |= chunking;
ret = data_common(state, argc, argv, burl, bdat_size);
if (ret == 0 && !last)
state->chunking |= SMTPD_CHUNKING_CONT;
else if (ret != 0)
state->chunking = old_chunking;
return ret;
}
#if defined(USE_SASL_AUTH) && defined(USE_TLS)
static int burl_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
if (USE_SMTPD_PROXY(state) || !imap_allowed(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "502 5.5.2 Error: command not recognized");
return -1;
}
if (state->sasl_username == NULL || *state->sasl_username == '\0') {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 5.5.1 Error: send AUTH command first");
return -1;
}
if (argc < 2 || argc > 3 ||
(argc == 3 && strcasecmp(argv[2].strval, "LAST") != 0)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Syntax: BURL <url> [LAST]");
return -1;
}
return chunking_common(state, argc, argv, 1, argc == 3, 0);
}
#endif
static int bdat_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
off_t size = -1;
int ret;
if (USE_SMTPD_PROXY(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "502 5.5.2 Error: command not recognized");
return -1;
}
errno = 0;
if (argc < 2 || argc > 3 ||
!alldig(argv[1].strval) ||
(size = off_cvt_string(argv[1].strval)) < 0 ||
(argc == 3 && strcasecmp(argv[2].strval, "LAST") != 0)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Syntax: BDAT <octet-count> [LAST]");
return -1;
}
ret = chunking_common(state, argc, argv, 0, argc == 3, size);
if (ret < 0 && strcmp(state->where, SMTPD_AFTER_DOT) != 0) {
smtp_discard(state->client, size);
}
return ret;
}
#endif
static int rset_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
{
if (argc != 1) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Syntax: RSET");
return (-1);
}
chat_reset(state, var_smtpd_hist_thrsh);
mail_reset(state);
rcpt_reset(state);
smtpd_chat_reply(state, "250 2.0.0 Ok");
return (0);
}
static int noop_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
{
#ifdef RFC821_SYNTAX
if (argc != 1) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Syntax: NOOP");
return (-1);
}
#endif
smtpd_chat_reply(state, "250 2.0.0 Ok");
return (0);
}
static int vrfy_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
const char *err = 0;
#define SLOPPY 0
if (var_disable_vrfy_cmd) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "502 5.5.1 VRFY command is disabled");
return (-1);
}
if (smtpd_milters != 0 && (err = milter_other_event(smtpd_milters)) != 0
&& (err[0] == '5' || err[0] == '4')) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "%s", err);
return (-1);
}
if (argc < 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Syntax: VRFY address");
return (-1);
}
if (argc > 2)
collapse_args(argc - 1, argv + 1);
if (extract_addr(state, argv + 1, REJECT_EMPTY_ADDR, SLOPPY) != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.1.3 Bad recipient address syntax");
return (-1);
}
if (SMTPD_STAND_ALONE(state) == 0
&& (err = smtpd_check_rcpt(state, STR(state->addr_buf))) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
smtpd_chat_reply(state, "252 2.0.0 %s", argv[1].strval);
return (0);
}
static int etrn_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
const char *err;
if (var_helo_required && state->helo_name == 0) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "503 Error: send HELO/EHLO first");
return (-1);
}
if (smtpd_milters != 0 && (err = milter_other_event(smtpd_milters)) != 0
&& (err[0] == '5' || err[0] == '4')) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "%s", err);
return (-1);
}
if (IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 Error: MAIL transaction in progress");
return (-1);
}
if (argc != 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "500 Syntax: ETRN domain");
return (-1);
}
if (argv[1].strval[0] == '@' || argv[1].strval[0] == '#')
argv[1].strval++;
if (!valid_hostname(argv[1].strval, DONT_GRIPE)
&& !valid_mailhost_literal(argv[1].strval, DONT_GRIPE)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Error: invalid parameter syntax");
return (-1);
}
if (SMTPD_STAND_ALONE(state)) {
msg_warn("do not use ETRN in \"sendmail -bs\" mode");
smtpd_chat_reply(state, "458 Unable to queue messages");
return (-1);
}
if ((err = smtpd_check_etrn(state, argv[1].strval)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
switch (flush_send_site(argv[1].strval)) {
case FLUSH_STAT_OK:
smtpd_chat_reply(state, "250 Queuing started");
return (0);
case FLUSH_STAT_DENY:
msg_warn("reject: ETRN %.100s... from %s",
argv[1].strval, state->namaddr);
smtpd_chat_reply(state, "459 <%s>: service unavailable",
argv[1].strval);
return (-1);
case FLUSH_STAT_BAD:
msg_warn("bad ETRN %.100s... from %s", argv[1].strval, state->namaddr);
smtpd_chat_reply(state, "458 Unable to queue messages");
return (-1);
default:
msg_warn("unable to talk to fast flush service");
smtpd_chat_reply(state, "458 Unable to queue messages");
return (-1);
}
}
static int quit_cmd(SMTPD_STATE *state, int unused_argc, SMTPD_TOKEN *unused_argv)
{
int out_pending = vstream_bufstat(state->client, VSTREAM_BST_OUT_PEND);
smtpd_chat_reply(state, "221 2.0.0 Bye");
if (out_pending > 0)
smtp_flush(state->client);
return (0);
}
static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
SMTPD_TOKEN *argp;
char *raw_value;
char *attr_value;
const char *bare_value;
char *attr_name;
int update_namaddr = 0;
int name_status;
static const NAME_CODE peer_codes[] = {
XCLIENT_UNAVAILABLE, SMTPD_PEER_CODE_PERM,
XCLIENT_TEMPORARY, SMTPD_PEER_CODE_TEMP,
0, SMTPD_PEER_CODE_OK,
};
static const NAME_CODE proto_names[] = {
MAIL_PROTO_SMTP, 1,
MAIL_PROTO_ESMTP, 2,
0, -1,
};
int got_helo = 0;
int got_proto = 0;
int got_login = 0;
if (IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 5.5.1 Error: MAIL transaction in progress");
return (-1);
}
if (argc < 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Syntax: %s attribute=value...",
XCLIENT_CMD);
return (-1);
}
if (xclient_hosts && xclient_hosts->error)
cant_permit_command(state, XCLIENT_CMD);
if (!xclient_allowed) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "550 5.7.0 Error: insufficient authorization");
return (-1);
}
#define STREQ(x,y) (strcasecmp((x), (y)) == 0)
#define UPDATE_STR(s, v) do { \
const char *_v = (v); \
if (s) myfree(s); \
s = (_v) ? mystrdup(_v) : 0; \
} while(0)
if (state->expand_buf == 0)
state->expand_buf = vstring_alloc(100);
for (argp = argv + 1; argp < argv + argc; argp++) {
attr_name = argp->strval;
if ((raw_value = split_at(attr_name, '=')) == 0 || *raw_value == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Error: attribute=value expected");
return (-1);
}
if (strlen(raw_value) > 255) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Error: attribute value too long");
return (-1);
}
attr_value = xtext_unquote(state->expand_buf, raw_value) ?
STR(state->expand_buf) : raw_value;
printable(attr_value, '?');
if (STREQ(attr_name, XCLIENT_NAME)) {
name_status = name_code(peer_codes, NAME_CODE_FLAG_NONE, attr_value);
if (name_status != SMTPD_PEER_CODE_OK) {
attr_value = CLIENT_NAME_UNKNOWN;
} else {
if (!valid_hostname(attr_value, DONT_GRIPE)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Bad %s syntax: %s",
XCLIENT_NAME, attr_value);
return (-1);
}
}
state->name_status = name_status;
UPDATE_STR(state->name, attr_value);
update_namaddr = 1;
if (name_status == SMTPD_PEER_CODE_OK) {
UPDATE_STR(state->reverse_name, attr_value);
state->reverse_name_status = name_status;
}
}
else if (STREQ(attr_name, XCLIENT_REVERSE_NAME)) {
name_status = name_code(peer_codes, NAME_CODE_FLAG_NONE, attr_value);
if (name_status != SMTPD_PEER_CODE_OK) {
attr_value = CLIENT_NAME_UNKNOWN;
} else {
if (!valid_hostname(attr_value, DONT_GRIPE)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Bad %s syntax: %s",
XCLIENT_REVERSE_NAME, attr_value);
return (-1);
}
}
state->reverse_name_status = name_status;
UPDATE_STR(state->reverse_name, attr_value);
}
else if (STREQ(attr_name, XCLIENT_ADDR)) {
if (STREQ(attr_value, XCLIENT_UNAVAILABLE)) {
attr_value = CLIENT_ADDR_UNKNOWN;
bare_value = attr_value;
} else {
if ((bare_value = valid_mailhost_addr(attr_value, DONT_GRIPE)) == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Bad %s syntax: %s",
XCLIENT_ADDR, attr_value);
return (-1);
}
}
UPDATE_STR(state->addr, bare_value);
UPDATE_STR(state->rfc_addr, attr_value);
#ifdef HAS_IPV6
if (strncasecmp(attr_value, INET_PROTO_NAME_IPV6 ":",
sizeof(INET_PROTO_NAME_IPV6 ":") - 1) == 0)
state->addr_family = AF_INET6;
else
#endif
state->addr_family = AF_INET;
update_namaddr = 1;
}
else if (STREQ(attr_name, XCLIENT_PORT)) {
if (STREQ(attr_value, XCLIENT_UNAVAILABLE)) {
attr_value = CLIENT_PORT_UNKNOWN;
} else {
if (!alldig(attr_value)
|| strlen(attr_value) > sizeof("65535") - 1) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Bad %s syntax: %s",
XCLIENT_PORT, attr_value);
return (-1);
}
}
UPDATE_STR(state->port, attr_value);
update_namaddr = 1;
}
else if (STREQ(attr_name, XCLIENT_HELO)) {
if (STREQ(attr_value, XCLIENT_UNAVAILABLE)) {
attr_value = CLIENT_HELO_UNKNOWN;
} else {
if (strlen(attr_value) > VALID_HOSTNAME_LEN) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Bad %s syntax: %s",
XCLIENT_HELO, attr_value);
return (-1);
}
neuter(attr_value, NEUTER_CHARACTERS, '?');
}
UPDATE_STR(state->helo_name, attr_value);
got_helo = 1;
}
else if (STREQ(attr_name, XCLIENT_PROTO)) {
if (name_code(proto_names, NAME_CODE_FLAG_NONE, attr_value) < 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Bad %s syntax: %s",
XCLIENT_PROTO, attr_value);
return (-1);
}
UPDATE_STR(state->protocol, uppercase(attr_value));
got_proto = 1;
}
#ifdef USE_SASL_AUTH
else if (STREQ(attr_name, XCLIENT_LOGIN)) {
if (STREQ(attr_value, XCLIENT_UNAVAILABLE) == 0) {
smtpd_sasl_auth_extern(state, attr_value, XCLIENT_CMD);
got_login = 1;
}
}
#endif
else {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Bad %s attribute name: %s",
XCLIENT_CMD, attr_name);
return (-1);
}
}
if (update_namaddr) {
if (state->namaddr)
myfree(state->namaddr);
state->namaddr =
SMTPD_BUILD_NAMADDRPORT(state->name, state->addr, state->port);
}
xclient_allowed =
namadr_list_match(xclient_hosts, state->name, state->addr);
if (got_helo == 0)
helo_reset(state);
if (got_proto == 0 && strcasecmp(state->protocol, MAIL_PROTO_SMTP) != 0) {
myfree(state->protocol);
state->protocol = mystrdup(MAIL_PROTO_SMTP);
}
#ifdef USE_SASL_AUTH
if (got_login == 0)
smtpd_sasl_auth_reset(state);
#endif
chat_reset(state, 0);
mail_reset(state);
rcpt_reset(state);
if (smtpd_milters)
milter_disc_event(smtpd_milters);
vstream_longjmp(state->client, SMTP_ERR_NONE);
return (0);
}
static int xforward_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
SMTPD_TOKEN *argp;
char *raw_value;
char *attr_value;
const char *bare_value;
char *attr_name;
int updated = 0;
static const NAME_CODE xforward_flags[] = {
XFORWARD_NAME, SMTPD_STATE_XFORWARD_NAME,
XFORWARD_ADDR, SMTPD_STATE_XFORWARD_ADDR,
XFORWARD_PORT, SMTPD_STATE_XFORWARD_PORT,
XFORWARD_PROTO, SMTPD_STATE_XFORWARD_PROTO,
XFORWARD_HELO, SMTPD_STATE_XFORWARD_HELO,
XFORWARD_IDENT, SMTPD_STATE_XFORWARD_IDENT,
XFORWARD_DOMAIN, SMTPD_STATE_XFORWARD_DOMAIN,
0, 0,
};
static const char *context_name[] = {
MAIL_ATTR_RWR_LOCAL,
MAIL_ATTR_RWR_REMOTE,
};
static const NAME_CODE xforward_to_context[] = {
XFORWARD_DOM_LOCAL, 0,
XFORWARD_DOM_REMOTE, 1,
0, -1,
};
int flag;
int context_code;
if (IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 5.5.1 Error: MAIL transaction in progress");
return (-1);
}
if (argc < 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Syntax: %s attribute=value...",
XFORWARD_CMD);
return (-1);
}
if (xforward_hosts && xforward_hosts->error)
cant_permit_command(state, XFORWARD_CMD);
if (!xforward_allowed) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "550 5.7.0 Error: insufficient authorization");
return (-1);
}
if (state->xforward.flags == 0)
smtpd_xforward_preset(state);
if (state->expand_buf == 0)
state->expand_buf = vstring_alloc(100);
for (argp = argv + 1; argp < argv + argc; argp++) {
attr_name = argp->strval;
if ((raw_value = split_at(attr_name, '=')) == 0 || *raw_value == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Error: attribute=value expected");
return (-1);
}
if (strlen(raw_value) > 255) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Error: attribute value too long");
return (-1);
}
attr_value = xtext_unquote(state->expand_buf, raw_value) ?
STR(state->expand_buf) : raw_value;
printable(attr_value, '?');
flag = name_code(xforward_flags, NAME_CODE_FLAG_NONE, attr_name);
switch (flag) {
case SMTPD_STATE_XFORWARD_NAME:
if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
attr_value = CLIENT_NAME_UNKNOWN;
} else {
neuter(attr_value, NEUTER_CHARACTERS, '?');
if (!valid_hostname(attr_value, DONT_GRIPE)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Bad %s syntax: %s",
XFORWARD_NAME, attr_value);
return (-1);
}
}
UPDATE_STR(state->xforward.name, attr_value);
break;
case SMTPD_STATE_XFORWARD_ADDR:
if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
attr_value = CLIENT_ADDR_UNKNOWN;
bare_value = attr_value;
} else {
neuter(attr_value, NEUTER_CHARACTERS, '?');
if ((bare_value = valid_mailhost_addr(attr_value, DONT_GRIPE)) == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Bad %s syntax: %s",
XFORWARD_ADDR, attr_value);
return (-1);
}
}
UPDATE_STR(state->xforward.addr, bare_value);
UPDATE_STR(state->xforward.rfc_addr, attr_value);
break;
case SMTPD_STATE_XFORWARD_PORT:
if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
attr_value = CLIENT_PORT_UNKNOWN;
} else {
if (!alldig(attr_value)
|| strlen(attr_value) > sizeof("65535") - 1) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Bad %s syntax: %s",
XFORWARD_PORT, attr_value);
return (-1);
}
}
UPDATE_STR(state->xforward.port, attr_value);
break;
case SMTPD_STATE_XFORWARD_HELO:
if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
attr_value = CLIENT_HELO_UNKNOWN;
} else {
neuter(attr_value, NEUTER_CHARACTERS, '?');
}
UPDATE_STR(state->xforward.helo_name, attr_value);
break;
case SMTPD_STATE_XFORWARD_PROTO:
if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
attr_value = CLIENT_PROTO_UNKNOWN;
} else {
if (strlen(attr_value) > 64) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Bad %s syntax: %s",
XFORWARD_PROTO, attr_value);
return (-1);
}
neuter(attr_value, NEUTER_CHARACTERS, '?');
}
UPDATE_STR(state->xforward.protocol, attr_value);
break;
case SMTPD_STATE_XFORWARD_IDENT:
if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
attr_value = CLIENT_IDENT_UNKNOWN;
} else {
neuter(attr_value, NEUTER_CHARACTERS, '?');
}
UPDATE_STR(state->xforward.ident, attr_value);
break;
case SMTPD_STATE_XFORWARD_DOMAIN:
if (STREQ(attr_value, XFORWARD_UNAVAILABLE))
attr_value = XFORWARD_DOM_LOCAL;
if ((context_code = name_code(xforward_to_context,
NAME_CODE_FLAG_NONE,
attr_value)) < 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Bad %s syntax: %s",
XFORWARD_DOMAIN, attr_value);
return (-1);
}
UPDATE_STR(state->xforward.domain, context_name[context_code]);
break;
default:
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Bad %s attribute name: %s",
XFORWARD_CMD, attr_name);
return (-1);
}
updated |= flag;
}
state->xforward.flags |= updated;
if (updated & (SMTPD_STATE_XFORWARD_NAME | SMTPD_STATE_XFORWARD_ADDR)) {
if (state->xforward.namaddr)
myfree(state->xforward.namaddr);
state->xforward.namaddr =
IS_AVAIL_CLIENT_ADDR(state->xforward.addr) ?
SMTPD_BUILD_NAMADDRPORT(state->xforward.name,
state->xforward.addr,
state->xforward.port) :
mystrdup(state->xforward.name);
}
smtpd_chat_reply(state, "250 2.0.0 Ok");
return (0);
}
static void chat_reset(SMTPD_STATE *state, int threshold)
{
if (state->history != 0 && state->history->argc > threshold) {
if (SMTPD_STAND_ALONE(state) == 0
&& (state->error_mask & state->notify_mask))
smtpd_chat_notify(state);
state->error_mask = 0;
smtpd_chat_reset(state);
}
}
#ifdef USE_TLS
static void smtpd_start_tls(SMTPD_STATE *state)
{
int rate;
int cert_present;
int requirecert;
#ifdef USE_TLSPROXY
vstream_control(state->tlsproxy, VSTREAM_CTL_DOUBLE, VSTREAM_CTL_END);
vstream_control(state->client, VSTREAM_CTL_SWAP_FD, state->tlsproxy,
VSTREAM_CTL_END);
(void) vstream_fclose(state->tlsproxy);
state->tlsproxy = 0;
state->tls_context = tls_proxy_context_receive(state->client);
requirecert = (var_smtpd_tls_req_ccert && var_smtpd_enforce_tls);
#else
TLS_SERVER_START_PROPS props;
static char *cipher_grade;
static VSTRING *cipher_exclusions;
#define ADD_EXCLUDE(vstr, str) \
do { \
if (*(str)) \
vstring_sprintf_append((vstr), "%s%s", \
VSTRING_LEN(vstr) ? " " : "", (str)); \
} while (0)
if (cipher_grade == 0) {
cipher_grade = var_smtpd_enforce_tls ?
var_smtpd_tls_mand_ciph : var_smtpd_tls_ciph;
cipher_exclusions = vstring_alloc(10);
ADD_EXCLUDE(cipher_exclusions, var_smtpd_tls_excl_ciph);
if (var_smtpd_enforce_tls)
ADD_EXCLUDE(cipher_exclusions, var_smtpd_tls_mand_excl);
if (ask_client_cert)
ADD_EXCLUDE(cipher_exclusions, "aNULL");
}
requirecert = (var_smtpd_tls_req_ccert && var_smtpd_enforce_tls);
state->tls_context =
TLS_SERVER_START(&props,
ctx = smtpd_tls_ctx,
stream = state->client,
fd = -1,
timeout = var_smtpd_starttls_tmout,
requirecert = requirecert,
serverid = state->service,
namaddr = state->namaddr,
cipher_grade = cipher_grade,
cipher_exclusions = STR(cipher_exclusions),
fpt_dgst = var_smtpd_tls_fpt_dgst);
#endif
if (var_smtpd_cntls_limit > 0
&& (state->tls_context == 0 || state->tls_context->session_reused == 0)
&& SMTPD_STAND_ALONE(state) == 0
&& !xclient_allowed
&& anvil_clnt
&& !namadr_list_match(hogger_list, state->name, state->addr)
&& anvil_clnt_newtls(anvil_clnt, state->service, state->addr,
&rate) == ANVIL_STAT_OK
&& rate > var_smtpd_cntls_limit) {
state->error_mask |= MAIL_ERROR_POLICY;
msg_warn("New TLS session rate limit exceeded: %d from %s for service %s",
rate, state->namaddr, state->service);
if (state->tls_context)
smtpd_chat_reply(state,
"421 4.7.0 %s Error: too many new TLS sessions from %s",
var_myhostname, state->namaddr);
vstream_longjmp(state->client, SMTP_ERR_QUIET);
}
if (state->tls_context == 0)
vstream_longjmp(state->client, SMTP_ERR_EOF);
if (requirecert && TLS_CERT_IS_TRUSTED(state->tls_context) == 0) {
cert_present = TLS_CERT_IS_PRESENT(state->tls_context);
msg_info("NOQUEUE: abort: TLS from %s: %s",
state->namaddr, cert_present ?
"Client certificate not trusted" :
"No client certificate presented");
smtpd_chat_query(state);
smtpd_chat_reply(state, "421 4.7.1 %s Error: %s",
var_myhostname, cert_present ?
"Client certificate not trusted" :
"No client certificate presented");
state->error_mask |= MAIL_ERROR_POLICY;
return;
}
#ifdef USE_SASL_AUTH
if (var_smtpd_sasl_enable) {
if (smtpd_sasl_is_active(state)) {
smtpd_sasl_auth_reset(state);
smtpd_sasl_deactivate(state);
}
if (smtpd_sasl_is_active(state) == 0)
smtpd_sasl_activate(state, VAR_SMTPD_SASL_TLS_OPTS,
var_smtpd_sasl_tls_opts);
}
#endif
}
static int starttls_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
{
const char *err;
int rate;
if (argc != 1) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.5.4 Syntax: STARTTLS");
return (-1);
}
if (smtpd_milters != 0 && (err = milter_other_event(smtpd_milters)) != 0) {
if (err[0] == '5') {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "%s", err);
return (-1);
}
else if (err[0] == '4') {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "454 4.3.0 Try again later");
return (-1);
}
}
if (state->tls_context != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "554 5.5.1 Error: TLS already active");
return (-1);
}
if (var_smtpd_use_tls == 0
|| (state->ehlo_discard_mask & EHLO_MASK_STARTTLS)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "502 5.5.1 Error: command not implemented");
return (-1);
}
#ifdef USE_TLSPROXY
#define PROXY_OPEN_FLAGS \
(TLS_PROXY_FLAG_ROLE_SERVER | TLS_PROXY_FLAG_SEND_CONTEXT)
state->tlsproxy = tls_proxy_open(var_tlsproxy_service, PROXY_OPEN_FLAGS,
state->client, state->addr,
state->port, var_smtpd_tmout);
if (state->tlsproxy == 0) {
state->error_mask |= MAIL_ERROR_SOFTWARE;
smtpd_chat_reply(state, "454 4.7.0 TLS not available due to local problem");
return (-1);
}
#else
if (smtpd_tls_ctx == 0) {
state->error_mask |= MAIL_ERROR_SOFTWARE;
smtpd_chat_reply(state, "454 4.7.0 TLS not available due to local problem");
return (-1);
}
#endif
if (var_smtpd_cntls_limit > 0
&& SMTPD_STAND_ALONE(state) == 0
&& !xclient_allowed
&& anvil_clnt
&& !namadr_list_match(hogger_list, state->name, state->addr)
&& anvil_clnt_newtls_stat(anvil_clnt, state->service, state->addr,
&rate) == ANVIL_STAT_OK
&& rate > var_smtpd_cntls_limit) {
state->error_mask |= MAIL_ERROR_POLICY;
msg_warn("Refusing STARTTLS request from %s for service %s",
state->namaddr, state->service);
smtpd_chat_reply(state,
"454 4.7.0 Error: too many new TLS sessions from %s",
state->namaddr);
#ifdef USE_TLSPROXY
(void) vstream_fclose(state->tlsproxy);
state->tlsproxy = 0;
#endif
return (-1);
}
smtpd_chat_reply(state, "220 2.0.0 Ready to start TLS");
smtp_flush(state->client);
vstream_fpurge(state->client, VSTREAM_PURGE_BOTH);
helo_reset(state);
mail_reset(state);
rcpt_reset(state);
smtpd_start_tls(state);
return (0);
}
static void tls_reset(SMTPD_STATE *state)
{
int failure = 0;
if (state->tls_context) {
if (vstream_feof(state->client) || vstream_ferror(state->client))
failure = 1;
vstream_fflush(state->client);
#ifdef USE_TLSPROXY
tls_proxy_context_free(state->tls_context);
#else
tls_server_stop(smtpd_tls_ctx, state->client, var_smtpd_starttls_tmout,
failure, state->tls_context);
#endif
state->tls_context = 0;
}
}
#endif
typedef struct SMTPD_CMD {
char *name;
int (*action) (SMTPD_STATE *, int, SMTPD_TOKEN *);
int flags;
} SMTPD_CMD;
#define SMTPD_CMD_FLAG_LIMIT (1<<0)
#define SMTPD_CMD_FLAG_PRE_TLS (1<<1)
#define SMTPD_CMD_FLAG_LAST (1<<2)
static SMTPD_CMD smtpd_cmd_table[] = {
SMTPD_CMD_HELO, helo_cmd, SMTPD_CMD_FLAG_LIMIT | SMTPD_CMD_FLAG_PRE_TLS | SMTPD_CMD_FLAG_LAST,
SMTPD_CMD_EHLO, ehlo_cmd, SMTPD_CMD_FLAG_LIMIT | SMTPD_CMD_FLAG_PRE_TLS | SMTPD_CMD_FLAG_LAST,
#ifdef USE_TLS
SMTPD_CMD_STARTTLS, starttls_cmd, SMTPD_CMD_FLAG_PRE_TLS,
#endif
#ifdef USE_SASL_AUTH
SMTPD_CMD_AUTH, smtpd_sasl_auth_cmd, 0,
#endif
SMTPD_CMD_MAIL, mail_cmd, 0,
SMTPD_CMD_RCPT, rcpt_cmd, 0,
SMTPD_CMD_DATA, data_cmd, SMTPD_CMD_FLAG_LAST,
#ifdef __APPLE_OS_X_SERVER__
#if defined(USE_SASL_AUTH) && defined(USE_TLS)
SMTPD_CMD_BURL, burl_cmd, 0,
#endif
SMTPD_CMD_BDAT, bdat_cmd, 0,
#endif
SMTPD_CMD_RSET, rset_cmd, SMTPD_CMD_FLAG_LIMIT,
SMTPD_CMD_NOOP, noop_cmd, SMTPD_CMD_FLAG_LIMIT | SMTPD_CMD_FLAG_PRE_TLS | SMTPD_CMD_FLAG_LAST,
SMTPD_CMD_VRFY, vrfy_cmd, SMTPD_CMD_FLAG_LIMIT | SMTPD_CMD_FLAG_LAST,
SMTPD_CMD_ETRN, etrn_cmd, SMTPD_CMD_FLAG_LIMIT,
SMTPD_CMD_QUIT, quit_cmd, SMTPD_CMD_FLAG_PRE_TLS,
SMTPD_CMD_XCLIENT, xclient_cmd, 0,
SMTPD_CMD_XFORWARD, xforward_cmd, 0,
0,
};
static STRING_LIST *smtpd_noop_cmds;
static STRING_LIST *smtpd_forbid_cmds;
static void smtpd_proto(SMTPD_STATE *state)
{
int argc;
SMTPD_TOKEN *argv;
SMTPD_CMD *cmdp;
int tls_rate;
const char *ehlo_words;
const char *err;
int status;
const char *cp;
smtp_stream_setup(state->client, var_smtpd_tmout, var_smtpd_rec_deadline);
while ((status = vstream_setjmp(state->client)) == SMTP_ERR_NONE)
;
switch (status) {
default:
msg_panic("smtpd_proto: unknown error reading from %s",
state->namaddr);
break;
case SMTP_ERR_TIME:
state->reason = REASON_TIMEOUT;
if (vstream_setjmp(state->client) == 0)
smtpd_chat_reply(state, "421 4.4.2 %s Error: timeout exceeded",
var_myhostname);
break;
case SMTP_ERR_EOF:
state->reason = REASON_LOST_CONNECTION;
break;
case SMTP_ERR_QUIET:
break;
case SMTP_ERR_DATA:
msg_info("%s: reject: %s from %s: "
"421 4.3.0 %s Server local data error",
(state->queue_id ? state->queue_id : "NOQUEUE"),
state->where, state->namaddr, var_myhostname);
state->error_mask |= MAIL_ERROR_DATA;
if (vstream_setjmp(state->client) == 0)
smtpd_chat_reply(state, "421 4.3.0 %s Server local data error",
var_myhostname);
break;
case 0:
#ifdef USE_TLS
if (SMTPD_STAND_ALONE(state) == 0 && var_smtpd_tls_wrappermode) {
#ifdef USE_TLSPROXY
state->tlsproxy = tls_proxy_open(var_tlsproxy_service,
PROXY_OPEN_FLAGS,
state->client, state->addr,
state->port, var_smtpd_tmout);
if (state->tlsproxy == 0) {
msg_warn("Wrapper-mode request dropped from %s for service %s."
" TLS context initialization failed. For details see"
" earlier warnings in your logs.",
state->namaddr, state->service);
break;
}
#else
if (smtpd_tls_ctx == 0) {
msg_warn("Wrapper-mode request dropped from %s for service %s."
" TLS context initialization failed. For details see"
" earlier warnings in your logs.",
state->namaddr, state->service);
break;
}
#endif
if (var_smtpd_cntls_limit > 0
&& !xclient_allowed
&& anvil_clnt
&& !namadr_list_match(hogger_list, state->name, state->addr)
&& anvil_clnt_newtls_stat(anvil_clnt, state->service,
state->addr, &tls_rate) == ANVIL_STAT_OK
&& tls_rate > var_smtpd_cntls_limit) {
state->error_mask |= MAIL_ERROR_POLICY;
msg_warn("Refusing TLS service request from %s for service %s",
state->namaddr, state->service);
break;
}
smtpd_start_tls(state);
}
#endif
if (SMTPD_STAND_ALONE(state) == 0
&& !xclient_allowed
&& anvil_clnt
&& !namadr_list_match(hogger_list, state->name, state->addr)
&& anvil_clnt_connect(anvil_clnt, state->service, state->addr,
&state->conn_count, &state->conn_rate)
== ANVIL_STAT_OK) {
if (var_smtpd_cconn_limit > 0
&& state->conn_count > var_smtpd_cconn_limit) {
state->error_mask |= MAIL_ERROR_POLICY;
msg_warn("Connection concurrency limit exceeded: %d from %s for service %s",
state->conn_count, state->namaddr, state->service);
smtpd_chat_reply(state, "421 4.7.0 %s Error: too many connections from %s",
var_myhostname, state->addr);
break;
}
if (var_smtpd_crate_limit > 0
&& state->conn_rate > var_smtpd_crate_limit) {
msg_warn("Connection rate limit exceeded: %d from %s for service %s",
state->conn_rate, state->namaddr, state->service);
smtpd_chat_reply(state, "421 4.7.0 %s Error: too many connections from %s",
var_myhostname, state->addr);
break;
}
}
if (ehlo_discard_maps == 0
|| (ehlo_words = maps_find(ehlo_discard_maps, state->addr, 0)) == 0)
ehlo_words = var_smtpd_ehlo_dis_words;
state->ehlo_discard_mask = ehlo_mask(ehlo_words);
if (SMTPD_STAND_ALONE(state) == 0
&& var_smtpd_delay_reject == 0
&& (err = smtpd_check_client(state)) != 0) {
state->error_mask |= MAIL_ERROR_POLICY;
state->access_denied = mystrdup(err);
smtpd_chat_reply(state, "%s", state->access_denied);
state->error_count++;
}
else {
err = 0;
if (smtpd_milters != 0 && SMTPD_STAND_ALONE(state) == 0) {
milter_macro_callback(smtpd_milters, smtpd_milter_eval,
(void *) state);
if ((err = milter_conn_event(smtpd_milters, state->name,
state->addr,
strcmp(state->port, CLIENT_PORT_UNKNOWN) ?
state->port : "0",
state->addr_family)) != 0)
err = check_milter_reply(state, err);
}
if (err && err[0] == '5') {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "554 %s ESMTP not accepting connections",
var_myhostname);
state->error_count++;
} else if (err && strncmp(err, "421", 3) == 0) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "421 %s Service unavailable - try again later",
var_myhostname);
} else {
smtpd_chat_reply(state, "220 %s", var_smtpd_banner);
}
}
#ifdef USE_SASL_AUTH
if (var_smtpd_sasl_enable && smtpd_sasl_is_active(state) == 0
#ifdef USE_TLS
&& state->tls_context == 0 && !var_smtpd_tls_auth_only
#else
&& var_smtpd_tls_auth_only == 0
#endif
)
smtpd_sasl_activate(state, VAR_SMTPD_SASL_OPTS,
var_smtpd_sasl_opts);
#endif
for (;;) {
if (state->flags & SMTPD_FLAG_HANGUP)
break;
if (state->error_count >= var_smtpd_hard_erlim) {
state->reason = REASON_ERROR_LIMIT;
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "421 4.7.0 %s Error: too many errors",
var_myhostname);
break;
}
watchdog_pat();
smtpd_chat_query(state);
if (smtpd_cmd_filter != 0) {
for (cp = STR(state->buffer); *cp && IS_SPACE_TAB(*cp); cp++)
;
if ((cp = dict_get(smtpd_cmd_filter, cp)) != 0) {
msg_info("%s: replacing command \"%.100s\" with \"%.100s\"",
state->namaddr, STR(state->buffer), cp);
vstring_strcpy(state->buffer, cp);
} else if (smtpd_cmd_filter->error != 0) {
msg_warn("%s:%s lookup error for \"%.100s\"",
smtpd_cmd_filter->type, smtpd_cmd_filter->name,
printable(STR(state->buffer), '?'));
vstream_longjmp(state->client, SMTP_ERR_DATA);
}
}
if ((argc = smtpd_token(vstring_str(state->buffer), &argv)) == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "500 5.5.2 Error: bad syntax");
state->error_count++;
continue;
}
if (*var_smtpd_noop_cmds
&& string_list_match(smtpd_noop_cmds, argv[0].strval)) {
smtpd_chat_reply(state, "250 2.0.0 Ok");
if (state->junk_cmds++ > var_smtpd_junk_cmd_limit)
state->error_count++;
continue;
}
for (cmdp = smtpd_cmd_table; cmdp->name != 0; cmdp++)
if (strcasecmp(argv[0].strval, cmdp->name) == 0)
break;
if (cmdp->name == 0) {
state->where = SMTPD_CMD_UNKNOWN;
if (is_header(argv[0].strval)
|| (*var_smtpd_forbid_cmds
&& string_list_match(smtpd_forbid_cmds, argv[0].strval))) {
msg_warn("non-SMTP command from %s: %.100s",
state->namaddr, vstring_str(state->buffer));
smtpd_chat_reply(state, "221 2.7.0 Error: I can break rules, too. Goodbye.");
break;
}
}
if (state->access_denied && cmdp->action != quit_cmd) {
if (strncmp(state->access_denied + 1, "21", 2) == 0) {
smtpd_chat_reply(state, "%s", state->access_denied);
continue;
}
smtpd_chat_reply(state, "503 5.7.0 Error: access denied for %s",
state->namaddr);
state->error_count++;
continue;
}
if (cmdp->name == 0) {
if (smtpd_milters != 0
&& SMTPD_STAND_ALONE(state) == 0
&& (err = milter_unknown_event(smtpd_milters,
argv[0].strval)) != 0
&& (err = check_milter_reply(state, err)) != 0) {
smtpd_chat_reply(state, "%s", err);
} else
smtpd_chat_reply(state, "502 5.5.2 Error: command not recognized");
state->error_mask |= MAIL_ERROR_PROTOCOL;
state->error_count++;
continue;
}
#ifdef USE_TLS
if (var_smtpd_enforce_tls &&
!state->tls_context &&
(cmdp->flags & SMTPD_CMD_FLAG_PRE_TLS) == 0) {
smtpd_chat_reply(state,
"530 5.7.0 Must issue a STARTTLS command first");
state->error_count++;
continue;
}
#endif
state->where = cmdp->name;
if (SMTPD_STAND_ALONE(state) == 0
&& (strcasecmp(state->protocol, MAIL_PROTO_ESMTP) != 0
|| (cmdp->flags & SMTPD_CMD_FLAG_LAST))
&& (state->flags & SMTPD_FLAG_ILL_PIPELINING) == 0
&& (vstream_peek(state->client) > 0
|| peekfd(vstream_fileno(state->client)) > 0)) {
if (state->expand_buf == 0)
state->expand_buf = vstring_alloc(100);
escape(state->expand_buf, vstream_peek_data(state->client),
vstream_peek(state->client) < 100 ?
vstream_peek(state->client) : 100);
msg_info("improper command pipelining after %s from %s: %s",
cmdp->name, state->namaddr, STR(state->expand_buf));
state->flags |= SMTPD_FLAG_ILL_PIPELINING;
}
if (cmdp->action(state, argc, argv) != 0)
state->error_count++;
if ((cmdp->flags & SMTPD_CMD_FLAG_LIMIT)
&& state->junk_cmds++ > var_smtpd_junk_cmd_limit)
state->error_count++;
if (cmdp->action == quit_cmd)
break;
}
break;
}
if (SMTPD_STAND_ALONE(state) == 0
&& !xclient_allowed
&& anvil_clnt
&& !namadr_list_match(hogger_list, state->name, state->addr))
anvil_clnt_disconnect(anvil_clnt, state->service, state->addr);
if (state->reason && state->where) {
if (strcmp(state->where, SMTPD_AFTER_DATA) == 0) {
msg_info("%s after %s (%lu bytes) from %s",
state->reason, SMTPD_CMD_DATA,
(long) (state->act_size + vstream_peek(state->client)),
state->namaddr);
} else if (strcmp(state->where, SMTPD_AFTER_DOT)
|| strcmp(state->reason, REASON_LOST_CONNECTION)) {
msg_info("%s after %s from %s",
state->reason, state->where, state->namaddr);
}
}
#ifdef USE_TLS
tls_reset(state);
#endif
helo_reset(state);
#ifdef USE_SASL_AUTH
smtpd_sasl_auth_reset(state);
if (smtpd_sasl_is_active(state)) {
smtpd_sasl_deactivate(state);
}
#endif
chat_reset(state, 0);
mail_reset(state);
rcpt_reset(state);
if (smtpd_milters)
milter_disc_event(smtpd_milters);
}
static void smtpd_service(VSTREAM *stream, char *service, char **argv)
{
SMTPD_STATE state;
if (argv[0])
msg_fatal("unexpected command-line argument: %s", argv[0]);
if (SMTPD_STAND_ALONE_STREAM(stream) == 0
&& inet_proto_info()->ai_family_list[0] == 0)
msg_fatal("all network protocols are disabled (%s = %s)",
VAR_INET_PROTOCOLS, var_inet_protocols);
smtpd_state_init(&state, stream, service);
msg_info("connect from %s", state.namaddr);
if (SMTPD_STAND_ALONE((&state))) {
var_smtpd_use_tls = 0;
var_smtpd_enforce_tls = 0;
var_smtpd_tls_auth_only = 0;
}
xclient_allowed = SMTPD_STAND_ALONE((&state)) == 0 &&
namadr_list_match(xclient_hosts, state.name, state.addr);
xforward_allowed = SMTPD_STAND_ALONE((&state)) == 0 &&
namadr_list_match(xforward_hosts, state.name, state.addr);
debug_peer_check(state.name, state.addr);
smtpd_proto(&state);
msg_info("disconnect from %s", state.namaddr);
smtpd_state_reset(&state);
debug_peer_restore();
}
static void pre_accept(char *unused_name, char **unused_argv)
{
const char *table;
if ((table = dict_changed_name()) != 0) {
msg_info("table %s has changed -- restarting", table);
exit(0);
}
}
static void pre_jail_init(char *unused_name, char **unused_argv)
{
smtpd_noop_cmds = string_list_init(MATCH_FLAG_RETURN, var_smtpd_noop_cmds);
smtpd_forbid_cmds = string_list_init(MATCH_FLAG_RETURN, var_smtpd_forbid_cmds);
verp_clients = namadr_list_init(MATCH_FLAG_RETURN, var_verp_clients);
xclient_hosts = namadr_list_init(MATCH_FLAG_RETURN, var_xclient_hosts);
xforward_hosts = namadr_list_init(MATCH_FLAG_RETURN, var_xforward_hosts);
hogger_list = namadr_list_init(MATCH_FLAG_RETURN, var_smtpd_hoggers);
if (getuid() == 0 || getuid() == var_owner_uid)
smtpd_check_init();
smtpd_expand_init();
debug_peer_init();
if (var_smtpd_sasl_enable)
#ifdef USE_SASL_AUTH
#ifdef __APPLE_OS_X_SERVER__
smtpd_sasl_initialize( var_smtpd_use_pw_server );
#else
smtpd_sasl_initialize();
#endif
if (*var_smtpd_sasl_exceptions_networks)
sasl_exceptions_networks =
namadr_list_init(MATCH_FLAG_RETURN,
var_smtpd_sasl_exceptions_networks);
#else
msg_warn("%s is true, but SASL support is not compiled in",
VAR_SMTPD_SASL_ENABLE);
#endif
if (*var_smtpd_cmd_filter)
smtpd_cmd_filter = dict_open(var_smtpd_cmd_filter, O_RDONLY,
DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
if (!var_smtpd_tls_wrappermode && *var_smtpd_tls_level) {
switch (tls_level_lookup(var_smtpd_tls_level)) {
default:
msg_fatal("Invalid TLS level \"%s\"", var_smtpd_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_SMTPD_TLS_LEVEL, var_smtpd_tls_level);
case TLS_LEV_ENCRYPT:
var_smtpd_enforce_tls = var_smtpd_use_tls = 1;
break;
case TLS_LEV_MAY:
var_smtpd_enforce_tls = 0;
var_smtpd_use_tls = 1;
break;
case TLS_LEV_NONE:
var_smtpd_enforce_tls = var_smtpd_use_tls = 0;
break;
}
}
var_smtpd_enforce_tls = var_smtpd_tls_wrappermode || var_smtpd_enforce_tls;
var_smtpd_tls_auth_only = var_smtpd_tls_auth_only || var_smtpd_enforce_tls;
var_smtpd_use_tls = var_smtpd_use_tls || var_smtpd_enforce_tls;
if (getuid() == 0 || getuid() == var_owner_uid) {
if (var_smtpd_use_tls) {
#ifdef USE_TLS
#ifndef USE_TLSPROXY
TLS_SERVER_INIT_PROPS props;
const char *cert_file;
int have_server_cert;
int no_server_cert_ok;
int require_server_cert;
ask_client_cert = require_server_cert =
(var_smtpd_tls_ask_ccert
|| (var_smtpd_enforce_tls && var_smtpd_tls_req_ccert));
if (strcasecmp(var_smtpd_tls_cert_file, "none") == 0) {
no_server_cert_ok = 1;
cert_file = "";
} else {
no_server_cert_ok = 0;
cert_file = var_smtpd_tls_cert_file;
}
have_server_cert =
(*cert_file || *var_smtpd_tls_dcert_file || *var_smtpd_tls_eccert_file);
if (!have_server_cert && require_server_cert)
msg_warn("Need a server cert to request client certs");
if (!var_smtpd_enforce_tls && var_smtpd_tls_req_ccert)
msg_warn("Can't require client certs unless TLS is required");
if (have_server_cert || (no_server_cert_ok && !require_server_cert))
smtpd_tls_ctx =
TLS_SERVER_INIT(&props,
log_param = VAR_SMTPD_TLS_LOGLEVEL,
log_level = var_smtpd_tls_loglevel,
verifydepth = var_smtpd_tls_ccert_vd,
cache_type = TLS_MGR_SCACHE_SMTPD,
scache_timeout
= var_smtpd_tls_scache_timeout,
set_sessid = var_smtpd_tls_set_sessid,
cert_file = cert_file,
key_file = var_smtpd_tls_key_file,
dcert_file = var_smtpd_tls_dcert_file,
dkey_file = var_smtpd_tls_dkey_file,
eccert_file = var_smtpd_tls_eccert_file,
eckey_file = var_smtpd_tls_eckey_file,
CAfile = var_smtpd_tls_CAfile,
CApath = var_smtpd_tls_CApath,
dh1024_param_file
= var_smtpd_tls_dh1024_param_file,
dh512_param_file
= var_smtpd_tls_dh512_param_file,
eecdh_grade = var_smtpd_tls_eecdh,
protocols = var_smtpd_enforce_tls ?
var_smtpd_tls_mand_proto :
var_smtpd_tls_proto,
ask_ccert = ask_client_cert,
fpt_dgst = var_smtpd_tls_fpt_dgst);
else
msg_warn("No server certs available. TLS won't be enabled");
#endif
#else
msg_warn("TLS has been selected, but TLS support is not compiled in");
#endif
}
}
flush_init();
if (*var_smtpd_ehlo_dis_maps)
ehlo_discard_maps = maps_create(VAR_SMTPD_EHLO_DIS_MAPS,
var_smtpd_ehlo_dis_maps,
DICT_FLAG_LOCK);
#ifdef __APPLE_OS_X_SERVER__
#if defined(USE_SASL_AUTH) && defined(USE_TLS)
imap_read_config();
#endif
#endif
}
static void post_jail_init(char *unused_name, char **unused_argv)
{
smtpd_input_transp_mask =
input_transp_mask(VAR_INPUT_TRANSP, var_input_transp);
if (*var_smtpd_proxy_filt)
smtpd_proxy_opts =
smtpd_proxy_parse_opts(VAR_SMTPD_PROXY_OPTS, var_smtpd_proxy_opts);
if ((smtpd_input_transp_mask & INPUT_TRANSP_MILTER) == 0) {
if (*var_smtpd_milters)
smtpd_milters = milter_create(var_smtpd_milters,
var_milt_conn_time,
var_milt_cmd_time,
var_milt_msg_time,
var_milt_protocol,
var_milt_def_action,
var_milt_conn_macros,
var_milt_helo_macros,
var_milt_mail_macros,
var_milt_rcpt_macros,
var_milt_data_macros,
var_milt_eoh_macros,
var_milt_eod_macros,
var_milt_unk_macros);
else
smtpd_input_transp_mask |= INPUT_TRANSP_MILTER;
}
if (var_queue_minfree > 0
&& var_message_limit > 0
&& var_queue_minfree / 1.5 < var_message_limit)
msg_warn("%s(%lu) should be at least 1.5*%s(%lu)",
VAR_QUEUE_MINFREE, (unsigned long) var_queue_minfree,
VAR_MESSAGE_LIMIT, (unsigned long) var_message_limit);
if (var_smtpd_crate_limit || var_smtpd_cconn_limit
|| var_smtpd_cmail_limit || var_smtpd_crcpt_limit
|| var_smtpd_cntls_limit)
anvil_clnt = anvil_clnt_create();
}
MAIL_VERSION_STAMP_DECLARE;
int main(int argc, char **argv)
{
static const CONFIG_NINT_TABLE nint_table[] = {
VAR_SMTPD_SOFT_ERLIM, DEF_SMTPD_SOFT_ERLIM, &var_smtpd_soft_erlim, 1, 0,
VAR_SMTPD_HARD_ERLIM, DEF_SMTPD_HARD_ERLIM, &var_smtpd_hard_erlim, 1, 0,
VAR_SMTPD_JUNK_CMD, DEF_SMTPD_JUNK_CMD, &var_smtpd_junk_cmd_limit, 1, 0,
VAR_VERIFY_POLL_COUNT, DEF_VERIFY_POLL_COUNT, &var_verify_poll_count, 1, 0,
0,
};
static const CONFIG_INT_TABLE int_table[] = {
VAR_SMTPD_RCPT_LIMIT, DEF_SMTPD_RCPT_LIMIT, &var_smtpd_rcpt_limit, 1, 0,
VAR_QUEUE_MINFREE, DEF_QUEUE_MINFREE, &var_queue_minfree, 0, 0,
VAR_UNK_CLIENT_CODE, DEF_UNK_CLIENT_CODE, &var_unk_client_code, 0, 0,
VAR_BAD_NAME_CODE, DEF_BAD_NAME_CODE, &var_bad_name_code, 0, 0,
VAR_UNK_NAME_CODE, DEF_UNK_NAME_CODE, &var_unk_name_code, 0, 0,
VAR_UNK_ADDR_CODE, DEF_UNK_ADDR_CODE, &var_unk_addr_code, 0, 0,
VAR_RELAY_CODE, DEF_RELAY_CODE, &var_relay_code, 0, 0,
VAR_MAPS_RBL_CODE, DEF_MAPS_RBL_CODE, &var_maps_rbl_code, 0, 0,
VAR_MAP_REJECT_CODE, DEF_MAP_REJECT_CODE, &var_map_reject_code, 0, 0,
VAR_MAP_DEFER_CODE, DEF_MAP_DEFER_CODE, &var_map_defer_code, 0, 0,
VAR_REJECT_CODE, DEF_REJECT_CODE, &var_reject_code, 0, 0,
VAR_DEFER_CODE, DEF_DEFER_CODE, &var_defer_code, 0, 0,
VAR_NON_FQDN_CODE, DEF_NON_FQDN_CODE, &var_non_fqdn_code, 0, 0,
VAR_SMTPD_RCPT_OVERLIM, DEF_SMTPD_RCPT_OVERLIM, &var_smtpd_rcpt_overlim, 1, 0,
VAR_SMTPD_HIST_THRSH, DEF_SMTPD_HIST_THRSH, &var_smtpd_hist_thrsh, 1, 0,
VAR_UNV_FROM_RCODE, DEF_UNV_FROM_RCODE, &var_unv_from_rcode, 200, 599,
VAR_UNV_RCPT_RCODE, DEF_UNV_RCPT_RCODE, &var_unv_rcpt_rcode, 200, 599,
VAR_UNV_FROM_DCODE, DEF_UNV_FROM_DCODE, &var_unv_from_dcode, 200, 499,
VAR_UNV_RCPT_DCODE, DEF_UNV_RCPT_DCODE, &var_unv_rcpt_dcode, 200, 499,
VAR_MUL_RCPT_CODE, DEF_MUL_RCPT_CODE, &var_mul_rcpt_code, 0, 0,
VAR_LOCAL_RCPT_CODE, DEF_LOCAL_RCPT_CODE, &var_local_rcpt_code, 0, 0,
VAR_VIRT_ALIAS_CODE, DEF_VIRT_ALIAS_CODE, &var_virt_alias_code, 0, 0,
VAR_VIRT_MAILBOX_CODE, DEF_VIRT_MAILBOX_CODE, &var_virt_mailbox_code, 0, 0,
VAR_RELAY_RCPT_CODE, DEF_RELAY_RCPT_CODE, &var_relay_rcpt_code, 0, 0,
VAR_PLAINTEXT_CODE, DEF_PLAINTEXT_CODE, &var_plaintext_code, 0, 0,
VAR_SMTPD_CRATE_LIMIT, DEF_SMTPD_CRATE_LIMIT, &var_smtpd_crate_limit, 0, 0,
VAR_SMTPD_CCONN_LIMIT, DEF_SMTPD_CCONN_LIMIT, &var_smtpd_cconn_limit, 0, 0,
VAR_SMTPD_CMAIL_LIMIT, DEF_SMTPD_CMAIL_LIMIT, &var_smtpd_cmail_limit, 0, 0,
VAR_SMTPD_CRCPT_LIMIT, DEF_SMTPD_CRCPT_LIMIT, &var_smtpd_crcpt_limit, 0, 0,
VAR_SMTPD_CNTLS_LIMIT, DEF_SMTPD_CNTLS_LIMIT, &var_smtpd_cntls_limit, 0, 0,
#ifdef USE_TLS
VAR_SMTPD_TLS_CCERT_VD, DEF_SMTPD_TLS_CCERT_VD, &var_smtpd_tls_ccert_vd, 0, 0,
#endif
0,
};
static const CONFIG_TIME_TABLE time_table[] = {
VAR_SMTPD_TMOUT, DEF_SMTPD_TMOUT, &var_smtpd_tmout, 1, 0,
VAR_SMTPD_ERR_SLEEP, DEF_SMTPD_ERR_SLEEP, &var_smtpd_err_sleep, 0, 0,
VAR_SMTPD_PROXY_TMOUT, DEF_SMTPD_PROXY_TMOUT, &var_smtpd_proxy_tmout, 1, 0,
VAR_VERIFY_POLL_DELAY, DEF_VERIFY_POLL_DELAY, &var_verify_poll_delay, 1, 0,
VAR_SMTPD_POLICY_TMOUT, DEF_SMTPD_POLICY_TMOUT, &var_smtpd_policy_tmout, 1, 0,
VAR_SMTPD_POLICY_IDLE, DEF_SMTPD_POLICY_IDLE, &var_smtpd_policy_idle, 1, 0,
VAR_SMTPD_POLICY_TTL, DEF_SMTPD_POLICY_TTL, &var_smtpd_policy_ttl, 1, 0,
#ifdef USE_TLS
VAR_SMTPD_STARTTLS_TMOUT, DEF_SMTPD_STARTTLS_TMOUT, &var_smtpd_starttls_tmout, 1, 0,
VAR_SMTPD_TLS_SCACHTIME, DEF_SMTPD_TLS_SCACHTIME, &var_smtpd_tls_scache_timeout, 0, 0,
#endif
VAR_MILT_CONN_TIME, DEF_MILT_CONN_TIME, &var_milt_conn_time, 1, 0,
VAR_MILT_CMD_TIME, DEF_MILT_CMD_TIME, &var_milt_cmd_time, 1, 0,
VAR_MILT_MSG_TIME, DEF_MILT_MSG_TIME, &var_milt_msg_time, 1, 0,
VAR_VERIFY_SENDER_TTL, DEF_VERIFY_SENDER_TTL, &var_verify_sender_ttl, 0, 0,
0,
};
static const CONFIG_BOOL_TABLE bool_table[] = {
VAR_HELO_REQUIRED, DEF_HELO_REQUIRED, &var_helo_required,
VAR_SMTPD_DELAY_REJECT, DEF_SMTPD_DELAY_REJECT, &var_smtpd_delay_reject,
VAR_STRICT_RFC821_ENV, DEF_STRICT_RFC821_ENV, &var_strict_rfc821_env,
VAR_DISABLE_VRFY_CMD, DEF_DISABLE_VRFY_CMD, &var_disable_vrfy_cmd,
VAR_ALLOW_UNTRUST_ROUTE, DEF_ALLOW_UNTRUST_ROUTE, &var_allow_untrust_route,
VAR_SMTPD_SASL_ENABLE, DEF_SMTPD_SASL_ENABLE, &var_smtpd_sasl_enable,
VAR_SMTPD_SASL_AUTH_HDR, DEF_SMTPD_SASL_AUTH_HDR, &var_smtpd_sasl_auth_hdr,
VAR_BROKEN_AUTH_CLNTS, DEF_BROKEN_AUTH_CLNTS, &var_broken_auth_clients,
VAR_SHOW_UNK_RCPT_TABLE, DEF_SHOW_UNK_RCPT_TABLE, &var_show_unk_rcpt_table,
VAR_SMTPD_REJ_UNL_FROM, DEF_SMTPD_REJ_UNL_FROM, &var_smtpd_rej_unl_from,
VAR_SMTPD_REJ_UNL_RCPT, DEF_SMTPD_REJ_UNL_RCPT, &var_smtpd_rej_unl_rcpt,
VAR_SMTPD_USE_TLS, DEF_SMTPD_USE_TLS, &var_smtpd_use_tls,
VAR_SMTPD_ENFORCE_TLS, DEF_SMTPD_ENFORCE_TLS, &var_smtpd_enforce_tls,
VAR_SMTPD_TLS_WRAPPER, DEF_SMTPD_TLS_WRAPPER, &var_smtpd_tls_wrappermode,
VAR_SMTPD_TLS_AUTH_ONLY, DEF_SMTPD_TLS_AUTH_ONLY, &var_smtpd_tls_auth_only,
#ifdef USE_TLS
VAR_SMTPD_TLS_ACERT, DEF_SMTPD_TLS_ACERT, &var_smtpd_tls_ask_ccert,
VAR_SMTPD_TLS_RCERT, DEF_SMTPD_TLS_RCERT, &var_smtpd_tls_req_ccert,
VAR_SMTPD_TLS_RECHEAD, DEF_SMTPD_TLS_RECHEAD, &var_smtpd_tls_received_header,
VAR_SMTPD_TLS_SET_SESSID, DEF_SMTPD_TLS_SET_SESSID, &var_smtpd_tls_set_sessid,
#endif
VAR_SMTPD_PEERNAME_LOOKUP, DEF_SMTPD_PEERNAME_LOOKUP, &var_smtpd_peername_lookup,
VAR_SMTPD_DELAY_OPEN, DEF_SMTPD_DELAY_OPEN, &var_smtpd_delay_open,
VAR_SMTPD_CLIENT_PORT_LOG, DEF_SMTPD_CLIENT_PORT_LOG, &var_smtpd_client_port_log,
#ifdef __APPLE_OS_X_SERVER__
VAR_SMTPD_USE_PW_SERVER, DEF_SMTPD_USE_PW_SERVER, &var_smtpd_use_pw_server,
#endif
0,
};
static const CONFIG_NBOOL_TABLE nbool_table[] = {
VAR_SMTPD_REC_DEADLINE, DEF_SMTPD_REC_DEADLINE, &var_smtpd_rec_deadline,
0,
};
static const CONFIG_STR_TABLE str_table[] = {
VAR_SMTPD_BANNER, DEF_SMTPD_BANNER, &var_smtpd_banner, 1, 0,
VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0,
VAR_CLIENT_CHECKS, DEF_CLIENT_CHECKS, &var_client_checks, 0, 0,
VAR_HELO_CHECKS, DEF_HELO_CHECKS, &var_helo_checks, 0, 0,
VAR_MAIL_CHECKS, DEF_MAIL_CHECKS, &var_mail_checks, 0, 0,
VAR_RCPT_CHECKS, DEF_RCPT_CHECKS, &var_rcpt_checks, 0, 0,
VAR_ETRN_CHECKS, DEF_ETRN_CHECKS, &var_etrn_checks, 0, 0,
VAR_DATA_CHECKS, DEF_DATA_CHECKS, &var_data_checks, 0, 0,
VAR_EOD_CHECKS, DEF_EOD_CHECKS, &var_eod_checks, 0, 0,
VAR_MAPS_RBL_DOMAINS, DEF_MAPS_RBL_DOMAINS, &var_maps_rbl_domains, 0, 0,
VAR_RBL_REPLY_MAPS, DEF_RBL_REPLY_MAPS, &var_rbl_reply_maps, 0, 0,
VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0,
VAR_REST_CLASSES, DEF_REST_CLASSES, &var_rest_classes, 0, 0,
VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps, 0, 0,
VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps, 0, 0,
VAR_VIRT_ALIAS_MAPS, DEF_VIRT_ALIAS_MAPS, &var_virt_alias_maps, 0, 0,
VAR_VIRT_MAILBOX_MAPS, DEF_VIRT_MAILBOX_MAPS, &var_virt_mailbox_maps, 0, 0,
VAR_ALIAS_MAPS, DEF_ALIAS_MAPS, &var_alias_maps, 0, 0,
VAR_LOCAL_RCPT_MAPS, DEF_LOCAL_RCPT_MAPS, &var_local_rcpt_maps, 0, 0,
VAR_SMTPD_SASL_OPTS, DEF_SMTPD_SASL_OPTS, &var_smtpd_sasl_opts, 0, 0,
VAR_SMTPD_SASL_PATH, DEF_SMTPD_SASL_PATH, &var_smtpd_sasl_path, 1, 0,
VAR_CYRUS_CONF_PATH, DEF_CYRUS_CONF_PATH, &var_cyrus_conf_path, 0, 0,
VAR_SMTPD_SASL_REALM, DEF_SMTPD_SASL_REALM, &var_smtpd_sasl_realm, 0, 0,
VAR_SMTPD_SASL_EXCEPTIONS_NETWORKS, DEF_SMTPD_SASL_EXCEPTIONS_NETWORKS, &var_smtpd_sasl_exceptions_networks, 0, 0,
VAR_FILTER_XPORT, DEF_FILTER_XPORT, &var_filter_xport, 0, 0,
VAR_PERM_MX_NETWORKS, DEF_PERM_MX_NETWORKS, &var_perm_mx_networks, 0, 0,
VAR_SMTPD_SND_AUTH_MAPS, DEF_SMTPD_SND_AUTH_MAPS, &var_smtpd_snd_auth_maps, 0, 0,
VAR_SMTPD_NOOP_CMDS, DEF_SMTPD_NOOP_CMDS, &var_smtpd_noop_cmds, 0, 0,
VAR_SMTPD_FORBID_CMDS, DEF_SMTPD_FORBID_CMDS, &var_smtpd_forbid_cmds, 0, 0,
VAR_SMTPD_NULL_KEY, DEF_SMTPD_NULL_KEY, &var_smtpd_null_key, 0, 0,
VAR_RELAY_RCPT_MAPS, DEF_RELAY_RCPT_MAPS, &var_relay_rcpt_maps, 0, 0,
VAR_VERIFY_SENDER, DEF_VERIFY_SENDER, &var_verify_sender, 0, 0,
VAR_VERP_CLIENTS, DEF_VERP_CLIENTS, &var_verp_clients, 0, 0,
VAR_SMTPD_PROXY_FILT, DEF_SMTPD_PROXY_FILT, &var_smtpd_proxy_filt, 0, 0,
VAR_SMTPD_PROXY_EHLO, DEF_SMTPD_PROXY_EHLO, &var_smtpd_proxy_ehlo, 0, 0,
VAR_SMTPD_PROXY_OPTS, DEF_SMTPD_PROXY_OPTS, &var_smtpd_proxy_opts, 0, 0,
VAR_INPUT_TRANSP, DEF_INPUT_TRANSP, &var_input_transp, 0, 0,
VAR_XCLIENT_HOSTS, DEF_XCLIENT_HOSTS, &var_xclient_hosts, 0, 0,
VAR_XFORWARD_HOSTS, DEF_XFORWARD_HOSTS, &var_xforward_hosts, 0, 0,
VAR_SMTPD_HOGGERS, DEF_SMTPD_HOGGERS, &var_smtpd_hoggers, 0, 0,
VAR_LOC_RWR_CLIENTS, DEF_LOC_RWR_CLIENTS, &var_local_rwr_clients, 0, 0,
VAR_SMTPD_EHLO_DIS_WORDS, DEF_SMTPD_EHLO_DIS_WORDS, &var_smtpd_ehlo_dis_words, 0, 0,
VAR_SMTPD_EHLO_DIS_MAPS, DEF_SMTPD_EHLO_DIS_MAPS, &var_smtpd_ehlo_dis_maps, 0, 0,
#ifdef USE_TLS
VAR_RELAY_CCERTS, DEF_RELAY_CCERTS, &var_smtpd_relay_ccerts, 0, 0,
VAR_SMTPD_SASL_TLS_OPTS, DEF_SMTPD_SASL_TLS_OPTS, &var_smtpd_sasl_tls_opts, 0, 0,
VAR_SMTPD_TLS_CERT_FILE, DEF_SMTPD_TLS_CERT_FILE, &var_smtpd_tls_cert_file, 0, 0,
VAR_SMTPD_TLS_KEY_FILE, DEF_SMTPD_TLS_KEY_FILE, &var_smtpd_tls_key_file, 0, 0,
VAR_SMTPD_TLS_DCERT_FILE, DEF_SMTPD_TLS_DCERT_FILE, &var_smtpd_tls_dcert_file, 0, 0,
VAR_SMTPD_TLS_DKEY_FILE, DEF_SMTPD_TLS_DKEY_FILE, &var_smtpd_tls_dkey_file, 0, 0,
VAR_SMTPD_TLS_ECCERT_FILE, DEF_SMTPD_TLS_ECCERT_FILE, &var_smtpd_tls_eccert_file, 0, 0,
VAR_SMTPD_TLS_ECKEY_FILE, DEF_SMTPD_TLS_ECKEY_FILE, &var_smtpd_tls_eckey_file, 0, 0,
VAR_SMTPD_TLS_CA_FILE, DEF_SMTPD_TLS_CA_FILE, &var_smtpd_tls_CAfile, 0, 0,
VAR_SMTPD_TLS_CA_PATH, DEF_SMTPD_TLS_CA_PATH, &var_smtpd_tls_CApath, 0, 0,
VAR_SMTPD_TLS_CIPH, DEF_SMTPD_TLS_CIPH, &var_smtpd_tls_ciph, 1, 0,
VAR_SMTPD_TLS_MAND_CIPH, DEF_SMTPD_TLS_MAND_CIPH, &var_smtpd_tls_mand_ciph, 1, 0,
VAR_SMTPD_TLS_EXCL_CIPH, DEF_SMTPD_TLS_EXCL_CIPH, &var_smtpd_tls_excl_ciph, 0, 0,
VAR_SMTPD_TLS_MAND_EXCL, DEF_SMTPD_TLS_MAND_EXCL, &var_smtpd_tls_mand_excl, 0, 0,
VAR_SMTPD_TLS_PROTO, DEF_SMTPD_TLS_PROTO, &var_smtpd_tls_proto, 0, 0,
VAR_SMTPD_TLS_MAND_PROTO, DEF_SMTPD_TLS_MAND_PROTO, &var_smtpd_tls_mand_proto, 0, 0,
VAR_SMTPD_TLS_512_FILE, DEF_SMTPD_TLS_512_FILE, &var_smtpd_tls_dh512_param_file, 0, 0,
VAR_SMTPD_TLS_1024_FILE, DEF_SMTPD_TLS_1024_FILE, &var_smtpd_tls_dh1024_param_file, 0, 0,
VAR_SMTPD_TLS_EECDH, DEF_SMTPD_TLS_EECDH, &var_smtpd_tls_eecdh, 1, 0,
VAR_SMTPD_TLS_FPT_DGST, DEF_SMTPD_TLS_FPT_DGST, &var_smtpd_tls_fpt_dgst, 1, 0,
VAR_SMTPD_TLS_LOGLEVEL, DEF_SMTPD_TLS_LOGLEVEL, &var_smtpd_tls_loglevel, 0, 0,
#endif
VAR_SMTPD_TLS_LEVEL, DEF_SMTPD_TLS_LEVEL, &var_smtpd_tls_level, 0, 0,
VAR_SMTPD_SASL_TYPE, DEF_SMTPD_SASL_TYPE, &var_smtpd_sasl_type, 1, 0,
VAR_SMTPD_MILTERS, DEF_SMTPD_MILTERS, &var_smtpd_milters, 0, 0,
VAR_MILT_CONN_MACROS, DEF_MILT_CONN_MACROS, &var_milt_conn_macros, 0, 0,
VAR_MILT_HELO_MACROS, DEF_MILT_HELO_MACROS, &var_milt_helo_macros, 0, 0,
VAR_MILT_MAIL_MACROS, DEF_MILT_MAIL_MACROS, &var_milt_mail_macros, 0, 0,
VAR_MILT_RCPT_MACROS, DEF_MILT_RCPT_MACROS, &var_milt_rcpt_macros, 0, 0,
VAR_MILT_DATA_MACROS, DEF_MILT_DATA_MACROS, &var_milt_data_macros, 0, 0,
VAR_MILT_EOH_MACROS, DEF_MILT_EOH_MACROS, &var_milt_eoh_macros, 0, 0,
VAR_MILT_EOD_MACROS, DEF_MILT_EOD_MACROS, &var_milt_eod_macros, 0, 0,
VAR_MILT_UNK_MACROS, DEF_MILT_UNK_MACROS, &var_milt_unk_macros, 0, 0,
VAR_MILT_PROTOCOL, DEF_MILT_PROTOCOL, &var_milt_protocol, 1, 0,
VAR_MILT_DEF_ACTION, DEF_MILT_DEF_ACTION, &var_milt_def_action, 1, 0,
VAR_MILT_DAEMON_NAME, DEF_MILT_DAEMON_NAME, &var_milt_daemon_name, 1, 0,
VAR_MILT_V, DEF_MILT_V, &var_milt_v, 1, 0,
VAR_STRESS, DEF_STRESS, &var_stress, 0, 0,
VAR_UNV_FROM_WHY, DEF_UNV_FROM_WHY, &var_unv_from_why, 0, 0,
VAR_UNV_RCPT_WHY, DEF_UNV_RCPT_WHY, &var_unv_rcpt_why, 0, 0,
VAR_REJECT_TMPF_ACT, DEF_REJECT_TMPF_ACT, &var_reject_tmpf_act, 1, 0,
VAR_UNK_NAME_TF_ACT, DEF_UNK_NAME_TF_ACT, &var_unk_name_tf_act, 1, 0,
VAR_UNK_ADDR_TF_ACT, DEF_UNK_ADDR_TF_ACT, &var_unk_addr_tf_act, 1, 0,
VAR_UNV_RCPT_TF_ACT, DEF_UNV_RCPT_TF_ACT, &var_unv_rcpt_tf_act, 1, 0,
VAR_UNV_FROM_TF_ACT, DEF_UNV_FROM_TF_ACT, &var_unv_from_tf_act, 1, 0,
VAR_SMTPD_CMD_FILTER, DEF_SMTPD_CMD_FILTER, &var_smtpd_cmd_filter, 0, 0,
#ifdef USE_TLSPROXY
VAR_TLSPROXY_SERVICE, DEF_TLSPROXY_SERVICE, &var_tlsproxy_service, 1, 0,
#endif
#ifdef __APPLE_OS_X_SERVER__
VAR_SMTPD_PW_SERVER_OPTS, DEF_SMTPD_PW_SERVER_OPTS, &var_smtpd_pw_server_opts, 0, 0,
#if defined(USE_SASL_AUTH) && defined(USE_TLS)
VAR_IMAP_SUBMIT_CRED_FILE, DEF_IMAP_SUBMIT_CRED_FILE, &var_imap_submit_cred_file, 0, 0,
#endif
#endif
0,
};
static const CONFIG_RAW_TABLE raw_table[] = {
VAR_SMTPD_EXP_FILTER, DEF_SMTPD_EXP_FILTER, &var_smtpd_exp_filter, 1, 0,
VAR_DEF_RBL_REPLY, DEF_DEF_RBL_REPLY, &var_def_rbl_reply, 1, 0,
VAR_SMTPD_REJ_FOOTER, DEF_SMTPD_REJ_FOOTER, &var_smtpd_rej_footer, 0, 0,
0,
};
MAIL_VERSION_STAMP_ALLOCATE;
single_server_main(argc, argv, smtpd_service,
MAIL_SERVER_NINT_TABLE, nint_table,
MAIL_SERVER_INT_TABLE, int_table,
MAIL_SERVER_STR_TABLE, str_table,
MAIL_SERVER_RAW_TABLE, raw_table,
MAIL_SERVER_BOOL_TABLE, bool_table,
MAIL_SERVER_NBOOL_TABLE, nbool_table,
MAIL_SERVER_TIME_TABLE, time_table,
MAIL_SERVER_PRE_INIT, pre_jail_init,
MAIL_SERVER_PRE_ACCEPT, pre_accept,
MAIL_SERVER_POST_INIT, post_jail_init,
0);
}