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