/*
* 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 <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifdef SOLARIS
#include <crypt.h>
#endif
#include <db.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <poll.h>
#include <pwd.h>
#include <radlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#ifdef USE_SSL
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#endif
#include <poputil.h>
#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);
}
syntax highlighted by Code2HTML, v. 0.9.1