Home | History | Annotate | Line # | Download | only in smtp
      1 /*	$NetBSD: smtp_reuse.c,v 1.4 2023/12/23 20:30:45 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	smtp_reuse 3
      6 /* SUMMARY
      7 /*	SMTP session cache glue
      8 /* SYNOPSIS
      9 /*	#include <smtp.h>
     10 /*	#include <smtp_reuse.h>
     11 /*
     12 /*	void	smtp_save_session(state, name_key_flags, endp_key_flags)
     13 /*	SMTP_STATE *state;
     14 /*	int	name_key_flags;
     15 /*	int	endp_key_flags;
     16 /*
     17 /*	SMTP_SESSION *smtp_reuse_nexthop(state, name_key_flags)
     18 /*	SMTP_STATE *state;
     19 /*	int	name_key_flags;
     20 /*
     21 /*	SMTP_SESSION *smtp_reuse_addr(state, endp_key_flags)
     22 /*	SMTP_STATE *state;
     23 /*	int	endp_key_flags;
     24 /* DESCRIPTION
     25 /*	This module implements the SMTP client specific interface to
     26 /*	the generic session cache infrastructure.
     27 /*
     28 /*	The caller needs to include additional state in _key_flags
     29 /*	to avoid false sharing of SASL-authenticated or TLS-authenticated
     30 /*	sessions.
     31 /*
     32 /*	smtp_save_session() stores the current session under the
     33 /*	delivery request next-hop logical destination (if applicable)
     34 /*	and under the remote server address. The SMTP_SESSION object
     35 /*	is destroyed.
     36 /*
     37 /*	smtp_reuse_nexthop() looks up a cached session by its
     38 /*	delivery request next-hop destination, and verifies that
     39 /*	the session is still alive. The restored session information
     40 /*	includes the "best MX" bit and overrides the iterator dest,
     41 /*	host and addr fields. The result is null in case of failure.
     42 /*
     43 /*	smtp_reuse_addr() looks up a cached session by its server
     44 /*	address, and verifies that the session is still alive.
     45 /*	The restored session information does not include the "best
     46 /*	MX" bit, and does not override the iterator dest, host and
     47 /*	addr fields. The result is null in case of failure.
     48 /*
     49 /*	Arguments:
     50 /* .IP state
     51 /*	SMTP client state, including the current session, the original
     52 /*	next-hop domain, etc.
     53 /* .IP name_key_flags
     54 /*	Explicit declaration of context that should be used to look
     55 /*	up a cached connection by its logical destination.
     56 /*	See smtp_key(3) for details.
     57 /* .IP endp_key_flags
     58 /*	Explicit declaration of context that should be used to look
     59 /*	up a cached connection by its server address.
     60 /*	See smtp_key(3) for details.
     61 /* LICENSE
     62 /* .ad
     63 /* .fi
     64 /*	The Secure Mailer license must be distributed with this software.
     65 /* AUTHOR(S)
     66 /*	Wietse Venema
     67 /*	IBM T.J. Watson Research
     68 /*	P.O. Box 704
     69 /*	Yorktown Heights, NY 10598, USA
     70 /*
     71 /*	Wietse Venema
     72 /*	Google, Inc.
     73 /*	111 8th Avenue
     74 /*	New York, NY 10011, USA
     75 /*--*/
     76 
     77 /* System library. */
     78 
     79 #include <sys_defs.h>
     80 #include <sys/socket.h>
     81 #include <netinet/in.h>
     82 #include <arpa/inet.h>
     83 #include <unistd.h>
     84 #include <string.h>
     85 
     86 /* Utility library. */
     87 
     88 #include <msg.h>
     89 #include <mymalloc.h>
     90 #include <vstream.h>
     91 #include <vstring.h>
     92 #include <htable.h>
     93 #include <stringops.h>
     94 
     95 /* Global library. */
     96 
     97 #include <scache.h>
     98 #include <mail_params.h>
     99 
    100 /* Application-specific. */
    101 
    102 #include <smtp.h>
    103 #include <smtp_reuse.h>
    104 
    105  /*
    106   * Key field delimiter, and place holder field value for
    107   * unavailable/inapplicable information.
    108   */
    109 #define SMTP_REUSE_KEY_DELIM_NA	"\n*"
    110 
    111 /* smtp_save_session - save session under next-hop name and server address */
    112 
    113 void    smtp_save_session(SMTP_STATE *state, int name_key_flags,
    114 			          int endp_key_flags)
    115 {
    116     SMTP_SESSION *session = state->session;
    117     int     fd;
    118 
    119     /*
    120      * Encode the delivery request next-hop destination, if applicable. Reuse
    121      * storage that is also used for cache lookup queries.
    122      *
    123      * HAVE_SCACHE_REQUEST_NEXTHOP() controls whether or not to reuse or cache a
    124      * connection by its delivery request next-hop destination. The idea is
    125      * 1) to allow a reuse request to skip over bad hosts, and 2) to avoid
    126      * caching a less-preferred connection when a more-preferred connection
    127      * was possible.
    128      */
    129     if (HAVE_SCACHE_REQUEST_NEXTHOP(state))
    130 	smtp_key_prefix(state->dest_label, SMTP_REUSE_KEY_DELIM_NA,
    131 			state->iterator, name_key_flags);
    132 
    133     /*
    134      * Encode the physical endpoint name. Reuse storage that is also used for
    135      * cache lookup queries.
    136      */
    137     smtp_key_prefix(state->endp_label, SMTP_REUSE_KEY_DELIM_NA,
    138 		    state->iterator, endp_key_flags);
    139 
    140     /*
    141      * Passivate the SMTP_SESSION object, destroying the object in the
    142      * process. Reuse storage that is also used for cache lookup results.
    143      */
    144     fd = smtp_session_passivate(session, state->dest_prop, state->endp_prop);
    145     state->session = 0;
    146 
    147     /*
    148      * Save the session under the delivery request next-hop name, if
    149      * applicable.
    150      *
    151      * XXX The logical to physical binding can be kept for as long as the DNS
    152      * allows us to (but that could result in the caching of lots of unused
    153      * bindings). The session should be idle for no more than 30 seconds or
    154      * so.
    155      */
    156     if (HAVE_SCACHE_REQUEST_NEXTHOP(state))
    157 	scache_save_dest(smtp_scache, var_smtp_cache_conn,
    158 			 STR(state->dest_label), STR(state->dest_prop),
    159 			 STR(state->endp_label));
    160 
    161     /*
    162      * Save every good session under its physical endpoint address.
    163      */
    164     scache_save_endp(smtp_scache, var_smtp_cache_conn, STR(state->endp_label),
    165 		     STR(state->endp_prop), fd);
    166 }
    167 
    168 /* smtp_reuse_common - common session reuse code */
    169 
    170 static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd,
    171 				               const char *label)
    172 {
    173     const char *myname = "smtp_reuse_common";
    174     SMTP_ITERATOR *iter = state->iterator;
    175     SMTP_SESSION *session;
    176 
    177     if (msg_verbose) {
    178 	msg_info("%s: dest_prop='%s'", myname, STR(state->dest_prop));
    179 	msg_info("%s: endp_prop='%s'", myname, STR(state->endp_prop));
    180     }
    181 
    182     /*
    183      * Re-activate the SMTP_SESSION object.
    184      */
    185     session = smtp_session_activate(fd, state->iterator, state->dest_prop,
    186 				    state->endp_prop);
    187     if (session == 0) {
    188 	msg_warn("%s: bad cached session attribute for %s", myname, label);
    189 	(void) close(fd);
    190 	return (0);
    191     }
    192     state->session = session;
    193     session->state = state;
    194 
    195     /*
    196      * Send an RSET probe to verify that the session is still good.
    197      */
    198     if (smtp_rset(state) < 0
    199 	|| (session->features & SMTP_FEATURE_RSET_REJECTED) != 0) {
    200 	smtp_session_free(session);
    201 	return (state->session = 0);
    202     }
    203 
    204     /*
    205      * Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE.
    206      */
    207     vstream_tweak_sock(session->stream);
    208 
    209     /*
    210      * Update the list of used cached addresses.
    211      */
    212     htable_enter(state->cache_used, STR(iter->addr), (void *) 0);
    213 
    214     return (session);
    215 }
    216 
    217 /* smtp_reuse_nexthop - reuse session cached under nexthop name */
    218 
    219 SMTP_SESSION *smtp_reuse_nexthop(SMTP_STATE *state, int name_key_flags)
    220 {
    221     const char *myname = "smtp_reuse_nexthop";
    222     SMTP_SESSION *session;
    223     int     fd;
    224 
    225     /*
    226      * Look up the session by its logical name.
    227      */
    228     smtp_key_prefix(state->dest_label, SMTP_REUSE_KEY_DELIM_NA,
    229 		    state->iterator, name_key_flags);
    230     if (msg_verbose)
    231 	msg_info("%s: dest_label='%s'", myname, STR(state->dest_label));
    232     if ((fd = scache_find_dest(smtp_scache, STR(state->dest_label),
    233 			       state->dest_prop, state->endp_prop)) < 0)
    234 	return (0);
    235 
    236     /*
    237      * Re-activate the SMTP_SESSION object, and verify that the session is
    238      * still good.
    239      */
    240     session = smtp_reuse_common(state, fd, STR(state->dest_label));
    241     return (session);
    242 }
    243 
    244 /* smtp_reuse_addr - reuse session cached under numerical address */
    245 
    246 SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, int endp_key_flags)
    247 {
    248     const char *myname = "smtp_reuse_addr";
    249     SMTP_SESSION *session;
    250     int     fd;
    251 
    252     /*
    253      * Address-based reuse is safe for security levels that require TLS
    254      * certificate checks, as long as the current nexhop is included in the
    255      * cache lookup key (COND_TLS_SMTP_KEY_FLAG_CUR_NEXTHOP). This is
    256      * sufficient to prevent the reuse of a TLS-authenticated connection to
    257      * the same MX hostname, IP address, and port, but for a different
    258      * current nexthop destination with a different TLS policy.
    259      */
    260 
    261     /*
    262      * Look up the session by its IP address. This means that we have no
    263      * destination-to-address binding properties.
    264      */
    265     smtp_key_prefix(state->endp_label, SMTP_REUSE_KEY_DELIM_NA,
    266 		    state->iterator, endp_key_flags);
    267     if (msg_verbose)
    268 	msg_info("%s: endp_label='%s'", myname, STR(state->endp_label));
    269     if ((fd = scache_find_endp(smtp_scache, STR(state->endp_label),
    270 			       state->endp_prop)) < 0)
    271 	return (0);
    272     VSTRING_RESET(state->dest_prop);
    273     VSTRING_TERMINATE(state->dest_prop);
    274 
    275     /*
    276      * Re-activate the SMTP_SESSION object, and verify that the session is
    277      * still good.
    278      */
    279     session = smtp_reuse_common(state, fd, STR(state->endp_label));
    280 
    281     return (session);
    282 }
    283