diff options
author | Jeremy Harris <jgh146exb@wizmail.org> | 2014-08-20 14:05:30 +0100 |
---|---|---|
committer | Jeremy Harris <jgh146exb@wizmail.org> | 2014-08-20 14:09:05 +0100 |
commit | a7538db17824b7fd70c12ef7561a67b85d6f247e (patch) | |
tree | 78e99ee1165d9a5d07d165b81c83be369bcf0d4d /src | |
parent | 9fc9357028c3ada0ca912e2f58d002d8f4c7a321 (diff) |
Expanded EXPERIMENTAL_TPDA feature
Note this introduces incompatible changes; users who are compiling
the feature in, and with configuration files using it, will need to
change their configurations appropriately. See the experimental-spec.txt
file.
Diffstat (limited to 'src')
-rw-r--r-- | src/src/acl.c | 31 | ||||
-rw-r--r-- | src/src/deliver.c | 178 | ||||
-rw-r--r-- | src/src/expand.c | 13 | ||||
-rw-r--r-- | src/src/functions.h | 8 | ||||
-rw-r--r-- | src/src/globals.c | 10 | ||||
-rw-r--r-- | src/src/globals.h | 10 | ||||
-rw-r--r-- | src/src/smtp_out.c | 14 | ||||
-rw-r--r-- | src/src/structs.h | 2 | ||||
-rw-r--r-- | src/src/tls-gnu.c | 82 | ||||
-rw-r--r-- | src/src/tls-openssl.c | 64 | ||||
-rw-r--r-- | src/src/transport.c | 4 | ||||
-rw-r--r-- | src/src/transports/smtp.c | 412 | ||||
-rw-r--r-- | src/src/transports/smtp.h | 3 | ||||
-rw-r--r-- | src/src/verify.c | 111 |
14 files changed, 575 insertions, 367 deletions
diff --git a/src/src/acl.c b/src/src/acl.c index 6e635fbf1..fe1e254bd 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -4129,7 +4129,11 @@ while (acl != NULL) switch(acl->verb) { case ACL_ACCEPT: - if (cond == OK || cond == DISCARD) return cond; + if (cond == OK || cond == DISCARD) + { + HDEBUG(D_acl) debug_printf("end of %s: ACCEPT\n", acl_name); + return cond; + } if (endpass_seen) { HDEBUG(D_acl) debug_printf("accept: endpass encountered - denying access\n"); @@ -4140,17 +4144,26 @@ while (acl != NULL) case ACL_DEFER: if (cond == OK) { + HDEBUG(D_acl) debug_printf("end of %s: DEFER\n", acl_name); acl_temp_details = TRUE; return DEFER; } break; case ACL_DENY: - if (cond == OK) return FAIL; + if (cond == OK) + { + HDEBUG(D_acl) debug_printf("end of %s: DENY\n", acl_name); + return FAIL; + } break; case ACL_DISCARD: - if (cond == OK || cond == DISCARD) return DISCARD; + if (cond == OK || cond == DISCARD) + { + HDEBUG(D_acl) debug_printf("end of %s: DISCARD\n", acl_name); + return DISCARD; + } if (endpass_seen) { HDEBUG(D_acl) debug_printf("discard: endpass encountered - denying access\n"); @@ -4159,11 +4172,19 @@ while (acl != NULL) break; case ACL_DROP: - if (cond == OK) return FAIL_DROP; + if (cond == OK) + { + HDEBUG(D_acl) debug_printf("end of %s: DROP\n", acl_name); + return FAIL_DROP; + } break; case ACL_REQUIRE: - if (cond != OK) return cond; + if (cond != OK) + { + HDEBUG(D_acl) debug_printf("end of %s: not OK\n", acl_name); + return cond; + } break; case ACL_WARN: diff --git a/src/src/deliver.c b/src/src/deliver.c index 87b54d88e..48d3fd7ec 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -141,11 +141,13 @@ the first address. */ if (addr->host_list == NULL) { deliver_host = deliver_host_address = US""; + deliver_host_port = 0; } else { deliver_host = addr->host_list->name; deliver_host_address = addr->host_list->address; + deliver_host_port = addr->host_list->port; } deliver_recipients = addr; @@ -705,6 +707,43 @@ d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr) } #endif + +#ifdef EXPERIMENTAL_TPDA +int +tpda_raise_event(uschar * action, uschar * event, uschar * ev_data) +{ +uschar * s; +if (action) + { + DEBUG(D_deliver) + debug_printf("TPDA(%s): tpda_event_action=|%s| tpda_delivery_IP=%s\n", + event, + action, deliver_host_address); + + tpda_event = event; + tpda_data = ev_data; + + if (!(s = expand_string(action)) && *expand_string_message) + log_write(0, LOG_MAIN|LOG_PANIC, + "failed to expand tpda_event_action %s in %s: %s\n", + event, transport_name, expand_string_message); + + tpda_event = tpda_data = NULL; + + /* If the expansion returns anything but an empty string, flag for + the caller to modify his normal processing + */ + if (s && *s) + { + DEBUG(D_deliver) + debug_printf("TPDA(%s): event_action returned \"%s\"\n", s); + return DEFER; + } + } +return OK; +} +#endif + /* If msg is NULL this is a delivery log and logchar is used. Otherwise this is a nonstandard call; no two-character delivery flag is written but sender-host and sender are prefixed and "msg" is inserted in the log line. @@ -728,12 +767,7 @@ have a pointer to the host item that succeeded; local deliveries can have a pointer to a single host item in their host list, for use by the transport. */ #ifdef EXPERIMENTAL_TPDA - tpda_delivery_ip = NULL; /* presume no successful remote delivery */ - tpda_delivery_port = 0; - tpda_delivery_fqdn = NULL; - tpda_delivery_local_part = NULL; - tpda_delivery_domain = NULL; - tpda_delivery_confirmation = NULL; + /* presume no successful remote delivery */ lookup_dnssec_authenticated = NULL; #endif @@ -782,13 +816,8 @@ if ((log_extra_selector & LX_delivery_size) != 0) if (addr->transport->info->local) { - if (addr->host_list != NULL) - { + if (addr->host_list) s = string_append(s, &size, &ptr, 2, US" H=", addr->host_list->name); - #ifdef EXPERIMENTAL_TPDA - tpda_delivery_fqdn = addr->host_list->name; - #endif - } if (addr->shadow_message != NULL) s = string_cat(s, &size, &ptr, addr->shadow_message, Ustrlen(addr->shadow_message)); @@ -804,24 +833,20 @@ else if (continue_sequence > 1) s = string_cat(s, &size, &ptr, US"*", 1); - #ifdef EXPERIMENTAL_TPDA - tpda_delivery_ip = addr->host_used->address; - tpda_delivery_port = addr->host_used->port; - tpda_delivery_fqdn = addr->host_used->name; - tpda_delivery_local_part = addr->local_part; - tpda_delivery_domain = addr->domain; - tpda_delivery_confirmation = addr->message; +#ifdef EXPERIMENTAL_TPDA + deliver_host_address = addr->host_used->address; + deliver_host_port = addr->host_used->port; /* DNS lookup status */ lookup_dnssec_authenticated = addr->host_used->dnssec==DS_YES ? US"yes" : addr->host_used->dnssec==DS_NO ? US"no" : NULL; - #endif +#endif } - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS s = d_tlslog(s, &size, &ptr, addr); - #endif +#endif if (addr->authenticator) { @@ -834,10 +859,10 @@ else } } - #ifndef DISABLE_PRDR +#ifndef DISABLE_PRDR if (addr->flags & af_prdr_used) s = string_append(s, &size, &ptr, 1, US" PRDR"); - #endif +#endif } /* confirmation message (SMTP (host_used) and LMTP (driver_name)) */ @@ -877,19 +902,22 @@ s[ptr] = 0; log_write(0, flags, "%s", s); #ifdef EXPERIMENTAL_TPDA -if (addr->transport->tpda_delivery_action) { - DEBUG(D_deliver) - debug_printf(" TPDA(Delivery): tpda_deliver_action=|%s| tpda_delivery_IP=%s\n", - addr->transport->tpda_delivery_action, tpda_delivery_ip); - - router_name = addr->router->name; - transport_name = addr->transport->name; - if (!expand_string(addr->transport->tpda_delivery_action) && *expand_string_message) - log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand tpda_deliver_action in %s: %s\n", - transport_name, expand_string_message); - router_name = NULL; - transport_name = NULL; + uschar * save_domain = deliver_domain; + uschar * save_local = deliver_localpart; + + router_name = addr->router ? addr->router->name : NULL; + transport_name = addr->transport ? addr->transport->name : NULL; + deliver_domain = addr->domain; + deliver_localpart = addr->local_part; + + (void) tpda_raise_event(addr->transport->tpda_event_action, US"msg:delivery", + addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0 + ? addr->message : NULL); + + deliver_localpart = save_local; + deliver_domain = save_domain; + router_name = transport_name = NULL; } #endif store_reset(reset_point); @@ -1089,7 +1117,7 @@ if (result == OK) } /* Certificates for logging (via TPDA) */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS tls_out.ourcert = addr->ourcert; addr->ourcert = NULL; tls_out.peercert = addr->peercert; @@ -1098,11 +1126,11 @@ if (result == OK) tls_out.cipher = addr->cipher; tls_out.peerdn = addr->peerdn; tls_out.ocsp = addr->ocsp; - #endif +#endif delivery_log(LOG_MAIN, addr, logchar, NULL); - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (tls_out.ourcert) { tls_free_cert(tls_out.ourcert); @@ -1116,7 +1144,7 @@ if (result == OK) tls_out.cipher = NULL; tls_out.peerdn = NULL; tls_out.ocsp = OCSP_NOT_REQ; - #endif +#endif } @@ -1297,9 +1325,9 @@ else if (addr->host_used != NULL) s = d_hostlog(s, &size, &ptr, addr); - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS s = d_tlslog(s, &size, &ptr, addr); - #endif +#endif if (addr->basic_errno > 0) s = string_append(s, &size, &ptr, 2, US": ", @@ -1888,19 +1916,19 @@ if ((pid = fork()) == 0) diagnosis that it's reasonable to make them something that has to be explicitly requested. */ - #ifdef RLIMIT_CORE +#ifdef RLIMIT_CORE struct rlimit rl; rl.rlim_cur = 0; rl.rlim_max = 0; if (setrlimit(RLIMIT_CORE, &rl) < 0) { - #ifdef SETRLIMIT_NOT_SUPPORTED +# ifdef SETRLIMIT_NOT_SUPPORTED if (errno != ENOSYS && errno != ENOTSUP) - #endif +# endif log_write(0, LOG_MAIN|LOG_PANIC, "setrlimit(RLIMIT_CORE) failed: %s", strerror(errno)); } - #endif +#endif /* Reset the random number generator, so different processes don't all have the same sequence. */ @@ -2987,7 +3015,7 @@ while (!done) it in with the other info, in order to keep each message short enough to guarantee it won't be split in the pipe. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS case 'X': if (addr == NULL) goto ADDR_MISMATCH; /* Below, in 'A' handler */ switch (*ptr++) @@ -3015,17 +3043,17 @@ while (!done) (void) tls_import_cert(ptr, &addr->ourcert); break; - #ifndef DISABLE_OCSP +# ifndef DISABLE_OCSP case '4': addr->ocsp = OCSP_NOT_REQ; if (*ptr) addr->ocsp = *ptr - '0'; break; - #endif +# endif } while (*ptr++); break; - #endif /*SUPPORT_TLS*/ +#endif /*SUPPORT_TLS*/ case 'C': /* client authenticator information */ switch (*ptr++) @@ -3049,14 +3077,14 @@ while (!done) break; #endif - #ifdef EXPERIMENTAL_DSN +#ifdef EXPERIMENTAL_DSN case 'D': if (addr == NULL) break; memcpy(&(addr->dsn_aware), ptr, sizeof(addr->dsn_aware)); ptr += sizeof(addr->dsn_aware); DEBUG(D_deliver) debug_printf("DSN read: addr->dsn_aware = %d\n", addr->dsn_aware); break; - #endif +#endif case 'A': if (addr == NULL) @@ -3954,11 +3982,11 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) that it can use either of them, though it prefers O_NONBLOCK, which distinguishes between EOF and no-more-data. */ - #ifdef O_NONBLOCK +#ifdef O_NONBLOCK (void)fcntl(pfd[pipe_read], F_SETFL, O_NONBLOCK); - #else +#else (void)fcntl(pfd[pipe_read], F_SETFL, O_NDELAY); - #endif +#endif /* If the maximum number of subprocesses already exist, wait for a process to finish. If we ran out of file descriptors, parmax will have been reduced @@ -4127,7 +4155,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) if (tls_out.certificate_verified) setflag(addr, af_cert_verified); /* Use an X item only if there's something to send */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (addr->cipher) { ptr = big_buffer; @@ -4163,7 +4191,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) *ptr++ = 0; rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); } - #ifndef DISABLE_OCSP +# ifndef DISABLE_OCSP if (addr->ocsp > OCSP_NOT_REQ) { ptr = big_buffer; @@ -4171,8 +4199,8 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) while(*ptr++); rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); } - # endif - #endif /*SUPPORT_TLS*/ +# endif +#endif /*SUPPORT_TLS*/ if (client_authenticator) { @@ -4196,17 +4224,17 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); } - #ifndef DISABLE_PRDR +#ifndef DISABLE_PRDR if (addr->flags & af_prdr_used) rmt_dlv_checked_write(fd, "P", 1); - #endif +#endif - #ifdef EXPERIMENTAL_DSN +#ifdef EXPERIMENTAL_DSN big_buffer[0] = 'D'; memcpy(big_buffer+1, &addr->dsn_aware, sizeof(addr->dsn_aware)); rmt_dlv_checked_write(fd, big_buffer, sizeof(addr->dsn_aware) + 1); DEBUG(D_deliver) debug_printf("DSN write: addr->dsn_aware = %d\n", addr->dsn_aware); - #endif +#endif /* Retry information: for most success cases this will be null. */ @@ -4921,7 +4949,7 @@ attempted. */ if (deliver_freeze) { - #ifdef SUPPORT_MOVE_FROZEN_MESSAGES +#ifdef SUPPORT_MOVE_FROZEN_MESSAGES /* Moving to another directory removes the message from Exim's view. Other tools must be used to deal with it. Logging of this action happens in spool_move_message() and its subfunctions. */ @@ -4929,7 +4957,7 @@ if (deliver_freeze) if (move_frozen_messages && spool_move_message(id, message_subdir, US"", US"F")) return continue_closedown(); /* yields DELIVER_NOT_ATTEMPTED */ - #endif +#endif /* For all frozen messages (bounces or not), timeout_frozen_after sets the maximum time to keep messages that are frozen. Thaw if we reach it, with a @@ -5358,13 +5386,13 @@ if (process_recipients != RECIP_IGNORE) if (r->pno >= 0) new->onetime_parent = recipients_list[r->pno].address; - #ifdef EXPERIMENTAL_DSN +#ifdef EXPERIMENTAL_DSN /* If DSN support is enabled, set the dsn flags and the original receipt to be passed on to other DSN enabled MTAs */ new->dsn_flags = r->dsn_flags & rf_dsnflags; new->dsn_orcpt = r->orcpt; DEBUG(D_deliver) debug_printf("DSN: set orcpt: %s flags: %d\n", new->dsn_orcpt, new->dsn_flags); - #endif +#endif switch (process_recipients) { @@ -6300,21 +6328,21 @@ if (addr_remote != NULL) regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)", FALSE, TRUE); - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (regex_STARTTLS == NULL) regex_STARTTLS = regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE); - #endif +#endif - #ifndef DISABLE_PRDR +#ifndef DISABLE_PRDR if (regex_PRDR == NULL) regex_PRDR = regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE); - #endif +#endif - #ifdef EXPERIMENTAL_DSN +#ifdef EXPERIMENTAL_DSN /* Set the regex to check for DSN support on remote MTA */ if (regex_DSN == NULL) regex_DSN = regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE); - #endif +#endif /* Now sort the addresses if required, and do the deliveries. The yield of do_remote_deliveries is FALSE when mua_wrapper is set and all addresses @@ -7653,10 +7681,10 @@ if (remove_journal) /* Move the message off the spool if reqested */ - #ifdef SUPPORT_MOVE_FROZEN_MESSAGES +#ifdef SUPPORT_MOVE_FROZEN_MESSAGES if (deliver_freeze && move_frozen_messages) (void)spool_move_message(id, message_subdir, US"", US"F"); - #endif +#endif } /* Closing the data file frees the lock; if the file has been unlinked it diff --git a/src/src/expand.c b/src/src/expand.c index e3e1c7833..f6b70cb47 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -501,6 +501,7 @@ static var_entry var_table[] = { { "host_data", vtype_stringptr, &host_data }, { "host_lookup_deferred",vtype_int, &host_lookup_deferred }, { "host_lookup_failed", vtype_int, &host_lookup_failed }, + { "host_port", vtype_int, &deliver_host_port }, { "inode", vtype_ino, &deliver_inode }, { "interface_address", vtype_stringptr, &interface_address }, { "interface_port", vtype_int, &interface_port }, @@ -706,14 +707,12 @@ static var_entry var_table[] = { { "tod_zone", vtype_todzone, NULL }, { "tod_zulu", vtype_todzulu, NULL }, #ifdef EXPERIMENTAL_TPDA + { "tpda_data", vtype_stringptr, &tpda_data }, + + /*XXX want to use generic vars for as many of these as possible*/ { "tpda_defer_errno", vtype_int, &tpda_defer_errno }, - { "tpda_defer_errstr", vtype_stringptr, &tpda_defer_errstr }, - { "tpda_delivery_confirmation", vtype_stringptr, &tpda_delivery_confirmation }, - { "tpda_delivery_domain", vtype_stringptr, &tpda_delivery_domain }, - { "tpda_delivery_fqdn", vtype_stringptr, &tpda_delivery_fqdn }, - { "tpda_delivery_ip", vtype_stringptr, &tpda_delivery_ip }, - { "tpda_delivery_local_part",vtype_stringptr,&tpda_delivery_local_part }, - { "tpda_delivery_port", vtype_int, &tpda_delivery_port }, + + { "tpda_event", vtype_stringptr, &tpda_event }, #endif { "transport_name", vtype_stringptr, &transport_name }, { "value", vtype_stringptr, &lookup_value }, diff --git a/src/src/functions.h b/src/src/functions.h index a6257a913..fee4429a5 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -44,7 +44,7 @@ extern uschar * tls_cert_fprt_sha1(void *); extern uschar * tls_cert_fprt_sha256(void *); extern int tls_client_start(int, host_item *, address_item *, - void *); + transport_instance *); extern void tls_close(BOOL, BOOL); extern int tls_export_cert(uschar *, size_t, void *); extern int tls_feof(void); @@ -337,7 +337,11 @@ extern int sieve_interpret(uschar *, int, uschar *, uschar *, uschar *, extern void sigalrm_handler(int); extern BOOL smtp_buffered(void); extern void smtp_closedown(uschar *); -extern int smtp_connect(host_item *, int, int, uschar *, int, BOOL, const uschar *); +extern int smtp_connect(host_item *, int, int, uschar *, int, BOOL, const uschar * +#ifdef EXPERIMENTAL_TPDA + , uschar * +#endif + ); extern int smtp_feof(void); extern int smtp_ferror(void); extern uschar *smtp_get_connection_info(void); diff --git a/src/src/globals.c b/src/src/globals.c index d3f99877c..f1b771ad3 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -577,6 +577,7 @@ time_t deliver_frozen_at = 0; uschar *deliver_home = NULL; uschar *deliver_host = NULL; uschar *deliver_host_address = NULL; +int deliver_host_port = 0; uschar *deliver_in_buffer = NULL; ino_t deliver_inode = 0; uschar *deliver_localpart = NULL; @@ -1326,13 +1327,8 @@ BOOL timestamps_utc = FALSE; #ifdef EXPERIMENTAL_TPDA int tpda_defer_errno = 0; -uschar *tpda_defer_errstr = NULL; -uschar *tpda_delivery_ip = NULL; -int tpda_delivery_port = 0; -uschar *tpda_delivery_fqdn = NULL; -uschar *tpda_delivery_local_part= NULL; -uschar *tpda_delivery_domain = NULL; -uschar *tpda_delivery_confirmation = NULL; +uschar *tpda_event = NULL; +uschar *tpda_data = NULL; #endif transport_instance *transports = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index 2bedcf523..f0a3091df 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -328,6 +328,7 @@ extern uschar *deliver_home; /* Home directory for pipes */ extern uschar *deliver_host; /* (First) host for routed local deliveries */ /* Remote host for filter */ extern uschar *deliver_host_address; /* Address for remote delivery filter */ +extern int deliver_host_port; /* Address for remote delivery filter */ extern uschar *deliver_in_buffer; /* Buffer for copying file */ extern ino_t deliver_inode; /* Inode for appendfile */ extern uschar *deliver_localpart; /* The local part for delivery */ @@ -870,13 +871,8 @@ extern BOOL timestamps_utc; /* Use UTC for all times */ #ifdef EXPERIMENTAL_TPDA extern int tpda_defer_errno; /* error number set when a remote delivery is deferred with a host error */ -extern uschar *tpda_defer_errstr; /* error string set when a remote delivery is deferred with a host error */ -extern uschar *tpda_delivery_ip; /* IP of host, which has accepted delivery */ -extern int tpda_delivery_port; /* port of host, which has accepted delivery */ -extern uschar *tpda_delivery_fqdn; /* FQDN of host, which has accepted delivery */ -extern uschar *tpda_delivery_local_part;/* local part of address being delivered */ -extern uschar *tpda_delivery_domain; /* domain part of address being delivered */ -extern uschar *tpda_delivery_confirmation; /* SMTP confirmation message */ +extern uschar *tpda_event; /* event classification */ +extern uschar *tpda_data;; /* event data */ #endif extern uschar *transport_name; /* Name of transport last started */ diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index b6ff51108..4920b7371 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -165,13 +165,18 @@ Arguments: timeout timeout value or 0 keepalive TRUE to use keepalive dscp DSCP value to assign to socket + tpda_event event expansion Returns: connected socket number, or -1 with errno set */ int smtp_connect(host_item *host, int host_af, int port, uschar *interface, - int timeout, BOOL keepalive, const uschar *dscp) + int timeout, BOOL keepalive, const uschar *dscp +#ifdef EXPERIMENTAL_TPDA + , uschar * tpda_event +#endif + ) { int on = 1; int save_errno = 0; @@ -198,6 +203,13 @@ HDEBUG(D_transport|D_acl|D_v) host->address, port, interface); } +#ifdef EXPERIMENTAL_TPDA + /*XXX Called from both delivery and verify. Is that status observable? */ + deliver_host_address = host->address; + deliver_host_port = port; + if (tpda_raise_event(tpda_event, US"tcp:connect", NULL) == DEFER) return -1; +#endif + /* Create the socket */ if ((sock = ip_socket(SOCK_STREAM, host_af)) < 0) return -1; diff --git a/src/src/structs.h b/src/src/structs.h index 71ac5d8e3..80c23fb0a 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -188,7 +188,7 @@ typedef struct transport_instance { BOOL log_defer_output; BOOL retry_use_local_part; /* Defaults true for local, false for remote */ #ifdef EXPERIMENTAL_TPDA - uschar *tpda_delivery_action; /* String to expand on success */ + uschar *tpda_event_action; /* String to expand on notable events */ #endif } transport_instance; diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 266ab8909..b7eae1793 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -47,6 +47,10 @@ require current GnuTLS, then we'll drop support for the ancient libraries). # warning "GnuTLS library version too old; define DISABLE_OCSP in Makefile" # define DISABLE_OCSP #endif +#if GNUTLS_VERSION_NUMBER < 0x020a00 && defined(EXPERIMENTAL_TPDA) +# warning "GnuTLS library version too old; TPDA tls:cert event unsupported" +# undef EXPERIMENTAL_TPDA +#endif #ifndef DISABLE_OCSP # include <gnutls/ocsp.h> @@ -115,6 +119,9 @@ typedef struct exim_gnutls_state { #ifdef EXPERIMENTAL_CERTNAMES uschar *exp_tls_verify_cert_hostnames; #endif +#ifdef EXPERIMENTAL_TPDA + uschar *event_action; +#endif tls_support *tlsp; /* set in tls_init() */ @@ -133,6 +140,9 @@ static const exim_gnutls_state_st exim_gnutls_state_init = { #ifdef EXPERIMENTAL_CERTNAMES NULL, #endif +#ifdef EXPERIMENTAL_TPDA + NULL, +#endif NULL, NULL, 0, 0, 0, 0, }; @@ -144,7 +154,9 @@ context we're currently dealing with" pointer and rely upon being single-threaded to keep from processing data on an inbound TLS connection while talking to another TLS connection for an outbound check. This does mean that there's no way for heart-beats to be responded to, for the duration of the -second connection. */ +second connection. +XXX But see gnutls_session_get_ptr() +*/ static exim_gnutls_state_st state_server, state_client; @@ -174,18 +186,18 @@ static BOOL exim_gnutls_base_init_done = FALSE; the library logging; a value less than 0 disables the calls to set up logging callbacks. */ #ifndef EXIM_GNUTLS_LIBRARY_LOG_LEVEL -#define EXIM_GNUTLS_LIBRARY_LOG_LEVEL -1 +# define EXIM_GNUTLS_LIBRARY_LOG_LEVEL -1 #endif #ifndef EXIM_CLIENT_DH_MIN_BITS -#define EXIM_CLIENT_DH_MIN_BITS 1024 +# define EXIM_CLIENT_DH_MIN_BITS 1024 #endif /* With GnuTLS 2.12.x+ we have gnutls_sec_param_to_pk_bits() with which we can ask for a bit-strength. Without that, we stick to the constant we had before, for now. */ #ifndef EXIM_SERVER_DH_BITS_PRE2_12 -#define EXIM_SERVER_DH_BITS_PRE2_12 1024 +# define EXIM_SERVER_DH_BITS_PRE2_12 1024 #endif #define exim_gnutls_err_check(Label) do { \ @@ -1512,6 +1524,52 @@ return 0; #endif +#ifdef EXPERIMENTAL_TPDA +/* +We use this callback to get observability and detail-level control +for an exim client TLS connection, raising a TPDA tls:cert event +for each cert in the chain presented by the server. Any event +can deny verification. + +Return 0 for the handshake to continue or non-zero to terminate. +*/ + +static int +client_verify_cb(gnutls_session_t session) +{ +const gnutls_datum * cert_list; +unsigned int cert_list_size = 0; +gnutls_x509_crt_t crt; +int rc; +exim_gnutls_state_st * state = gnutls_session_get_ptr(session); + +cert_list = gnutls_certificate_get_peers(session, &cert_list_size); +if (cert_list) + while (cert_list_size--) + { + rc = import_cert(&cert_list[cert_list_size], &crt); + if (rc != GNUTLS_E_SUCCESS) + { + DEBUG(D_tls) debug_printf("TLS: peer cert problem: depth %d: %s\n", + cert_list_size, gnutls_strerror(rc)); + break; + } + + state->tlsp->peercert = crt; + if (tpda_raise_event(state->event_action, + US"tls:cert", string_sprintf("%d", cert_list_size)) == DEFER) + { + log_write(0, LOG_MAIN, + "SSL verify denied by event-action: depth=%d", cert_list_size); + return 1; /* reject */ + } + state->tlsp->peercert = NULL; + } + +return 0; +} + +#endif @@ -1694,7 +1752,7 @@ Arguments: fd the fd of the connection host connected host (for messages) addr the first address (not used) - ob smtp transport options + tb transport (always smtp) Returns: OK/DEFER/FAIL (because using common functions), but for a client, DEFER and FAIL have the same meaning @@ -1703,9 +1761,10 @@ Returns: OK/DEFER/FAIL (because using common functions), int tls_client_start(int fd, host_item *host, address_item *addr ARG_UNUSED, - void *v_ob) + transport_instance *tb) { -smtp_transport_options_block *ob = v_ob; +smtp_transport_options_block *ob = + (smtp_transport_options_block *)tb->options_block; int rc; const char *error; exim_gnutls_state_st *state = NULL; @@ -1804,6 +1863,15 @@ if (request_ocsp) } #endif +#ifdef EXPERIMENTAL_TPDA +if (tb->tpda_event_action) + { + state->event_action = tb->tpda_event_action; + gnutls_session_set_ptr(state->session, state); + gnutls_certificate_set_verify_function(state->x509_cred, client_verify_cb); + } +#endif + gnutls_transport_set_ptr(state->session, (gnutls_transport_ptr)(long) fd); state->fd_in = fd; state->fd_out = fd; diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index e562a8926..c031b8e4d 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -116,6 +116,9 @@ typedef struct tls_ext_ctx_cb { #ifdef EXPERIMENTAL_CERTNAMES uschar * verify_cert_hostnames; #endif +#ifdef EXPERIMENTAL_TPDA + uschar * event_action; +#endif } tls_ext_ctx_cb; /* should figure out a cleanup of API to handle state preserved per @@ -262,6 +265,9 @@ when asked. We get here only if a certificate has been received. Handling of optional verification for this case is done when requesting SSL to verify, by setting SSL_VERIFY_FAIL_IF_NO_PEER_CERT in the non-optional case. +May be called multiple times for different issues with a certificate, even +for a given "depth" in the certificate chain. + Arguments: state current yes/no state as 1/0 x509ctx certificate information. @@ -275,6 +281,7 @@ verify_callback(int state, X509_STORE_CTX *x509ctx, tls_support *tlsp, BOOL *calledp, BOOL *optionalp) { X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx); +int depth = X509_STORE_CTX_get_error_depth(x509ctx); static uschar txt[256]; X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt)); @@ -282,7 +289,7 @@ X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt)); if (state == 0) { log_write(0, LOG_MAIN, "SSL verify error: depth=%d error=%s cert=%s", - X509_STORE_CTX_get_error_depth(x509ctx), + depth, X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509ctx)), txt); tlsp->certificate_verified = FALSE; @@ -296,10 +303,9 @@ if (state == 0) "tls_try_verify_hosts)\n"); } -else if (X509_STORE_CTX_get_error_depth(x509ctx) != 0) +else if (depth != 0) { - DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n", - X509_STORE_CTX_get_error_depth(x509ctx), txt); + DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n", depth, txt); #ifndef DISABLE_OCSP if (tlsp == &tls_out && client_static_cbinfo->u_ocsp.client.verify_store) { /* client, wanting stapling */ @@ -311,6 +317,23 @@ else if (X509_STORE_CTX_get_error_depth(x509ctx) != 0) ERR_clear_error(); } #endif +#ifdef EXPERIMENTAL_TPDA + if (tlsp == &tls_out && client_static_cbinfo->event_action) + { + tlsp->peercert = X509_dup(cert); + if (tpda_raise_event(client_static_cbinfo->event_action, + US"tls:cert", string_sprintf("%d", depth)) == DEFER) + { + log_write(0, LOG_MAIN, "SSL verify denied by event-action: " + "depth=%d cert=%s", depth, txt); + tlsp->certificate_verified = FALSE; + *calledp = TRUE; + return 0; /* reject */ + } + X509_free(tlsp->peercert); + tlsp->peercert = NULL; + } +#endif } else { @@ -361,6 +384,21 @@ else return 0; /* reject */ } # endif +#endif /*EXPERIMENTAL_CERTNAMES*/ + +#ifdef EXPERIMENTAL_TPDA + if (tlsp == &tls_out) + { + if (tpda_raise_event(client_static_cbinfo->event_action, + US"tls:cert", US"0") == DEFER) + { + log_write(0, LOG_MAIN, "SSL verify denied by event-action: " + "depth=0 cert=%s", txt); + tlsp->certificate_verified = FALSE; + *calledp = TRUE; + return 0; /* reject */ + } + } #endif DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n", @@ -369,7 +407,7 @@ else *calledp = TRUE; } -return 1; /* accept */ +return 1; /* accept, at least for this level */ } static int @@ -1032,7 +1070,7 @@ tls_init(SSL_CTX **ctxp, host_item *host, uschar *dhparam, uschar *certificate, long init_options; int rc; BOOL okay; -tls_ext_ctx_cb *cbinfo; +tls_ext_ctx_cb * cbinfo; cbinfo = store_malloc(sizeof(tls_ext_ctx_cb)); cbinfo->certificate = certificate; @@ -1050,6 +1088,9 @@ else cbinfo->dhparam = dhparam; cbinfo->server_cipher_list = NULL; cbinfo->host = host; +#ifdef EXPERIMENTAL_TPDA +cbinfo->event_action = NULL; +#endif SSL_load_error_strings(); /* basic set up */ OpenSSL_add_ssl_algorithms(); @@ -1546,7 +1587,7 @@ Argument: fd the fd of the connection host connected host (for messages) addr the first address - ob smtp transport options + tb transport (always smtp) Returns: OK on success FAIL otherwise - note that tls_error() will not give DEFER @@ -1555,9 +1596,10 @@ Returns: OK on success int tls_client_start(int fd, host_item *host, address_item *addr, - void *v_ob) + transport_instance *tb) { -smtp_transport_options_block * ob = v_ob; +smtp_transport_options_block * ob = + (smtp_transport_options_block *)tb->options_block; static uschar txt[256]; uschar *expciphers; X509* server_cert; @@ -1672,6 +1714,10 @@ if (request_ocsp) } #endif +#ifdef EXPERIMENTAL_TPDA +client_static_cbinfo->event_action = tb->tpda_event_action; +#endif + /* There doesn't seem to be a built-in timeout on connection. */ DEBUG(D_tls) debug_printf("Calling SSL_connect\n"); diff --git a/src/src/transport.c b/src/src/transport.c index 3648bfc82..31437b146 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -95,8 +95,8 @@ optionlist optionlist_transports[] = { { "shadow_transport", opt_stringptr|opt_public, (void *)offsetof(transport_instance, shadow) }, #ifdef EXPERIMENTAL_TPDA - { "tpda_delivery_action",opt_stringptr | opt_public, - (void *)offsetof(transport_instance, tpda_delivery_action) }, + { "tpda_event_action",opt_stringptr | opt_public, + (void *)offsetof(transport_instance, tpda_event_action) }, #endif { "transport_filter", opt_stringptr|opt_public, (void *)offsetof(transport_instance, filter_command) }, diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 40eebe8b6..0dfa01958 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -172,10 +172,6 @@ optionlist smtp_transport_options[] = { { "tls_verify_hosts", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_verify_hosts) } #endif -#ifdef EXPERIMENTAL_TPDA - ,{ "tpda_host_defer_action", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, tpda_host_defer_action) }, -#endif }; /* Size of the options list. An extern variable has to be used so that its @@ -261,9 +257,6 @@ smtp_transport_options_block smtp_transport_option_defaults = { NULL, /* dkim_sign_headers */ NULL /* dkim_strict */ #endif -#ifdef EXPERIMENTAL_TPDA - ,NULL /* tpda_host_defer_action */ -#endif }; #ifdef EXPERIMENTAL_DSN @@ -485,7 +478,8 @@ Arguments: Returns: TRUE if an SMTP "QUIT" command should be sent, else FALSE */ -static BOOL check_response(host_item *host, int *errno_value, int more_errno, +static BOOL +check_response(host_item *host, int *errno_value, int more_errno, uschar *buffer, int *yield, uschar **message, BOOL *pass_message) { uschar *pl = US""; @@ -636,7 +630,6 @@ else It might, for example, be used to write to the database log. Arguments: - ob transport options block addr the address item containing error information host the current host @@ -644,36 +637,39 @@ Returns: nothing */ static void -tpda_deferred(smtp_transport_options_block *ob, address_item *addr, host_item *host) +tpda_deferred(address_item *addr, host_item *host) { -uschar *action = ob->tpda_host_defer_action; +uschar * action = addr->transport->tpda_event_action; +uschar * save_domain; +uschar * save_local; + if (!action) - return; - -tpda_delivery_ip = string_copy(host->address); -tpda_delivery_port = (host->port == PORT_NONE)? 25 : host->port; -tpda_delivery_fqdn = string_copy(host->name); -tpda_delivery_local_part = string_copy(addr->local_part); -tpda_delivery_domain = string_copy(addr->domain); -tpda_defer_errno = addr->basic_errno; - -tpda_defer_errstr = addr->message - ? addr->basic_errno > 0 - ? string_sprintf("%s: %s", addr->message, strerror(addr->basic_errno)) - : string_copy(addr->message) - : addr->basic_errno > 0 - ? string_copy(US strerror(addr->basic_errno)) - : NULL; + return; -DEBUG(D_transport) - debug_printf(" TPDA(host defer): tpda_host_defer_action=|%s| tpda_delivery_IP=%s\n", - action, tpda_delivery_ip); +save_domain = deliver_domain; +save_local = deliver_localpart; + +/*XXX would ip & port already be set up? */ +deliver_host_address = string_copy(host->address); +deliver_host_port = (host->port == PORT_NONE)? 25 : host->port; +tpda_defer_errno = addr->basic_errno; router_name = addr->router->name; transport_name = addr->transport->name; -if (!expand_string(action) && *expand_string_message) - log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand tpda_defer_action in %s: %s\n", - transport_name, expand_string_message); +deliver_domain = addr->domain; +deliver_localpart = addr->local_part; + +(void) tpda_raise_event(action, US"msg:host:defer", + addr->message + ? addr->basic_errno > 0 + ? string_sprintf("%s: %s", addr->message, strerror(addr->basic_errno)) + : string_copy(addr->message) + : addr->basic_errno > 0 + ? string_copy(US strerror(addr->basic_errno)) + : NULL); + +deliver_localpart = save_local; +deliver_domain = save_domain; router_name = transport_name = NULL; } #endif @@ -940,147 +936,147 @@ smtp_auth(uschar *buffer, unsigned bufsize, address_item *addrlist, host_item *h smtp_transport_options_block *ob, BOOL is_esmtp, smtp_inblock *ibp, smtp_outblock *obp) { - int require_auth; - uschar *fail_reason = US"server did not advertise AUTH support"; +int require_auth; +uschar *fail_reason = US"server did not advertise AUTH support"; - smtp_authenticated = FALSE; - client_authenticator = client_authenticated_id = client_authenticated_sender = NULL; - require_auth = verify_check_this_host(&(ob->hosts_require_auth), NULL, - host->name, host->address, NULL); +smtp_authenticated = FALSE; +client_authenticator = client_authenticated_id = client_authenticated_sender = NULL; +require_auth = verify_check_this_host(&(ob->hosts_require_auth), NULL, + host->name, host->address, NULL); - if (is_esmtp && !regex_AUTH) regex_AUTH = - regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)", - FALSE, TRUE); +if (is_esmtp && !regex_AUTH) regex_AUTH = + regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)", + FALSE, TRUE); - if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1)) - { - uschar *names = string_copyn(expand_nstring[1], expand_nlength[1]); - expand_nmax = -1; /* reset */ +if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1)) + { + uschar *names = string_copyn(expand_nstring[1], expand_nlength[1]); + expand_nmax = -1; /* reset */ - /* Must not do this check until after we have saved the result of the - regex match above. */ + /* Must not do this check until after we have saved the result of the + regex match above. */ - if (require_auth == OK || - verify_check_this_host(&(ob->hosts_try_auth), NULL, host->name, - host->address, NULL) == OK) - { - auth_instance *au; - fail_reason = US"no common mechanisms were found"; + if (require_auth == OK || + verify_check_this_host(&(ob->hosts_try_auth), NULL, host->name, + host->address, NULL) == OK) + { + auth_instance *au; + fail_reason = US"no common mechanisms were found"; - DEBUG(D_transport) debug_printf("scanning authentication mechanisms\n"); + DEBUG(D_transport) debug_printf("scanning authentication mechanisms\n"); - /* Scan the configured authenticators looking for one which is configured - for use as a client, which is not suppressed by client_condition, and - whose name matches an authentication mechanism supported by the server. - If one is found, attempt to authenticate by calling its client function. - */ + /* Scan the configured authenticators looking for one which is configured + for use as a client, which is not suppressed by client_condition, and + whose name matches an authentication mechanism supported by the server. + If one is found, attempt to authenticate by calling its client function. + */ - for (au = auths; !smtp_authenticated && au != NULL; au = au->next) - { - uschar *p = names; - if (!au->client || - (au->client_condition != NULL && - !expand_check_condition(au->client_condition, au->name, - US"client authenticator"))) - { - DEBUG(D_transport) debug_printf("skipping %s authenticator: %s\n", - au->name, - (au->client)? "client_condition is false" : - "not configured as a client"); - continue; - } + for (au = auths; !smtp_authenticated && au != NULL; au = au->next) + { + uschar *p = names; + if (!au->client || + (au->client_condition != NULL && + !expand_check_condition(au->client_condition, au->name, + US"client authenticator"))) + { + DEBUG(D_transport) debug_printf("skipping %s authenticator: %s\n", + au->name, + (au->client)? "client_condition is false" : + "not configured as a client"); + continue; + } - /* Loop to scan supported server mechanisms */ + /* Loop to scan supported server mechanisms */ - while (*p != 0) - { - int rc; - int len = Ustrlen(au->public_name); - while (isspace(*p)) p++; + while (*p != 0) + { + int rc; + int len = Ustrlen(au->public_name); + while (isspace(*p)) p++; - if (strncmpic(au->public_name, p, len) != 0 || - (p[len] != 0 && !isspace(p[len]))) - { - while (*p != 0 && !isspace(*p)) p++; - continue; - } + if (strncmpic(au->public_name, p, len) != 0 || + (p[len] != 0 && !isspace(p[len]))) + { + while (*p != 0 && !isspace(*p)) p++; + continue; + } - /* Found data for a listed mechanism. Call its client entry. Set - a flag in the outblock so that data is overwritten after sending so - that reflections don't show it. */ + /* Found data for a listed mechanism. Call its client entry. Set + a flag in the outblock so that data is overwritten after sending so + that reflections don't show it. */ - fail_reason = US"authentication attempt(s) failed"; - obp->authenticating = TRUE; - rc = (au->info->clientcode)(au, ibp, obp, - ob->command_timeout, buffer, bufsize); - obp->authenticating = FALSE; - DEBUG(D_transport) debug_printf("%s authenticator yielded %d\n", - au->name, rc); + fail_reason = US"authentication attempt(s) failed"; + obp->authenticating = TRUE; + rc = (au->info->clientcode)(au, ibp, obp, + ob->command_timeout, buffer, bufsize); + obp->authenticating = FALSE; + DEBUG(D_transport) debug_printf("%s authenticator yielded %d\n", + au->name, rc); - /* A temporary authentication failure must hold up delivery to - this host. After a permanent authentication failure, we carry on - to try other authentication methods. If all fail hard, try to - deliver the message unauthenticated unless require_auth was set. */ + /* A temporary authentication failure must hold up delivery to + this host. After a permanent authentication failure, we carry on + to try other authentication methods. If all fail hard, try to + deliver the message unauthenticated unless require_auth was set. */ - switch(rc) - { - case OK: - smtp_authenticated = TRUE; /* stops the outer loop */ - client_authenticator = au->name; - if (au->set_client_id != NULL) - client_authenticated_id = expand_string(au->set_client_id); - break; - - /* Failure after writing a command */ - - case FAIL_SEND: - return FAIL_SEND; - - /* Failure after reading a response */ - - case FAIL: - if (errno != 0 || buffer[0] != '5') return FAIL; - log_write(0, LOG_MAIN, "%s authenticator failed H=%s [%s] %s", - au->name, host->name, host->address, buffer); - break; - - /* Failure by some other means. In effect, the authenticator - decided it wasn't prepared to handle this case. Typically this - is the result of "fail" in an expansion string. Do we need to - log anything here? Feb 2006: a message is now put in the buffer - if logging is required. */ - - case CANCELLED: - if (*buffer != 0) - log_write(0, LOG_MAIN, "%s authenticator cancelled " - "authentication H=%s [%s] %s", au->name, host->name, - host->address, buffer); - break; - - /* Internal problem, message in buffer. */ - - case ERROR: - set_errno(addrlist, 0, string_copy(buffer), DEFER, FALSE); - return ERROR; - } + switch(rc) + { + case OK: + smtp_authenticated = TRUE; /* stops the outer loop */ + client_authenticator = au->name; + if (au->set_client_id != NULL) + client_authenticated_id = expand_string(au->set_client_id); + break; + + /* Failure after writing a command */ + + case FAIL_SEND: + return FAIL_SEND; + + /* Failure after reading a response */ + + case FAIL: + if (errno != 0 || buffer[0] != '5') return FAIL; + log_write(0, LOG_MAIN, "%s authenticator failed H=%s [%s] %s", + au->name, host->name, host->address, buffer); + break; + + /* Failure by some other means. In effect, the authenticator + decided it wasn't prepared to handle this case. Typically this + is the result of "fail" in an expansion string. Do we need to + log anything here? Feb 2006: a message is now put in the buffer + if logging is required. */ + + case CANCELLED: + if (*buffer != 0) + log_write(0, LOG_MAIN, "%s authenticator cancelled " + "authentication H=%s [%s] %s", au->name, host->name, + host->address, buffer); + break; + + /* Internal problem, message in buffer. */ + + case ERROR: + set_errno(addrlist, 0, string_copy(buffer), DEFER, FALSE); + return ERROR; + } - break; /* If not authenticated, try next authenticator */ - } /* Loop for scanning supported server mechanisms */ - } /* Loop for further authenticators */ - } + break; /* If not authenticated, try next authenticator */ + } /* Loop for scanning supported server mechanisms */ + } /* Loop for further authenticators */ } + } - /* If we haven't authenticated, but are required to, give up. */ +/* If we haven't authenticated, but are required to, give up. */ - if (require_auth == OK && !smtp_authenticated) - { - set_errno(addrlist, ERRNO_AUTHFAIL, - string_sprintf("authentication required but %s", fail_reason), DEFER, - FALSE); - return DEFER; - } +if (require_auth == OK && !smtp_authenticated) + { + set_errno(addrlist, ERRNO_AUTHFAIL, + string_sprintf("authentication required but %s", fail_reason), DEFER, + FALSE); + return DEFER; + } - return OK; +return OK; } @@ -1283,9 +1279,14 @@ specially so they can be identified for retries. */ if (continue_hostname == NULL) { + /* This puts port into host->port */ inblock.sock = outblock.sock = smtp_connect(host, host_af, port, interface, ob->connect_timeout, - ob->keepalive, ob->dscp); /* This puts port into host->port */ + ob->keepalive, ob->dscp +#ifdef EXPERIMENTAL_TPDA + , tblock->tpda_event_action +#endif + ); if (inblock.sock < 0) { @@ -1309,6 +1310,17 @@ if (continue_hostname == NULL) if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', ob->command_timeout)) goto RESPONSE_FAILED; +#ifdef EXPERIMENTAL_TPDA + if (tpda_raise_event(tblock->tpda_event_action, US"smtp:connect", buffer) + == DEFER) + { + uschar *message = US"deferred by smtp:connect event expansion"; + set_errno(addrlist, 0, message, DEFER, FALSE); + yield = DEFER; + goto SEND_QUIT; + } +#endif + /* Now check if the helo_data expansion went well, and sign off cleanly if it didn't. */ @@ -1363,7 +1375,7 @@ goto SEND_QUIT; /* Alas; be careful, since this goto is not an error-out, so conceivably we might set data between here and the target which we assume to exist and be usable. I can see this coming back to bite us. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (smtps) { tls_offered = TRUE; @@ -1372,7 +1384,7 @@ goto SEND_QUIT; smtp_command = US"SSL-on-connect"; goto TLS_NEGOTIATE; } - #endif +#endif if (esmtp) { @@ -1409,13 +1421,13 @@ goto SEND_QUIT; /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS tls_offered = esmtp && pcre_exec(regex_STARTTLS, NULL, CS buffer, Ustrlen(buffer), 0, PCRE_EOPT, NULL, 0) >= 0; - #endif +#endif - #ifndef DISABLE_PRDR +#ifndef DISABLE_PRDR prdr_offered = esmtp && (pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(buffer), 0, PCRE_EOPT, NULL, 0) >= 0) && @@ -1424,7 +1436,7 @@ goto SEND_QUIT; if (prdr_offered) {DEBUG(D_transport) debug_printf("PRDR usable\n");} - #endif +#endif } /* For continuing deliveries down the same channel, the socket is the standard @@ -1481,7 +1493,7 @@ if (tls_offered && !suppress_tls && else TLS_NEGOTIATE: { - int rc = tls_client_start(inblock.sock, host, addrlist, ob); + int rc = tls_client_start(inblock.sock, host, addrlist, tblock); /* TLS negotiation failed; give an error. From outside, this function may be called again to try in clear on a new connection, if the options permit @@ -1582,9 +1594,9 @@ continued session down a previously-used socket, we haven't just done EHLO, so we skip this. */ if (continue_hostname == NULL - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS || tls_out.active >= 0 - #endif +#endif ) { /* Set for IGNOREQUOTA if the response to LHLO specifies support and the @@ -1721,12 +1733,11 @@ if (prdr_offered) { /* at least two recipients to send */ prdr_active = TRUE; sprintf(CS p, " PRDR"); p += 5; - goto prdr_is_active; + break; } break; } } -prdr_is_active: #endif #ifdef EXPERIMENTAL_DSN @@ -1769,7 +1780,10 @@ otherwise no check - this feature is expected to be used with LMTP and other cases where non-standard addresses (e.g. without domains) might be required. */ if (smtp_mail_auth_str(p, sizeof(buffer) - (p-buffer), addrlist, ob)) - return ERROR; + { + yield = ERROR; + goto SEND_QUIT; + } /* From here until we send the DATA command, we can make use of PIPELINING if the server host supports it. The code has to be able to check the responses @@ -1823,25 +1837,22 @@ for (addr = first_addr; int count; BOOL no_flush; - #ifdef EXPERIMENTAL_DSN - if(smtp_use_dsn) - addr->dsn_aware = dsn_support_yes; - else - addr->dsn_aware = dsn_support_no; - #endif +#ifdef EXPERIMENTAL_DSN + addr->dsn_aware = smtp_use_dsn ? dsn_support_yes : dsn_support_no; +#endif if (addr->transport_return != PENDING_DEFER) continue; address_count++; no_flush = smtp_use_pipelining && (!mua_wrapper || addr->next != NULL); - #ifdef EXPERIMENTAL_DSN +#ifdef EXPERIMENTAL_DSN /* Add any DSN flags to the rcpt command and add to the sent string */ p = buffer; *p = 0; - if ((smtp_use_dsn) && ((addr->dsn_flags & rf_dsnlasthop) != 1)) + if (smtp_use_dsn && (addr->dsn_flags & rf_dsnlasthop) != 1) { if ((addr->dsn_flags & rf_dsnflags) != 0) { @@ -1850,7 +1861,6 @@ for (addr = first_addr; strcpy(p, " NOTIFY="); while (*p) p++; for (i = 0; i < 4; i++) - { if ((addr->dsn_flags & rf_list[i]) != 0) { if (!first) *p++ = ','; @@ -1858,16 +1868,16 @@ for (addr = first_addr; strcpy(p, rf_names[i]); while (*p) p++; } - } } - if (addr->dsn_orcpt != NULL) { + if (addr->dsn_orcpt != NULL) + { string_format(p, sizeof(buffer) - (p-buffer), " ORCPT=%s", addr->dsn_orcpt); while (*p) p++; } } - #endif +#endif /* Now send the RCPT command, and process outstanding responses when @@ -1875,13 +1885,13 @@ for (addr = first_addr; yield as OK, because this error can often mean that there is a problem with just one address, so we don't want to delay the host. */ - #ifdef EXPERIMENTAL_DSN +#ifdef EXPERIMENTAL_DSN count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s%s\r\n", transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr, buffer); - #else +#else count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s\r\n", transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr); - #endif +#endif if (count < 0) goto SEND_FAILED; if (count > 0) @@ -1972,6 +1982,7 @@ if (!ok) ok = TRUE; else DEBUG(D_transport|D_v) debug_printf(" SMTP>> writing message and terminating \".\"\n"); transport_count = 0; + #ifndef DISABLE_DKIM ok = dkim_transport_write_message(addrlist, inblock.sock, topt_use_crlf | topt_end_dot | topt_escape_headers | @@ -2098,9 +2109,9 @@ if (!ok) ok = TRUE; else /* Set up confirmation if needed - applies only to SMTP */ if ( - #ifndef EXPERIMENTAL_TPDA +#ifndef EXPERIMENTAL_TPDA (log_extra_selector & LX_smtp_confirmation) != 0 && - #endif +#endif !lmtp ) { @@ -2276,10 +2287,10 @@ if (!ok) in message and save_errno, and setting_up will always be true. Treat as a temporary error. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS TLS_FAILED: code = '4'; - #endif +#endif /* If the failure happened while setting up the call, see if the failure was a 5xx response (this will either be on connection, or following HELO - a 5xx @@ -2472,7 +2483,7 @@ if (completed_address && ok && send_quit) when TLS is shut down. We test for this by sending a new EHLO. If we don't get a good response, we don't attempt to pass the socket on. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (tls_out.active >= 0) { tls_close(FALSE, TRUE); @@ -2483,7 +2494,7 @@ if (completed_address && ok && send_quit) smtp_read_response(&inblock, buffer, sizeof(buffer), '2', ob->command_timeout); } - #endif +#endif /* If the socket is successfully passed, we musn't send QUIT (or indeed anything!) from here. */ @@ -2539,6 +2550,11 @@ specified in the transports, and therefore not visible at top level, in which case continue_more won't get set. */ (void)close(inblock.sock); + +#ifdef EXPERIMENTAL_TPDA +(void) tpda_raise_event(tblock->tpda_event_action, US"tcp:close", NULL); +#endif + continue_transport = NULL; continue_hostname = NULL; return yield; @@ -2627,13 +2643,13 @@ for (addr = addrlist; addr != NULL; addr = addr->next) addr->basic_errno = 0; addr->more_errno = (host->mx >= 0)? 'M' : 'A'; addr->message = NULL; - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS addr->cipher = NULL; addr->ourcert = NULL; addr->peercert = NULL; addr->peerdn = NULL; addr->ocsp = OCSP_NOT_REQ; - #endif +#endif } return first_addr; } @@ -3246,10 +3262,10 @@ for (cutoff_retry = 0; expired && first_addr->basic_errno != ERRNO_TLSFAILURE) write_logs(first_addr, host); - #ifdef EXPERIMENTAL_TPDA +#ifdef EXPERIMENTAL_TPDA if (rc == DEFER) - tpda_deferred(ob, first_addr, host); - #endif + tpda_deferred(first_addr, host); +#endif /* If STARTTLS was accepted, but there was a failure in setting up the TLS session (usually a certificate screwup), and the host is not in @@ -3260,7 +3276,7 @@ for (cutoff_retry = 0; expired && session, so the in-clear transmission after those errors, if permitted, happens inside smtp_deliver().] */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (rc == DEFER && first_addr->basic_errno == ERRNO_TLSFAILURE && ob->tls_tempfail_tryclear && verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name, @@ -3273,12 +3289,12 @@ for (cutoff_retry = 0; expired && expanded_hosts != NULL, &message_defer, TRUE); if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL) write_logs(first_addr, host); - #ifdef EXPERIMENTAL_TPDA +# ifdef EXPERIMENTAL_TPDA if (rc == DEFER) - tpda_deferred(ob, first_addr, host); - #endif + tpda_deferred(first_addr, host); +# endif } - #endif +#endif } /* Delivery attempt finished */ diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index dd41e1f15..3030f3a91 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -81,9 +81,6 @@ typedef struct { uschar *dkim_sign_headers; uschar *dkim_strict; #endif -#ifdef EXPERIMENTAL_TPDA - uschar *tpda_host_defer_action; -#endif } smtp_transport_options_block; /* Data for reading the private options. */ diff --git a/src/src/verify.c b/src/src/verify.c index b1b9f29a4..8564aacc2 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -462,6 +462,7 @@ else deliver_host = host->name; deliver_host_address = host->address; + deliver_host_port = host->port; deliver_domain = addr->domain; if (!smtp_get_interface(tf->interface, host_af, addr, NULL, &interface, @@ -501,7 +502,12 @@ else tls_retry_connection: inblock.sock = outblock.sock = - smtp_connect(host, host_af, port, interface, callout_connect, TRUE, NULL); + smtp_connect(host, host_af, port, interface, callout_connect, TRUE, NULL +#ifdef EXPERIMENTAL_TPDA + /*XXX tpda action? NULL for now. */ + , NULL +#endif + ); /* reconsider DSCP here */ if (inblock.sock < 0) { @@ -533,12 +539,23 @@ else /* Unless ssl-on-connect, wait for the initial greeting */ smtps_redo_greeting: - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (!smtps || (smtps && tls_out.active >= 0)) - #endif +#endif + { if (!(done= smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout))) goto RESPONSE_FAILED; +#ifdef EXPERIMENTAL_TPDA + if (tpda_raise_event(addr->transport->tpda_event_action, + US"smtp:connect", responsebuffer) == DEFER) + { + /* Logging? Debug? */ + goto RESPONSE_FAILED; + } +#endif + } + /* Not worth checking greeting line for ESMTP support */ if (!(esmtp = verify_check_this_host(&(ob->hosts_avoid_esmtp), NULL, host->name, host->address, NULL) != OK)) @@ -547,14 +564,14 @@ else tls_redo_helo: - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (smtps && tls_out.active < 0) /* ssl-on-connect, first pass */ { tls_offered = TRUE; ob->tls_tempfail_tryclear = FALSE; } - else /* all other cases */ - #endif + else /* all other cases */ +#endif { esmtp_retry: @@ -568,26 +585,26 @@ else done= FALSE; goto RESPONSE_FAILED; } - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS tls_offered = FALSE; - #endif +#endif esmtp = FALSE; goto esmtp_retry; /* fallback to HELO */ } /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (esmtp && !suppress_tls && tls_out.active < 0) - { - if (regex_STARTTLS == NULL) regex_STARTTLS = - regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE); + { + if (regex_STARTTLS == NULL) regex_STARTTLS = + regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE); - tls_offered = pcre_exec(regex_STARTTLS, NULL, CS responsebuffer, - Ustrlen(responsebuffer), 0, PCRE_EOPT, NULL, 0) >= 0; + tls_offered = pcre_exec(regex_STARTTLS, NULL, CS responsebuffer, + Ustrlen(responsebuffer), 0, PCRE_EOPT, NULL, 0) >= 0; } else tls_offered = FALSE; - #endif +#endif } /* If TLS is available on this connection attempt to @@ -598,7 +615,7 @@ else the client not be required to use TLS. If the response is bad, copy the buffer for error analysis. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (tls_offered && verify_check_this_host(&(ob->hosts_avoid_tls), NULL, host->name, host->address, NULL) != OK && @@ -623,11 +640,11 @@ else { if (errno != 0 || buffer2[0] == 0 || (buffer2[0] == '4' && !ob->tls_tempfail_tryclear)) - { - Ustrncpy(responsebuffer, buffer2, sizeof(responsebuffer)); - done= FALSE; - goto RESPONSE_FAILED; - } + { + Ustrncpy(responsebuffer, buffer2, sizeof(responsebuffer)); + done= FALSE; + goto RESPONSE_FAILED; + } } /* STARTTLS accepted or ssl-on-connect: try to negotiate a TLS session. */ @@ -637,29 +654,33 @@ else int rc; ob->command_timeout = callout; - rc = tls_client_start(inblock.sock, host, addr, ob); + rc = tls_client_start(inblock.sock, host, addr, addr->transport); ob->command_timeout = oldtimeout; /* TLS negotiation failed; give an error. Try in clear on a new connection, if the options permit it for this host. */ if (rc != OK) { - if (rc == DEFER && ob->tls_tempfail_tryclear && !smtps && - verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name, - host->address, NULL) != OK) - { - (void)close(inblock.sock); - log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted " - "to %s [%s] (not in hosts_require_tls)", host->name, host->address); - suppress_tls = TRUE; - goto tls_retry_connection; - } - /*save_errno = ERRNO_TLSFAILURE;*/ - /*message = US"failure while setting up TLS session";*/ - send_quit = FALSE; - done= FALSE; - goto TLS_FAILED; - } + if (rc == DEFER && ob->tls_tempfail_tryclear && !smtps && + verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name, + host->address, NULL) != OK) + { + (void)close(inblock.sock); +#ifdef EXPERIMENTAL_TPDA + (void) tpda_raise_event(addr->transport->tpda_event_action, + US"tcp:close", NULL); +#endif + log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted " + "to %s [%s] (not in hosts_require_tls)", host->name, host->address); + suppress_tls = TRUE; + goto tls_retry_connection; + } + /*save_errno = ERRNO_TLSFAILURE;*/ + /*message = US"failure while setting up TLS session";*/ + send_quit = FALSE; + done= FALSE; + goto TLS_FAILED; + } /* TLS session is set up. Copy info for logging. */ addr->cipher = tls_out.cipher; @@ -667,7 +688,7 @@ else /* For SMTPS we need to wait for the initial OK response, then do HELO. */ if (smtps) - goto smtps_redo_greeting; + goto smtps_redo_greeting; /* For STARTTLS we need to redo EHLO */ goto tls_redo_helo; @@ -702,13 +723,13 @@ else cutthrough_delivery= FALSE; HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of transport filter\n"); } - #ifndef DISABLE_DKIM +#ifndef DISABLE_DKIM if (ob->dkim_domain) { cutthrough_delivery= FALSE; HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of DKIM signing\n"); } - #endif +#endif } SEND_FAILED: @@ -994,10 +1015,14 @@ else cancel_cutthrough_connection("multiple verify calls"); if (send_quit) (void)smtp_write_command(&outblock, FALSE, "QUIT\r\n"); - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS tls_close(FALSE, TRUE); - #endif +#endif (void)close(inblock.sock); +#ifdef EXPERIMENTAL_TPDA + (void) tpda_raise_event(addr->transport->tpda_event_action, + US"tcp:close", NULL); +#endif } } /* Loop through all hosts, while !done */ |