summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/src/acl.c31
-rw-r--r--src/src/deliver.c178
-rw-r--r--src/src/expand.c13
-rw-r--r--src/src/functions.h8
-rw-r--r--src/src/globals.c10
-rw-r--r--src/src/globals.h10
-rw-r--r--src/src/smtp_out.c14
-rw-r--r--src/src/structs.h2
-rw-r--r--src/src/tls-gnu.c82
-rw-r--r--src/src/tls-openssl.c64
-rw-r--r--src/src/transport.c4
-rw-r--r--src/src/transports/smtp.c412
-rw-r--r--src/src/transports/smtp.h3
-rw-r--r--src/src/verify.c111
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 */