xref: /netbsd-src/external/ibm-public/postfix/dist/src/tls/tls_proxy_clnt.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: tls_proxy_clnt.c,v 1.2 2017/02/14 01:16:48 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	tlsproxy_clnt 3
6 /* SUMMARY
7 /*	postscreen TLS proxy support
8 /* SYNOPSIS
9 /*	#include <tlsproxy_clnt.h>
10 /*
11 /*	VSTREAM *tls_proxy_open(service, flags, peer_stream, peer_addr,
12 /*			          peer_port, timeout)
13 /*	const char *service;
14 /*	int	flags;
15 /*	VSTREAM *peer_stream;
16 /*	const char *peer_addr;
17 /*	const char *peer_port;
18 /*	int	timeout;
19 /*
20 /*	TLS_SESS_STATE *tls_proxy_context_receive(proxy_stream)
21 /*	VSTREAM *proxy_stream;
22 /*
23 /*	void	tls_proxy_context_free(tls_context)
24 /*	TLS_SESS_STATE *tls_context;
25 /* DESCRIPTION
26 /*	tls_proxy_open() prepares for inserting the tlsproxy(8)
27 /*	daemon between the current process and a remote peer (the
28 /*	actual insert operation is described in the next paragraph).
29 /*	The result value is a null pointer on failure. The peer_stream
30 /*	is not closed.  The resulting proxy stream is single-buffered.
31 /*
32 /*	After this, it is a good idea to use the CA_VSTREAM_CTL_SWAP_FD
33 /*	request to swap the file descriptors between the plaintext
34 /*	peer_stream and the proxy stream from tls_proxy_open().
35 /*	This avoids the loss of application-configurable VSTREAM
36 /*	attributes on the plaintext peer_stream (such as longjmp
37 /*	buffer, timeout, etc.). Once the file descriptors are
38 /*	swapped, the proxy stream should be closed.
39 /*
40 /*	tls_proxy_context_receive() receives the TLS context object
41 /*	for the named proxy stream. This function must be called
42 /*	only if the TLS_PROXY_SEND_CONTEXT flag was specified in
43 /*	the tls_proxy_open() call. Note that this TLS context object
44 /*	is not compatible with tls_session_free(). It must be given
45 /*	to tls_proxy_context_free() instead.
46 /*
47 /*	After this, the proxy_stream is ready for plain-text I/O.
48 /*
49 /*	tls_proxy_context_free() destroys a TLS context object that
50 /*	was received with tls_proxy_context_receive().
51 /*
52 /*	Arguments:
53 /* .IP service
54 /*	The (base) name of the tlsproxy service.
55 /* .IP flags
56 /*	Bit-wise OR of:
57 /* .RS
58 /* .IP TLS_PROXY_FLAG_ROLE_SERVER
59 /*	Request the TLS server proxy role.
60 /* .IP TLS_PROXY_FLAG_ROLE_CLIENT
61 /*	Request the TLS client proxy role.
62 /* .IP TLS_PROXY_FLAG_SEND_CONTEXT
63 /*	Send the TLS context object.
64 /* .RE
65 /* .IP peer_stream
66 /*	Stream that connects the current process to a remote peer.
67 /* .IP peer_addr
68 /*	Printable IP address of the remote peer_stream endpoint.
69 /* .IP peer_port
70 /*	Printable TCP port of the remote peer_stream endpoint.
71 /* .IP timeout
72 /*	Time limit that the tlsproxy(8) daemon should use.
73 /* .IP proxy_stream
74 /*	Stream from tls_proxy_open().
75 /* .IP tls_context
76 /*	TLS session object from tls_proxy_context_receive().
77 /* LICENSE
78 /* .ad
79 /* .fi
80 /*	The Secure Mailer license must be distributed with this software.
81 /* AUTHOR(S)
82 /*	Wietse Venema
83 /*	IBM T.J. Watson Research
84 /*	P.O. Box 704
85 /*	Yorktown Heights, NY 10598, USA
86 /*--*/
87 
88 #ifdef USE_TLS
89 
90 /* System library. */
91 
92 #include <sys_defs.h>
93 
94 /* Utility library. */
95 
96 #include <msg.h>
97 #include <mymalloc.h>
98 #include <connect.h>
99 #include <stringops.h>
100 #include <vstring.h>
101 
102 /* Global library. */
103 
104 #include <mail_proto.h>
105 #include <mail_params.h>
106 
107 /* TLS library-specific. */
108 
109 #include <tls.h>
110 #include <tls_proxy.h>
111 
112 #define TLSPROXY_INIT_TIMEOUT		10
113 
114 /* SLMs. */
115 
116 #define STR	vstring_str
117 
118 /* tls_proxy_open - open negotiations with TLS proxy */
119 
120 VSTREAM *tls_proxy_open(const char *service, int flags,
121 			        VSTREAM *peer_stream,
122 			        const char *peer_addr,
123 			        const char *peer_port,
124 			        int timeout)
125 {
126     VSTREAM *tlsproxy_stream;
127     int     status;
128     int     fd;
129     static VSTRING *tlsproxy_service = 0;
130     static VSTRING *remote_endpt = 0;
131 
132     /*
133      * Initialize.
134      */
135     if (tlsproxy_service == 0) {
136 	tlsproxy_service = vstring_alloc(20);
137 	remote_endpt = vstring_alloc(20);
138     }
139 
140     /*
141      * Connect to the tlsproxy(8) daemon.
142      */
143     vstring_sprintf(tlsproxy_service, "%s/%s", MAIL_CLASS_PRIVATE, service);
144     if ((fd = LOCAL_CONNECT(STR(tlsproxy_service), BLOCKING,
145 			    TLSPROXY_INIT_TIMEOUT)) < 0) {
146 	msg_warn("connect to %s service: %m", STR(tlsproxy_service));
147 	return (0);
148     }
149 
150     /*
151      * Initial handshake. Send the data attributes now, and send the client
152      * file descriptor in a later transaction.
153      *
154      * XXX The formatted endpoint should be a state member. Then, we can
155      * simplify all the format strings throughout the program.
156      */
157     tlsproxy_stream = vstream_fdopen(fd, O_RDWR);
158     vstring_sprintf(remote_endpt, "[%s]:%s", peer_addr, peer_port);
159     attr_print(tlsproxy_stream, ATTR_FLAG_NONE,
160 	       SEND_ATTR_STR(MAIL_ATTR_REMOTE_ENDPT, STR(remote_endpt)),
161 	       SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags),
162 	       SEND_ATTR_INT(MAIL_ATTR_TIMEOUT, timeout),
163 	       ATTR_TYPE_END);
164     if (vstream_fflush(tlsproxy_stream) != 0) {
165 	msg_warn("error sending request to %s service: %m",
166 		 STR(tlsproxy_service));
167 	vstream_fclose(tlsproxy_stream);
168 	return (0);
169     }
170 
171     /*
172      * Receive the "TLS is available" indication.
173      *
174      * This may seem out of order, but we must have a read transaction between
175      * sending the request attributes and sending the SMTP client file
176      * descriptor. We can't assume UNIX-domain socket semantics here.
177      */
178     if (attr_scan(tlsproxy_stream, ATTR_FLAG_STRICT,
179 		  RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
180 		  ATTR_TYPE_END) != 1 || status == 0) {
181 
182 	/*
183 	 * The TLS proxy reports that the TLS engine is not available (due to
184 	 * configuration error, or other causes).
185 	 */
186 	msg_warn("%s service role \"%s\" is not available",
187 		 STR(tlsproxy_service),
188 		 (flags & TLS_PROXY_FLAG_ROLE_SERVER) ? "server" :
189 		 (flags & TLS_PROXY_FLAG_ROLE_CLIENT) ? "client" :
190 		 "bogus role");
191 	vstream_fclose(tlsproxy_stream);
192 	return (0);
193     }
194 
195     /*
196      * Send the remote SMTP client file descriptor.
197      */
198     if (LOCAL_SEND_FD(vstream_fileno(tlsproxy_stream),
199 		      vstream_fileno(peer_stream)) < 0) {
200 
201 	/*
202 	 * Some error: drop the TLS proxy stream.
203 	 */
204 	msg_warn("sending file handle to %s service: %m",
205 		 STR(tlsproxy_service));
206 	vstream_fclose(tlsproxy_stream);
207 	return (0);
208     }
209     return (tlsproxy_stream);
210 }
211 
212 /* tls_proxy_context_receive - receive TLS session object from tlsproxy(8) */
213 
214 TLS_SESS_STATE *tls_proxy_context_receive(VSTREAM *proxy_stream)
215 {
216     TLS_SESS_STATE *tls_context;
217 
218     tls_context = (TLS_SESS_STATE *) mymalloc(sizeof(*tls_context));
219 
220     if (attr_scan(proxy_stream, ATTR_FLAG_STRICT,
221 	       RECV_ATTR_FUNC(tls_proxy_context_scan, (void *) tls_context),
222 		  ATTR_TYPE_END) != 1) {
223 	tls_proxy_context_free(tls_context);
224 	return (0);
225     } else {
226 	return (tls_context);
227     }
228 }
229 
230 /* tls_proxy_context_free - destroy object from tls_proxy_context_receive() */
231 
232 void    tls_proxy_context_free(TLS_SESS_STATE *tls_context)
233 {
234     if (tls_context->peer_CN)
235 	myfree(tls_context->peer_CN);
236     if (tls_context->issuer_CN)
237 	myfree(tls_context->issuer_CN);
238     if (tls_context->peer_cert_fprint)
239 	myfree(tls_context->peer_cert_fprint);
240     if (tls_context->protocol)
241 	myfree((void *) tls_context->protocol);
242     if (tls_context->cipher_name)
243 	myfree((void *) tls_context->cipher_name);
244     myfree((void *) tls_context);
245 }
246 
247 #endif
248