X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fsrc%2Fsmtp_in.c;h=0fcedc8216cb68719cdb5f416c3276f4330ca22a;hb=33191679e1a86ba6d9c38a74d0795d00c300f2c5;hp=bdac07be7bf5a6a3ac7640af07606c58f0e0c6d9;hpb=2679d413f3f22e7bbc797f4403cc4333bee0073d;p=user%2Fhenk%2Fcode%2Fexim.git diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index bdac07be7..0fcedc821 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1,10 +1,10 @@ -/* $Cambridge: exim/src/src/smtp_in.c,v 1.56 2007/03/21 15:10:39 ph10 Exp $ */ +/* $Cambridge: exim/src/src/smtp_in.c,v 1.67 2010/06/12 15:21:26 jetmore Exp $ */ /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2007 */ +/* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for handling an incoming SMTP call. */ @@ -31,6 +31,7 @@ including that header, and restore its value afterwards. */ int allow_severity = LOG_INFO; int deny_severity = LOG_NOTICE; +uschar *tcp_wrappers_name; #endif @@ -123,6 +124,7 @@ static BOOL pipelining_advertised; static BOOL rcpt_smtp_response_same; static BOOL rcpt_in_progress; static int nonmail_command_count; +static BOOL smtp_exit_function_called = 0; static int synprot_error_count; static int unknown_command_count; static int sync_cmd_limit; @@ -263,6 +265,9 @@ if (smtp_inptr >= smtp_inend) else smtp_had_eof = 1; return EOF; } +#ifndef DISABLE_DKIM + dkim_exim_verify_feed(smtp_inbuffer, rc); +#endif smtp_inend = smtp_inbuffer + rc; smtp_inptr = smtp_inbuffer; } @@ -333,6 +338,23 @@ return smtp_had_error; +/************************************************* +* Test for characters in the SMTP buffer * +*************************************************/ + +/* Used at the end of a message + +Arguments: none +Returns: TRUE/FALSE +*/ + +BOOL +smtp_buffered(void) +{ +return smtp_inptr < smtp_inend; +} + + /************************************************* * Write formatted string to SMTP channel * @@ -358,26 +380,40 @@ smtp_printf(char *format, ...) { va_list ap; +va_start(ap, format); +smtp_vprintf(format, ap); +va_end(ap); +} + +/* This is split off so that verify.c:respond_printf() can, in effect, call +smtp_printf(), bearing in mind that in C a vararg function can't directly +call another vararg function, only a function which accepts a va_list. */ + +void +smtp_vprintf(char *format, va_list ap) +{ +BOOL yield; + +yield = string_vformat(big_buffer, big_buffer_size, format, 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); + void *reset_point = store_get(0); + uschar *msg_copy, *cr, *end; + msg_copy = string_copy(big_buffer); + end = msg_copy + Ustrlen(msg_copy); + while ((cr = Ustrchr(msg_copy, '\r')) != NULL) /* lose CRs */ + memmove(cr, cr + 1, (end--) - cr); + debug_printf("SMTP>> %s", msg_copy); + store_reset(reset_point); } -va_start(ap, format); -if (!string_vformat(big_buffer, big_buffer_size, format, ap)) +if (!yield) { log_write(0, LOG_MAIN|LOG_PANIC, "string too large in smtp_printf()"); smtp_closedown(US"Unexpected error"); exim_exit(EXIT_FAILURE); } -va_end(ap); /* If this is the first output for a (non-batch) RCPT command, see if all RCPTs have had the same. Note: this code is also present in smtp_respond(). It would @@ -453,9 +489,8 @@ log_write(L_lost_incoming_connection, host_and_ident(FALSE)); if (smtp_batched_input) moan_smtp_batch(NULL, "421 SMTP command timeout"); /* Does not return */ -smtp_printf("421 %s: SMTP command timeout - closing connection\r\n", - smtp_active_hostname); -mac_smtp_fflush(); +smtp_notquit_exit(US"command-timeout", US"421", + US"%s: SMTP command timeout - closing connection", smtp_active_hostname); exim_exit(EXIT_FAILURE); } @@ -478,8 +513,8 @@ sig = sig; /* Keep picky compilers happy */ log_write(0, LOG_MAIN, "%s closed after SIGTERM", smtp_get_connection_info()); if (smtp_batched_input) moan_smtp_batch(NULL, "421 SIGTERM received"); /* Does not return */ -smtp_printf("421 %s: Service not available - closing connection\r\n", - smtp_active_hostname); +smtp_notquit_exit(US"signal-exit", US"421", + US"%s: Service not available - closing connection", smtp_active_hostname); exim_exit(EXIT_FAILURE); } @@ -685,7 +720,9 @@ phase, sends the reply string, and gives an error to all subsequent commands except QUIT. The existence of an SMTP call is detected by the non-NULLness of smtp_in. -Argument: SMTP reply string to send, excluding the code +Arguments: + message SMTP reply string to send, excluding the code + Returns: nothing */ @@ -799,7 +836,8 @@ if ((log_extra_selector & LX_tls_certificate_verified) != 0 && s = string_append(s, &size, &ptr, 2, US" CV=", tls_certificate_verified? "yes":"no"); if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_peerdn != NULL) - s = string_append(s, &size, &ptr, 3, US" DN=\"", tls_peerdn, US"\""); + s = string_append(s, &size, &ptr, 3, US" DN=\"", + string_printing(tls_peerdn), US"\""); #endif sep = (smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE)? @@ -1002,8 +1040,10 @@ authenticated_sender = NULL; bmi_run = 0; bmi_verdicts = NULL; #endif -#ifdef EXPERIMENTAL_DOMAINKEYS -dk_do_verify = 0; +#ifndef DISABLE_DKIM +dkim_signers = NULL; +dkim_disable_verify = FALSE; +dkim_collect_input = FALSE; #endif #ifdef EXPERIMENTAL_SPF spf_header_comment = NULL; @@ -1327,6 +1367,7 @@ auth_advertised = FALSE; pipelining_advertised = FALSE; pipelining_enable = TRUE; sync_cmd_limit = NON_SYNC_CMD_NON_PIPELINING; +smtp_exit_function_called = FALSE; /* For avoiding loop in not-quit exit */ memset(sender_host_cache, 0, sizeof(sender_host_cache)); @@ -1378,6 +1419,7 @@ receive_getc = smtp_getc; receive_ungetc = smtp_ungetc; receive_feof = smtp_feof; receive_ferror = smtp_ferror; +receive_smtp_buffered = smtp_buffered; smtp_inptr = smtp_inend = smtp_inbuffer; smtp_had_eof = smtp_had_error = 0; @@ -1650,7 +1692,14 @@ if (!sender_host_unknown) #ifdef USE_TCP_WRAPPERS errno = 0; - if (!hosts_ctl("exim", + tcp_wrappers_name = expand_string(tcp_wrappers_daemon_name); + if (tcp_wrappers_name == NULL) + { + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Expansion of \"%s\" " + "(tcp_wrappers_name) failed: %s", string_printing(tcp_wrappers_name), + expand_string_message); + } + if (!hosts_ctl(tcp_wrappers_name, (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)) @@ -2248,12 +2297,98 @@ if (!drop) return 0; log_write(L_smtp_connection, LOG_MAIN, "%s closed by DROP in ACL", smtp_get_connection_info()); + +/* Run the not-quit ACL, but without any custom messages. This should not be a +problem, because we get here only if some other ACL has issued "drop", and +in that case, *its* custom messages will have been used above. */ + +smtp_notquit_exit(US"acl-drop", NULL, NULL); return 2; } +/************************************************* +* Handle SMTP exit when QUIT is not given * +*************************************************/ + +/* This function provides a logging/statistics hook for when an SMTP connection +is dropped on the floor or the other end goes away. It's a global function +because it's called from receive.c as well as this module. As well as running +the NOTQUIT ACL, if there is one, this function also outputs a final SMTP +response, either with a custom message from the ACL, or using a default. There +is one case, however, when no message is output - after "drop". In that case, +the ACL that obeyed "drop" has already supplied the custom message, and NULL is +passed to this function. + +In case things go wrong while processing this function, causing an error that +may re-enter this funtion, there is a recursion check. + +Arguments: + reason What $smtp_notquit_reason will be set to in the ACL; + if NULL, the ACL is not run + code The error code to return as part of the response + defaultrespond The default message if there's no user_msg + +Returns: Nothing +*/ + +void +smtp_notquit_exit(uschar *reason, uschar *code, uschar *defaultrespond, ...) +{ +int rc; +uschar *user_msg = NULL; +uschar *log_msg = NULL; + +/* Check for recursive acll */ + +if (smtp_exit_function_called) + { + log_write(0, LOG_PANIC, "smtp_notquit_exit() called more than once (%s)", + reason); + return; + } +smtp_exit_function_called = TRUE; + +/* Call the not-QUIT ACL, if there is one, unless no reason is given. */ + +if (acl_smtp_notquit != NULL && reason != NULL) + { + smtp_notquit_reason = reason; + rc = acl_check(ACL_WHERE_NOTQUIT, NULL, acl_smtp_notquit, &user_msg, + &log_msg); + if (rc == ERROR) + log_write(0, LOG_MAIN|LOG_PANIC, "ACL for not-QUIT returned ERROR: %s", + log_msg); + } + +/* Write an SMTP response if we are expected to give one. As the default +responses are all internal, they should always fit in the buffer, but code a +warning, just in case. Note that string_vformat() still leaves a complete +string, even if it is incomplete. */ + +if (code != NULL && defaultrespond != NULL) + { + if (user_msg == NULL) + { + uschar buffer[128]; + va_list ap; + va_start(ap, defaultrespond); + if (!string_vformat(buffer, sizeof(buffer), CS defaultrespond, ap)) + log_write(0, LOG_MAIN|LOG_PANIC, "string too large in smtp_notquit_exit()"); + smtp_printf("%s %s\r\n", code, buffer); + va_end(ap); + } + else + smtp_respond(code, 3, TRUE, user_msg); + mac_smtp_fflush(); + } +} + + + + /************************************************* * Verify HELO argument * *************************************************/ @@ -3116,7 +3251,7 @@ while (done <= 0) in order to be able to log the sender address on failure. */ if (strcmpic(name, US"SIZE") == 0 && - ((size = (int)Ustrtoul(value, &end, 10)), *end == 0)) + ((size = Ustrtoul(value, &end, 10)), *end == 0)) { if ((size == ULONG_MAX && errno == ERANGE) || size > INT_MAX) size = INT_MAX; @@ -3768,11 +3903,29 @@ while (done <= 0) case EOF_CMD: log_write(L_smtp_connection, LOG_MAIN, "%s closed by EOF", smtp_get_connection_info()); + smtp_notquit_exit(US"tls-failed", NULL, NULL); done = 2; break; + /* It is perhaps arguable as to which exit ACL should be called here, + but as it is probably a situtation that almost never arises, it + probably doesn't matter. We choose to call the real QUIT ACL, which in + some sense is perhaps "right". */ + case QUIT_CMD: - smtp_printf("221 %s closing connection\r\n", smtp_active_hostname); + user_msg = NULL; + if (acl_smtp_quit != NULL) + { + rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, &user_msg, + &log_msg); + if (rc == ERROR) + log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s", + log_msg); + } + if (user_msg == NULL) + smtp_printf("221 %s closing connection\r\n", smtp_active_hostname); + else + smtp_respond(US"221", 3, TRUE, user_msg); log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT", smtp_get_connection_info()); done = 2; @@ -3795,15 +3948,13 @@ while (done <= 0) case QUIT_CMD: HAD(SCH_QUIT); incomplete_transaction_log(US"QUIT"); - if (acl_smtp_quit != NULL) { - rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit,&user_msg,&log_msg); + rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, &user_msg, &log_msg); if (rc == ERROR) log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s", log_msg); } - if (user_msg == NULL) smtp_printf("221 %s closing connection\r\n", smtp_active_hostname); else @@ -3835,9 +3986,10 @@ while (done <= 0) break; - /* Show ETRN/EXPN/VRFY if there's - an ACL for checking hosts; if actually used, a check will be done for - permitted hosts. */ + /* Show ETRN/EXPN/VRFY if there's an ACL for checking hosts; if actually + used, a check will be done for permitted hosts. Show STARTTLS only if not + already in a TLS session and if it would be advertised in the EHLO + response. */ case HELP_CMD: HAD(SCH_HELP); @@ -3847,7 +3999,9 @@ while (done <= 0) buffer[0] = 0; Ustrcat(buffer, " AUTH"); #ifdef SUPPORT_TLS - Ustrcat(buffer, " STARTTLS"); + if (tls_active < 0 && + verify_check_host(&tls_advertise_hosts) != FAIL) + Ustrcat(buffer, " STARTTLS"); #endif Ustrcat(buffer, " HELO EHLO MAIL RCPT DATA"); Ustrcat(buffer, " NOOP QUIT RSET HELP"); @@ -3861,7 +4015,8 @@ while (done <= 0) case EOF_CMD: incomplete_transaction_log(US"connection lost"); - smtp_printf("421 %s lost input connection\r\n", smtp_active_hostname); + smtp_notquit_exit(US"connection-lost", US"421", + US"%s lost input connection", smtp_active_hostname); /* Don't log by default unless in the middle of a message, as some mailers just drop the call rather than sending QUIT, and it clutters up the logs. @@ -4067,7 +4222,8 @@ while (done <= 0) pipelining_advertised? "" : " not", smtp_cmd_buffer, host_and_ident(TRUE), string_printing(smtp_inptr)); - smtp_printf("554 SMTP synchronization error\r\n"); + smtp_notquit_exit(US"synchronization-error", US"554", + US"SMTP synchronization error"); done = 1; /* Pretend eof - drops connection */ break; @@ -4079,7 +4235,7 @@ while (done <= 0) log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many " "nonmail commands (last was \"%.*s\")", host_and_ident(FALSE), s - smtp_cmd_buffer, smtp_cmd_buffer); - smtp_printf("554 Too many nonmail commands\r\n"); + smtp_notquit_exit(US"bad-commands", US"554", US"Too many nonmail commands"); done = 1; /* Pretend eof - drops connection */ break; @@ -4092,7 +4248,8 @@ while (done <= 0) string_printing(smtp_cmd_buffer), host_and_ident(TRUE), US"unrecognized command"); incomplete_transaction_log(US"unrecognized command"); - smtp_printf("500 Too many unrecognized commands\r\n"); + smtp_notquit_exit(US"bad-commands", US"500", + US"Too many unrecognized commands"); done = 2; log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many " "unrecognized commands (last was \"%s\")", host_and_ident(FALSE),