summaryrefslogtreecommitdiffstats
path: root/src/smtp/smtp_reuse.c
blob: f93ba296a0fe5b43170d2c33234b466cd5706539 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
/*++
/* NAME
/*	smtp_reuse 3
/* SUMMARY
/*	SMTP session cache glue
/* SYNOPSIS
/*	#include <smtp.h>
/*	#include <smtp_reuse.h>
/*
/*	void	smtp_save_session(state, name_key_flags, endp_key_flags)
/*	SMTP_STATE *state;
/*	int	name_key_flags;
/*	int	endp_key_flags;
/*
/*	SMTP_SESSION *smtp_reuse_nexthop(state, name_key_flags)
/*	SMTP_STATE *state;
/*	int	name_key_flags;
/*
/*	SMTP_SESSION *smtp_reuse_addr(state, endp_key_flags)
/*	SMTP_STATE *state;
/*	int	endp_key_flags;
/* DESCRIPTION
/*	This module implements the SMTP client specific interface to
/*	the generic session cache infrastructure.
/*
/*	The caller needs to include additional state in _key_flags
/*	to avoid false sharing of SASL-authenticated or TLS-authenticated
/*	sessions.
/*
/*	smtp_save_session() stores the current session under the
/*	delivery request next-hop logical destination (if applicable)
/*	and under the remote server address. The SMTP_SESSION object
/*	is destroyed.
/*
/*	smtp_reuse_nexthop() looks up a cached session by its
/*	delivery request next-hop destination, and verifies that
/*	the session is still alive. The restored session information
/*	includes the "best MX" bit and overrides the iterator dest,
/*	host and addr fields. The result is null in case of failure.
/*
/*	smtp_reuse_addr() looks up a cached session by its server
/*	address, and verifies that the session is still alive.
/*	The restored session information does not include the "best
/*	MX" bit, and does not override the iterator dest, host and
/*	addr fields. The result is null in case of failure.
/*
/*	Arguments:
/* .IP state
/*	SMTP client state, including the current session, the original
/*	next-hop domain, etc.
/* .IP name_key_flags
/*	Explicit declaration of context that should be used to look
/*	up a cached connection by its logical destination.
/*	See smtp_key(3) for details.
/* .IP endp_key_flags
/*	Explicit declaration of context that should be used to look
/*	up a cached connection by its server address.
/*	See smtp_key(3) for details.
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*--*/

/* System library. */

#include <sys_defs.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>

/* Utility library. */

#include <msg.h>
#include <mymalloc.h>
#include <vstream.h>
#include <vstring.h>
#include <htable.h>
#include <stringops.h>

/* Global library. */

#include <scache.h>
#include <mail_params.h>

/* Application-specific. */

#include <smtp.h>
#include <smtp_reuse.h>

 /*
  * Key field delimiter, and place holder field value for
  * unavailable/inapplicable information.
  */
#define SMTP_REUSE_KEY_DELIM_NA	"\n*"

/* smtp_save_session - save session under next-hop name and server address */

void    smtp_save_session(SMTP_STATE *state, int name_key_flags,
			          int endp_key_flags)
{
    SMTP_SESSION *session = state->session;
    int     fd;

    /*
     * Encode the delivery request next-hop destination, if applicable. Reuse
     * storage that is also used for cache lookup queries.
     * 
     * HAVE_SCACHE_REQUEST_NEXTHOP() controls whether or not to reuse or cache a
     * connection by its delivery request next-hop destination. The idea is
     * 1) to allow a reuse request to skip over bad hosts, and 2) to avoid
     * caching a less-preferred connection when a more-preferred connection
     * was possible.
     */
    if (HAVE_SCACHE_REQUEST_NEXTHOP(state))
	smtp_key_prefix(state->dest_label, SMTP_REUSE_KEY_DELIM_NA,
			state->iterator, name_key_flags);

    /*
     * Encode the physical endpoint name. Reuse storage that is also used for
     * cache lookup queries.
     */
    smtp_key_prefix(state->endp_label, SMTP_REUSE_KEY_DELIM_NA,
		    state->iterator, endp_key_flags);

    /*
     * Passivate the SMTP_SESSION object, destroying the object in the
     * process. Reuse storage that is also used for cache lookup results.
     */
    fd = smtp_session_passivate(session, state->dest_prop, state->endp_prop);
    state->session = 0;

    /*
     * Save the session under the delivery request next-hop name, if
     * applicable.
     * 
     * XXX The logical to physical binding can be kept for as long as the DNS
     * allows us to (but that could result in the caching of lots of unused
     * bindings). The session should be idle for no more than 30 seconds or
     * so.
     */
    if (HAVE_SCACHE_REQUEST_NEXTHOP(state))
	scache_save_dest(smtp_scache, var_smtp_cache_conn,
			 STR(state->dest_label), STR(state->dest_prop),
			 STR(state->endp_label));

    /*
     * Save every good session under its physical endpoint address.
     */
    scache_save_endp(smtp_scache, var_smtp_cache_conn, STR(state->endp_label),
		     STR(state->endp_prop), fd);
}

/* smtp_reuse_common - common session reuse code */

static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd,
				               const char *label)
{
    const char *myname = "smtp_reuse_common";
    SMTP_ITERATOR *iter = state->iterator;
    SMTP_SESSION *session;

    /*
     * Re-activate the SMTP_SESSION object.
     */
    session = smtp_session_activate(fd, state->iterator, state->dest_prop,
				    state->endp_prop);
    if (session == 0) {
	msg_warn("%s: bad cached session attribute for %s", myname, label);
	(void) close(fd);
	return (0);
    }
    state->session = session;
    session->state = state;

    /*
     * Send an RSET probe to verify that the session is still good.
     */
    if (smtp_rset(state) < 0
	|| (session->features & SMTP_FEATURE_RSET_REJECTED) != 0) {
	smtp_session_free(session);
	return (state->session = 0);
    }

    /*
     * Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE.
     */
    vstream_tweak_sock(session->stream);

    /*
     * Update the list of used cached addresses.
     */
    htable_enter(state->cache_used, STR(iter->addr), (void *) 0);

    return (session);
}

/* smtp_reuse_nexthop - reuse session cached under nexthop name */

SMTP_SESSION *smtp_reuse_nexthop(SMTP_STATE *state, int name_key_flags)
{
    SMTP_SESSION *session;
    int     fd;

    /*
     * Look up the session by its logical name.
     */
    smtp_key_prefix(state->dest_label, SMTP_REUSE_KEY_DELIM_NA,
		    state->iterator, name_key_flags);
    if ((fd = scache_find_dest(smtp_scache, STR(state->dest_label),
			       state->dest_prop, state->endp_prop)) < 0)
	return (0);

    /*
     * Re-activate the SMTP_SESSION object, and verify that the session is
     * still good.
     */
    session = smtp_reuse_common(state, fd, STR(state->dest_label));
    return (session);
}

/* smtp_reuse_addr - reuse session cached under numerical address */

SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, int endp_key_flags)
{
    SMTP_SESSION *session;
    int     fd;

    /*
     * Address-based reuse is safe for security levels that require TLS
     * certificate checks, as long as the current nexhop is included in the
     * cache lookup key (COND_TLS_SMTP_KEY_FLAG_CUR_NEXTHOP). This is
     * sufficient to prevent the reuse of a TLS-authenticated connection to
     * the same MX hostname, IP address, and port, but for a different
     * current nexthop destination with a different TLS policy.
     */

    /*
     * Look up the session by its IP address. This means that we have no
     * destination-to-address binding properties.
     */
    smtp_key_prefix(state->endp_label, SMTP_REUSE_KEY_DELIM_NA,
		    state->iterator, endp_key_flags);
    if ((fd = scache_find_endp(smtp_scache, STR(state->endp_label),
			       state->endp_prop)) < 0)
	return (0);
    VSTRING_RESET(state->dest_prop);
    VSTRING_TERMINATE(state->dest_prop);

    /*
     * Re-activate the SMTP_SESSION object, and verify that the session is
     * still good.
     */
    session = smtp_reuse_common(state, fd, STR(state->endp_label));

    return (session);
}