1 /*++
2 /* NAME
3 /*	postscreen_starttls 3
4 /* SUMMARY
5 /*	postscreen TLS proxy support
6 /* SYNOPSIS
7 /*	#include <postscreen.h>
8 /*
9 /*	int	psc_starttls_open(state, resume_event)
10 /*	PSC_STATE *state;
11 /*	void	(*resume_event)(int unused_event, char *context);
12 /* DESCRIPTION
13 /*	This module inserts the tlsproxy(8) proxy between the
14 /*	postscreen(8) server and the remote SMTP client. The entire
15 /*	process happens in the background, including notification
16 /*	of completion to the remote SMTP client and to the calling
17 /*	application.
18 /*
19 /*	Before calling psc_starttls_open() the caller must turn off
20 /*	all pending timer and I/O event requests on the SMTP client
21 /*	stream.
22 /*
23 /*	psc_starttls_open() starts the first transaction in the
24 /*	tlsproxy(8) hand-off protocol, and sets up event handlers
25 /*	for the successive protocol stages.
26 /*
27 /*	Upon completion, the event handlers call resume_event()
28 /*	which must reset the SMTP helo/sender/etc. state when the
29 /*	PSC_STATE_FLAG_USING_TLS is set, and set up timer and read
30 /*	event requests to receive the next SMTP command.
31 /* LICENSE
32 /* .ad
33 /* .fi
34 /*	The Secure Mailer license must be distributed with this software.
35 /* AUTHOR(S)
36 /*	Wietse Venema
37 /*	IBM T.J. Watson Research
38 /*	P.O. Box 704
39 /*	Yorktown Heights, NY 10598, USA
40 /*
41 /*	Wietse Venema
42 /*	Google, Inc.
43 /*	111 8th Avenue
44 /*	New York, NY 10011, USA
45 /*--*/
46 
47 /* System library. */
48 
49 #include <sys_defs.h>
50 
51 /* Utility library. */
52 
53 #include <msg.h>
54 #include <mymalloc.h>
55 #include <connect.h>
56 #include <stringops.h>			/* concatenate() */
57 #include <vstring.h>
58 
59 /* Global library. */
60 
61 #include <mail_params.h>
62 #include <mail_proto.h>
63 
64 /* TLS library. */
65 
66 #include <tls_proxy.h>
67 
68 /* Application-specific. */
69 
70 #include <postscreen.h>
71 
72  /*
73   * For now, this code is built into the postscreen(8) daemon. In the future
74   * it may be abstracted into a reusable library module for use by other
75   * event-driven programs (perhaps smtp-source and smtp-sink).
76   */
77 
78  /*
79   * Transient state for the portscreen(8)-to-tlsproxy(8) hand-off protocol.
80   */
81 typedef struct {
82     VSTREAM *tlsproxy_stream;		/* hand-off negotiation */
83     EVENT_NOTIFY_FN resume_event;	/* call-back handler */
84     PSC_STATE *smtp_state;		/* SMTP session state */
85 } PSC_STARTTLS;
86 
87 #define TLSPROXY_INIT_TIMEOUT		10
88 
89 static char *psc_tlsp_service = 0;
90 
91 /* Resume the dummy SMTP engine after an event handling error */
92 
93 #define PSC_STARTTLS_EVENT_ERR_RESUME_RETURN() do { \
94 	event_disable_readwrite(vstream_fileno(tlsproxy_stream)); \
95 	PSC_STARTTLS_EVENT_RESUME_RETURN(starttls_state); \
96     } while (0);
97 
98 /* Resume the dummy SMTP engine, possibly after swapping streams */
99 
100 #define PSC_STARTTLS_EVENT_RESUME_RETURN(starttls_state) do { \
101 	vstream_fclose(tlsproxy_stream); \
102 	starttls_state->resume_event(event, (void *) smtp_state); \
103 	myfree((void *) starttls_state); \
104 	return; \
105     } while (0)
106 
107 /* psc_starttls_finish - complete negotiation with TLS proxy */
108 
psc_starttls_finish(int event,void * context)109 static void psc_starttls_finish(int event, void *context)
110 {
111     const char *myname = "psc_starttls_finish";
112     PSC_STARTTLS *starttls_state = (PSC_STARTTLS *) context;
113     PSC_STATE *smtp_state = starttls_state->smtp_state;
114     VSTREAM *tlsproxy_stream = starttls_state->tlsproxy_stream;
115     int     status;
116 
117     if (msg_verbose)
118 	msg_info("%s: send client handle on proxy socket %d"
119 		 " for smtp socket %d from [%s]:%s flags=%s",
120 		 myname, vstream_fileno(tlsproxy_stream),
121 		 vstream_fileno(smtp_state->smtp_client_stream),
122 		 smtp_state->smtp_client_addr, smtp_state->smtp_client_port,
123 		 psc_print_state_flags(smtp_state->flags, myname));
124 
125     /*
126      * We leave read-event notification enabled on the postscreen to TLS
127      * proxy stream, to avoid two kqueue/epoll/etc. system calls: one here,
128      * and one when resuming the dummy SMTP engine.
129      */
130     if (event != EVENT_TIME)
131 	event_cancel_timer(psc_starttls_finish, (void *) starttls_state);
132 
133     /*
134      * Receive the "TLS is available" indication.
135      *
136      * This may seem out of order, but we must have a read transaction between
137      * sending the request attributes and sending the SMTP client file
138      * descriptor. We can't assume UNIX-domain socket semantics here.
139      */
140     if (event != EVENT_READ
141 	|| attr_scan(tlsproxy_stream, ATTR_FLAG_STRICT,
142 		     RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
143 		     ATTR_TYPE_END) != 1 || status == 0) {
144 
145 	/*
146 	 * The TLS proxy reports that the TLS engine is not available (due to
147 	 * configuration error, or other causes).
148 	 */
149 	msg_warn("%s receiving status from %s service",
150 	     event == EVENT_TIME ? "timeout" : "problem", psc_tlsp_service);
151 	PSC_SEND_REPLY(smtp_state,
152 		    "454 4.7.0 TLS not available due to local problem\r\n");
153 	PSC_STARTTLS_EVENT_ERR_RESUME_RETURN();
154     }
155 
156     /*
157      * Send the remote SMTP client file descriptor.
158      */
159     else if (LOCAL_SEND_FD(vstream_fileno(tlsproxy_stream),
160 		      vstream_fileno(smtp_state->smtp_client_stream)) < 0) {
161 
162 	/*
163 	 * Some error: drop the TLS proxy stream.
164 	 */
165 	msg_warn("problem sending file handle to %s service", psc_tlsp_service);
166 	PSC_SEND_REPLY(smtp_state,
167 		    "454 4.7.0 TLS not available due to local problem\r\n");
168 	PSC_STARTTLS_EVENT_ERR_RESUME_RETURN();
169     }
170 
171     /*
172      * After we send the plaintext 220 greeting, the client-side TLS engine
173      * is supposed to talk first, then the server-side TLS engine. However,
174      * postscreen(8) will not participate in that conversation.
175      */
176     else {
177 	PSC_SEND_REPLY(smtp_state, "220 2.0.0 Ready to start TLS\r\n");
178 
179 	/*
180 	 * Swap the SMTP client stream and the TLS proxy stream, and close
181 	 * the direct connection to the SMTP client. The TLS proxy will talk
182 	 * directly to the SMTP client, and once the TLS handshake is
183 	 * completed, the TLS proxy will talk plaintext to postscreen(8).
184 	 *
185 	 * Swap the file descriptors from under the VSTREAM so that we don't
186 	 * have to worry about loss of user-configurable VSTREAM attributes.
187 	 */
188 	vstream_fpurge(smtp_state->smtp_client_stream, VSTREAM_PURGE_BOTH);
189 	vstream_control(smtp_state->smtp_client_stream,
190 			CA_VSTREAM_CTL_SWAP_FD(tlsproxy_stream),
191 			CA_VSTREAM_CTL_END);
192 	smtp_state->flags |= PSC_STATE_FLAG_USING_TLS;
193 	PSC_STARTTLS_EVENT_RESUME_RETURN(starttls_state);
194     }
195 }
196 
197 /* psc_starttls_first - start negotiation with TLS proxy */
198 
psc_starttls_first(int event,void * context)199 static void psc_starttls_first(int event, void *context)
200 {
201     const char *myname = "psc_starttls_first";
202     PSC_STARTTLS *starttls_state = (PSC_STARTTLS *) context;
203     PSC_STATE *smtp_state = starttls_state->smtp_state;
204     VSTREAM *tlsproxy_stream = starttls_state->tlsproxy_stream;
205     static VSTRING *remote_endpt = 0;
206 
207     if (msg_verbose)
208 	msg_info("%s: receive server protocol on proxy socket %d"
209 		 " for smtp socket %d from [%s]:%s flags=%s",
210 		 myname, vstream_fileno(tlsproxy_stream),
211 		 vstream_fileno(smtp_state->smtp_client_stream),
212 		 smtp_state->smtp_client_addr, smtp_state->smtp_client_port,
213 		 psc_print_state_flags(smtp_state->flags, myname));
214 
215     /*
216      * We leave read-event notification enabled on the postscreen to TLS
217      * proxy stream, to avoid two kqueue/epoll/etc. system calls: one here,
218      * and one when resuming the dummy SMTP engine.
219      */
220     if (event != EVENT_TIME)
221 	event_cancel_timer(psc_starttls_first, (void *) starttls_state);
222 
223     /*
224      * Receive and verify the server protocol.
225      */
226     if (event != EVENT_READ
227 	|| attr_scan(tlsproxy_stream, ATTR_FLAG_STRICT,
228 		 RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_TLSPROXY),
229 		     ATTR_TYPE_END) != 0) {
230 	msg_warn("%s receiving %s attribute from %s service: %m",
231 		 event == EVENT_TIME ? "timeout" : "problem",
232 		 MAIL_ATTR_PROTO, psc_tlsp_service);
233 	PSC_SEND_REPLY(smtp_state,
234 		    "454 4.7.0 TLS not available due to local problem\r\n");
235 	PSC_STARTTLS_EVENT_ERR_RESUME_RETURN();
236     }
237 
238     /*
239      * Send the data attributes now, and send the client file descriptor in a
240      * later transaction. We report all errors asynchronously, to avoid
241      * having to maintain multiple error delivery paths.
242      *
243      * XXX The formatted endpoint should be a state member. Then, we can
244      * simplify all the format strings throughout the program.
245      */
246     if (remote_endpt == 0)
247 	remote_endpt = vstring_alloc(20);
248     vstring_sprintf(remote_endpt, "[%s]:%s", smtp_state->smtp_client_addr,
249 		    smtp_state->smtp_client_port);
250     attr_print(tlsproxy_stream, ATTR_FLAG_NONE,
251 	       SEND_ATTR_STR(TLS_ATTR_REMOTE_ENDPT, STR(remote_endpt)),
252 	       SEND_ATTR_INT(TLS_ATTR_FLAGS, TLS_PROXY_FLAG_ROLE_SERVER),
253 	       SEND_ATTR_INT(TLS_ATTR_TIMEOUT, psc_normal_cmd_time_limit),
254 	       SEND_ATTR_INT(TLS_ATTR_TIMEOUT, psc_normal_cmd_time_limit),
255 	       SEND_ATTR_STR(TLS_ATTR_SERVERID, MAIL_SERVICE_SMTPD),	/* XXX */
256 	       ATTR_TYPE_END);
257     if (vstream_fflush(tlsproxy_stream) != 0) {
258 	msg_warn("error sending request to %s service: %m", psc_tlsp_service);
259 	PSC_SEND_REPLY(smtp_state,
260 		    "454 4.7.0 TLS not available due to local problem\r\n");
261 	PSC_STARTTLS_EVENT_ERR_RESUME_RETURN();
262     }
263 
264     /*
265      * Set up a read event for the next phase of the TLS proxy handshake.
266      */
267     PSC_READ_EVENT_REQUEST(vstream_fileno(tlsproxy_stream), psc_starttls_finish,
268 			   (void *) starttls_state, TLSPROXY_INIT_TIMEOUT);
269 }
270 
271 /* psc_starttls_open - open negotiations with TLS proxy */
272 
psc_starttls_open(PSC_STATE * smtp_state,EVENT_NOTIFY_FN resume_event)273 void    psc_starttls_open(PSC_STATE *smtp_state, EVENT_NOTIFY_FN resume_event)
274 {
275     const char *myname = "psc_starttls_open";
276     PSC_STARTTLS *starttls_state;
277     VSTREAM *tlsproxy_stream;
278     int     fd;
279 
280     if (psc_tlsp_service == 0) {
281 	psc_tlsp_service = concatenate(MAIL_CLASS_PRIVATE "/",
282 				       var_tlsproxy_service, (char *) 0);
283     }
284 
285     /*
286      * Connect to the tlsproxy(8) daemon. We report all errors
287      * asynchronously, to avoid having to maintain multiple delivery paths.
288      */
289     if ((fd = LOCAL_CONNECT(psc_tlsp_service, NON_BLOCKING, 1)) < 0) {
290 	msg_warn("connect to %s service: %m", psc_tlsp_service);
291 	PSC_SEND_REPLY(smtp_state,
292 		    "454 4.7.0 TLS not available due to local problem\r\n");
293 	event_request_timer(resume_event, (void *) smtp_state, 0);
294 	return;
295     }
296     if (msg_verbose)
297 	msg_info("%s: connecting to proxy socket %d"
298 		 " for smtp socket %d from [%s]:%s flags=%s",
299 		 myname, fd, vstream_fileno(smtp_state->smtp_client_stream),
300 		 smtp_state->smtp_client_addr, smtp_state->smtp_client_port,
301 		 psc_print_state_flags(smtp_state->flags, myname));
302 
303     tlsproxy_stream = vstream_fdopen(fd, O_RDWR);
304     vstream_control(tlsproxy_stream,
305 		    VSTREAM_CTL_PATH, psc_tlsp_service,
306 		    VSTREAM_CTL_END);
307 
308     /*
309      * Set up a read event for the next phase of the TLS proxy handshake.
310      */
311     starttls_state = (PSC_STARTTLS *) mymalloc(sizeof(*starttls_state));
312     starttls_state->tlsproxy_stream = tlsproxy_stream;
313     starttls_state->resume_event = resume_event;
314     starttls_state->smtp_state = smtp_state;
315     PSC_READ_EVENT_REQUEST(vstream_fileno(tlsproxy_stream), psc_starttls_first,
316 			   (void *) starttls_state, TLSPROXY_INIT_TIMEOUT);
317 }
318