1 /* $NetBSD: smtp_reuse.c,v 1.4 2023/12/23 20:30:45 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* smtp_reuse 3 6 /* SUMMARY 7 /* SMTP session cache glue 8 /* SYNOPSIS 9 /* #include <smtp.h> 10 /* #include <smtp_reuse.h> 11 /* 12 /* void smtp_save_session(state, name_key_flags, endp_key_flags) 13 /* SMTP_STATE *state; 14 /* int name_key_flags; 15 /* int endp_key_flags; 16 /* 17 /* SMTP_SESSION *smtp_reuse_nexthop(state, name_key_flags) 18 /* SMTP_STATE *state; 19 /* int name_key_flags; 20 /* 21 /* SMTP_SESSION *smtp_reuse_addr(state, endp_key_flags) 22 /* SMTP_STATE *state; 23 /* int endp_key_flags; 24 /* DESCRIPTION 25 /* This module implements the SMTP client specific interface to 26 /* the generic session cache infrastructure. 27 /* 28 /* The caller needs to include additional state in _key_flags 29 /* to avoid false sharing of SASL-authenticated or TLS-authenticated 30 /* sessions. 31 /* 32 /* smtp_save_session() stores the current session under the 33 /* delivery request next-hop logical destination (if applicable) 34 /* and under the remote server address. The SMTP_SESSION object 35 /* is destroyed. 36 /* 37 /* smtp_reuse_nexthop() looks up a cached session by its 38 /* delivery request next-hop destination, and verifies that 39 /* the session is still alive. The restored session information 40 /* includes the "best MX" bit and overrides the iterator dest, 41 /* host and addr fields. The result is null in case of failure. 42 /* 43 /* smtp_reuse_addr() looks up a cached session by its server 44 /* address, and verifies that the session is still alive. 45 /* The restored session information does not include the "best 46 /* MX" bit, and does not override the iterator dest, host and 47 /* addr fields. The result is null in case of failure. 48 /* 49 /* Arguments: 50 /* .IP state 51 /* SMTP client state, including the current session, the original 52 /* next-hop domain, etc. 53 /* .IP name_key_flags 54 /* Explicit declaration of context that should be used to look 55 /* up a cached connection by its logical destination. 56 /* See smtp_key(3) for details. 57 /* .IP endp_key_flags 58 /* Explicit declaration of context that should be used to look 59 /* up a cached connection by its server address. 60 /* See smtp_key(3) for details. 61 /* LICENSE 62 /* .ad 63 /* .fi 64 /* The Secure Mailer license must be distributed with this software. 65 /* AUTHOR(S) 66 /* Wietse Venema 67 /* IBM T.J. Watson Research 68 /* P.O. Box 704 69 /* Yorktown Heights, NY 10598, USA 70 /* 71 /* Wietse Venema 72 /* Google, Inc. 73 /* 111 8th Avenue 74 /* New York, NY 10011, USA 75 /*--*/ 76 77 /* System library. */ 78 79 #include <sys_defs.h> 80 #include <sys/socket.h> 81 #include <netinet/in.h> 82 #include <arpa/inet.h> 83 #include <unistd.h> 84 #include <string.h> 85 86 /* Utility library. */ 87 88 #include <msg.h> 89 #include <mymalloc.h> 90 #include <vstream.h> 91 #include <vstring.h> 92 #include <htable.h> 93 #include <stringops.h> 94 95 /* Global library. */ 96 97 #include <scache.h> 98 #include <mail_params.h> 99 100 /* Application-specific. */ 101 102 #include <smtp.h> 103 #include <smtp_reuse.h> 104 105 /* 106 * Key field delimiter, and place holder field value for 107 * unavailable/inapplicable information. 108 */ 109 #define SMTP_REUSE_KEY_DELIM_NA "\n*" 110 111 /* smtp_save_session - save session under next-hop name and server address */ 112 113 void smtp_save_session(SMTP_STATE *state, int name_key_flags, 114 int endp_key_flags) 115 { 116 SMTP_SESSION *session = state->session; 117 int fd; 118 119 /* 120 * Encode the delivery request next-hop destination, if applicable. Reuse 121 * storage that is also used for cache lookup queries. 122 * 123 * HAVE_SCACHE_REQUEST_NEXTHOP() controls whether or not to reuse or cache a 124 * connection by its delivery request next-hop destination. The idea is 125 * 1) to allow a reuse request to skip over bad hosts, and 2) to avoid 126 * caching a less-preferred connection when a more-preferred connection 127 * was possible. 128 */ 129 if (HAVE_SCACHE_REQUEST_NEXTHOP(state)) 130 smtp_key_prefix(state->dest_label, SMTP_REUSE_KEY_DELIM_NA, 131 state->iterator, name_key_flags); 132 133 /* 134 * Encode the physical endpoint name. Reuse storage that is also used for 135 * cache lookup queries. 136 */ 137 smtp_key_prefix(state->endp_label, SMTP_REUSE_KEY_DELIM_NA, 138 state->iterator, endp_key_flags); 139 140 /* 141 * Passivate the SMTP_SESSION object, destroying the object in the 142 * process. Reuse storage that is also used for cache lookup results. 143 */ 144 fd = smtp_session_passivate(session, state->dest_prop, state->endp_prop); 145 state->session = 0; 146 147 /* 148 * Save the session under the delivery request next-hop name, if 149 * applicable. 150 * 151 * XXX The logical to physical binding can be kept for as long as the DNS 152 * allows us to (but that could result in the caching of lots of unused 153 * bindings). The session should be idle for no more than 30 seconds or 154 * so. 155 */ 156 if (HAVE_SCACHE_REQUEST_NEXTHOP(state)) 157 scache_save_dest(smtp_scache, var_smtp_cache_conn, 158 STR(state->dest_label), STR(state->dest_prop), 159 STR(state->endp_label)); 160 161 /* 162 * Save every good session under its physical endpoint address. 163 */ 164 scache_save_endp(smtp_scache, var_smtp_cache_conn, STR(state->endp_label), 165 STR(state->endp_prop), fd); 166 } 167 168 /* smtp_reuse_common - common session reuse code */ 169 170 static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd, 171 const char *label) 172 { 173 const char *myname = "smtp_reuse_common"; 174 SMTP_ITERATOR *iter = state->iterator; 175 SMTP_SESSION *session; 176 177 if (msg_verbose) { 178 msg_info("%s: dest_prop='%s'", myname, STR(state->dest_prop)); 179 msg_info("%s: endp_prop='%s'", myname, STR(state->endp_prop)); 180 } 181 182 /* 183 * Re-activate the SMTP_SESSION object. 184 */ 185 session = smtp_session_activate(fd, state->iterator, state->dest_prop, 186 state->endp_prop); 187 if (session == 0) { 188 msg_warn("%s: bad cached session attribute for %s", myname, label); 189 (void) close(fd); 190 return (0); 191 } 192 state->session = session; 193 session->state = state; 194 195 /* 196 * Send an RSET probe to verify that the session is still good. 197 */ 198 if (smtp_rset(state) < 0 199 || (session->features & SMTP_FEATURE_RSET_REJECTED) != 0) { 200 smtp_session_free(session); 201 return (state->session = 0); 202 } 203 204 /* 205 * Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE. 206 */ 207 vstream_tweak_sock(session->stream); 208 209 /* 210 * Update the list of used cached addresses. 211 */ 212 htable_enter(state->cache_used, STR(iter->addr), (void *) 0); 213 214 return (session); 215 } 216 217 /* smtp_reuse_nexthop - reuse session cached under nexthop name */ 218 219 SMTP_SESSION *smtp_reuse_nexthop(SMTP_STATE *state, int name_key_flags) 220 { 221 const char *myname = "smtp_reuse_nexthop"; 222 SMTP_SESSION *session; 223 int fd; 224 225 /* 226 * Look up the session by its logical name. 227 */ 228 smtp_key_prefix(state->dest_label, SMTP_REUSE_KEY_DELIM_NA, 229 state->iterator, name_key_flags); 230 if (msg_verbose) 231 msg_info("%s: dest_label='%s'", myname, STR(state->dest_label)); 232 if ((fd = scache_find_dest(smtp_scache, STR(state->dest_label), 233 state->dest_prop, state->endp_prop)) < 0) 234 return (0); 235 236 /* 237 * Re-activate the SMTP_SESSION object, and verify that the session is 238 * still good. 239 */ 240 session = smtp_reuse_common(state, fd, STR(state->dest_label)); 241 return (session); 242 } 243 244 /* smtp_reuse_addr - reuse session cached under numerical address */ 245 246 SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, int endp_key_flags) 247 { 248 const char *myname = "smtp_reuse_addr"; 249 SMTP_SESSION *session; 250 int fd; 251 252 /* 253 * Address-based reuse is safe for security levels that require TLS 254 * certificate checks, as long as the current nexhop is included in the 255 * cache lookup key (COND_TLS_SMTP_KEY_FLAG_CUR_NEXTHOP). This is 256 * sufficient to prevent the reuse of a TLS-authenticated connection to 257 * the same MX hostname, IP address, and port, but for a different 258 * current nexthop destination with a different TLS policy. 259 */ 260 261 /* 262 * Look up the session by its IP address. This means that we have no 263 * destination-to-address binding properties. 264 */ 265 smtp_key_prefix(state->endp_label, SMTP_REUSE_KEY_DELIM_NA, 266 state->iterator, endp_key_flags); 267 if (msg_verbose) 268 msg_info("%s: endp_label='%s'", myname, STR(state->endp_label)); 269 if ((fd = scache_find_endp(smtp_scache, STR(state->endp_label), 270 state->endp_prop)) < 0) 271 return (0); 272 VSTRING_RESET(state->dest_prop); 273 VSTRING_TERMINATE(state->dest_prop); 274 275 /* 276 * Re-activate the SMTP_SESSION object, and verify that the session is 277 * still good. 278 */ 279 session = smtp_reuse_common(state, fd, STR(state->endp_label)); 280 281 return (session); 282 } 283