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