1 /* $NetBSD: smtp_reuse.c,v 1.1.1.3 2013/09/25 19:06:35 tron 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) 13 /* SMTP_STATE *state; 14 /* 15 /* SMTP_SESSION *smtp_reuse_domain(state, lookup_mx, domain, port) 16 /* SMTP_STATE *state; 17 /* int lookup_mx; 18 /* char *domain; 19 /* unsigned port; 20 /* 21 /* SMTP_SESSION *smtp_reuse_addr(state, addr, port) 22 /* SMTP_STATE *state; 23 /* const char *addr; 24 /* unsigned port; 25 /* DESCRIPTION 26 /* This module implements the SMTP client specific interface to 27 /* the generic session cache infrastructure. 28 /* 29 /* Each cached connection identifier includes the name of the 30 /* mail delivery service. Thus, cached connections are not 31 /* shared between different services. 32 /* 33 /* smtp_save_session() stores the current session under the 34 /* next-hop logical destination (if available) and under the 35 /* remote server address. The SMTP_SESSION object is destroyed. 36 /* 37 /* smtp_reuse_domain() looks up a cached session by its logical 38 /* destination, and verifies that the session is still alive. 39 /* The restored session information includes the "best MX" bit. 40 /* The result is null in case of failure. 41 /* 42 /* smtp_reuse_addr() looks up a cached session by its server 43 /* address, and verifies that the session is still alive. 44 /* This operation is disabled when the legacy tls_per_site 45 /* or smtp_sasl_password_maps features are enabled. 46 /* The result is null in case of failure. 47 /* 48 /* Arguments: 49 /* .IP state 50 /* SMTP client state, including the current session, the original 51 /* next-hop domain, etc. 52 /* .IP lookup_mx 53 /* Whether or not the domain is subject to MX lookup. 54 /* .IP domain 55 /* Domain name or bare numerical address. 56 /* .IP addr 57 /* The remote server address as printable text. 58 /* .IP port 59 /* The remote server port, network byte order. 60 /* LICENSE 61 /* .ad 62 /* .fi 63 /* The Secure Mailer license must be distributed with this software. 64 /* AUTHOR(S) 65 /* Wietse Venema 66 /* IBM T.J. Watson Research 67 /* P.O. Box 704 68 /* Yorktown Heights, NY 10598, USA 69 /*--*/ 70 71 /* System library. */ 72 73 #include <sys_defs.h> 74 #include <sys/socket.h> 75 #include <netinet/in.h> 76 #include <arpa/inet.h> 77 #include <unistd.h> 78 #include <string.h> 79 80 /* Utility library. */ 81 82 #include <msg.h> 83 #include <mymalloc.h> 84 #include <vstream.h> 85 #include <vstring.h> 86 #include <htable.h> 87 #include <stringops.h> 88 89 /* Global library. */ 90 91 #include <scache.h> 92 #include <mail_params.h> 93 94 /* Application-specific. */ 95 96 #include <smtp.h> 97 #include <smtp_reuse.h> 98 99 /* 100 * We encode the MX lookup/A lookup method into the name under which SMTP 101 * session information is cached. The following macros serve to make the 102 * remainder of the code less obscure. 103 */ 104 #define NO_MX_LOOKUP 0 105 106 #define SMTP_SCACHE_LABEL(mx_lookup_flag) \ 107 ((mx_lookup_flag) ? "%s:%s:%u" : "%s:[%s]:%u") 108 109 /* smtp_save_session - save session under next-hop name and server address */ 110 111 void smtp_save_session(SMTP_STATE *state) 112 { 113 SMTP_SESSION *session = state->session; 114 int fd; 115 116 /* 117 * Encode the next-hop logical destination, if available. Reuse storage 118 * that is also used for cache lookup queries. 119 * 120 * Note: if the label needs to be made more specific (with e.g., SASL login 121 * information), just append the text with vstring_sprintf_append(). 122 */ 123 if (HAVE_NEXTHOP_STATE(state)) 124 vstring_sprintf(state->dest_label, 125 SMTP_SCACHE_LABEL(state->nexthop_lookup_mx), 126 state->service, state->nexthop_domain, 127 ntohs(state->nexthop_port)); 128 129 /* 130 * Encode the physical endpoint name. Reuse storage that is also used for 131 * cache lookup queries. 132 * 133 * Note: if the label needs to be made more specific (with e.g., SASL login 134 * information), just append the text with vstring_sprintf_append(). 135 */ 136 vstring_sprintf(state->endp_label, 137 SMTP_SCACHE_LABEL(NO_MX_LOOKUP), 138 state->service, session->addr, ntohs(session->port)); 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 next-hop name, if available. 149 * 150 * XXX The logical to physical binding can be kept for as long as the DNS 151 * allows us to (but that could result in the caching of lots of unused 152 * bindings). The session should be idle for no more than 30 seconds or 153 * so. 154 */ 155 if (HAVE_NEXTHOP_STATE(state)) 156 scache_save_dest(smtp_scache, var_smtp_cache_conn, STR(state->dest_label), 157 STR(state->dest_prop), STR(state->endp_label)); 158 159 /* 160 * Save every good session under its physical endpoint address. 161 */ 162 scache_save_endp(smtp_scache, var_smtp_cache_conn, STR(state->endp_label), 163 STR(state->endp_prop), fd); 164 } 165 166 /* smtp_reuse_common - common session reuse code */ 167 168 static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd, 169 const char *label) 170 { 171 const char *myname = "smtp_reuse_common"; 172 SMTP_SESSION *session; 173 174 /* 175 * Re-activate the SMTP_SESSION object. 176 */ 177 session = smtp_session_activate(fd, state->dest_prop, state->endp_prop); 178 if (session == 0) { 179 msg_warn("%s: bad cached session attribute for %s", myname, label); 180 (void) close(fd); 181 return (0); 182 } 183 state->session = session; 184 session->state = state; 185 186 /* 187 * XXX Temporary fix. 188 * 189 * Cached connections are always plaintext. They must never be reused when 190 * TLS encryption is required. 191 * 192 * As long as we support the legacy smtp_tls_per_site feature, we must 193 * search the connection cache before making TLS policy decisions. This 194 * is because the policy can depend on the server name. For example, a 195 * site could have a global policy that requires encryption, with 196 * per-server exceptions that allow plaintext. 197 * 198 * With the newer smtp_tls_policy_maps feature, the policy depends on the 199 * next-hop destination only. We can avoid unnecessary connection cache 200 * lookups, because we can compute the TLS policy much earlier. 201 */ 202 #ifdef USE_TLS 203 if (session->tls_level >= TLS_LEV_ENCRYPT) { 204 if (msg_verbose) 205 msg_info("%s: skipping plain-text cached session to %s", 206 myname, label); 207 smtp_quit(state); /* Close politely */ 208 smtp_session_free(session); /* And avoid leaks */ 209 return (state->session = 0); 210 } 211 #endif 212 213 /* 214 * Send an RSET probe to verify that the session is still good. 215 */ 216 if (smtp_rset(state) < 0 217 || (session->features & SMTP_FEATURE_RSET_REJECTED) != 0) { 218 smtp_session_free(session); 219 return (state->session = 0); 220 } 221 222 /* 223 * Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE. 224 */ 225 vstream_tweak_sock(session->stream); 226 227 /* 228 * Update the list of used cached addresses. 229 */ 230 htable_enter(state->cache_used, session->addr, (char *) 0); 231 232 return (session); 233 } 234 235 /* smtp_reuse_domain - reuse session cached under domain name */ 236 237 SMTP_SESSION *smtp_reuse_domain(SMTP_STATE *state, int lookup_mx, 238 const char *domain, unsigned port) 239 { 240 SMTP_SESSION *session; 241 int fd; 242 243 /* 244 * Look up the session by its logical name. 245 * 246 * Note: if the label needs to be made more specific (with e.g., SASL login 247 * information), just append the text with vstring_sprintf_append(). 248 */ 249 vstring_sprintf(state->dest_label, SMTP_SCACHE_LABEL(lookup_mx), 250 state->service, domain, ntohs(port)); 251 if ((fd = scache_find_dest(smtp_scache, STR(state->dest_label), 252 state->dest_prop, state->endp_prop)) < 0) 253 return (0); 254 255 /* 256 * Re-activate the SMTP_SESSION object, and verify that the session is 257 * still good. 258 */ 259 session = smtp_reuse_common(state, fd, STR(state->dest_label)); 260 return (session); 261 } 262 263 /* smtp_reuse_addr - reuse session cached under numerical address */ 264 265 SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, const char *addr, 266 unsigned port) 267 { 268 SMTP_SESSION *session; 269 int fd; 270 271 /* 272 * XXX Disable connection cache lookup by server IP address when the 273 * tls_per_site policy or smtp_sasl_password_maps features are enabled. 274 * This connection may have been created under a different hostname that 275 * resolves to the same IP address. We don't want to use the wrong SASL 276 * credentials or the wrong TLS policy. 277 */ 278 if ((var_smtp_tls_per_site && *var_smtp_tls_per_site) 279 || (var_smtp_tls_policy && *var_smtp_tls_policy)) 280 return (0); 281 282 /* 283 * Look up the session by its IP address. This means that we have no 284 * destination-to-address binding properties. 285 * 286 * Note: if the label needs to be made more specific (with e.g., SASL login 287 * information), just append the text with vstring_sprintf_append(). 288 */ 289 vstring_sprintf(state->endp_label, SMTP_SCACHE_LABEL(NO_MX_LOOKUP), 290 state->service, addr, ntohs(port)); 291 if ((fd = scache_find_endp(smtp_scache, STR(state->endp_label), 292 state->endp_prop)) < 0) 293 return (0); 294 VSTRING_RESET(state->dest_prop); 295 VSTRING_TERMINATE(state->dest_prop); 296 297 /* 298 * Re-activate the SMTP_SESSION object, and verify that the session is 299 * still good. 300 */ 301 session = smtp_reuse_common(state, fd, STR(state->endp_label)); 302 303 /* 304 * XXX What if hostnames don't match (addr->name versus session->name), 305 * or if the SASL login name for this host does not match the SASL login 306 * name that was used when opening this session? If something depends 307 * critically on such information being identical, then that information 308 * should be included in the logical and physical labels under which a 309 * session is cached. 310 */ 311 return (session); 312 } 313