static void ssl_input(struct ssl_proxy *proxy) { int rcvd, sent; rcvd = proxy_recv_ssl(proxy, proxy->outbuf_plain, sizeof(proxy->outbuf_plain)); if (rcvd <= 0) return; sent = net_transmit(proxy->fd_plain, proxy->outbuf_plain, (size_t)rcvd); if (sent == rcvd) return; if (sent < 0) { /* disconnected */ ssl_proxy_destroy(proxy); return; } /* everything wasn't sent - don't read anything until we've sent it all */ proxy->outbuf_pos_plain = 0; proxy->send_left_plain = rcvd - sent; io_remove(proxy->io_ssl); proxy->io_ssl = io_add(proxy->fd_ssl, IO_WRITE, ssl_output, proxy); }
static int handle_ssl_error(struct ssl_proxy *proxy, int error) { if (!gnutls_error_is_fatal(error)) { if (!verbose_ssl) return 0; if (error == GNUTLS_E_WARNING_ALERT_RECEIVED) { i_warning("Received SSL warning alert: %s [%s]", get_alert_text(proxy), net_ip2addr(&proxy->ip)); } else { i_warning("Non-fatal SSL error: %s: %s", get_alert_text(proxy), net_ip2addr(&proxy->ip)); } return 0; } if (verbose_ssl) { /* fatal error occurred */ if (error == GNUTLS_E_FATAL_ALERT_RECEIVED) { i_warning("Received SSL fatal alert: %s [%s]", get_alert_text(proxy), net_ip2addr(&proxy->ip)); } else { i_warning("Error reading from SSL client: %s [%s]", gnutls_strerror(error), net_ip2addr(&proxy->ip)); } } gnutls_alert_send_appropriate(proxy->session, error); ssl_proxy_destroy(proxy); return -1; }
static void login_proxy_free_final(struct login_proxy *proxy) { if (proxy->delayed_disconnect) { DLLIST_REMOVE(&login_proxies_disconnecting, proxy); i_assert(proxy->state_rec->num_delayed_client_disconnects > 0); if (--proxy->state_rec->num_delayed_client_disconnects == 0) proxy->state_rec->num_disconnects_since_ts = 0; timeout_remove(&proxy->to); } if (proxy->client_io != NULL) io_remove(&proxy->client_io); if (proxy->client_input != NULL) i_stream_destroy(&proxy->client_input); if (proxy->client_output != NULL) o_stream_destroy(&proxy->client_output); if (proxy->client_fd != -1) net_disconnect(proxy->client_fd); if (proxy->ssl_server_proxy != NULL) { ssl_proxy_destroy(proxy->ssl_server_proxy); ssl_proxy_free(&proxy->ssl_server_proxy); } i_free(proxy->host); i_free(proxy); }
static int proxy_recv_ssl(struct ssl_proxy *proxy, void *data, size_t size) { int rcvd; rcvd = gnutls_record_recv(proxy->session, data, size); if (rcvd > 0) return rcvd; if (rcvd == 0 || rcvd == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) { /* disconnected, either by nicely telling us that we'll close the connection, or by simply killing the connection which gives us the packet length error. */ ssl_proxy_destroy(proxy); return -1; } return handle_ssl_error(proxy, rcvd); }
static int proxy_send_ssl(struct ssl_proxy *proxy, const void *data, size_t size) { int sent; sent = gnutls_record_send(proxy->session, data, size); if (sent >= 0) return sent; if (sent == GNUTLS_E_PUSH_ERROR || sent == GNUTLS_E_INVALID_SESSION) { /* don't warn about errors related to unexpected disconnection */ ssl_proxy_destroy(proxy); return -1; } return handle_ssl_error(proxy, sent); }
int ssl_proxy_new(int fd, struct ip_addr *ip) { struct ssl_proxy *proxy; gnutls_session session; int sfd[2]; if (!ssl_initialized) { i_error("SSL support not enabled in configuration"); return -1; } session = initialize_state(); gnutls_transport_set_ptr(session, fd); if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) == -1) { i_error("socketpair() failed: %m"); gnutls_deinit(session); return -1; } net_set_nonblock(sfd[0], TRUE); net_set_nonblock(sfd[1], TRUE); net_set_nonblock(fd, TRUE); proxy = i_new(struct ssl_proxy, 1); proxy->refcount = 1; proxy->session = session; proxy->fd_ssl = fd; proxy->fd_plain = sfd[0]; proxy->ip = *ip; hash_table_insert(ssl_proxies, proxy, proxy); proxy->refcount++; ssl_handshake(proxy); if (!ssl_proxy_destroy(proxy)) { /* handshake failed. return the disconnected socket anyway so the caller doesn't try to use the old closed fd */ return sfd[1]; } main_ref(); return sfd[1]; }
static void ssl_output(struct ssl_proxy *proxy) { int sent; sent = net_transmit(proxy->fd_plain, proxy->outbuf_plain + proxy->outbuf_pos_plain, proxy->send_left_plain); if (sent < 0) { /* disconnected */ ssl_proxy_destroy(proxy); return; } proxy->send_left_plain -= sent; proxy->outbuf_pos_plain += sent; if (proxy->send_left_plain > 0) return; /* everything is sent, start reading again */ io_remove(proxy->io_ssl); proxy->io_ssl = io_add(proxy->fd_ssl, IO_READ, ssl_input, proxy); }
static void plain_input(struct ssl_proxy *proxy) { char buf[1024]; ssize_t rcvd, sent; rcvd = net_receive(proxy->fd_plain, buf, sizeof(buf)); if (rcvd < 0) { /* disconnected */ gnutls_bye(proxy->session, 1); ssl_proxy_destroy(proxy); return; } sent = proxy_send_ssl(proxy, buf, (size_t)rcvd); if (sent < 0 || sent == rcvd) return; /* everything wasn't sent - don't read anything until we've sent it all */ proxy->send_left_ssl = rcvd - sent; io_remove(proxy->io_plain); proxy->io_plain = io_add(proxy->fd_ssl, IO_WRITE, plain_output, proxy); }
void client_destroy(struct client *client, const char *reason) { if (client->destroyed) return; client->destroyed = TRUE; if (!client->login_success && reason != NULL) { reason = t_strconcat(reason, " ", client_get_extra_disconnect_reason(client), NULL); } if (reason != NULL) client_log(client, reason); if (last_client == client) last_client = client->prev; DLLIST_REMOVE(&clients, client); if (client->output != NULL) o_stream_uncork(client->output); if (!client->login_success && client->ssl_proxy != NULL) ssl_proxy_destroy(client->ssl_proxy); if (client->input != NULL) i_stream_close(client->input); if (client->output != NULL) o_stream_close(client->output); if (client->master_tag != 0) { i_assert(client->auth_request == NULL); i_assert(client->authenticating); i_assert(client->refcount > 1); client->authenticating = FALSE; master_auth_request_abort(master_auth, client->master_tag); client->refcount--; } else if (client->auth_request != NULL) { i_assert(client->authenticating); sasl_server_auth_abort(client); } else { i_assert(!client->authenticating); } if (client->io != NULL) io_remove(&client->io); if (client->to_disconnect != NULL) timeout_remove(&client->to_disconnect); if (client->to_auth_waiting != NULL) timeout_remove(&client->to_auth_waiting); if (client->auth_response != NULL) str_free(&client->auth_response); if (client->fd != -1) { net_disconnect(client->fd); client->fd = -1; } if (client->proxy_password != NULL) { safe_memset(client->proxy_password, 0, strlen(client->proxy_password)); i_free_and_null(client->proxy_password); } if (client->proxy_sasl_client != NULL) dsasl_client_free(&client->proxy_sasl_client); if (client->login_proxy != NULL) login_proxy_free(&client->login_proxy); if (client->v.destroy != NULL) client->v.destroy(client); if (client_unref(&client) && initial_service_count == 1) { /* as soon as this connection is done with proxying (or whatever), the process will die. there's no need for authentication anymore, so close the connection. do this only with initial service_count=1, in case there are other clients with pending authentications */ auth_client_disconnect(auth_client, "unnecessary connection"); } login_client_destroyed(); login_refresh_proctitle(); }