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