* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) The Exim Maintainers 2020 - 2023 */
+/* Copyright (c) The Exim Maintainers 2020 - 2024 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
/* SPDX-License-Identifier: GPL-2.0-or-later */
HELO_CMD, EHLO_CMD, DATA_CMD, /* These are listed in the pipelining */
VRFY_CMD, EXPN_CMD, NOOP_CMD, /* RFC as requiring synchronization */
- ETRN_CMD, /* This by analogy with TURN from the RFC */
+ ATRN_CMD, ETRN_CMD, /* This by analogy with TURN from the RFC */
STARTTLS_CMD, /* Required by the STARTTLS RFC */
TLS_AUTH_CMD, /* auto-command at start of SSL */
#ifdef EXPERIMENTAL_XCLIENT
/* These commands need not be synchronized when pipelining */
MAIL_CMD, RCPT_CMD, RSET_CMD,
+#ifndef DISABLE_WELLKNOWN
+ WELLKNOWN_CMD,
+#endif
/* This is a dummy to identify the non-sync commands when not pipelining */
/* These are specials that don't correspond to actual commands */
EOF_CMD, OTHER_CMD, BADARG_CMD, BADCHAR_CMD, BADSYN_CMD,
- TOO_MANY_NONMAIL_CMD };
+ TOO_MANY_NONMAIL_CMD
+};
/* This is a convenience macro for adding the identity of an SMTP command
{ "bdat", sizeof("bdat")-1, BDAT_CMD, TRUE, TRUE },
{ "quit", sizeof("quit")-1, QUIT_CMD, FALSE, TRUE },
{ "noop", sizeof("noop")-1, NOOP_CMD, TRUE, FALSE },
+ { "atrn", sizeof("atrn")-1, ATRN_CMD, TRUE, FALSE },
{ "etrn", sizeof("etrn")-1, ETRN_CMD, TRUE, FALSE },
{ "vrfy", sizeof("vrfy")-1, VRFY_CMD, TRUE, FALSE },
{ "expn", sizeof("expn")-1, EXPN_CMD, TRUE, FALSE },
- { "help", sizeof("help")-1, HELP_CMD, TRUE, FALSE }
+ { "help", sizeof("help")-1, HELP_CMD, TRUE, FALSE },
+#ifndef DISABLE_WELLKNOWN
+ { "wellknown", sizeof("wellknown")-1, WELLKNOWN_CMD, TRUE, FALSE },
+#endif
};
/* This list of names is used for performing the smtp_no_mail logging action. */
[SCH_RSET] = US"RSET",
[SCH_STARTTLS] = US"STARTTLS",
[SCH_VRFY] = US"VRFY",
+#ifndef DISABLE_WELLKNOWN
+ [SCH_WELLKNOWN] = US"WELLKNOWN",
+#endif
#ifdef EXPERIMENTAL_XCLIENT
[SCH_XCLIENT] = US"XCLIENT",
#endif
/* forward declarations */
static int smtp_read_command(BOOL check_sync, unsigned buffer_lim);
-static int synprot_error(int type, int code, uschar *data, uschar *errmess);
static void smtp_quit_handler(uschar **, uschar **);
static void smtp_rset_handler(void);
+#ifndef DISABLE_DKIM
+/* Feed received message data to the dkim module */
+/*XXX maybe a global dkim_info? */
+void
+smtp_verify_feed(const uschar * s, unsigned n)
+{
+static misc_module_info * dkim_mi = NULL;
+typedef void (*fn_t)(const uschar *, int);
+
+if (!dkim_mi && !(dkim_mi = misc_mod_findonly(US"dkim")))
+ return;
+
+(((fn_t *) dkim_mi->functions)[DKIM_VERIFY_FEED]) (s, n);
+}
+#endif
+
+
/* Refill the buffer, and notify DKIM verification code.
Return false for error or EOF.
*/
return FALSE;
}
#ifndef DISABLE_DKIM
-dkim_exim_verify_feed(smtp_inbuffer, rc);
+smtp_verify_feed(smtp_inbuffer, rc);
#endif
smtp_inend = smtp_inbuffer + rc;
smtp_inptr = smtp_inbuffer;
if (n > lim)
n = lim;
if (n > 0)
- dkim_exim_verify_feed(smtp_inptr, n);
+ smtp_verify_feed(smtp_inptr, n);
#endif
}
uschar * user_msg = NULL;
uschar * log_msg;
-for(;;)
- {
#ifndef DISABLE_DKIM
- unsigned dkim_save;
+misc_module_info * dkim_info = misc_mod_findonly(US"dkim");
+typedef void (*dkim_pause_t)(BOOL);
+dkim_pause_t dkim_pause;
+
+dkim_pause = dkim_info
+ ? ((dkim_pause_t *) dkim_info->functions)[DKIM_VERIFY_PAUSE] : NULL;
#endif
+for(;;)
+ {
+
if (chunking_data_left > 0)
return lwr_receive_getc(chunking_data_left--);
bdat_pop_receive_functions();
#ifndef DISABLE_DKIM
- dkim_save = dkim_collect_input;
- dkim_collect_input = 0;
+ if (dkim_pause) dkim_pause(TRUE);
#endif
/* Unless PIPELINING was offered, there should be no next command
if (chunking_state == CHUNKING_LAST)
{
#ifndef DISABLE_DKIM
- dkim_collect_input = dkim_save;
- dkim_exim_verify_feed(NULL, 0); /* notify EOD */
- dkim_collect_input = 0;
+ smtp_verify_feed(NULL, 0); /* notify EOD */
#endif
return EOD;
}
bdat_push_receive_functions();
#ifndef DISABLE_DKIM
- dkim_collect_input = dkim_save;
+ if (dkim_pause) dkim_pause(FALSE);
#endif
break; /* to top of main loop */
}
follow the sender address. */
smtp_cmd_argument = smtp_cmd_buffer + p->len;
- while (isspace(*smtp_cmd_argument)) smtp_cmd_argument++;
+ Uskip_whitespace(&smtp_cmd_argument);
Ustrcpy(smtp_data_buffer, smtp_cmd_argument);
smtp_cmd_data = smtp_data_buffer;
gstring * g = string_catn(NULL, US"SMTP connection", 15);
if (LOGGING(connection_id))
- g = string_fmt_append(g, " Ci=%lu", connection_id);
+ g = string_fmt_append(g, " Ci=%s", connection_id);
g = string_catn(g, US" from ", 6);
if (host_checking)
g = string_cat(g, hostname);
else if (f.sender_host_unknown || f.sender_host_notsocket)
- g = string_cat(g, sender_ident);
+ g = string_cat(g, sender_ident ? sender_ident : US"NULL");
+
+else if (atrn_mode)
+ g = string_append(g, 2, hostname, US" (ODMR customer)");
else if (f.is_inetd)
g = string_append(g, 2, hostname, US" (via inetd)");
bmi_verdicts = NULL;
#endif
dnslist_domain = dnslist_matched = NULL;
-#ifdef SUPPORT_SPF
-spf_header_comment = spf_received = spf_result = spf_smtp_comment = NULL;
-spf_result_guessed = FALSE;
-#endif
-#ifndef DISABLE_DKIM
-dkim_cur_signer = dkim_signers =
-dkim_signing_domain = dkim_signing_selector = dkim_signatures = NULL;
-dkim_cur_signer = dkim_signers = dkim_signing_domain = dkim_signing_selector = NULL;
-f.dkim_disable_verify = FALSE;
-dkim_collect_input = 0;
-dkim_verify_overall = dkim_verify_status = dkim_verify_reason = NULL;
-dkim_key_length = 0;
-#endif
-#ifdef SUPPORT_DMARC
-f.dmarc_has_been_checked = f.dmarc_disable_verify = f.dmarc_enable_forensic = FALSE;
-dmarc_domain_policy = dmarc_status = dmarc_status_text =
-dmarc_used_domain = NULL;
-#endif
-#ifdef EXPERIMENTAL_ARC
-arc_state = arc_state_reason = NULL;
-arc_received_instance = 0;
-#endif
+
dsn_ret = 0;
dsn_envid = NULL;
deliver_host = deliver_host_address = NULL; /* Can be set by ACL */
store_free(this);
}
+misc_mod_smtp_reset();
message_tidyup();
store_reset(reset_point);
/* Check maximum number allowed */
- if (recipients_max > 0 && recipients_count + 1 > recipients_max)
+ if ( recipients_max_expanded > 0
+ && recipients_count + 1 > recipients_max_expanded)
/* The function moan_smtp_batch() does not return. */
moan_smtp_batch(smtp_cmd_buffer, "%s too many recipients",
- recipients_max_reject? "552": "452");
+ recipients_max_reject ? "552": "452");
/* Apply SMTP rewrite, then extract address. Don't allow "<>" as a
recipient address */
break;
- /* The VRFY, EXPN, HELP, ETRN, and NOOP commands are ignored. */
+ /* The VRFY, EXPN, HELP, ETRN, ATRN and NOOP commands are ignored. */
- case VRFY_CMD:
- case EXPN_CMD:
- case HELP_CMD:
- case NOOP_CMD:
- case ETRN_CMD:
+ case VRFY_CMD: case EXPN_CMD: case HELP_CMD: case NOOP_CMD:
+ case ETRN_CMD: case ATRN_CMD:
+#ifndef DISABLE_WELLKNOWN
+ case WELLKNOWN_CMD:
+#endif
bsmtp_transaction_linecount = receive_linecount;
break;
{
if (log_reject_target)
{
+#ifdef DISABLE_TLS
+ uschar * tls = NULL;
+#else
gstring * g = s_tlslog(NULL);
uschar * tls = string_from_gstring(g);
-
+#endif
log_write(L_connection_reject,
log_reject_target, "%s%s%s dropped by %s%s%s",
LOGGING(dnssec) && sender_host_dnssec ? US" DS" : US"",
}
+
+
+/* IP options: log and reject the connection.
+
+Deal with any IP options that are set. On the systems I have looked at,
+the value of MAX_IPOPTLEN has been 40, meaning that there should never be
+more logging data than will fit in big_buffer. Nevertheless, after somebody
+questioned this code, I've added in some paranoid checking.
+
+Given the disuse of options on the internet as of 2024 I'm tempted to
+drop the detailed parsing and logging. */
+
+#ifdef GLIBC_IP_OPTIONS
+# if (!defined __GLIBC__) || (__GLIBC__ < 2)
+# define OPTSTYLE 1
+# else
+# define OPTSTYLE 2
+# endif
+#elif defined DARWIN_IP_OPTIONS
+# define OPTSTYLE 2
+#else
+# define OPTSTYLE 3
+# endif
+
+#if OPTSTYLE == 1
+# define EXIM_IP_OPT_T struct ip_options
+# define OPTSTART (ipopt->__data)
+#elif OPTSTYLE == 2
+# define EXIM_IP_OPT_T struct ip_opts
+# define OPTSTART (ipopt->ip_opts);
+#else
+# define EXIM_IP_OPT_T struct ipoption
+# define OPTSTART (ipopt->ipopt_list);
+#endif
+
+static BOOL
+smtp_in_reject_options(EXIM_IP_OPT_T * ipopt, EXIM_SOCKLEN_T optlen)
+{
+uschar * p, * pend = big_buffer + big_buffer_size;
+uschar * adptr;
+int optcount, sprint_len;
+struct in_addr addr;
+uschar * optstart = US OPTSTART;
+
+DEBUG(D_receive) debug_printf("IP options exist\n");
+
+p = Ustpcpy(big_buffer, "IP options on incoming call:");
+
+for (uschar * opt = optstart; opt && opt < US (ipopt) + optlen; )
+ switch (*opt)
+ {
+ case IPOPT_EOL:
+ opt = NULL;
+ break;
+
+ case IPOPT_NOP:
+ opt++;
+ break;
+
+ case IPOPT_SSRR:
+ case IPOPT_LSRR:
+ if (!
+# if OPTSTYLE == 1
+ string_format(p, pend-p, " %s [@%s%n",
+ *opt == IPOPT_SSRR ? "SSRR" : "LSRR",
+ inet_ntoa(*(struct in_addr *)&ipopt->faddr),
+ &sprint_len)
+# elif OPTSTYLE == 2
+ string_format(p, pend-p, " %s [@%s%n",
+ *opt == IPOPT_SSRR ? "SSRR" : "LSRR",
+ inet_ntoa(ipopt->ip_dst),
+ &sprint_len)
+# else
+ string_format(p, pend-p, " %s [@%s%n",
+ *opt == IPOPT_SSRR ? "SSRR" : "LSRR",
+ inet_ntoa(ipopt->ipopt_dst),
+ &sprint_len)
+# endif
+ )
+ opt = NULL;
+ else
+ {
+ p += sprint_len;
+ optcount = (opt[1] - 3) / sizeof(struct in_addr);
+ adptr = opt + 3;
+ while (optcount-- > 0)
+ {
+ memcpy(&addr, adptr, sizeof(addr));
+ if (!string_format(p, pend - p - 1, "%s%s%n",
+ optcount == 0 ? ":" : "@", inet_ntoa(addr), &sprint_len))
+ { opt = NULL; goto bad_srr; }
+ p += sprint_len;
+ adptr += sizeof(struct in_addr);
+ }
+ *p++ = ']';
+ opt += opt[1];
+ }
+bad_srr: break;
+
+ default:
+ if (pend - p < 4 + 3*opt[1])
+ opt = NULL;
+ else
+ {
+ p = Ustpcpy(p, "[ ");
+ for (int i = 0; i < opt[1]; i++)
+ p += sprintf(CS p, "%2.2x ", opt[i]);
+ *p++ = ']';
+ opt += opt[1];
+ }
+ break;
+ }
+
+*p = '\0';
+log_write(0, LOG_MAIN, "%s", big_buffer);
+
+/* Refuse any call with IP options. This is what tcpwrappers 7.5 does. */
+
+log_write(0, LOG_MAIN|LOG_REJECT,
+ "connection from %s refused (IP options)", host_and_ident(FALSE));
+
+smtp_printf("554 SMTP service not available\r\n", SP_NO_MORE);
+return FALSE;
+}
+
+
/*************************************************
* Start an SMTP session *
*************************************************/
authenticated_by = NULL;
#ifndef DISABLE_TLS
-tls_in.ver = tls_in.cipher = tls_in.peerdn = NULL;
-tls_in.ourcert = tls_in.peercert = NULL;
-tls_in.sni = NULL;
-tls_in.ocsp = OCSP_NOT_REQ;
+if (!atrn_mode)
+ {
+ tls_in.ver = tls_in.cipher = tls_in.peerdn = NULL;
+ tls_in.ourcert = tls_in.peercert = NULL;
+ tls_in.sni = NULL;
+ tls_in.ocsp = OCSP_NOT_REQ;
+ }
fl.tls_advertised = FALSE;
#endif
fl.dsn_advertised = FALSE;
smtp_buf_init();
-receive_getc = smtp_getc;
-receive_getbuf = smtp_getbuf;
-receive_get_cache = smtp_get_cache;
-receive_hasc = smtp_hasc;
-receive_ungetc = smtp_ungetc;
-receive_feof = smtp_feof;
-receive_ferror = smtp_ferror;
+#ifndef DISABLE_TLS
+if (atrn_mode && tls_in.active.sock >= 0)
+ {
+ receive_getc = tls_getc;
+ receive_getbuf = tls_getbuf;
+ receive_get_cache = tls_get_cache;
+ receive_hasc = tls_hasc;
+ receive_ungetc = tls_ungetc;
+ receive_feof = tls_feof;
+ receive_ferror = tls_ferror;
+ }
+else
+#endif
+ {
+ receive_getc = smtp_getc;
+ receive_getbuf = smtp_getbuf;
+ receive_get_cache = smtp_get_cache;
+ receive_hasc = smtp_hasc;
+ receive_ungetc = smtp_ungetc;
+ receive_feof = smtp_feof;
+ receive_ferror = smtp_ferror;
+ }
lwr_receive_getc = NULL;
lwr_receive_getbuf = NULL;
lwr_receive_hasc = NULL;
/* Set up the message size limit; this may be host-specific */
+GET_OPTION("message_size_limit");
thismessage_size_limit = expand_string_integer(message_size_limit, TRUE);
if (expand_string_message)
{
#if !HAVE_IPV6 && !defined(NO_IP_OPTIONS)
-# ifdef GLIBC_IP_OPTIONS
-# if (!defined __GLIBC__) || (__GLIBC__ < 2)
-# define OPTSTYLE 1
-# else
-# define OPTSTYLE 2
-# endif
-# elif defined DARWIN_IP_OPTIONS
-# define OPTSTYLE 2
-# else
-# define OPTSTYLE 3
-# endif
-
if (!host_checking && !f.sender_host_notsocket)
{
# if OPTSTYLE == 1
- EXIM_SOCKLEN_T optlen = sizeof(struct ip_options) + MAX_IPOPTLEN;
- struct ip_options *ipopt = store_get(optlen, GET_UNTAINTED);
-# elif OPTSTYLE == 2
- struct ip_opts ipoptblock;
- struct ip_opts *ipopt = &ipoptblock;
- EXIM_SOCKLEN_T optlen = sizeof(ipoptblock);
+ EXIM_SOCKLEN_T optlen = sizeof(EXIM_IP_OPT_T) + MAX_IPOPTLEN;
+ EXIM_IP_OPT_T * ipopt = store_get(optlen, GET_UNTAINTED);
# else
- struct ipoption ipoptblock;
- struct ipoption *ipopt = &ipoptblock;
- EXIM_SOCKLEN_T optlen = sizeof(ipoptblock);
+ EXIM_IP_OPT_T ipoptblock;
+ EXIM_IP_OPT_T * ipopt = &ipoptblock;
+ EXIM_SOCKLEN_T optlen = sizeof(EXIM_IP_OPT_T);
# endif
/* Occasional genuine failures of getsockopt() have been seen - for
DEBUG(D_receive) debug_printf("checking for IP options\n");
- if (getsockopt(fileno(smtp_out), IPPROTO_IP, IP_OPTIONS, US (ipopt),
+ if (getsockopt(fileno(smtp_out), IPPROTO_IP, IP_OPTIONS, US ipopt,
&optlen) < 0)
{
if (errno != ENOPROTOOPT)
questioned this code, I've added in some paranoid checking. */
else if (optlen > 0)
- {
- uschar * p = big_buffer;
- uschar * pend = big_buffer + big_buffer_size;
- uschar * adptr;
- int optcount;
- struct in_addr addr;
-
-# if OPTSTYLE == 1
- uschar * optstart = US (ipopt->__data);
-# elif OPTSTYLE == 2
- uschar * optstart = US (ipopt->ip_opts);
-# else
- uschar * optstart = US (ipopt->ipopt_list);
-# endif
-
- DEBUG(D_receive) debug_printf("IP options exist\n");
-
- Ustrcpy(p, "IP options on incoming call:");
- p += Ustrlen(p);
-
- for (uschar * opt = optstart; opt && opt < US (ipopt) + optlen; )
- switch (*opt)
- {
- case IPOPT_EOL:
- opt = NULL;
- break;
-
- case IPOPT_NOP:
- opt++;
- break;
-
- case IPOPT_SSRR:
- case IPOPT_LSRR:
- if (!
-# if OPTSTYLE == 1
- string_format(p, pend-p, " %s [@%s",
- (*opt == IPOPT_SSRR)? "SSRR" : "LSRR",
- inet_ntoa(*((struct in_addr *)(&(ipopt->faddr)))))
-# elif OPTSTYLE == 2
- string_format(p, pend-p, " %s [@%s",
- (*opt == IPOPT_SSRR)? "SSRR" : "LSRR",
- inet_ntoa(ipopt->ip_dst))
-# else
- string_format(p, pend-p, " %s [@%s",
- (*opt == IPOPT_SSRR)? "SSRR" : "LSRR",
- inet_ntoa(ipopt->ipopt_dst))
-# endif
- )
- {
- opt = NULL;
- break;
- }
-
- p += Ustrlen(p);
- optcount = (opt[1] - 3) / sizeof(struct in_addr);
- adptr = opt + 3;
- while (optcount-- > 0)
- {
- memcpy(&addr, adptr, sizeof(addr));
- if (!string_format(p, pend - p - 1, "%s%s",
- (optcount == 0)? ":" : "@", inet_ntoa(addr)))
- {
- opt = NULL;
- break;
- }
- p += Ustrlen(p);
- adptr += sizeof(struct in_addr);
- }
- *p++ = ']';
- opt += opt[1];
- break;
-
- default:
- {
- if (pend - p < 4 + 3*opt[1]) { opt = NULL; break; }
- Ustrcat(p, "[ ");
- p += 2;
- for (int i = 0; i < opt[1]; i++)
- p += sprintf(CS p, "%2.2x ", opt[i]);
- *p++ = ']';
- }
- opt += opt[1];
- break;
- }
-
- *p = 0;
- log_write(0, LOG_MAIN, "%s", big_buffer);
-
- /* Refuse any call with IP options. This is what tcpwrappers 7.5 does. */
-
- log_write(0, LOG_MAIN|LOG_REJECT,
- "connection from %s refused (IP options)", host_and_ident(FALSE));
-
- smtp_printf("554 SMTP service not available\r\n", SP_NO_MORE);
- return FALSE;
- }
+ return smtp_in_reject_options(ipopt);
/* Length of options = 0 => there are no options */
fl.helo_accept_junk = verify_check_host(&helo_accept_junk_hosts) == OK;
}
+/* Expand recipients_max, if needed */
+ {
+ uschar * rme = expand_string(recipients_max);
+ recipients_max_expanded = atoi(CCS rme);
+ }
/* For batch SMTP input we are now done. */
if (smtp_batched_input) return TRUE;
-#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) || defined(EXPERIMETAL_XCLIENT)
+#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) || defined(EXPERIMENTAL_XCLIENT)
proxy_session = FALSE;
#endif
/* Run the connect ACL if it exists */
user_msg = NULL;
+GET_OPTION("acl_smtp_connect");
if (acl_smtp_connect)
{
int rc;
esclen = codelen - 4;
}
}
-else if (!(s = expand_string(smtp_banner)))
+else
{
- log_write(0, f.expand_string_forcedfail ? LOG_MAIN : LOG_MAIN|LOG_PANIC_DIE,
- "Expansion of \"%s\" (smtp_banner) failed: %s",
- smtp_banner, expand_string_message);
- /* for force-fail */
-#ifndef DISABLE_TLS
- if (tls_in.on_connect) tls_close(NULL, TLS_SHUTDOWN_WAIT);
-#endif
- return FALSE;
+ GET_OPTION("smtp_banner");
+ if (!(s = expand_string(smtp_banner)))
+ {
+ log_write(0, f.expand_string_forcedfail ? LOG_MAIN : LOG_MAIN|LOG_PANIC_DIE,
+ "Expansion of \"%s\" (smtp_banner) failed: %s",
+ smtp_banner, expand_string_message);
+ /* for force-fail */
+ #ifndef DISABLE_TLS
+ if (tls_in.on_connect) tls_close(NULL, TLS_SHUTDOWN_WAIT);
+ #endif
+ return FALSE;
+ }
}
/* Remove any terminating newlines; might as well remove trailing space too */
handshake arrived. If so we must have managed a TFO. */
#ifdef TCP_FASTOPEN
-if (sender_host_address && !f.sender_host_notsocket) tfo_in_check();
+if (sender_host_address && !f.sender_host_notsocket && !atrn_mode)
+ tfo_in_check();
#endif
return TRUE;
These values fit in with the values of the "done" variable in the main
processing loop in smtp_setup_msg(). */
-static int
+int
synprot_error(int type, int code, uschar *data, uschar *errmess)
{
int yield = -1;
+#ifndef DISABLE_EVENT
+event_raise(event_action,
+ L_smtp_syntax_error ? US"smtp:fail:syntax" : US"smtp:fail:protocol",
+ errmess, NULL);
+#endif
+
log_write(type, LOG_MAIN, "SMTP %s error in \"%s\" %s %s",
type == L_smtp_syntax_error ? "syntax" : "protocol",
string_printing(smtp_cmd_buffer), host_and_ident(TRUE), errmess);
We only handle pipelining these responses as far as nonfinal/final groups,
not the whole MAIL/RCPT/DATA response set. */
-for (;;)
- {
- uschar *nl = Ustrchr(msg, '\n');
- if (!nl)
+for (uschar * nl;;)
+ if (!(nl = Ustrchr(msg, '\n')))
{
smtp_printf("%.3s%c%.*s%s\r\n", !final, code, final ? ' ':'-', esclen, esc, msg);
return;
msg = nl + 1;
Uskip_whitespace(&msg);
}
- }
}
smtp_message_code(&smtp_code, &codelen, &user_msg, &log_msg,
where != ACL_WHERE_VRFY);
-/* We used to have sender_address here; however, there was a bug that was not
+/* Get info for logging.
+We used to have sender_address here; however, there was a bug that was not
updating sender_address after a rewrite during a verify. When this bug was
fixed, sender_address at this point became the rewritten address. I'm not sure
this is what should be logged, so I've changed to logging the unrewritten
if (where == ACL_WHERE_AUTH) /* avoid logging auth creds */
{
- uschar * s;
- for (s = smtp_cmd_data; *s && !isspace(*s); ) s++;
- lim = s - smtp_cmd_data; /* atop after method */
+ uschar * s = smtp_cmd_data;
+ Uskip_nonwhite(&s);
+ lim = s - smtp_cmd_data; /* stop after method */
}
what = string_sprintf("%s %.*s", acl_wherenames[where], lim, place);
}
/* Call the not-QUIT ACL, if there is one, unless no reason is given. */
+GET_OPTION("acl_smtp_notquit");
if (acl_smtp_notquit && reason)
{
smtp_notquit_reason = reason;
*/
static void
-smtp_user_msg(uschar *code, uschar *user_msg)
+smtp_user_msg(uschar * code, uschar * user_msg)
{
int len = 3;
smtp_message_code(&code, &len, &user_msg, NULL, TRUE);
/* Set up globals for error messages */
-authenticator_name = au->name;
-driver_srcfile = au->srcfile;
-driver_srcline = au->srcline;
+authenticator_name = au->drinst.name;
+driver_srcfile = au->drinst.srcfile;
+driver_srcline = au->drinst.srcline;
/* Run the checking code, passing the remainder of the command line as
data. Initials the $auth<n> variables as empty. Initialize $0 empty and set
expand_nmax = 0;
expand_nlength[0] = 0; /* $0 contains nothing */
-rc = (au->info->servercode)(au, smtp_cmd_data);
+ {
+ auth_info * ai = au->drinst.info;
+ rc = (ai->servercode)(au, smtp_cmd_data);
+ }
if (au->set_id) set_id = expand_string(au->set_id);
expand_nmax = -1; /* Reset numeric variables */
for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; /* Reset $auth<n> */
if (!au->set_id || set_id) /* Complete success */
{
if (set_id) authenticated_id = string_copy_perm(set_id, TRUE);
- sender_host_authenticated = au->name;
+ sender_host_authenticated = au->drinst.name;
sender_host_auth_pubname = au->public_name;
authentication_failed = FALSE;
authenticated_fail_id = NULL; /* Impossible to already be set? */
HAD(SCH_QUIT);
f.smtp_in_quit = TRUE;
incomplete_transaction_log(US"QUIT");
+GET_OPTION("acl_smtp_quit");
if ( acl_smtp_quit
&& acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, user_msgp, log_msgp)
== ERROR)
}
+#ifndef DISABLE_WELLKNOWN
+static int
+smtp_wellknown_handler(void)
+{
+if (verify_check_host(&wellknown_advertise_hosts) != FAIL)
+ {
+ GET_OPTION("acl_smtp_wellknown");
+ if (acl_smtp_wellknown)
+ {
+ uschar * user_msg = NULL, * log_msg;
+ int rc;
+
+ if ((rc = acl_check(ACL_WHERE_WELLKNOWN, NULL, acl_smtp_wellknown,
+ &user_msg, &log_msg)) != OK)
+ return smtp_handle_acl_fail(ACL_WHERE_WELLKNOWN, rc, user_msg, log_msg);
+ else if (!wellknown_response)
+ return smtp_handle_acl_fail(ACL_WHERE_WELLKNOWN, ERROR, user_msg, log_msg);
+ smtp_user_msg(US"250", wellknown_response);
+ return 0;
+ }
+ }
+
+smtp_printf("554 not permitted\r\n", SP_NO_MORE);
+log_write(0, LOG_MAIN|LOG_REJECT, "rejected \"%s\" from %s",
+ smtp_cmd_buffer, sender_helo_name, host_and_ident(FALSE));
+return 0;
+}
+#endif
+
+
static int
expand_mailmax(const uschar * s)
{
cmd_list[CL_STLS].is_mail_cmd = TRUE;
#endif
-if (lwr_receive_getc != NULL)
+if (lwr_receive_getc && !atrn_mode)
{
/* This should have already happened, but if we've gotten confused,
force a reset here. */
while (done <= 0)
{
- const uschar **argv;
- uschar *etrn_command;
- uschar *etrn_serialize_key;
- uschar *errmess;
- uschar *log_msg, *smtp_code;
- uschar *user_msg = NULL;
- uschar *recipient = NULL;
- uschar *hello = NULL;
- uschar *s, *ss;
- BOOL was_rej_mail = FALSE;
- BOOL was_rcpt = FALSE;
+ const uschar ** argv;
+ uschar * etrn_command, * etrn_serialize_key, * errmess;
+ uschar * log_msg, * smtp_code;
+ uschar * user_msg = NULL, * recipient = NULL, * hello = NULL;
+ uschar * s, * ss;
+ BOOL was_rej_mail = FALSE, was_rcpt = FALSE;
void (*oldsignal)(int);
pid_t pid;
int start, end, sender_domain, recipient_domain;
- int rc;
- int c;
- uschar *orcpt = NULL;
- int dsn_flags;
+ int rc, c, dsn_flags;
+ uschar * orcpt = NULL;
gstring * g;
#ifdef AUTH_TLS
{
cmd_list[CL_TLAU].is_mail_cmd = FALSE;
- for (auth_instance * au = auths; au; au = au->next)
- if (strcmpic(US"tls", au->driver_name) == 0)
+ for (auth_instance * au = auths; au; au = au->drinst.next)
+ if (strcmpic(US"tls", au->drinst.driver_name) == 0)
{
+ GET_OPTION("acl_smtp_auth");
if ( acl_smtp_auth
&& (rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth,
&user_msg, &log_msg)) != OK
#ifndef DISABLE_EVENT
{
uschar * save_name = sender_host_authenticated, * logmsg;
- sender_host_authenticated = au->name;
+ sender_host_authenticated = au->drinst.name;
if ((logmsg = event_raise(event_action, US"auth:fail", s, NULL)))
log_write(0, LOG_MAIN, "%s", logmsg);
sender_host_authenticated = save_name;
/* Check the ACL */
+ GET_OPTION("acl_smtp_auth");
if ( acl_smtp_auth
&& (rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth,
&user_msg, &log_msg)) != OK
if (*smtp_cmd_data)
{
- *smtp_cmd_data++ = 0;
- while (isspace(*smtp_cmd_data)) smtp_cmd_data++;
+ *smtp_cmd_data++ = '\0';
+ Uskip_whitespace(&smtp_cmd_data);
}
/* Search for an authentication mechanism which is configured for use
auth_instance * au;
uschar * smtp_resp, * errmsg;
- for (au = auths; au; au = au->next)
+ for (au = auths; au; au = au->drinst.next)
if (strcmpic(s, au->public_name) == 0 && au->server &&
(au->advertised || f.allow_auth_unadvertised))
break;
uschar * logmsg = NULL;
#ifndef DISABLE_EVENT
{uschar * save_name = sender_host_authenticated;
- sender_host_authenticated = au->name;
+ sender_host_authenticated = au->drinst.name;
logmsg = event_raise(event_action, US"auth:fail", smtp_resp, NULL);
sender_host_authenticated = save_name;
}
log_write(0, LOG_MAIN|LOG_REJECT, "%s", logmsg);
else
log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s",
- au->name, host_and_ident(FALSE), errmsg);
+ au->drinst.name, host_and_ident(FALSE), errmsg);
}
}
else
if (!f.sender_host_unknown)
{
BOOL old_helo_verified = f.helo_verified;
- uschar *p = smtp_cmd_data;
+ uschar * p = smtp_cmd_data;
- while (*p != 0 && !isspace(*p)) { *p = tolower(*p); p++; }
- *p = 0;
+ while (*p && !isspace(*p)) { *p = tolower(*p); p++; }
+ *p = '\0';
/* Force a reverse lookup if HELO quoted something in helo_lookup_domains
because otherwise the log can be confusing. */
}
}
-#ifdef SUPPORT_SPF
- /* set up SPF context */
- spf_conn_init(sender_helo_name, sender_host_address);
-#endif
+ /* For any misc-module having a connection-init routine, call it. */
+
+ if (misc_mod_conn_init(sender_helo_name, sender_host_address) != OK)
+ {
+ DEBUG(D_receive) debug_printf("A module conn-init routine failed\n");
+ done = 1;
+ break;
+ }
/* Apply an ACL check if one is defined; afterwards, recheck
synchronization in case the client started sending in a delay. */
+ GET_OPTION("acl_smtp_helo");
if (acl_smtp_helo)
if ((rc = acl_check(ACL_WHERE_HELO, NULL, acl_smtp_helo,
&user_msg, &log_msg)) != OK)
g = string_catn(g, US"-SIZE\r\n", 7);
}
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
- if ( (smtp_mailcmd_max > 0 || recipients_max)
+#ifndef DISABLE_ESMTP_LIMITS
+ if ( (smtp_mailcmd_max > 0 || recipients_max_expanded > 0)
&& verify_check_host(&limits_advertise_hosts) == OK)
{
g = string_fmt_append(g, "%.3s-LIMITS", smtp_code);
if (smtp_mailcmd_max > 0)
g = string_fmt_append(g, " MAILMAX=%d", smtp_mailcmd_max);
- if (recipients_max)
- g = string_fmt_append(g, " RCPTMAX=%d", recipients_max);
+ if (recipients_max_expanded > 0)
+ g = string_fmt_append(g, " RCPTMAX=%d", recipients_max_expanded);
g = string_catn(g, US"\r\n", 2);
}
#endif
fl.dsn_advertised = TRUE;
}
- /* Advertise ETRN/VRFY/EXPN if there's are ACL checking whether a host is
- permitted to issue them; a check is made when any host actually tries. */
+ /* Advertise ATRN/ETRN/VRFY/EXPN if there's are ACL checking whether a
+ host is permitted to issue them; a check is made when any host actually
+ tries. */
+ GET_OPTION("acl_smtp_atrn");
+ if (acl_smtp_atrn && !atrn_mode)
+ {
+ const uschar * s = expand_cstring(acl_smtp_atrn);
+ if (s && *s)
+ {
+ g = string_catn(g, smtp_code, 3);
+ g = string_catn(g, US"-ATRN\r\n", 7);
+ }
+ }
+ GET_OPTION("acl_smtp_etrn");
if (acl_smtp_etrn)
{
g = string_catn(g, smtp_code, 3);
g = string_catn(g, US"-ETRN\r\n", 7);
}
+ GET_OPTION("acl_smtp_vrfy");
if (acl_smtp_vrfy)
{
g = string_catn(g, smtp_code, 3);
g = string_catn(g, US"-VRFY\r\n", 7);
}
+ GET_OPTION("acl_smtp_expn");
if (acl_smtp_expn)
{
g = string_catn(g, smtp_code, 3);
)
{
BOOL first = TRUE;
- for (auth_instance * au = auths; au; au = au->next)
+ for (auth_instance * au = auths; au; au = au->drinst.next)
{
au->advertised = FALSE;
if (au->server)
{
DEBUG(D_auth+D_expand) debug_printf_indent(
"Evaluating advertise_condition for %s %s athenticator\n",
- au->name, au->public_name);
+ au->drinst.name, au->public_name);
if ( !au->advertise_condition
- || expand_check_condition(au->advertise_condition, au->name,
- US"authenticator")
+ || expand_check_condition(au->advertise_condition,
+ au->drinst.name, US"authenticator")
)
{
int saveptr;
chunking_state = CHUNKING_OFFERED;
}
+#ifndef DISABLE_TLS
/* Advertise TLS (Transport Level Security) aka SSL (Secure Socket Layer)
if it has been included in the binary, and the host matches
tls_advertise_hosts. We must *not* advertise if we are already in a
secure connection. */
-#ifndef DISABLE_TLS
if (tls_in.active.sock < 0 &&
verify_check_host(&tls_advertise_hosts) != FAIL)
{
fl.smtputf8_advertised = TRUE;
}
#endif
+#ifndef DISABLE_WELLKNOWN
+ if (verify_check_host(&wellknown_advertise_hosts) != FAIL)
+ {
+ g = string_catn(g, smtp_code, 3);
+ g = string_catn(g, US"-WELLKNOWN\r\n", 12);
+ }
+#endif
/* Finish off the multiline reply with one that is always available. */
toomany = FALSE;
break; /* HELO/EHLO */
+#ifndef DISABLE_WELLKNOWN
+ case WELLKNOWN_CMD:
+ HAD(SCH_WELLKNOWN);
+ smtp_mailcmd_count++;
+ smtp_wellknown_handler();
+ break;
+#endif
+
#ifdef EXPERIMENTAL_XCLIENT
case XCLIENT_CMD:
{
if ( fl.helo_verify_required
|| verify_check_host(&hosts_require_helo) == OK)
{
- smtp_printf("503 HELO or EHLO required\r\n", SP_NO_MORE);
log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL from %s: no "
"HELO/EHLO given", host_and_ident(FALSE));
+ done = synprot_error(L_smtp_protocol_error, 503, NULL,
+ US"HELO or EHLO required");
break;
}
else if (smtp_mailcmd_max < 0)
int rc;
uschar *ignore_msg;
- if (auth_xtextdecode(value, &authenticated_sender) < 0)
+ if (xtextdecode(value, &authenticated_sender) < 0)
{
/* Put back terminator overrides for error message */
value[-1] = '=';
US"invalid data for AUTH");
goto COMMAND_LOOP;
}
+ GET_OPTION("acl_smtp_mailauth");
if (!acl_smtp_mailauth)
{
ignore_msg = US"client not authenticated";
if (authenticated_by == NULL ||
authenticated_by->mail_auth_condition == NULL ||
expand_check_condition(authenticated_by->mail_auth_condition,
- authenticated_by->name, US"authenticator"))
+ authenticated_by->drinst.name, US"authenticator"))
break; /* Accept the AUTH */
ignore_msg = US"server_mail_auth_condition failed";
when pipelining is not advertised, do another sync check in case the ACL
delayed and the client started sending in the meantime. */
+ GET_OPTION("acl_smtp_mail");
if (acl_smtp_mail)
{
rc = acl_check(ACL_WHERE_MAIL, NULL, acl_smtp_mail, &user_msg, &log_msg);
/* Check maximum allowed */
- if (rcpt_count+1 < 0 || rcpt_count > recipients_max && recipients_max > 0)
+ if ( rcpt_count+1 < 0
+ || rcpt_count > recipients_max_expanded && recipients_max_expanded > 0)
{
if (recipients_max_reject)
{
if (f.recipients_discarded)
rc = DISCARD;
else
+ {
+ GET_OPTION("acl_smtp_rcpt");
if ( (rc = acl_check(ACL_WHERE_RCPT, recipient, acl_smtp_rcpt, &user_msg,
&log_msg)) == OK
&& !f.smtp_in_pipelining_advertised && !check_sync())
goto SYNC_FAILURE;
+ }
/* The ACL was happy */
since the ACL may have delayed. To handle cutthrough delivery enforce a
dummy call to get the DATA command sent. */
+ GET_OPTION("acl_smtp_predata");
if (!acl_smtp_predata && cutthrough.cctx.sock < 0)
- {
- if (!check_sync()) goto SYNC_FAILURE;
rc = OK;
- }
else
{
uschar * acl = acl_smtp_predata ? acl_smtp_predata : US"accept";
US"verify")))
break;
+ GET_OPTION("acl_smtp_vrfy");
if ((rc = acl_check(ACL_WHERE_VRFY, address, acl_smtp_vrfy,
&user_msg, &log_msg)) != OK)
done = smtp_handle_acl_fail(ACL_WHERE_VRFY, rc, user_msg, log_msg);
case EXPN_CMD:
HAD(SCH_EXPN);
+ GET_OPTION("acl_smtp_expn");
rc = acl_check(ACL_WHERE_EXPN, NULL, acl_smtp_expn, &user_msg, &log_msg);
if (rc != OK)
done = smtp_handle_acl_fail(ACL_WHERE_EXPN, rc, user_msg, log_msg);
/* Apply an ACL check if one is defined */
+ GET_OPTION("acl_smtp_starttls");
if ( acl_smtp_starttls
&& (rc = acl_check(ACL_WHERE_STARTTLS, NULL, acl_smtp_starttls,
&user_msg, &log_msg)) != OK
case QUIT_CMD:
f.smtp_in_quit = TRUE;
user_msg = NULL;
+ GET_OPTION("acl_smtp_quit");
if ( acl_smtp_quit
&& ((rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, &user_msg,
&log_msg)) == ERROR))
#endif
smtp_printf(" HELO EHLO MAIL RCPT DATA BDAT", SP_MORE);
smtp_printf(" NOOP QUIT RSET HELP", SP_MORE);
+ if (acl_smtp_atrn) smtp_printf(" ATRN", SP_MORE);
if (acl_smtp_etrn) smtp_printf(" ETRN", SP_MORE);
if (acl_smtp_expn) smtp_printf(" EXPN", SP_MORE);
if (acl_smtp_vrfy) smtp_printf(" VRFY", SP_MORE);
+#ifndef DISABLE_WELLKNOWN
+ if (verify_check_host(&wellknown_advertise_hosts) != FAIL)
+ smtp_printf(" WELLKNOWN", SP_MORE);
+#endif
#ifdef EXPERIMENTAL_XCLIENT
if (proxy_session || verify_check_host(&hosts_xclient) != FAIL)
smtp_printf(" XCLIENT", SP_MORE);
break;
+ case ATRN_CMD:
+ HAD(SCH_ATRN);
+ done = atrn_handle_provider(&user_msg, &log_msg); /* Normal: exit() */
+ break; /* Error cases */
+
case ETRN_CMD:
HAD(SCH_ETRN);
if (sender_address)
log_write(L_etrn, LOG_MAIN, "ETRN %s received from %s", smtp_cmd_argument,
host_and_ident(FALSE));
+ GET_OPTION("acl_smtp_etrn");
if ((rc = acl_check(ACL_WHERE_ETRN, NULL, acl_smtp_etrn,
&user_msg, &log_msg)) != OK)
{
since that is strictly the only kind of ETRN that can be implemented
according to the RFC. */
+ GET_OPTION("smtp_etrn_command");
if (smtp_etrn_command)
{
uschar *error;
case TOO_MANY_NONMAIL_CMD:
s = smtp_cmd_buffer;
- while (*s && !isspace(*s)) s++;
+ Uskip_nonwhite(&s);
incomplete_transaction_log(US"too many non-mail commands");
log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
"nonmail commands (last was \"%.*s\")", host_and_ident(FALSE),