1 /* $NetBSD: postscreen_starttls.c,v 1.4 2022/10/08 16:12:48 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* postscreen_starttls 3 6 /* SUMMARY 7 /* postscreen TLS proxy support 8 /* SYNOPSIS 9 /* #include <postscreen.h> 10 /* 11 /* int psc_starttls_open(state, resume_event) 12 /* PSC_STATE *state; 13 /* void (*resume_event)(int unused_event, char *context); 14 /* DESCRIPTION 15 /* This module inserts the tlsproxy(8) proxy between the 16 /* postscreen(8) server and the remote SMTP client. The entire 17 /* process happens in the background, including notification 18 /* of completion to the remote SMTP client and to the calling 19 /* application. 20 /* 21 /* Before calling psc_starttls_open() the caller must turn off 22 /* all pending timer and I/O event requests on the SMTP client 23 /* stream. 24 /* 25 /* psc_starttls_open() starts the first transaction in the 26 /* tlsproxy(8) hand-off protocol, and sets up event handlers 27 /* for the successive protocol stages. 28 /* 29 /* Upon completion, the event handlers call resume_event() 30 /* which must reset the SMTP helo/sender/etc. state when the 31 /* PSC_STATE_FLAG_USING_TLS is set, and set up timer and read 32 /* event requests to receive the next SMTP command. 33 /* LICENSE 34 /* .ad 35 /* .fi 36 /* The Secure Mailer license must be distributed with this software. 37 /* AUTHOR(S) 38 /* Wietse Venema 39 /* IBM T.J. Watson Research 40 /* P.O. Box 704 41 /* Yorktown Heights, NY 10598, USA 42 /* 43 /* Wietse Venema 44 /* Google, Inc. 45 /* 111 8th Avenue 46 /* New York, NY 10011, USA 47 /*--*/ 48 49 /* System library. */ 50 51 #include <sys_defs.h> 52 53 /* Utility library. */ 54 55 #include <msg.h> 56 #include <mymalloc.h> 57 #include <connect.h> 58 #include <stringops.h> /* concatenate() */ 59 #include <vstring.h> 60 61 /* Global library. */ 62 63 #include <mail_params.h> 64 #include <mail_proto.h> 65 66 /* TLS library. */ 67 68 #include <tls_proxy.h> 69 70 /* Application-specific. */ 71 72 #include <postscreen.h> 73 74 /* 75 * For now, this code is built into the postscreen(8) daemon. In the future 76 * it may be abstracted into a reusable library module for use by other 77 * event-driven programs (perhaps smtp-source and smtp-sink). 78 */ 79 80 /* 81 * Transient state for the postscreen(8)-to-tlsproxy(8) hand-off protocol. 82 */ 83 typedef struct { 84 VSTREAM *tlsproxy_stream; /* hand-off negotiation */ 85 EVENT_NOTIFY_FN resume_event; /* call-back handler */ 86 PSC_STATE *smtp_state; /* SMTP session state */ 87 } PSC_STARTTLS; 88 89 #define TLSPROXY_INIT_TIMEOUT 10 90 91 static char *psc_tlsp_service = 0; 92 93 /* Resume the dummy SMTP engine after an event handling error */ 94 95 #define PSC_STARTTLS_EVENT_ERR_RESUME_RETURN() do { \ 96 event_disable_readwrite(vstream_fileno(tlsproxy_stream)); \ 97 PSC_STARTTLS_EVENT_RESUME_RETURN(starttls_state); \ 98 } while (0); 99 100 /* Resume the dummy SMTP engine, possibly after swapping streams */ 101 102 #define PSC_STARTTLS_EVENT_RESUME_RETURN(starttls_state) do { \ 103 vstream_fclose(tlsproxy_stream); \ 104 starttls_state->resume_event(event, (void *) smtp_state); \ 105 myfree((void *) starttls_state); \ 106 return; \ 107 } while (0) 108 109 /* psc_starttls_finish - complete negotiation with TLS proxy */ 110 111 static void psc_starttls_finish(int event, void *context) 112 { 113 const char *myname = "psc_starttls_finish"; 114 PSC_STARTTLS *starttls_state = (PSC_STARTTLS *) context; 115 PSC_STATE *smtp_state = starttls_state->smtp_state; 116 VSTREAM *tlsproxy_stream = starttls_state->tlsproxy_stream; 117 int status; 118 119 if (msg_verbose) 120 msg_info("%s: send client handle on proxy socket %d" 121 " for smtp socket %d from [%s]:%s flags=%s", 122 myname, vstream_fileno(tlsproxy_stream), 123 vstream_fileno(smtp_state->smtp_client_stream), 124 smtp_state->smtp_client_addr, smtp_state->smtp_client_port, 125 psc_print_state_flags(smtp_state->flags, myname)); 126 127 /* 128 * We leave read-event notification enabled on the postscreen to TLS 129 * proxy stream, to avoid two kqueue/epoll/etc. system calls: one here, 130 * and one when resuming the dummy SMTP engine. 131 */ 132 if (event != EVENT_TIME) 133 event_cancel_timer(psc_starttls_finish, (void *) starttls_state); 134 135 /* 136 * Receive the "TLS is available" indication. 137 * 138 * This may seem out of order, but we must have a read transaction between 139 * sending the request attributes and sending the SMTP client file 140 * descriptor. We can't assume UNIX-domain socket semantics here. 141 */ 142 if (event != EVENT_READ 143 || attr_scan(tlsproxy_stream, ATTR_FLAG_STRICT, 144 RECV_ATTR_INT(MAIL_ATTR_STATUS, &status), 145 ATTR_TYPE_END) != 1 || status == 0) { 146 147 /* 148 * The TLS proxy reports that the TLS engine is not available (due to 149 * configuration error, or other causes). 150 */ 151 msg_warn("%s receiving status from %s service", 152 event == EVENT_TIME ? "timeout" : "problem", psc_tlsp_service); 153 PSC_SEND_REPLY(smtp_state, 154 "454 4.7.0 TLS not available due to local problem\r\n"); 155 PSC_STARTTLS_EVENT_ERR_RESUME_RETURN(); 156 } 157 158 /* 159 * Send the remote SMTP client file descriptor. 160 */ 161 else if (LOCAL_SEND_FD(vstream_fileno(tlsproxy_stream), 162 vstream_fileno(smtp_state->smtp_client_stream)) < 0) { 163 164 /* 165 * Some error: drop the TLS proxy stream. 166 */ 167 msg_warn("problem sending file handle to %s service", psc_tlsp_service); 168 PSC_SEND_REPLY(smtp_state, 169 "454 4.7.0 TLS not available due to local problem\r\n"); 170 PSC_STARTTLS_EVENT_ERR_RESUME_RETURN(); 171 } 172 173 /* 174 * After we send the plaintext 220 greeting, the client-side TLS engine 175 * is supposed to talk first, then the server-side TLS engine. However, 176 * postscreen(8) will not participate in that conversation. 177 */ 178 else { 179 PSC_SEND_REPLY(smtp_state, "220 2.0.0 Ready to start TLS\r\n"); 180 181 /* 182 * Swap the SMTP client stream and the TLS proxy stream, and close 183 * the direct connection to the SMTP client. The TLS proxy will talk 184 * directly to the SMTP client, and once the TLS handshake is 185 * completed, the TLS proxy will talk plaintext to postscreen(8). 186 * 187 * Swap the file descriptors from under the VSTREAM so that we don't 188 * have to worry about loss of user-configurable VSTREAM attributes. 189 */ 190 vstream_fpurge(smtp_state->smtp_client_stream, VSTREAM_PURGE_BOTH); 191 vstream_control(smtp_state->smtp_client_stream, 192 CA_VSTREAM_CTL_SWAP_FD(tlsproxy_stream), 193 CA_VSTREAM_CTL_END); 194 smtp_state->flags |= PSC_STATE_FLAG_USING_TLS; 195 PSC_STARTTLS_EVENT_RESUME_RETURN(starttls_state); 196 } 197 } 198 199 /* psc_starttls_first - start negotiation with TLS proxy */ 200 201 static void psc_starttls_first(int event, void *context) 202 { 203 const char *myname = "psc_starttls_first"; 204 PSC_STARTTLS *starttls_state = (PSC_STARTTLS *) context; 205 PSC_STATE *smtp_state = starttls_state->smtp_state; 206 VSTREAM *tlsproxy_stream = starttls_state->tlsproxy_stream; 207 static VSTRING *remote_endpt = 0; 208 209 if (msg_verbose) 210 msg_info("%s: receive server protocol on proxy socket %d" 211 " for smtp socket %d from [%s]:%s flags=%s", 212 myname, vstream_fileno(tlsproxy_stream), 213 vstream_fileno(smtp_state->smtp_client_stream), 214 smtp_state->smtp_client_addr, smtp_state->smtp_client_port, 215 psc_print_state_flags(smtp_state->flags, myname)); 216 217 /* 218 * We leave read-event notification enabled on the postscreen to TLS 219 * proxy stream, to avoid two kqueue/epoll/etc. system calls: one here, 220 * and one when resuming the dummy SMTP engine. 221 */ 222 if (event != EVENT_TIME) 223 event_cancel_timer(psc_starttls_first, (void *) starttls_state); 224 225 /* 226 * Receive and verify the server protocol. 227 */ 228 if (event != EVENT_READ 229 || attr_scan(tlsproxy_stream, ATTR_FLAG_STRICT, 230 RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_TLSPROXY), 231 ATTR_TYPE_END) != 0) { 232 msg_warn("%s receiving %s attribute from %s service: %m", 233 event == EVENT_TIME ? "timeout" : "problem", 234 MAIL_ATTR_PROTO, psc_tlsp_service); 235 PSC_SEND_REPLY(smtp_state, 236 "454 4.7.0 TLS not available due to local problem\r\n"); 237 PSC_STARTTLS_EVENT_ERR_RESUME_RETURN(); 238 } 239 240 /* 241 * Send the data attributes now, and send the client file descriptor in a 242 * later transaction. We report all errors asynchronously, to avoid 243 * having to maintain multiple error delivery paths. 244 * 245 * XXX The formatted endpoint should be a state member. Then, we can 246 * simplify all the format strings throughout the program. 247 */ 248 if (remote_endpt == 0) 249 remote_endpt = vstring_alloc(20); 250 vstring_sprintf(remote_endpt, "[%s]:%s", smtp_state->smtp_client_addr, 251 smtp_state->smtp_client_port); 252 attr_print(tlsproxy_stream, ATTR_FLAG_NONE, 253 SEND_ATTR_STR(TLS_ATTR_REMOTE_ENDPT, STR(remote_endpt)), 254 SEND_ATTR_INT(TLS_ATTR_FLAGS, TLS_PROXY_FLAG_ROLE_SERVER), 255 SEND_ATTR_INT(TLS_ATTR_TIMEOUT, psc_normal_cmd_time_limit), 256 SEND_ATTR_INT(TLS_ATTR_TIMEOUT, psc_normal_cmd_time_limit), 257 SEND_ATTR_STR(TLS_ATTR_SERVERID, MAIL_SERVICE_SMTPD), /* XXX */ 258 ATTR_TYPE_END); 259 if (vstream_fflush(tlsproxy_stream) != 0) { 260 msg_warn("error sending request to %s service: %m", psc_tlsp_service); 261 PSC_SEND_REPLY(smtp_state, 262 "454 4.7.0 TLS not available due to local problem\r\n"); 263 PSC_STARTTLS_EVENT_ERR_RESUME_RETURN(); 264 } 265 266 /* 267 * Set up a read event for the next phase of the TLS proxy handshake. 268 */ 269 PSC_READ_EVENT_REQUEST(vstream_fileno(tlsproxy_stream), psc_starttls_finish, 270 (void *) starttls_state, TLSPROXY_INIT_TIMEOUT); 271 } 272 273 /* psc_starttls_open - open negotiations with TLS proxy */ 274 275 void psc_starttls_open(PSC_STATE *smtp_state, EVENT_NOTIFY_FN resume_event) 276 { 277 const char *myname = "psc_starttls_open"; 278 PSC_STARTTLS *starttls_state; 279 VSTREAM *tlsproxy_stream; 280 int fd; 281 282 if (psc_tlsp_service == 0) { 283 psc_tlsp_service = concatenate(MAIL_CLASS_PRIVATE "/", 284 var_tlsproxy_service, (char *) 0); 285 } 286 287 /* 288 * Connect to the tlsproxy(8) daemon. We report all errors 289 * asynchronously, to avoid having to maintain multiple delivery paths. 290 */ 291 if ((fd = LOCAL_CONNECT(psc_tlsp_service, NON_BLOCKING, 1)) < 0) { 292 msg_warn("connect to %s service: %m", psc_tlsp_service); 293 PSC_SEND_REPLY(smtp_state, 294 "454 4.7.0 TLS not available due to local problem\r\n"); 295 event_request_timer(resume_event, (void *) smtp_state, 0); 296 return; 297 } 298 if (msg_verbose) 299 msg_info("%s: connecting to proxy socket %d" 300 " for smtp socket %d from [%s]:%s flags=%s", 301 myname, fd, vstream_fileno(smtp_state->smtp_client_stream), 302 smtp_state->smtp_client_addr, smtp_state->smtp_client_port, 303 psc_print_state_flags(smtp_state->flags, myname)); 304 305 tlsproxy_stream = vstream_fdopen(fd, O_RDWR); 306 vstream_control(tlsproxy_stream, 307 VSTREAM_CTL_PATH, psc_tlsp_service, 308 VSTREAM_CTL_END); 309 310 /* 311 * Set up a read event for the next phase of the TLS proxy handshake. 312 */ 313 starttls_state = (PSC_STARTTLS *) mymalloc(sizeof(*starttls_state)); 314 starttls_state->tlsproxy_stream = tlsproxy_stream; 315 starttls_state->resume_event = resume_event; 316 starttls_state->smtp_state = smtp_state; 317 PSC_READ_EVENT_REQUEST(vstream_fileno(tlsproxy_stream), psc_starttls_first, 318 (void *) starttls_state, TLSPROXY_INIT_TIMEOUT); 319 } 320