X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Facl.c;h=5cd0c3507f9c99b8d99fc228c88ad2bcf77488da;hp=b93ac6965ab1b95e17241c3834bd0dd9a0e76d5f;hb=3e8abda0fa92b78c4a3dfbad940b12fc90c241e3;hpb=ef8406816ea0fc82b5d80009b30cb83ad9af6f2f diff --git a/src/src/acl.c b/src/src/acl.c index b93ac6965..5cd0c3507 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Code for handling Access Control Lists (ACLs) */ @@ -173,9 +173,11 @@ enum { #ifndef DISABLE_DKIM CONTROL_DKIM_VERIFY, #endif + CONTROL_DSCP, CONTROL_ERROR, CONTROL_CASEFUL_LOCAL_PART, CONTROL_CASELOWER_LOCAL_PART, + CONTROL_CUTTHROUGH_DELIVERY, CONTROL_ENFORCE_SYNC, CONTROL_NO_ENFORCE_SYNC, CONTROL_FREEZE, @@ -207,9 +209,11 @@ static uschar *controls[] = { #ifndef DISABLE_DKIM US"dkim_disable_verify", #endif + US"dscp", US"error", US"caseful_local_part", US"caselower_local_part", + US"cutthrough_delivery", US"enforce_sync", US"no_enforce_sync", US"freeze", @@ -232,7 +236,7 @@ at the outer level. In the other cases, expansion already occurs in the checking functions. */ static uschar cond_expand_at_top[] = { - TRUE, /* acl */ + FALSE, /* acl */ TRUE, /* add_header */ FALSE, /* authenticated */ #ifdef EXPERIMENTAL_BRIGHTMAIL @@ -524,6 +528,10 @@ static unsigned int control_forbids[] = { (1<value) test whether it was successful or not. (This is for optional verification; for mandatory verification, the connection doesn't last this long.) */ - if (tls_certificate_verified) return OK; + if (tls_in.certificate_verified) return OK; *user_msgptr = US"no verified certificate"; return FAIL; @@ -2772,14 +2785,14 @@ for (; cb != NULL; cb = cb->next) "discard" verb. */ case ACLC_ACL: - rc = acl_check_internal(where, addr, arg, level+1, user_msgptr, log_msgptr); - if (rc == DISCARD && verb != ACL_ACCEPT && verb != ACL_DISCARD) - { - *log_msgptr = string_sprintf("nested ACL returned \"discard\" for " - "\"%s\" command (only allowed with \"accept\" or \"discard\")", - verbs[verb]); - return ERROR; - } + rc = acl_check_wargs(where, addr, arg, level+1, user_msgptr, log_msgptr); + if (rc == DISCARD && verb != ACL_ACCEPT && verb != ACL_DISCARD) + { + *log_msgptr = string_sprintf("nested ACL returned \"discard\" for " + "\"%s\" command (only allowed with \"accept\" or \"discard\")", + verbs[verb]); + return ERROR; + } break; case ACLC_AUTHENTICATED: @@ -2847,6 +2860,46 @@ for (; cb != NULL; cb = cb->next) break; #endif + case CONTROL_DSCP: + if (*p == '/') + { + int fd, af, level, optname, value; + /* If we are acting on stdin, the setsockopt may fail if stdin is not + a socket; we can accept that, we'll just debug-log failures anyway. */ + fd = fileno(smtp_in); + af = ip_get_address_family(fd); + if (af < 0) + { + HDEBUG(D_acl) + debug_printf("smtp input is probably not a socket [%s], not setting DSCP\n", + strerror(errno)); + break; + } + if (dscp_lookup(p+1, af, &level, &optname, &value)) + { + if (setsockopt(fd, level, optname, &value, sizeof(value)) < 0) + { + HDEBUG(D_acl) debug_printf("failed to set input DSCP[%s]: %s\n", + p+1, strerror(errno)); + } + else + { + HDEBUG(D_acl) debug_printf("set input DSCP to \"%s\"\n", p+1); + } + } + else + { + *log_msgptr = string_sprintf("unrecognised DSCP value in \"control=%s\"", arg); + return ERROR; + } + } + else + { + *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg); + return ERROR; + } + break; + case CONTROL_ERROR: return ERROR; @@ -2986,6 +3039,20 @@ for (; cb != NULL; cb = cb->next) case CONTROL_SUPPRESS_LOCAL_FIXUPS: suppress_local_fixups = TRUE; break; + + case CONTROL_CUTTHROUGH_DELIVERY: + if (deliver_freeze) + { + *log_msgptr = string_sprintf("\"control=%s\" on frozen item", arg); + return ERROR; + } + if (queue_only_policy) + { + *log_msgptr = string_sprintf("\"control=%s\" on queue-only item", arg); + return ERROR; + } + cutthrough_delivery = TRUE; + break; } break; @@ -3099,11 +3166,11 @@ for (; cb != NULL; cb = cb->next) writing is poorly documented. */ case ACLC_ENCRYPTED: - if (tls_cipher == NULL) rc = FAIL; else + if (tls_in.cipher == NULL) rc = FAIL; else { uschar *endcipher = NULL; - uschar *cipher = Ustrchr(tls_cipher, ':'); - if (cipher == NULL) cipher = tls_cipher; else + uschar *cipher = Ustrchr(tls_in.cipher, ':'); + if (cipher == NULL) cipher = tls_in.cipher; else { endcipher = Ustrchr(++cipher, ':'); if (endcipher != NULL) *endcipher = 0; @@ -3796,6 +3863,48 @@ return FAIL; } + + +/* Same args as acl_check_internal() above, but the string s is +the name of an ACL followed optionally by up to 9 space-separated arguments. +The name and args are separately expanded. Args go into $acl_arg globals. */ +static int +acl_check_wargs(int where, address_item *addr, uschar *s, int level, + uschar **user_msgptr, uschar **log_msgptr) +{ +uschar * tmp; +uschar * tmp_arg[9]; /* must match acl_arg[] */ +uschar * name; +int i; + +if (!(tmp = string_dequote(&s)) || !(name = expand_string(tmp))) + goto bad; + +for (i = 0; i < 9; i++) + { + while (*s && isspace(*s)) s++; + if (!*s) break; + if (!(tmp = string_dequote(&s)) || !(tmp_arg[i] = expand_string(tmp))) + { + tmp = name; + goto bad; + } + } +acl_narg = i; +for (i = 0; i < acl_narg; i++) acl_arg[i] = tmp_arg[i]; +while (i < 9) acl_arg[i++] = NULL; + +return acl_check_internal(where, addr, name, level, user_msgptr, log_msgptr); + +bad: +if (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; +} + + + /************************************************* * Check access using an ACL * *************************************************/ @@ -3848,6 +3957,50 @@ if (where == ACL_WHERE_RCPT) rc = acl_check_internal(where, addr, s, 0, user_msgptr, log_msgptr); +/* Cutthrough - if requested, +and WHERE_RCPT and not yet opened conn as result of recipient-verify, +and rcpt acl returned accept, +and first recipient (cancel on any subsequents) +open one now and run it up to RCPT acceptance. +A failed verify should cancel cutthrough request. + +Initial implementation: dual-write to spool. +Assume the rxd datastream is now being copied byte-for-byte to an open cutthrough connection. + +Cease cutthrough copy on rxd final dot; do not send one. + +On a data acl, if not accept and a cutthrough conn is open, hard-close it (no SMTP niceness). + +On data acl accept, terminate the dataphase on an open cutthrough conn. If accepted or +perm-rejected, reflect that to the original sender - and dump the spooled copy. +If temp-reject, close the conn (and keep the spooled copy). +If conn-failure, no action (and keep the spooled copy). +*/ +switch (where) +{ +case ACL_WHERE_RCPT: + if( rcpt_count > 1 ) + cancel_cutthrough_connection("more than one recipient"); + else if (rc == OK && cutthrough_delivery && cutthrough_fd < 0) + open_cutthrough_connection(addr); + break; + +case ACL_WHERE_PREDATA: + if( rc == OK ) + cutthrough_predata(); + else + cancel_cutthrough_connection("predata acl not ok"); + break; + +case ACL_WHERE_QUIT: +case ACL_WHERE_NOTQUIT: + cancel_cutthrough_connection("quit or notquit"); + break; + +default: + break; +} + deliver_domain = deliver_localpart = deliver_address_data = sender_address_data = NULL;