完成ssl stream的流程梳理和接口定义。

This commit is contained in:
zhengchao
2018-08-23 19:46:38 +08:00
parent 7995af9c77
commit 18a6dda00f
7 changed files with 463 additions and 353 deletions

View File

@@ -4,6 +4,8 @@
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <stdint.h>
#include <sys/socket.h>
#include <event2/event.h>
#include <event2/listener.h>
@@ -26,7 +28,33 @@
#include <stream.h>
#include <cert.h>
#include <ssl.h>
#include <MESA_htable.h>
#define SSL_EX_DATA_IDX_SSLMGR 0
struct ssl_mgr
{
uint8_t sslcomp;
uint8_t no_ssl2;
uint8_t no_ssl3;
uint8_t no_tls10;
uint8_t no_tls11;
uint8_t no_tls12;
CONST_SSL_METHOD *(*sslmethod)(void);
int sslversion;
char ssl_session_context[8];
MESA_htable_handle down_sess_cache;
MESA_htable_handle up_sess_cache;
char* default_ciphers;
DH * dh;
char * ecdhcurve;
char * crlurl;
uint8_t SSL_MODE_RELEASE_BUFFERS;
};
struct ssl_stream
{
SSL * ssl;
struct ssl_mgr* ref_mgr;
char* sni;
};
struct peek_client_hello_ctx
{
@@ -35,6 +63,71 @@ struct peek_client_hello_ctx
struct event_base* evbase;
};
struct ssl_connect_origin_ctx
{
struct bufferevent *bev;
struct ssl_stream* ssl;
struct sockaddr addr;
socklen_t addrlen;
};
struct ssl_mgr* init_ssl_manager(const char* ini_profile, const char* section)
{
struct ssl_mgr* mgr=ALLOC(struct ssl_mgr*, 1);
memcpy(mgr->ssl_session_context,"mesa-tfe",sizeof(mgr->ssl_session_context));
return mgr;
}
void destroy_ssl_manager(struct ssl_mgr* mgr)
{
free(mgr);
}
SSL_SESSION* up_session_get(MESA_htable_handle* cache, struct sockaddr * addr, socklen_t addr_len, const char* sni)
{
}
void up_session_set(MESA_htable_handle* cache, struct sockaddr * addr, socklen_t addr_len, const char* sni, SSL_SESSION * value)
{
}
SSL_SESSION* down_session_get(MESA_htable_handle* cache, unsigned char * id, int idlen)
{
dynbuf_t *valbuf = (dynbuf_t *)val;
MESA_htable_search_cb(const MESA_htable_handle table, const uchar * key, uint size, hash_cb_fun_t * cb, void * arg, long * cb_ret);
SSL_SESSION *sess;
const unsigned char *p;
p = (const unsigned char *)valbuf->buf;
sess = d2i_SSL_SESSION(NULL, &p, valbuf->sz); /* increments p */
if (!sess)
return NULL;
if (!ssl_session_is_valid(sess)) {
SSL_SESSION_free(sess);
return NULL;
}
if (copy)
return sess;
SSL_SESSION_free(sess);
return ((void*)-1);
}
void down_session_set(MESA_htable_handle* cache, SSL_SESSION* sess)
{
unsigned int len=0;
const unsigned char* id = SSL_SESSION_get_id(sess, &len);
unsigned char *p=NULL;
size_t asn1sz=0;
asn1sz = i2d_SSL_SESSION(sess, NULL);
p=ALLOC(char, asn1sz);
i2d_SSL_SESSION(sess, &p); /* updates p */
int ret=MESA_htable_add(cache, id, len, p);
return;
}
void down_session_del(MESA_htable_handle* cache, SSL_SESSION* sess)
{
}
void peek_client_hello_ctx_free(void* ctx)
{
struct peek_client_hello_ctx * _ctx=(struct peek_client_hello_ctx *)ctx;
@@ -45,10 +138,10 @@ void peek_client_hello_ctx_free(void* ctx)
free(_ctx);
return;
}
struct ssl_client_hello* ssl_get_peek_result(future_result_t* result)
struct ssl_chello* ssl_peek_result_release_chello(future_result_t* result)
{
struct ssl_client_hello* p=(struct ssl_client_hello* )result, *copy=NULL;
copy=ALLOC(struct ssl_client_hello*,1);
struct ssl_chello* p=(struct ssl_chello* )result, *copy=NULL;
copy=ALLOC(struct ssl_chello*,1);
if(p!=NULL)
{
copy->sni=tfe_strdup(p->sni);
@@ -57,7 +150,7 @@ struct ssl_client_hello* ssl_get_peek_result(future_result_t* result)
}
return copy;
}
void ssl_free_peek_result(struct ssl_client_hello* p)
void ssl_free_chello(struct ssl_chello* p)
{
if(p==NULL)
{
@@ -75,8 +168,11 @@ void ssl_free_peek_result(struct ssl_client_hello* p)
static void peek_client_hello_cb(evutil_socket_t fd, short what, void * arg)
{
struct promise* promise=(struct promise*)arg;
struct peek_client_hello_ctx* ctx= (struct peek_client_hello_ctx*)promise->ctx;
struct peek_client_hello_ctx* ctx= (struct peek_client_hello_ctx*)promise_get_ctx(promise);
struct ssl_chello *result=NULL;
const char* reason_too_many_retries="too many tries";
const char* reason_see_no_client_hello="see no client hello";
const char* reason=NULL;
memset(&result, 0, sizeof(result));
char* sni=NULL;
@@ -97,44 +193,50 @@ static void peek_client_hello_cb(evutil_socket_t fd, short what, void * arg)
goto failed;
}
result=ssl_get_peek_result(NULL);
//todo: parse version and cipher suites.
//or we should use sni proxy instead? https://github.com/dlundquist/sniproxy/blob/master/src/tls.c
rv = ssl_tls_clienthello_parse(buf, n, 0, &chello, &result->sni);
rv = ssl_tls_clienthello_parse(buf, n, 0, &chello, &result->sni);
if(rv==0)
{
TFE_LOG_ERROR("Peeking did not yield a (truncated) ClientHello message, aborting connection\n");
{
promise_dettach_ctx(promise);
promise_success(promise, result);
ssl_free_chello(result);
peek_client_hello_ctx_free(ctx);
}
}
else
{
if(!chello)
{
TFE_LOG_ERROR("Peeking did not yield a (truncated) ClientHello message, aborting connection\n");
reason="see no client hello";
goto failed;
}
if(ctx->sni_peek_retries++ > 50)
{
TFE_LOG_ERROR("Peek failed due to too many retries\n");
reason="too many peek retries";
goto failed;
}
/* 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
/* 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};
struct timeval retry_delay = {0, 100};
event_free(ctx->ev);
ctx->ev = event_new(ctx->evbase, fd, 0, peek_client_hello_cb, promise);
assert(ctx->ev!=NULL);
event_add(ctx->ev, &retry_delay);
event_add(ctx->ev, &retry_delay);
}
promise_dettach_ctx(promise);
promise_success(promise, result);
ssl_free_peek_result(result);
}
return;
failed:
promise_failed(promise,FUTURE_ERROR_EXCEPTION,"too many tries");
failed:
promise_dettach_ctx(promise);
ssl_free_peek_result(result);
promise_dettach_ctx(promise);
promise_failed(promise,FUTURE_ERROR_EXCEPTION,reason);
peek_client_hello_ctx_free(ctx);
ssl_free_chello(result);
return;
}
@@ -151,21 +253,101 @@ void ssl_async_peek_client_hello(struct future* future, evutil_socket_t fd, str
promise_set_ctx(p, ctx, peek_client_hello_ctx_free);
return;
}
/*
* Create new SSL context for outgoing connections to the original destination.
* If hostname sni is provided, use it for Server Name Indication.
*/
static SSL* upstream_ssl_create(struct ssl_mgr* mgr, const struct ssl_chello* chello, struct sockaddr* addr, socklen_t addrlen)
{
struct bufferevent *bev;
{
SSL_CTX* sslctx=NULL;
SSL* ssl=NULL;
SSL_SESSION* sess=NULL;
sslctx = SSL_CTX_new(mgr->sslmethod());
pxy_sslctx_setoptions(sslctx, mgr);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
if (mgr->sslversion) {
if (SSL_CTX_set_min_proto_version(sslctx, chello->version) == 0 ||
SSL_CTX_set_max_proto_version(sslctx, chello->version) == 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 (chello->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 = up_session_get(mgr->up_sess_cache, addr,
addrlen, chello->sni); /* new sess inst */
if (sess)
{
SSL_set_session(ssl, sess); /* increments sess refcount */
SSL_SESSION_free(sess);
}
return ssl;
}
void ssl_stream_shutdown(struct bufferevent *bev,struct ssl_stream* ssl)
{
//TODO:
}
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.
{
if(ctx->ssl!=NULL)
{
ssl_stream_shutdown(ctx->bev,ctx->ssl);
free(ctx->ssl);
}
if(ctx->bev!=NULL)
{
bufferevent_free(ctx->bev);
ctx->bev=NULL;
}
free(ctx);
return;
}
struct ssl_stream* ssl_conn_origin_result_release_stream(future_result_t* result)
{
struct ssl_connect_origin_ctx* ctx=(struct ssl_connect_origin_ctx* )result;
struct ssl_stream* ret=ctx->ssl;
ctx->ssl=NULL; //giveup ownership
return ret;
}
struct bufferevent* ssl_conn_origin_result_release_bev(future_result_t* result)
{
struct ssl_connect_origin_ctx* ctx=(struct ssl_connect_origin_ctx* )result;
struct bufferevent * ret=ctx->bev;
ctx->bev=NULL; //giveup ownership
return ret;
}
/*
* Callback for meta events on the up- and downstream connection bufferevents.
@@ -173,22 +355,25 @@ void ssl_connect_origin_ctx_free(struct ssl_connect_origin_ctx* ctx)
*/
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 promise* promise=(struct promise*)arg;
struct ssl_connect_origin_ctx* ctx=(struct ssl_connect_origin_ctx*)promise_get_ctx(promise);
SSL_SESSION * to_cached=NULL;
struct tfe_stream_private* _stream = (struct tfe_stream_private*)arg;
if (events & BEV_EVENT_ERROR)
{
{
promise_failed(promise, FUTURE_ERROR_EXCEPTION, "ssl connect failed");
}
else
{
if (events & BEV_EVENT_CONNECTED)
{
if (events & BEV_EVENT_CONNECTED)
{
bufferevent_setcb(ctx->bev, NULL, NULL, NULL, NULL);//leave a clean bev for on_success
to_cached=SSL_get0_session(ctx->ssl->ssl);
up_session_set(ctx->ssl->ref_mgr->up_sess_cache, &(ctx->addr), ctx->addrlen, ctx->ssl->sni, to_cached);
promise_dettach_ctx(promise);
promise_success(promise, ctx);
ctx->bev=NULL;
}
}
else
{
@@ -198,11 +383,19 @@ static void ssl_connect_origin_eventcb(struct bufferevent * bev, short events, v
ssl_connect_origin_ctx_free(ctx);
return
}
void ssl_async_connect_origin(struct future* future, struct ssl_mgr* mgr, const struct ssl_chello* chello, evutil_socket_t fd, struct event_base *evbase)
{
struct promise* p=future_to_promise(future);
struct ssl_connect_origin_ctx* ctx=ALLOC(struct ssl_connect_origin_ctx, 1);
struct ssl_connect_origin_ctx* ctx=ALLOC(struct ssl_connect_origin_ctx, 1);
int ret=0;
struct sockaddr addr;
socklen_t addrlen;
ret=getpeername(fd, &(ctx->addr), &(ctx->addrlen));
assert(ret==0);
ctx->ssl=ALLOC(struct ssl_stream,1);
ctx->ssl->ref_mgr=mgr;
ctx->ssl->sni=tfe_strdup(chello->sni);
ctx->ssl->ssl=upstream_ssl_create(mgr, chello, &(ctx->addr), ctx->addrlen);
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);
@@ -211,7 +404,7 @@ void ssl_async_connect_origin(struct future* future, const struct ssl_client_hel
bufferevent_enable(ctx->bev, EV_READ | EV_WRITE);
}
/*
/*
* Dump information on a cert to the debug log.
*/
static void pxy_debug_crt(X509 * crt)
@@ -241,7 +434,7 @@ static void pxy_debug_crt(X509 * crt)
free(fpr);
}
#ifdef DEBUG_CERTIFICATE
#ifdef DEBUG_CERTIFICATE
/* dump cert */
log_dbg_print_free(ssl_x509_to_str(crt));
log_dbg_print_free(ssl_x509_to_pem(crt));
@@ -406,35 +599,22 @@ out:
* 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
*/
static int
pxy_ossl_sessnew_cb(MAYBE_UNUSED SSL * ssl, SSL_SESSION * sess)
static int
pxy_ossl_sessnew_cb(SSL * ssl, SSL_SESSION * sess)
{
#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");
}
{
struct ssl_mgr* mgr=(struct ssl_mgr*) SSL_get_ex_data(ssl, SSL_EX_DATA_IDX_SSLMGR, mgr);
#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"
if (SSL_version(ssl) == SSL2_VERSION) {
return 0;
}
#endif /* HAVE_SSLV2 */
if (sess)
{
{
down_session_set(mgr->down_sess_cache, sess);
}
return 0;
@@ -447,46 +627,26 @@ pxy_ossl_sessnew_cb(MAYBE_UNUSED SSL * ssl, SSL_SESSION * sess)
*/
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");
}
{
struct ssl_mgr* mgr=(struct ssl_mgr*) SSL_get_ex_data(ssl, SSL_EX_DATA_IDX_SSLMGR, mgr);
if (sess)
{
{
down_session_del(mgr->down_sess_cache, sess)
}
return;
}
/*
* Called by OpenSSL when a src SSL session is requested by the client.
OPENSSL_VERSION_NUMBER >= 0x10100000L required.
*/
static SSL_SESSION *
#if OPENSSL_VERSION_NUMBER < 0x10100000L
pxy_ossl_sessget_cb(UNUSED SSL * ssl, unsigned char * id, int idlen, int * copy)
static SSL_SESSION *
pxy_ossl_sessget_cb(UNUSED SSL *ssl, const unsigned char *id, int idlen, int *copy)
pxy_ossl_sessget_cb(UNUSED SSL *ssl, const unsigned char *id, int idlen, int *copy)
{
struct ssl_mgr* mgr=(struct ssl_mgr*) SSL_get_ex_data(ssl, SSL_EX_DATA_IDX_SSLMGR, mgr);
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 */
*copy = 0; /* SSL should not increment reference count of session */
sess = (SSL_SESSION *) down_session_get(mgr->down_sess_cache,id, idlen);
return sess;
}
@@ -494,7 +654,7 @@ pxy_ossl_sessget_cb(UNUSED SSL *ssl, const unsigned char *id, int idlen, int *co
/*
* Set SSL_CTX options that are the same for incoming and outgoing SSL_CTX.
*/
static void
static void
pxy_sslctx_setoptions(SSL_CTX * sslctx, struct ssl_mgr* mgr)
{
SSL_CTX_set_options(sslctx, SSL_OP_ALL);
@@ -512,7 +672,7 @@ pxy_sslctx_setoptions(SSL_CTX * sslctx, tfe_config opts)
#endif /* SSL_OP_NO_TICKET */
#ifdef SSL_OP_NO_SSLv2
#ifdef HAVE_SSLV2
#ifdef HAVE_SSLV2
if (mgr->no_ssl2) {
#endif /* HAVE_SSLV2 */
SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv2);
@@ -520,131 +680,40 @@ pxy_sslctx_setoptions(SSL_CTX * sslctx, tfe_config opts)
}
#endif /* HAVE_SSLV2 */
#endif /* !SSL_OP_NO_SSLv2 */
#ifdef HAVE_SSLV3
#ifdef HAVE_SSLV3
if (mgr->no_ssl3)
{
SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv3);
}
#endif /* HAVE_SSLV3 */
#ifdef HAVE_TLSV10
#ifdef HAVE_TLSV10
if (mgr->no_tls10)
{
SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1);
}
#endif /* HAVE_TLSV10 */
#ifdef HAVE_TLSV11
#ifdef HAVE_TLSV11
if (mgr->no_tls11)
{
SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1_1);
}
#endif /* HAVE_TLSV11 */
#ifdef HAVE_TLSV12
#ifdef HAVE_TLSV12
if (mgr->no_tls12)
{
SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1_2);
}
#endif /* HAVE_TLSV12 */
#ifdef SSL_OP_NO_COMPRESSION
#ifdef SSL_OP_NO_COMPRESSION
if (!mgr->sslcomp)
{
SSL_CTX_set_options(sslctx, SSL_OP_NO_COMPRESSION);
}
#endif /* SSL_OP_NO_COMPRESSION */
SSL_CTX_set_cipher_list(sslctx, mgr->default_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
@@ -683,22 +752,22 @@ static void pxy_srccert_write(pxy_conn_ctx_t * ctx)
{
if (pxy_srccert_write_to_gendir(ctx,
SSL_get_certificate(ctx->src.ssl), 0) == -1)
{
{
log_err_printf("Failed to write used cert\n");
}
}
if (ctx->opts->certgen_writeall)
{
if (pxy_srccert_write_to_gendir(ctx, ctx->origcrt, 1) == -1)
{
{
log_err_printf("Failed to write orig cert\n");
}
}
}
/*
* Create new SSL context for the incoming connection, based on the original
* destination SSL certificate.
* Create new SSL context for the incoming connection, based on the original
* destination SSL cert.
* Returns NULL if no suitable cert could be found.
*/
@@ -729,10 +798,10 @@ static SSL * downstream_ssl_create(cert_t * cert, struct tfe_config* opts)
/*
* 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.
* TLS extension in the clientHello. Must switch to a new SSL_CTX with
* a different cert if we want to replace the server cert here.
* We generate a new cert if the current one does not match the
* supplied servername. This should only happen if the original destination
* supplied servername. This should only happen if the original destination
* server supplies a cert which does not match the server name we
* indicate to it.
*/
@@ -779,7 +848,7 @@ static int pxy_ossl_servername_cb(SSL * ssl, UNUSED int * al, void * arg)
"[%s] != [%s]\n", ctx->sni, sn);
}
}
/* generate a new cert with sn as additional altSubjectName
* and replace it both in the current SSL ctx and in the cert cache */
if (!ctx->immutable_cert &&
@@ -805,7 +874,7 @@ static int pxy_ossl_servername_cb(SSL * ssl, UNUSED int * al, void * arg)
ctx->generated_cert = 1;
if (OPTS_DEBUG(ctx->opts))
{
log_dbg_printf("===> Updated forged server "
log_dbg_printf("===> Updated forged server "
"cert:\n");
pxy_debug_crt(newcrt);
}
@@ -854,81 +923,7 @@ static int pxy_ossl_servername_cb(SSL * ssl, UNUSED int * al, void * arg)
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.
static SSL* upstream_ssl_create(const struct ssl_client_hello* client_hello, int sslversion, const char* sni, MESA_htable_handle* dsess_cache)
{
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)
@@ -936,16 +931,120 @@ void ssl_upstream_free(struct ssl_upstream* p)
X509_free(p->orig_cert);
//todo close ssl with a callback.
//SSL_free(ctx->ssl);
}
}
/*
* Create and set up a new SSL_CTX instance for terminating SSL.
* Set up all the necessary callbacks, the cert, the cert chain and key.
*/
static SSL_CTX * down_sslctx_create(struct ssl_mgr* mgr, struct cert* crt)
{
struct ssl_downstream* p=NULL;
p=ALLOC(struct ssl_downstream, 1);
{
SSL_CTX * sslctx = SSL_CTX_new(mgr->sslmethod());
if (!sslctx)
return NULL;
pxy_sslctx_setoptions(sslctx, mgr);
//TFE's OPENSSL_VERSION_NUMBER >= 0x10100000L
if (mgr->sslversion) {
if (SSL_CTX_set_min_proto_version(sslctx,mgr->sslversion) == 0 ||
SSL_CTX_set_max_proto_version(sslctx, mgr->sslversion) == 0) {
SSL_CTX_free(sslctx);
return NULL;
}
}
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 *)(mgr->ssl_session_context),
sizeof(mgr->ssl_session_context));
#endif /* USE_SSL_SESSION_ID_CONTEXT */
#ifndef OPENSSL_NO_DH
if (mgr->dh)
{
SSL_CTX_set_tmp_dh(sslctx, mgr->dh);
}
else
{
SSL_CTX_set_tmp_dh_callback(sslctx, ssl_tmp_dh_callback);
}
#endif /* !OPENSSL_NO_DH */
#ifndef OPENSSL_NO_ECDH
if (mgr->ecdhcurve)
{
EC_KEY * ecdh = ssl_ec_by_name(mgr->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->crt);
SSL_CTX_use_PrivateKey(sslctx, crt->key);
for (int i = 0; i < sk_X509_num(crt->chain); i++)
{
X509 * c = sk_X509_value(crt->chain, i);
ssl_x509_refcount_inc(c); /* next call consumes a reference */
SSL_CTX_add_extra_chain_cert(sslctx, c);
}
return sslctx;
}
}
/*
* Create new SSL context for the incoming connection, based on the original
* destination SSL cert.
* Returns NULL if no suitable cert could be found.
*/
struct ssl_stream * ssl_downstream_create(struct ssl_mgr* mgr, struct ssl_chello* hello, struct cert* crt)
{
free(p->sni);
{
struct ssl_stream* p=NULL;
if(crt==NULL)
{
goto error_out;
}
p=ALLOC(struct ssl_stream, 1);
p->ref_mgr=mgr;
//could be optimized by store the sslctx.
SSL_CTX * sslctx = down_sslctx_create(mgr,crt);
cert_mgr_free_cert(crt);
if (!sslctx)
{
goto error_out;
}
p->ssl = SSL_new(sslctx);
int ret=SSL_set_ex_data(p->ssl, SSL_EX_DATA_IDX_SSLMGR, mgr);
assert(ret==1);
SSL_CTX_free(sslctx); /* SSL_new() increments refcount */
if (!p->ssl)
{
goto error_out;
}
if(mgr->SSL_MODE_RELEASE_BUFFERS==1)
{
/* lower memory footprint for idle connections */
SSL_set_mode( p->ssl, SSL_get_mode(p->ssl) | SSL_MODE_RELEASE_BUFFERS);
}
p->sni=tfe_strdup(hello->sni);
return p;
error_out:
free(p);
return NULL;
}
void ssl_stream_free_and_close_fd(struct ssl_stream* stream, struct event_base *evbase, evutil_socket_t fd)
{
free(stream->sni);
stream->sni=NULL;
// X509_free(p->fake_cert);
}