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