1 /*++
2 /* NAME
3 /*	tlsproxy_clnt 3
4 /* SUMMARY
5 /*	tlsproxy(8) client support
6 /* SYNOPSIS
7 /*	#include <tlsproxy_clnt.h>
8 /*
9 /*	VSTREAM *tls_proxy_open(service, flags, peer_stream, peer_addr,
10 /*				peer_port, handshake_timeout, session_timeout,
11 /*				serverid, tls_params, init_props, start_props)
12 /*	const char *service;
13 /*	int	flags;
14 /*	VSTREAM *peer_stream;
15 /*	const char *peer_addr;
16 /*	const char *peer_port;
17 /*	int	handshake_timeout;
18 /*	int	session_timeout;
19 /*	const char *serverid;
20 /*	void	*tls_params;
21 /*	void	*init_props;
22 /*	void	*start_props;
23 /*
24 /*	TLS_SESS_STATE *tls_proxy_context_receive(proxy_stream)
25 /*	VSTREAM *proxy_stream;
26 /* AUXILIARY FUNCTIONS
27 /*	VSTREAM *tls_proxy_legacy_open(service, flags, peer_stream,
28 /*					peer_addr, peer_port,
29 /*					timeout, serverid)
30 /*	const char *service;
31 /*	int	flags;
32 /*	VSTREAM *peer_stream;
33 /*	const char *peer_addr;
34 /*	const char *peer_port;
35 /*	int	timeout;
36 /*	const char *serverid;
37 /* DESCRIPTION
38 /*	tls_proxy_open() prepares for inserting the tlsproxy(8)
39 /*	daemon between the current process and a remote peer (the
40 /*	actual insert operation is described in the next paragraph).
41 /*	The result value is a null pointer on failure. The peer_stream
42 /*	is not closed.  The resulting proxy stream is single-buffered.
43 /*
44 /*	After this, it is a good idea to use the CA_VSTREAM_CTL_SWAP_FD
45 /*	request to swap the file descriptors between the plaintext
46 /*	peer_stream and the proxy stream from tls_proxy_open().
47 /*	This avoids the loss of application-configurable VSTREAM
48 /*	attributes on the plaintext peer_stream (such as longjmp
49 /*	buffer, timeout, etc.). Once the file descriptors are
50 /*	swapped, the proxy stream should be closed.
51 /*
52 /*	tls_proxy_context_receive() receives the TLS context object
53 /*	for the named proxy stream. This function must be called
54 /*	only if the TLS_PROXY_SEND_CONTEXT flag was specified in
55 /*	the tls_proxy_open() call. Note that this TLS context object
56 /*	is not compatible with tls_session_free(). It must be given
57 /*	to tls_proxy_context_free() instead.
58 /*
59 /*	After this, the proxy_stream is ready for plain-text I/O.
60 /*
61 /*	tls_proxy_legacy_open() is a backwards-compatibility feature
62 /*	that provides a historical interface.
63 /*
64 /*	Arguments:
65 /* .IP service
66 /*	The (base) name of the tlsproxy service.
67 /* .IP flags
68 /*	Bit-wise OR of:
69 /* .RS
70 /* .IP TLS_PROXY_FLAG_ROLE_SERVER
71 /*	Request the TLS server proxy role.
72 /* .IP TLS_PROXY_FLAG_ROLE_CLIENT
73 /*	Request the TLS client proxy role.
74 /* .IP TLS_PROXY_FLAG_SEND_CONTEXT
75 /*	Send the TLS context object.
76 /* .RE
77 /* .IP peer_stream
78 /*	Stream that connects the current process to a remote peer.
79 /* .IP peer_addr
80 /*	Printable IP address of the remote peer_stream endpoint.
81 /* .IP peer_port
82 /*	Printable TCP port of the remote peer_stream endpoint.
83 /* .IP handshake_timeout
84 /*	Time limit that the tlsproxy(8) daemon should use during
85 /*	the TLS handshake.
86 /* .IP session_timeout
87 /*	Time limit that the tlsproxy(8) daemon should use after the
88 /*	TLS handshake.
89 /* .IP serverid
90 /*	Unique service identifier.
91 /* .IP tls_params
92 /*	Pointer to TLS_CLIENT_PARAMS or TLS_SERVER_PARAMS.
93 /* .IP init_props
94 /*	Pointer to TLS_CLIENT_INIT_PROPS or TLS_SERVER_INIT_PROPS.
95 /* .IP start_props
96 /*	Pointer to TLS_CLIENT_START_PROPS or TLS_SERVER_START_PROPS.
97 /* .IP proxy_stream
98 /*	Stream from tls_proxy_open().
99 /* .IP tls_context
100 /*	TLS session object from tls_proxy_context_receive().
101 /* LICENSE
102 /* .ad
103 /* .fi
104 /*	The Secure Mailer license must be distributed with this software.
105 /* AUTHOR(S)
106 /*	Wietse Venema
107 /*	IBM T.J. Watson Research
108 /*	P.O. Box 704
109 /*	Yorktown Heights, NY 10598, USA
110 /*
111 /*	Wietse Venema
112 /*	Google, Inc.
113 /*	111 8th Avenue
114 /*	New York, NY 10011, USA
115 /*--*/
116 
117 #ifdef USE_TLS
118 
119 /* System library. */
120 
121 #include <sys_defs.h>
122 
123 /* Utility library. */
124 
125 #include <msg.h>
126 #include <mymalloc.h>
127 #include <connect.h>
128 #include <stringops.h>
129 #include <vstring.h>
130 
131 /* Global library. */
132 
133 #include <mail_proto.h>
134 #include <mail_params.h>
135 
136 /* TLS library-specific. */
137 
138 #include <tls.h>
139 #include <tls_proxy.h>
140 
141 #define TLSPROXY_INIT_TIMEOUT		10
142 
143 /* SLMs. */
144 
145 #define STR	vstring_str
146 
147 /* tls_proxy_open - open negotiations with TLS proxy */
148 
tls_proxy_open(const char * service,int flags,VSTREAM * peer_stream,const char * peer_addr,const char * peer_port,int handshake_timeout,int session_timeout,const char * serverid,void * tls_params,void * init_props,void * start_props)149 VSTREAM *tls_proxy_open(const char *service, int flags,
150 			        VSTREAM *peer_stream,
151 			        const char *peer_addr,
152 			        const char *peer_port,
153 			        int handshake_timeout,
154 			        int session_timeout,
155 			        const char *serverid,
156 			        void *tls_params,
157 			        void *init_props,
158 			        void *start_props)
159 {
160     const char myname[] = "tls_proxy_open";
161     VSTREAM *tlsproxy_stream;
162     int     status;
163     int     fd;
164     static VSTRING *tlsproxy_service = 0;
165     static VSTRING *remote_endpt = 0;
166 
167     /*
168      * Initialize.
169      */
170     if (tlsproxy_service == 0) {
171 	tlsproxy_service = vstring_alloc(20);
172 	remote_endpt = vstring_alloc(20);
173     }
174 
175     /*
176      * Connect to the tlsproxy(8) daemon.
177      */
178     vstring_sprintf(tlsproxy_service, "%s/%s", MAIL_CLASS_PRIVATE, service);
179     if ((fd = LOCAL_CONNECT(STR(tlsproxy_service), BLOCKING,
180 			    TLSPROXY_INIT_TIMEOUT)) < 0) {
181 	msg_warn("connect to %s service: %m", STR(tlsproxy_service));
182 	return (0);
183     }
184 
185     /*
186      * Initial handshake. Send common data attributes now, and send the
187      * remote peer file descriptor in a later transaction.
188      */
189     tlsproxy_stream = vstream_fdopen(fd, O_RDWR);
190     if (attr_scan(tlsproxy_stream, ATTR_FLAG_STRICT,
191 		  RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_TLSPROXY),
192 		  ATTR_TYPE_END) != 0) {
193 	msg_warn("error receiving %s service initial response",
194 		 STR(tlsproxy_service));
195 	vstream_fclose(tlsproxy_stream);
196 	return (0);
197     }
198     vstring_sprintf(remote_endpt, "[%s]:%s", peer_addr, peer_port);
199     attr_print(tlsproxy_stream, ATTR_FLAG_NONE,
200 	       SEND_ATTR_STR(TLS_ATTR_REMOTE_ENDPT, STR(remote_endpt)),
201 	       SEND_ATTR_INT(TLS_ATTR_FLAGS, flags),
202 	       SEND_ATTR_INT(TLS_ATTR_TIMEOUT, handshake_timeout),
203 	       SEND_ATTR_INT(TLS_ATTR_TIMEOUT, session_timeout),
204 	       SEND_ATTR_STR(TLS_ATTR_SERVERID, serverid),
205 	       ATTR_TYPE_END);
206     /* Do not flush the stream yet. */
207     if (vstream_ferror(tlsproxy_stream) != 0) {
208 	msg_warn("error sending request to %s service: %m",
209 		 STR(tlsproxy_service));
210 	vstream_fclose(tlsproxy_stream);
211 	return (0);
212     }
213     switch (flags & (TLS_PROXY_FLAG_ROLE_CLIENT | TLS_PROXY_FLAG_ROLE_SERVER)) {
214     case TLS_PROXY_FLAG_ROLE_CLIENT:
215 	attr_print(tlsproxy_stream, ATTR_FLAG_NONE,
216 		   SEND_ATTR_FUNC(tls_proxy_client_param_print, tls_params),
217 		   SEND_ATTR_FUNC(tls_proxy_client_init_print, init_props),
218 		   SEND_ATTR_FUNC(tls_proxy_client_start_print, start_props),
219 		   ATTR_TYPE_END);
220 	break;
221     case TLS_PROXY_FLAG_ROLE_SERVER:
222 #if 0
223 	attr_print(tlsproxy_stream, ATTR_FLAG_NONE,
224 		   SEND_ATTR_FUNC(tls_proxy_server_param_print, tls_params),
225 		   SEND_ATTR_FUNC(tls_proxy_server_init_print, init_props),
226 		   SEND_ATTR_FUNC(tls_proxy_server_start_print, start_props),
227 		   ATTR_TYPE_END);
228 #endif
229 	break;
230     default:
231 	msg_panic("%s: bad flags: 0x%x", myname, flags);
232     }
233     if (vstream_fflush(tlsproxy_stream) != 0) {
234 	msg_warn("error sending request to %s service: %m",
235 		 STR(tlsproxy_service));
236 	vstream_fclose(tlsproxy_stream);
237 	return (0);
238     }
239 
240     /*
241      * Receive the "TLS is available" indication.
242      *
243      * This may seem out of order, but we must have a read transaction between
244      * sending the request attributes and sending the plaintext file
245      * descriptor. We can't assume UNIX-domain socket semantics here.
246      */
247     if (attr_scan(tlsproxy_stream, ATTR_FLAG_STRICT,
248 		  RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
249     /* TODO: informative message. */
250 		  ATTR_TYPE_END) != 1 || status == 0) {
251 
252 	/*
253 	 * The TLS proxy reports that the TLS engine is not available (due to
254 	 * configuration error, or other causes).
255 	 */
256 	msg_warn("%s service role \"%s\" is not available",
257 		 STR(tlsproxy_service),
258 		 (flags & TLS_PROXY_FLAG_ROLE_SERVER) ? "server" :
259 		 (flags & TLS_PROXY_FLAG_ROLE_CLIENT) ? "client" :
260 		 "bogus role");
261 	vstream_fclose(tlsproxy_stream);
262 	return (0);
263     }
264 
265     /*
266      * Send the remote peer file descriptor.
267      */
268     if (LOCAL_SEND_FD(vstream_fileno(tlsproxy_stream),
269 		      vstream_fileno(peer_stream)) < 0) {
270 
271 	/*
272 	 * Some error: drop the TLS proxy stream.
273 	 */
274 	msg_warn("sending file handle to %s service: %m",
275 		 STR(tlsproxy_service));
276 	vstream_fclose(tlsproxy_stream);
277 	return (0);
278     }
279     return (tlsproxy_stream);
280 }
281 
282 
283 /* tls_proxy_context_receive - receive TLS session object from tlsproxy(8) */
284 
tls_proxy_context_receive(VSTREAM * proxy_stream)285 TLS_SESS_STATE *tls_proxy_context_receive(VSTREAM *proxy_stream)
286 {
287     TLS_SESS_STATE *tls_context = 0;
288 
289     if (attr_scan(proxy_stream, ATTR_FLAG_STRICT,
290 	      RECV_ATTR_FUNC(tls_proxy_context_scan, (void *) &tls_context),
291 		  ATTR_TYPE_END) != 1) {
292 	if (tls_context)
293 	    tls_proxy_context_free(tls_context);
294 	return (0);
295     } else {
296 	return (tls_context);
297     }
298 }
299 
300 #endif
301