X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/6a3f1455e3402f214d60abb87969660844770d91..d45b1de81aa47cef4ca098a4b2725d855b154162:/src/src/smtp_in.c diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index cc6486a40..6c27c15de 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/smtp_in.c,v 1.31 2006/02/13 12:02:59 ph10 Exp $ */ +/* $Cambridge: exim/src/src/smtp_in.c,v 1.42 2006/09/18 14:49:23 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -338,8 +338,13 @@ va_list ap; DEBUG(D_receive) { + uschar *cr, *end; va_start(ap, format); (void) string_vformat(big_buffer, big_buffer_size, format, ap); + va_end(ap); + end = big_buffer + Ustrlen(big_buffer); + while ((cr = Ustrchr(big_buffer, '\r')) != NULL) /* lose CRs */ + memmove(cr, cr + 1, (end--) - cr); debug_printf("SMTP>> %s", big_buffer); } @@ -518,7 +523,10 @@ if required. */ for (p = cmd_list; p < cmd_list_end; p++) { - if (strncmpic(smtp_cmd_buffer, US p->name, p->len) == 0) + if (strncmpic(smtp_cmd_buffer, US p->name, p->len) == 0 && + (smtp_cmd_buffer[p->len-1] == ':' || /* "mail from:" or "rcpt to:" */ + smtp_cmd_buffer[p->len] == 0 || + smtp_cmd_buffer[p->len] == ' ')) { if (smtp_inptr < smtp_inend && /* Outstanding input */ p->cmd < sync_cmd_limit && /* Command should sync */ @@ -626,6 +634,8 @@ for (;;) /* This function is called when logging information about an SMTP connection. It sets up appropriate source information, depending on the type of connection. +If sender_fullhost is NULL, we are at a very early stage of the connection; +just use the IP address. Argument: none Returns: a string describing the connection @@ -634,21 +644,24 @@ Returns: a string describing the connection uschar * smtp_get_connection_info(void) { +uschar *hostname = (sender_fullhost == NULL)? + sender_host_address : sender_fullhost; + if (host_checking) - return string_sprintf("SMTP connection from %s", sender_fullhost); + return string_sprintf("SMTP connection from %s", hostname); if (sender_host_unknown || sender_host_notsocket) return string_sprintf("SMTP connection from %s", sender_ident); if (is_inetd) - return string_sprintf("SMTP connection from %s (via inetd)", sender_fullhost); + return string_sprintf("SMTP connection from %s (via inetd)", hostname); if ((log_extra_selector & LX_incoming_interface) != 0 && interface_address != NULL) - return string_sprintf("SMTP connection from %s I=[%s]:%d", sender_fullhost, + return string_sprintf("SMTP connection from %s I=[%s]:%d", hostname, interface_address, interface_port); -return string_sprintf("SMTP connection from %s", sender_fullhost); +return string_sprintf("SMTP connection from %s", hostname); } @@ -803,7 +816,7 @@ rcpt_count = rcpt_defer_count = rcpt_fail_count = raw_recipients_count = recipients_count = recipients_list_max = 0; message_linecount = 0; message_size = -1; -acl_warn_headers = NULL; +acl_added_headers = NULL; queue_only_policy = FALSE; deliver_freeze = FALSE; /* Can be set by ACL */ freeze_tell = freeze_tell_config; /* Can be set by ACL */ @@ -1146,7 +1159,10 @@ sync_cmd_limit = NON_SYNC_CMD_NON_PIPELINING; memset(sender_host_cache, 0, sizeof(sender_host_cache)); -sender_host_authenticated = NULL; +/* If receiving by -bs from a trusted user, or testing with -bh, we allow +authentication settings from -oMaa to remain in force. */ + +if (!host_checking && !sender_host_notsocket) sender_host_authenticated = NULL; authenticated_by = NULL; #ifdef SUPPORT_TLS @@ -1195,8 +1211,8 @@ smtp_had_eof = smtp_had_error = 0; /* Set up the message size limit; this may be host-specific */ -thismessage_size_limit = expand_string_integer(message_size_limit); -if (thismessage_size_limit < 0) +thismessage_size_limit = expand_string_integer(message_size_limit, TRUE); +if (expand_string_message != NULL) { if (thismessage_size_limit == -1) log_write(0, LOG_MAIN|LOG_PANIC, "unable to expand message_size_limit: " @@ -1449,19 +1465,40 @@ if (!sender_host_unknown) return FALSE; } - /* Test with TCP Wrappers if so configured */ + /* Test with TCP Wrappers if so configured. There is a problem in that + hosts_ctl() returns 0 (deny) under a number of system failure circumstances, + such as disks dying. In these cases, it is desirable to reject with a 4xx + error instead of a 5xx error. There isn't a "right" way to detect such + problems. The following kludge is used: errno is zeroed before calling + hosts_ctl(). If the result is "reject", a 5xx error is given only if the + value of errno is 0 or ENOENT (which happens if /etc/hosts.{allow,deny} does + not exist). */ #ifdef USE_TCP_WRAPPERS + errno = 0; if (!hosts_ctl("exim", (sender_host_name == NULL)? STRING_UNKNOWN : CS sender_host_name, (sender_host_address == NULL)? STRING_UNKNOWN : CS sender_host_address, (sender_ident == NULL)? STRING_UNKNOWN : CS sender_ident)) { - HDEBUG(D_receive) debug_printf("tcp wrappers rejection\n"); - log_write(L_connection_reject, - LOG_MAIN|LOG_REJECT, "refused connection from %s " - "(tcp wrappers)", host_and_ident(FALSE)); - smtp_printf("554 SMTP service not available\r\n"); + if (errno == 0 || errno == ENOENT) + { + HDEBUG(D_receive) debug_printf("tcp wrappers rejection\n"); + log_write(L_connection_reject, + LOG_MAIN|LOG_REJECT, "refused connection from %s " + "(tcp wrappers)", host_and_ident(FALSE)); + smtp_printf("554 SMTP service not available\r\n"); + } + else + { + int save_errno = errno; + HDEBUG(D_receive) debug_printf("tcp wrappers rejected with unexpected " + "errno value %d\n", save_errno); + log_write(L_connection_reject, + LOG_MAIN|LOG_REJECT, "temporarily refused connection from %s " + "(tcp wrappers errno=%d)", host_and_ident(FALSE), save_errno); + smtp_printf("451 Temporary local problem - please try later\r\n"); + } return FALSE; } #endif @@ -1736,7 +1773,8 @@ responses. If no_multiline_responses is TRUE (it can be set from an ACL), we output nothing for non-final calls, and only the first line for anything else. Arguments: - code SMTP code + code SMTP code, may involve extended status codes + codelen length of smtp code; uf > 3 there's an ESC final FALSE if the last line isn't the final line msg message text, possibly containing newlines @@ -1744,26 +1782,36 @@ Returns: nothing */ void -smtp_respond(int code, BOOL final, uschar *msg) +smtp_respond(uschar* code, int codelen, BOOL final, uschar *msg) { +int esclen = 0; +uschar *esc = US""; + if (!final && no_multiline_responses) return; +if (codelen > 3) + { + esc = code + 4; + esclen = codelen - 4; + } + for (;;) { uschar *nl = Ustrchr(msg, '\n'); if (nl == NULL) { - smtp_printf("%d%c%s\r\n", code, final? ' ':'-', msg); + smtp_printf("%.3s%c%.*s%s\r\n", code, final? ' ':'-', esclen, esc, msg); return; } else if (nl[1] == 0 || no_multiline_responses) { - smtp_printf("%d%c%.*s\r\n", code, final? ' ':'-', (int)(nl - msg), msg); + smtp_printf("%.3s%c%.*s%.*s\r\n", code, final? ' ':'-', esclen, esc, + (int)(nl - msg), msg); return; } else { - smtp_printf("%d-%.*s\r\n", code, (int)(nl - msg), msg); + smtp_printf("%.3s-%.*s%.*s\r\n", code, esclen, esc, (int)(nl - msg), msg); msg = nl + 1; while (isspace(*msg)) msg++; } @@ -1783,13 +1831,18 @@ logging the incident, and sets up the error response. A message containing newlines is turned into a multiline SMTP response, but for logging, only the first line is used. -There's a table of the response codes to use in globals.c, along with the table -of names. VFRY is special. Despite RFC1123 it defaults disabled in Exim. -However, discussion in connection with RFC 821bis (aka RFC 2821) has concluded -that the response should be 252 in the disabled state, because there are broken -clients that try VRFY before RCPT. A 5xx response should be given only when the -address is positively known to be undeliverable. Sigh. Also, for ETRN, 458 is -given on refusal, and for AUTH, 503. +There's a table of default permanent failure response codes to use in +globals.c, along with the table of names. VFRY is special. Despite RFC1123 it +defaults disabled in Exim. However, discussion in connection with RFC 821bis +(aka RFC 2821) has concluded that the response should be 252 in the disabled +state, because there are broken clients that try VRFY before RCPT. A 5xx +response should be given only when the address is positively known to be +undeliverable. Sigh. Also, for ETRN, 458 is given on refusal, and for AUTH, +503. + +From Exim 4.63, it is possible to override the response code details by +providing a suitable response code string at the start of the message provided +in user_msg. The code's first digit is checked for validity. Arguments: where where the ACL was called from @@ -1806,8 +1859,10 @@ Returns: 0 in most cases int smtp_handle_acl_fail(int where, int rc, uschar *user_msg, uschar *log_msg) { -int code = acl_wherecodes[where]; BOOL drop = rc == FAIL_DROP; +int codelen = 3; +int ovector[3]; +uschar *smtp_code; uschar *lognl; uschar *sender_info = US""; uschar *what = @@ -1822,6 +1877,41 @@ uschar *what = if (drop) rc = FAIL; +/* Set the default SMTP code */ + +smtp_code = (rc != FAIL)? US"451" : acl_wherecodes[where]; + +/* Check a user message for starting with a response code and optionally an +extended status code. If found, check that the first digit is valid, and if so, +use it instead of the default code. */ + +if (user_msg != NULL) + { + int n = pcre_exec(regex_smtp_code, NULL, CS user_msg, Ustrlen(user_msg), 0, + PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int)); + if (n >= 0) + { + if (user_msg[0] != smtp_code[0]) + { + log_write(0, LOG_MAIN|LOG_PANIC, "configured error code starts with " + "incorrect digit (expected %c) in \"%s\"", smtp_code[0], user_msg); + + /* If log_msg == user_msg (the default set in acl.c if no log message is + specified, we must adjust the log message to show the code that is + actually going to be used. */ + + if (log_msg == user_msg) + log_msg = string_sprintf("%s %s", smtp_code, log_msg + ovector[1]); + } + else + { + smtp_code = user_msg; + codelen = ovector[1]; /* Includes final space */ + } + user_msg += ovector[1]; /* Chop the code off the message */ + } + } + /* 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 @@ -1840,22 +1930,24 @@ if (where == ACL_WHERE_RCPT || where == ACL_WHERE_DATA || where == ACL_WHERE_MIM /* If there's been a sender verification failure with a specific message, and we have not sent a response about it yet, do so now, as a preliminary line for -failures, but not defers. However, log it in both cases. */ +failures, but not defers. However, always log it for defer, and log it for fail +unless the sender_verify_fail log selector has been turned off. */ if (sender_verified_failed != NULL && !testflag(sender_verified_failed, af_sverify_told)) { setflag(sender_verified_failed, af_sverify_told); - log_write(0, LOG_MAIN|LOG_REJECT, "%s sender verify %s for <%s>%s", - host_and_ident(TRUE), - ((sender_verified_failed->special_action & 255) == DEFER)? "defer" : "fail", - sender_verified_failed->address, - (sender_verified_failed->message == NULL)? US"" : - string_sprintf(": %s", sender_verified_failed->message)); + if (rc != FAIL || (log_extra_selector & LX_sender_verify_fail) != 0) + log_write(0, LOG_MAIN|LOG_REJECT, "%s sender verify %s for <%s>%s", + host_and_ident(TRUE), + ((sender_verified_failed->special_action & 255) == DEFER)? "defer":"fail", + sender_verified_failed->address, + (sender_verified_failed->message == NULL)? US"" : + string_sprintf(": %s", sender_verified_failed->message)); if (rc == FAIL && sender_verified_failed->user_message != NULL) - smtp_respond(code, FALSE, string_sprintf( + smtp_respond(smtp_code, codelen, FALSE, string_sprintf( testflag(sender_verified_failed, af_verify_pmfail)? "Postmaster verification failed while checking <%s>\n%s\n" "Several RFCs state that you are required to have a postmaster\n" @@ -1885,7 +1977,7 @@ if (lognl != NULL) *lognl = 0; always a 5xx one - see comments at the start of this function. If the original rc was FAIL_DROP we drop the connection and yield 2. */ -if (rc == FAIL) smtp_respond(code, TRUE, (user_msg == NULL)? +if (rc == FAIL) smtp_respond(smtp_code, codelen, TRUE, (user_msg == NULL)? US"Administrative prohibition" : user_msg); /* Send temporary failure response to the command. Don't give any details, @@ -1904,12 +1996,13 @@ else sender_verified_failed != NULL && sender_verified_failed->message != NULL) { - smtp_respond(451, FALSE, sender_verified_failed->message); + smtp_respond(smtp_code, codelen, FALSE, sender_verified_failed->message); } - smtp_respond(451, TRUE, user_msg); + smtp_respond(smtp_code, codelen, TRUE, user_msg); } else - smtp_printf("451 Temporary local problem - please try later\r\n"); + smtp_respond(smtp_code, codelen, TRUE, + US"Temporary local problem - please try later"); } /* Log the incident. If the connection is not forcibly to be dropped, return 0. @@ -2140,10 +2233,14 @@ while (done <= 0) switch(smtp_read_command(TRUE)) { /* The AUTH command is not permitted to occur inside a transaction, and may - occur successfully only once per connection, and then only when we've - advertised it. Actually, that isn't quite true. When TLS is started, all - previous information about a connection must be discarded, so a new AUTH is - permitted at that time. + occur successfully only once per connection. Actually, that isn't quite + true. When TLS is started, all previous information about a connection must + be discarded, so a new AUTH is permitted at that time. + + AUTH may only be used when it has been advertised. However, it seems that + there are clients that send AUTH when it hasn't been advertised, some of + them even doing this after HELO. And there are MTAs that accept this. Sigh. + So there's a get-out that allows this to happen. AUTH is initially labelled as a "nonmail command" so that one occurrence doesn't get counted. We change the label here so that multiple failing @@ -2153,7 +2250,7 @@ while (done <= 0) authentication_failed = TRUE; cmd_list[CMD_LIST_AUTH].is_mail_cmd = FALSE; - if (!auth_advertised) + if (!auth_advertised && !allow_auth_unadvertised) { done = synprot_error(L_smtp_protocol_error, 503, NULL, US"AUTH command used when not advertised"); @@ -2208,12 +2305,13 @@ while (done <= 0) } /* Search for an authentication mechanism which is configured for use - as a server and which has been advertised. */ + as a server and which has been advertised (unless, sigh, allow_auth_ + unadvertised is set). */ for (au = auths; au != NULL; au = au->next) { if (strcmpic(s, au->public_name) == 0 && au->server && - au->advertised) break; + (au->advertised || allow_auth_unadvertised)) break; } if (au == NULL) @@ -2631,7 +2729,13 @@ while (done <= 0) #endif (void)fwrite(s, 1, ptr, smtp_out); - DEBUG(D_receive) debug_printf("SMTP>> %s", s); + DEBUG(D_receive) + { + uschar *cr; + while ((cr = Ustrchr(s, '\r')) != NULL) /* lose CRs */ + memmove(cr, cr + 1, (ptr--) - (cr - s)); + debug_printf("SMTP>> %s", s); + } helo_seen = TRUE; break; /* HELO/EHLO */ @@ -3185,14 +3289,14 @@ while (done <= 0) break; case DEFER: - s = (addr->message != NULL)? - string_sprintf("451 <%s> %s", address, addr->message) : + s = (addr->user_message != NULL)? + string_sprintf("451 <%s> %s", address, addr->user_message) : string_sprintf("451 Cannot resolve <%s> at this time", address); break; case FAIL: - s = (addr->message != NULL)? - string_sprintf("550 <%s> %s", address, addr->message) : + s = (addr->user_message != NULL)? + string_sprintf("550 <%s> %s", address, addr->user_message) : string_sprintf("550 <%s> is not deliverable", address); log_write(0, LOG_MAIN, "VRFY failed for %s %s", smtp_cmd_argument, host_and_ident(TRUE));