|
|
|
/*-
|
|
|
|
* SSLsplit - transparent SSL/TLS interception
|
|
|
|
* https://www.roe.ch/SSLsplit
|
|
|
|
*
|
|
|
|
* Copyright (c) 2009-2019, Daniel Roethlisberger <daniel@roe.ch>.
|
|
|
|
* Copyright (c) 2017-2024, Soner Tari <sonertari@gmail.com>.
|
|
|
|
* 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 COPYRIGHT HOLDER 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 COPYRIGHT HOLDER 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "protossl.h"
|
|
|
|
#include "prototcp.h"
|
|
|
|
#include "protopassthrough.h"
|
|
|
|
|
|
|
|
#include "cachemgr.h"
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <event2/bufferevent_ssl.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Context used for all server sessions.
|
|
|
|
*/
|
|
|
|
#ifdef USE_SSL_SESSION_ID_CONTEXT
|
|
|
|
static unsigned long ssl_session_context = 0x31415926;
|
|
|
|
#endif /* USE_SSL_SESSION_ID_CONTEXT */
|
|
|
|
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
|
|
|
#define ERR_GET_FUNC(x) 0
|
|
|
|
#define ERR_func_error_string(x) ""
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void
|
|
|
|
protossl_log_ssl_error(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
unsigned long sslerr;
|
|
|
|
|
|
|
|
/* Can happen for socket errs, ssl errs;
|
|
|
|
* may happen for unclean ssl socket shutdowns. */
|
|
|
|
sslerr = bufferevent_get_openssl_error(bev);
|
|
|
|
if (sslerr)
|
|
|
|
ctx->sslctx->have_sslerr = 1;
|
|
|
|
if (!errno && !sslerr) {
|
|
|
|
#if LIBEVENT_VERSION_NUMBER >= 0x02010000
|
|
|
|
/* We have disabled notification for unclean shutdowns
|
|
|
|
* so this should not happen; log a warning. */
|
|
|
|
log_err_level_printf(LOG_WARNING, "Spurious error from bufferevent (errno=0,sslerr=0)\n");
|
|
|
|
#else /* LIBEVENT_VERSION_NUMBER < 0x02010000 */
|
|
|
|
/* Older versions of libevent will report these. */
|
|
|
|
if (OPTS_DEBUG(ctx->global)) {
|
|
|
|
log_dbg_printf("Unclean SSL shutdown, fd=%d\n", ctx->fd);
|
|
|
|
}
|
|
|
|
#endif /* LIBEVENT_VERSION_NUMBER < 0x02010000 */
|
|
|
|
} else if (ERR_GET_REASON(sslerr) == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE) {
|
|
|
|
/* these can happen due to client cert auth,
|
|
|
|
* only log error if debugging is activated */
|
|
|
|
log_dbg_printf("Error from bufferevent: %i:%s %lu:%i:%s:%i:%s:%i:%s\n",
|
|
|
|
errno, errno ? strerror(errno) : "-", sslerr,
|
|
|
|
ERR_GET_REASON(sslerr), STRORDASH(ERR_reason_error_string(sslerr)),
|
|
|
|
ERR_GET_LIB(sslerr), STRORDASH(ERR_lib_error_string(sslerr)),
|
|
|
|
ERR_GET_FUNC(sslerr), STRORDASH(ERR_func_error_string(sslerr)));
|
|
|
|
while ((sslerr = bufferevent_get_openssl_error(bev))) {
|
|
|
|
log_dbg_printf("Additional SSL error: %lu:%i:%s:%i:%s:%i:%s\n",
|
|
|
|
sslerr,
|
|
|
|
ERR_GET_REASON(sslerr), STRORDASH(ERR_reason_error_string(sslerr)),
|
|
|
|
ERR_GET_LIB(sslerr), STRORDASH(ERR_lib_error_string(sslerr)),
|
|
|
|
ERR_GET_FUNC(sslerr), STRORDASH(ERR_func_error_string(sslerr)));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* real errors */
|
|
|
|
log_err_printf("Error from bufferevent: %i:%s %lu:%i:%s:%i:%s:%i:%s\n",
|
|
|
|
errno, errno ? strerror(errno) : "-",
|
|
|
|
sslerr,
|
|
|
|
ERR_GET_REASON(sslerr), STRORDASH(ERR_reason_error_string(sslerr)),
|
|
|
|
ERR_GET_LIB(sslerr), STRORDASH(ERR_lib_error_string(sslerr)),
|
|
|
|
ERR_GET_FUNC(sslerr), STRORDASH(ERR_func_error_string(sslerr)));
|
|
|
|
while ((sslerr = bufferevent_get_openssl_error(bev))) {
|
|
|
|
log_err_printf("Additional SSL error: %lu:%i:%s:%i:%s:%i:%s\n",
|
|
|
|
sslerr,
|
|
|
|
ERR_GET_REASON(sslerr), STRORDASH(ERR_reason_error_string(sslerr)),
|
|
|
|
ERR_GET_LIB(sslerr), STRORDASH(ERR_lib_error_string(sslerr)),
|
|
|
|
ERR_GET_FUNC(sslerr), STRORDASH(ERR_func_error_string(sslerr)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ctx->spec->opts->filter && !ctx->pass) {
|
|
|
|
log_err_level_printf(LOG_WARNING, "Closing on ssl error without filter match: %s:%s, %s:%s, "
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
"%s, %s, "
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
"%s, %s\n",
|
|
|
|
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
STRORDASH(ctx->user), STRORDASH(ctx->desc),
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
STRORDASH(ctx->sslctx->sni), STRORDASH(ctx->sslctx->ssl_names));
|
Add support for passsite substring match
Now the site field in PassSite option can have an '*' suffix to search
for a match anywhere in sni or common names. Note that this is not a
regex or wildcard search.
Previously, we only supported exact matches in sni and between slashes
in common names. This change makes it possible to cover multiple sites
in one PassSite option. In fact, without this change, certain sites
could not be added as passsite, because it was impossible to know their
subdomain names beforehand, for example *.fbcdn.net, which may have many
subdomain names in place of asterisk.
So to use substring match, append an '*' to a site name in PassSite
option (the asterisk is removed before substring search). For example,
use ".fbcdn.net*" to match all subdomains of fbcdn.net, notice the
asterisk at the end.
We also add a warning log starting with "Closing on ssl error without
passsite match" to report sites that can be added as passsite, which is
expected to help in writing PassSite rules.
Also, we now set dstaddr_str earlier in conn handling, so we can print
it in debug logs. This also helps in IDLE and EXPIRED conn logs.
3 years ago
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
protossl_log_masterkey(pxy_conn_ctx_t *ctx, pxy_conn_desc_t *this)
|
|
|
|
{
|
|
|
|
// XXX: Remove ssl check? But the caller function is called by non-ssl protos.
|
|
|
|
if (this->ssl) {
|
|
|
|
/* log master key */
|
|
|
|
if (ctx->log_master && ctx->global->masterkeylog) {
|
|
|
|
char *keystr;
|
|
|
|
keystr = ssl_ssl_masterkey_to_str(this->ssl);
|
|
|
|
if ((keystr == NULL) ||
|
|
|
|
(log_masterkey_print_free(keystr) == -1)) {
|
|
|
|
if (errno == ENOMEM)
|
|
|
|
ctx->enomem = 1;
|
|
|
|
pxy_conn_term(ctx, 1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* forward declaration of OpenSSL callbacks */
|
|
|
|
#ifndef OPENSSL_NO_TLSEXT
|
|
|
|
static int protossl_ossl_servername_cb(SSL *ssl, int *al, void *arg);
|
|
|
|
#endif /* !OPENSSL_NO_TLSEXT */
|
|
|
|
static int protossl_ossl_sessnew_cb(SSL *, SSL_SESSION *);
|
|
|
|
static void protossl_ossl_sessremove_cb(SSL_CTX *, SSL_SESSION *);
|
|
|
|
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20800000L)
|
|
|
|
static SSL_SESSION * protossl_ossl_sessget_cb(SSL *, unsigned char *, int, int *);
|
|
|
|
#else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
static SSL_SESSION * protossl_ossl_sessget_cb(SSL *, const unsigned char *, int, int *);
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Dump information on a certificate to the debug log.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
protossl_debug_crt(X509 *crt)
|
|
|
|
{
|
|
|
|
char *sj = ssl_x509_subject(crt);
|
|
|
|
if (sj) {
|
|
|
|
log_dbg_printf("Subject DN: %s\n", sj);
|
|
|
|
free(sj);
|
|
|
|
}
|
|
|
|
|
|
|
|
char *names = ssl_x509_names_to_str(crt);
|
|
|
|
if (names) {
|
|
|
|
log_dbg_printf("Common Names: %s\n", names);
|
|
|
|
free(names);
|
|
|
|
}
|
|
|
|
|
|
|
|
char *fpr;
|
|
|
|
if (!(fpr = ssl_x509_fingerprint(crt, 1))) {
|
|
|
|
log_err_level_printf(LOG_WARNING, "Error generating X509 fingerprint\n");
|
|
|
|
} else {
|
|
|
|
log_dbg_printf("Fingerprint: %s\n", fpr);
|
|
|
|
free(fpr);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_CERTIFICATE
|
|
|
|
/* dump certificate */
|
|
|
|
log_dbg_print_free(ssl_x509_to_str(crt));
|
|
|
|
log_dbg_print_free(ssl_x509_to_pem(crt));
|
|
|
|
#endif /* DEBUG_CERTIFICATE */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called by OpenSSL when a new src SSL session is created.
|
|
|
|
* OpenSSL increments the refcount before calling the callback and will
|
|
|
|
* decrement it again if we return 0. Returning 1 will make OpenSSL skip
|
|
|
|
* the refcount decrementing. In other words, return 0 if we did not
|
|
|
|
* keep a pointer to the object (which we never do here).
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_SSLV2
|
|
|
|
#define MAYBE_UNUSED
|
|
|
|
#else /* !HAVE_SSLV2 */
|
|
|
|
#define MAYBE_UNUSED UNUSED
|
|
|
|
#endif /* !HAVE_SSLV2 */
|
|
|
|
static int
|
|
|
|
protossl_ossl_sessnew_cb(MAYBE_UNUSED SSL *ssl, SSL_SESSION *sess)
|
|
|
|
#undef MAYBE_UNUSED
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_SESSION_CACHE
|
|
|
|
log_dbg_printf("===> OpenSSL new session callback:\n");
|
|
|
|
if (sess) {
|
|
|
|
log_dbg_print_free(ssl_session_to_str(sess));
|
|
|
|
} else {
|
|
|
|
log_dbg_printf("(null)\n");
|
|
|
|
}
|
|
|
|
#endif /* DEBUG_SESSION_CACHE */
|
|
|
|
#ifdef HAVE_SSLV2
|
|
|
|
/* Session resumption seems to fail for SSLv2 with protocol
|
|
|
|
* parsing errors, so we disable caching for SSLv2. */
|
|
|
|
if (SSL_version(ssl) == SSL2_VERSION) {
|
|
|
|
log_err_level_printf(LOG_WARNING, "Session resumption denied to SSLv2"
|
|
|
|
"client.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* HAVE_SSLV2 */
|
|
|
|
if (sess) {
|
|
|
|
cachemgr_ssess_set(sess);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called by OpenSSL when a src SSL session should be removed.
|
|
|
|
* OpenSSL calls SSL_SESSION_free() after calling the callback;
|
|
|
|
* we do not need to free the reference here.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
protossl_ossl_sessremove_cb(UNUSED SSL_CTX *sslctx, SSL_SESSION *sess)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_SESSION_CACHE
|
|
|
|
log_dbg_printf("===> OpenSSL remove session callback:\n");
|
|
|
|
if (sess) {
|
|
|
|
log_dbg_print_free(ssl_session_to_str(sess));
|
|
|
|
} else {
|
|
|
|
log_dbg_printf("(null)\n");
|
|
|
|
}
|
|
|
|
#endif /* DEBUG_SESSION_CACHE */
|
|
|
|
if (sess) {
|
|
|
|
cachemgr_ssess_del(sess);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called by OpenSSL when a src SSL session is requested by the client.
|
|
|
|
*/
|
|
|
|
static SSL_SESSION *
|
|
|
|
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20800000L)
|
|
|
|
protossl_ossl_sessget_cb(UNUSED SSL *ssl, unsigned char *id, int idlen, int *copy)
|
|
|
|
#else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
protossl_ossl_sessget_cb(UNUSED SSL *ssl, const unsigned char *id, int idlen, int *copy)
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
{
|
|
|
|
SSL_SESSION *sess;
|
|
|
|
|
|
|
|
#ifdef DEBUG_SESSION_CACHE
|
|
|
|
log_dbg_printf("===> OpenSSL get session callback:\n");
|
|
|
|
#endif /* DEBUG_SESSION_CACHE */
|
|
|
|
|
|
|
|
*copy = 0; /* SSL should not increment reference count of session */
|
|
|
|
sess = cachemgr_ssess_get(id, idlen);
|
|
|
|
|
|
|
|
#ifdef DEBUG_SESSION_CACHE
|
|
|
|
if (sess) {
|
|
|
|
log_dbg_print_free(ssl_session_to_str(sess));
|
|
|
|
}
|
|
|
|
#endif /* DEBUG_SESSION_CACHE */
|
|
|
|
|
|
|
|
log_dbg_printf("SSL session cache: %s\n", sess ? "HIT" : "MISS");
|
|
|
|
return sess;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set SSL_CTX options that are the same for incoming and outgoing SSL_CTX.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
protossl_sslctx_setoptions(SSL_CTX *sslctx, pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
SSL_CTX_set_options(sslctx, SSL_OP_ALL);
|
|
|
|
#ifdef SSL_OP_TLS_ROLLBACK_BUG
|
|
|
|
SSL_CTX_set_options(sslctx, SSL_OP_TLS_ROLLBACK_BUG);
|
|
|
|
#endif /* SSL_OP_TLS_ROLLBACK_BUG */
|
|
|
|
#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
|
|
|
|
SSL_CTX_set_options(sslctx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
|
|
|
|
#endif /* SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION */
|
|
|
|
#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
|
|
|
|
SSL_CTX_set_options(sslctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
|
|
|
|
#endif /* SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */
|
|
|
|
#ifdef SSL_OP_NO_TICKET
|
|
|
|
SSL_CTX_set_options(sslctx, SSL_OP_NO_TICKET);
|
|
|
|
#endif /* SSL_OP_NO_TICKET */
|
|
|
|
|
|
|
|
#ifdef SSL_OP_NO_SSLv2
|
|
|
|
#ifdef HAVE_SSLV2
|
|
|
|
if (ctx->conn_opts->no_ssl2) {
|
|
|
|
#endif /* HAVE_SSLV2 */
|
|
|
|
SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv2);
|
|
|
|
#ifdef HAVE_SSLV2
|
|
|
|
}
|
|
|
|
#endif /* HAVE_SSLV2 */
|
|
|
|
#endif /* !SSL_OP_NO_SSLv2 */
|
|
|
|
#ifdef HAVE_SSLV3
|
|
|
|
if (ctx->conn_opts->no_ssl3) {
|
|
|
|
SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv3);
|
|
|
|
}
|
|
|
|
#endif /* HAVE_SSLV3 */
|
|
|
|
#ifdef HAVE_TLSV10
|
|
|
|
if (ctx->conn_opts->no_tls10) {
|
|
|
|
SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1);
|
|
|
|
}
|
|
|
|
#endif /* HAVE_TLSV10 */
|
|
|
|
#ifdef HAVE_TLSV11
|
|
|
|
if (ctx->conn_opts->no_tls11) {
|
|
|
|
SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1_1);
|
|
|
|
}
|
|
|
|
#endif /* HAVE_TLSV11 */
|
|
|
|
#ifdef HAVE_TLSV12
|
|
|
|
if (ctx->conn_opts->no_tls12) {
|
|
|
|
SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1_2);
|
|
|
|
}
|
|
|
|
#endif /* HAVE_TLSV12 */
|
|
|
|
#ifdef HAVE_TLSV13
|
|
|
|
if (ctx->conn_opts->no_tls13) {
|
|
|
|
SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1_3);
|
|
|
|
}
|
|
|
|
#endif /* HAVE_TLSV13 */
|
|
|
|
|
|
|
|
#ifdef SSL_OP_NO_COMPRESSION
|
|
|
|
if (!ctx->conn_opts->sslcomp) {
|
|
|
|
SSL_CTX_set_options(sslctx, SSL_OP_NO_COMPRESSION);
|
|
|
|
}
|
|
|
|
#endif /* SSL_OP_NO_COMPRESSION */
|
|
|
|
|
|
|
|
SSL_CTX_set_cipher_list(sslctx, ctx->conn_opts->ciphers);
|
|
|
|
#ifdef HAVE_TLSV13
|
|
|
|
SSL_CTX_set_ciphersuites(sslctx, ctx->conn_opts->ciphersuites);
|
|
|
|
#endif /* HAVE_TLSV13 */
|
|
|
|
|
|
|
|
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)
|
|
|
|
/* If the security level of OpenSSL is set to 2+ in system configuration,
|
|
|
|
* our forged certificates with 1024-bit RSA key size will be rejected */
|
|
|
|
SSL_CTX_set_security_level(sslctx, 1);
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create and set up a new SSL_CTX instance for terminating SSL.
|
|
|
|
* Set up all the necessary callbacks, the certificate, the cert chain and key.
|
|
|
|
*/
|
|
|
|
static SSL_CTX *
|
|
|
|
protossl_srcsslctx_create(pxy_conn_ctx_t *ctx, X509 *crt, STACK_OF(X509) *chain,
|
|
|
|
EVP_PKEY *key)
|
|
|
|
{
|
|
|
|
SSL_CTX *sslctx = SSL_CTX_new(ctx->conn_opts->sslmethod());
|
|
|
|
if (!sslctx) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
protossl_sslctx_setoptions(sslctx, ctx);
|
|
|
|
|
|
|
|
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x20702000L)
|
|
|
|
if (ctx->conn_opts->minsslversion) {
|
|
|
|
if (SSL_CTX_set_min_proto_version(sslctx, ctx->conn_opts->minsslversion) == 0) {
|
|
|
|
SSL_CTX_free(sslctx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ctx->conn_opts->maxsslversion) {
|
|
|
|
if (SSL_CTX_set_max_proto_version(sslctx, ctx->conn_opts->maxsslversion) == 0) {
|
|
|
|
SSL_CTX_free(sslctx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// ForceSSLproto has precedence
|
|
|
|
if (ctx->conn_opts->sslversion) {
|
|
|
|
if (SSL_CTX_set_min_proto_version(sslctx, ctx->conn_opts->sslversion) == 0 ||
|
|
|
|
SSL_CTX_set_max_proto_version(sslctx, ctx->conn_opts->sslversion) == 0) {
|
|
|
|
SSL_CTX_free(sslctx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
|
|
|
|
SSL_CTX_sess_set_new_cb(sslctx, protossl_ossl_sessnew_cb);
|
|
|
|
SSL_CTX_sess_set_remove_cb(sslctx, protossl_ossl_sessremove_cb);
|
|
|
|
SSL_CTX_sess_set_get_cb(sslctx, protossl_ossl_sessget_cb);
|
|
|
|
SSL_CTX_set_session_cache_mode(sslctx, SSL_SESS_CACHE_SERVER |
|
|
|
|
SSL_SESS_CACHE_NO_INTERNAL);
|
|
|
|
#ifdef USE_SSL_SESSION_ID_CONTEXT
|
|
|
|
SSL_CTX_set_session_id_context(sslctx, (void *)(&ssl_session_context),
|
|
|
|
sizeof(ssl_session_context));
|
|
|
|
#endif /* USE_SSL_SESSION_ID_CONTEXT */
|
|
|
|
#ifndef OPENSSL_NO_TLSEXT
|
|
|
|
SSL_CTX_set_tlsext_servername_callback(sslctx, protossl_ossl_servername_cb);
|
|
|
|
SSL_CTX_set_tlsext_servername_arg(sslctx, ctx);
|
|
|
|
#endif /* !OPENSSL_NO_TLSEXT */
|
|
|
|
#ifndef OPENSSL_NO_DH
|
|
|
|
if (ctx->conn_opts->dh) {
|
|
|
|
SSL_CTX_set_tmp_dh(sslctx, ctx->conn_opts->dh);
|
|
|
|
} else {
|
|
|
|
SSL_CTX_set_tmp_dh_callback(sslctx, ssl_tmp_dh_callback);
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_DH */
|
|
|
|
#ifndef OPENSSL_NO_ECDH
|
|
|
|
if (ctx->conn_opts->ecdhcurve) {
|
|
|
|
EC_KEY *ecdh = ssl_ec_by_name(ctx->conn_opts->ecdhcurve);
|
|
|
|
SSL_CTX_set_tmp_ecdh(sslctx, ecdh);
|
|
|
|
EC_KEY_free(ecdh);
|
|
|
|
} else {
|
|
|
|
EC_KEY *ecdh = ssl_ec_by_name(NULL);
|
|
|
|
SSL_CTX_set_tmp_ecdh(sslctx, ecdh);
|
|
|
|
EC_KEY_free(ecdh);
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_ECDH */
|
|
|
|
if (SSL_CTX_use_certificate(sslctx, crt) != 1) {
|
|
|
|
log_dbg_printf("loading src server certificate failed\n");
|
|
|
|
SSL_CTX_free(sslctx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (SSL_CTX_use_PrivateKey(sslctx, key) != 1) {
|
|
|
|
log_dbg_printf("loading src server key failed\n");
|
|
|
|
SSL_CTX_free(sslctx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < sk_X509_num(chain); i++) {
|
|
|
|
X509 *c = sk_X509_value(chain, i);
|
|
|
|
ssl_x509_refcount_inc(c); /* next call consumes a reference */
|
|
|
|
SSL_CTX_add_extra_chain_cert(sslctx, c);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_SESSION_CACHE
|
|
|
|
if (OPTS_DEBUG(ctx->global)) {
|
|
|
|
int mode = SSL_CTX_get_session_cache_mode(sslctx);
|
|
|
|
log_dbg_printf("SSL session cache mode: %08x\n", mode);
|
|
|
|
if (mode == SSL_SESS_CACHE_OFF)
|
|
|
|
log_dbg_printf("SSL_SESS_CACHE_OFF\n");
|
|
|
|
if (mode & SSL_SESS_CACHE_CLIENT)
|
|
|
|
log_dbg_printf("SSL_SESS_CACHE_CLIENT\n");
|
|
|
|
if (mode & SSL_SESS_CACHE_SERVER)
|
|
|
|
log_dbg_printf("SSL_SESS_CACHE_SERVER\n");
|
|
|
|
if (mode & SSL_SESS_CACHE_NO_AUTO_CLEAR)
|
|
|
|
log_dbg_printf("SSL_SESS_CACHE_NO_AUTO_CLEAR\n");
|
|
|
|
if (mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)
|
|
|
|
log_dbg_printf("SSL_SESS_CACHE_NO_INTERNAL_LOOKUP\n");
|
|
|
|
if (mode & SSL_SESS_CACHE_NO_INTERNAL_STORE)
|
|
|
|
log_dbg_printf("SSL_SESS_CACHE_NO_INTERNAL_STORE\n");
|
|
|
|
}
|
|
|
|
#endif /* DEBUG_SESSION_CACHE */
|
|
|
|
|
|
|
|
return sslctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
protossl_srccert_write_to_gendir(pxy_conn_ctx_t *ctx, X509 *crt, int is_orig)
|
|
|
|
{
|
|
|
|
char *fn;
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
if (!ctx->sslctx->origcrtfpr)
|
|
|
|
return -1;
|
|
|
|
if (is_orig) {
|
|
|
|
rv = asprintf(&fn, "%s/%s.crt", ctx->global->certgendir,
|
|
|
|
ctx->sslctx->origcrtfpr);
|
|
|
|
} else {
|
|
|
|
if (!ctx->sslctx->usedcrtfpr)
|
|
|
|
return -1;
|
|
|
|
rv = asprintf(&fn, "%s/%s-%s.crt", ctx->global->certgendir,
|
|
|
|
ctx->sslctx->origcrtfpr, ctx->sslctx->usedcrtfpr);
|
|
|
|
}
|
|
|
|
if (rv == -1) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
rv = log_cert_submit(fn, crt);
|
|
|
|
free(fn);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
protossl_srccert_write(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
if (ctx->global->certgen_writeall || ctx->sslctx->generated_cert) {
|
|
|
|
if (protossl_srccert_write_to_gendir(ctx,
|
|
|
|
SSL_get_certificate(ctx->src.ssl), 0) == -1) {
|
|
|
|
log_err_level_printf(LOG_CRIT, "Failed to write used certificate\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ctx->global->certgen_writeall) {
|
|
|
|
if (protossl_srccert_write_to_gendir(ctx, ctx->sslctx->origcrt, 1) == -1) {
|
|
|
|
log_err_level_printf(LOG_CRIT, "Failed to write orig certificate\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static cert_t *
|
|
|
|
protossl_srccert_create(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
cert_t *cert = NULL;
|
|
|
|
|
|
|
|
if (ctx->global->leafcertdir) {
|
|
|
|
if (ctx->sslctx->sni) {
|
|
|
|
cert = cachemgr_tgcrt_get(ctx->sslctx->sni);
|
|
|
|
if (!cert) {
|
|
|
|
char *wildcarded;
|
|
|
|
wildcarded = ssl_wildcardify(ctx->sslctx->sni);
|
|
|
|
if (!wildcarded) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
cert = cachemgr_tgcrt_get(wildcarded);
|
|
|
|
free(wildcarded);
|
|
|
|
}
|
|
|
|
if (cert && OPTS_DEBUG(ctx->global)) {
|
|
|
|
log_dbg_printf("Target cert by SNI\n");
|
|
|
|
}
|
|
|
|
} else if (ctx->sslctx->origcrt) {
|
|
|
|
char **names = ssl_x509_names(ctx->sslctx->origcrt);
|
|
|
|
for (char **p = names; *p; p++) {
|
|
|
|
if (!cert) {
|
|
|
|
cert = cachemgr_tgcrt_get(*p);
|
|
|
|
}
|
|
|
|
if (!cert) {
|
|
|
|
char *wildcarded;
|
|
|
|
wildcarded = ssl_wildcardify(*p);
|
|
|
|
if (!wildcarded) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
} else {
|
|
|
|
/* increases ref count */
|
|
|
|
cert = cachemgr_tgcrt_get(
|
|
|
|
wildcarded);
|
|
|
|
free(wildcarded);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(*p);
|
|
|
|
}
|
|
|
|
free(names);
|
|
|
|
if (ctx->enomem) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (cert && OPTS_DEBUG(ctx->global)) {
|
|
|
|
log_dbg_printf("Target cert by origcrt\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cert) {
|
|
|
|
ctx->sslctx->immutable_cert = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cert && ctx->global->defaultleafcert) {
|
|
|
|
cert = ctx->global->defaultleafcert;
|
|
|
|
cert_refcount_inc(cert);
|
|
|
|
ctx->sslctx->immutable_cert = 1;
|
|
|
|
if (OPTS_DEBUG(ctx->global)) {
|
|
|
|
log_dbg_printf("Using default leaf certificate\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cert && ctx->sslctx->origcrt && ctx->global->leafkey) {
|
|
|
|
cert = cert_new();
|
|
|
|
|
|
|
|
cert->crt = cachemgr_fkcrt_get(ctx->sslctx->origcrt);
|
|
|
|
if (cert->crt) {
|
|
|
|
if (OPTS_DEBUG(ctx->global))
|
|
|
|
log_dbg_printf("Certificate cache: HIT\n");
|
|
|
|
} else {
|
|
|
|
if (OPTS_DEBUG(ctx->global))
|
|
|
|
log_dbg_printf("Certificate cache: MISS\n");
|
|
|
|
cert->crt = ssl_x509_forge(ctx->conn_opts->cacrt,
|
|
|
|
ctx->conn_opts->cakey,
|
|
|
|
ctx->sslctx->origcrt,
|
|
|
|
ctx->global->leafkey,
|
|
|
|
NULL,
|
|
|
|
ctx->conn_opts->leafcrlurl);
|
|
|
|
cachemgr_fkcrt_set(ctx->sslctx->origcrt, cert->crt);
|
|
|
|
}
|
|
|
|
cert_set_key(cert, ctx->global->leafkey);
|
|
|
|
cert_set_chain(cert, ctx->conn_opts->chain);
|
|
|
|
ctx->sslctx->generated_cert = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((WANT_CONNECT_LOG(ctx) || ctx->global->certgendir) && ctx->sslctx->origcrt) {
|
|
|
|
ctx->sslctx->origcrtfpr = ssl_x509_fingerprint(ctx->sslctx->origcrt, 0);
|
|
|
|
if (!ctx->sslctx->origcrtfpr)
|
|
|
|
ctx->enomem = 1;
|
|
|
|
}
|
|
|
|
if ((WANT_CONNECT_LOG(ctx) || ctx->global->certgen_writeall) &&
|
|
|
|
cert && cert->crt) {
|
|
|
|
ctx->sslctx->usedcrtfpr = ssl_x509_fingerprint(cert->crt, 0);
|
|
|
|
if (!ctx->sslctx->usedcrtfpr)
|
|
|
|
ctx->enomem = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cert;
|
|
|
|
}
|
|
|
|
|
|
|
|
static filter_action_t * NONNULL(1,2)
|
|
|
|
protossl_filter_match_sni(pxy_conn_ctx_t *ctx, filter_list_t *list)
|
|
|
|
{
|
|
|
|
filter_site_t *site = filter_site_find(list->sni_btree, list->sni_acm, list->sni_all, ctx->sslctx->sni);
|
|
|
|
if (!site)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
log_fine_va("Found site (line=%d): %s for %s:%s, %s:%s, %s, %s, %s", site->action.line_num, site->site,
|
|
|
|
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
|
|
|
|
STRORDASH(ctx->user), STRORDASH(ctx->desc), STRORDASH(ctx->sslctx->sni));
|
|
|
|
#else /* WITHOUT_USERAUTH */
|
|
|
|
log_fine_va("Found site (line=%d): %s for %s:%s, %s:%s, %s", site->action.line_num, site->site,
|
|
|
|
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
|
|
|
|
STRORDASH(ctx->sslctx->sni));
|
|
|
|
#endif /* WITHOUT_USERAUTH */
|
|
|
|
|
|
|
|
if (!site->port_btree && !site->port_acm && (site->action.precedence < ctx->filter_precedence)) {
|
|
|
|
log_finest_va("Rule precedence lower than conn filter precedence %d < %d (line=%d): %s, %s", site->action.precedence, ctx->filter_precedence, site->action.line_num, site->site, ctx->sslctx->sni);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_PROXY
|
|
|
|
if (site->all_sites)
|
|
|
|
log_finest_va("Match all sni (line=%d): %s, %s", site->action.line_num, site->site, ctx->sslctx->sni);
|
|
|
|
else if (site->exact)
|
|
|
|
log_finest_va("Match exact with sni (line=%d): %s, %s", site->action.line_num, site->site, ctx->sslctx->sni);
|
|
|
|
else
|
|
|
|
log_finest_va("Match substring in sni (line=%d): %s, %s", site->action.line_num, site->site, ctx->sslctx->sni);
|
|
|
|
#endif /* DEBUG_PROXY */
|
|
|
|
|
|
|
|
filter_action_t *port_action = pxy_conn_filter_port(ctx, site);
|
|
|
|
if (port_action)
|
|
|
|
return port_action;
|
|
|
|
|
|
|
|
return &site->action;
|
|
|
|
}
|
|
|
|
|
|
|
|
static filter_action_t * NONNULL(1,2)
|
|
|
|
protossl_filter_match_cn(pxy_conn_ctx_t *ctx, filter_list_t *list)
|
|
|
|
{
|
|
|
|
filter_site_t *site = NULL;
|
|
|
|
|
|
|
|
// ballpark figures
|
|
|
|
#define MAX_CN_LEN 4096
|
|
|
|
#define MAX_CN_TOKENS 100
|
|
|
|
|
|
|
|
int argc = 0;
|
|
|
|
char *p, *last = NULL;
|
Add support for passsite substring match
Now the site field in PassSite option can have an '*' suffix to search
for a match anywhere in sni or common names. Note that this is not a
regex or wildcard search.
Previously, we only supported exact matches in sni and between slashes
in common names. This change makes it possible to cover multiple sites
in one PassSite option. In fact, without this change, certain sites
could not be added as passsite, because it was impossible to know their
subdomain names beforehand, for example *.fbcdn.net, which may have many
subdomain names in place of asterisk.
So to use substring match, append an '*' to a site name in PassSite
option (the asterisk is removed before substring search). For example,
use ".fbcdn.net*" to match all subdomains of fbcdn.net, notice the
asterisk at the end.
We also add a warning log starting with "Closing on ssl error without
passsite match" to report sites that can be added as passsite, which is
expected to help in writing PassSite rules.
Also, we now set dstaddr_str earlier in conn handling, so we can print
it in debug logs. This also helps in IDLE and EXPIRED conn logs.
3 years ago
|
|
|
|
|
|
|
size_t len = strlen(ctx->sslctx->ssl_names);
|
|
|
|
|
|
|
|
if (len > MAX_CN_LEN) {
|
|
|
|
log_err_level_printf(LOG_WARNING, "Skip too long common names, max len %d: %s\n", MAX_CN_LEN, ctx->sslctx->ssl_names);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do not tokenize ssl_names if there is no rule to match exact common names
|
|
|
|
if (list->cn_btree) {
|
|
|
|
// strtok_r() modifies the string param, so copy ssl_names to a local var and pass it to strtok_r()
|
|
|
|
char _cn[len + 1];
|
|
|
|
memcpy(_cn, ctx->sslctx->ssl_names, len);
|
|
|
|
_cn[len] = '\0';
|
|
|
|
|
|
|
|
for ((p = strtok_r(_cn, "/", &last));
|
|
|
|
p;
|
|
|
|
(p = strtok_r(NULL, "/", &last))) {
|
|
|
|
if (argc++ < MAX_CN_TOKENS) {
|
|
|
|
site = filter_site_exact_match(list->cn_btree, p);
|
|
|
|
if (site) {
|
|
|
|
log_finest_va("Match exact with common name (%d) (line=%d): %s, %s", argc, site->action.line_num, p, ctx->sslctx->ssl_names);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
log_err_level_printf(LOG_WARNING, "Too many tokens in common names, max tokens %d: %s\n", MAX_CN_TOKENS, ctx->sslctx->ssl_names);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!site) {
|
|
|
|
site = filter_site_substring_match(list->cn_acm, ctx->sslctx->ssl_names);
|
|
|
|
if (site)
|
|
|
|
log_finest_va("Match substring in common names (line=%d): %s, %s", site->action.line_num, site->site, ctx->sslctx->ssl_names);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!site)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
log_fine_va("Found site (line=%d): %s for %s:%s, %s:%s, %s, %s, %s", site->action.line_num, site->site,
|
|
|
|
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
|
|
|
|
STRORDASH(ctx->user), STRORDASH(ctx->desc), STRORDASH(ctx->sslctx->ssl_names));
|
|
|
|
#else /* WITHOUT_USERAUTH */
|
|
|
|
log_fine_va("Found site (line=%d): %s for %s:%s, %s:%s, %s", site->action.line_num, site->site,
|
|
|
|
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
|
|
|
|
STRORDASH(ctx->sslctx->ssl_names));
|
|
|
|
#endif /* WITHOUT_USERAUTH */
|
|
|
|
|
|
|
|
if (!site->port_btree && !site->port_acm && (site->action.precedence < ctx->filter_precedence)) {
|
|
|
|
log_finest_va("Rule precedence lower than conn filter precedence %d < %d (line=%d): %s, %s", site->action.precedence, ctx->filter_precedence, site->action.line_num, site->site, ctx->sslctx->ssl_names);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (site->all_sites)
|
|
|
|
log_finest_va("Match all common names (line=%d): %s, %s", site->action.line_num, site->site, ctx->sslctx->ssl_names);
|
|
|
|
|
|
|
|
filter_action_t *port_action = pxy_conn_filter_port(ctx, site);
|
|
|
|
if (port_action)
|
|
|
|
return port_action;
|
|
|
|
|
|
|
|
return &site->action;
|
|
|
|
}
|
|
|
|
|
|
|
|
static filter_action_t * NONNULL(1,2)
|
|
|
|
protossl_filter(pxy_conn_ctx_t *ctx, filter_list_t *list)
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
{
|
|
|
|
filter_action_t *action_sni = NULL;
|
|
|
|
filter_action_t *action_cn = NULL;
|
|
|
|
|
|
|
|
if (ctx->sslctx->sni) {
|
|
|
|
if (!(action_sni = protossl_filter_match_sni(ctx, list))) {
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
log_finest_va("No filter match with sni: %s:%s, %s:%s, %s, %s, %s, %s",
|
|
|
|
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
|
|
|
|
STRORDASH(ctx->user), STRORDASH(ctx->desc), STRORDASH(ctx->sslctx->sni), STRORDASH(ctx->sslctx->ssl_names));
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
#else /* WITHOUT_USERAUTH */
|
|
|
|
log_finest_va("No filter match with sni: %s:%s, %s:%s, %s, %s",
|
|
|
|
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
|
|
|
|
STRORDASH(ctx->sslctx->sni), STRORDASH(ctx->sslctx->ssl_names));
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
}
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx->sslctx->ssl_names) {
|
|
|
|
if (!(action_cn = protossl_filter_match_cn(ctx, list))) {
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
log_finest_va("No filter match with common names: %s:%s, %s:%s, %s, %s, %s, %s",
|
|
|
|
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
|
|
|
|
STRORDASH(ctx->user), STRORDASH(ctx->desc), STRORDASH(ctx->sslctx->sni), STRORDASH(ctx->sslctx->ssl_names));
|
|
|
|
#else /* WITHOUT_USERAUTH */
|
|
|
|
log_finest_va("No filter match with common names: %s:%s, %s:%s, %s, %s",
|
|
|
|
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
|
|
|
|
STRORDASH(ctx->sslctx->sni), STRORDASH(ctx->sslctx->ssl_names));
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
}
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
if (action_sni || action_cn)
|
|
|
|
return pxy_conn_set_filter_action(action_sni, action_cn
|
|
|
|
#ifdef DEBUG_PROXY
|
|
|
|
, ctx, ctx->sslctx->sni, ctx->sslctx->ssl_names
|
|
|
|
#endif /* DEBUG_PROXY */
|
|
|
|
);
|
|
|
|
|
|
|
|
return NULL;
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
protossl_reconnect_srvdst(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
log_fine("ENTER");
|
|
|
|
|
|
|
|
// Reconnect only once
|
|
|
|
ctx->sslctx->reconnected = 1;
|
|
|
|
|
|
|
|
ctx->srvdst.free(ctx->srvdst.bev, ctx);
|
|
|
|
ctx->srvdst.bev = NULL;
|
|
|
|
ctx->srvdst.ssl = NULL;
|
|
|
|
ctx->connected = 0;
|
|
|
|
|
|
|
|
if (protossl_conn_connect(ctx) == -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bufferevent_socket_connect(ctx->srvdst.bev, (struct sockaddr *)&ctx->dstaddr, ctx->dstaddrlen) == -1) {
|
|
|
|
log_err_level(LOG_CRIT, "bufferevent_socket_connect for srvdst failed");
|
|
|
|
pxy_conn_term(ctx, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
static int
|
|
|
|
protossl_apply_filter(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
int rv = 0;
|
|
|
|
filter_action_t *a;
|
|
|
|
if ((a = pxy_conn_filter(ctx, protossl_filter))) {
|
|
|
|
unsigned int action = pxy_conn_translate_filter_action(ctx, a);
|
|
|
|
|
|
|
|
ctx->filter_precedence = action & FILTER_PRECEDENCE;
|
|
|
|
|
|
|
|
if (action & FILTER_ACTION_DIVERT) {
|
|
|
|
ctx->deferred_action = FILTER_ACTION_NONE;
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
ctx->divert = 1;
|
|
|
|
}
|
|
|
|
else if (action & FILTER_ACTION_SPLIT) {
|
|
|
|
ctx->deferred_action = FILTER_ACTION_NONE;
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
ctx->divert = 0;
|
|
|
|
}
|
|
|
|
else if (action & FILTER_ACTION_PASS) {
|
|
|
|
ctx->deferred_action = FILTER_ACTION_NONE;
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
ctx->pass = 1;
|
|
|
|
rv = 1;
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
}
|
|
|
|
else if (action & FILTER_ACTION_BLOCK) {
|
|
|
|
// Always defer block action, the only action we can defer from this point on
|
|
|
|
// This block action should override any deferred pass action,
|
|
|
|
// because the current rule must have a higher precedence
|
|
|
|
log_fine("Deferring block action");
|
|
|
|
ctx->deferred_action = FILTER_ACTION_BLOCK;
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
}
|
|
|
|
//else { /* FILTER_ACTION_MATCH */ }
|
|
|
|
|
|
|
|
// Filtering rules at higher precedence can enable/disable logging
|
|
|
|
if (action & FILTER_LOG_CONNECT)
|
|
|
|
ctx->log_connect = 1;
|
|
|
|
else if (action & FILTER_LOG_NOCONNECT)
|
|
|
|
ctx->log_connect = 0;
|
|
|
|
if (action & FILTER_LOG_MASTER)
|
|
|
|
ctx->log_master = 1;
|
|
|
|
else if (action & FILTER_LOG_NOMASTER)
|
|
|
|
ctx->log_master = 0;
|
|
|
|
if (action & FILTER_LOG_CERT)
|
|
|
|
ctx->log_cert = 1;
|
|
|
|
else if (action & FILTER_LOG_NOCERT)
|
|
|
|
ctx->log_cert = 0;
|
|
|
|
if (action & FILTER_LOG_CONTENT)
|
|
|
|
ctx->log_content = 1;
|
|
|
|
else if (action & FILTER_LOG_NOCONTENT)
|
|
|
|
ctx->log_content = 0;
|
|
|
|
if (action & FILTER_LOG_PCAP)
|
|
|
|
ctx->log_pcap = 1;
|
|
|
|
else if (action & FILTER_LOG_NOPCAP)
|
|
|
|
ctx->log_pcap = 0;
|
|
|
|
#ifndef WITHOUT_MIRROR
|
|
|
|
if (action & FILTER_LOG_MIRROR)
|
|
|
|
ctx->log_mirror = 1;
|
|
|
|
else if (action & FILTER_LOG_NOMIRROR)
|
|
|
|
ctx->log_mirror = 0;
|
|
|
|
#endif /* !WITHOUT_MIRROR */
|
|
|
|
|
|
|
|
if (a->conn_opts) {
|
|
|
|
ctx->conn_opts = a->conn_opts;
|
|
|
|
|
|
|
|
if (ctx->conn_opts->reconnect_ssl) {
|
|
|
|
// Reconnect srvdst only once, if ReconnectSSL set in the rule
|
|
|
|
if (!ctx->sslctx->reconnected) {
|
|
|
|
protossl_reconnect_srvdst(ctx);
|
|
|
|
// Return immediately to avoid applying deferred pass action
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
log_finest("Already reconnected once, will not reconnect again");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
// Cannot defer pass action any longer
|
|
|
|
// Match action should not override pass action, hence no 'else if'
|
|
|
|
if (ctx->deferred_action & FILTER_ACTION_PASS) {
|
|
|
|
log_fine("Applying deferred pass action");
|
|
|
|
ctx->deferred_action = FILTER_ACTION_NONE;
|
|
|
|
ctx->pass = 1;
|
|
|
|
rv = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create new SSL context for the incoming connection, based on the original
|
|
|
|
* destination SSL certificate.
|
|
|
|
* Returns NULL if no suitable certificate could be found or the site should
|
|
|
|
* be passed through.
|
|
|
|
*/
|
|
|
|
static SSL *
|
|
|
|
protossl_srcssl_create(pxy_conn_ctx_t *ctx, SSL *origssl)
|
|
|
|
{
|
|
|
|
cert_t *cert;
|
|
|
|
|
|
|
|
cachemgr_dsess_set((struct sockaddr*)&ctx->dstaddr,
|
|
|
|
ctx->dstaddrlen, ctx->sslctx->sni,
|
|
|
|
SSL_get0_session(origssl));
|
|
|
|
|
|
|
|
ctx->sslctx->origcrt = SSL_get_peer_certificate(origssl);
|
|
|
|
|
|
|
|
if (OPTS_DEBUG(ctx->global)) {
|
|
|
|
if (ctx->sslctx->origcrt) {
|
|
|
|
log_dbg_printf("===> Original server certificate:\n");
|
|
|
|
protossl_debug_crt(ctx->sslctx->origcrt);
|
|
|
|
} else {
|
|
|
|
log_dbg_printf("===> Original server has no cert!\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cert = protossl_srccert_create(ctx);
|
|
|
|
if (!cert)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (OPTS_DEBUG(ctx->global)) {
|
|
|
|
log_dbg_printf("===> Forged server certificate:\n");
|
|
|
|
protossl_debug_crt(cert->crt);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (WANT_CONNECT_LOG(ctx) || ctx->spec->opts->filter) {
|
|
|
|
ctx->sslctx->ssl_names = ssl_x509_names_to_str(ctx->sslctx->origcrt ?
|
|
|
|
ctx->sslctx->origcrt :
|
|
|
|
cert->crt);
|
|
|
|
if (!ctx->sslctx->ssl_names)
|
|
|
|
ctx->enomem = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Defers any block action until HTTP filter application
|
|
|
|
// or until the first src readcb of non-http protos
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
if (protossl_apply_filter(ctx)) {
|
|
|
|
cert_free(cert);
|
|
|
|
return NULL;
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
SSL_CTX *sslctx = protossl_srcsslctx_create(ctx, cert->crt, cert->chain,
|
|
|
|
cert->key);
|
|
|
|
cert_free(cert);
|
|
|
|
if (!sslctx)
|
|
|
|
return NULL;
|
|
|
|
SSL *ssl = SSL_new(sslctx);
|
|
|
|
SSL_CTX_free(sslctx); /* SSL_new() increments refcount */
|
|
|
|
if (!ssl) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#ifdef SSL_MODE_RELEASE_BUFFERS
|
|
|
|
/* lower memory footprint for idle connections */
|
|
|
|
SSL_set_mode(ssl, SSL_get_mode(ssl) | SSL_MODE_RELEASE_BUFFERS);
|
|
|
|
#endif /* SSL_MODE_RELEASE_BUFFERS */
|
|
|
|
return ssl;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef OPENSSL_NO_TLSEXT
|
|
|
|
/*
|
|
|
|
* OpenSSL servername callback, called when OpenSSL receives a servername
|
|
|
|
* TLS extension in the clientHello. Must switch to a new SSL_CTX with
|
|
|
|
* a different certificate if we want to replace the server cert here.
|
|
|
|
* We generate a new certificate if the current one does not match the
|
|
|
|
* supplied servername. This should only happen if the original destination
|
|
|
|
* server supplies a certificate which does not match the server name we
|
|
|
|
* indicate to it.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
protossl_ossl_servername_cb(SSL *ssl, UNUSED int *al, void *arg)
|
|
|
|
{
|
|
|
|
pxy_conn_ctx_t *ctx = arg;
|
|
|
|
const char *sn;
|
|
|
|
X509 *sslcrt;
|
|
|
|
|
|
|
|
if (!(sn = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name)))
|
|
|
|
return SSL_TLSEXT_ERR_NOACK;
|
|
|
|
|
|
|
|
if (!ctx->sslctx->sni) {
|
|
|
|
if (OPTS_DEBUG(ctx->global)) {
|
|
|
|
log_dbg_printf("Warning: SNI parser yielded no "
|
|
|
|
"hostname, copying OpenSSL one: "
|
|
|
|
"[NULL] != [%s]\n", sn);
|
|
|
|
}
|
|
|
|
ctx->sslctx->sni = strdup(sn);
|
|
|
|
if (!ctx->sslctx->sni) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
return SSL_TLSEXT_ERR_NOACK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (OPTS_DEBUG(ctx->global)) {
|
|
|
|
if (!!strcmp(sn, ctx->sslctx->sni)) {
|
|
|
|
/*
|
|
|
|
* This may happen if the client resumes a session, but
|
|
|
|
* uses a different SNI hostname when resuming than it
|
|
|
|
* used when the session was created. OpenSSL
|
|
|
|
* correctly ignores the SNI in the ClientHello in this
|
|
|
|
* case, but since we have already sent the SNI onwards
|
|
|
|
* to the original destination, there is no way back.
|
|
|
|
* We log an error and hope this never happens.
|
|
|
|
*/
|
|
|
|
log_dbg_printf("Warning: SNI parser yielded different "
|
|
|
|
"hostname than OpenSSL callback for "
|
|
|
|
"the same ClientHello message: "
|
|
|
|
"[%s] != [%s]\n", ctx->sslctx->sni, sn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* generate a new certificate with sn as additional altSubjectName
|
|
|
|
* and replace it both in the current SSL ctx and in the cert cache */
|
|
|
|
if (ctx->conn_opts->allow_wrong_host && !ctx->sslctx->immutable_cert &&
|
|
|
|
!ssl_x509_names_match((sslcrt = SSL_get_certificate(ssl)), sn)) {
|
|
|
|
X509 *newcrt;
|
|
|
|
SSL_CTX *newsslctx;
|
|
|
|
|
|
|
|
if (OPTS_DEBUG(ctx->global)) {
|
|
|
|
log_dbg_printf("Certificate cache: UPDATE "
|
|
|
|
"(SNI mismatch)\n");
|
|
|
|
}
|
|
|
|
newcrt = ssl_x509_forge(ctx->conn_opts->cacrt, ctx->conn_opts->cakey,
|
|
|
|
sslcrt, ctx->global->leafkey,
|
|
|
|
sn, ctx->conn_opts->leafcrlurl);
|
|
|
|
if (!newcrt) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
return SSL_TLSEXT_ERR_NOACK;
|
|
|
|
}
|
|
|
|
cachemgr_fkcrt_set(ctx->sslctx->origcrt, newcrt);
|
|
|
|
ctx->sslctx->generated_cert = 1;
|
|
|
|
if (OPTS_DEBUG(ctx->global)) {
|
|
|
|
log_dbg_printf("===> Updated forged server "
|
|
|
|
"certificate:\n");
|
|
|
|
protossl_debug_crt(newcrt);
|
|
|
|
}
|
|
|
|
if (WANT_CONNECT_LOG(ctx) || ctx->spec->opts->filter) {
|
|
|
|
if (ctx->sslctx->ssl_names) {
|
|
|
|
free(ctx->sslctx->ssl_names);
|
|
|
|
}
|
|
|
|
ctx->sslctx->ssl_names = ssl_x509_names_to_str(newcrt);
|
|
|
|
if (!ctx->sslctx->ssl_names) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (WANT_CONNECT_LOG(ctx) || ctx->global->certgendir) {
|
|
|
|
if (ctx->sslctx->usedcrtfpr) {
|
|
|
|
free(ctx->sslctx->usedcrtfpr);
|
|
|
|
}
|
|
|
|
ctx->sslctx->usedcrtfpr = ssl_x509_fingerprint(newcrt, 0);
|
|
|
|
if (!ctx->sslctx->usedcrtfpr) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
newsslctx = protossl_srcsslctx_create(ctx, newcrt, ctx->conn_opts->chain,
|
|
|
|
ctx->global->leafkey);
|
|
|
|
if (!newsslctx) {
|
|
|
|
X509_free(newcrt);
|
|
|
|
return SSL_TLSEXT_ERR_NOACK;
|
|
|
|
}
|
|
|
|
SSL_set_SSL_CTX(ssl, newsslctx); /* decr's old incr new refc */
|
|
|
|
SSL_CTX_free(newsslctx);
|
|
|
|
X509_free(newcrt);
|
|
|
|
} else if (OPTS_DEBUG(ctx->global)) {
|
|
|
|
log_dbg_printf("Certificate cache: KEEP (SNI match or "
|
|
|
|
"target mode)\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return SSL_TLSEXT_ERR_OK;
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_TLSEXT */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create new SSL context for outgoing connections to the original destination.
|
|
|
|
* If hostname sni is provided, use it for Server Name Indication.
|
|
|
|
*/
|
|
|
|
SSL *
|
|
|
|
protossl_dstssl_create(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
SSL_CTX *sslctx;
|
|
|
|
SSL *ssl;
|
|
|
|
SSL_SESSION *sess;
|
|
|
|
|
|
|
|
sslctx = SSL_CTX_new(ctx->conn_opts->sslmethod());
|
|
|
|
if (!sslctx) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
protossl_sslctx_setoptions(sslctx, ctx);
|
|
|
|
|
|
|
|
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x20702000L)
|
|
|
|
if (ctx->conn_opts->minsslversion) {
|
|
|
|
if (SSL_CTX_set_min_proto_version(sslctx, ctx->conn_opts->minsslversion) == 0) {
|
|
|
|
SSL_CTX_free(sslctx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ctx->conn_opts->maxsslversion) {
|
|
|
|
if (SSL_CTX_set_max_proto_version(sslctx, ctx->conn_opts->maxsslversion) == 0) {
|
|
|
|
SSL_CTX_free(sslctx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// ForceSSLproto has precedence
|
|
|
|
if (ctx->conn_opts->sslversion) {
|
|
|
|
if (SSL_CTX_set_min_proto_version(sslctx, ctx->conn_opts->sslversion) == 0 ||
|
|
|
|
SSL_CTX_set_max_proto_version(sslctx, ctx->conn_opts->sslversion) == 0) {
|
|
|
|
SSL_CTX_free(sslctx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
|
|
|
|
if (ctx->conn_opts->verify_peer) {
|
|
|
|
SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, NULL);
|
|
|
|
SSL_CTX_set_default_verify_paths(sslctx);
|
|
|
|
} else {
|
|
|
|
SSL_CTX_set_verify(sslctx, SSL_VERIFY_NONE, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx->conn_opts->clientcrt &&
|
|
|
|
(SSL_CTX_use_certificate(sslctx, ctx->conn_opts->clientcrt) != 1)) {
|
|
|
|
log_dbg_printf("loading dst client certificate failed\n");
|
|
|
|
SSL_CTX_free(sslctx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (ctx->conn_opts->clientkey &&
|
|
|
|
(SSL_CTX_use_PrivateKey(sslctx, ctx->conn_opts->clientkey) != 1)) {
|
|
|
|
log_dbg_printf("loading dst client key failed\n");
|
|
|
|
SSL_CTX_free(sslctx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssl = SSL_new(sslctx);
|
|
|
|
SSL_CTX_free(sslctx); /* SSL_new() increments refcount */
|
|
|
|
if (!ssl) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_TLSEXT
|
|
|
|
if (ctx->sslctx->sni) {
|
|
|
|
SSL_set_tlsext_host_name(ssl, ctx->sslctx->sni);
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_TLSEXT */
|
|
|
|
|
|
|
|
#ifdef SSL_MODE_RELEASE_BUFFERS
|
|
|
|
/* lower memory footprint for idle connections */
|
|
|
|
SSL_set_mode(ssl, SSL_get_mode(ssl) | SSL_MODE_RELEASE_BUFFERS);
|
|
|
|
#endif /* SSL_MODE_RELEASE_BUFFERS */
|
|
|
|
|
|
|
|
/* session resuming based on remote endpoint address and port */
|
|
|
|
sess = cachemgr_dsess_get((struct sockaddr *)&ctx->dstaddr,
|
|
|
|
ctx->dstaddrlen, ctx->sslctx->sni); /* new sess inst */
|
|
|
|
if (sess) {
|
|
|
|
if (OPTS_DEBUG(ctx->global)) {
|
|
|
|
log_dbg_printf("Attempt reuse dst SSL session\n");
|
|
|
|
}
|
|
|
|
SSL_set_session(ssl, sess); /* increments sess refcount */
|
|
|
|
SSL_SESSION_free(sess);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ssl;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up a bufferevent structure for either a dst or src connection,
|
|
|
|
* optionally with or without SSL. Sets all callbacks, enables read
|
|
|
|
* and write events, but does not call bufferevent_socket_connect().
|
|
|
|
*
|
|
|
|
* For dst connections, pass -1 as fd. Pass a pointer to an initialized
|
|
|
|
* SSL struct as ssl if the connection should use SSL.
|
|
|
|
*
|
|
|
|
* Returns pointer to initialized bufferevent structure, as returned
|
|
|
|
* by bufferevent_socket_new() or bufferevent_openssl_socket_new().
|
|
|
|
*/
|
|
|
|
static struct bufferevent * NONNULL(1,3)
|
|
|
|
protossl_bufferevent_setup(pxy_conn_ctx_t *ctx, evutil_socket_t fd, SSL *ssl)
|
|
|
|
{
|
|
|
|
log_finest_va("ENTER, fd=%d", fd);
|
|
|
|
|
|
|
|
struct bufferevent *bev = bufferevent_openssl_socket_new(ctx->thr->evbase, fd, ssl,
|
|
|
|
((fd == -1) ? BUFFEREVENT_SSL_CONNECTING : BUFFEREVENT_SSL_ACCEPTING), BEV_OPT_DEFER_CALLBACKS);
|
|
|
|
if (!bev) {
|
|
|
|
log_err_level_printf(LOG_CRIT, "Error creating bufferevent socket\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#if LIBEVENT_VERSION_NUMBER >= 0x02010000
|
|
|
|
log_finest_va("bufferevent_openssl_set_allow_dirty_shutdown, fd=%d", fd);
|
|
|
|
|
|
|
|
/* Prevent unclean (dirty) shutdowns to cause error
|
|
|
|
* events on the SSL socket bufferevent. */
|
|
|
|
bufferevent_openssl_set_allow_dirty_shutdown(bev, 1);
|
|
|
|
#endif /* LIBEVENT_VERSION_NUMBER >= 0x02010000 */
|
|
|
|
|
|
|
|
// @attention Do not set callbacks here, we do not set r cb for tcp/ssl srvdst
|
|
|
|
//bufferevent_setcb(bev, pxy_bev_readcb, pxy_bev_writecb, pxy_bev_eventcb, ctx);
|
|
|
|
// @attention Do not enable r/w events here, we do not set r cb for tcp/ssl srvdst
|
|
|
|
// Also, to avoid r/w cb before connected, we should enable r/w events after the conn is connected
|
|
|
|
//bufferevent_enable(bev, EV_READ|EV_WRITE);
|
|
|
|
return bev;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bufferevent * NONNULL(1,3)
|
|
|
|
protossl_bufferevent_setup_child(pxy_conn_child_ctx_t *ctx, evutil_socket_t fd, SSL *ssl)
|
|
|
|
{
|
|
|
|
log_finest_va("ENTER, fd=%d", fd);
|
|
|
|
|
|
|
|
struct bufferevent *bev = bufferevent_openssl_socket_new(ctx->conn->thr->evbase, fd, ssl,
|
|
|
|
((fd == -1) ? BUFFEREVENT_SSL_CONNECTING : BUFFEREVENT_SSL_ACCEPTING), BEV_OPT_DEFER_CALLBACKS);
|
|
|
|
if (!bev) {
|
|
|
|
log_err_level_printf(LOG_CRIT, "Error creating bufferevent socket\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if LIBEVENT_VERSION_NUMBER >= 0x02010000
|
|
|
|
log_finest_va("bufferevent_openssl_set_allow_dirty_shutdown, fd=%d", fd);
|
|
|
|
|
|
|
|
/* Prevent unclean (dirty) shutdowns to cause error
|
|
|
|
* events on the SSL socket bufferevent. */
|
|
|
|
bufferevent_openssl_set_allow_dirty_shutdown(bev, 1);
|
|
|
|
#endif /* LIBEVENT_VERSION_NUMBER >= 0x02010000 */
|
|
|
|
|
|
|
|
bufferevent_setcb(bev, pxy_bev_readcb_child, pxy_bev_writecb_child, pxy_bev_eventcb_child, ctx);
|
|
|
|
|
|
|
|
// @attention We cannot enable events here, because src events will be deferred until after dst is connected
|
|
|
|
// Also, to avoid r/w cb before connected, we should enable r/w events after the conn is connected
|
|
|
|
//bufferevent_enable(bev, EV_READ|EV_WRITE);
|
|
|
|
return bev;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free bufferevent and close underlying socket properly.
|
|
|
|
* For OpenSSL bufferevents, this will shutdown the SSL connection.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
protossl_bufferevent_free_and_close_fd(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
SSL *ssl = bufferevent_openssl_get_ssl(bev); /* does not inc refc */
|
|
|
|
evutil_socket_t fd = bufferevent_getfd(bev);
|
|
|
|
|
|
|
|
log_finer_va("in=%zu, out=%zu, fd=%d", evbuffer_get_length(bufferevent_get_input(bev)), evbuffer_get_length(bufferevent_get_output(bev)), fd);
|
|
|
|
|
|
|
|
// @see https://stackoverflow.com/questions/31688709/knowing-all-callbacks-have-run-with-libevent-and-bufferevent-free
|
|
|
|
bufferevent_setcb(bev, NULL, NULL, NULL, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* From the libevent book: SSL_RECEIVED_SHUTDOWN tells
|
|
|
|
* SSL_shutdown to act as if we had already received a close
|
|
|
|
* notify from the other end. SSL_shutdown will then send the
|
|
|
|
* final close notify in reply. The other end will receive the
|
|
|
|
* close notify and send theirs. By this time, we will have
|
|
|
|
* already closed the socket and the other end's real close
|
|
|
|
* notify will never be received. In effect, both sides will
|
|
|
|
* think that they have completed a clean shutdown and keep
|
|
|
|
* their sessions valid. This strategy will fail if the socket
|
|
|
|
* is not ready for writing, in which case this hack will lead
|
|
|
|
* to an unclean shutdown and lost session on the other end.
|
|
|
|
*/
|
|
|
|
SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
|
|
|
|
SSL_shutdown(ssl);
|
|
|
|
|
|
|
|
bufferevent_disable(bev, EV_READ|EV_WRITE);
|
|
|
|
bufferevent_free(bev);
|
|
|
|
|
|
|
|
if (OPTS_DEBUG(ctx->global)) {
|
|
|
|
char *str = ssl_ssl_state_to_str(ssl, "SSL_free() in state ", 1);
|
|
|
|
if (str)
|
|
|
|
log_dbg_print_free(str);
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_PROXY
|
|
|
|
char *str = ssl_ssl_state_to_str(ssl, "SSL_free() in state ", 0);
|
|
|
|
if (str) {
|
|
|
|
log_finer_va("fd=%d, %s", fd, str);
|
|
|
|
free(str);
|
|
|
|
}
|
|
|
|
#endif /* DEBUG_PROXY */
|
|
|
|
|
|
|
|
SSL_free(ssl);
|
|
|
|
/* bufferevent_getfd() returns -1 if no file descriptor is associated
|
|
|
|
* with the bufferevent */
|
|
|
|
if (fd >= 0)
|
|
|
|
evutil_closesocket(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
protossl_free(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
if (ctx->sslctx->ssl_names) {
|
|
|
|
free(ctx->sslctx->ssl_names);
|
|
|
|
}
|
|
|
|
if (ctx->sslctx->origcrtfpr) {
|
|
|
|
free(ctx->sslctx->origcrtfpr);
|
|
|
|
}
|
|
|
|
if (ctx->sslctx->usedcrtfpr) {
|
|
|
|
free(ctx->sslctx->usedcrtfpr);
|
|
|
|
}
|
|
|
|
if (ctx->sslctx->origcrt) {
|
|
|
|
X509_free(ctx->sslctx->origcrt);
|
|
|
|
}
|
|
|
|
if (ctx->sslctx->sni) {
|
|
|
|
free(ctx->sslctx->sni);
|
|
|
|
}
|
|
|
|
if (ctx->sslctx->srvdst_ssl_version) {
|
|
|
|
free(ctx->sslctx->srvdst_ssl_version);
|
|
|
|
}
|
|
|
|
if (ctx->sslctx->srvdst_ssl_cipher) {
|
|
|
|
free(ctx->sslctx->srvdst_ssl_cipher);
|
|
|
|
}
|
|
|
|
free(ctx->sslctx);
|
|
|
|
// It is necessary to NULL the sslctx to prevent passthrough mode trying to access it (signal 11 crash)
|
|
|
|
ctx->sslctx = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef OPENSSL_NO_TLSEXT
|
|
|
|
/*
|
|
|
|
* The SNI hostname has been resolved. Fill the first resolved address into
|
|
|
|
* the context and continue connecting.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
protossl_sni_resolve_cb(int errcode, struct evutil_addrinfo *ai, void *arg)
|
|
|
|
{
|
|
|
|
pxy_conn_ctx_t *ctx = arg;
|
|
|
|
|
|
|
|
log_finest("ENTER");
|
|
|
|
|
|
|
|
if (errcode) {
|
|
|
|
log_err_printf("Cannot resolve SNI hostname '%s': %s\n", ctx->sslctx->sni, evutil_gai_strerror(errcode));
|
|
|
|
evutil_closesocket(ctx->fd);
|
|
|
|
pxy_conn_ctx_free(ctx, 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(&ctx->dstaddr, ai->ai_addr, ai->ai_addrlen);
|
|
|
|
ctx->dstaddrlen = ai->ai_addrlen;
|
|
|
|
evutil_freeaddrinfo(ai);
|
|
|
|
pxy_conn_connect(ctx);
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_TLSEXT */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The src fd is readable. This is used to sneak-preview the SNI on SSL
|
|
|
|
* connections. If ctx->ev is NULL, it was called manually for a non-SSL
|
|
|
|
* connection. If ctx->opts->passthrough is set, it was called a second time
|
|
|
|
* after the first ssl callout failed because of client cert auth.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
protossl_fd_readcb(evutil_socket_t fd, UNUSED short what, void *arg)
|
|
|
|
{
|
|
|
|
pxy_conn_ctx_t *ctx = arg;
|
|
|
|
|
|
|
|
log_finest("ENTER");
|
|
|
|
|
|
|
|
event_free(ctx->ev);
|
|
|
|
ctx->ev = NULL;
|
|
|
|
|
|
|
|
// Child connections will use the sni info obtained by the parent conn
|
|
|
|
/* for SSL, peek ClientHello and parse SNI from it */
|
|
|
|
|
|
|
|
unsigned char buf[1024];
|
|
|
|
ssize_t n;
|
|
|
|
const unsigned char *chello;
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
n = recv(fd, buf, sizeof(buf), MSG_PEEK);
|
|
|
|
if (n == -1) {
|
|
|
|
log_err_printf("Error peeking on fd, aborting connection\n");
|
|
|
|
log_fine("Error peeking on fd, aborting connection");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (n == 0) {
|
|
|
|
/* socket got closed while we were waiting */
|
|
|
|
log_err_printf("Socket got closed while waiting\n");
|
|
|
|
log_fine("Socket got closed while waiting");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = ssl_tls_clienthello_parse(buf, n, 0, &chello, &ctx->sslctx->sni);
|
|
|
|
if ((rv == 1) && !chello) {
|
|
|
|
log_err_printf("Peeking did not yield a (truncated) ClientHello message, aborting connection\n");
|
|
|
|
log_fine("Peeking did not yield a (truncated) ClientHello message, aborting connection");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (OPTS_DEBUG(ctx->global)) {
|
|
|
|
log_dbg_printf("SNI peek: [%s] [%s], fd=%d\n", ctx->sslctx->sni ? ctx->sslctx->sni : "n/a",
|
|
|
|
((rv == 1) && chello) ? "incomplete" : "complete", ctx->fd);
|
|
|
|
}
|
|
|
|
if ((rv == 1) && chello && (ctx->sslctx->sni_peek_retries++ < 50)) {
|
|
|
|
/* ssl_tls_clienthello_parse indicates that we
|
|
|
|
* should retry later when we have more data, and we
|
|
|
|
* haven't reached the maximum retry count yet.
|
|
|
|
* Reschedule this event as timeout-only event in
|
|
|
|
* order to prevent busy looping over the read event.
|
|
|
|
* Because we only peeked at the pending bytes and
|
|
|
|
* never actually read them, fd is still ready for
|
|
|
|
* reading now. We use 25 * 0.2 s = 5 s timeout. */
|
|
|
|
struct timeval retry_delay = {0, 100};
|
|
|
|
|
|
|
|
ctx->ev = event_new(ctx->thr->evbase, fd, 0, protossl_fd_readcb, ctx);
|
|
|
|
if (!ctx->ev) {
|
|
|
|
log_err_level(LOG_CRIT, "Error creating retry event, aborting connection");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (event_add(ctx->ev, &retry_delay) == -1)
|
|
|
|
goto out;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx->sslctx->sni && !ctx->dstaddrlen && ctx->spec->sni_port) {
|
|
|
|
char sniport[6];
|
|
|
|
struct evutil_addrinfo hints;
|
|
|
|
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
hints.ai_family = ctx->af;
|
|
|
|
hints.ai_flags = EVUTIL_AI_ADDRCONFIG;
|
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
|
|
|
|
|
|
snprintf(sniport, sizeof(sniport), "%i", ctx->spec->sni_port);
|
|
|
|
evdns_getaddrinfo(ctx->thr->dnsbase, ctx->sslctx->sni, sniport, &hints, protossl_sni_resolve_cb, ctx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pxy_conn_connect(ctx);
|
|
|
|
return;
|
|
|
|
out:
|
|
|
|
evutil_closesocket(fd);
|
|
|
|
pxy_conn_ctx_free(ctx, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
protossl_init_conn(evutil_socket_t fd, UNUSED short what, void *arg)
|
|
|
|
{
|
|
|
|
pxy_conn_ctx_t *ctx = arg;
|
|
|
|
|
|
|
|
log_finest("ENTER");
|
|
|
|
|
|
|
|
event_free(ctx->ev);
|
|
|
|
ctx->ev = NULL;
|
|
|
|
|
|
|
|
if (pxy_conn_init(ctx) == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
#ifdef OPENSSL_NO_TLSEXT
|
|
|
|
pxy_conn_connect(ctx);
|
|
|
|
return;
|
|
|
|
#endif /* !OPENSSL_NO_TLSEXT */
|
|
|
|
|
|
|
|
/* for SSL, defer dst connection setup to initial_readcb */
|
|
|
|
ctx->ev = event_new(ctx->thr->evbase, ctx->fd, EV_READ, protossl_fd_readcb, ctx);
|
|
|
|
if (!ctx->ev)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (event_add(ctx->ev, NULL) == -1) {
|
|
|
|
log_finest("event_add failed");
|
|
|
|
// Note that the timercb of the connection handling thread may try to access the ctx
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
out:
|
|
|
|
evutil_closesocket(fd);
|
|
|
|
pxy_conn_ctx_free(ctx, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
protossl_setup_dst_ssl(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
ctx->dst.ssl = protossl_dstssl_create(ctx);
|
|
|
|
if (!ctx->dst.ssl) {
|
|
|
|
log_err_level_printf(LOG_CRIT, "Error creating SSL for dst\n");
|
|
|
|
pxy_conn_term(ctx, 1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
protossl_setup_srvdst_ssl(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
ctx->srvdst.ssl = protossl_dstssl_create(ctx);
|
|
|
|
if (!ctx->srvdst.ssl) {
|
|
|
|
log_err_level_printf(LOG_CRIT, "Error creating SSL for srvdst\n");
|
|
|
|
pxy_conn_term(ctx, 1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
protossl_setup_srvdst(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
if (protossl_setup_srvdst_ssl(ctx) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx->srvdst.bev = protossl_bufferevent_setup(ctx, -1, ctx->srvdst.ssl);
|
|
|
|
if (!ctx->srvdst.bev) {
|
|
|
|
log_err_level_printf(LOG_CRIT, "Error creating srvdst\n");
|
|
|
|
SSL_free(ctx->srvdst.ssl);
|
|
|
|
ctx->srvdst.ssl = NULL;
|
|
|
|
pxy_conn_term(ctx, 1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ctx->srvdst.free = protossl_bufferevent_free_and_close_fd;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
protossl_conn_connect(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
log_finest("ENTER");
|
|
|
|
|
|
|
|
/* create server-side socket and eventbuffer */
|
|
|
|
if (protossl_setup_srvdst(ctx) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Disable and NULL r/w cbs, we do nothing for srvdst in r/w cbs
|
|
|
|
bufferevent_setcb(ctx->srvdst.bev, NULL, NULL, pxy_bev_eventcb, ctx);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
protossl_setup_dst_ssl_child(pxy_conn_child_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
// Children rely on the findings of parent
|
|
|
|
ctx->dst.ssl = protossl_dstssl_create(ctx->conn);
|
|
|
|
if (!ctx->dst.ssl) {
|
|
|
|
log_err_level_printf(LOG_CRIT, "Error creating SSL\n");
|
|
|
|
// pxy_conn_free()>pxy_conn_free_child() will close the fd, since we have a non-NULL src.bev now
|
|
|
|
pxy_conn_term(ctx->conn, 1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
protossl_connect_child(pxy_conn_child_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
log_finest("ENTER");
|
|
|
|
|
|
|
|
/* create server-side socket and eventbuffer */
|
|
|
|
if (ctx->conn->srvdst.bev) {
|
|
|
|
// Reuse srvdst of parent in the first child conn
|
|
|
|
ctx->dst = ctx->conn->srvdst;
|
|
|
|
|
|
|
|
// See the comments in prototcp_setup_dst()
|
|
|
|
prototcp_disable_srvdst(ctx->conn);
|
|
|
|
|
|
|
|
bufferevent_setcb(ctx->dst.bev, pxy_bev_readcb_child, pxy_bev_writecb_child, pxy_bev_eventcb_child, ctx);
|
|
|
|
ctx->protoctx->bev_eventcb(ctx->dst.bev, BEV_EVENT_CONNECTED, ctx);
|
|
|
|
|
|
|
|
// Return 1 to signal the caller that we have reused srvdst as the dst of the first child conn
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (protossl_setup_dst_ssl_child(ctx) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx->dst.bev = protossl_bufferevent_setup_child(ctx, -1, ctx->dst.ssl);
|
|
|
|
if (!ctx->dst.bev) {
|
|
|
|
log_err_level_printf(LOG_CRIT, "Error creating dst bufferevent\n");
|
|
|
|
SSL_free(ctx->dst.ssl);
|
|
|
|
ctx->dst.ssl = NULL;
|
|
|
|
pxy_conn_term(ctx->conn, 1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ctx->dst.free = protossl_bufferevent_free_and_close_fd;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
static int NONNULL(1)
|
|
|
|
protossl_setup_src_ssl(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
// @todo Make srvdst.ssl the origssl param
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
if (ctx->src.ssl || (ctx->src.ssl = protossl_srcssl_create(ctx, ctx->srvdst.ssl))) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else if (ctx->term) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (!ctx->enomem && (ctx->pass || ctx->conn_opts->passthrough)) {
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
log_err_level_printf(LOG_WARNING, "Falling back to passthrough\n");
|
|
|
|
protopassthrough_engage(ctx);
|
|
|
|
// report protocol change by returning 1
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else if (ctx->sslctx->reconnected) {
|
|
|
|
return -1;
|
|
|
|
}
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
pxy_conn_term(ctx, 1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
protossl_setup_src_ssl_from_dst(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
// @attention We cannot engage passthrough mode upon ssl errors on already enabled src
|
|
|
|
// This function is used by protoautossl only
|
|
|
|
// srvdst may or may not have been xfered to child, or it may be divert or split mode
|
|
|
|
// so make sure dst.ssl is not NULL
|
|
|
|
if (ctx->src.ssl || (ctx->src.ssl = protossl_srcssl_create(ctx, ctx->srvdst.ssl ? ctx->srvdst.ssl : ctx->dst.ssl))) {
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else if (ctx->term) {
|
|
|
|
return -1;
|
|
|
|
}
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
pxy_conn_term(ctx, 1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
protossl_setup_src_ssl_from_child_dst(pxy_conn_child_ctx_t *ctx)
|
|
|
|
{
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
// @attention We cannot engage passthrough mode upon ssl errors on already enabled src
|
|
|
|
// This function is used by protoautossl only
|
|
|
|
if (ctx->conn->src.ssl || (ctx->conn->src.ssl = protossl_srcssl_create(ctx->conn, ctx->dst.ssl))) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else if (ctx->conn->term) {
|
|
|
|
return -1;
|
|
|
|
}
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
pxy_conn_term(ctx->conn, 1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int NONNULL(1)
|
|
|
|
protossl_setup_src(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
if ((rv = protossl_setup_src_ssl(ctx)) != 0) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx->src.bev = protossl_bufferevent_setup(ctx, ctx->fd, ctx->src.ssl);
|
|
|
|
if (!ctx->src.bev) {
|
|
|
|
log_err_level_printf(LOG_CRIT, "Error creating src bufferevent\n");
|
|
|
|
SSL_free(ctx->src.ssl);
|
|
|
|
ctx->src.ssl = NULL;
|
|
|
|
pxy_conn_term(ctx, 1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ctx->src.free = protossl_bufferevent_free_and_close_fd;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
protossl_enable_src(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
// @todo The return value of protossl_enable_src() never used, just return?
|
|
|
|
int rv;
|
|
|
|
if ((rv = protossl_setup_src(ctx)) != 0) {
|
|
|
|
// Might have switched to passthrough mode
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
bufferevent_setcb(ctx->src.bev, pxy_bev_readcb, pxy_bev_writecb, pxy_bev_eventcb, ctx);
|
|
|
|
|
|
|
|
// Save the srvdst ssl info for logging
|
|
|
|
ctx->sslctx->srvdst_ssl_version = strdup(SSL_get_version(ctx->srvdst.ssl));
|
|
|
|
ctx->sslctx->srvdst_ssl_cipher = strdup(SSL_get_cipher(ctx->srvdst.ssl));
|
|
|
|
|
|
|
|
if (pxy_setup_child_listener(ctx) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_finer("Enabling src");
|
|
|
|
// Now open the gates
|
|
|
|
bufferevent_enable(ctx->src.bev, EV_READ|EV_WRITE);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void NONNULL(1,2)
|
|
|
|
protossl_bev_eventcb_connected_dst(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
log_finest("ENTER");
|
|
|
|
|
|
|
|
ctx->connected = 1;
|
|
|
|
bufferevent_enable(bev, EV_READ|EV_WRITE);
|
|
|
|
|
|
|
|
protossl_enable_src(ctx);
|
|
|
|
}
|
|
|
|
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
static void
|
|
|
|
protossl_bev_eventcb_connected_srvdst(UNUSED struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
log_finest("ENTER");
|
|
|
|
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
pxy_userauth(ctx);
|
|
|
|
if (ctx->term || ctx->enomem) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
|
|
|
|
// Defer any pass or block action until SSL filter application below
|
|
|
|
if (pxy_conn_apply_filter(ctx, FILTER_ACTION_PASS | FILTER_ACTION_BLOCK)) {
|
|
|
|
// We never reach here, since we defer pass and block actions
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set src ssl up early to apply SSL filter,
|
|
|
|
// this is the last moment we can take divert or split action
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
if (protossl_setup_src_ssl(ctx) != 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prototcp_setup_dst(ctx) == -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx->divert) {
|
|
|
|
bufferevent_setcb(ctx->dst.bev, pxy_bev_readcb, pxy_bev_writecb, pxy_bev_eventcb, ctx);
|
|
|
|
if (bufferevent_socket_connect(ctx->dst.bev, (struct sockaddr *)&ctx->spec->divert_addr, ctx->spec->divert_addrlen) == -1) {
|
|
|
|
log_fine("FAILED bufferevent_socket_connect for divert addr");
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
pxy_conn_term(ctx, 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void NONNULL(1,2)
|
|
|
|
protossl_bev_eventcb_error_srvdst(UNUSED struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
log_fine("ENTER");
|
|
|
|
|
|
|
|
if (!ctx->connected) {
|
|
|
|
log_fine("!ctx->connected");
|
|
|
|
|
|
|
|
/* the callout to the original destination failed,
|
|
|
|
* e.g. because it asked for client cert auth, so
|
|
|
|
* close the accepted socket and clean up */
|
|
|
|
if (((ctx->conn_opts->passthrough && ctx->sslctx->have_sslerr) || (ctx->pass && !ctx->sslctx->have_sslerr))) {
|
|
|
|
/* ssl callout failed, fall back to plain TCP passthrough of SSL connection */
|
|
|
|
log_err_level_printf(LOG_WARNING, "SSL srvdst connection failed; falling back to passthrough\n");
|
|
|
|
ctx->sslctx->have_sslerr = 0;
|
|
|
|
protopassthrough_engage(ctx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pxy_conn_term(ctx, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void NONNULL(1)
|
|
|
|
protossl_bev_eventcb_dst(struct bufferevent *bev, short events, pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
if (events & BEV_EVENT_CONNECTED) {
|
|
|
|
protossl_bev_eventcb_connected_dst(bev, ctx);
|
|
|
|
} else if (events & BEV_EVENT_EOF) {
|
|
|
|
prototcp_bev_eventcb_eof_dst(bev, ctx);
|
|
|
|
} else if (events & BEV_EVENT_ERROR) {
|
|
|
|
prototcp_bev_eventcb_error_dst(bev, ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
protossl_bev_eventcb_srvdst(struct bufferevent *bev, short events, pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
if (events & BEV_EVENT_CONNECTED) {
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
protossl_bev_eventcb_connected_srvdst(bev, ctx);
|
|
|
|
} else if (events & BEV_EVENT_EOF) {
|
|
|
|
prototcp_bev_eventcb_eof_srvdst(bev, ctx);
|
|
|
|
} else if (events & BEV_EVENT_ERROR) {
|
|
|
|
protossl_bev_eventcb_error_srvdst(bev, ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
protossl_bev_eventcb(struct bufferevent *bev, short events, void *arg)
|
|
|
|
{
|
|
|
|
pxy_conn_ctx_t *ctx = arg;
|
|
|
|
|
|
|
|
if (events & BEV_EVENT_ERROR) {
|
|
|
|
protossl_log_ssl_error(bev, ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bev == ctx->src.bev) {
|
|
|
|
prototcp_bev_eventcb_src(bev, events, ctx);
|
|
|
|
} else if (bev == ctx->dst.bev) {
|
|
|
|
protossl_bev_eventcb_dst(bev, events, ctx);
|
|
|
|
} else if (bev == ctx->srvdst.bev) {
|
|
|
|
protossl_bev_eventcb_srvdst(bev, events, ctx);
|
|
|
|
} else {
|
|
|
|
log_err_printf("protossl_bev_eventcb: UNKWN conn end\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
protossl_bev_eventcb_child(struct bufferevent *bev, short events, void *arg)
|
|
|
|
{
|
|
|
|
pxy_conn_child_ctx_t *ctx = arg;
|
|
|
|
|
|
|
|
if (events & BEV_EVENT_ERROR) {
|
|
|
|
protossl_log_ssl_error(bev, ctx->conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bev == ctx->src.bev) {
|
|
|
|
prototcp_bev_eventcb_src_child(bev, events, ctx);
|
|
|
|
} else if (bev == ctx->dst.bev) {
|
|
|
|
prototcp_bev_eventcb_dst_child(bev, events, ctx);
|
|
|
|
} else {
|
|
|
|
log_err_printf("protossl_bev_eventcb_child: UNKWN conn end\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// @attention Called by thrmgr thread
|
|
|
|
protocol_t
|
|
|
|
protossl_setup(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
ctx->protoctx->proto = PROTO_SSL;
|
|
|
|
ctx->protoctx->connectcb = protossl_conn_connect;
|
|
|
|
ctx->protoctx->init_conn = protossl_init_conn;
|
|
|
|
|
|
|
|
ctx->protoctx->bev_eventcb = protossl_bev_eventcb;
|
|
|
|
|
|
|
|
ctx->protoctx->proto_free = protossl_free;
|
|
|
|
|
|
|
|
ctx->sslctx = malloc(sizeof(ssl_ctx_t));
|
|
|
|
if (!ctx->sslctx) {
|
|
|
|
return PROTO_ERROR;
|
|
|
|
}
|
|
|
|
memset(ctx->sslctx, 0, sizeof(ssl_ctx_t));
|
|
|
|
|
|
|
|
return PROTO_SSL;
|
|
|
|
}
|
|
|
|
|
|
|
|
protocol_t
|
|
|
|
protossl_setup_child(pxy_conn_child_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
ctx->protoctx->proto = PROTO_SSL;
|
|
|
|
ctx->protoctx->connectcb = protossl_connect_child;
|
|
|
|
|
|
|
|
ctx->protoctx->bev_eventcb = protossl_bev_eventcb_child;
|
|
|
|
|
|
|
|
return PROTO_SSL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vim: set noet ft=c: */
|