xref: /netbsd-src/external/ibm-public/postfix/dist/src/smtp/smtp_reuse.c (revision c48c605c14fd8622b523d1d6a3f0c0bad133ea89)
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 
smtp_save_session(SMTP_STATE * state,int name_key_flags,int endp_key_flags)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 
smtp_reuse_common(SMTP_STATE * state,int fd,const char * label)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 
smtp_reuse_nexthop(SMTP_STATE * state,int name_key_flags)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 
smtp_reuse_addr(SMTP_STATE * state,int endp_key_flags)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