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