xref: /netbsd-src/external/ibm-public/postfix/dist/src/postscreen/postscreen_starttls.c (revision 230b95665bbd3a9d1a53658a36b1053f8382a519)
1 /*	$NetBSD: postscreen_starttls.c,v 1.1.1.2 2013/01/02 18:59:04 tron 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 
44 /* System library. */
45 
46 #include <sys_defs.h>
47 
48 /* Utility library. */
49 
50 #include <msg.h>
51 #include <mymalloc.h>
52 #include <connect.h>
53 #include <stringops.h>			/* concatenate() */
54 #include <vstring.h>
55 
56 /* Global library. */
57 
58 #include <mail_params.h>
59 #include <mail_proto.h>
60 
61 /* TLS library. */
62 
63 #include <tls_proxy.h>
64 
65 /* Application-specific. */
66 
67 #include <postscreen.h>
68 
69  /*
70   * For now, this code is built into the postscreen(8) daemon. In the future
71   * it may be abstracted into a reusable library module for use by other
72   * event-driven programs (perhaps smtp-source and smtp-sink).
73   */
74 
75  /*
76   * Transient state for the portscreen(8)-to-tlsproxy(8) hand-off protocol.
77   */
78 typedef struct {
79     VSTREAM *tlsproxy_stream;		/* hand-off negotiation */
80     EVENT_NOTIFY_FN resume_event;	/* call-back handler */
81     PSC_STATE *smtp_state;		/* SMTP session state */
82 } PSC_STARTTLS;
83 
84 #define TLSPROXY_INIT_TIMEOUT		10
85 
86 static char *psc_tlsp_service = 0;
87 
88 /* psc_starttls_finish - complete negotiation with TLS proxy */
89 
90 static void psc_starttls_finish(int event, char *context)
91 {
92     const char *myname = "psc_starttls_finish";
93     PSC_STARTTLS *starttls_state = (PSC_STARTTLS *) context;
94     PSC_STATE *smtp_state = starttls_state->smtp_state;
95     VSTREAM *tlsproxy_stream = starttls_state->tlsproxy_stream;
96     int     status;
97 
98     if (msg_verbose)
99 	msg_info("%s: send client handle on proxy socket %d"
100 		 " for smtp socket %d from [%s]:%s flags=%s",
101 		 myname, vstream_fileno(tlsproxy_stream),
102 		 vstream_fileno(smtp_state->smtp_client_stream),
103 		 smtp_state->smtp_client_addr, smtp_state->smtp_client_port,
104 		 psc_print_state_flags(smtp_state->flags, myname));
105 
106     /*
107      * We leave read-event notification enabled on the postscreen to TLS
108      * proxy stream, to avoid two kqueue/epoll/etc. system calls: one here,
109      * and one when resuming the dummy SMTP engine.
110      */
111     if (event != EVENT_TIME)
112 	event_cancel_timer(psc_starttls_finish, (char *) starttls_state);
113 
114     /*
115      * Receive the "TLS is available" indication.
116      *
117      * This may seem out of order, but we must have a read transaction between
118      * sending the request attributes and sending the SMTP client file
119      * descriptor. We can't assume UNIX-domain socket semantics here.
120      */
121     if (event != EVENT_READ
122 	|| attr_scan(tlsproxy_stream, ATTR_FLAG_STRICT,
123 		     ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
124 		     ATTR_TYPE_END) != 1 || status == 0) {
125 
126 	/*
127 	 * The TLS proxy reports that the TLS engine is not available (due to
128 	 * configuration error, or other causes).
129 	 */
130 	event_disable_readwrite(vstream_fileno(tlsproxy_stream));
131 	vstream_fclose(tlsproxy_stream);
132 	PSC_SEND_REPLY(smtp_state,
133 		    "454 4.7.0 TLS not available due to local problem\r\n");
134     }
135 
136     /*
137      * Send the remote SMTP client file descriptor.
138      */
139     else if (LOCAL_SEND_FD(vstream_fileno(tlsproxy_stream),
140 		      vstream_fileno(smtp_state->smtp_client_stream)) < 0) {
141 
142 	/*
143 	 * Some error: drop the TLS proxy stream.
144 	 */
145 	msg_warn("%s sending file handle to %s service",
146 		 event == EVENT_TIME ? "timeout" : "problem",
147 		 psc_tlsp_service);
148 	event_disable_readwrite(vstream_fileno(tlsproxy_stream));
149 	vstream_fclose(tlsproxy_stream);
150 	PSC_SEND_REPLY(smtp_state,
151 		    "454 4.7.0 TLS not available due to local problem\r\n");
152     }
153 
154     /*
155      * After we send the plaintext 220 greeting, the client-side TLS engine
156      * is supposed to talk first, then the server-side TLS engine. However,
157      * postscreen(8) will not participate in that conversation.
158      */
159     else {
160 	PSC_SEND_REPLY(smtp_state, "220 2.0.0 Ready to start TLS\r\n");
161 
162 	/*
163 	 * Replace our SMTP client stream by the TLS proxy stream.  Once the
164 	 * TLS handshake is done, the TLS proxy will deliver plaintext SMTP
165 	 * commands to postscreen(8).
166 	 *
167 	 * Swap the file descriptors from under the VSTREAM so that we don't
168 	 * have to worry about loss of user-configurable VSTREAM attributes.
169 	 */
170 	vstream_fpurge(smtp_state->smtp_client_stream, VSTREAM_PURGE_BOTH);
171 	vstream_control(smtp_state->smtp_client_stream,
172 			VSTREAM_CTL_SWAP_FD, tlsproxy_stream,
173 			VSTREAM_CTL_END);
174 	vstream_fclose(tlsproxy_stream);	/* direct-to-client stream! */
175 	smtp_state->flags |= PSC_STATE_FLAG_USING_TLS;
176     }
177 
178     /*
179      * Resume the postscreen(8) dummy SMTP engine and clean up.
180      */
181     starttls_state->resume_event(event, (char *) smtp_state);
182     myfree((char *) starttls_state);
183 }
184 
185 /* psc_starttls_open - open negotiations with TLS proxy */
186 
187 void    psc_starttls_open(PSC_STATE *smtp_state, EVENT_NOTIFY_FN resume_event)
188 {
189     const char *myname = "psc_starttls_open";
190     PSC_STARTTLS *starttls_state;
191     VSTREAM *tlsproxy_stream;
192     int     fd;
193     static VSTRING *remote_endpt = 0;
194 
195     if (psc_tlsp_service == 0) {
196 	psc_tlsp_service = concatenate(MAIL_CLASS_PRIVATE "/",
197 				       var_tlsproxy_service, (char *) 0);
198 	remote_endpt = vstring_alloc(20);
199     }
200 
201     /*
202      * Connect to the tlsproxy(8) daemon. We report all errors
203      * asynchronously, to avoid having to maintain multiple delivery paths.
204      */
205     if ((fd = LOCAL_CONNECT(psc_tlsp_service, NON_BLOCKING, 1)) < 0) {
206 	msg_warn("connect to %s service: %m", psc_tlsp_service);
207 	PSC_SEND_REPLY(smtp_state,
208 		    "454 4.7.0 TLS not available due to local problem\r\n");
209 	event_request_timer(resume_event, (char *) smtp_state, 0);
210 	return;
211     }
212     if (msg_verbose)
213 	msg_info("%s: send client name/address on proxy socket %d"
214 		 " for smtp socket %d from [%s]:%s flags=%s",
215 		 myname, fd, vstream_fileno(smtp_state->smtp_client_stream),
216 		 smtp_state->smtp_client_addr, smtp_state->smtp_client_port,
217 		 psc_print_state_flags(smtp_state->flags, myname));
218 
219     /*
220      * Initial handshake. Send the data attributes now, and send the client
221      * file descriptor in a later transaction. We report all errors
222      * asynchronously, to avoid having to maintain multiple delivery paths.
223      *
224      * XXX The formatted endpoint should be a state member. Then, we can
225      * simplify all the format strings throughout the program.
226      */
227     tlsproxy_stream = vstream_fdopen(fd, O_RDWR);
228     vstring_sprintf(remote_endpt, "[%s]:%s", smtp_state->smtp_client_addr,
229 		    smtp_state->smtp_client_port);
230     attr_print(tlsproxy_stream, ATTR_FLAG_NONE,
231 	       ATTR_TYPE_STR, MAIL_ATTR_REMOTE_ENDPT, STR(remote_endpt),
232 	       ATTR_TYPE_INT, MAIL_ATTR_FLAGS, TLS_PROXY_FLAG_ROLE_SERVER,
233 	       ATTR_TYPE_INT, MAIL_ATTR_TIMEOUT, psc_normal_cmd_time_limit,
234 	       ATTR_TYPE_STR, MAIL_ATTR_SERVER_ID, MAIL_SERVICE_SMTPD,	/* XXX */
235 	       ATTR_TYPE_END);
236     if (vstream_fflush(tlsproxy_stream) != 0) {
237 	msg_warn("error sending request to %s service: %m", psc_tlsp_service);
238 	vstream_fclose(tlsproxy_stream);
239 	PSC_SEND_REPLY(smtp_state,
240 		    "454 4.7.0 TLS not available due to local problem\r\n");
241 	event_request_timer(resume_event, (char *) smtp_state, 0);
242 	return;
243     }
244 
245     /*
246      * Set up a read event for the next phase of the TLS proxy handshake.
247      */
248     starttls_state = (PSC_STARTTLS *) mymalloc(sizeof(*starttls_state));
249     starttls_state->tlsproxy_stream = tlsproxy_stream;
250     starttls_state->resume_event = resume_event;
251     starttls_state->smtp_state = smtp_state;
252     PSC_READ_EVENT_REQUEST(vstream_fileno(tlsproxy_stream), psc_starttls_finish,
253 			   (char *) starttls_state, TLSPROXY_INIT_TIMEOUT);
254 }
255