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