1 /* $NetBSD: smtp_reuse.c,v 1.3 2020/03/18 19:05:20 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 /* 178 * Re-activate the SMTP_SESSION object. 179 */ 180 session = smtp_session_activate(fd, state->iterator, state->dest_prop, 181 state->endp_prop); 182 if (session == 0) { 183 msg_warn("%s: bad cached session attribute for %s", myname, label); 184 (void) close(fd); 185 return (0); 186 } 187 state->session = session; 188 session->state = state; 189 190 /* 191 * Send an RSET probe to verify that the session is still good. 192 */ 193 if (smtp_rset(state) < 0 194 || (session->features & SMTP_FEATURE_RSET_REJECTED) != 0) { 195 smtp_session_free(session); 196 return (state->session = 0); 197 } 198 199 /* 200 * Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE. 201 */ 202 vstream_tweak_sock(session->stream); 203 204 /* 205 * Update the list of used cached addresses. 206 */ 207 htable_enter(state->cache_used, STR(iter->addr), (void *) 0); 208 209 return (session); 210 } 211 212 /* smtp_reuse_nexthop - reuse session cached under nexthop name */ 213 214 SMTP_SESSION *smtp_reuse_nexthop(SMTP_STATE *state, int name_key_flags) 215 { 216 SMTP_SESSION *session; 217 int fd; 218 219 /* 220 * Look up the session by its logical name. 221 */ 222 smtp_key_prefix(state->dest_label, SMTP_REUSE_KEY_DELIM_NA, 223 state->iterator, name_key_flags); 224 if ((fd = scache_find_dest(smtp_scache, STR(state->dest_label), 225 state->dest_prop, state->endp_prop)) < 0) 226 return (0); 227 228 /* 229 * Re-activate the SMTP_SESSION object, and verify that the session is 230 * still good. 231 */ 232 session = smtp_reuse_common(state, fd, STR(state->dest_label)); 233 return (session); 234 } 235 236 /* smtp_reuse_addr - reuse session cached under numerical address */ 237 238 SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, int endp_key_flags) 239 { 240 SMTP_SESSION *session; 241 int fd; 242 243 /* 244 * Address-based reuse is safe for security levels that require TLS 245 * certificate checks, as long as the current nexhop is included in the 246 * cache lookup key (COND_TLS_SMTP_KEY_FLAG_CUR_NEXTHOP). This is 247 * sufficient to prevent the reuse of a TLS-authenticated connection to 248 * the same MX hostname, IP address, and port, but for a different 249 * current nexthop destination with a different TLS policy. 250 */ 251 252 /* 253 * Look up the session by its IP address. This means that we have no 254 * destination-to-address binding properties. 255 */ 256 smtp_key_prefix(state->endp_label, SMTP_REUSE_KEY_DELIM_NA, 257 state->iterator, endp_key_flags); 258 if ((fd = scache_find_endp(smtp_scache, STR(state->endp_label), 259 state->endp_prop)) < 0) 260 return (0); 261 VSTRING_RESET(state->dest_prop); 262 VSTRING_TERMINATE(state->dest_prop); 263 264 /* 265 * Re-activate the SMTP_SESSION object, and verify that the session is 266 * still good. 267 */ 268 session = smtp_reuse_common(state, fd, STR(state->endp_label)); 269 270 return (session); 271 } 272