From: michael Date: Wed, 1 Jul 2015 18:09:27 +0000 (+0000) Subject: - Added HTTPS support for SSL/TLS-enabled HTTP proxies X-Git-Tag: 1.1.0beta1~7 X-Git-Url: http://git.serene-ircd.net/?a=commitdiff_plain;h=ade99cca6b6183d542a73c2931ee35459c16f85e;p=hopm.git - Added HTTPS support for SSL/TLS-enabled HTTP proxies git-svn-id: svn://svn.ircd-hybrid.org/svnroot/hopm/trunk@6222 82007160-df01-0410-b94d-b575c5fd34c7 --- diff --git a/configure.ac b/configure.ac index bb2fe7a..4c1931f 100644 --- a/configure.ac +++ b/configure.ac @@ -28,6 +28,7 @@ GCC_STACK_PROTECT_CC GCC_STACK_PROTECT_LIB AX_LIBRARY_NET +AX_ARG_OPENSSL AX_ARG_ENABLE_ASSERT AX_ARG_ENABLE_WARNINGS diff --git a/doc/reference.conf b/doc/reference.conf index fb83075..4cc24c5 100644 --- a/doc/reference.conf +++ b/doc/reference.conf @@ -467,6 +467,9 @@ scanner { protocol = HTTP:3128; protocol = HTTP:6588; + protocol = HTTPS:443; + protocol = HTTPS:8443; + /* * SOCKS4/5 - well known proxy protocols, probably the second most * common for insecure proxies, also offers transparent two way TCP @@ -511,6 +514,9 @@ scanner { */ protocol = HTTPPOST:80; + protocol = HTTPSPOST:443; + protocol = HTTPSPOST:8443; + /* * IP address this scanner will bind to. Use this if you need your scans to * come FROM a particular interface on the machine you run HOPM from. diff --git a/m4/ax_arg_openssl.m4 b/m4/ax_arg_openssl.m4 new file mode 100644 index 0000000..324555b --- /dev/null +++ b/m4/ax_arg_openssl.m4 @@ -0,0 +1,84 @@ +AC_DEFUN([AX_ARG_OPENSSL], [ +AC_ARG_ENABLE(openssl, +[ --enable-openssl[=DIR] Enable LibreSSL/OpenSSL support (DIR optional). + --disable-openssl Disable LibreSSL/OpenSSL support. ], +[ cf_enable_openssl=$enableval ], +[ cf_enable_openssl="auto" ]) +AC_MSG_CHECKING([for LibreSSL/OpenSSL]) +if test "$cf_enable_openssl" != "no"; then + cf_openssl_basedir="" + if test "$cf_enable_openssl" != "auto" && + test "$cf_enable_openssl" != "yes"; then + dnl Support for --enable-openssl=/some/place + cf_openssl_basedir="${cf_enable_openssl}" + else + dnl Do the auto-probe here. Check some common directory paths. + for dirs in /usr/local/ssl /usr/pkg /usr/local /usr/lib /usr/lib/ssl\ + /opt /opt/openssl /usr/local/openssl; do + if test -f "${dirs}/include/openssl/opensslv.h"; then + cf_openssl_basedir="${dirs}" + break + fi + done + unset dirs + fi + + dnl Now check cf_openssl_found to see if we found anything. + if test ! -z "$cf_openssl_basedir"; then + if test -f "${cf_openssl_basedir}/include/openssl/opensslv.h"; then + CPPFLAGS="-I${cf_openssl_basedir}/include $CPPFLAGS" + LDFLAGS="-L${cf_openssl_basedir}/lib $LDFLAGS" + else + dnl OpenSSL wasn't found in the directory specified. Naughty + dnl administrator... + cf_openssl_basedir="" + fi + else + dnl Check for stock FreeBSD 4.x and 5.x systems, since their files + dnl are in /usr/include and /usr/lib. In this case, we don't want to + dnl change INCLUDES or LIBS, but still want to enable OpenSSL. + dnl We can't do this check above, because some people want two versions + dnl of OpenSSL installed (stock FreeBSD 4.x/5.x and /usr/local/ssl) + dnl and they want /usr/local/ssl to have preference. + if test -f "/usr/include/openssl/opensslv.h"; then + cf_openssl_basedir="/usr" + fi + fi + + dnl If we have a basedir defined, then everything is okay. Otherwise, + dnl we have a problem. + if test ! -z "$cf_openssl_basedir"; then + AC_MSG_RESULT([$cf_openssl_basedir]) + cf_enable_openssl="yes" + else + AC_MSG_RESULT([not found. Please check your path.]) + cf_enable_openssl="no" + fi + unset cf_openssl_basedir +else + dnl If --disable-openssl was specified + AC_MSG_RESULT([disabled]) +fi + +AS_IF([test "$cf_enable_openssl" != "no"], + [AC_MSG_CHECKING(for LibreSSL or OpenSSL 0.9.8o and above) + AC_RUN_IFELSE([ + AC_LANG_PROGRAM([ + #include + #include ], + [[ exit(!(OPENSSL_VERSION_NUMBER >= 0x009080ffL)); ]])], + [cf_openssl_version_ok=yes], + [cf_openssl_version_ok=no], + [cf_openssl_version_ok=no]) + + AS_IF([test "$cf_openssl_version_ok" = "yes"], + [AC_MSG_RESULT(found) + + AC_CHECK_LIB(crypto, RSA_free) + AS_IF([test "$ac_cv_lib_crypto_RSA_free" = "yes"], + [AC_CHECK_LIB(ssl, SSL_connect)]) + ],[AC_MSG_RESULT(no - LibreSSL/OpenSSL support disabled) + cf_enable_openssl="no"])]) + +AM_CONDITIONAL(ENABLE_SSL, [test "$ac_cv_lib_ssl_SSL_connect" = yes]) +]) diff --git a/src/config-lexer.l b/src/config-lexer.l index 82c173a..40bf5a1 100644 --- a/src/config-lexer.l +++ b/src/config-lexer.l @@ -174,6 +174,16 @@ HTTPPOST { return PROTOCOLTYPE; } +HTTPS { + yylval.number = OPM_TYPE_HTTPS; + return PROTOCOLTYPE; + } + +HTTPSPOST { + yylval.number = OPM_TYPE_HTTPSPOST; + return PROTOCOLTYPE; + } + SOCKS4 { yylval.number = OPM_TYPE_SOCKS4; return PROTOCOLTYPE; diff --git a/src/libopm/src/libopm.c b/src/libopm/src/libopm.c index d78458e..6c84d0a 100644 --- a/src/libopm/src/libopm.c +++ b/src/libopm/src/libopm.c @@ -39,6 +39,9 @@ #include #include #include +#ifdef HAVE_LIBCRYPTO +#include +#endif static OPM_PROTOCOL_CONFIG_T *libopm_protocol_config_create(void); @@ -66,6 +69,7 @@ static void libopm_check_queue(OPM_T *); static void libopm_do_connect(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); static void libopm_do_readready(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); +static int libopm_do_readready_tls(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); static void libopm_do_writeready(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); static void libopm_do_hup(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); static void libopm_do_read(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); @@ -84,13 +88,15 @@ static OPM_REMOTE_T *libopm_setup_remote(OPM_REMOTE_T *, OPM_CONNECTION_T *); */ static OPM_PROTOCOL_T OPM_PROTOCOLS[] = { - { OPM_TYPE_HTTP, libopm_proxy_http_write, NULL }, - { OPM_TYPE_SOCKS4, libopm_proxy_socks4_write, NULL }, - { OPM_TYPE_SOCKS5, libopm_proxy_socks5_write, NULL }, - { OPM_TYPE_ROUTER, libopm_proxy_router_write, NULL }, - { OPM_TYPE_WINGATE, libopm_proxy_wingate_write, NULL }, - { OPM_TYPE_HTTPPOST, libopm_proxy_httppost_write, NULL }, - { OPM_TYPE_DREAMBOX, libopm_proxy_dreambox_write, NULL } + { OPM_TYPE_HTTP, libopm_proxy_http_write, NULL, 0 }, + { OPM_TYPE_SOCKS4, libopm_proxy_socks4_write, NULL, 0 }, + { OPM_TYPE_SOCKS5, libopm_proxy_socks5_write, NULL, 0 }, + { OPM_TYPE_ROUTER, libopm_proxy_router_write, NULL, 0 }, + { OPM_TYPE_WINGATE, libopm_proxy_wingate_write, NULL, 0 }, + { OPM_TYPE_HTTPPOST, libopm_proxy_httppost_write, NULL, 0 }, + { OPM_TYPE_DREAMBOX, libopm_proxy_dreambox_write, NULL, 0 }, + { OPM_TYPE_HTTPS, libopm_proxy_https_write, libopm_do_readready_tls, 1 }, + { OPM_TYPE_HTTPSPOST, libopm_proxy_httpspost_write, libopm_do_readready_tls, 1 } }; /* opm_create @@ -294,6 +300,10 @@ opm_addtype(OPM_T *scanner, int type, unsigned short int port) { if (type == OPM_PROTOCOLS[i].type) { +#ifndef HAVE_LIBCRYPTO + if (OPM_PROTOCOLS[i].use_tls) + return OPM_ERR_BADPROTOCOL; +#endif protocol_config = libopm_protocol_config_create(); protocol_config->type = &OPM_PROTOCOLS[i]; protocol_config->port = port; @@ -330,6 +340,10 @@ OPM_ERR_T opm_remote_addtype(OPM_REMOTE_T *remote, int type, unsigned short int { if (type == OPM_PROTOCOLS[i].type) { +#ifndef HAVE_LIBCRYPTO + if (OPM_PROTOCOLS[i].use_tls) + return OPM_ERR_BADPROTOCOL; +#endif protocol_config = libopm_protocol_config_create(); protocol_config->type = &OPM_PROTOCOLS[i]; protocol_config->port = port; @@ -595,6 +609,20 @@ libopm_scan_create(OPM_T *scanner, OPM_REMOTE_T *remote) OPM_SCAN_T *ret; OPM_CONNECTION_T *conn; OPM_NODE_T *node, *p; +#ifdef HAVE_LIBCRYPTO + static int tls_init = 0; + static SSL_CTX *ctx_client; + + if (!tls_init) + { + tls_init = 1; + SSLeay_add_ssl_algorithms(); + + ctx_client = SSL_CTX_new(SSLv23_client_method()); + if (!ctx_client) + exit(EXIT_FAILURE); + } +#endif ret = xcalloc(sizeof(*ret)); ret->remote = remote; @@ -608,6 +636,12 @@ libopm_scan_create(OPM_T *scanner, OPM_REMOTE_T *remote) conn->protocol = ((OPM_PROTOCOL_CONFIG_T *)p->data)->type; conn->port = ((OPM_PROTOCOL_CONFIG_T *)p->data)->port; +#ifdef HAVE_LIBCRYPTO + if (conn->protocol->use_tls) + /* SSL_new does only fail if OOM in which case HOPM exits anyway */ + conn->tls_handle = SSL_new(ctx_client); +#endif + node = libopm_node_create(conn); libopm_list_add(ret->connections, node); } @@ -622,6 +656,12 @@ libopm_scan_create(OPM_T *scanner, OPM_REMOTE_T *remote) conn->protocol = ((OPM_PROTOCOL_CONFIG_T *)p->data)->type; conn->port = ((OPM_PROTOCOL_CONFIG_T *)p->data)->port; +#ifdef HAVE_LIBCRYPTO + if (conn->protocol->use_tls) + /* SSL_new does only fail if OOM in which case HOPM exits anyway */ + conn->tls_handle = SSL_new(ctx_client); +#endif + node = libopm_node_create(conn); libopm_list_add(ret->connections, node); } @@ -846,6 +886,15 @@ libopm_check_closed(OPM_T *scanner) if (conn->state == OPM_STATE_CLOSED) { +#ifdef HAVE_LIBCRYPTO + if (conn->protocol->use_tls) + { + SSL_set_shutdown(conn->tls_handle, SSL_RECEIVED_SHUTDOWN); + if (!SSL_shutdown(conn->tls_handle)) + SSL_shutdown(conn->tls_handle); + SSL_free(conn->tls_handle); + } +#endif if (conn->fd > 0) close(conn->fd); @@ -859,6 +908,15 @@ libopm_check_closed(OPM_T *scanner) if (((present - conn->creation) >= timeout) && conn->state != OPM_STATE_UNESTABLISHED) { +#ifdef HAVE_LIBCRYPTO + if (conn->protocol->use_tls) + { + SSL_set_shutdown(conn->tls_handle, SSL_RECEIVED_SHUTDOWN); + if (!SSL_shutdown(conn->tls_handle)) + SSL_shutdown(conn->tls_handle); + SSL_free(conn->tls_handle); + } +#endif close(conn->fd); scanner->fd_use--; @@ -938,6 +996,11 @@ libopm_do_connect(OPM_T * scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) connect(conn->fd, (struct sockaddr *)addr, sizeof(*addr)); +#ifdef HAVE_LIBCRYPTO + if (conn->protocol->use_tls) + SSL_set_fd(conn->tls_handle, conn->fd); +#endif + conn->state = OPM_STATE_ESTABLISHED; time(&conn->creation); /* Stamp creation time, for timeout */ } @@ -1047,6 +1110,63 @@ libopm_check_poll(OPM_T *scanner) } } +static int +libopm_do_readready_tls(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) +{ +#ifdef HAVE_LIBCRYPTO + int max_read, length; + char readbuf[LIBOPM_TLS_RECORD_SIZE]; + + if (!SSL_is_init_finished(conn->tls_handle)) + return 0; + + if ((length = SSL_read(conn->tls_handle, readbuf, sizeof(readbuf))) <= 0) + { + switch (SSL_get_error(conn->tls_handle, length)) + { + /* TBD: possibly could recover here from some errors */ + default: + libopm_do_hup(scanner, scan, conn); + return 0; + } + } + + max_read = *(int *)libopm_config(scanner->config, OPM_CONFIG_MAX_READ); + + for (const char *p = readbuf, *end = readbuf + length; p < end; ++p) + { + conn->bytes_read++; + + if (conn->bytes_read >= max_read) + { + libopm_do_callback(scanner, libopm_setup_remote(scan->remote, conn), OPM_CALLBACK_ERROR, OPM_ERR_MAX_READ); + conn->state = OPM_STATE_CLOSED; + return 0; + } + + if (*p == '\0' || *p == '\r') + continue; + + if (*p == '\n') + { + conn->readbuf[conn->readlen] = '\0'; + conn->readlen = 0; + + libopm_do_read(scanner, scan, conn); + + if (conn->state == OPM_STATE_CLOSED) + return 0; + + continue; + } + + if (conn->readlen < READBUFLEN) + conn->readbuf[++(conn->readlen) - 1] = *p; /* -1 to pad for null term */ + } +#endif + return 0; +} + /* do_readready * * Remote connection is read ready, read the data into a buffer and check it against @@ -1200,6 +1320,17 @@ libopm_do_writeready(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) { OPM_PROTOCOL_T *protocol; +#ifdef HAVE_LIBCRYPTO + if (conn->protocol->use_tls) + { + if (!SSL_is_init_finished(conn->tls_handle)) + { + SSL_connect(conn->tls_handle); + return; + } + } +#endif + protocol = conn->protocol; /* Call write function for specific protocol */ diff --git a/src/libopm/src/libopm.h b/src/libopm/src/libopm.h index cffd545..72b19ae 100644 --- a/src/libopm/src/libopm.h +++ b/src/libopm/src/libopm.h @@ -9,6 +9,7 @@ #define CBLEN 5 /* Number of callback functions */ #define READBUFLEN 128 /* Size of conn->readbuf */ #define SENDBUFLEN 512 /* Size of sendbuffer in proxy.c */ +#define LIBOPM_TLS_RECORD_SIZE 16384 typedef struct _OPM_SCAN OPM_SCAN_T; typedef struct _OPM_CONNECTION OPM_CONNECTION_T; @@ -39,6 +40,7 @@ struct _OPM_CONNECTION unsigned short int readlen; /* Length of readbuf */ unsigned short int state; /* State of connection */ time_t creation; /* When this connection was established */ + void *tls_handle; /* SSL structure created by SSL_new() */ }; struct _OPM_PROTOCOL_CONFIG @@ -52,5 +54,6 @@ struct _OPM_PROTOCOL int type; /* Protocol type */ OPM_PROXYWRITE_T *write_function; /* Write function handler for this protocol */ OPM_PROXYREAD_T *read_function; /* Read function handler for this protocol */ + int use_tls; /* TLS/SSL-enabled protocol such as HTTPS */ }; #endif /* LIBOPM_H */ diff --git a/src/libopm/src/opm_types.h b/src/libopm/src/opm_types.h index d01ccaa..5fb50f8 100644 --- a/src/libopm/src/opm_types.h +++ b/src/libopm/src/opm_types.h @@ -25,6 +25,9 @@ #define OPM_TYPE_ROUTER 5 #define OPM_TYPE_HTTPPOST 6 #define OPM_TYPE_DREAMBOX 7 +#define OPM_TYPE_HTTPS 8 +#define OPM_TYPE_HTTPSPOST 9 + /* States */ #define OPM_STATE_UNESTABLISHED 1 diff --git a/src/libopm/src/proxy.c b/src/libopm/src/proxy.c index e69a622..1f2e70f 100644 --- a/src/libopm/src/proxy.c +++ b/src/libopm/src/proxy.c @@ -25,6 +25,9 @@ #include #include #include +#ifdef HAVE_LIBCRYPTO +#include +#endif #include "inet.h" #include "config.h" @@ -270,3 +273,45 @@ libopm_proxy_dreambox_write(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T * return OPM_SUCCESS; } + +int +libopm_proxy_https_write(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) +{ +#ifdef HAVE_LIBCRYPTO + size_t len = snprintf(SENDBUF, SENDBUFLEN, "CONNECT %s:%d HTTP/1.0\r\n\r\n", + (char *)libopm_config(scanner->config, OPM_CONFIG_SCAN_IP), + *(int *)libopm_config(scanner->config, OPM_CONFIG_SCAN_PORT)); + + SSL_write(conn->tls_handle, SENDBUF, len); + + /* extra linefeed required for MikroTik HttpProxy, must be separate send() */ + SSL_write(conn->tls_handle, "\r\n", 2); +#endif + return OPM_SUCCESS; +} + +/* + * HTTPS POST Scanning + * + */ +int +libopm_proxy_httpspost_write(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) +{ +#ifdef HAVE_LIBCRYPTO + size_t len; + int scan_port; + char *scan_ip; + + scan_ip = (char *)libopm_config(scanner->config, OPM_CONFIG_SCAN_IP); + scan_port = *(int *)libopm_config(scanner->config, OPM_CONFIG_SCAN_PORT); + + len = snprintf(SENDBUF, SENDBUFLEN, + "POST http://%s:%d/ HTTP/1.0\r\n" + "Content-type: text/plain\r\n" + "Content-length: 5\r\n\r\n" + "quit\r\n\r\n", scan_ip, scan_port); + + SSL_write(conn->tls_handle, SENDBUF, len); +#endif + return OPM_SUCCESS; +} diff --git a/src/libopm/src/proxy.h b/src/libopm/src/proxy.h index 2485184..c006203 100644 --- a/src/libopm/src/proxy.h +++ b/src/libopm/src/proxy.h @@ -10,4 +10,6 @@ extern int libopm_proxy_wingate_write(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *) extern int libopm_proxy_router_write(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); extern int libopm_proxy_httppost_write(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); extern int libopm_proxy_dreambox_write(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); +extern int libopm_proxy_https_write(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); +extern int libopm_proxy_httpspost_write(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); #endif /* PROXY_H */ diff --git a/src/scan.c b/src/scan.c index 76a0347..845793f 100644 --- a/src/scan.c +++ b/src/scan.c @@ -145,13 +145,15 @@ scan_gettype(int protocol) static const char *undef = "undefined"; static const struct protocol_assoc protocols[] = { - { OPM_TYPE_HTTP, "HTTP" }, - { OPM_TYPE_HTTPPOST, "HTTPPOST" }, - { OPM_TYPE_SOCKS4, "SOCKS4" }, - { OPM_TYPE_SOCKS5, "SOCKS5" }, - { OPM_TYPE_WINGATE, "WINGATE" }, - { OPM_TYPE_ROUTER, "ROUTER" }, - { OPM_TYPE_DREAMBOX, "DREAMBOX" } + { OPM_TYPE_HTTP, "HTTP" }, + { OPM_TYPE_HTTPPOST, "HTTPPOST" }, + { OPM_TYPE_SOCKS4, "SOCKS4" }, + { OPM_TYPE_SOCKS5, "SOCKS5" }, + { OPM_TYPE_WINGATE, "WINGATE" }, + { OPM_TYPE_ROUTER, "ROUTER" }, + { OPM_TYPE_HTTPS, "HTTPS" }, + { OPM_TYPE_HTTPSPOST, "HTTPSPOST" }, + { OPM_TYPE_DREAMBOX, "DREAMBOX" } }; for (unsigned int i = 0; i < (sizeof(protocols) / sizeof(struct protocol_assoc)); ++i) diff --git a/src/stats.c b/src/stats.c index 00d4c16..c5ca132 100644 --- a/src/stats.c +++ b/src/stats.c @@ -43,13 +43,17 @@ static unsigned int STATS_DNSBLSENT; static struct StatsHash STATS_PROXIES[] = { - { OPM_TYPE_HTTP, 0, "HTTP" }, - { OPM_TYPE_HTTPPOST, 0, "HTTPPOST" }, - { OPM_TYPE_SOCKS4, 0, "SOCKS4" }, - { OPM_TYPE_SOCKS5, 0, "SOCKS5" }, - { OPM_TYPE_ROUTER, 0, "ROUTER" }, - { OPM_TYPE_WINGATE, 0, "WINGATE" }, - { OPM_TYPE_DREAMBOX, 0, "DREAMBOX" }, + { OPM_TYPE_HTTP, 0, "HTTP" }, + { OPM_TYPE_HTTPPOST, 0, "HTTPPOST" }, +#ifdef HAVE_LIBCRYPTO + { OPM_TYPE_HTTPS, 0, "HTTPS" }, + { OPM_TYPE_HTTPSPOST, 0, "HTTPSPOST" }, +#endif + { OPM_TYPE_SOCKS4, 0, "SOCKS4" }, + { OPM_TYPE_SOCKS5, 0, "SOCKS5" }, + { OPM_TYPE_ROUTER, 0, "ROUTER" }, + { OPM_TYPE_WINGATE, 0, "WINGATE" }, + { OPM_TYPE_DREAMBOX, 0, "DREAMBOX" }, { 0, 0, NULL } };