- /* 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;
-
- 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);
-
- break; /* AUTH_CMD */
-
- /* The HELO/EHLO commands are permitted to appear in the middle of a
- session as well as at the beginning. They have the effect of a reset in
- addition to their other functions. Their absence at the start cannot be
- taken to be an error.
-
- RFC 2821 says:
-
- If the EHLO command is not acceptable to the SMTP server, 501, 500,
- or 502 failure replies MUST be returned as appropriate. The SMTP
- server MUST stay in the same state after transmitting these replies
- that it was in before the EHLO was received.
-
- Therefore, we do not do the reset until after checking the command for
- acceptability. This change was made for Exim release 4.11. Previously
- it did the reset first. */
-
- case HELO_CMD:
- HAD(SCH_HELO);
- hello = US"HELO";
- esmtp = FALSE;
- goto HELO_EHLO;
-
- case EHLO_CMD:
- HAD(SCH_EHLO);
- hello = US"EHLO";
- esmtp = TRUE;
-
- HELO_EHLO: /* Common code for HELO and EHLO */
- cmd_list[CMD_LIST_HELO].is_mail_cmd = FALSE;
- cmd_list[CMD_LIST_EHLO].is_mail_cmd = FALSE;
-
- /* Reject the HELO if its argument was invalid or non-existent. A
- successful check causes the argument to be saved in malloc store. */
-
- if (!check_helo(smtp_cmd_data))
- {
- smtp_printf("501 Syntactically invalid %s argument(s)\r\n", hello);
-
- log_write(0, LOG_MAIN|LOG_REJECT, "rejected %s from %s: syntactically "
- "invalid argument(s): %s", hello, host_and_ident(FALSE),
- (*smtp_cmd_argument == 0)? US"(no argument given)" :
- string_printing(smtp_cmd_argument));
-
- if (++synprot_error_count > smtp_max_synprot_errors)
- {
- log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
- "syntax or protocol errors (last command was \"%s\")",
- host_and_ident(FALSE), smtp_cmd_buffer);
- done = 1;
- }
-
- break;
- }
-
- /* If sender_host_unknown is true, we have got here via the -bs interface,
- not called from inetd. Otherwise, we are running an IP connection and the
- host address will be set. If the helo name is the primary name of this
- host and we haven't done a reverse lookup, force one now. If helo_required
- is set, ensure that the HELO name matches the actual host. If helo_verify
- is set, do the same check, but softly. */
-
- if (!sender_host_unknown)
- {
- BOOL old_helo_verified = helo_verified;
- uschar *p = smtp_cmd_data;
-
- while (*p != 0 && !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. */
-
- if (sender_host_name == NULL &&
- (deliver_domain = sender_helo_name, /* set $domain */
- match_isinlist(sender_helo_name, &helo_lookup_domains, 0,
- &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) == OK)
- (void)host_name_lookup();
-
- /* Rebuild the fullhost info to include the HELO name (and the real name
- if it was looked up.) */
-
- host_build_sender_fullhost(); /* Rebuild */
- set_process_info("handling%s incoming connection from %s",
- (tls_in.active >= 0)? " TLS" : "", host_and_ident(FALSE));
-
- /* Verify if configured. This doesn't give much security, but it does
- make some people happy to be able to do it. If helo_required is set,
- (host matches helo_verify_hosts) failure forces rejection. If helo_verify
- is set (host matches helo_try_verify_hosts), it does not. This is perhaps
- now obsolescent, since the verification can now be requested selectively
- at ACL time. */
-
- helo_verified = helo_verify_failed = FALSE;
- if (helo_required || helo_verify)
- {
- BOOL tempfail = !smtp_verify_helo();
- if (!helo_verified)
- {
- if (helo_required)
- {
- smtp_printf("%d %s argument does not match calling host\r\n",
- tempfail? 451 : 550, hello);
- log_write(0, LOG_MAIN|LOG_REJECT, "%srejected \"%s %s\" from %s",
- tempfail? "temporarily " : "",
- hello, sender_helo_name, host_and_ident(FALSE));
- helo_verified = old_helo_verified;
- break; /* End of HELO/EHLO processing */
- }
- HDEBUG(D_all) debug_printf("%s verification failed but host is in "
- "helo_try_verify_hosts\n", hello);
- }
- }
- }
-
-#ifdef EXPERIMENTAL_SPF
- /* set up SPF context */
- spf_init(sender_helo_name, sender_host_address);
-#endif
-
- /* Apply an ACL check if one is defined; afterwards, recheck
- synchronization in case the client started sending in a delay. */
-
- if (acl_smtp_helo != NULL)
- {
- rc = acl_check(ACL_WHERE_HELO, NULL, acl_smtp_helo, &user_msg, &log_msg);
- if (rc != OK)
- {
- done = smtp_handle_acl_fail(ACL_WHERE_HELO, rc, user_msg, log_msg);
- sender_helo_name = NULL;
- host_build_sender_fullhost(); /* Rebuild */
- break;
- }
- else if (!check_sync()) goto SYNC_FAILURE;
- }
-
- /* Generate an OK reply. The default string includes the ident if present,
- and also the IP address if present. Reflecting back the ident is intended
- as a deterrent to mail forgers. For maximum efficiency, and also because
- some broken systems expect each response to be in a single packet, arrange
- that the entire reply is sent in one write(). */
-
- auth_advertised = FALSE;
- pipelining_advertised = FALSE;
- #ifdef SUPPORT_TLS
- tls_advertised = FALSE;
- #endif
-
- smtp_code = US"250 "; /* Default response code plus space*/
- if (user_msg == NULL)
- {
- s = string_sprintf("%.3s %s Hello %s%s%s",
- smtp_code,
- smtp_active_hostname,
- (sender_ident == NULL)? US"" : sender_ident,
- (sender_ident == NULL)? US"" : US" at ",
- (sender_host_name == NULL)? sender_helo_name : sender_host_name);
-
- ptr = Ustrlen(s);
- size = ptr + 1;
-
- if (sender_host_address != NULL)
- {
- s = string_cat(s, &size, &ptr, US" [", 2);
- s = string_cat(s, &size, &ptr, sender_host_address,
- Ustrlen(sender_host_address));
- s = string_cat(s, &size, &ptr, US"]", 1);
- }
- }
-
- /* A user-supplied EHLO greeting may not contain more than one line. Note
- that the code returned by smtp_message_code() includes the terminating
- whitespace character. */
-
- else
- {
- char *ss;
- int codelen = 4;
- smtp_message_code(&smtp_code, &codelen, &user_msg, NULL);
- s = string_sprintf("%.*s%s", codelen, smtp_code, user_msg);
- if ((ss = strpbrk(CS s, "\r\n")) != NULL)
- {
- log_write(0, LOG_MAIN|LOG_PANIC, "EHLO/HELO response must not contain "
- "newlines: message truncated: %s", string_printing(s));
- *ss = 0;
- }
- ptr = Ustrlen(s);
- size = ptr + 1;
- }
-
- s = string_cat(s, &size, &ptr, US"\r\n", 2);
-
- /* If we received EHLO, we must create a multiline response which includes
- the functions supported. */
-
- if (esmtp)
- {
- s[3] = '-';
-
- /* I'm not entirely happy with this, as an MTA is supposed to check
- that it has enough room to accept a message of maximum size before
- it sends this. However, there seems little point in not sending it.
- The actual size check happens later at MAIL FROM time. By postponing it
- till then, VRFY and EXPN can be used after EHLO when space is short. */
-
- if (thismessage_size_limit > 0)
- {
- sprintf(CS big_buffer, "%.3s-SIZE %d\r\n", smtp_code,
- thismessage_size_limit);
- s = string_cat(s, &size, &ptr, big_buffer, Ustrlen(big_buffer));
- }
- else
- {
- s = string_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US"-SIZE\r\n", 7);
- }
-
- /* Exim does not do protocol conversion or data conversion. It is 8-bit
- clean; if it has an 8-bit character in its hand, it just sends it. It
- cannot therefore specify 8BITMIME and remain consistent with the RFCs.
- However, some users want this option simply in order to stop MUAs
- mangling messages that contain top-bit-set characters. It is therefore
- provided as an option. */
-
- if (accept_8bitmime)
- {
- s = string_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US"-8BITMIME\r\n", 11);
- }
-
- /* Advertise ETRN if there's an ACL checking whether a host is
- permitted to issue it; a check is made when any host actually tries. */
-
- if (acl_smtp_etrn != NULL)
- {
- s = string_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US"-ETRN\r\n", 7);
- }
-
- /* Advertise EXPN if there's an ACL checking whether a host is
- permitted to issue it; a check is made when any host actually tries. */
-
- if (acl_smtp_expn != NULL)
- {
- s = string_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US"-EXPN\r\n", 7);
- }
-
- /* Exim is quite happy with pipelining, so let the other end know that
- it is safe to use it, unless advertising is disabled. */
-
- if (pipelining_enable &&
- verify_check_host(&pipelining_advertise_hosts) == OK)
- {
- s = string_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US"-PIPELINING\r\n", 13);
- sync_cmd_limit = NON_SYNC_CMD_PIPELINING;
- pipelining_advertised = TRUE;
- }
-
-
- /* If any server authentication mechanisms are configured, advertise
- them if the current host is in auth_advertise_hosts. The problem with
- advertising always is that some clients then require users to
- authenticate (and aren't configurable otherwise) even though it may not
- be necessary (e.g. if the host is in host_accept_relay).
-
- RFC 2222 states that SASL mechanism names contain only upper case
- 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);
- }
- }
-
- /* 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. */
-
- #ifdef SUPPORT_TLS
- if (tls_in.active < 0 &&
- verify_check_host(&tls_advertise_hosts) != FAIL)
- {
- s = string_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US"-STARTTLS\r\n", 11);
- tls_advertised = TRUE;
- }
- #endif
-
- #ifdef EXPERIMENTAL_PRDR
- /* Per Recipient Data Response, draft by Eric A. Hall extending RFC */
- if (prdr_enable) {
- s = string_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US"-PRDR\r\n", 7);
- }
- #endif
-
- /* Finish off the multiline reply with one that is always available. */
-
- s = string_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US" HELP\r\n", 7);
- }
-
- /* Terminate the string (for debug), write it, and note that HELO/EHLO
- has been seen. */
-
- s[ptr] = 0;
-
- #ifdef SUPPORT_TLS
- if (tls_in.active >= 0) (void)tls_write(TRUE, s, ptr); else
- #endif
-
- {
- int i = fwrite(s, 1, ptr, smtp_out); i = i; /* compiler quietening */
- }
- 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;
-
- /* Reset the protocol and the state, abandoning any previous message. */
-
- received_protocol = (esmtp?
- protocols[pextend +
- ((sender_host_authenticated != NULL)? pauthed : 0) +
- ((tls_in.active >= 0)? pcrpted : 0)]
- :
- protocols[pnormal + ((tls_in.active >= 0)? pcrpted : 0)])
- +
- ((sender_host_address != NULL)? pnlocal : 0);
-
- smtp_reset(reset_point);
- toomany = FALSE;
- break; /* HELO/EHLO */