X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fsrc%2Fsmtp_in.c;h=cf0a5d64276e32623dd6f26af3d46da4e511d092;hb=2f460950af2d4379deaa804c43caf24a9fb055a0;hp=9fa2ae6edc1473f2201df0942517794a192176c1;hpb=6023a6ad2ac0294879b14127f62795095da573b5;p=user%2Fhenk%2Fcode%2Fexim.git diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 9fa2ae6ed..cf0a5d642 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) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for handling an incoming SMTP call. */ @@ -71,6 +71,7 @@ enum { VRFY_CMD, EXPN_CMD, NOOP_CMD, /* RFC as requiring synchronization */ 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 */ /* This is a dummy to identify the non-sync commands when pipelining */ @@ -158,7 +159,10 @@ AUTH is already forbidden. After a TLS session is started, AUTH's flag is again forced TRUE, to allow for the re-authentication that can happen at that point. QUIT is also "falsely" labelled as a mail command so that it doesn't up the -count of non-mail commands and possibly provoke an error. */ +count of non-mail commands and possibly provoke an error. + +tls_auth is a pseudo-command, never expected in input. It is activated +on TLS startup and looks for a tls authenticator. */ static smtp_cmd_list cmd_list[] = { /* name len cmd has_arg is_mail_cmd */ @@ -169,6 +173,7 @@ static smtp_cmd_list cmd_list[] = { { "auth", sizeof("auth")-1, AUTH_CMD, TRUE, TRUE }, #ifdef SUPPORT_TLS { "starttls", sizeof("starttls")-1, STARTTLS_CMD, FALSE, FALSE }, + { "tls_auth", 0, TLS_AUTH_CMD, FALSE, TRUE }, #endif /* If you change anything above here, also fix the definitions below. */ @@ -192,6 +197,7 @@ static smtp_cmd_list *cmd_list_end = #define CMD_LIST_EHLO 2 #define CMD_LIST_AUTH 3 #define CMD_LIST_STARTTLS 4 +#define CMD_LIST_TLS_AUTH 5 /* This list of names is used for performing the smtp_no_mail logging action. It must be kept in step with the SCH_xxx enumerations. */ @@ -1025,10 +1031,12 @@ for (p = cmd_list; p < cmd_list_end; p++) continue; } #endif - 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 ( p->len + && 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 */ @@ -1512,7 +1520,9 @@ sender_verified_list = NULL; /* No senders verified */ memset(sender_address_cache, 0, sizeof(sender_address_cache)); memset(sender_domain_cache, 0, sizeof(sender_domain_cache)); +#ifndef DISABLE_PRDR prdr_requested = FALSE; +#endif /* Reset the DSN flags */ dsn_ret = 0; @@ -1645,6 +1655,7 @@ while (done <= 0) it is the canonical extracted address which is all that is kept. */ case MAIL_CMD: + smtp_mailcmd_count++; /* Count for no-mail log */ if (sender_address != NULL) /* The function moan_smtp_batch() does not return. */ moan_smtp_batch(smtp_cmd_buffer, "503 Sender already given"); @@ -3003,29 +3014,25 @@ else /* If a host name is known, check it and all its aliases. */ - if (sender_host_name != NULL) - { - helo_verified = strcmpic(sender_host_name, sender_helo_name) == 0; - - if (helo_verified) + if (sender_host_name) + if ((helo_verified = strcmpic(sender_host_name, sender_helo_name) == 0)) { + sender_helo_dnssec = sender_host_dnssec; HDEBUG(D_receive) debug_printf("matched host name\n"); } else { uschar **aliases = sender_host_aliases; - while (*aliases != NULL) - { - helo_verified = strcmpic(*aliases++, sender_helo_name) == 0; - if (helo_verified) break; - } - HDEBUG(D_receive) - { - if (helo_verified) + while (*aliases) + if ((helo_verified = strcmpic(*aliases++, sender_helo_name) == 0)) + { + sender_helo_dnssec = sender_host_dnssec; + break; + } + + HDEBUG(D_receive) if (helo_verified) debug_printf("matched alias %s\n", *(--aliases)); - } } - } /* Final attempt: try a forward lookup of the helo name */ @@ -3033,29 +3040,34 @@ else { int rc; host_item h; + dnssec_domains d; + host_item *hh; + h.name = sender_helo_name; h.address = NULL; h.mx = MX_NONE; h.next = NULL; + d.request = US"*"; + d.require = US""; + HDEBUG(D_receive) debug_printf("getting IP address for %s\n", sender_helo_name); - rc = host_find_byname(&h, NULL, 0, NULL, TRUE); + rc = host_find_bydns(&h, NULL, HOST_FIND_BY_A, + NULL, NULL, NULL, &d, NULL, NULL); if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL) - { - host_item *hh = &h; - while (hh != NULL) - { + for (hh = &h; hh; hh = hh->next) if (Ustrcmp(hh->address, sender_host_address) == 0) { helo_verified = TRUE; + if (h.dnssec == DS_YES) sender_helo_dnssec = TRUE; HDEBUG(D_receive) - debug_printf("IP address for %s matches calling address\n", - sender_helo_name); + { + debug_printf("IP address for %s matches calling address\n" + "Forward DNS security status: %sverified\n", + sender_helo_name, sender_helo_dnssec ? "" : "un"); + } break; } - hh = hh->next; - } - } } } @@ -3092,6 +3104,113 @@ smtp_respond(code, len, TRUE, user_msg); +static int +smtp_in_auth(auth_instance *au, uschar ** s, uschar ** ss) +{ +const uschar *set_id = NULL; +int rc, i; + +/* Run the checking code, passing the remainder of the command line as +data. Initials the $auth variables as empty. Initialize $0 empty and set +it as the only set numerical variable. The authenticator may set $auth +and also set other numeric variables. The $auth variables are preferred +nowadays; the numerical variables remain for backwards compatibility. + +Afterwards, have a go at expanding the set_id string, even if +authentication failed - for bad passwords it can be useful to log the +userid. On success, require set_id to expand and exist, and put it in +authenticated_id. Save this in permanent store, as the working store gets +reset at HELO, RSET, etc. */ + +for (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); +if (au->set_id) set_id = expand_string(au->set_id); +expand_nmax = -1; /* Reset numeric variables */ +for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; /* Reset $auth */ + +/* The value of authenticated_id is stored in the spool file and printed in +log lines. It must not contain binary zeros or newline characters. In +normal use, it never will, but when playing around or testing, this error +can (did) happen. To guard against this, ensure that the id contains only +printing characters. */ + +if (set_id) set_id = string_printing(set_id); + +/* For the non-OK cases, set up additional logging data if set_id +is not empty. */ + +if (rc != OK) + set_id = set_id && *set_id + ? string_sprintf(" (set_id=%s)", set_id) : US""; + +/* Switch on the result */ + +switch(rc) + { + case OK: + if (!au->set_id || set_id) /* Complete success */ + { + if (set_id) authenticated_id = string_copy_malloc(set_id); + sender_host_authenticated = au->name; + authentication_failed = FALSE; + authenticated_fail_id = NULL; /* Impossible to already be set? */ + + received_protocol = + (sender_host_address ? protocols : protocols_local) + [pextend + pauthed + (tls_in.active >= 0 ? pcrpted:0)]; + *s = *ss = US"235 Authentication succeeded"; + authenticated_by = au; + break; + } + + /* Authentication succeeded, but we failed to expand the set_id string. + Treat this as a temporary error. */ + + auth_defer_msg = expand_string_message; + /* Fall through */ + + case DEFER: + if (set_id) authenticated_fail_id = string_copy_malloc(set_id); + *s = string_sprintf("435 Unable to authenticate at present%s", + auth_defer_user_msg); + *ss = string_sprintf("435 Unable to authenticate at present%s: %s", + set_id, auth_defer_msg); + break; + + case BAD64: + *s = *ss = US"501 Invalid base64 data"; + break; + + case CANCELLED: + *s = *ss = US"501 Authentication cancelled"; + break; + + case UNEXPECTED: + *s = *ss = US"553 Initial data not expected"; + break; + + case FAIL: + if (set_id) authenticated_fail_id = string_copy_malloc(set_id); + *s = US"535 Incorrect authentication data"; + *ss = string_sprintf("535 Incorrect authentication data%s", set_id); + break; + + default: + if (set_id) authenticated_fail_id = string_copy_malloc(set_id); + *s = US"435 Internal error"; + *ss = string_sprintf("435 Internal error%s: return %d from authentication " + "check", set_id, rc); + break; + } + +return rc; +} + + + /************************************************* * Initialize for SMTP incoming message * *************************************************/ @@ -3143,6 +3262,7 @@ cmd_list[CMD_LIST_HELO].is_mail_cmd = TRUE; cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE; #ifdef SUPPORT_TLS cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = TRUE; +cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; #endif /* Set the local signal handler for SIGTERM - it tries to end off tidily */ @@ -3166,7 +3286,6 @@ while (done <= 0) uschar *user_msg = NULL; uschar *recipient = NULL; uschar *hello = NULL; - const uschar *set_id = NULL; uschar *s, *ss; BOOL was_rej_mail = FALSE; BOOL was_rcpt = FALSE; @@ -3179,6 +3298,41 @@ while (done <= 0) uschar *orcpt = NULL; int flags; +#if defined(SUPPORT_TLS) && defined(AUTH_TLS) + /* Check once per STARTTLS or SSL-on-connect for a TLS AUTH */ + if ( tls_in.active >= 0 + && tls_in.peercert + && tls_in.certificate_verified + && cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd + ) + { + cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = FALSE; + if (acl_smtp_auth) + { + rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, &user_msg, &log_msg); + if (rc != OK) + { + done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg); + continue; + } + } + + for (au = auths; au; au = au->next) + if (strcmpic(US"tls", au->driver_name) == 0) + { + smtp_cmd_data = NULL; + + if ((c = smtp_in_auth(au, &s, &ss)) != OK) + log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s", + au->name, host_and_ident(FALSE), ss); + else + DEBUG(D_auth) debug_printf("tls auth succeeded\n"); + + break; + } + } +#endif + switch(smtp_read_command(TRUE)) { /* The AUTH command is not permitted to occur inside a transaction, and may @@ -3206,13 +3360,13 @@ while (done <= 0) US"AUTH command used when not advertised"); break; } - if (sender_host_authenticated != NULL) + if (sender_host_authenticated) { done = synprot_error(L_smtp_protocol_error, 503, NULL, US"already authenticated"); break; } - if (sender_address != NULL) + if (sender_address) { done = synprot_error(L_smtp_protocol_error, 503, NULL, US"not permitted in mail transaction"); @@ -3221,7 +3375,7 @@ while (done <= 0) /* Check the ACL */ - if (acl_smtp_auth != NULL) + if (acl_smtp_auth) { rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, &user_msg, &log_msg); if (rc != OK) @@ -3258,122 +3412,23 @@ while (done <= 0) as a server and which has been advertised (unless, sigh, allow_auth_ unadvertised is set). */ - for (au = auths; au != NULL; au = au->next) - { + for (au = auths; au; au = au->next) if (strcmpic(s, au->public_name) == 0 && au->server && - (au->advertised || allow_auth_unadvertised)) break; - } - - if (au == NULL) - { - done = synprot_error(L_smtp_protocol_error, 504, NULL, - string_sprintf("%s authentication mechanism not supported", s)); - break; - } - - /* Run the checking code, passing the remainder of the command line as - data. Initials the $auth variables as empty. Initialize $0 empty and set - it as the only set numerical variable. The authenticator may set $auth - and also set other numeric variables. The $auth variables are preferred - nowadays; the numerical variables remain for backwards compatibility. - - Afterwards, have a go at expanding the set_id string, even if - authentication failed - for bad passwords it can be useful to log the - userid. On success, require set_id to expand and exist, and put it in - authenticated_id. Save this in permanent store, as the working store gets - reset at HELO, RSET, etc. */ - - for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; - expand_nmax = 0; - expand_nlength[0] = 0; /* $0 contains nothing */ + (au->advertised || allow_auth_unadvertised)) + break; - c = (au->info->servercode)(au, smtp_cmd_data); - if (au->set_id != NULL) set_id = expand_string(au->set_id); - expand_nmax = -1; /* Reset numeric variables */ - for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; /* Reset $auth */ - - /* The value of authenticated_id is stored in the spool file and printed in - log lines. It must not contain binary zeros or newline characters. In - normal use, it never will, but when playing around or testing, this error - can (did) happen. To guard against this, ensure that the id contains only - printing characters. */ - - if (set_id != NULL) set_id = string_printing(set_id); - - /* For the non-OK cases, set up additional logging data if set_id - is not empty. */ - - if (c != OK) - { - if (set_id != NULL && *set_id != 0) - set_id = string_sprintf(" (set_id=%s)", set_id); - else set_id = US""; - } - - /* Switch on the result */ - - switch(c) + if (au) { - case OK: - if (au->set_id == NULL || set_id != NULL) /* Complete success */ - { - if (set_id != NULL) authenticated_id = string_copy_malloc(set_id); - sender_host_authenticated = au->name; - authentication_failed = FALSE; - authenticated_fail_id = NULL; /* Impossible to already be set? */ - - received_protocol = - (sender_host_address ? protocols : protocols_local) - [pextend + pauthed + (tls_in.active >= 0 ? pcrpted:0)]; - s = ss = US"235 Authentication succeeded"; - authenticated_by = au; - break; - } - - /* Authentication succeeded, but we failed to expand the set_id string. - Treat this as a temporary error. */ - - auth_defer_msg = expand_string_message; - /* Fall through */ - - case DEFER: - if (set_id != NULL) authenticated_fail_id = string_copy_malloc(set_id); - s = string_sprintf("435 Unable to authenticate at present%s", - auth_defer_user_msg); - ss = string_sprintf("435 Unable to authenticate at present%s: %s", - set_id, auth_defer_msg); - break; + c = smtp_in_auth(au, &s, &ss); - case BAD64: - s = ss = US"501 Invalid base64 data"; - break; - - case CANCELLED: - s = ss = US"501 Authentication cancelled"; - break; - - case UNEXPECTED: - s = ss = US"553 Initial data not expected"; - break; - - case FAIL: - if (set_id != NULL) authenticated_fail_id = string_copy_malloc(set_id); - s = US"535 Incorrect authentication data"; - ss = string_sprintf("535 Incorrect authentication data%s", set_id); - break; - - default: - if (set_id != NULL) authenticated_fail_id = string_copy_malloc(set_id); - s = US"435 Internal error"; - ss = string_sprintf("435 Internal error%s: return %d from authentication " - "check", set_id, c); - break; + smtp_printf("%s\r\n", s); + if (c != OK) + log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s", + au->name, host_and_ident(FALSE), ss); } - - smtp_printf("%s\r\n", s); - if (c != OK) - log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s", - au->name, host_and_ident(FALSE), ss); + else + done = synprot_error(L_smtp_protocol_error, 504, NULL, + string_sprintf("%s authentication mechanism not supported", s)); break; /* AUTH_CMD */ @@ -3469,7 +3524,7 @@ while (done <= 0) now obsolescent, since the verification can now be requested selectively at ACL time. */ - helo_verified = helo_verify_failed = FALSE; + helo_verified = helo_verify_failed = sender_helo_dnssec = FALSE; if (helo_required || helo_verify) { BOOL tempfail = !smtp_verify_helo(); @@ -3659,38 +3714,40 @@ while (done <= 0) letters, so output the names in upper case, though we actually recognize them in either case in the AUTH command. */ - if (auths != NULL) - { - if (verify_check_host(&auth_advertise_hosts) == OK) - { - auth_instance *au; - BOOL first = TRUE; - for (au = auths; au != NULL; au = au->next) - { - if (au->server && (au->advertise_condition == NULL || - expand_check_condition(au->advertise_condition, au->name, - US"authenticator"))) - { - int saveptr; - if (first) - { - s = string_cat(s, &size, &ptr, smtp_code, 3); - s = string_cat(s, &size, &ptr, US"-AUTH", 5); - first = FALSE; - auth_advertised = TRUE; - } - saveptr = ptr; - s = string_cat(s, &size, &ptr, US" ", 1); - s = string_cat(s, &size, &ptr, au->public_name, - Ustrlen(au->public_name)); - while (++saveptr < ptr) s[saveptr] = toupper(s[saveptr]); - au->advertised = TRUE; - } - else au->advertised = FALSE; - } - if (!first) s = string_cat(s, &size, &ptr, US"\r\n", 2); - } - } + if ( auths +#if defined(SUPPORT_TLS) && defined(AUTH_TLS) + && !sender_host_authenticated +#endif + && verify_check_host(&auth_advertise_hosts) == OK + ) + { + auth_instance *au; + BOOL first = TRUE; + for (au = auths; au; au = au->next) + if (au->server && (au->advertise_condition == NULL || + expand_check_condition(au->advertise_condition, au->name, + US"authenticator"))) + { + int saveptr; + if (first) + { + s = string_cat(s, &size, &ptr, smtp_code, 3); + s = string_cat(s, &size, &ptr, US"-AUTH", 5); + first = FALSE; + auth_advertised = TRUE; + } + saveptr = ptr; + s = string_cat(s, &size, &ptr, US" ", 1); + s = string_cat(s, &size, &ptr, au->public_name, + Ustrlen(au->public_name)); + while (++saveptr < ptr) s[saveptr] = toupper(s[saveptr]); + au->advertised = TRUE; + } + else + au->advertised = FALSE; + + if (!first) s = string_cat(s, &size, &ptr, US"\r\n", 2); + } /* Advertise TLS (Transport Level Security) aka SSL (Secure Socket Layer) if it has been included in the binary, and the host matches @@ -4425,13 +4482,11 @@ while (done <= 0) else smtp_user_msg(US"250", user_msg); rcpt_fail_count++; discarded = TRUE; - log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> rejected RCPT %s: " + log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> RCPT %s: " "discarded by %s ACL%s%s", host_and_ident(TRUE), - (sender_address_unrewritten != NULL)? - sender_address_unrewritten : sender_address, + sender_address_unrewritten? sender_address_unrewritten : sender_address, smtp_cmd_argument, recipients_discarded? "MAIL" : "RCPT", - (log_msg == NULL)? US"" : US": ", - (log_msg == NULL)? US"" : log_msg); + log_msg ? US": " : US"", log_msg ? log_msg : US""); } /* Either the ACL failed the address, or it was deferred. */ @@ -4667,6 +4722,7 @@ while (done <= 0) helo_seen = esmtp = auth_advertised = pipelining_advertised = FALSE; cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE; cmd_list[CMD_LIST_AUTH].is_mail_cmd = TRUE; + cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; if (sender_helo_name != NULL) { store_free(sender_helo_name);