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