Home | History | Annotate | Line # | Download | only in smtp
      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 
    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 
    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 
    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 
    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