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