xref: /netbsd-src/external/ibm-public/postfix/dist/src/smtp/smtp_session.c (revision c48c605c14fd8622b523d1d6a3f0c0bad133ea89)
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