2018-08-21 16:11:50 +08:00
|
|
|
#include <netinet/in.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
|
|
#include <event2/event.h>
|
|
|
|
|
#include <event2/listener.h>
|
|
|
|
|
#include <event2/bufferevent.h>
|
|
|
|
|
#include <event2/bufferevent_ssl.h>
|
|
|
|
|
#include <event2/buffer.h>
|
|
|
|
|
#include <event2/thread.h>
|
|
|
|
|
#include <event2/dns.h>
|
|
|
|
|
|
|
|
|
|
#include <openssl/ssl.h>
|
|
|
|
|
#include <openssl/err.h>
|
|
|
|
|
#include <openssl/rand.h>
|
|
|
|
|
#include <openssl/x509.h>
|
|
|
|
|
#include <openssl/x509v3.h>
|
|
|
|
|
|
2018-08-21 19:01:10 +08:00
|
|
|
|
2018-08-21 16:11:50 +08:00
|
|
|
#include <tfe_stream.h>
|
|
|
|
|
#include <tfe_utils.h>
|
|
|
|
|
#include <tfe_future.h>
|
|
|
|
|
#include <stream.h>
|
|
|
|
|
#include <cert.h>
|
|
|
|
|
#include <ssl.h>
|
|
|
|
|
|
2018-08-21 19:01:10 +08:00
|
|
|
struct peek_client_hello_ctx
|
2018-08-21 16:11:50 +08:00
|
|
|
{
|
2018-08-21 19:01:10 +08:00
|
|
|
|
2018-08-21 16:11:50 +08:00
|
|
|
unsigned char sni_peek_retries; /* max 64 SNI parse retries */
|
|
|
|
|
struct event* ev;
|
|
|
|
|
struct event_base* evbase;
|
|
|
|
|
|
|
|
|
|
};
|
2018-08-21 19:01:10 +08:00
|
|
|
void peek_client_hello_ctx_free(void* ctx)
|
2018-08-21 16:11:50 +08:00
|
|
|
{
|
2018-08-21 19:01:10 +08:00
|
|
|
struct peek_client_hello_ctx * _ctx=(struct peek_client_hello_ctx *)ctx;
|
2018-08-21 16:11:50 +08:00
|
|
|
event_free(_ctx->ev);
|
|
|
|
|
_ctx->ev = NULL;
|
|
|
|
|
free(_ctx->sni);
|
|
|
|
|
_ctx->sni=NULL;
|
|
|
|
|
free(_ctx);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-08-21 19:32:37 +08:00
|
|
|
struct ssl_client_hello* ssl_get_peek_result(future_result_t* result)
|
2018-08-21 19:01:10 +08:00
|
|
|
{
|
|
|
|
|
struct ssl_client_hello* p=(struct ssl_client_hello* )result, *copy=NULL;
|
|
|
|
|
copy=ALLOC(struct ssl_client_hello*,1);
|
|
|
|
|
if(p!=NULL)
|
|
|
|
|
{
|
|
|
|
|
copy->sni=tfe_strdup(p->sni);
|
|
|
|
|
copy->cipher_suites=tfe_strdup(p->cipher_suites);
|
|
|
|
|
copy->version=p->version;
|
|
|
|
|
}
|
|
|
|
|
return copy;
|
|
|
|
|
}
|
|
|
|
|
void ssl_free_peek_result(struct ssl_client_hello* p)
|
|
|
|
|
{
|
|
|
|
|
if(p==NULL)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
free(p->sni);
|
|
|
|
|
p->sni=NULL;
|
|
|
|
|
free(p->cipher_suites);
|
|
|
|
|
p->cipher_suites=NULL;
|
|
|
|
|
p->cipher_suites=NULL;
|
|
|
|
|
free(p);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-08-21 16:11:50 +08:00
|
|
|
|
2018-08-21 19:01:10 +08:00
|
|
|
static void peek_client_hello_cb(evutil_socket_t fd, short what, void * arg)
|
2018-08-21 16:11:50 +08:00
|
|
|
{
|
|
|
|
|
struct promise* promise=(struct promise*)arg;
|
2018-08-21 19:01:10 +08:00
|
|
|
struct peek_client_hello_ctx* ctx= (struct peek_client_hello_ctx*)promise->ctx;
|
|
|
|
|
struct ssl_client_hello *result=NULL;
|
|
|
|
|
memset(&result, 0, sizeof(result));
|
|
|
|
|
char* sni=NULL;
|
2018-08-21 16:11:50 +08:00
|
|
|
unsigned char buf[1024];
|
|
|
|
|
ssize_t n=0;
|
|
|
|
|
const unsigned char * chello=NULL;
|
|
|
|
|
int rv=0;
|
|
|
|
|
|
|
|
|
|
n = recv(fd, buf, sizeof(buf), MSG_PEEK);
|
|
|
|
|
if (n == -1)
|
|
|
|
|
{
|
|
|
|
|
TFE_LOG_ERROR("Error peeking on fd, aborting connection\n");
|
2018-08-21 19:01:10 +08:00
|
|
|
goto failed;
|
2018-08-21 16:11:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (n == 0)
|
|
|
|
|
{
|
2018-08-21 19:01:10 +08:00
|
|
|
goto failed;
|
2018-08-21 16:11:50 +08:00
|
|
|
}
|
2018-08-21 19:01:10 +08:00
|
|
|
result=ssl_get_peek_result(NULL);
|
|
|
|
|
//todo: parse version and cipher suites.
|
|
|
|
|
rv = ssl_tls_clienthello_parse(buf, n, 0, &chello, &result->sni);
|
2018-08-21 16:11:50 +08:00
|
|
|
if ((rv == 1) && !chello)
|
|
|
|
|
{
|
|
|
|
|
TFE_LOG_ERROR("Peeking did not yield a (truncated) ClientHello message, aborting connection\n");
|
2018-08-21 19:01:10 +08:00
|
|
|
goto failed;
|
2018-08-21 16:11:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((rv == 1) && chello && (ctx->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};
|
|
|
|
|
|
|
|
|
|
event_free(ctx->ev);
|
2018-08-21 19:01:10 +08:00
|
|
|
ctx->ev = event_new(ctx->evbase, fd, 0, peek_client_hello_cb, promise);
|
2018-08-21 16:11:50 +08:00
|
|
|
assert(ctx->ev!=NULL);
|
|
|
|
|
event_add(ctx->ev, &retry_delay);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-21 19:01:10 +08:00
|
|
|
|
|
|
|
|
promise_dettach_ctx(promise);
|
|
|
|
|
promise_success(promise, result);
|
|
|
|
|
ssl_free_peek_result(result);
|
|
|
|
|
peek_client_hello_ctx_free(ctx);
|
2018-08-21 16:11:50 +08:00
|
|
|
return;
|
2018-08-21 19:01:10 +08:00
|
|
|
failed:
|
|
|
|
|
promise_failed(promise,FUTURE_ERROR_EXCEPTION,"too many tries");
|
|
|
|
|
peek_client_hello_ctx_free(ctx);
|
|
|
|
|
promise_dettach_ctx(promise);
|
|
|
|
|
ssl_free_peek_result(result);
|
|
|
|
|
|
2018-08-21 16:11:50 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-21 19:01:10 +08:00
|
|
|
|
|
|
|
|
void ssl_async_peek_client_hello(struct future* future, evutil_socket_t fd, struct event_base *evbase)
|
2018-08-21 16:11:50 +08:00
|
|
|
{
|
|
|
|
|
struct event * ev=NULL;
|
|
|
|
|
struct promise* p=future_to_promise(future);
|
2018-08-21 19:01:10 +08:00
|
|
|
|
|
|
|
|
struct peek_client_hello_ctx* ctx=ALLOC(struct peek_client_hello_ctx, 1);
|
|
|
|
|
ctx->ev = event_new(evbase, fd, EV_READ, peek_client_hello_cb, p);
|
2018-08-21 16:11:50 +08:00
|
|
|
event_add(evbase, NULL);
|
2018-08-21 19:01:10 +08:00
|
|
|
promise_set_ctx(p, ctx, peek_client_hello_ctx_free);
|
2018-08-21 16:11:50 +08:00
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ssl_connect_origin_ctx
|
|
|
|
|
{
|
|
|
|
|
struct bufferevent *bev;
|
|
|
|
|
SSL* ssl;
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
void ssl_connect_origin_ctx_free(struct ssl_connect_origin_ctx* ctx)
|
|
|
|
|
{
|
|
|
|
|
ctx->ssl=NULL;
|
|
|
|
|
bufferevent_free(ctx->bev);
|
|
|
|
|
ctx->bev=NULL;
|
|
|
|
|
//Do not free bev and ssl, reserved for user.
|
|
|
|
|
free(ctx);
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* Callback for meta events on the up- and downstream connection bufferevents.
|
|
|
|
|
* Called when EOF has been reached, a connection has been made, and on errors.
|
|
|
|
|
*/
|
|
|
|
|
static void ssl_connect_origin_eventcb(struct bufferevent * bev, short events, void * arg)
|
|
|
|
|
{
|
|
|
|
|
struct promise* promise=(struct promise*)arg;
|
|
|
|
|
struct ssl_connect_origin_ctx* ctx=(struct ssl_connect_origin_ctx*)promise_dettach_ctx(promise);
|
|
|
|
|
|
|
|
|
|
struct tfe_stream_private* _stream = (struct tfe_stream_private*)arg;
|
|
|
|
|
|
|
|
|
|
if (events & BEV_EVENT_ERROR)
|
|
|
|
|
{
|
|
|
|
|
promise->f.cb_failed(FUTURE_ERROR_EXCEPTION,"ssl connect failed",promise->f.user);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (events & BEV_EVENT_CONNECTED)
|
|
|
|
|
{
|
|
|
|
|
promise->f.cb_success(ctx->bev,promise->f.user);
|
|
|
|
|
ctx->bev=NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ssl_connect_origin_ctx_free(ctx);
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-21 19:01:10 +08:00
|
|
|
void ssl_async_connect_origin(struct future* future, const struct ssl_client_hello* client_hello, evutil_socket_t fd, const char* sni, struct event_base *evbase)
|
2018-08-21 16:11:50 +08:00
|
|
|
{
|
|
|
|
|
struct promise* p=future_to_promise(future);
|
|
|
|
|
struct ssl_connect_origin_ctx* ctx=ALLOC(struct ssl_connect_origin_ctx, 1);
|
|
|
|
|
ctx->ssl=upstream_ssl_create(opts,sni);
|
|
|
|
|
ctx->bev = bufferevent_openssl_socket_new(evbase, fd, ctx->ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_DEFER_CALLBACKS);
|
|
|
|
|
promise_set_ctx(p, ctx, ssl_connect_origin_ctx_free);
|
|
|
|
|
bufferevent_openssl_set_allow_dirty_shutdown(ctx->bev, 1);
|
|
|
|
|
bufferevent_setcb(ctx->bev, NULL, NULL, ssl_connect_origin_eventcb, p);
|
|
|
|
|
bufferevent_enable(ctx->bev, EV_READ | EV_WRITE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Dump information on a certificate to the debug log.
|
|
|
|
|
*/
|
|
|
|
|
static void pxy_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_printf("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 */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
pxy_log_connect_nonhttp(pxy_conn_ctx_t * ctx)
|
|
|
|
|
{
|
|
|
|
|
char * msg;
|
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The following ifdef's within asprintf arguments list generates
|
|
|
|
|
* warnings with -Wembedded-directive on some compilers.
|
|
|
|
|
* Not fixing the code in order to avoid more code duplication.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (!ctx->src.ssl)
|
|
|
|
|
{
|
|
|
|
|
rv = asprintf(&msg, "%s %s %s %s %s"
|
|
|
|
|
#ifdef HAVE_LOCAL_PROCINFO
|
|
|
|
|
" %s"
|
|
|
|
|
#endif /* HAVE_LOCAL_PROCINFO */
|
|
|
|
|
"\n",
|
|
|
|
|
ctx->passthrough ? "passthrough" : "tcp",
|
|
|
|
|
STRORDASH(ctx->srchost_str),
|
|
|
|
|
STRORDASH(ctx->srcport_str),
|
|
|
|
|
STRORDASH(ctx->dsthost_str),
|
|
|
|
|
STRORDASH(ctx->dstport_str)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
rv = asprintf(&msg, "%s %s %s %s %s "
|
|
|
|
|
"sni:%s names:%s "
|
|
|
|
|
"sproto:%s:%s dproto:%s:%s "
|
|
|
|
|
"origcrt:%s usedcrt:%s\n",
|
|
|
|
|
ctx->clienthello_found ? "upgrade" : "ssl",
|
|
|
|
|
STRORDASH(ctx->srchost_str),
|
|
|
|
|
STRORDASH(ctx->srcport_str),
|
|
|
|
|
STRORDASH(ctx->dsthost_str),
|
|
|
|
|
STRORDASH(ctx->dstport_str),
|
|
|
|
|
STRORDASH(ctx->sni),
|
|
|
|
|
STRORDASH(ctx->ssl_names),
|
|
|
|
|
SSL_get_version(ctx->src.ssl),
|
|
|
|
|
SSL_get_cipher(ctx->src.ssl),
|
|
|
|
|
SSL_get_version(ctx->dst.ssl),
|
|
|
|
|
SSL_get_cipher(ctx->dst.ssl),
|
|
|
|
|
STRORDASH(ctx->origcrtfpr),
|
|
|
|
|
STRORDASH(ctx->usedcrtfpr)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if ((rv < 0) || !msg)
|
|
|
|
|
{
|
|
|
|
|
ctx->enomem = 1;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (!ctx->opts->detach)
|
|
|
|
|
{
|
|
|
|
|
log_err_printf("%s", msg);
|
|
|
|
|
}
|
|
|
|
|
if (ctx->opts->connectlog)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
free(msg);
|
|
|
|
|
}
|
|
|
|
|
out:
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
pxy_log_connect_http(pxy_conn_ctx_t * ctx)
|
|
|
|
|
{
|
|
|
|
|
char * msg;
|
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG_PROXY
|
|
|
|
|
if (ctx->passthrough) {
|
|
|
|
|
log_err_printf("Warning: pxy_log_connect_http called while in "
|
|
|
|
|
"passthrough mode\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The following ifdef's within asprintf arguments list generates
|
|
|
|
|
* warnings with -Wembedded-directive on some compilers.
|
|
|
|
|
* Not fixing the code in order to avoid more code duplication.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (!ctx->spec->ssl)
|
|
|
|
|
{
|
|
|
|
|
rv = asprintf(&msg, "http %s %s %s %s %s %s %s %s %s %s\n",
|
|
|
|
|
STRORDASH(ctx->srchost_str),
|
|
|
|
|
STRORDASH(ctx->srcport_str),
|
|
|
|
|
STRORDASH(ctx->dsthost_str),
|
|
|
|
|
STRORDASH(ctx->dstport_str),
|
|
|
|
|
STRORDASH(ctx->http_host),
|
|
|
|
|
STRORDASH(ctx->http_method),
|
|
|
|
|
STRORDASH(ctx->http_uri),
|
|
|
|
|
STRORDASH(ctx->http_status_code),
|
|
|
|
|
STRORDASH(ctx->http_content_length),
|
|
|
|
|
ctx->ocsp_denied ? " ocsp:denied" : "");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
rv = asprintf(&msg, "https %s %s %s %s %s %s %s %s %s "
|
|
|
|
|
"sni:%s names:%s "
|
|
|
|
|
"sproto:%s:%s dproto:%s:%s "
|
|
|
|
|
"origcrt:%s usedcrt:%s"
|
|
|
|
|
#ifdef HAVE_LOCAL_PROCINFO
|
|
|
|
|
" %s"
|
|
|
|
|
#endif /* HAVE_LOCAL_PROCINFO */
|
|
|
|
|
"%s\n",
|
|
|
|
|
STRORDASH(ctx->srchost_str),
|
|
|
|
|
STRORDASH(ctx->srcport_str),
|
|
|
|
|
STRORDASH(ctx->dsthost_str),
|
|
|
|
|
STRORDASH(ctx->dstport_str),
|
|
|
|
|
STRORDASH(ctx->http_host),
|
|
|
|
|
STRORDASH(ctx->http_method),
|
|
|
|
|
STRORDASH(ctx->http_uri),
|
|
|
|
|
STRORDASH(ctx->http_status_code),
|
|
|
|
|
STRORDASH(ctx->http_content_length),
|
|
|
|
|
STRORDASH(ctx->sni),
|
|
|
|
|
STRORDASH(ctx->ssl_names),
|
|
|
|
|
SSL_get_version(ctx->src.ssl),
|
|
|
|
|
SSL_get_cipher(ctx->src.ssl),
|
|
|
|
|
SSL_get_version(ctx->dst.ssl),
|
|
|
|
|
SSL_get_cipher(ctx->dst.ssl),
|
|
|
|
|
STRORDASH(ctx->origcrtfpr),
|
|
|
|
|
STRORDASH(ctx->usedcrtfpr),
|
|
|
|
|
ctx->ocsp_denied ? " ocsp:denied" : "");
|
|
|
|
|
}
|
|
|
|
|
if ((rv < 0) || !msg)
|
|
|
|
|
{
|
|
|
|
|
ctx->enomem = 1;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (!ctx->opts->detach)
|
|
|
|
|
{
|
|
|
|
|
log_err_printf("%s", msg);
|
|
|
|
|
}
|
|
|
|
|
if (ctx->opts->connectlog)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
free(msg);
|
|
|
|
|
}
|
|
|
|
|
out:
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
|
pxy_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_printf("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
|
|
|
|
|
pxy_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
|
|
|
|
|
pxy_ossl_sessget_cb(UNUSED SSL * ssl, unsigned char * id, int idlen, int * copy)
|
|
|
|
|
#else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
|
pxy_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 = (SSL_SESSION *) 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
|
|
|
|
|
pxy_sslctx_setoptions(SSL_CTX * sslctx, tfe_config opts)
|
|
|
|
|
{
|
|
|
|
|
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 (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 (opts->no_ssl3)
|
|
|
|
|
{
|
|
|
|
|
SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv3);
|
|
|
|
|
}
|
|
|
|
|
#endif /* HAVE_SSLV3 */
|
|
|
|
|
#ifdef HAVE_TLSV10
|
|
|
|
|
if (opts->no_tls10)
|
|
|
|
|
{
|
|
|
|
|
SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1);
|
|
|
|
|
}
|
|
|
|
|
#endif /* HAVE_TLSV10 */
|
|
|
|
|
#ifdef HAVE_TLSV11
|
|
|
|
|
if (opts->no_tls11)
|
|
|
|
|
{
|
|
|
|
|
SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1_1);
|
|
|
|
|
}
|
|
|
|
|
#endif /* HAVE_TLSV11 */
|
|
|
|
|
#ifdef HAVE_TLSV12
|
|
|
|
|
if (opts->no_tls12)
|
|
|
|
|
{
|
|
|
|
|
SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1_2);
|
|
|
|
|
}
|
|
|
|
|
#endif /* HAVE_TLSV12 */
|
|
|
|
|
|
|
|
|
|
#ifdef SSL_OP_NO_COMPRESSION
|
|
|
|
|
if (!opts->sslcomp)
|
|
|
|
|
{
|
|
|
|
|
SSL_CTX_set_options(sslctx, SSL_OP_NO_COMPRESSION);
|
|
|
|
|
}
|
|
|
|
|
#endif /* SSL_OP_NO_COMPRESSION */
|
|
|
|
|
|
|
|
|
|
SSL_CTX_set_cipher_list(sslctx, opts->ciphers);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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 * downstream_sslctx_create(
|
|
|
|
|
pxy_conn_ctx_t * ctx, X509 * crt, STACK_OF(X509) * chain,
|
|
|
|
|
EVP_PKEY * key, struct tfe_config* opts, void* cb_arg)
|
|
|
|
|
{
|
|
|
|
|
SSL_CTX * sslctx = SSL_CTX_new(opts->sslmethod());
|
|
|
|
|
if (!sslctx)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
pxy_sslctx_setoptions(sslctx, opts);
|
|
|
|
|
|
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
|
|
|
if (opts->sslversion) {
|
|
|
|
|
if (SSL_CTX_set_min_proto_version(sslctx, opts->sslversion) == 0 ||
|
|
|
|
|
SSL_CTX_set_max_proto_version(sslctx, opts->sslversion) == 0) {
|
|
|
|
|
SSL_CTX_free(sslctx);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
|
|
|
|
|
|
SSL_CTX_sess_set_new_cb(sslctx, pxy_ossl_sessnew_cb);
|
|
|
|
|
SSL_CTX_sess_set_remove_cb(sslctx, pxy_ossl_sessremove_cb);
|
|
|
|
|
SSL_CTX_sess_set_get_cb(sslctx, pxy_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, pxy_ossl_servername_cb);
|
|
|
|
|
SSL_CTX_set_tlsext_servername_arg(sslctx, ctx);
|
|
|
|
|
#endif /* !OPENSSL_NO_TLSEXT */
|
|
|
|
|
#ifndef OPENSSL_NO_DH
|
|
|
|
|
if (ctx->opts->dh)
|
|
|
|
|
{
|
|
|
|
|
SSL_CTX_set_tmp_dh(sslctx, ctx->opts->dh);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SSL_CTX_set_tmp_dh_callback(sslctx, ssl_tmp_dh_callback);
|
|
|
|
|
}
|
|
|
|
|
#endif /* !OPENSSL_NO_DH */
|
|
|
|
|
#ifndef OPENSSL_NO_ECDH
|
|
|
|
|
if (ctx->opts->ecdhcurve)
|
|
|
|
|
{
|
|
|
|
|
EC_KEY * ecdh = ssl_ec_by_name(ctx->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 */
|
|
|
|
|
SSL_CTX_use_certificate(sslctx, crt);
|
|
|
|
|
SSL_CTX_use_PrivateKey(sslctx, key);
|
|
|
|
|
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->opts)) {
|
|
|
|
|
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
|
|
|
|
|
pxy_srccert_write_to_gendir(pxy_conn_ctx_t * ctx, X509 * crt, int is_orig)
|
|
|
|
|
{
|
|
|
|
|
char * fn;
|
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
|
|
if (!ctx->origcrtfpr)
|
|
|
|
|
return -1;
|
|
|
|
|
if (is_orig)
|
|
|
|
|
{
|
|
|
|
|
rv = asprintf(&fn, "%s/%s.crt", ctx->opts->certgendir,
|
|
|
|
|
ctx->origcrtfpr);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!ctx->usedcrtfpr)
|
|
|
|
|
return -1;
|
|
|
|
|
rv = asprintf(&fn, "%s/%s-%s.crt", ctx->opts->certgendir,
|
|
|
|
|
ctx->origcrtfpr, ctx->usedcrtfpr);
|
|
|
|
|
}
|
|
|
|
|
if (rv == -1)
|
|
|
|
|
{
|
|
|
|
|
ctx->enomem = 1;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(fn);
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void pxy_srccert_write(pxy_conn_ctx_t * ctx)
|
|
|
|
|
{
|
|
|
|
|
if (ctx->opts->certgen_writeall || ctx->generated_cert)
|
|
|
|
|
{
|
|
|
|
|
if (pxy_srccert_write_to_gendir(ctx,
|
|
|
|
|
SSL_get_certificate(ctx->src.ssl), 0) == -1)
|
|
|
|
|
{
|
|
|
|
|
log_err_printf("Failed to write used certificate\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (ctx->opts->certgen_writeall)
|
|
|
|
|
{
|
|
|
|
|
if (pxy_srccert_write_to_gendir(ctx, ctx->origcrt, 1) == -1)
|
|
|
|
|
{
|
|
|
|
|
log_err_printf("Failed to write orig certificate\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Create new SSL context for the incoming connection, based on the original
|
|
|
|
|
* destination SSL certificate.
|
|
|
|
|
* Returns NULL if no suitable certificate could be found.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static SSL * downstream_ssl_create(cert_t * cert, struct tfe_config* opts)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
assert(cert!=NULL);
|
|
|
|
|
SSL_CTX * sslctx = downstream_sslctx_create(ctx, cert->crt, cert->chain,
|
|
|
|
|
cert->key);
|
|
|
|
|
|
|
|
|
|
if (!sslctx)
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
SSL * ssl = SSL_new(sslctx);
|
|
|
|
|
SSL_CTX_free(sslctx); /* SSL_new() increments refcount */
|
|
|
|
|
if (!ssl)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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 pxy_ossl_servername_cb(SSL * ssl, UNUSED int * al, void * arg)
|
|
|
|
|
{
|
|
|
|
|
pxy_conn_ctx_t * ctx = (pxy_conn_ctx_t *) arg;
|
|
|
|
|
const char * sn;
|
|
|
|
|
X509 * sslcrt;
|
|
|
|
|
|
|
|
|
|
if (!(sn = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name)))
|
|
|
|
|
return SSL_TLSEXT_ERR_NOACK;
|
|
|
|
|
|
|
|
|
|
if (!ctx->sni)
|
|
|
|
|
{
|
|
|
|
|
if (OPTS_DEBUG(ctx->opts))
|
|
|
|
|
{
|
|
|
|
|
log_dbg_printf("Warning: SNI parser yielded no "
|
|
|
|
|
"hostname, copying OpenSSL one: "
|
|
|
|
|
"[NULL] != [%s]\n", sn);
|
|
|
|
|
}
|
|
|
|
|
ctx->sni = strdup(sn);
|
|
|
|
|
if (!ctx->sni)
|
|
|
|
|
{
|
|
|
|
|
ctx->enomem = 1;
|
|
|
|
|
return SSL_TLSEXT_ERR_NOACK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (OPTS_DEBUG(ctx->opts))
|
|
|
|
|
{
|
|
|
|
|
if (strcmp(sn, ctx->sni) != 0)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* 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->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->immutable_cert &&
|
|
|
|
|
!ssl_x509_names_match((sslcrt = SSL_get_certificate(ssl)), sn))
|
|
|
|
|
{
|
|
|
|
|
X509 * newcrt;
|
|
|
|
|
SSL_CTX * newsslctx;
|
|
|
|
|
|
|
|
|
|
if (OPTS_DEBUG(ctx->opts))
|
|
|
|
|
{
|
|
|
|
|
log_dbg_printf("Certificate cache: UPDATE "
|
|
|
|
|
"(SNI mismatch)\n");
|
|
|
|
|
}
|
|
|
|
|
newcrt = ssl_x509_forge(ctx->opts->cacrt, ctx->opts->cakey,
|
|
|
|
|
sslcrt, ctx->opts->key,
|
|
|
|
|
sn, ctx->opts->crlurl);
|
|
|
|
|
if (!newcrt)
|
|
|
|
|
{
|
|
|
|
|
ctx->enomem = 1;
|
|
|
|
|
return SSL_TLSEXT_ERR_NOACK;
|
|
|
|
|
}
|
|
|
|
|
cachemgr_fkcrt_set(ctx->origcrt, newcrt);
|
|
|
|
|
ctx->generated_cert = 1;
|
|
|
|
|
if (OPTS_DEBUG(ctx->opts))
|
|
|
|
|
{
|
|
|
|
|
log_dbg_printf("===> Updated forged server "
|
|
|
|
|
"certificate:\n");
|
|
|
|
|
pxy_debug_crt(newcrt);
|
|
|
|
|
}
|
|
|
|
|
if (WANT_CONNECT_LOG(ctx))
|
|
|
|
|
{
|
|
|
|
|
if (ctx->ssl_names)
|
|
|
|
|
{
|
|
|
|
|
free(ctx->ssl_names);
|
|
|
|
|
}
|
|
|
|
|
ctx->ssl_names = ssl_x509_names_to_str(newcrt);
|
|
|
|
|
if (!ctx->ssl_names)
|
|
|
|
|
{
|
|
|
|
|
ctx->enomem = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (WANT_CONNECT_LOG(ctx) || ctx->opts->certgendir)
|
|
|
|
|
{
|
|
|
|
|
if (ctx->usedcrtfpr)
|
|
|
|
|
{
|
|
|
|
|
free(ctx->usedcrtfpr);
|
|
|
|
|
}
|
|
|
|
|
ctx->usedcrtfpr = ssl_x509_fingerprint(newcrt, 0);
|
|
|
|
|
if (!ctx->usedcrtfpr)
|
|
|
|
|
{
|
|
|
|
|
ctx->enomem = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
newsslctx = downstream_sslctx_create(ctx, newcrt, ctx->opts->chain,
|
|
|
|
|
ctx->opts->key);
|
|
|
|
|
if (!newsslctx)
|
|
|
|
|
{
|
|
|
|
|
X509_free(newcrt);
|
|
|
|
|
ctx->enomem = 1;
|
|
|
|
|
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->opts))
|
|
|
|
|
{
|
|
|
|
|
log_dbg_printf("Certificate cache: KEEP (SNI match or "
|
|
|
|
|
"target mode)\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return SSL_TLSEXT_ERR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Create new SSL context for outgoing connections to the original destination.
|
|
|
|
|
* If hostname sni is provided, use it for Server Name Indication.
|
|
|
|
|
*/
|
2018-08-21 19:01:10 +08:00
|
|
|
|
|
|
|
|
static SSL* upstream_ssl_create(const struct ssl_client_hello* client_hello, int sslversion, const char* sni, MESA_htable_handle* dsess_cache)
|
2018-08-21 16:11:50 +08:00
|
|
|
{
|
|
|
|
|
SSL_CTX* sslctx=NULL;
|
|
|
|
|
SSL* ssl=NULL;
|
|
|
|
|
SSL_SESSION* sess=NULL;
|
|
|
|
|
|
|
|
|
|
sslctx = SSL_CTX_new(opts->sslmethod());
|
|
|
|
|
|
|
|
|
|
pxy_sslctx_setoptions(sslctx, opts);
|
|
|
|
|
|
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
|
|
|
if (opts->sslversion) {
|
|
|
|
|
if (SSL_CTX_set_min_proto_version(sslctx, opts->sslversion) == 0 ||
|
|
|
|
|
SSL_CTX_set_max_proto_version(sslctx, opts->sslversion) == 0) {
|
|
|
|
|
SSL_CTX_free(sslctx);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
|
|
|
|
|
|
SSL_CTX_set_verify(sslctx, SSL_VERIFY_NONE, NULL);
|
|
|
|
|
|
|
|
|
|
ssl = SSL_new(sslctx);
|
|
|
|
|
SSL_CTX_free(sslctx); /* SSL_new() increments refcount */
|
|
|
|
|
if (!ssl)
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
#ifndef OPENSSL_NO_TLSEXT
|
|
|
|
|
if (sni)
|
|
|
|
|
{
|
|
|
|
|
SSL_set_tlsext_host_name(ssl, 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 */
|
|
|
|
|
|
|
|
|
|
//Todo: Refactor below session resumption.
|
|
|
|
|
/* session resuming based on remote endpoint address and port */
|
|
|
|
|
sess = (SSL_SESSION *) cachemgr_dsess_get((struct sockaddr *) &ctx->addr,
|
|
|
|
|
ctx->addrlen, ctx->sni); /* new sess inst */
|
|
|
|
|
if (sess)
|
|
|
|
|
{
|
|
|
|
|
if (OPTS_DEBUG(opts))
|
|
|
|
|
{
|
|
|
|
|
log_dbg_printf("Attempt reuse dst SSL session\n");
|
|
|
|
|
}
|
|
|
|
|
SSL_set_session(ssl, sess); /* increments sess refcount */
|
|
|
|
|
SSL_SESSION_free(sess);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ssl;
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
struct ssl_upstream* ssl_upstream_create(tfe_config*opts, const char* sni)
|
|
|
|
|
{
|
|
|
|
|
struct ssl_upstream* upstream=NULL;
|
|
|
|
|
SSL* ssl=NULL;
|
|
|
|
|
ssl=upstream_ssl_create(opts,sni);
|
|
|
|
|
if(ssl==NULL)
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
upstream=ALLOC(struct ssl_upstream,1);
|
|
|
|
|
upstream->ssl=ssl;
|
|
|
|
|
return upstream;
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
void ssl_upstream_free(struct ssl_upstream* p)
|
|
|
|
|
{
|
|
|
|
|
X509_free(p->orig_cert);
|
|
|
|
|
//todo close ssl with a callback.
|
|
|
|
|
//SSL_free(ctx->ssl);
|
|
|
|
|
}
|
|
|
|
|
struct ssl_downstream* ssl_downstream_create(void)
|
|
|
|
|
{
|
|
|
|
|
struct ssl_downstream* p=NULL;
|
|
|
|
|
p=ALLOC(struct ssl_downstream, 1);
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
void ssl_downstream_free(struct ssl_downstream* p)
|
|
|
|
|
{
|
|
|
|
|
free(p->sni);
|
|
|
|
|
p->sni=NULL;
|
|
|
|
|
// X509_free(p->fake_cert);
|
|
|
|
|
}
|
|
|
|
|
|