From: Jeremy Harris Date: Sun, 22 Apr 2012 21:23:42 +0000 (+0100) Subject: Code refactoring in acl.c (bug 1184) X-Git-Tag: exim-4_80_RC1~44 X-Git-Url: https://git.exim.org/exim.git/commitdiff_plain/8958301403f65d219d8fd5283cef525b666b4971 Code refactoring in acl.c (bug 1184) Move to a table-driven approach for the parsing of "verify =". --- diff --git a/src/src/acl.c b/src/src/acl.c index 32e4c713f..50d5d8594 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -1464,6 +1464,60 @@ switch (dns_lookup(&dnsa, target, type, NULL)) * Handle verification (address & other) * *************************************************/ +enum { VERIFY_REV_HOST_LKUP, VERIFY_CERT, VERIFY_HELO, VERIFY_CSA, VERIFY_HDR_SYNTAX, + VERIFY_NOT_BLIND, VERIFY_HDR_SNDR, VERIFY_SNDR, VERIFY_RCPT + }; +typedef struct { + uschar * name; + int value; + unsigned where_allowed; /* bitmap */ + BOOL no_options; /* Never has /option(s) following */ + unsigned alt_opt_sep; /* >0 Non-/ option separator (custom parser) */ + } verify_type_t; +static verify_type_t verify_type_list[] = { + { US"reverse_host_lookup", VERIFY_REV_HOST_LKUP, ~0, TRUE, 0 }, + { US"certificate", VERIFY_CERT, ~0, TRUE, 0 }, + { US"helo", VERIFY_HELO, ~0, TRUE, 0 }, + { US"csa", VERIFY_CSA, ~0, FALSE, 0 }, + { US"header_syntax", VERIFY_HDR_SYNTAX, (1<alt_opt_sep ? strncmpic(ss, vp->name, vp->alt_opt_sep) == 0 + : strcmpic (ss, vp->name) == 0) + break; +if ((char *)vp >= (char *)verify_type_list + sizeof(verify_type_list)) + goto BAD_VERIFY; + +if (vp->no_options && slash != NULL) { - if (slash != NULL) goto NO_OPTIONS; - if (sender_host_address == NULL) return OK; - return acl_verify_reverse(user_msgptr, log_msgptr); + *log_msgptr = string_sprintf("unexpected '/' found in \"%s\" " + "(this verify item has no options)", arg); + return ERROR; } - -/* TLS certificate verification is done at STARTTLS time; here we just -test whether it was successful or not. (This is for optional verification; for -mandatory verification, the connection doesn't last this long.) */ - -if (strcmpic(ss, US"certificate") == 0) +if (!(vp->where_allowed & (1<name, acl_wherenames[where]); + return ERROR; } - -/* We can test the result of optional HELO verification that might have -occurred earlier. If not, we can attempt the verification now. */ - -if (strcmpic(ss, US"helo") == 0) +switch(vp->value) { - if (slash != NULL) goto NO_OPTIONS; - if (!helo_verified && !helo_verify_failed) smtp_verify_helo(); - return helo_verified? OK : FAIL; - } + case VERIFY_REV_HOST_LKUP: + if (sender_host_address == NULL) return OK; + return acl_verify_reverse(user_msgptr, log_msgptr); -/* Do Client SMTP Authorization checks in a separate function, and turn the -result code into user-friendly strings. */ + case VERIFY_CERT: + /* TLS certificate verification is done at STARTTLS time; here we just + test whether it was successful or not. (This is for optional verification; for + mandatory verification, the connection doesn't last this long.) */ -if (strcmpic(ss, US"csa") == 0) - { - rc = acl_verify_csa(list); - *log_msgptr = *user_msgptr = string_sprintf("client SMTP authorization %s", - csa_reason_string[rc]); - csa_status = csa_status_string[rc]; - DEBUG(D_acl) debug_printf("CSA result %s\n", csa_status); - return csa_return_code[rc]; - } + if (tls_certificate_verified) return OK; + *user_msgptr = US"no verified certificate"; + return FAIL; -/* Check that all relevant header lines have the correct syntax. If there is -a syntax error, we return details of the error to the sender if configured to -send out full details. (But a "message" setting on the ACL can override, as -always). */ + case VERIFY_HELO: + /* We can test the result of optional HELO verification that might have + occurred earlier. If not, we can attempt the verification now. */ -if (strcmpic(ss, US"header_syntax") == 0) - { - if (slash != NULL) goto NO_OPTIONS; - if (where != ACL_WHERE_DATA && where != ACL_WHERE_NOTSMTP) goto WRONG_ACL; - rc = verify_check_headers(log_msgptr); - if (rc != OK && smtp_return_error_details && *log_msgptr != NULL) - *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); - return rc; - } + if (!helo_verified && !helo_verify_failed) smtp_verify_helo(); + return helo_verified? OK : FAIL; -/* Check that no recipient of this message is "blind", that is, every envelope -recipient must be mentioned in either To: or Cc:. */ + case VERIFY_CSA: + /* Do Client SMTP Authorization checks in a separate function, and turn the + result code into user-friendly strings. */ -if (strcmpic(ss, US"not_blind") == 0) - { - if (slash != NULL) goto NO_OPTIONS; - if (where != ACL_WHERE_DATA && where != ACL_WHERE_NOTSMTP) goto WRONG_ACL; - rc = verify_check_notblind(); - if (rc != OK) - { - *log_msgptr = string_sprintf("bcc recipient detected"); - if (smtp_return_error_details) + rc = acl_verify_csa(list); + *log_msgptr = *user_msgptr = string_sprintf("client SMTP authorization %s", + csa_reason_string[rc]); + csa_status = csa_status_string[rc]; + DEBUG(D_acl) debug_printf("CSA result %s\n", csa_status); + return csa_return_code[rc]; + + case VERIFY_HDR_SYNTAX: + /* Check that all relevant header lines have the correct syntax. If there is + a syntax error, we return details of the error to the sender if configured to + send out full details. (But a "message" setting on the ACL can override, as + always). */ + + rc = verify_check_headers(log_msgptr); + if (rc != OK && smtp_return_error_details && *log_msgptr != NULL) *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); - } - return rc; - } + return rc; -/* The remaining verification tests check recipient and sender addresses, -either from the envelope or from the header. There are a number of -slash-separated options that are common to all of them. */ + case VERIFY_NOT_BLIND: + /* Check that no recipient of this message is "blind", that is, every envelope + recipient must be mentioned in either To: or Cc:. */ + rc = verify_check_notblind(); + if (rc != OK) + { + *log_msgptr = string_sprintf("bcc recipient detected"); + if (smtp_return_error_details) + *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); + } + return rc; -/* Check that there is at least one verifiable sender address in the relevant -header lines. This can be followed by callout and defer options, just like -sender and recipient. */ + /* The remaining verification tests check recipient and sender addresses, + either from the envelope or from the header. There are a number of + slash-separated options that are common to all of them. */ -if (strcmpic(ss, US"header_sender") == 0) - { - if (where != ACL_WHERE_DATA && where != ACL_WHERE_NOTSMTP) goto WRONG_ACL; - verify_header_sender = TRUE; - } - -/* Otherwise, first item in verify argument must be "sender" or "recipient". -In the case of a sender, this can optionally be followed by an address to use -in place of the actual sender (rare special-case requirement). */ + case VERIFY_HDR_SNDR: + verify_header_sender = TRUE; + break; -else if (strncmpic(ss, US"sender", 6) == 0) - { - uschar *s = ss + 6; - if (where > ACL_WHERE_NOTSMTP) - { - *log_msgptr = string_sprintf("cannot verify sender in ACL for %s " - "(only possible for MAIL, RCPT, PREDATA, or DATA)", - acl_wherenames[where]); - return ERROR; - } - if (*s == 0) - verify_sender_address = sender_address; - else + case VERIFY_SNDR: + /* In the case of a sender, this can optionally be followed by an address to use + in place of the actual sender (rare special-case requirement). */ { - while (isspace(*s)) s++; - if (*s++ != '=') goto BAD_VERIFY; - while (isspace(*s)) s++; - verify_sender_address = string_copy(s); - } - } -else - { - if (strcmpic(ss, US"recipient") != 0) goto BAD_VERIFY; - if (addr == NULL) - { - *log_msgptr = string_sprintf("cannot verify recipient in ACL for %s " - "(only possible for RCPT)", acl_wherenames[where]); - return ERROR; + uschar *s = ss + 6; + if (*s == 0) + verify_sender_address = sender_address; + else + { + while (isspace(*s)) s++; + if (*s++ != '=') goto BAD_VERIFY; + while (isspace(*s)) s++; + verify_sender_address = string_copy(s); + } } + break; + + case VERIFY_RCPT: + break; } + + /* Remaining items are optional; they apply to sender and recipient verification, including "header sender" verification. */ @@ -1680,112 +1719,60 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)) uschar buffer[256]; while (isspace(*ss)) ss++; - /* This callout option handling code has become a mess as new options - have been added in an ad hoc manner. It should be tidied up into some - kind of table-driven thing. */ - while ((opt = string_nextinlist(&ss, &optsep, buffer, sizeof(buffer))) != NULL) { - if (strcmpic(opt, US"defer_ok") == 0) callout_defer_ok = TRUE; - else if (strcmpic(opt, US"no_cache") == 0) - verify_options |= vopt_callout_no_cache; - else if (strcmpic(opt, US"random") == 0) - verify_options |= vopt_callout_random; - else if (strcmpic(opt, US"use_sender") == 0) - verify_options |= vopt_callout_recipsender; - else if (strcmpic(opt, US"use_postmaster") == 0) - verify_options |= vopt_callout_recippmaster; - else if (strcmpic(opt, US"postmaster") == 0) pm_mailfrom = US""; - else if (strcmpic(opt, US"fullpostmaster") == 0) - { - pm_mailfrom = US""; - verify_options |= vopt_callout_fullpm; - } + callout_opt_t * op; + double period; - else if (strncmpic(opt, US"mailfrom", 8) == 0) - { - if (!verify_header_sender) - { - *log_msgptr = string_sprintf("\"mailfrom\" is allowed as a " - "callout option only for verify=header_sender (detected in ACL " - "condition \"%s\")", arg); - return ERROR; - } - opt += 8; - while (isspace(*opt)) opt++; - if (*opt++ != '=') - { - *log_msgptr = string_sprintf("'=' expected after " - "\"mailfrom\" in ACL condition \"%s\"", arg); - return ERROR; - } - while (isspace(*opt)) opt++; - se_mailfrom = string_copy(opt); - } + for (op= callout_opt_list; op->name; op++) + if (strncmpic(opt, op->name, strlen(op->name)) == 0) + break; - else if (strncmpic(opt, US"postmaster_mailfrom", 19) == 0) - { - opt += 19; + verify_options |= op->flag; + if (op->has_option) + { + opt += strlen(op->name); while (isspace(*opt)) opt++; if (*opt++ != '=') { *log_msgptr = string_sprintf("'=' expected after " - "\"postmaster_mailfrom\" in ACL condition \"%s\"", arg); - return ERROR; - } - while (isspace(*opt)) opt++; - pm_mailfrom = string_copy(opt); - } - - else if (strncmpic(opt, US"maxwait", 7) == 0) - { - opt += 7; - while (isspace(*opt)) opt++; - if (*opt++ != '=') - { - *log_msgptr = string_sprintf("'=' expected after \"maxwait\" in " - "ACL condition \"%s\"", arg); + "\"%s\" in ACL verify condition \"%s\"", op->name, arg); return ERROR; } while (isspace(*opt)) opt++; - callout_overall = readconf_readtime(opt, 0, FALSE); - if (callout_overall < 0) + } + if (op->timeval) + { + period = readconf_readtime(opt, 0, FALSE); + if (period < 0) { *log_msgptr = string_sprintf("bad time value in ACL condition " "\"verify %s\"", arg); return ERROR; } - } - else if (strncmpic(opt, US"connect", 7) == 0) - { - opt += 7; - while (isspace(*opt)) opt++; - if (*opt++ != '=') - { - *log_msgptr = string_sprintf("'=' expected after " - "\"callout_overaall\" in ACL condition \"%s\"", arg); - return ERROR; - } - while (isspace(*opt)) opt++; - callout_connect = readconf_readtime(opt, 0, FALSE); - if (callout_connect < 0) - { - *log_msgptr = string_sprintf("bad time value in ACL condition " - "\"verify %s\"", arg); - return ERROR; - } - } - else /* Plain time is callout connect/command timeout */ - { - callout = readconf_readtime(opt, 0, FALSE); - if (callout < 0) - { - *log_msgptr = string_sprintf("bad time value in ACL condition " - "\"verify %s\"", arg); - return ERROR; - } - } + } + + switch(op->value) + { + case CALLOUT_DEFER_OK: callout_defer_ok = TRUE; break; + case CALLOUT_POSTMASTER: pm_mailfrom = US""; break; + case CALLOUT_FULLPOSTMASTER: pm_mailfrom = US""; break; + case CALLOUT_MAILFROM: + if (!verify_header_sender) + { + *log_msgptr = string_sprintf("\"mailfrom\" is allowed as a " + "callout option only for verify=header_sender (detected in ACL " + "condition \"%s\")", arg); + return ERROR; + } + se_mailfrom = string_copy(opt); + break; + case CALLOUT_POSTMASTER_MAILFROM: pm_mailfrom = string_copy(opt); break; + case CALLOUT_MAXWAIT: callout_overall = period; break; + case CALLOUT_CONNECT: callout_connect = period; break; + case CALLOUT_TIME: callout = period; break; + } } } else @@ -2034,20 +2021,6 @@ BAD_VERIFY: "\"reverse_host_lookup\" at start of ACL condition " "\"verify %s\"", arg); return ERROR; - -/* Options supplied when not allowed come here */ - -NO_OPTIONS: -*log_msgptr = string_sprintf("unexpected '/' found in \"%s\" " - "(this verify item has no options)", arg); -return ERROR; - -/* Calls in the wrong ACL come here */ - -WRONG_ACL: -*log_msgptr = string_sprintf("cannot check header contents in ACL for %s " - "(only possible in ACL for DATA)", acl_wherenames[where]); -return ERROR; } diff --git a/test/log/0027 b/test/log/0027 index 280541371..9aade8869 100644 --- a/test/log/0027 +++ b/test/log/0027 @@ -2,11 +2,11 @@ 1999-03-02 09:44:33 U=CALLER sender verify fail for : Unrouteable address 1999-03-02 09:44:33 U=CALLER F= rejected RCPT : Sender verify failed 1999-03-02 09:44:33 U=CALLER F= rejected RCPT : deny for userx -1999-03-02 09:44:33 U=CALLER F=<> temporarily rejected RCPT <"deny verify = header_syntax"@test.ex>: cannot check header contents in ACL for RCPT (only possible in ACL for DATA) +1999-03-02 09:44:33 U=CALLER F=<> temporarily rejected RCPT <"deny verify = header_syntax"@test.ex>: cannot verify header_syntax in ACL for RCPT 1999-03-02 09:44:33 U=CALLER F=<> temporarily rejected RCPT <"deny verify = junk"@test.ex>: expected "sender[=address]", "recipient", "helo", "header_syntax", "header_sender" or "reverse_host_lookup" at start of ACL condition "verify junk" 1999-03-02 09:44:33 U=CALLER F=<> temporarily rejected RCPT <"deny vorify = junk"@test.ex>: unknown ACL condition/modifier in "deny vorify = junk" 1999-03-02 09:44:33 U=CALLER F=<> temporarily rejected RCPT <"dony verify = junk"@test.ex>: unknown ACL verb "dony" in "dony verify = junk" 1999-03-02 09:44:33 U=CALLER F=<> temporarily rejected RCPT <"deny !message = abcd"@test.ex>: ACL error: negation is not allowed with "message" -1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F=<> temporarily rejected after DATA: cannot verify recipient in ACL for DATA (only possible for RCPT) +1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F=<> temporarily rejected after DATA: cannot verify recipient in ACL for DATA 1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F=<> temporarily rejected after DATA: cannot test domains condition in DATA ACL 1999-03-02 09:44:33 10HmaZ-0005vi-00 U=CALLER F=<> temporarily rejected after DATA: cannot test local_parts condition in DATA ACL diff --git a/test/rejectlog/0027 b/test/rejectlog/0027 index 9a9421433..24bcc70e9 100644 --- a/test/rejectlog/0027 +++ b/test/rejectlog/0027 @@ -2,12 +2,12 @@ 1999-03-02 09:44:33 U=CALLER sender verify fail for : Unrouteable address 1999-03-02 09:44:33 U=CALLER F= rejected RCPT : Sender verify failed 1999-03-02 09:44:33 U=CALLER F= rejected RCPT : deny for userx -1999-03-02 09:44:33 U=CALLER F=<> temporarily rejected RCPT <"deny verify = header_syntax"@test.ex>: cannot check header contents in ACL for RCPT (only possible in ACL for DATA) +1999-03-02 09:44:33 U=CALLER F=<> temporarily rejected RCPT <"deny verify = header_syntax"@test.ex>: cannot verify header_syntax in ACL for RCPT 1999-03-02 09:44:33 U=CALLER F=<> temporarily rejected RCPT <"deny verify = junk"@test.ex>: expected "sender[=address]", "recipient", "helo", "header_syntax", "header_sender" or "reverse_host_lookup" at start of ACL condition "verify junk" 1999-03-02 09:44:33 U=CALLER F=<> temporarily rejected RCPT <"deny vorify = junk"@test.ex>: unknown ACL condition/modifier in "deny vorify = junk" 1999-03-02 09:44:33 U=CALLER F=<> temporarily rejected RCPT <"dony verify = junk"@test.ex>: unknown ACL verb "dony" in "dony verify = junk" 1999-03-02 09:44:33 U=CALLER F=<> temporarily rejected RCPT <"deny !message = abcd"@test.ex>: ACL error: negation is not allowed with "message" -1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F=<> temporarily rejected after DATA: cannot verify recipient in ACL for DATA (only possible for RCPT) +1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F=<> temporarily rejected after DATA: cannot verify recipient in ACL for DATA Envelope-from: <> Envelope-to: P Received: from CALLER by myhost.test.ex with local-smtp (Exim x.yz)