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 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 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