X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/f70940c9489d0ff5dc44db5ba5ed5258a8fe8772..HEAD:/src/src/smtp_in.c diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index ffc7779f8..e76790fea 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* 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 */ @@ -86,6 +86,9 @@ enum { /* 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 */ @@ -121,7 +124,8 @@ enum { /* 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 @@ -230,7 +234,10 @@ static smtp_cmd_list cmd_list[] = { { "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. */ @@ -253,6 +260,9 @@ uschar * smtp_names[] = [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 @@ -454,6 +464,23 @@ smtp_had_eof = smtp_had_error = 0; +#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. */ @@ -497,7 +524,7 @@ if (rc <= 0) 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; @@ -560,7 +587,7 @@ int n = smtp_inend - smtp_inptr; if (n > lim) n = lim; if (n > 0) - dkim_exim_verify_feed(smtp_inptr, n); + smtp_verify_feed(smtp_inptr, n); #endif } @@ -716,19 +743,24 @@ bdat_getc(unsigned lim) 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 @@ -757,9 +789,7 @@ for(;;) 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; } @@ -833,7 +863,7 @@ next_cmd: 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 */ } @@ -1227,7 +1257,7 @@ for (smtp_cmd_list * p = cmd_list; p < cmd_list + nelem(cmd_list); p++) 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; @@ -1345,14 +1375,14 @@ const uschar * hostname = sender_fullhost 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 (f.is_inetd) g = string_append(g, 2, hostname, US" (via inetd)"); @@ -1671,28 +1701,7 @@ bmi_run = 0; 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 */ @@ -1727,6 +1736,7 @@ while (acl_warn_logged) store_free(this); } +misc_mod_smtp_reset(); message_tidyup(); store_reset(reset_point); @@ -1876,10 +1886,11 @@ while (done <= 0) /* 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 */ @@ -1945,6 +1956,9 @@ while (done <= 0) case HELP_CMD: case NOOP_CMD: case ETRN_CMD: +#ifndef DISABLE_WELLKNOWN + case WELLKNOWN_CMD: +#endif bsmtp_transaction_linecount = receive_linecount; break; @@ -2051,16 +2065,22 @@ else DEBUG(D_receive) static void log_connect_tls_drop(const uschar * what, const uschar * log_msg) { -gstring * g = s_tlslog(NULL); -uschar * tls = string_from_gstring(g); - -log_write(L_connection_reject, - log_reject_target, "%s%s%s dropped by %s%s%s", - LOGGING(dnssec) && sender_host_dnssec ? US" DS" : US"", - host_and_ident(TRUE), - tls ? tls : US"", - what, - log_msg ? US": " : US"", log_msg); +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"", + host_and_ident(TRUE), + tls ? tls : US"", + what, + log_msg ? US": " : US"", log_msg); + } } @@ -2168,6 +2188,7 @@ lwr_receive_ungetc = 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) { @@ -2537,11 +2558,16 @@ if (!f.sender_host_unknown) 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 @@ -2560,6 +2586,7 @@ if (proxy_protocol_host()) /* Run the connect ACL if it exists */ user_msg = NULL; +GET_OPTION("acl_smtp_connect"); if (acl_smtp_connect) { int rc; @@ -2606,16 +2633,20 @@ if (user_msg) 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 */ @@ -2735,6 +2766,12 @@ 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); @@ -2814,10 +2851,8 @@ if (fl.rcpt_in_progress) 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; @@ -2834,7 +2869,6 @@ for (;;) msg = nl + 1; Uskip_whitespace(&msg); } - } } @@ -2953,7 +2987,8 @@ smtp_code = rc == FAIL ? acl_wherecodes[where] : US"451"; 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 @@ -2976,9 +3011,9 @@ switch (where) 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); } @@ -3085,7 +3120,7 @@ else the connection is not forcibly to be dropped, return 0. Otherwise, log why it is closing if required and return 2. */ -if (log_reject_target != 0) +if (log_reject_target) { #ifndef DISABLE_TLS gstring * g = s_tlslog(NULL); @@ -3176,6 +3211,7 @@ fl.smtp_exit_function_called = TRUE; /* 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; @@ -3356,7 +3392,7 @@ Returns: nothing */ 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); @@ -3373,9 +3409,9 @@ int rc; /* 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 variables as empty. Initialize $0 empty and set @@ -3393,7 +3429,10 @@ for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; 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 */ @@ -3422,7 +3461,7 @@ switch(rc) 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? */ @@ -3512,6 +3551,7 @@ smtp_quit_handler(uschar ** user_msgp, uschar ** log_msgp) 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) @@ -3561,6 +3601,36 @@ if (chunking_state > CHUNKING_OFFERED) } +#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) { @@ -3666,9 +3736,8 @@ while (done <= 0) void (*oldsignal)(int); pid_t pid; int start, end, sender_domain, recipient_domain; - int rc; - int c; - uschar *orcpt = NULL; + int rc, c; + uschar * orcpt = NULL; int dsn_flags; gstring * g; @@ -3682,9 +3751,10 @@ while (done <= 0) { 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 @@ -3702,7 +3772,7 @@ while (done <= 0) #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; @@ -3763,6 +3833,7 @@ while (done <= 0) /* 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 @@ -3788,8 +3859,8 @@ while (done <= 0) 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 @@ -3800,7 +3871,7 @@ while (done <= 0) 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; @@ -3815,7 +3886,7 @@ while (done <= 0) 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; } @@ -3824,7 +3895,7 @@ while (done <= 0) 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 @@ -3900,10 +3971,10 @@ while (done <= 0) 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. */ @@ -3949,14 +4020,19 @@ while (done <= 0) } } -#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) @@ -4051,15 +4127,15 @@ while (done <= 0) 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 @@ -4088,16 +4164,19 @@ while (done <= 0) /* 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. */ + 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); @@ -4144,17 +4223,17 @@ while (done <= 0) ) { 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; @@ -4187,12 +4266,12 @@ while (done <= 0) 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) { @@ -4226,6 +4305,13 @@ while (done <= 0) 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. */ @@ -4273,6 +4359,14 @@ while (done <= 0) 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: { @@ -4326,9 +4420,10 @@ while (done <= 0) 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) @@ -4484,7 +4579,7 @@ while (done <= 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] = '='; @@ -4493,6 +4588,7 @@ while (done <= 0) US"invalid data for AUTH"); goto COMMAND_LOOP; } + GET_OPTION("acl_smtp_mailauth"); if (!acl_smtp_mailauth) { ignore_msg = US"client not authenticated"; @@ -4511,7 +4607,7 @@ while (done <= 0) 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"; @@ -4689,6 +4785,7 @@ while (done <= 0) 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); @@ -4897,7 +4994,8 @@ while (done <= 0) /* 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) { @@ -4943,10 +5041,13 @@ while (done <= 0) 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 */ @@ -5102,13 +5203,14 @@ while (done <= 0) } if (chunking_state > CHUNKING_OFFERED) - rc = OK; /* No predata ACL or go-ahead output for BDAT */ + rc = OK; /* There is no predata ACL or go-ahead output for BDAT */ else { - /* If there is an ACL, re-check the synchronization afterwards, since the - ACL may have delayed. To handle cutthrough delivery enforce a dummy call - to get the DATA command sent. */ + /* If there is a predata-ACL, re-check the synchronization afterwards, + 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) rc = OK; else @@ -5167,6 +5269,7 @@ while (done <= 0) 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); @@ -5205,6 +5308,7 @@ while (done <= 0) 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); @@ -5234,6 +5338,7 @@ while (done <= 0) /* 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 @@ -5352,6 +5457,7 @@ while (done <= 0) 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)) @@ -5417,6 +5523,10 @@ while (done <= 0) 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); @@ -5471,6 +5581,7 @@ while (done <= 0) 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) { @@ -5487,6 +5598,7 @@ while (done <= 0) 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; @@ -5658,7 +5770,7 @@ while (done <= 0) 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),