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