diff options
Diffstat (limited to 'src/smtp/smtp_reuse.c')
-rw-r--r-- | src/smtp/smtp_reuse.c | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/src/smtp/smtp_reuse.c b/src/smtp/smtp_reuse.c new file mode 100644 index 0000000..f93ba29 --- /dev/null +++ b/src/smtp/smtp_reuse.c @@ -0,0 +1,269 @@ +/*++ +/* NAME +/* smtp_reuse 3 +/* SUMMARY +/* SMTP session cache glue +/* SYNOPSIS +/* #include <smtp.h> +/* #include <smtp_reuse.h> +/* +/* void smtp_save_session(state, name_key_flags, endp_key_flags) +/* SMTP_STATE *state; +/* int name_key_flags; +/* int endp_key_flags; +/* +/* SMTP_SESSION *smtp_reuse_nexthop(state, name_key_flags) +/* SMTP_STATE *state; +/* int name_key_flags; +/* +/* SMTP_SESSION *smtp_reuse_addr(state, endp_key_flags) +/* SMTP_STATE *state; +/* int endp_key_flags; +/* DESCRIPTION +/* This module implements the SMTP client specific interface to +/* the generic session cache infrastructure. +/* +/* The caller needs to include additional state in _key_flags +/* to avoid false sharing of SASL-authenticated or TLS-authenticated +/* sessions. +/* +/* smtp_save_session() stores the current session under the +/* delivery request next-hop logical destination (if applicable) +/* and under the remote server address. The SMTP_SESSION object +/* is destroyed. +/* +/* smtp_reuse_nexthop() looks up a cached session by its +/* delivery request next-hop destination, and verifies that +/* the session is still alive. The restored session information +/* includes the "best MX" bit and overrides the iterator dest, +/* host and addr fields. The result is null in case of failure. +/* +/* smtp_reuse_addr() looks up a cached session by its server +/* address, and verifies that the session is still alive. +/* The restored session information does not include the "best +/* MX" bit, and does not override the iterator dest, host and +/* addr fields. The result is null in case of failure. +/* +/* Arguments: +/* .IP state +/* SMTP client state, including the current session, the original +/* next-hop domain, etc. +/* .IP name_key_flags +/* Explicit declaration of context that should be used to look +/* up a cached connection by its logical destination. +/* See smtp_key(3) for details. +/* .IP endp_key_flags +/* Explicit declaration of context that should be used to look +/* up a cached connection by its server address. +/* See smtp_key(3) for details. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <string.h> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <vstream.h> +#include <vstring.h> +#include <htable.h> +#include <stringops.h> + +/* Global library. */ + +#include <scache.h> +#include <mail_params.h> + +/* Application-specific. */ + +#include <smtp.h> +#include <smtp_reuse.h> + + /* + * Key field delimiter, and place holder field value for + * unavailable/inapplicable information. + */ +#define SMTP_REUSE_KEY_DELIM_NA "\n*" + +/* smtp_save_session - save session under next-hop name and server address */ + +void smtp_save_session(SMTP_STATE *state, int name_key_flags, + int endp_key_flags) +{ + SMTP_SESSION *session = state->session; + int fd; + + /* + * Encode the delivery request next-hop destination, if applicable. Reuse + * storage that is also used for cache lookup queries. + * + * HAVE_SCACHE_REQUEST_NEXTHOP() controls whether or not to reuse or cache a + * connection by its delivery request next-hop destination. The idea is + * 1) to allow a reuse request to skip over bad hosts, and 2) to avoid + * caching a less-preferred connection when a more-preferred connection + * was possible. + */ + if (HAVE_SCACHE_REQUEST_NEXTHOP(state)) + smtp_key_prefix(state->dest_label, SMTP_REUSE_KEY_DELIM_NA, + state->iterator, name_key_flags); + + /* + * Encode the physical endpoint name. Reuse storage that is also used for + * cache lookup queries. + */ + smtp_key_prefix(state->endp_label, SMTP_REUSE_KEY_DELIM_NA, + state->iterator, endp_key_flags); + + /* + * Passivate the SMTP_SESSION object, destroying the object in the + * process. Reuse storage that is also used for cache lookup results. + */ + fd = smtp_session_passivate(session, state->dest_prop, state->endp_prop); + state->session = 0; + + /* + * Save the session under the delivery request next-hop name, if + * applicable. + * + * XXX The logical to physical binding can be kept for as long as the DNS + * allows us to (but that could result in the caching of lots of unused + * bindings). The session should be idle for no more than 30 seconds or + * so. + */ + if (HAVE_SCACHE_REQUEST_NEXTHOP(state)) + scache_save_dest(smtp_scache, var_smtp_cache_conn, + STR(state->dest_label), STR(state->dest_prop), + STR(state->endp_label)); + + /* + * Save every good session under its physical endpoint address. + */ + scache_save_endp(smtp_scache, var_smtp_cache_conn, STR(state->endp_label), + STR(state->endp_prop), fd); +} + +/* smtp_reuse_common - common session reuse code */ + +static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd, + const char *label) +{ + const char *myname = "smtp_reuse_common"; + SMTP_ITERATOR *iter = state->iterator; + SMTP_SESSION *session; + + /* + * Re-activate the SMTP_SESSION object. + */ + session = smtp_session_activate(fd, state->iterator, state->dest_prop, + state->endp_prop); + if (session == 0) { + msg_warn("%s: bad cached session attribute for %s", myname, label); + (void) close(fd); + return (0); + } + state->session = session; + session->state = state; + + /* + * Send an RSET probe to verify that the session is still good. + */ + if (smtp_rset(state) < 0 + || (session->features & SMTP_FEATURE_RSET_REJECTED) != 0) { + smtp_session_free(session); + return (state->session = 0); + } + + /* + * Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE. + */ + vstream_tweak_sock(session->stream); + + /* + * Update the list of used cached addresses. + */ + htable_enter(state->cache_used, STR(iter->addr), (void *) 0); + + return (session); +} + +/* smtp_reuse_nexthop - reuse session cached under nexthop name */ + +SMTP_SESSION *smtp_reuse_nexthop(SMTP_STATE *state, int name_key_flags) +{ + SMTP_SESSION *session; + int fd; + + /* + * Look up the session by its logical name. + */ + smtp_key_prefix(state->dest_label, SMTP_REUSE_KEY_DELIM_NA, + state->iterator, name_key_flags); + if ((fd = scache_find_dest(smtp_scache, STR(state->dest_label), + state->dest_prop, state->endp_prop)) < 0) + return (0); + + /* + * Re-activate the SMTP_SESSION object, and verify that the session is + * still good. + */ + session = smtp_reuse_common(state, fd, STR(state->dest_label)); + return (session); +} + +/* smtp_reuse_addr - reuse session cached under numerical address */ + +SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, int endp_key_flags) +{ + SMTP_SESSION *session; + int fd; + + /* + * Address-based reuse is safe for security levels that require TLS + * certificate checks, as long as the current nexhop is included in the + * cache lookup key (COND_TLS_SMTP_KEY_FLAG_CUR_NEXTHOP). This is + * sufficient to prevent the reuse of a TLS-authenticated connection to + * the same MX hostname, IP address, and port, but for a different + * current nexthop destination with a different TLS policy. + */ + + /* + * Look up the session by its IP address. This means that we have no + * destination-to-address binding properties. + */ + smtp_key_prefix(state->endp_label, SMTP_REUSE_KEY_DELIM_NA, + state->iterator, endp_key_flags); + if ((fd = scache_find_endp(smtp_scache, STR(state->endp_label), + state->endp_prop)) < 0) + return (0); + VSTRING_RESET(state->dest_prop); + VSTRING_TERMINATE(state->dest_prop); + + /* + * Re-activate the SMTP_SESSION object, and verify that the session is + * still good. + */ + session = smtp_reuse_common(state, fd, STR(state->endp_label)); + + return (session); +} |