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