xref: /netbsd-src/external/ibm-public/postfix/dist/src/smtp/smtp_reuse.c (revision c48c605c14fd8622b523d1d6a3f0c0bad133ea89)
1*c48c605cSchristos /*	$NetBSD: smtp_reuse.c,v 1.4 2023/12/23 20:30:45 christos Exp $	*/
241fbaed0Stron 
341fbaed0Stron /*++
441fbaed0Stron /* NAME
541fbaed0Stron /*	smtp_reuse 3
641fbaed0Stron /* SUMMARY
741fbaed0Stron /*	SMTP session cache glue
841fbaed0Stron /* SYNOPSIS
941fbaed0Stron /*	#include <smtp.h>
1041fbaed0Stron /*	#include <smtp_reuse.h>
1141fbaed0Stron /*
1216d67a18Stron /*	void	smtp_save_session(state, name_key_flags, endp_key_flags)
1341fbaed0Stron /*	SMTP_STATE *state;
1416d67a18Stron /*	int	name_key_flags;
1516d67a18Stron /*	int	endp_key_flags;
1641fbaed0Stron /*
1716d67a18Stron /*	SMTP_SESSION *smtp_reuse_nexthop(state, name_key_flags)
1841fbaed0Stron /*	SMTP_STATE *state;
1916d67a18Stron /*	int	name_key_flags;
2041fbaed0Stron /*
2116d67a18Stron /*	SMTP_SESSION *smtp_reuse_addr(state, endp_key_flags)
2241fbaed0Stron /*	SMTP_STATE *state;
2316d67a18Stron /*	int	endp_key_flags;
2441fbaed0Stron /* DESCRIPTION
2541fbaed0Stron /*	This module implements the SMTP client specific interface to
2641fbaed0Stron /*	the generic session cache infrastructure.
2741fbaed0Stron /*
2833881f77Schristos /*	The caller needs to include additional state in _key_flags
2933881f77Schristos /*	to avoid false sharing of SASL-authenticated or TLS-authenticated
3033881f77Schristos /*	sessions.
31e6ca80d4Stron /*
3241fbaed0Stron /*	smtp_save_session() stores the current session under the
3333881f77Schristos /*	delivery request next-hop logical destination (if applicable)
3433881f77Schristos /*	and under the remote server address. The SMTP_SESSION object
3533881f77Schristos /*	is destroyed.
3641fbaed0Stron /*
3733881f77Schristos /*	smtp_reuse_nexthop() looks up a cached session by its
3833881f77Schristos /*	delivery request next-hop destination, and verifies that
3933881f77Schristos /*	the session is still alive. The restored session information
4033881f77Schristos /*	includes the "best MX" bit and overrides the iterator dest,
4133881f77Schristos /*	host and addr fields. The result is null in case of failure.
4241fbaed0Stron /*
4341fbaed0Stron /*	smtp_reuse_addr() looks up a cached session by its server
4441fbaed0Stron /*	address, and verifies that the session is still alive.
4516d67a18Stron /*	The restored session information does not include the "best
4616d67a18Stron /*	MX" bit, and does not override the iterator dest, host and
4733881f77Schristos /*	addr fields. The result is null in case of failure.
4841fbaed0Stron /*
4941fbaed0Stron /*	Arguments:
5041fbaed0Stron /* .IP state
5141fbaed0Stron /*	SMTP client state, including the current session, the original
5241fbaed0Stron /*	next-hop domain, etc.
5316d67a18Stron /* .IP name_key_flags
5416d67a18Stron /*	Explicit declaration of context that should be used to look
5516d67a18Stron /*	up a cached connection by its logical destination.
5616d67a18Stron /*	See smtp_key(3) for details.
5716d67a18Stron /* .IP endp_key_flags
5816d67a18Stron /*	Explicit declaration of context that should be used to look
5916d67a18Stron /*	up a cached connection by its server address.
6016d67a18Stron /*	See smtp_key(3) for details.
6141fbaed0Stron /* LICENSE
6241fbaed0Stron /* .ad
6341fbaed0Stron /* .fi
6441fbaed0Stron /*	The Secure Mailer license must be distributed with this software.
6541fbaed0Stron /* AUTHOR(S)
6641fbaed0Stron /*	Wietse Venema
6741fbaed0Stron /*	IBM T.J. Watson Research
6841fbaed0Stron /*	P.O. Box 704
6941fbaed0Stron /*	Yorktown Heights, NY 10598, USA
7033881f77Schristos /*
7133881f77Schristos /*	Wietse Venema
7233881f77Schristos /*	Google, Inc.
7333881f77Schristos /*	111 8th Avenue
7433881f77Schristos /*	New York, NY 10011, USA
7541fbaed0Stron /*--*/
7641fbaed0Stron 
7741fbaed0Stron /* System library. */
7841fbaed0Stron 
7941fbaed0Stron #include <sys_defs.h>
8041fbaed0Stron #include <sys/socket.h>
8141fbaed0Stron #include <netinet/in.h>
8241fbaed0Stron #include <arpa/inet.h>
8341fbaed0Stron #include <unistd.h>
8441fbaed0Stron #include <string.h>
8541fbaed0Stron 
8641fbaed0Stron /* Utility library. */
8741fbaed0Stron 
8841fbaed0Stron #include <msg.h>
8941fbaed0Stron #include <mymalloc.h>
9041fbaed0Stron #include <vstream.h>
9141fbaed0Stron #include <vstring.h>
9241fbaed0Stron #include <htable.h>
9341fbaed0Stron #include <stringops.h>
9441fbaed0Stron 
9541fbaed0Stron /* Global library. */
9641fbaed0Stron 
9741fbaed0Stron #include <scache.h>
9841fbaed0Stron #include <mail_params.h>
9941fbaed0Stron 
10041fbaed0Stron /* Application-specific. */
10141fbaed0Stron 
10241fbaed0Stron #include <smtp.h>
10341fbaed0Stron #include <smtp_reuse.h>
10441fbaed0Stron 
10541fbaed0Stron  /*
10616d67a18Stron   * Key field delimiter, and place holder field value for
10716d67a18Stron   * unavailable/inapplicable information.
10841fbaed0Stron   */
10916d67a18Stron #define SMTP_REUSE_KEY_DELIM_NA	"\n*"
11041fbaed0Stron 
11141fbaed0Stron /* smtp_save_session - save session under next-hop name and server address */
11241fbaed0Stron 
smtp_save_session(SMTP_STATE * state,int name_key_flags,int endp_key_flags)11316d67a18Stron void    smtp_save_session(SMTP_STATE *state, int name_key_flags,
11416d67a18Stron 			          int endp_key_flags)
11541fbaed0Stron {
11641fbaed0Stron     SMTP_SESSION *session = state->session;
11741fbaed0Stron     int     fd;
11841fbaed0Stron 
11941fbaed0Stron     /*
12033881f77Schristos      * Encode the delivery request next-hop destination, if applicable. Reuse
12133881f77Schristos      * storage that is also used for cache lookup queries.
12233881f77Schristos      *
12333881f77Schristos      * HAVE_SCACHE_REQUEST_NEXTHOP() controls whether or not to reuse or cache a
12433881f77Schristos      * connection by its delivery request next-hop destination. The idea is
12533881f77Schristos      * 1) to allow a reuse request to skip over bad hosts, and 2) to avoid
12633881f77Schristos      * caching a less-preferred connection when a more-preferred connection
12733881f77Schristos      * was possible.
12841fbaed0Stron      */
12933881f77Schristos     if (HAVE_SCACHE_REQUEST_NEXTHOP(state))
13016d67a18Stron 	smtp_key_prefix(state->dest_label, SMTP_REUSE_KEY_DELIM_NA,
13116d67a18Stron 			state->iterator, name_key_flags);
13241fbaed0Stron 
13341fbaed0Stron     /*
13441fbaed0Stron      * Encode the physical endpoint name. Reuse storage that is also used for
13541fbaed0Stron      * cache lookup queries.
13641fbaed0Stron      */
13716d67a18Stron     smtp_key_prefix(state->endp_label, SMTP_REUSE_KEY_DELIM_NA,
13816d67a18Stron 		    state->iterator, endp_key_flags);
13941fbaed0Stron 
14041fbaed0Stron     /*
14141fbaed0Stron      * Passivate the SMTP_SESSION object, destroying the object in the
14241fbaed0Stron      * process. Reuse storage that is also used for cache lookup results.
14341fbaed0Stron      */
14441fbaed0Stron     fd = smtp_session_passivate(session, state->dest_prop, state->endp_prop);
14541fbaed0Stron     state->session = 0;
14641fbaed0Stron 
14741fbaed0Stron     /*
14833881f77Schristos      * Save the session under the delivery request next-hop name, if
14933881f77Schristos      * applicable.
15041fbaed0Stron      *
15141fbaed0Stron      * XXX The logical to physical binding can be kept for as long as the DNS
15241fbaed0Stron      * allows us to (but that could result in the caching of lots of unused
15341fbaed0Stron      * bindings). The session should be idle for no more than 30 seconds or
15441fbaed0Stron      * so.
15541fbaed0Stron      */
15633881f77Schristos     if (HAVE_SCACHE_REQUEST_NEXTHOP(state))
15733881f77Schristos 	scache_save_dest(smtp_scache, var_smtp_cache_conn,
15833881f77Schristos 			 STR(state->dest_label), STR(state->dest_prop),
15933881f77Schristos 			 STR(state->endp_label));
16041fbaed0Stron 
16141fbaed0Stron     /*
16241fbaed0Stron      * Save every good session under its physical endpoint address.
16341fbaed0Stron      */
16441fbaed0Stron     scache_save_endp(smtp_scache, var_smtp_cache_conn, STR(state->endp_label),
16541fbaed0Stron 		     STR(state->endp_prop), fd);
16641fbaed0Stron }
16741fbaed0Stron 
16841fbaed0Stron /* smtp_reuse_common - common session reuse code */
16941fbaed0Stron 
smtp_reuse_common(SMTP_STATE * state,int fd,const char * label)17041fbaed0Stron static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd,
17141fbaed0Stron 				               const char *label)
17241fbaed0Stron {
17341fbaed0Stron     const char *myname = "smtp_reuse_common";
17416d67a18Stron     SMTP_ITERATOR *iter = state->iterator;
17541fbaed0Stron     SMTP_SESSION *session;
17641fbaed0Stron 
177*c48c605cSchristos     if (msg_verbose) {
178*c48c605cSchristos 	msg_info("%s: dest_prop='%s'", myname, STR(state->dest_prop));
179*c48c605cSchristos 	msg_info("%s: endp_prop='%s'", myname, STR(state->endp_prop));
180*c48c605cSchristos     }
181*c48c605cSchristos 
18241fbaed0Stron     /*
18341fbaed0Stron      * Re-activate the SMTP_SESSION object.
18441fbaed0Stron      */
18516d67a18Stron     session = smtp_session_activate(fd, state->iterator, state->dest_prop,
18616d67a18Stron 				    state->endp_prop);
18741fbaed0Stron     if (session == 0) {
18841fbaed0Stron 	msg_warn("%s: bad cached session attribute for %s", myname, label);
18941fbaed0Stron 	(void) close(fd);
19041fbaed0Stron 	return (0);
19141fbaed0Stron     }
19241fbaed0Stron     state->session = session;
19341fbaed0Stron     session->state = state;
19441fbaed0Stron 
19541fbaed0Stron     /*
19641fbaed0Stron      * Send an RSET probe to verify that the session is still good.
19741fbaed0Stron      */
19841fbaed0Stron     if (smtp_rset(state) < 0
19941fbaed0Stron 	|| (session->features & SMTP_FEATURE_RSET_REJECTED) != 0) {
20041fbaed0Stron 	smtp_session_free(session);
20141fbaed0Stron 	return (state->session = 0);
20241fbaed0Stron     }
20341fbaed0Stron 
20441fbaed0Stron     /*
20541fbaed0Stron      * Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE.
20641fbaed0Stron      */
20741fbaed0Stron     vstream_tweak_sock(session->stream);
20841fbaed0Stron 
20941fbaed0Stron     /*
21041fbaed0Stron      * Update the list of used cached addresses.
21141fbaed0Stron      */
212e262b48eSchristos     htable_enter(state->cache_used, STR(iter->addr), (void *) 0);
21341fbaed0Stron 
21441fbaed0Stron     return (session);
21541fbaed0Stron }
21641fbaed0Stron 
21716d67a18Stron /* smtp_reuse_nexthop - reuse session cached under nexthop name */
21841fbaed0Stron 
smtp_reuse_nexthop(SMTP_STATE * state,int name_key_flags)21916d67a18Stron SMTP_SESSION *smtp_reuse_nexthop(SMTP_STATE *state, int name_key_flags)
22041fbaed0Stron {
221*c48c605cSchristos     const char *myname = "smtp_reuse_nexthop";
22241fbaed0Stron     SMTP_SESSION *session;
22341fbaed0Stron     int     fd;
22441fbaed0Stron 
22541fbaed0Stron     /*
22616d67a18Stron      * Look up the session by its logical name.
22716d67a18Stron      */
22816d67a18Stron     smtp_key_prefix(state->dest_label, SMTP_REUSE_KEY_DELIM_NA,
22916d67a18Stron 		    state->iterator, name_key_flags);
230*c48c605cSchristos     if (msg_verbose)
231*c48c605cSchristos 	msg_info("%s: dest_label='%s'", myname, STR(state->dest_label));
23241fbaed0Stron     if ((fd = scache_find_dest(smtp_scache, STR(state->dest_label),
23341fbaed0Stron 			       state->dest_prop, state->endp_prop)) < 0)
23441fbaed0Stron 	return (0);
23541fbaed0Stron 
23641fbaed0Stron     /*
23741fbaed0Stron      * Re-activate the SMTP_SESSION object, and verify that the session is
23841fbaed0Stron      * still good.
23941fbaed0Stron      */
24041fbaed0Stron     session = smtp_reuse_common(state, fd, STR(state->dest_label));
24141fbaed0Stron     return (session);
24241fbaed0Stron }
24341fbaed0Stron 
24441fbaed0Stron /* smtp_reuse_addr - reuse session cached under numerical address */
24541fbaed0Stron 
smtp_reuse_addr(SMTP_STATE * state,int endp_key_flags)24616d67a18Stron SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, int endp_key_flags)
24741fbaed0Stron {
248*c48c605cSchristos     const char *myname = "smtp_reuse_addr";
24941fbaed0Stron     SMTP_SESSION *session;
25041fbaed0Stron     int     fd;
25141fbaed0Stron 
25241fbaed0Stron     /*
25333881f77Schristos      * Address-based reuse is safe for security levels that require TLS
25433881f77Schristos      * certificate checks, as long as the current nexhop is included in the
25533881f77Schristos      * cache lookup key (COND_TLS_SMTP_KEY_FLAG_CUR_NEXTHOP). This is
25633881f77Schristos      * sufficient to prevent the reuse of a TLS-authenticated connection to
25733881f77Schristos      * the same MX hostname, IP address, and port, but for a different
25833881f77Schristos      * current nexthop destination with a different TLS policy.
25941fbaed0Stron      */
26041fbaed0Stron 
26141fbaed0Stron     /*
26241fbaed0Stron      * Look up the session by its IP address. This means that we have no
26341fbaed0Stron      * destination-to-address binding properties.
26441fbaed0Stron      */
26516d67a18Stron     smtp_key_prefix(state->endp_label, SMTP_REUSE_KEY_DELIM_NA,
26616d67a18Stron 		    state->iterator, endp_key_flags);
267*c48c605cSchristos     if (msg_verbose)
268*c48c605cSchristos 	msg_info("%s: endp_label='%s'", myname, STR(state->endp_label));
26941fbaed0Stron     if ((fd = scache_find_endp(smtp_scache, STR(state->endp_label),
27041fbaed0Stron 			       state->endp_prop)) < 0)
27141fbaed0Stron 	return (0);
27241fbaed0Stron     VSTRING_RESET(state->dest_prop);
27341fbaed0Stron     VSTRING_TERMINATE(state->dest_prop);
27441fbaed0Stron 
27541fbaed0Stron     /*
27641fbaed0Stron      * Re-activate the SMTP_SESSION object, and verify that the session is
27741fbaed0Stron      * still good.
27841fbaed0Stron      */
27941fbaed0Stron     session = smtp_reuse_common(state, fd, STR(state->endp_label));
28041fbaed0Stron 
28141fbaed0Stron     return (session);
28241fbaed0Stron }
283