static int tlsp_eval_tls_error(TLSP_STATE *state, int err) { int ciphertext_fd = state->ciphertext_fd; /* * The ciphertext file descriptor is in non-blocking mode, meaning that * each SSL_accept/connect/read/write/shutdown request may return an * "error" indication that it needs to read or write more ciphertext. The * purpose of this routine is to translate those "error" indications into * the appropriate read/write/timeout event requests. */ switch (err) { /* * No error from SSL_read and SSL_write means that the plaintext * output buffer is full and that the plaintext input buffer is * empty. Stop read/write events on the ciphertext stream. Keep the * timer alive as a safety mechanism for the case that the plaintext * pseudothreads get stuck. */ case SSL_ERROR_NONE: if (state->ssl_last_err != SSL_ERROR_NONE) { event_disable_readwrite(ciphertext_fd); event_request_timer(tlsp_ciphertext_event, (char *) state, state->timeout); state->ssl_last_err = SSL_ERROR_NONE; } return (0); /* * The TLS engine wants to write to the network. Turn on * write/timeout events on the ciphertext stream. */ case SSL_ERROR_WANT_WRITE: if (state->ssl_last_err == SSL_ERROR_WANT_READ) event_disable_readwrite(ciphertext_fd); if (state->ssl_last_err != SSL_ERROR_WANT_WRITE) { event_enable_write(ciphertext_fd, tlsp_ciphertext_event, (char *) state); state->ssl_last_err = SSL_ERROR_WANT_WRITE; } event_request_timer(tlsp_ciphertext_event, (char *) state, state->timeout); return (0); /* * The TLS engine wants to read from the network. Turn on * read/timeout events on the ciphertext stream. */ case SSL_ERROR_WANT_READ: if (state->ssl_last_err == SSL_ERROR_WANT_WRITE) event_disable_readwrite(ciphertext_fd); if (state->ssl_last_err != SSL_ERROR_WANT_READ) { event_enable_read(ciphertext_fd, tlsp_ciphertext_event, (char *) state); state->ssl_last_err = SSL_ERROR_WANT_READ; } event_request_timer(tlsp_ciphertext_event, (char *) state, state->timeout); return (0); /* * Some error. Self-destruct. This automagically cleans up all * pending read/write and timeout event requests, making state a * dangling pointer. */ case SSL_ERROR_SSL: tls_print_errors(); /* FALLTHROUGH */ default: tlsp_state_free(state); return (-1); } }
static void tlsp_get_request_event(int event, char *context) { const char *myname = "tlsp_get_request_event"; TLSP_STATE *state = (TLSP_STATE *) context; VSTREAM *plaintext_stream = state->plaintext_stream; int plaintext_fd = vstream_fileno(plaintext_stream); static VSTRING *remote_endpt; static VSTRING *server_id; int req_flags; int timeout; int ready; /* * One-time initialization. */ if (remote_endpt == 0) { remote_endpt = vstring_alloc(10); server_id = vstring_alloc(10); } /* * At this point we still manually manage plaintext read/write/timeout * events. Turn off timer events. Below we disable read events on error, * and redefine read events on success. */ if (event != EVENT_TIME) event_cancel_timer(tlsp_get_request_event, (char *) state); /* * We must send some data, after receiving the request attributes and * before receiving the remote file descriptor. We can't assume * UNIX-domain socket semantics here. */ if (event != EVENT_READ || attr_scan(plaintext_stream, ATTR_FLAG_STRICT, ATTR_TYPE_STR, MAIL_ATTR_REMOTE_ENDPT, remote_endpt, ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &req_flags, ATTR_TYPE_INT, MAIL_ATTR_TIMEOUT, &timeout, ATTR_TYPE_STR, MAIL_ATTR_SERVER_ID, server_id, ATTR_TYPE_END) != 4) { msg_warn("%s: receive request attributes: %m", myname); event_disable_readwrite(plaintext_fd); tlsp_state_free(state); return; } /* * If the requested TLS engine is unavailable, hang up after making sure * that the plaintext peer has received our "sorry" indication. */ ready = ((req_flags & TLS_PROXY_FLAG_ROLE_SERVER) != 0 && tlsp_server_ctx != 0); if (attr_print(plaintext_stream, ATTR_FLAG_NONE, ATTR_TYPE_INT, MAIL_ATTR_STATUS, ready, ATTR_TYPE_END) != 0 || vstream_fflush(plaintext_stream) != 0 || ready == 0) { read_wait(plaintext_fd, TLSP_INIT_TIMEOUT); /* XXX */ event_disable_readwrite(plaintext_fd); tlsp_state_free(state); return; } /* * XXX We use the same fixed timeout throughout the entire session for * both plaintext and ciphertext communication. This timeout is just a * safety feature; the real timeout will be enforced by our plaintext * peer. */ else { state->remote_endpt = mystrdup(STR(remote_endpt)); state->server_id = mystrdup(STR(server_id)); msg_info("CONNECT %s %s", (req_flags & TLS_PROXY_FLAG_ROLE_SERVER) ? "from" : (req_flags & TLS_PROXY_FLAG_ROLE_CLIENT) ? "to" : "(bogus_direction)", state->remote_endpt); state->req_flags = req_flags; state->timeout = timeout + 10; /* XXX */ event_enable_read(plaintext_fd, tlsp_get_fd_event, (char *) state); event_request_timer(tlsp_get_fd_event, (char *) state, TLSP_INIT_TIMEOUT); return; } }