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