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