1 /* $NetBSD: postscreen_starttls.c,v 1.1.1.2 2013/01/02 18:59:04 tron 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 44 /* System library. */ 45 46 #include <sys_defs.h> 47 48 /* Utility library. */ 49 50 #include <msg.h> 51 #include <mymalloc.h> 52 #include <connect.h> 53 #include <stringops.h> /* concatenate() */ 54 #include <vstring.h> 55 56 /* Global library. */ 57 58 #include <mail_params.h> 59 #include <mail_proto.h> 60 61 /* TLS library. */ 62 63 #include <tls_proxy.h> 64 65 /* Application-specific. */ 66 67 #include <postscreen.h> 68 69 /* 70 * For now, this code is built into the postscreen(8) daemon. In the future 71 * it may be abstracted into a reusable library module for use by other 72 * event-driven programs (perhaps smtp-source and smtp-sink). 73 */ 74 75 /* 76 * Transient state for the portscreen(8)-to-tlsproxy(8) hand-off protocol. 77 */ 78 typedef struct { 79 VSTREAM *tlsproxy_stream; /* hand-off negotiation */ 80 EVENT_NOTIFY_FN resume_event; /* call-back handler */ 81 PSC_STATE *smtp_state; /* SMTP session state */ 82 } PSC_STARTTLS; 83 84 #define TLSPROXY_INIT_TIMEOUT 10 85 86 static char *psc_tlsp_service = 0; 87 88 /* psc_starttls_finish - complete negotiation with TLS proxy */ 89 90 static void psc_starttls_finish(int event, char *context) 91 { 92 const char *myname = "psc_starttls_finish"; 93 PSC_STARTTLS *starttls_state = (PSC_STARTTLS *) context; 94 PSC_STATE *smtp_state = starttls_state->smtp_state; 95 VSTREAM *tlsproxy_stream = starttls_state->tlsproxy_stream; 96 int status; 97 98 if (msg_verbose) 99 msg_info("%s: send client handle on proxy socket %d" 100 " for smtp socket %d from [%s]:%s flags=%s", 101 myname, vstream_fileno(tlsproxy_stream), 102 vstream_fileno(smtp_state->smtp_client_stream), 103 smtp_state->smtp_client_addr, smtp_state->smtp_client_port, 104 psc_print_state_flags(smtp_state->flags, myname)); 105 106 /* 107 * We leave read-event notification enabled on the postscreen to TLS 108 * proxy stream, to avoid two kqueue/epoll/etc. system calls: one here, 109 * and one when resuming the dummy SMTP engine. 110 */ 111 if (event != EVENT_TIME) 112 event_cancel_timer(psc_starttls_finish, (char *) starttls_state); 113 114 /* 115 * Receive the "TLS is available" indication. 116 * 117 * This may seem out of order, but we must have a read transaction between 118 * sending the request attributes and sending the SMTP client file 119 * descriptor. We can't assume UNIX-domain socket semantics here. 120 */ 121 if (event != EVENT_READ 122 || attr_scan(tlsproxy_stream, ATTR_FLAG_STRICT, 123 ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status, 124 ATTR_TYPE_END) != 1 || status == 0) { 125 126 /* 127 * The TLS proxy reports that the TLS engine is not available (due to 128 * configuration error, or other causes). 129 */ 130 event_disable_readwrite(vstream_fileno(tlsproxy_stream)); 131 vstream_fclose(tlsproxy_stream); 132 PSC_SEND_REPLY(smtp_state, 133 "454 4.7.0 TLS not available due to local problem\r\n"); 134 } 135 136 /* 137 * Send the remote SMTP client file descriptor. 138 */ 139 else if (LOCAL_SEND_FD(vstream_fileno(tlsproxy_stream), 140 vstream_fileno(smtp_state->smtp_client_stream)) < 0) { 141 142 /* 143 * Some error: drop the TLS proxy stream. 144 */ 145 msg_warn("%s sending file handle to %s service", 146 event == EVENT_TIME ? "timeout" : "problem", 147 psc_tlsp_service); 148 event_disable_readwrite(vstream_fileno(tlsproxy_stream)); 149 vstream_fclose(tlsproxy_stream); 150 PSC_SEND_REPLY(smtp_state, 151 "454 4.7.0 TLS not available due to local problem\r\n"); 152 } 153 154 /* 155 * After we send the plaintext 220 greeting, the client-side TLS engine 156 * is supposed to talk first, then the server-side TLS engine. However, 157 * postscreen(8) will not participate in that conversation. 158 */ 159 else { 160 PSC_SEND_REPLY(smtp_state, "220 2.0.0 Ready to start TLS\r\n"); 161 162 /* 163 * Replace our SMTP client stream by the TLS proxy stream. Once the 164 * TLS handshake is done, the TLS proxy will deliver plaintext SMTP 165 * commands to postscreen(8). 166 * 167 * Swap the file descriptors from under the VSTREAM so that we don't 168 * have to worry about loss of user-configurable VSTREAM attributes. 169 */ 170 vstream_fpurge(smtp_state->smtp_client_stream, VSTREAM_PURGE_BOTH); 171 vstream_control(smtp_state->smtp_client_stream, 172 VSTREAM_CTL_SWAP_FD, tlsproxy_stream, 173 VSTREAM_CTL_END); 174 vstream_fclose(tlsproxy_stream); /* direct-to-client stream! */ 175 smtp_state->flags |= PSC_STATE_FLAG_USING_TLS; 176 } 177 178 /* 179 * Resume the postscreen(8) dummy SMTP engine and clean up. 180 */ 181 starttls_state->resume_event(event, (char *) smtp_state); 182 myfree((char *) starttls_state); 183 } 184 185 /* psc_starttls_open - open negotiations with TLS proxy */ 186 187 void psc_starttls_open(PSC_STATE *smtp_state, EVENT_NOTIFY_FN resume_event) 188 { 189 const char *myname = "psc_starttls_open"; 190 PSC_STARTTLS *starttls_state; 191 VSTREAM *tlsproxy_stream; 192 int fd; 193 static VSTRING *remote_endpt = 0; 194 195 if (psc_tlsp_service == 0) { 196 psc_tlsp_service = concatenate(MAIL_CLASS_PRIVATE "/", 197 var_tlsproxy_service, (char *) 0); 198 remote_endpt = vstring_alloc(20); 199 } 200 201 /* 202 * Connect to the tlsproxy(8) daemon. We report all errors 203 * asynchronously, to avoid having to maintain multiple delivery paths. 204 */ 205 if ((fd = LOCAL_CONNECT(psc_tlsp_service, NON_BLOCKING, 1)) < 0) { 206 msg_warn("connect to %s service: %m", psc_tlsp_service); 207 PSC_SEND_REPLY(smtp_state, 208 "454 4.7.0 TLS not available due to local problem\r\n"); 209 event_request_timer(resume_event, (char *) smtp_state, 0); 210 return; 211 } 212 if (msg_verbose) 213 msg_info("%s: send client name/address on proxy socket %d" 214 " for smtp socket %d from [%s]:%s flags=%s", 215 myname, fd, vstream_fileno(smtp_state->smtp_client_stream), 216 smtp_state->smtp_client_addr, smtp_state->smtp_client_port, 217 psc_print_state_flags(smtp_state->flags, myname)); 218 219 /* 220 * Initial handshake. Send the data attributes now, and send the client 221 * file descriptor in a later transaction. We report all errors 222 * asynchronously, to avoid having to maintain multiple delivery paths. 223 * 224 * XXX The formatted endpoint should be a state member. Then, we can 225 * simplify all the format strings throughout the program. 226 */ 227 tlsproxy_stream = vstream_fdopen(fd, O_RDWR); 228 vstring_sprintf(remote_endpt, "[%s]:%s", smtp_state->smtp_client_addr, 229 smtp_state->smtp_client_port); 230 attr_print(tlsproxy_stream, ATTR_FLAG_NONE, 231 ATTR_TYPE_STR, MAIL_ATTR_REMOTE_ENDPT, STR(remote_endpt), 232 ATTR_TYPE_INT, MAIL_ATTR_FLAGS, TLS_PROXY_FLAG_ROLE_SERVER, 233 ATTR_TYPE_INT, MAIL_ATTR_TIMEOUT, psc_normal_cmd_time_limit, 234 ATTR_TYPE_STR, MAIL_ATTR_SERVER_ID, MAIL_SERVICE_SMTPD, /* XXX */ 235 ATTR_TYPE_END); 236 if (vstream_fflush(tlsproxy_stream) != 0) { 237 msg_warn("error sending request to %s service: %m", psc_tlsp_service); 238 vstream_fclose(tlsproxy_stream); 239 PSC_SEND_REPLY(smtp_state, 240 "454 4.7.0 TLS not available due to local problem\r\n"); 241 event_request_timer(resume_event, (char *) smtp_state, 0); 242 return; 243 } 244 245 /* 246 * Set up a read event for the next phase of the TLS proxy handshake. 247 */ 248 starttls_state = (PSC_STARTTLS *) mymalloc(sizeof(*starttls_state)); 249 starttls_state->tlsproxy_stream = tlsproxy_stream; 250 starttls_state->resume_event = resume_event; 251 starttls_state->smtp_state = smtp_state; 252 PSC_READ_EVENT_REQUEST(vstream_fileno(tlsproxy_stream), psc_starttls_finish, 253 (char *) starttls_state, TLSPROXY_INIT_TIMEOUT); 254 } 255