xref: /netbsd-src/external/ibm-public/postfix/dist/src/smtp/smtp_session.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: smtp_session.c,v 1.2 2017/02/14 01:16:48 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	smtp_session 3
6 /* SUMMARY
7 /*	SMTP_SESSION structure management
8 /* SYNOPSIS
9 /*	#include "smtp.h"
10 /*
11 /*	SMTP_SESSION *smtp_session_alloc(stream, iter, start, flags)
12 /*	VSTREAM	*stream;
13 /*	SMTP_ITERATOR *iter;
14 /*	time_t	start;
15 /*	int	flags;
16 /*
17 /*	void	smtp_session_free(session)
18 /*	SMTP_SESSION *session;
19 /*
20 /*	int	smtp_session_passivate(session, dest_prop, endp_prop)
21 /*	SMTP_SESSION *session;
22 /*	VSTRING	*dest_prop;
23 /*	VSTRING	*endp_prop;
24 /*
25 /*	SMTP_SESSION *smtp_session_activate(fd, iter, dest_prop, endp_prop)
26 /*	int	fd;
27 /*	SMTP_ITERATOR *iter;
28 /*	VSTRING	*dest_prop;
29 /*	VSTRING	*endp_prop;
30 /* DESCRIPTION
31 /*	smtp_session_alloc() allocates memory for an SMTP_SESSION structure
32 /*	and initializes it with the given stream and destination, host name
33 /*	and address information.  The host name and address strings are
34 /*	copied. The port is in network byte order.
35 /*
36 /*	smtp_session_free() destroys an SMTP_SESSION structure and its
37 /*	members, making memory available for reuse. It will handle the
38 /*	case of a null stream and will assume it was given a different
39 /*	purpose.
40 /*
41 /*	smtp_session_passivate() flattens an SMTP session so that
42 /*	it can be cached. The SMTP_SESSION structure is destroyed.
43 /*
44 /*	smtp_session_activate() inflates a flattened SMTP session
45 /*	so that it can be used. The input property arguments are
46 /*	modified.
47 /*
48 /*	Arguments:
49 /* .IP stream
50 /*	A full-duplex stream.
51 /* .IP iter
52 /*	The literal next-hop or fall-back destination including
53 /*	the optional [] and including the :port or :service;
54 /*	the name of the remote host;
55 /*	the printable address of the remote host;
56 /*	the remote port in network byte order.
57 /* .IP start
58 /*	The time when this connection was opened.
59 /* .IP flags
60 /*	Zero or more of the following:
61 /* .RS
62 /* .IP SMTP_MISC_FLAG_CONN_LOAD
63 /*	Enable re-use of cached SMTP or LMTP connections.
64 /* .IP SMTP_MISC_FLAG_CONN_STORE
65 /*	Enable saving of cached SMTP or LMTP connections.
66 /* .RE
67 /*	SMTP_MISC_FLAG_CONN_MASK corresponds with both _LOAD and _STORE.
68 /* .IP dest_prop
69 /*	Destination specific session properties: the server is the
70 /*	best MX host for the current logical destination, the dest,
71 /*	host, and addr properties. When dest_prop is non-empty, it
72 /*	overrides the iterator dest, host, and addr properties.  It
73 /*	is the caller's responsibility to save the current nexthop
74 /*	with SMTP_ITER_SAVE_DEST() and to restore it afterwards
75 /*	with SMTP_ITER_RESTORE_DEST() before trying alternatives.
76 /* .IP endp_prop
77 /*	Endpoint specific session properties: all the features
78 /*	advertised by the remote server.
79 /* LICENSE
80 /* .ad
81 /* .fi
82 /*	The Secure Mailer license must be distributed with this software.
83 /* AUTHOR(S)
84 /*	Wietse Venema
85 /*	IBM T.J. Watson Research
86 /*	P.O. Box 704
87 /*	Yorktown Heights, NY 10598, USA
88 /*
89 /*	Viktor Dukhovni
90 /*--*/
91 
92 /* System library. */
93 
94 #include <sys_defs.h>
95 #include <stdlib.h>
96 #include <string.h>
97 #include <netinet/in.h>
98 
99 /* Utility library. */
100 
101 #include <msg.h>
102 #include <mymalloc.h>
103 #include <vstring.h>
104 #include <vstream.h>
105 #include <stringops.h>
106 
107 /* Global library. */
108 
109 #include <mime_state.h>
110 #include <debug_peer.h>
111 #include <mail_params.h>
112 
113 /* Application-specific. */
114 
115 #include "smtp.h"
116 #include "smtp_sasl.h"
117 
118 /* smtp_session_alloc - allocate and initialize SMTP_SESSION structure */
119 
120 SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, SMTP_ITERATOR *iter,
121 				         time_t start, int flags)
122 {
123     SMTP_SESSION *session;
124     const char *host = STR(iter->host);
125     const char *addr = STR(iter->addr);
126     unsigned port = iter->port;
127 
128     session = (SMTP_SESSION *) mymalloc(sizeof(*session));
129     session->stream = stream;
130     session->iterator = iter;
131     session->namaddr = concatenate(host, "[", addr, "]", (char *) 0);
132     session->helo = 0;
133     session->port = port;
134     session->features = 0;
135 
136     session->size_limit = 0;
137     session->error_mask = 0;
138     session->buffer = vstring_alloc(100);
139     session->scratch = vstring_alloc(100);
140     session->scratch2 = vstring_alloc(100);
141     smtp_chat_init(session);
142     session->mime_state = 0;
143 
144     if (session->port) {
145 	vstring_sprintf(session->buffer, "%s:%d",
146 			session->namaddr, ntohs(session->port));
147 	session->namaddrport = mystrdup(STR(session->buffer));
148     } else
149 	session->namaddrport = mystrdup(session->namaddr);
150 
151     session->send_proto_helo = 0;
152 
153     if (flags & SMTP_MISC_FLAG_CONN_STORE)
154 	CACHE_THIS_SESSION_UNTIL(start + var_smtp_reuse_time);
155     else
156 	DONT_CACHE_THIS_SESSION;
157     session->reuse_count = 0;
158     USE_NEWBORN_SESSION;			/* He's not dead Jim! */
159 
160 #ifdef USE_SASL_AUTH
161     smtp_sasl_connect(session);
162 #endif
163 
164 #ifdef USE_TLS
165     session->tls_context = 0;
166     session->tls_retry_plain = 0;
167     session->tls_nexthop = 0;
168 #endif
169     session->state = 0;
170     debug_peer_check(host, addr);
171     return (session);
172 }
173 
174 /* smtp_session_free - destroy SMTP_SESSION structure and contents */
175 
176 void    smtp_session_free(SMTP_SESSION *session)
177 {
178 #ifdef USE_TLS
179     if (session->stream) {
180 	vstream_fflush(session->stream);
181 	if (session->tls_context)
182 	    tls_client_stop(smtp_tls_ctx, session->stream,
183 			  var_smtp_starttls_tmout, 0, session->tls_context);
184     }
185 #endif
186     if (session->stream)
187 	vstream_fclose(session->stream);
188     myfree(session->namaddr);
189     myfree(session->namaddrport);
190     if (session->helo)
191 	myfree(session->helo);
192 
193     vstring_free(session->buffer);
194     vstring_free(session->scratch);
195     vstring_free(session->scratch2);
196 
197     if (session->history)
198 	smtp_chat_reset(session);
199     if (session->mime_state)
200 	mime_state_free(session->mime_state);
201 
202 #ifdef USE_SASL_AUTH
203     smtp_sasl_cleanup(session);
204 #endif
205 
206     debug_peer_restore();
207     myfree((void *) session);
208 }
209 
210 /* smtp_session_passivate - passivate an SMTP_SESSION object */
211 
212 int     smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop,
213 			               VSTRING *endp_prop)
214 {
215     SMTP_ITERATOR *iter = session->iterator;
216     int     fd;
217 
218     /*
219      * Encode the local-to-physical binding properties: whether or not this
220      * server is best MX host for the next-hop or fall-back logical
221      * destination (this information is needed for loop handling in
222      * smtp_proto()).
223      *
224      * XXX It would be nice to have a VSTRING to VSTREAM adapter so that we can
225      * serialize the properties with attr_print() instead of using ad-hoc,
226      * non-reusable, code and hard-coded format strings.
227      *
228      * TODO: save SASL username and password information so that we can
229      * correctly save a reused authenticated connection.
230      *
231      */
232     vstring_sprintf(dest_prop, "%s\n%s\n%s\n%u",
233 		    STR(iter->dest), STR(iter->host), STR(iter->addr),
234 		    session->features & SMTP_FEATURE_DESTINATION_MASK);
235 
236     /*
237      * Encode the physical endpoint properties: all the session properties
238      * except for "session from cache", "best MX", or "RSET failure".
239      *
240      * XXX It would be nice to have a VSTRING to VSTREAM adapter so that we can
241      * serialize the properties with attr_print() instead of using obscure
242      * hard-coded format strings.
243      *
244      * XXX Should also record an absolute time when a session must be closed,
245      * how many non-delivering mail transactions there were during this
246      * session, and perhaps other statistics, so that we don't reuse a
247      * session too much.
248      *
249      * XXX Be sure to use unsigned types in the format string. Sign characters
250      * would be rejected by the alldig() test on the reading end.
251      */
252     vstring_sprintf(endp_prop, "%u\n%u\n%lu",
253 		    session->reuse_count,
254 		    session->features & SMTP_FEATURE_ENDPOINT_MASK,
255 		    (long) session->expire_time);
256 
257     /*
258      * Append the passivated SASL attributes.
259      */
260 #ifdef notdef
261     if (smtp_sasl_enable)
262 	smtp_sasl_passivate(endp_prop, session);
263 #endif
264 
265     /*
266      * Salvage the underlying file descriptor, and destroy the session
267      * object.
268      */
269     fd = vstream_fileno(session->stream);
270     vstream_fdclose(session->stream);
271     session->stream = 0;
272     smtp_session_free(session);
273 
274     return (fd);
275 }
276 
277 /* smtp_session_activate - re-activate a passivated SMTP_SESSION object */
278 
279 SMTP_SESSION *smtp_session_activate(int fd, SMTP_ITERATOR *iter,
280 				            VSTRING *dest_prop,
281 				            VSTRING *endp_prop)
282 {
283     const char *myname = "smtp_session_activate";
284     SMTP_SESSION *session;
285     char   *dest_props;
286     char   *endp_props;
287     const char *prop;
288     const char *dest;
289     const char *host;
290     const char *addr;
291     unsigned features;			/* server features */
292     time_t  expire_time;		/* session re-use expiration time */
293     unsigned reuse_count;		/* # times reused */
294 
295     /*
296      * XXX it would be nice to have a VSTRING to VSTREAM adapter so that we
297      * can de-serialize the properties with attr_scan(), instead of using
298      * ad-hoc, non-reusable code.
299      *
300      * XXX As a preliminary solution we use mystrtok(), but that function is not
301      * suitable for zero-length fields.
302      */
303     endp_props = STR(endp_prop);
304     if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) {
305 	msg_warn("%s: bad cached session reuse count property", myname);
306 	return (0);
307     }
308     reuse_count = atoi(prop);
309     if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) {
310 	msg_warn("%s: bad cached session features property", myname);
311 	return (0);
312     }
313     features = atoi(prop);
314     if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) {
315 	msg_warn("%s: bad cached session expiration time property", myname);
316 	return (0);
317     }
318 #ifdef MISSING_STRTOUL
319     expire_time = strtol(prop, 0, 10);
320 #else
321     expire_time = strtoul(prop, 0, 10);
322 #endif
323 
324     /*
325      * Clobber the iterator's current nexthop, host and address fields with
326      * cached-connection information. This is done when a session is looked
327      * up by request nexthop instead of address and port. It is the caller's
328      * responsibility to save and restore the request nexthop with
329      * SMTP_ITER_SAVE_DEST() and SMTP_ITER_RESTORE_DEST().
330      *
331      * TODO: Eliminate the duplication between SMTP_ITERATOR and SMTP_SESSION.
332      *
333      * TODO: restore SASL username and password information so that we can
334      * correctly save a reused authenticated connection.
335      */
336     if (dest_prop && VSTRING_LEN(dest_prop)) {
337 	dest_props = STR(dest_prop);
338 	if ((dest = mystrtok(&dest_props, "\n")) == 0) {
339 	    msg_warn("%s: missing cached session destination property", myname);
340 	    return (0);
341 	}
342 	if ((host = mystrtok(&dest_props, "\n")) == 0) {
343 	    msg_warn("%s: missing cached session hostname property", myname);
344 	    return (0);
345 	}
346 	if ((addr = mystrtok(&dest_props, "\n")) == 0) {
347 	    msg_warn("%s: missing cached session address property", myname);
348 	    return (0);
349 	}
350 	if ((prop = mystrtok(&dest_props, "\n")) == 0 || !alldig(prop)) {
351 	    msg_warn("%s: bad cached destination features property", myname);
352 	    return (0);
353 	}
354 	features |= atoi(prop);
355 	SMTP_ITER_CLOBBER(iter, dest, host, addr);
356     }
357 
358     /*
359      * Allright, bundle up what we have sofar.
360      */
361 #define NO_FLAGS	0
362 
363     session = smtp_session_alloc(vstream_fdopen(fd, O_RDWR), iter,
364 				 (time_t) 0, NO_FLAGS);
365     session->features = (features | SMTP_FEATURE_FROM_CACHE);
366     CACHE_THIS_SESSION_UNTIL(expire_time);
367     session->reuse_count = ++reuse_count;
368 
369     if (msg_verbose)
370 	msg_info("%s: dest=%s host=%s addr=%s port=%u features=0x%x, "
371 		 "ttl=%ld, reuse=%d",
372 		 myname, STR(iter->dest), STR(iter->host),
373 		 STR(iter->addr), ntohs(iter->port), features,
374 		 (long) (expire_time - time((time_t *) 0)),
375 		 reuse_count);
376 
377     /*
378      * Re-activate the SASL attributes.
379      */
380 #ifdef notdef
381     if (smtp_sasl_enable && smtp_sasl_activate(session, endp_props) < 0) {
382 	vstream_fdclose(session->stream);
383 	session->stream = 0;
384 	smtp_session_free(session);
385 	return (0);
386     }
387 #endif
388 
389     return (session);
390 }
391