1 /* $NetBSD: smtp_session.c,v 1.5 2023/12/23 20:30:45 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* smtp_session 3
6 /* SUMMARY
7 /* SMTP_SESSION structure management
8 /* SYNOPSIS
9 /* #include "smtp.h"
10 /*
11 /* SMTP_SESSION *smtp_session_alloc(stream, iter, start, flags)
12 /* VSTREAM *stream;
13 /* SMTP_ITERATOR *iter;
14 /* time_t start;
15 /* int flags;
16 /*
17 /* void smtp_session_free(session)
18 /* SMTP_SESSION *session;
19 /*
20 /* int smtp_session_passivate(session, dest_prop, endp_prop)
21 /* SMTP_SESSION *session;
22 /* VSTRING *dest_prop;
23 /* VSTRING *endp_prop;
24 /*
25 /* SMTP_SESSION *smtp_session_activate(fd, iter, dest_prop, endp_prop)
26 /* int fd;
27 /* SMTP_ITERATOR *iter;
28 /* VSTRING *dest_prop;
29 /* VSTRING *endp_prop;
30 /* DESCRIPTION
31 /* smtp_session_alloc() allocates memory for an SMTP_SESSION structure
32 /* and initializes it with the given stream and destination, host name
33 /* and address information. The host name and address strings are
34 /* copied. The port is in network byte order.
35 /*
36 /* smtp_session_free() destroys an SMTP_SESSION structure and its
37 /* members, making memory available for reuse. It will handle the
38 /* case of a null stream and will assume it was given a different
39 /* purpose.
40 /*
41 /* smtp_session_passivate() flattens an SMTP session (including
42 /* TLS context) so that it can be cached. The SMTP_SESSION
43 /* structure is destroyed.
44 /*
45 /* smtp_session_activate() inflates a flattened SMTP session
46 /* so that it can be used. The input property arguments are
47 /* modified.
48 /*
49 /* Arguments:
50 /* .IP stream
51 /* A full-duplex stream.
52 /* .IP iter
53 /* The literal next-hop or fall-back destination including
54 /* the optional [] and including the :port or :service;
55 /* the name of the remote host;
56 /* the printable address of the remote host;
57 /* the remote port in network byte order.
58 /* .IP start
59 /* The time when this connection was opened.
60 /* .IP flags
61 /* Zero or more of the following:
62 /* .RS
63 /* .IP SMTP_MISC_FLAG_CONN_LOAD
64 /* Enable re-use of cached SMTP or LMTP connections.
65 /* .IP SMTP_MISC_FLAG_CONN_STORE
66 /* Enable saving of cached SMTP or LMTP connections.
67 /* .RE
68 /* SMTP_MISC_FLAG_CONN_MASK corresponds with both _LOAD and _STORE.
69 /* .IP dest_prop
70 /* Destination specific session properties: the server is the
71 /* best MX host for the current logical destination, the dest,
72 /* host, and addr properties. When dest_prop is non-empty, it
73 /* overrides the iterator dest, host, and addr properties. It
74 /* is the caller's responsibility to save the current nexthop
75 /* with SMTP_ITER_SAVE_DEST() and to restore it afterwards
76 /* with SMTP_ITER_RESTORE_DEST() before trying alternatives.
77 /* .IP endp_prop
78 /* Endpoint specific session properties: all the features
79 /* advertised by the remote server.
80 /* LICENSE
81 /* .ad
82 /* .fi
83 /* The Secure Mailer license must be distributed with this software.
84 /* AUTHOR(S)
85 /* Wietse Venema
86 /* IBM T.J. Watson Research
87 /* P.O. Box 704
88 /* Yorktown Heights, NY 10598, USA
89 /*
90 /* Wietse Venema
91 /* Google, Inc.
92 /* 111 8th Avenue
93 /* New York, NY 10011, USA
94 /*
95 /* Viktor Dukhovni
96 /*--*/
97
98 /* System library. */
99
100 #include <sys_defs.h>
101 #include <stdlib.h>
102 #include <string.h>
103 #include <netinet/in.h>
104
105 /* Utility library. */
106
107 #include <msg.h>
108 #include <mymalloc.h>
109 #include <vstring.h>
110 #include <vstream.h>
111 #include <stringops.h>
112
113 /* Global library. */
114
115 #include <mime_state.h>
116 #include <debug_peer.h>
117 #include <mail_params.h>
118
119 /* TLS Library. */
120
121 #include <tls_proxy.h>
122
123 /* Application-specific. */
124
125 #include "smtp.h"
126 #include "smtp_sasl.h"
127
128 /*
129 * Local, because these are meaningful only for code in this file.
130 */
131 #define SESS_ATTR_DEST "destination"
132 #define SESS_ATTR_HOST "host_name"
133 #define SESS_ATTR_ADDR "host_addr"
134 #define SESS_ATTR_PORT "host_port"
135 #define SESS_ATTR_DEST_FEATURES "destination_features"
136
137 #define SESS_ATTR_TLS_LEVEL "tls_level"
138 #define SESS_ATTR_REUSE_COUNT "reuse_count"
139 #define SESS_ATTR_ENDP_FEATURES "endpoint_features"
140 #define SESS_ATTR_EXPIRE_TIME "expire_time"
141
142 /* smtp_session_alloc - allocate and initialize SMTP_SESSION structure */
143
smtp_session_alloc(VSTREAM * stream,SMTP_ITERATOR * iter,time_t start,int flags)144 SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, SMTP_ITERATOR *iter,
145 time_t start, int flags)
146 {
147 SMTP_SESSION *session;
148 const char *host = STR(iter->host);
149 const char *addr = STR(iter->addr);
150 unsigned port = iter->port;
151
152 session = (SMTP_SESSION *) mymalloc(sizeof(*session));
153 session->stream = stream;
154 session->iterator = iter;
155 session->namaddr = concatenate(host, "[", addr, "]", (char *) 0);
156 session->helo = 0;
157 session->port = port;
158 session->features = 0;
159
160 session->size_limit = 0;
161 session->error_mask = 0;
162 session->buffer = vstring_alloc(100);
163 session->scratch = vstring_alloc(100);
164 session->scratch2 = vstring_alloc(100);
165 smtp_chat_init(session);
166 session->mime_state = 0;
167
168 if (session->port) {
169 vstring_sprintf(session->buffer, "%s:%d",
170 session->namaddr, ntohs(session->port));
171 session->namaddrport = mystrdup(STR(session->buffer));
172 } else
173 session->namaddrport = mystrdup(session->namaddr);
174
175 session->send_proto_helo = 0;
176
177 if (flags & SMTP_MISC_FLAG_CONN_STORE)
178 CACHE_THIS_SESSION_UNTIL(start + var_smtp_reuse_time);
179 else
180 DONT_CACHE_THIS_SESSION;
181 session->reuse_count = 0;
182 USE_NEWBORN_SESSION; /* He's not dead Jim! */
183
184 #ifdef USE_SASL_AUTH
185 smtp_sasl_connect(session);
186 #endif
187
188 #ifdef USE_TLS
189 session->tls_context = 0;
190 session->tls_retry_plain = 0;
191 session->tls_nexthop = 0;
192 #endif
193 session->state = 0;
194 debug_peer_check(host, addr);
195 return (session);
196 }
197
198 /* smtp_session_free - destroy SMTP_SESSION structure and contents */
199
smtp_session_free(SMTP_SESSION * session)200 void smtp_session_free(SMTP_SESSION *session)
201 {
202 #ifdef USE_TLS
203 if (session->stream) {
204 vstream_fflush(session->stream);
205 }
206 if (session->tls_context) {
207 if (session->features &
208 (SMTP_FEATURE_FROM_CACHE | SMTP_FEATURE_FROM_PROXY))
209 tls_proxy_context_free(session->tls_context);
210 else
211 tls_client_stop(smtp_tls_ctx, session->stream,
212 var_smtp_starttls_tmout, 0, session->tls_context);
213 }
214 #endif
215 if (session->stream)
216 vstream_fclose(session->stream);
217 myfree(session->namaddr);
218 myfree(session->namaddrport);
219 if (session->helo)
220 myfree(session->helo);
221
222 vstring_free(session->buffer);
223 vstring_free(session->scratch);
224 vstring_free(session->scratch2);
225
226 if (session->history)
227 smtp_chat_reset(session);
228 if (session->mime_state)
229 mime_state_free(session->mime_state);
230
231 #ifdef USE_SASL_AUTH
232 smtp_sasl_cleanup(session);
233 #endif
234
235 if (session->state->debug_peer_per_nexthop == 0)
236 debug_peer_restore();
237 myfree((void *) session);
238 }
239
240 /* smtp_session_passivate - passivate an SMTP_SESSION object */
241
smtp_session_passivate(SMTP_SESSION * session,VSTRING * dest_prop,VSTRING * endp_prop)242 int smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop,
243 VSTRING *endp_prop)
244 {
245 SMTP_ITERATOR *iter = session->iterator;
246 VSTREAM *mp;
247 int fd;
248
249 /*
250 * Encode the delivery request next-hop to endpoint binding properties:
251 * whether or not this server is best MX host for the delivery request
252 * next-hop or fall-back logical destination (this information is needed
253 * for loop handling in smtp_proto()).
254 *
255 * TODO: save SASL username and password information so that we can
256 * correctly save a reused authenticated connection.
257 *
258 * These memory writes should never fail.
259 */
260 if ((mp = vstream_memopen(dest_prop, O_WRONLY)) == 0
261 || attr_print_plain(mp, ATTR_FLAG_NONE,
262 SEND_ATTR_STR(SESS_ATTR_DEST, STR(iter->dest)),
263 SEND_ATTR_STR(SESS_ATTR_HOST, STR(iter->host)),
264 SEND_ATTR_STR(SESS_ATTR_ADDR, STR(iter->addr)),
265 SEND_ATTR_UINT(SESS_ATTR_PORT, iter->port),
266 SEND_ATTR_INT(SESS_ATTR_DEST_FEATURES,
267 session->features & SMTP_FEATURE_DESTINATION_MASK),
268 ATTR_TYPE_END) != 0
269 || vstream_fclose(mp) != 0)
270 msg_fatal("smtp_session_passivate: can't save dest properties: %m");
271
272 /*
273 * Encode the physical endpoint properties: all the session properties
274 * except for "session from cache", "best MX", or "RSET failure". Plus
275 * the TLS level, reuse count, and connection expiration time.
276 *
277 * XXX Should also record how many non-delivering mail transactions there
278 * were during this session, and perhaps other statistics, so that we
279 * don't reuse a session too much.
280 *
281 * TODO: passivate SASL username and password information so that we can
282 * correctly save a reused authenticated connection.
283 *
284 * These memory writes should never fail.
285 */
286 if ((mp = vstream_memopen(endp_prop, O_WRONLY)) == 0
287 || attr_print_plain(mp, ATTR_FLAG_NONE,
288 #ifdef USE_TLS
289 SEND_ATTR_INT(SESS_ATTR_TLS_LEVEL,
290 session->state->tls->level),
291 #endif
292 SEND_ATTR_INT(SESS_ATTR_REUSE_COUNT,
293 session->reuse_count),
294 SEND_ATTR_INT(SESS_ATTR_ENDP_FEATURES,
295 session->features & SMTP_FEATURE_ENDPOINT_MASK),
296 SEND_ATTR_LONG(SESS_ATTR_EXPIRE_TIME,
297 (long) session->expire_time),
298 ATTR_TYPE_END) != 0
299
300 /*
301 * Append the passivated TLS context. These memory writes should never
302 * fail.
303 */
304 #ifdef USE_TLS
305 || (session->tls_context
306 && attr_print_plain(mp, ATTR_FLAG_NONE,
307 SEND_ATTR_FUNC(tls_proxy_context_print,
308 (void *) session->tls_context),
309 ATTR_TYPE_END) != 0)
310 #endif
311 || vstream_fclose(mp) != 0)
312 msg_fatal("smtp_session_passivate: cannot save TLS context: %m");
313
314 /*
315 * Salvage the underlying file descriptor, and destroy the session
316 * object.
317 */
318 fd = vstream_fileno(session->stream);
319 vstream_fdclose(session->stream);
320 session->stream = 0;
321 smtp_session_free(session);
322
323 return (fd);
324 }
325
326 /* smtp_session_activate - re-activate a passivated SMTP_SESSION object */
327
smtp_session_activate(int fd,SMTP_ITERATOR * iter,VSTRING * dest_prop,VSTRING * endp_prop)328 SMTP_SESSION *smtp_session_activate(int fd, SMTP_ITERATOR *iter,
329 VSTRING *dest_prop,
330 VSTRING *endp_prop)
331 {
332 const char *myname = "smtp_session_activate";
333 VSTREAM *mp;
334 SMTP_SESSION *session;
335 int endp_features; /* server features */
336 int dest_features; /* server features */
337 long expire_time; /* session re-use expiration time */
338 int reuse_count; /* # times reused */
339
340 #ifdef USE_TLS
341 TLS_SESS_STATE *tls_context = 0;
342 SMTP_TLS_POLICY *tls = iter->parent->tls;
343
344 #define TLS_PROXY_CONTEXT_FREE() do { \
345 if (tls_context) \
346 tls_proxy_context_free(tls_context); \
347 } while (0)
348 #else
349 #define TLS_PROXY_CONTEXT_FREE() /* nothing */
350 #endif
351
352 #define SMTP_SESSION_ACTIVATE_ERR_RETURN() do { \
353 TLS_PROXY_CONTEXT_FREE(); \
354 return (0); \
355 } while (0)
356
357 /*
358 * Sanity check: if TLS is required, the cached properties must contain a
359 * TLS context.
360 */
361 if ((mp = vstream_memopen(endp_prop, O_RDONLY)) == 0
362 || attr_scan_plain(mp, ATTR_FLAG_NONE,
363 #ifdef USE_TLS
364 RECV_ATTR_INT(SESS_ATTR_TLS_LEVEL,
365 &tls->level),
366 #endif
367 RECV_ATTR_INT(SESS_ATTR_REUSE_COUNT,
368 &reuse_count),
369 RECV_ATTR_INT(SESS_ATTR_ENDP_FEATURES,
370 &endp_features),
371 RECV_ATTR_LONG(SESS_ATTR_EXPIRE_TIME,
372 &expire_time),
373 ATTR_TYPE_END) != 4
374 #ifdef USE_TLS
375 || ((tls->level > TLS_LEV_MAY
376 || (tls->level == TLS_LEV_MAY && vstream_peek(mp) > 0))
377 && attr_scan_plain(mp, ATTR_FLAG_NONE,
378 RECV_ATTR_FUNC(tls_proxy_context_scan,
379 (void *) &tls_context),
380 ATTR_TYPE_END) != 1)
381 #endif
382 || vstream_fclose(mp) != 0) {
383 msg_warn("smtp_session_activate: bad cached endp properties");
384 SMTP_SESSION_ACTIVATE_ERR_RETURN();
385 }
386
387 /*
388 * Clobber the iterator's current nexthop, host and address fields with
389 * cached-connection information. This is done when a session is looked
390 * up by delivery request nexthop instead of address and port. It is the
391 * caller's responsibility to save and restore the delivery request
392 * nexthop with SMTP_ITER_SAVE_DEST() and SMTP_ITER_RESTORE_DEST().
393 *
394 * TODO: Eliminate the duplication between SMTP_ITERATOR and SMTP_SESSION.
395 *
396 * TODO: restore SASL username and password information so that we can
397 * correctly save a reused authenticated connection.
398 */
399 if (dest_prop && VSTRING_LEN(dest_prop)) {
400 if ((mp = vstream_memopen(dest_prop, O_RDONLY)) == 0
401 || attr_scan_plain(mp, ATTR_FLAG_NONE,
402 RECV_ATTR_STR(SESS_ATTR_DEST, iter->dest),
403 RECV_ATTR_STR(SESS_ATTR_HOST, iter->host),
404 RECV_ATTR_STR(SESS_ATTR_ADDR, iter->addr),
405 RECV_ATTR_UINT(SESS_ATTR_PORT, &iter->port),
406 RECV_ATTR_INT(SESS_ATTR_DEST_FEATURES,
407 &dest_features),
408 ATTR_TYPE_END) != 5
409 || vstream_fclose(mp) != 0) {
410 msg_warn("smtp_session_passivate: bad cached dest properties");
411 SMTP_SESSION_ACTIVATE_ERR_RETURN();
412 }
413 } else {
414 dest_features = 0;
415 }
416 #ifdef USE_TLS
417 if (msg_verbose)
418 msg_info("%s: tls_level=%d", myname, tls->level);
419 #endif
420
421 /*
422 * Allright, bundle up what we have sofar.
423 */
424 #define NO_FLAGS 0
425
426 session = smtp_session_alloc(vstream_fdopen(fd, O_RDWR), iter,
427 (time_t) 0, NO_FLAGS);
428 session->features =
429 (endp_features | dest_features | SMTP_FEATURE_FROM_CACHE);
430 #ifdef USE_TLS
431 session->tls_context = tls_context;
432 #endif
433 CACHE_THIS_SESSION_UNTIL(expire_time);
434 session->reuse_count = ++reuse_count;
435
436 if (msg_verbose)
437 msg_info("%s: dest=%s host=%s addr=%s port=%u features=0x%x, "
438 "ttl=%ld, reuse=%d",
439 myname, STR(iter->dest), STR(iter->host),
440 STR(iter->addr), ntohs(iter->port),
441 endp_features | dest_features,
442 (long) (expire_time - time((time_t *) 0)),
443 reuse_count);
444
445 #if USE_TLS
446 if (tls_context)
447 tls_log_summary(TLS_ROLE_CLIENT, TLS_USAGE_USED,
448 session->tls_context);
449 #endif
450
451 return (session);
452 }
453