xref: /netbsd-src/external/ibm-public/postfix/dist/src/smtp/smtp_reuse.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
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