X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/6372d4c990f39ba6ad84a91af0a3a61a63bd50a3..a310a8d09c56e6049714ae4e4070c16ecb6aa2b1:/src/src/acl.c diff --git a/src/src/acl.c b/src/src/acl.c index 35d955da6..ac2d39c0c 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -367,6 +367,9 @@ enum { CONTROL_NO_PIPELINING, CONTROL_QUEUE_ONLY, +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS) + CONTROL_REQUIRETLS, +#endif CONTROL_SUBMISSION, CONTROL_SUPPRESS_LOCAL_FIXUPS, #ifdef SUPPORT_I18N @@ -510,6 +513,18 @@ static control_def controls_list[] = { // ACL_BIT_PRDR| /* Not allow one user to freeze for all */ ACL_BIT_NOTSMTP | ACL_BIT_MIME) }, + + +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS) +[CONTROL_REQUIRETLS] = + { US"requiretls", FALSE, + (unsigned) + ~(ACL_BIT_MAIL | ACL_BIT_RCPT | ACL_BIT_PREDATA | + ACL_BIT_DATA | ACL_BIT_MIME | + ACL_BIT_NOTSMTP) + }, +#endif + [CONTROL_SUBMISSION] = { US"submission", TRUE, (unsigned) @@ -628,8 +643,7 @@ Returns: index of a control entry, or -1 if not found static int find_control(const uschar * name, control_def * ol, int last) { -int first = 0; -while (last > first) +for (int first = 0; last > first; ) { int middle = (first + last)/2; uschar * s = ol[middle].name; @@ -660,8 +674,7 @@ Returns: offset in list, or -1 if not found static int acl_checkcondition(uschar * name, condition_def * list, int end) { -int start = 0; -while (start < end) +for (int start = 0; start < end; ) { int mid = (start + end)/2; int c = Ustrcmp(name, list[mid].name); @@ -690,9 +703,7 @@ Returns: offset in list, or -1 if not found static int acl_checkname(uschar *name, uschar **list, int end) { -int start = 0; - -while (start < end) +for (int start = 0; start < end; ) { int mid = (start + end)/2; int c = Ustrcmp(name, list[mid]); @@ -730,7 +741,7 @@ acl_block **lastp = &yield; acl_block *this = NULL; acl_condition_block *cond; acl_condition_block **condp = NULL; -uschar *s; +uschar * s; *error = NULL; @@ -1042,33 +1053,15 @@ uschar * fn_hdrs_added(void) { gstring * g = NULL; -header_line * h = acl_added_headers; -uschar * s; -uschar * cp; -if (!h) return NULL; - -do +for (header_line * h = acl_added_headers; h; h = h->next) { - s = h->text; - while ((cp = Ustrchr(s, '\n')) != NULL) - { - if (cp[1] == '\0') break; - - /* contains embedded newline; needs doubling */ - g = string_catn(g, s, cp-s+1); - g = string_catn(g, US"\n", 1); - s = cp+1; - } - /* last bit of header */ - -/*XXX could we use add_listele? */ - g = string_catn(g, s, cp-s+1); /* newline-sep list */ + int i = h->slen; + if (h->text[i-1] == '\n') i--; + g = string_append_listele_n(g, '\n', h->text, i); } -while((h = h->next)); -g->s[g->ptr - 1] = '\0'; /* overwrite last newline */ -return g->s; +return g ? g->s : NULL; } @@ -1087,7 +1080,7 @@ Returns: nothing static void setup_remove_header(const uschar *hnames) { -if (*hnames != 0) +if (*hnames) acl_removed_headers = acl_removed_headers ? string_sprintf("%s : %s", acl_removed_headers, hnames) : string_copy(hnames); @@ -1136,10 +1129,10 @@ if (log_message != NULL && log_message != user_message) /* Search previously logged warnings. They are kept in malloc store so they can be freed at the start of a new message. */ - for (logged = acl_warn_logged; logged != NULL; logged = logged->next) + for (logged = acl_warn_logged; logged; logged = logged->next) if (Ustrcmp(logged->text, text) == 0) break; - if (logged == NULL) + if (!logged) { int length = Ustrlen(text) + 1; log_write(0, LOG_MAIN, "%s", text); @@ -1153,7 +1146,7 @@ if (log_message != NULL && log_message != user_message) /* If there's no user message, we are done. */ -if (user_message == NULL) return; +if (!user_message) return; /* If this isn't a message ACL, we can't do anything with a user message. Log an error. */ @@ -1218,11 +1211,10 @@ HDEBUG(D_acl) if ((rc = host_name_lookup()) != OK) { - *log_msgptr = (rc == DEFER)? - US"host lookup deferred for reverse lookup check" - : - string_sprintf("host lookup failed for reverse lookup check%s", - host_lookup_msg); + *log_msgptr = rc == DEFER + ? US"host lookup deferred for reverse lookup check" + : string_sprintf("host lookup failed for reverse lookup check%s", + host_lookup_msg); return rc; /* DEFER or FAIL */ } @@ -1260,13 +1252,10 @@ static int acl_verify_csa_address(dns_answer *dnsa, dns_scan *dnss, int reset, uschar *target) { -dns_record *rr; -dns_address *da; +int rc = CSA_FAIL_NOADDR; -BOOL target_found = FALSE; - -for (rr = dns_next_rr(dnsa, dnss, reset); - rr != NULL; +for (dns_record * rr = dns_next_rr(dnsa, dnss, reset); + rr; rr = dns_next_rr(dnsa, dnss, RESET_NEXT)) { /* Check this is an address RR for the target hostname. */ @@ -1279,12 +1268,12 @@ for (rr = dns_next_rr(dnsa, dnss, reset); if (strcmpic(target, rr->name) != 0) continue; - target_found = TRUE; + rc = CSA_FAIL_MISMATCH; /* Turn the target address RR into a list of textual IP addresses and scan the list. There may be more than one if it is an A6 RR. */ - for (da = dns_address_from_rr(dnsa, rr); da != NULL; da = da->next) + for (dns_address * da = dns_address_from_rr(dnsa, rr); da; da = da->next) { /* If the client IP address matches the target IP address, it's good! */ @@ -1298,8 +1287,7 @@ for (rr = dns_next_rr(dnsa, dnss, reset); using an unauthorized IP address, otherwise the target has no authorized IP addresses. */ -if (target_found) return CSA_FAIL_MISMATCH; -else return CSA_FAIL_NOADDR; +return rc; } @@ -1458,7 +1446,7 @@ for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); /* If we didn't break the loop then no appropriate records were found. */ -if (rr == NULL) return t->data.val = CSA_UNKNOWN; +if (!rr) return t->data.val = CSA_UNKNOWN; /* Do not check addresses if the target is ".", in accordance with RFC 2782. A target of "." indicates there are no valid addresses, so the client cannot @@ -1631,7 +1619,7 @@ if (!ss) goto BAD_VERIFY; /* Handle name/address consistency verification in a separate function. */ -for (vp= verify_type_list; +for (vp = verify_type_list; CS vp < CS verify_type_list + sizeof(verify_type_list); vp++ ) @@ -1676,8 +1664,8 @@ switch(vp->value) /* We can test the result of optional HELO verification that might have occurred earlier. If not, we can attempt the verification now. */ - if (!helo_verified && !helo_verify_failed) smtp_verify_helo(); - return helo_verified ? OK : FAIL; + if (!f.helo_verified && !f.helo_verify_failed) smtp_verify_helo(); + return f.helo_verified ? OK : FAIL; case VERIFY_CSA: /* Do Client SMTP Authorization checks in a separate function, and turn the @@ -1777,8 +1765,7 @@ switch(vp->value) /* Remaining items are optional; they apply to sender and recipient verification, including "header sender" verification. */ -while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)) - != NULL) +while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) { if (strcmpic(ss, US"defer_ok") == 0) defer_ok = TRUE; else if (strcmpic(ss, US"no_details") == 0) no_details = TRUE; @@ -1811,10 +1798,10 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)) { const uschar * sublist = ss; int optsep = ','; - uschar *opt; uschar buffer[256]; - while (isspace(*sublist)) sublist++; + uschar * opt; + while (isspace(*sublist)) sublist++; while ((opt = string_nextinlist(&sublist, &optsep, buffer, sizeof(buffer)))) { callout_opt_t * op; @@ -1909,7 +1896,7 @@ if (verify_header_sender) { if (!*user_msgptr && *log_msgptr) *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); - if (rc == DEFER) acl_temp_details = TRUE; + if (rc == DEFER) f.acl_temp_details = TRUE; } } } @@ -2062,7 +2049,7 @@ else addr2.user_message : addr2.message; /* Allow details for temporary error if the address is so flagged. */ - if (testflag((&addr2), af_pass_message)) acl_temp_details = TRUE; + if (testflag((&addr2), af_pass_message)) f.acl_temp_details = TRUE; /* Make $address_data visible */ deliver_address_data = addr2.prop.address_data; @@ -2167,8 +2154,6 @@ Arguments: log_msgptr for error messages format format string ... supplementary arguments - ss ratelimit option name - where ACL_WHERE_xxxx indicating which ACL this is Returns: ERROR */ @@ -2177,14 +2162,15 @@ static int ratelimit_error(uschar **log_msgptr, const char *format, ...) { va_list ap; -uschar buffer[STRING_SPRINTF_BUFFER_SIZE]; +gstring * g = + string_cat(NULL, US"error in arguments to \"ratelimit\" condition: "); + va_start(ap, format); -if (!string_vformat(buffer, sizeof(buffer), format, ap)) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, - "string_sprintf expansion was longer than " SIZE_T_FMT, sizeof(buffer)); +g = string_vformat(g, TRUE, format, ap); va_end(ap); -*log_msgptr = string_sprintf( - "error in arguments to \"ratelimit\" condition: %s", buffer); + +gstring_reset_unused(g); +*log_msgptr = string_from_gstring(g); return ERROR; } @@ -2340,7 +2326,7 @@ if (leaky + strict + readonly > 1) return ratelimit_error(log_msgptr, "conflicting update modes"); if (badacl && (leaky || strict) && !noupdate) return ratelimit_error(log_msgptr, - "\"%s\" must not have /leaky or /strict option in %s ACL", + "\"%s\" must not have /leaky or /strict option, or cannot be used in %s ACL", ratelimit_option_string[mode], acl_wherenames[where]); /* Set the default values of any unset options. In readonly mode we @@ -2865,7 +2851,7 @@ int rc = OK; int sep = -'/'; #endif -for (; cb != NULL; cb = cb->next) +for (; cb; cb = cb->next) { const uschar *arg; int control_type; @@ -2904,10 +2890,10 @@ for (; cb != NULL; cb = cb->next) arg = cb->arg; else if (!(arg = expand_string(cb->arg))) { - if (expand_string_forcedfail) continue; + if (f.expand_string_forcedfail) continue; *log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s", cb->arg, expand_string_message); - return search_find_defer ? DEFER : ERROR; + return f.search_find_defer ? DEFER : ERROR; } /* Show condition, and expanded condition if it's different */ @@ -3029,7 +3015,7 @@ for (; cb != NULL; cb = cb->next) switch(control_type) { case CONTROL_AUTH_UNADVERTISED: - allow_auth_unadvertised = TRUE; + f.allow_auth_unadvertised = TRUE; break; #ifdef EXPERIMENTAL_BRIGHTMAIL @@ -3040,22 +3026,22 @@ for (; cb != NULL; cb = cb->next) #ifndef DISABLE_DKIM case CONTROL_DKIM_VERIFY: - dkim_disable_verify = TRUE; + f.dkim_disable_verify = TRUE; #ifdef EXPERIMENTAL_DMARC /* Since DKIM was blocked, skip DMARC too */ - dmarc_disable_verify = TRUE; - dmarc_enable_forensic = FALSE; + f.dmarc_disable_verify = TRUE; + f.dmarc_enable_forensic = FALSE; #endif break; #endif #ifdef EXPERIMENTAL_DMARC case CONTROL_DMARC_VERIFY: - dmarc_disable_verify = TRUE; + f.dmarc_disable_verify = TRUE; break; case CONTROL_DMARC_FORENSIC: - dmarc_enable_forensic = TRUE; + f.dmarc_enable_forensic = TRUE; break; #endif @@ -3120,24 +3106,24 @@ for (; cb != NULL; cb = cb->next) #ifdef WITH_CONTENT_SCAN case CONTROL_NO_MBOX_UNSPOOL: - no_mbox_unspool = TRUE; + f.no_mbox_unspool = TRUE; break; #endif case CONTROL_NO_MULTILINE: - no_multiline_responses = TRUE; + f.no_multiline_responses = TRUE; break; case CONTROL_NO_PIPELINING: - pipelining_enable = FALSE; + f.pipelining_enable = FALSE; break; case CONTROL_NO_DELAY_FLUSH: - disable_delay_flush = TRUE; + f.disable_delay_flush = TRUE; break; case CONTROL_NO_CALLOUT_FLUSH: - disable_callout_flush = TRUE; + f.disable_callout_flush = TRUE; break; case CONTROL_FAKEREJECT: @@ -3147,7 +3133,7 @@ for (; cb != NULL; cb = cb->next) if (*p == '/') { const uschar *pp = p + 1; - while (*pp != 0) pp++; + while (*pp) pp++; fake_response_text = expand_string(string_copyn(p+1, pp-p-1)); p = pp; } @@ -3159,7 +3145,7 @@ for (; cb != NULL; cb = cb->next) break; case CONTROL_FREEZE: - deliver_freeze = TRUE; + f.deliver_freeze = TRUE; deliver_frozen_at = time(NULL); freeze_tell = freeze_tell_config; /* Reset to configured value */ if (Ustrncmp(p, "/no_tell", 8) == 0) @@ -3176,25 +3162,30 @@ for (; cb != NULL; cb = cb->next) break; case CONTROL_QUEUE_ONLY: - queue_only_policy = TRUE; + f.queue_only_policy = TRUE; cancel_cutthrough_connection(TRUE, US"queueing forced"); break; +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS) + case CONTROL_REQUIRETLS: + tls_requiretls |= REQUIRETLS_MSG; + break; +#endif case CONTROL_SUBMISSION: originator_name = US""; - submission_mode = TRUE; + f.submission_mode = TRUE; while (*p == '/') { if (Ustrncmp(p, "/sender_retain", 14) == 0) { p += 14; - active_local_sender_retain = TRUE; - active_local_from_check = FALSE; + f.active_local_sender_retain = TRUE; + f.active_local_from_check = FALSE; } else if (Ustrncmp(p, "/domain=", 8) == 0) { const uschar *pp = p + 8; - while (*pp != 0 && *pp != '/') pp++; + while (*pp && *pp != '/') pp++; submission_domain = string_copyn(p+8, pp-p-8); p = pp; } @@ -3203,7 +3194,7 @@ for (; cb != NULL; cb = cb->next) else if (Ustrncmp(p, "/name=", 6) == 0) { const uschar *pp = p + 6; - while (*pp != 0) pp++; + while (*pp) pp++; submission_name = string_copy(parse_fix_phrase(p+6, pp-p-6, big_buffer, big_buffer_size)); p = pp; @@ -3254,7 +3245,7 @@ for (; cb != NULL; cb = cb->next) break; case CONTROL_SUPPRESS_LOCAL_FIXUPS: - suppress_local_fixups = TRUE; + f.suppress_local_fixups = TRUE; break; case CONTROL_CUTTHROUGH_DELIVERY: @@ -3271,9 +3262,9 @@ for (; cb != NULL; cb = cb->next) ignored = US"PRDR active"; else { - if (deliver_freeze) + if (f.deliver_freeze) ignored = US"frozen"; - else if (queue_only_policy) + else if (f.queue_only_policy) ignored = US"queue-only"; else if (fake_response == FAIL) ignored = US"fakereject"; @@ -3401,7 +3392,7 @@ for (; cb != NULL; cb = cb->next) else { - if (smtp_out != NULL && !disable_delay_flush) + if (smtp_out && !f.disable_delay_flush) mac_smtp_fflush(); #if !defined(NO_POLL_H) && defined (POLLRDHUP) @@ -3418,16 +3409,16 @@ for (; cb != NULL; cb = cb->next) HDEBUG(D_acl) debug_printf_indent("delay cancelled by peer close\n"); } #else - /* It appears to be impossible to detect that a TCP/IP connection has - gone away without reading from it. This means that we cannot shorten - the delay below if the client goes away, because we cannot discover - that the client has closed its end of the connection. (The connection - is actually in a half-closed state, waiting for the server to close its - end.) It would be nice to be able to detect this state, so that the - Exim process is not held up unnecessarily. However, it seems that we - can't. The poll() function does not do the right thing, and in any case - it is not always available. - */ + /* Lacking POLLRDHUP it appears to be impossible to detect that a + TCP/IP connection has gone away without reading from it. This means + that we cannot shorten the delay below if the client goes away, + because we cannot discover that the client has closed its end of the + connection. (The connection is actually in a half-closed state, + waiting for the server to close its end.) It would be nice to be able + to detect this state, so that the Exim process is not held up + unnecessarily. However, it seems that we can't. The poll() function + does not do the right thing, and in any case it is not always + available. */ while (delay > 0) delay = sleep(delay); #endif @@ -3453,9 +3444,9 @@ for (; cb != NULL; cb = cb->next) #ifdef EXPERIMENTAL_DMARC case ACLC_DMARC_STATUS: - if (!dmarc_has_been_checked) + if (!f.dmarc_has_been_checked) dmarc_process(); - dmarc_has_been_checked = TRUE; + f.dmarc_has_been_checked = TRUE; /* used long way of dmarc_exim_expand_query() in case we need more * view into the process in the future. */ rc = match_isinlist(dmarc_exim_expand_query(DMARC_VERIFY_STATUS), @@ -3519,7 +3510,7 @@ for (; cb != NULL; cb = cb->next) int logbits = 0; int sep = 0; const uschar *s = arg; - uschar *ss; + uschar * ss; while ((ss = string_nextinlist(&s, &sep, big_buffer, big_buffer_size))) { if (Ustrcmp(ss, "main") == 0) logbits |= LOG_MAIN; @@ -3563,7 +3554,6 @@ for (; cb != NULL; cb = cb->next) } while (isspace(*s)) s++; - if (logbits == 0) logbits = LOG_MAIN; log_write(0, logbits, "%s", string_printing(s)); } @@ -3574,8 +3564,8 @@ for (; cb != NULL; cb = cb->next) { /* Separate the regular expression and any optional parameters. */ const uschar * list = arg; - uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size); - uschar *opt; + uschar * ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size); + uschar * opt; BOOL defer_ok = FALSE; int timeout = 0; @@ -3769,7 +3759,7 @@ if ((BIT(rc) & msgcond[verb]) != 0) expmessage = expand_string(user_message); if (!expmessage) { - if (!expand_string_forcedfail) + if (!f.expand_string_forcedfail) log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand ACL message \"%s\": %s", user_message, expand_string_message); } @@ -3782,7 +3772,7 @@ if ((BIT(rc) & msgcond[verb]) != 0) expmessage = expand_string(log_message); if (!expmessage) { - if (!expand_string_forcedfail) + if (!f.expand_string_forcedfail) log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand ACL message \"%s\": %s", log_message, expand_string_message); } @@ -3850,7 +3840,7 @@ for(;;) if (*acl_text == 0) return NULL; /* No more data */ yield = acl_text; /* Potential data line */ - while (*acl_text != 0 && *acl_text != '\n') acl_text++; + while (*acl_text && *acl_text != '\n') acl_text++; /* If we hit the end before a newline, we have the whole logical line. If it's a comment, there's no more data to be given. Otherwise, yield it. */ @@ -3970,7 +3960,7 @@ if (acl_level == 0) { if (!(ss = expand_string(s))) { - if (expand_string_forcedfail) return OK; + if (f.expand_string_forcedfail) return OK; *log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s", s, expand_string_message); return ERROR; @@ -4045,13 +4035,13 @@ if (Ustrchr(ss, ' ') == NULL) in the ACL tree, having read it into the POOL_PERM store pool so that it persists between multiple messages. */ -if (acl == NULL) +if (!acl) { int old_pool = store_pool; if (fd >= 0) store_pool = POOL_PERM; acl = acl_read(acl_getline, log_msgptr); store_pool = old_pool; - if (acl == NULL && *log_msgptr != NULL) return ERROR; + if (!acl && *log_msgptr) return ERROR; if (fd >= 0) { tree_node *t = store_get_perm(sizeof(tree_node) + Ustrlen(ss)); @@ -4063,7 +4053,7 @@ if (acl == NULL) /* Now we have an ACL to use. It's possible it may be NULL. */ -while (acl != NULL) +while (acl) { int cond; int basic_errno = 0; @@ -4072,7 +4062,7 @@ while (acl != NULL) && (where == ACL_WHERE_QUIT || where == ACL_WHERE_NOTQUIT); *log_msgptr = *user_msgptr = NULL; - acl_temp_details = FALSE; + f.acl_temp_details = FALSE; HDEBUG(D_acl) debug_printf_indent("processing \"%s\"\n", verbs[acl->verb]); @@ -4094,12 +4084,10 @@ while (acl != NULL) { if (search_error_message != NULL && *search_error_message != 0) *log_msgptr = search_error_message; - if (smtp_return_error_details) acl_temp_details = TRUE; + if (smtp_return_error_details) f.acl_temp_details = TRUE; } else - { - acl_temp_details = TRUE; - } + f.acl_temp_details = TRUE; if (acl->verb != ACL_WARN) return DEFER; break; @@ -4155,7 +4143,7 @@ while (acl != NULL) { HDEBUG(D_acl) debug_printf_indent("end of %s: DEFER\n", acl_name); if (acl_quit_check) goto badquit; - acl_temp_details = TRUE; + f.acl_temp_details = TRUE; return DEFER; } break; @@ -4288,10 +4276,10 @@ for (i = 0; i < 9; i++) acl_arg[i] = sav_arg[i]; return ret; bad: -if (expand_string_forcedfail) return ERROR; +if (f.expand_string_forcedfail) return ERROR; *log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s", tmp, expand_string_message); -return search_find_defer?DEFER:ERROR; +return f.search_find_defer ? DEFER : ERROR; } @@ -4422,7 +4410,7 @@ switch (where) case ACL_WHERE_PRDR: #endif - if (host_checking_callout) /* -bhc mode */ + if (f.host_checking_callout) /* -bhc mode */ cancel_cutthrough_connection(TRUE, US"host-checking mode"); else if ( rc == OK @@ -4438,7 +4426,7 @@ switch (where) while (*s) s++; do --s; while (!isdigit(*s)); if (*--s && isdigit(*s) && *--s && isdigit(*s)) *user_msgptr = s; - acl_temp_details = TRUE; + f.acl_temp_details = TRUE; } else {