/* * Copyright (c) 1999 Ian Freislich * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: proxy.c,v 1.7 2002/11/21 13:32:41 ianf Exp $ */ #include #include #include #include #include #include #ifdef SOLARIS #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_SSL #include #include #include #include #include #endif #include #include "config.h" #include "popd.h" #include "paths.h" #include "authenticate.h" #include "proxy.h" #include "signals.h" /* global variables */ #ifdef USE_SSL static SSL_CTX *ctx = NULL; static SSL *ssl = NULL; static X509 *server_cert = NULL; static SSL_METHOD *meth = NULL; #endif extern struct config config; static char *remote_auth(struct connection *); static size_t proxy_read(int fd, void *ptr, size_t nbytes); static size_t proxy_write(int fd, void *ptr, size_t nbytes); void proxy(struct connection *cxn) { int i, len, result; char *response, buffer[MAXBUFLEN]; struct protoent *prot; struct sockaddr_in pop3_server; struct pollfd *pollfd; #ifdef USE_SSL if (config.ssl_proxy) { SSLeay_add_ssl_algorithms(); meth = SSLv2_client_method(); SSL_load_error_strings(); if ((ctx = SSL_CTX_new(meth)) == NULL) exit_error(EX_SOFTWARE, "Unable to initialise SSL"); if ((ssl = SSL_new (ctx)) == NULL) exit_error(EX_SOFTWARE, "Unable to initialise SSL"); SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); } #endif if ((prot = getprotobyname("TCP")) == NULL) exit(EX_PROTOCOL); config.proxy_socket = socket(PF_INET, SOCK_STREAM, prot->p_proto); pop3_server.sin_family = AF_INET; pop3_server.sin_addr = config.proxy_addr; pop3_server.sin_port = config.proxy_port; if (!connect(config.proxy_socket, (struct sockaddr *)&pop3_server, sizeof(pop3_server))) { #ifdef USE_SSL if (config.ssl_proxy) { SSL_set_fd(ssl, config.proxy_socket); if (SSL_connect(ssl) < 0) exit_error(EX_PROTOCOL, "Unable to establish " "SSL connection to remote server"); if ((server_cert = SSL_get_peer_certificate(ssl)) != NULL) { /* Perhaps do some certificate verification in * the future */ X509_free(server_cert); } } #endif response = remote_auth(cxn); sendline(SEND_FLUSH, "%s", response); pollfd = xmalloc(2 * sizeof(struct pollfd)); pollfd[0].fd = fileno(config.socket_in); pollfd[0].events = POLLRDNORM; pollfd[0].revents = 0; pollfd[1].fd = config.proxy_socket; pollfd[1].events = POLLRDNORM; pollfd[1].revents = 0; while ((result = poll(pollfd, 2, INFTIM))) { if (errno == EINTR) { removesignal(); continue; } for (i = 0; i < 2; ++i) { if (!pollfd[i].revents) continue; if (i == 0) { len = xread(buffer, MAXBUFLEN); if (len == 0) exit(EX_OK); proxy_write(pollfd[1].fd, buffer, len); } if (i == 1) { len = proxy_read(pollfd[1].fd, buffer, MAXBUFLEN); if (len == 0) exit(EX_OK); xwrite(buffer, len); } pollfd[i].revents = 0; } } } else { sendline(SEND_FLUSH, "-ERR unable to connect to %s:%d", inet_ntoa(config.proxy_addr), ntohs(config.proxy_port)); } } static size_t proxy_read(int fd, void *ptr, size_t nbytes) { size_t len; #ifdef USE_SSL if (config.ssl_proxy) len = SSL_read(ssl, ptr, nbytes); else #endif len = read(fd, ptr, nbytes); return(len); } static size_t proxy_write(int fd, void *ptr, size_t nbytes) { int len; #ifdef USE_SSL if (config.ssl_proxy) len = SSL_write(ssl, ptr, nbytes); else #endif len = write(fd, ptr, nbytes); return(len); } static char * remote_auth(struct connection *cxn) { int len; static char buffer[MAXBUFLEN + 1]; if ((len = proxy_read(config.proxy_socket, buffer, MAXBUFLEN)) < 2) { sendline(SEND_FLUSH, "-ERR remote error: mangled response " "from backend"); exit(EX_PROTOCOL); } buffer[len - 2] = '\0'; if (strncmp(buffer, "+OK ", 4)) { sendline(SEND_FLUSH, "-ERR remote error: '%s'", buffer); exit(EX_PROTOCOL); } len = sprintf(buffer, "user %s\r\n", config.proxy_name ? config.proxy_name : cxn->auth_string); proxy_write(config.proxy_socket, buffer, len); if ((len = proxy_read(config.proxy_socket, buffer, MAXBUFLEN)) < 2) { sendline(SEND_FLUSH, "-ERR remote error: mangled response " "from backend"); exit(EX_PROTOCOL); } buffer[len - 2] = '\0'; if (strncmp(buffer, "+OK ", 4)) { sendline(SEND_FLUSH, "-ERR remote error: '%s'", buffer); exit(EX_PROTOCOL); } len = sprintf(buffer, "pass %s\r\n", config.proxy_passwd ? config.proxy_passwd : cxn->password); proxy_write(config.proxy_socket, buffer, len); if ((len = proxy_read(config.proxy_socket, buffer, MAXBUFLEN)) < 2) { sendline(SEND_FLUSH, "-ERR remote error: mangled response " "from backend"); exit(EX_PROTOCOL); } buffer[len - 2] = '\0'; if (strncmp(buffer, "+OK ", 4)) { sendline(SEND_FLUSH, "-ERR remote error: '%s'", buffer); exit(EX_PROTOCOL); } /* No errors, so return the response string from the remote server */ return(buffer); }