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