/* * active port forwarder - software for secure forwarding * Copyright (C) 2003-2007 jeremian * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include "http_proxy_functions.h" #include "http_proxy_client.h" #include "thread_management.h" #include "network.h" #include "stats.h" #include "logging.h" #include "base64.h" #ifdef HAVE_LIBPTHREAD typedef struct { int sockfd; char *host; char *serv; HttpProxyOptions* hpo; char type; SSL_CTX* ctx; } proxy_argT; /* * Function name: clean_return * Description: Closes the connection and exits the thread. * Arguments: sockfd - the descriptor of the connection */ static void clean_return(int sockfd) { aflog(LOG_T_MAIN, LOG_I_DEBUG, "http proxy: clean_return"); close(sockfd); pthread_exit(NULL); } /* * Function name: http_proxy_client * Description: Function responsible for the client part of the http proxy connection. * Arguments: vptr - the structure with all the information needed for http proxy tunnel */ void* http_proxy_client(void *vptr) { char tab[9000]; connection conn; header hdr; int j, n, maxfdp1; fd_set rset, allset; struct timeval tv; int timeout = 5; int tmp; char *host, *serv, *proxyname, *proxyport, *credentials, *name = ""; char b64cred[100]; char type, authtype, https; SSL_CTX* ctx; proxy_argT *proxy_argptr; start_critical_section(); proxy_argptr = (proxy_argT *) vptr; host = proxy_argptr->host; serv = proxy_argptr->serv; proxyname = HttpProxyOptions_get_proxyname(proxy_argptr->hpo); proxyport = HttpProxyOptions_get_proxyport(proxy_argptr->hpo); credentials = HttpProxyOptions_get_proxyauth_cred(proxy_argptr->hpo); type = proxy_argptr->type; authtype = HttpProxyOptions_get_proxyauth_type(proxy_argptr->hpo); conn.sockfd = proxy_argptr->sockfd; https = HttpProxyOptions_is_https(proxy_argptr->hpo); ctx = proxy_argptr->ctx; broadcast_condition(); end_critical_section(); conn.postFd = SslFd_new(); conn.getFd = SslFd_new(); conn.tmpFd = SslFd_new(); if ((conn.postFd == NULL) || (conn.getFd == NULL) || (conn.tmpFd == NULL)) { aflog(LOG_T_INIT, LOG_I_CRIT, "http%s proxy: Can't allocate memory... exiting.", name); exit(1); } if (https) { name = "s"; SslFd_set_ssl(conn.postFd, SSL_new(ctx)); SslFd_set_ssl(conn.getFd, SSL_new(ctx)); SslFd_set_ssl(conn.tmpFd, SSL_new(ctx)); if ((SslFd_get_ssl(conn.postFd) == NULL) || (SslFd_get_ssl(conn.getFd) == NULL) || (SslFd_get_ssl(conn.tmpFd) == NULL)) { aflog(LOG_T_INIT, LOG_I_CRIT, "http%s proxy: Can't allocate memory... exiting.", name); exit(1); } } if (authtype == PROXYAUTH_TYPE_WRONG) { aflog(LOG_T_MAIN, LOG_I_WARNING, "Wrong type of proxy authorizaton --> switching to no authorization"); credentials = NULL; } if (credentials) { if (b64_ntop((unsigned char*)credentials, strlen(credentials), b64cred, 100) == -1) { aflog(LOG_T_MAIN, LOG_I_ERR, "Cannot encode credentials for proxy authorization"); b64cred[0] = 0; } else { if (authtype == PROXYAUTH_TYPE_NOTSET) { authtype = PROXYAUTH_TYPE_BASIC; } } } FD_ZERO(&allset); tv.tv_usec = 0; tv.tv_sec = timeout; memset(conn.id, 0, 10); for (j = 0; j < 9; ++j) { conn.id[j] = myrand(65, 90); } conn.id[9] = 0; /* postfd */ aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: connecting (postfd)...", name); if (ip_connect(&tmp, proxyname, proxyport, type, NULL, NULL)) { clean_return(conn.sockfd); } SslFd_set_fd(conn.postFd, tmp); if (https) { if (SSL_set_fd(SslFd_get_ssl(conn.postFd), SslFd_get_fd(conn.postFd)) != 1) { aflog(LOG_T_INIT, LOG_I_CRIT, "https proxy: Problem with initializing ssl"); clean_return(conn.sockfd); } if (SSL_connect(SslFd_get_ssl(conn.postFd)) != 1) { aflog(LOG_T_INIT, LOG_I_CRIT, "https proxy: SSL_connect has failed"); clean_return(conn.sockfd); } } memset(tab, 0, 9000); switch (authtype) { case PROXYAUTH_TYPE_BASIC: sprintf(tab, "POST http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" "Host: %s:%s\r\n" "Content-Length: 90000\r\n" "Connection: close\r\n" "Proxy-Authorization: Basic %s\r\n\r\n", host, serv, conn.id, host, serv, b64cred); break; default: sprintf(tab, "POST http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" "Host: %s:%s\r\n" "Content-Length: 90000\r\n" "Connection: close\r\n\r\n", host, serv, conn.id, host, serv); } j = strlen (tab); aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: writing POST request...", name); if (http_write(https, conn.postFd, (unsigned char*) tab, j) <= 0) { clean_return(conn.sockfd); } /* getfd */ aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: connecting (getfd)...", name); if (ip_connect(&tmp, proxyname, proxyport, type, NULL, NULL)) { clean_return(conn.sockfd); } SslFd_set_fd(conn.getFd, tmp); if (https) { if (SSL_set_fd(SslFd_get_ssl(conn.getFd), SslFd_get_fd(conn.getFd)) != 1) { aflog(LOG_T_INIT, LOG_I_CRIT, "https proxy: Problem with initializing ssl"); clean_return(conn.sockfd); } if (SSL_connect(SslFd_get_ssl(conn.getFd)) != 1) { aflog(LOG_T_INIT, LOG_I_CRIT, "https proxy: SSL_connect has failed"); clean_return(conn.sockfd); } } memset(tab, 0, 9000); switch (authtype) { case PROXYAUTH_TYPE_BASIC: sprintf(tab, "GET http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" "Host: %s:%s\r\n" "Connection: close\r\n" "Proxy-Authorization: Basic %s\r\n\r\n", host, serv, conn.id, host, serv, b64cred); break; default: sprintf(tab, "GET http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" "Host: %s:%s\r\n" "Connection: close\r\n\r\n", host, serv, conn.id, host, serv); } j = strlen (tab); aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: writing GET request...", name); if (http_write(https, conn.getFd, (unsigned char*) tab, j) <= 0) { clean_return(conn.sockfd); } set_fd(conn.sockfd, &maxfdp1, &allset); set_fd(SslFd_get_fd(conn.postFd), &maxfdp1, &allset); set_fd(SslFd_get_fd(conn.getFd), &maxfdp1, &allset); conn.state = C_OPEN; memset(tab, 0, 9000); aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: parsing header from getfd", name); if (parse_header(conn.getFd, tab, &hdr, https)) { clean_return(conn.sockfd); } aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: checking hdr.type", name); if (hdr.type != H_TYPE_OK) { clean_return(conn.sockfd); } if (hdr.length) { conn.received += hdr.length; aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: reading message...", name); if (read_message(conn.sockfd, hdr.length, &conn, tab, hdr.ptr)) { clean_return(conn.sockfd); } } while (1) { if ((conn.state == C_OPEN) && (!(FD_ISSET(conn.sockfd, &allset)))) { FD_SET(conn.sockfd, &allset); } rset = allset; if (select(maxfdp1, &rset, NULL, NULL, &tv) == 0) { aflog(LOG_T_MAIN, LOG_I_DDEBUG, "http%s proxy: timeout", name); tv.tv_sec = timeout; if (conn.sent_ptr+1 >= 90000) { if (conn.state == C_CLOSED) { continue; } aflog(LOG_T_MAIN, LOG_I_DDEBUG, "http%s proxy: send T", name); http_write(https, conn.postFd, (unsigned char*) "T", 1); conn.sent_ptr = 0; clear_sslFd(conn.postFd, &allset); /* postfd */ aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: connecting (postfd)...", name); if (ip_connect(&tmp, proxyname, proxyport, type, NULL, NULL)) { clean_return(conn.sockfd); } SslFd_set_fd(conn.postFd, tmp); if (https) { if (SSL_set_fd(SslFd_get_ssl(conn.postFd), SslFd_get_fd(conn.postFd)) != 1) { aflog(LOG_T_INIT, LOG_I_CRIT, "https proxy: Problem with initializing ssl"); clean_return(conn.sockfd); } if (SSL_connect(SslFd_get_ssl(conn.postFd)) != 1) { aflog(LOG_T_INIT, LOG_I_CRIT, "https proxy: SSL_connect has failed"); clean_return(conn.sockfd); } } memset(tab, 0, 9000); switch (authtype) { case PROXYAUTH_TYPE_BASIC: sprintf(tab, "POST http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" "Host: %s:%s\r\n" "Content-Length: 90000\r\n" "Connection: close\r\n" "Proxy-Authorization: Basic %s\r\n\r\n", host, serv, conn.id, host, serv, b64cred); break; default: sprintf(tab, "POST http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" "Host: %s:%s\r\n" "Content-Length: 90000\r\n" "Connection: close\r\n\r\n", host, serv, conn.id, host, serv); } j = strlen (tab); aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: writing POST request...", name); if (http_write(https, conn.postFd, (unsigned char *) tab, j) <= 0) { clean_return(conn.sockfd); } conn.sent_ptr = 0; conn.ptr = 0; conn.length = 0; conn.state = C_CLOSED; set_fd(SslFd_get_fd(conn.postFd), &maxfdp1, &allset); } else { aflog(LOG_T_MAIN, LOG_I_DDEBUG, "http%s proxy: send T", name); http_write(https, conn.postFd, (unsigned char *) "T", 1); conn.sent_ptr += 1; } continue; } /* sockfd */ if (FD_ISSET(conn.sockfd, &rset)) { aflog(LOG_T_MAIN, LOG_I_DDEBUG, "http%s proxy: FD_ISSET(conn.sockfd)", name); n = read(conn.sockfd, conn.buf+5, 8995); if (n <= 0) { aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: send Q", name); http_write(https, conn.postFd, (unsigned char *) "Q", 1); clean_return(conn.sockfd); } if ((conn.state == C_CLOSED) && (conn.sent_ptr > 70000)) { FD_CLR(conn.sockfd, &allset); } conn.buf[0] = 'M'; tmp = htonl(n); memcpy(&conn.buf[1], &tmp, 4); if (conn.sent_ptr+5 + n >= 90000) { aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: send message", name); http_write(https, conn.postFd, (unsigned char *) conn.buf, 90000 - conn.sent_ptr); conn.ptr = 90000 - conn.sent_ptr; conn.length = 5+n - conn.ptr; conn.sent_ptr = 0; clear_sslFd(conn.postFd, &allset); /* postfd */ aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: connecting (postfd)...", name); if (ip_connect(&tmp, proxyname, proxyport, type, NULL, NULL)) { clean_return(conn.sockfd); } SslFd_set_fd(conn.postFd, tmp); if (https) { if (SSL_set_fd(SslFd_get_ssl(conn.postFd), SslFd_get_fd(conn.postFd)) != 1) { aflog(LOG_T_INIT, LOG_I_CRIT, "https proxy: Problem with initializing ssl"); clean_return(conn.sockfd); } if (SSL_connect(SslFd_get_ssl(conn.postFd)) != 1) { aflog(LOG_T_INIT, LOG_I_CRIT, "https proxy: SSL_connect has failed"); clean_return(conn.sockfd); } } memset(tab, 0, 9000); switch (authtype) { case PROXYAUTH_TYPE_BASIC: sprintf(tab, "POST http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" "Host: %s:%s\r\n" "Content-Length: 90000\r\n" "Connection: close\r\n" "Proxy-Authorization: Basic %s\r\n\r\n", host, serv, conn.id, host, serv, b64cred); break; default: sprintf(tab, "POST http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" "Host: %s:%s\r\n" "Content-Length: 90000\r\n" "Connection: close\r\n\r\n", host, serv, conn.id, host, serv); } j = strlen (tab); aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: writing POST request...", name); if (http_write(https, conn.postFd, (unsigned char *) tab, j) <= 0) { clean_return(conn.sockfd); } if (conn.length > 0) { aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: writing old data...", name); if (http_write(https, conn.postFd, (unsigned char *) (conn.buf+conn.ptr), conn.length) <= 0) { clean_return(conn.sockfd); } } conn.sent_ptr = conn.length; conn.ptr = 0; conn.length = 0; conn.state = C_CLOSED; set_fd(SslFd_get_fd(conn.postFd), &maxfdp1, &allset); } else { aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: send message", name); http_write(https, conn.postFd, (unsigned char *) conn.buf, 5+n); conn.sent_ptr += 5+n; } } /* getfd */ if (FD_ISSET(SslFd_get_fd(conn.getFd), &rset)) { aflog(LOG_T_MAIN, LOG_I_DDEBUG, "http%s proxy: FD_ISSET(conn.getfd)", name); n = http_read(https, conn.getFd, (unsigned char*) tab, 9000); conn.received += n; if (n == 0) { conn.received = 0; clear_sslFd(conn.getFd, &allset); /* getfd */ aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: connecting (getfd)...", name); if (ip_connect(&tmp, proxyname, proxyport, type, NULL, NULL)) { clean_return(conn.sockfd); } SslFd_set_fd(conn.getFd, tmp); if (https) { if (SSL_set_fd(SslFd_get_ssl(conn.getFd), SslFd_get_fd(conn.getFd)) != 1) { aflog(LOG_T_INIT, LOG_I_CRIT, "https proxy: Problem with initializing ssl"); clean_return(conn.sockfd); } if (SSL_connect(SslFd_get_ssl(conn.getFd)) != 1) { aflog(LOG_T_INIT, LOG_I_CRIT, "https proxy: SSL_connect has failed"); clean_return(conn.sockfd); } } memset(tab, 0, 9000); switch (authtype) { case PROXYAUTH_TYPE_BASIC: sprintf(tab, "GET http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" "Host: %s:%s\r\n" "Connection: close\r\n" "Proxy-Authorization: Basic %s\r\n\r\n", host, serv, conn.id, host, serv, b64cred); break; default: sprintf(tab, "GET http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" "Host: %s:%s\r\n" "Connection: close\r\n\r\n", host, serv, conn.id, host, serv); } j = strlen (tab); aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: writing GET request...", name); if (http_write(https, conn.getFd, (unsigned char *) tab, j) <= 0) { clean_return(conn.sockfd); } memset(tab, 0, 9000); aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: parsing header from getfd", name); if (parse_header(conn.getFd, tab, &hdr, https)) { clean_return(conn.sockfd); } aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: checking hdr.type", name); if (hdr.type != H_TYPE_OK) { clean_return(conn.sockfd); } set_fd(SslFd_get_fd(conn.getFd), &maxfdp1, &allset); if (hdr.length) { conn.received += hdr.length; aflog(LOG_T_MAIN, LOG_I_DEBUG, "http%s proxy: reading message...", name); if (read_message(conn.sockfd, hdr.length, &conn, tab, hdr.ptr)) { clean_return(conn.sockfd); } } } else { if (read_message(conn.sockfd, n, &conn, tab, 0)) { clean_return(conn.sockfd); } } } /* postfd */ if (FD_ISSET(SslFd_get_fd(conn.postFd), &rset)) { aflog(LOG_T_MAIN, LOG_I_DDEBUG, "http%s proxy: FD_ISSET(conn.postfd) --> clean_return", name); clear_sslFd(conn.postFd, &allset); clean_return(conn.sockfd); } } clean_return(conn.sockfd); } /* * Function name: initialize_http_proxy_client * Description: Initializes the thread responsible for http proxy connection. * Arguments: sockfd - the new connection descriptor will be stored here * cr - the pointer to ClientRealm structure * ctx - the pointer to SSL_CTX structure * Returns: 0 - success, * !0 - failure. */ int initialize_http_proxy_client(int* sockfd, ClientRealm* cr, SSL_CTX* ctx) { int retval; int sockets[2]; pthread_t proxy_thread; static proxy_argT arg; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets)) { return 1; } (*sockfd) = sockets[0]; if (HttpProxyOptions_get_proxyname(ClientRealm_get_httpProxyOptions(cr)) == NULL) { return 1; } start_critical_section(); arg.host = ClientRealm_get_serverName(cr); arg.serv = ClientRealm_get_managePort(cr); arg.hpo = ClientRealm_get_httpProxyOptions(cr); arg.type = ClientRealm_get_ipFamily(cr); arg.sockfd = sockets[1]; arg.ctx = ctx; retval = pthread_create(&proxy_thread, NULL, &http_proxy_client, &arg); wait_for_condition(); end_critical_section(); return retval; } #endif