Merge branch 'feature-ssl-stream' into 'develop-tfe3a'
Feature ssl stream See merge request tango/tfe!5
This commit is contained in:
@@ -2,35 +2,22 @@
|
||||
#include <event2/event.h>
|
||||
#include <tfe_future.h>
|
||||
#include <cert.h>
|
||||
|
||||
#include <field_stat2.h>
|
||||
|
||||
|
||||
struct ssl_stream;
|
||||
|
||||
struct ssl_mgr;
|
||||
struct ssl_mgr* init_ssl_manager(const char* ini_profile, const char* section);
|
||||
void destroy_ssl_manager(struct ssl_mgr* mgr);
|
||||
|
||||
struct ssl_chello
|
||||
{
|
||||
//client hello
|
||||
int version;
|
||||
char* sni;
|
||||
char* cipher_suites;
|
||||
};
|
||||
|
||||
struct ssl_chello* ssl_peek_result_release_chello(future_result_t* result);
|
||||
void ssl_chello_free(struct ssl_chello* client_hello);
|
||||
void ssl_async_peek_client_hello(struct future* future, evutil_socket_t fd, struct event_base *evbase);
|
||||
|
||||
struct ssl_mgr* ssl_manager_init(const char* ini_profile, const char* section, struct event_base *evbase, void* logger, screen_stat_handle_t* fs);
|
||||
void ssl_manager_destroy(struct ssl_mgr* mgr);
|
||||
|
||||
struct ssl_stream* ssl_upstream_create_result_release_stream(future_result_t* result);
|
||||
struct bufferevent* ssl_upstream_create_result_release_bev(future_result_t* result);
|
||||
void ssl_async_upstream_create(struct future* future, struct ssl_mgr* mgr, const struct ssl_chello* chello, evutil_socket_t fd, struct event_base *evbase);
|
||||
void ssl_async_upstream_create(struct future* f, struct ssl_mgr* mgr, evutil_socket_t fd_upstream, evutil_socket_t fd_downstream, struct event_base *evbase);
|
||||
|
||||
struct ssl_stream* ssl_downstream_create_result_release_stream(future_result_t* result);
|
||||
struct bufferevent* ssl_downstream_create_result_release_bev(future_result_t* result);
|
||||
void ssl_async_downstream_create(struct future* f, struct ssl_mgr* mgr, struct ssl_stream* upstream, evutil_socket_t fd_downstream, struct event_base *evbase);
|
||||
|
||||
|
||||
struct ssl_stream * ssl_downstream_create(struct ssl_mgr* mgr, struct ssl_chello* hello, struct cert* crt);
|
||||
void ssl_stream_free_and_close_fd(struct ssl_stream* stream, struct event_base *evbase, evutil_socket_t fd);
|
||||
|
||||
|
||||
|
||||
299
platform/src/ssl_sess_cache.cpp
Normal file
299
platform/src/ssl_sess_cache.cpp
Normal file
@@ -0,0 +1,299 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include <ssl_sess_cache.h>
|
||||
#include <ssl.h>
|
||||
#include <MESA_htable.h>
|
||||
#include <field_stat2.h>
|
||||
|
||||
#define SESS_CACHE_NOT_FOUND -1
|
||||
#define SESS_CACHE_FOUND 0
|
||||
#define SESS_CACHE_UPDATE_OLD 1
|
||||
#define SESS_CACHE_ADD_NEW 2
|
||||
#define SESS_CACHE_INVALID 3
|
||||
|
||||
struct asn1_sess
|
||||
{
|
||||
unsigned char* buff;
|
||||
size_t size;
|
||||
};
|
||||
struct sess_set_args
|
||||
{
|
||||
MESA_htable_handle hash;
|
||||
struct asn1_sess* new_sess;
|
||||
};
|
||||
struct sess_cache
|
||||
{
|
||||
enum tfe_conn_dir served_for;
|
||||
MESA_htable_handle hash;
|
||||
long long hit_cnt, miss_cnt,del_err;
|
||||
};
|
||||
|
||||
static void ssl_sess_free_serialized(void *data)
|
||||
{
|
||||
struct asn1_sess* p=(struct asn1_sess*)data;
|
||||
free(p->buff);
|
||||
p->size=0;
|
||||
free(p);
|
||||
return;
|
||||
}
|
||||
static struct asn1_sess* ssl_sess_serialize(SSL_SESSION *sess)
|
||||
{
|
||||
struct asn1_sess* result=ALLOC(struct asn1_sess,1);
|
||||
result->size = i2d_SSL_SESSION(sess, NULL);
|
||||
/*When using i2d_SSL_SESSION(), the memory location pointed to by pp must be large enough to hold the binary representation of the session.
|
||||
There is no known limit on the size of the created ASN1 representation, so the necessary amount of space should be obtained by first calling
|
||||
i2d_SSL_SESSION() with pp=NULL, and obtain the size needed, then allocate the memory and call i2d_SSL_SESSION() again.*/
|
||||
result->buff=ALLOC(unsigned char,result->size);
|
||||
i2d_SSL_SESSION(sess, &(result->buff));
|
||||
return result;
|
||||
}
|
||||
static SSL_SESSION * ssl_sess_deserialize(const struct asn1_sess* asn1)
|
||||
{
|
||||
SSL_SESSION *sess=NULL;
|
||||
sess = d2i_SSL_SESSION(NULL, &(asn1->buff), asn1->size); /* increments asn1 */
|
||||
return sess;
|
||||
}
|
||||
static int ssl_sess_varify_cb(void *data, int eliminate_type)
|
||||
{
|
||||
SSL_SESSION *sess=NULL;
|
||||
int ret=0;
|
||||
const struct asn1_sess* asn1=(struct asn1_sess*)data;
|
||||
if(eliminate_type==ELIMINATE_TYPE_NUM)
|
||||
{
|
||||
return 1; //direct expired.
|
||||
}
|
||||
sess=ssl_sess_deserialize(asn1);
|
||||
ret=ssl_session_is_valid(sess);
|
||||
SSL_SESSION_free(sess);
|
||||
if(ret==0)
|
||||
{
|
||||
return 1; //should be expired (deleted).
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static long sess_cache_get_cb(void *data, const uchar *key, uint size, void *user_arg)
|
||||
{
|
||||
SSL_SESSION *sess=NULL;
|
||||
int is_valid=0;
|
||||
if(data==NULL)
|
||||
{
|
||||
return SESS_CACHE_NOT_FOUND;
|
||||
}
|
||||
const struct asn1_sess* asn1=(struct asn1_sess*)data;
|
||||
sess=ssl_sess_deserialize(data,asn1);
|
||||
is_valid=ssl_session_is_valid(sess);
|
||||
if(is_valid==0)
|
||||
{
|
||||
SSL_SESSION_free(sess);
|
||||
return SESS_CACHE_INVALID;
|
||||
}
|
||||
else
|
||||
{
|
||||
*(SSL_SESSION **)user_arg=sess;
|
||||
return SESS_CACHE_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
static long sess_cache_set_cb(void *data, const uchar *key, uint size, void *user_arg)
|
||||
{
|
||||
|
||||
struct sess_set_args* args=(struct sess_set_args*)user_arg;
|
||||
struct asn1_sess* new_asn1=args->new_sess;
|
||||
struct asn1_sess *cur_asn1=(struct asn1_sess*)data;
|
||||
int ret=0;
|
||||
if(cur_asn1!=NULL)
|
||||
{
|
||||
free(cur_asn1->buff);
|
||||
cur_asn1->size=new_asn1->size;
|
||||
cur_asn1->buff=ALLOC(unsigned char, cur_asn1->size);
|
||||
memcpy(cur_asn1->buff,new_asn1->buff,cur_asn1->size);
|
||||
return SESS_CACHE_UPDATE_OLD;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret=MESA_htable_add(args->hash, key, size, new_asn1);
|
||||
assert(ret>=0);
|
||||
return SESS_CACHE_ADD_NEW;
|
||||
}
|
||||
}
|
||||
|
||||
static int upsess_mk_key(struct sockaddr * addr, socklen_t addr_len, const char* sni, unsigned char** key_buf)
|
||||
{
|
||||
size_t key_size=0;
|
||||
unsigned char* tmp=NULL, p=NULL;
|
||||
size_t tmp_size;
|
||||
dynbuf_t tmp, *db;
|
||||
short port;
|
||||
size_t snilen;
|
||||
|
||||
switch (((struct sockaddr_storage *)addr)->ss_family) {
|
||||
case AF_INET:
|
||||
tmp = (unsigned char *)
|
||||
&((struct sockaddr_in*)addr)->sin_addr;
|
||||
tmp_size = sizeof(struct in_addr);
|
||||
port = ((struct sockaddr_in*)addr)->sin_port;
|
||||
break;
|
||||
case AF_INET6:
|
||||
tmp = (unsigned char *)
|
||||
&((struct sockaddr_in6*)addr)->sin6_addr;
|
||||
tmp_size = sizeof(struct in6_addr);
|
||||
port = ((struct sockaddr_in6*)addr)->sin6_port;
|
||||
break;
|
||||
default:
|
||||
//should never happens.
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
snilen = sni ? strlen(sni) : 0;
|
||||
key_size=tmp_size+sizeof(port)+snilen;
|
||||
*key_buf=ALLOC(unsigned char, key_size);
|
||||
p=*key_buff;
|
||||
memcpy(p,tmp,tmp_size);
|
||||
p+=tmp_size;
|
||||
memcpy(p, (char*)&port, sizeof(port));
|
||||
p+=sizeof(port);
|
||||
return key_size;
|
||||
|
||||
}
|
||||
void up_session_set(struct sess_cache* cache, struct sockaddr * addr, socklen_t addr_len, const char* sni, SSL_SESSION * sess)
|
||||
{
|
||||
char* key=NULL;
|
||||
int ret=0;
|
||||
size_t key_size=0;
|
||||
long cb_ret=0;
|
||||
void* no_use=NULL;
|
||||
assert(cache->served_for==CONN_DIR_UPSTREAM);
|
||||
key_size=upsess_mk_key(addr, addr_len, sni, &key);
|
||||
struct asn1_sess* asn1=NULL;
|
||||
asn1=ssl_sess_serialize(sess);
|
||||
|
||||
struct sess_set_args set_args;
|
||||
set_args.hash=cache->hash;
|
||||
set_args.new_sess=asn1;
|
||||
no_use=MESA_htable_search_cb(cache->hash, key, key_size, sess_cache_set_cb, &set_args,&cb_ret);
|
||||
if(cb_ret==SESS_CACHE_UPDATE_OLD)
|
||||
{
|
||||
ssl_sess_free_serialized(asn1);
|
||||
}
|
||||
free(key);
|
||||
return;
|
||||
}
|
||||
SSL_SESSION* up_session_get(struct sess_cache* cache, struct sockaddr * addr, socklen_t addr_len, const char* sni)
|
||||
{
|
||||
SSL_SESSION* sess=NULL;
|
||||
void* no_use=NULL;
|
||||
long cb_ret=0;
|
||||
char* key=NULL;
|
||||
size_t key_size=0;
|
||||
assert(cache->served_for==CONN_DIR_UPSTREAM);
|
||||
key_size=upsess_mk_key(addr, addr_len, sni, &key);
|
||||
no_use=MESA_htable_search_cb(cache->hash, key, key_size,sess_cache_get_cb, &sess, &cb_ret);
|
||||
free(key);
|
||||
key=NULL;
|
||||
if(cb_ret==1)
|
||||
{
|
||||
cache->hit_cnt++;
|
||||
return sess;
|
||||
}
|
||||
else
|
||||
{
|
||||
cache->miss_cnt++;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void down_session_set(struct sess_cache* cache, const SSL_SESSION* sess)
|
||||
{
|
||||
unsigned int idlen=0;
|
||||
struct asn1_sess* asn1=NULL;
|
||||
long cb_ret=0;
|
||||
void* no_use=NULL;
|
||||
int ret=0;
|
||||
assert(cache->served_for==CONN_DIR_DOWNSTREAM);
|
||||
asn1=ssl_sess_serialize(sess);
|
||||
/*
|
||||
* SSL_SESSION_get_id() returns a pointer to the internal session id value for the session s.
|
||||
* The length of the id in bytes is stored in *idlen. The length may be 0.
|
||||
* The caller should not free the returned pointer directly.
|
||||
*/
|
||||
const unsigned char* id = SSL_SESSION_get_id(sess, &idlen);
|
||||
struct sess_set_args set_args;
|
||||
set_args.hash=cache->hash;
|
||||
set_args.new_sess=asn1;
|
||||
no_use=MESA_htable_search_cb(cache->hash, id, (unsigned int)idlen, sess_cache_set_cb, &set_args,&cb_ret);
|
||||
if(cb_ret==SESS_CACHE_UPDATE_OLD)
|
||||
{
|
||||
ssl_sess_free_serialized(asn1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
SSL_SESSION* down_session_get(struct sess_cache* cache, unsigned char * id, int idlen)
|
||||
{
|
||||
SSL_SESSION* sess=NULL;
|
||||
void* no_use=NULL;
|
||||
long cb_ret=0;
|
||||
assert(cache->served_for==CONN_DIR_DOWNSTREAM);
|
||||
no_use=MESA_htable_search_cb(cache->hash, id, (unsigned int)idlen, sess_cache_get_cb, &sess,&cb_ret);
|
||||
if(cb_ret==1)
|
||||
{
|
||||
cache->hit_cnt++;
|
||||
return sess;
|
||||
}
|
||||
else
|
||||
{
|
||||
cache->miss_cnt++;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void down_session_del(struct sess_cache* cache, const SSL_SESSION* sess)
|
||||
{
|
||||
assert(cache->served_for==CONN_DIR_DOWNSTREAM);
|
||||
unsigned int len=0;
|
||||
const unsigned char* id = SSL_SESSION_get_id(sess, &len);
|
||||
int ret=MESA_htable_del(cache->hash, id, len, NULL);
|
||||
if(ret!=MESA_HTABLE_RET_OK)
|
||||
{
|
||||
cache->del_err++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
struct sess_cache* ssl_sess_cache_create(int slot_size, int expire_seconds, enum tfe_conn_dir served)
|
||||
{
|
||||
struct sess_cache* cache=ALLOC(struct sess_cache, 1);
|
||||
MESA_htable_handle htable=NULL;
|
||||
int ret=0,max_num=slot_size*4;
|
||||
htable=MESA_htable_born();
|
||||
value=0;//no print
|
||||
ret=MESA_htable_set_opt(htable, MHO_SCREEN_PRINT_CTRL, &(value), sizeof(value));
|
||||
value=1;//thread safe
|
||||
ret=MESA_htable_set_opt(htable, MHO_THREAD_SAFE, value, sizeof(value));
|
||||
assert(ret==0);
|
||||
value=16;
|
||||
ret=MESA_htable_set_opt(htable, MHO_MUTEX_NUM, value, sizeof(value));
|
||||
ret=MESA_htable_set_opt(htable, MHO_HASH_SLOT_SIZE, &(slot_size), sizeof(slot_size));
|
||||
ret=MESA_htable_set_opt(htable, MHO_HASH_MAX_ELEMENT_NUM, &(max_num), sizeof(max_num));
|
||||
ret=MESA_htable_set_opt(htable, MHO_EXPIRE_TIME, &(expire_seconds), sizeof(expire_seconds));
|
||||
value=HASH_ELIMINATE_ALGO_FIFO;
|
||||
ret=MESA_htable_set_opt(htable, MHO_ELIMIMINATE_TYPE, &(value), sizeof(value));
|
||||
ret=MESA_htable_set_opt(htable, MHO_CBFUN_DATA_FREE, ssl_sess_free_serialized, sizeof(ssl_sess_free_serialized));
|
||||
ret=MESA_htable_set_opt(htable, MHO_CBFUN_DATA_EXPIRE_NOTIFY, ssl_sess_varify_cb, sizeof(ssl_sess_varify_cb));
|
||||
assert(ret==0);
|
||||
ret=MESA_htable_mature(htable);
|
||||
assert(ret==0);
|
||||
cache->hash=htable;
|
||||
cache->served_for=served;
|
||||
return cache;
|
||||
}
|
||||
void ssl_sess_cache_destroy(struct sess_cache* cache)
|
||||
{
|
||||
MESA_htable_destroy(cache->hash, NULL);
|
||||
cache->hash=NULL;
|
||||
free(cache);
|
||||
return;
|
||||
}
|
||||
|
||||
15
platform/src/ssl_sess_cache.h
Normal file
15
platform/src/ssl_sess_cache.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include <sys/socket.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include <tfe_stream.h>
|
||||
|
||||
struct sess_cache;
|
||||
struct sess_cache* ssl_sess_cache_create(int slot_size, int expire_seconds, enum tfe_conn_dir served);
|
||||
void ssl_sess_cache_destroy(struct sess_cache* cache);
|
||||
|
||||
void up_session_set(struct sess_cache* cache, struct sockaddr * addr, socklen_t addr_len, const char* sni, SSL_SESSION * value)
|
||||
SSL_SESSION* up_session_get(struct sess_cache* cache, struct sockaddr *addr, socklen_t addr_len, const char* sni);
|
||||
void down_session_set(struct sess_cache* cache, const SSL_SESSION* sess);
|
||||
SSL_SESSION* down_session_get(struct sess_cache* cache, unsigned char * id, int idlen);
|
||||
|
||||
@@ -27,41 +27,53 @@
|
||||
#include <tfe_future.h>
|
||||
#include <stream.h>
|
||||
#include <cert.h>
|
||||
#include <ssl_sess_cache.h>
|
||||
#include <ssl.h>
|
||||
#include <MESA_htable.h>
|
||||
#define SSL_EX_DATA_IDX_SSLMGR 0
|
||||
#define MAX_NET_RETRIES 50
|
||||
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 sslcomp;
|
||||
int no_ssl2;
|
||||
int no_ssl3;
|
||||
int no_tls10;
|
||||
int no_tls11;
|
||||
int no_tls12;
|
||||
CONST_SSL_METHOD *(*sslmethod)(void); //Parameter of SSL_CTX_new
|
||||
int sslversion;
|
||||
char ssl_session_context[8];
|
||||
MESA_htable_handle down_sess_cache;
|
||||
MESA_htable_handle up_sess_cache;
|
||||
char* default_ciphers;
|
||||
int cache_slot_num;
|
||||
int sess_expire_seconds;
|
||||
struct sess_cache* down_sess_cache;
|
||||
struct sess_cache* up_sess_cache;
|
||||
char* default_ciphers[TFE_SYMBOL_MAX];
|
||||
DH * dh;
|
||||
char * ecdhcurve;
|
||||
char * crlurl;
|
||||
uint8_t SSL_MODE_RELEASE_BUFFERS;
|
||||
void *logger;
|
||||
};
|
||||
struct ssl_stream
|
||||
{
|
||||
enum tfe_conn_dir dir;
|
||||
SSL * ssl;
|
||||
struct ssl_mgr* ref_mgr;
|
||||
char* sni;
|
||||
};
|
||||
struct ssl_chello
|
||||
{
|
||||
//client hello
|
||||
int version;
|
||||
char* sni;
|
||||
char* cipher_suites;
|
||||
};
|
||||
|
||||
struct peek_client_hello_ctx
|
||||
{
|
||||
|
||||
unsigned char sni_peek_retries; /* max 64 SNI parse retries */
|
||||
struct event* ev;
|
||||
struct event_base* evbase;
|
||||
|
||||
};
|
||||
struct ssl_connect_origin_ctx
|
||||
{
|
||||
@@ -70,63 +82,85 @@ struct ssl_connect_origin_ctx
|
||||
struct sockaddr addr;
|
||||
socklen_t addrlen;
|
||||
};
|
||||
|
||||
struct ssl_mgr* init_ssl_manager(const char* ini_profile, const char* section)
|
||||
/*
|
||||
* SSL shutdown context.
|
||||
*/
|
||||
struct ssl_shutdown_ctx {
|
||||
struct ssl_stream* s_stream;
|
||||
struct event_base *evbase;
|
||||
struct event *ev;
|
||||
unsigned int retries;
|
||||
};
|
||||
static int parse_ssl_version(const char * version_str)
|
||||
{
|
||||
struct ssl_mgr* mgr=ALLOC(struct ssl_mgr*, 1);
|
||||
memcpy(mgr->ssl_session_context,"mesa-tfe",sizeof(mgr->ssl_session_context));
|
||||
return mgr;
|
||||
int sslversion=-1;
|
||||
assert(OPENSSL_VERSION_NUMBER >= 0x10100000L);
|
||||
/*
|
||||
* Support for SSLv2 and the corresponding SSLv2_method(),
|
||||
* SSLv2_server_method() and SSLv2_client_method() functions were
|
||||
* removed in OpenSSL 1.1.0.
|
||||
*/
|
||||
if (!strcmp(version_str, "ssl3"))
|
||||
{
|
||||
sslversion = SSL3_VERSION;
|
||||
}
|
||||
else if (!strcmp(version_str, "tls10") || !strcmp(version_str, "tls1"))
|
||||
{
|
||||
sslversion = TLS1_VERSION;
|
||||
}
|
||||
else if (!strcmp(version_str, "tls11"))
|
||||
{
|
||||
sslversion = TLS1_1_VERSION;
|
||||
}
|
||||
else if (!strcmp(version_str, "tls12"))
|
||||
{
|
||||
sslversion = TLS1_2_VERSION;
|
||||
}
|
||||
else
|
||||
{
|
||||
sslversion =-1;
|
||||
}
|
||||
return sslversion;
|
||||
}
|
||||
void destroy_ssl_manager(struct ssl_mgr* mgr)
|
||||
void ssl_manager_destroy(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)
|
||||
struct ssl_mgr* ssl_manager_init(const char* ini_profile, const char* section, void* logger)
|
||||
{
|
||||
|
||||
}
|
||||
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;
|
||||
struct ssl_mgr* mgr=ALLOC(struct ssl_mgr, 1);
|
||||
int ret=0,value=0;
|
||||
char version_str[TFE_SYMBOL_MAX];
|
||||
mgr->logger=logger;
|
||||
MESA_load_profile_string_def(ini_profile, section, "ssl_version", version_str, sizeof(version_str),"tls12");
|
||||
mgr->sslversion=parse_ssl_version(version_str);
|
||||
if(mgr->sslversion<0)
|
||||
{
|
||||
TFE_LOG_ERROR(logger, "Unsupported SSL/TLS protocol %s",version_str);
|
||||
goto error_out;
|
||||
}
|
||||
if (copy)
|
||||
return sess;
|
||||
SSL_SESSION_free(sess);
|
||||
return ((void*)-1);
|
||||
//tfe2a uses SSLv23_method, it was been deprecated and replaced with the TLS_method() in openssl 1.1.0.
|
||||
mgr->sslmethod=TLS_method;
|
||||
MESA_load_profile_int_def(ini_profile, section, "ssl_compression", &(mgr->sslcomp), 1);
|
||||
MESA_load_profile_int_def(ini_profile, section, "no_ssl2", &(mgr->no_ssl2), 1);
|
||||
MESA_load_profile_int_def(ini_profile, section, "no_ssl3", &(mgr->no_ssl3), 1);
|
||||
MESA_load_profile_int_def(ini_profile, section, "no_tls10", &(mgr->no_tls10), 1);
|
||||
MESA_load_profile_int_def(ini_profile, section, "no_tls11", &(mgr->no_tls11), 0);
|
||||
MESA_load_profile_int_def(ini_profile, section, "no_tls12", &(mgr->no_tls12), 0);
|
||||
MESA_load_profile_int_def(ini_profile, section, "session_cache_slot_num", &(mgr->cache_slot_num), 4*1024*1024);
|
||||
MESA_load_profile_int_def(ini_profile, section, "session_cache_slot_num", &(mgr->sess_expire_seconds), 30*60);
|
||||
|
||||
mgr->up_sess_cache=ssl_sess_cache_create(mgr->cache_slot_num, mgr->sess_expire_seconds,CONN_DIR_UPSTREAM);
|
||||
mgr->down_sess_cache=ssl_sess_cache_create(mgr->cache_slot_num, mgr->sess_expire_seconds,CONN_DIR_DOWNSTREAM);
|
||||
|
||||
memcpy(mgr->ssl_session_context,"mesa-tfe",sizeof(mgr->ssl_session_context));
|
||||
return mgr;
|
||||
error_out:
|
||||
ssl_manager_destroy(mgr);
|
||||
return NULL;
|
||||
}
|
||||
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)
|
||||
{
|
||||
@@ -211,7 +245,7 @@ static void peek_client_hello_cb(evutil_socket_t fd, short what, void * arg)
|
||||
reason="see no client hello";
|
||||
goto failed;
|
||||
}
|
||||
|
||||
|
||||
if(ctx->sni_peek_retries++ > MAX_NET_RETRIES)
|
||||
{
|
||||
TFE_LOG_ERROR("Peek failed due to too many retries\n");
|
||||
@@ -267,7 +301,7 @@ static SSL* upstream_ssl_create(struct ssl_mgr* mgr, const struct ssl_chello* ch
|
||||
|
||||
|
||||
sslctx = SSL_CTX_new(mgr->sslmethod());
|
||||
|
||||
|
||||
sslctx_set_opts(sslctx, mgr);
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
@@ -312,11 +346,6 @@ static SSL* upstream_ssl_create(struct ssl_mgr* mgr, const struct ssl_chello* ch
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -383,7 +412,7 @@ static void ssl_connect_origin_eventcb(struct bufferevent * bev, short events, v
|
||||
ssl_connect_origin_ctx_free(ctx);
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
void ssl_async_upstream_create(struct future* f, struct ssl_mgr* mgr, const struct ssl_chello* chello, evutil_socket_t fd, struct event_base *evbase)
|
||||
{
|
||||
struct promise* p=future_to_promise(future);
|
||||
@@ -394,6 +423,7 @@ void ssl_async_connect_origin(struct future* future, struct ssl_mgr* mgr, const
|
||||
ret=getpeername(fd, &(ctx->addr), &(ctx->addrlen));
|
||||
assert(ret==0);
|
||||
ctx->ssl=ALLOC(struct ssl_stream,1);
|
||||
ctx->ssl->ref_mgr=mgr;
|
||||
ctx->ssl->dir=CONN_DIR_UPSTREAM;
|
||||
ctx->ssl->sni=tfe_strdup(chello->sni);
|
||||
ctx->ssl->ssl=upstream_ssl_create(mgr, chello, &(ctx->addr), ctx->addrlen);
|
||||
@@ -403,194 +433,6 @@ void ssl_async_connect_origin(struct future* future, struct ssl_mgr* mgr, const
|
||||
bufferevent_setcb(ctx->bev, NULL, NULL, ssl_connect_origin_eventcb, p);
|
||||
bufferevent_enable(ctx->bev, EV_READ | EV_WRITE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump information on a cert 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 cert */
|
||||
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;
|
||||
|
||||
|
||||
/*
|
||||
@@ -601,7 +443,7 @@ out:
|
||||
* keep a pointer to the object (which we never do here).
|
||||
*/
|
||||
|
||||
static int
|
||||
static int
|
||||
ossl_sessnew_cb(SSL * ssl, SSL_SESSION * sess)
|
||||
{
|
||||
struct ssl_mgr* mgr=(struct ssl_mgr*) SSL_get_ex_data(ssl, SSL_EX_DATA_IDX_SSLMGR, mgr);
|
||||
@@ -625,9 +467,10 @@ pxy_ossl_sessnew_cb(SSL * ssl, SSL_SESSION * sess)
|
||||
* OpenSSL calls SSL_SESSION_free() after calling the callback;
|
||||
* we do not need to free the reference here.
|
||||
*/
|
||||
static void
|
||||
static void
|
||||
ossl_sessremove_cb(UNUSED SSL_CTX * sslctx, SSL_SESSION * sess)
|
||||
{
|
||||
struct ssl_mgr* mgr=(struct ssl_mgr*) SSL_get_ex_data(ssl, SSL_EX_DATA_IDX_SSLMGR, mgr);
|
||||
assert(mgr!=NULL);
|
||||
if (sess)
|
||||
{
|
||||
@@ -640,7 +483,7 @@ pxy_ossl_sessremove_cb(UNUSED SSL_CTX * sslctx, SSL_SESSION * sess)
|
||||
* Called by OpenSSL when a src SSL session is requested by the client.
|
||||
OPENSSL_VERSION_NUMBER >= 0x10100000L required.
|
||||
*/
|
||||
static SSL_SESSION *
|
||||
static SSL_SESSION *
|
||||
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);
|
||||
@@ -654,7 +497,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
|
||||
sslctx_set_opts(SSL_CTX * sslctx, struct ssl_mgr* mgr)
|
||||
{
|
||||
SSL_CTX_set_options(sslctx, SSL_OP_ALL);
|
||||
@@ -715,223 +558,6 @@ pxy_sslctx_setoptions(SSL_CTX * sslctx, struct ssl_mgr* mgr)
|
||||
SSL_CTX_set_cipher_list(sslctx, mgr->default_ciphers);
|
||||
}
|
||||
|
||||
|
||||
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 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 cert.
|
||||
* Returns NULL if no suitable cert 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 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
|
||||
* server supplies a cert 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 cert 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 "
|
||||
"cert:\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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
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.
|
||||
@@ -942,7 +568,7 @@ static SSL_CTX * down_sslctx_create(struct ssl_mgr* mgr, struct cert* crt)
|
||||
SSL_CTX * sslctx = SSL_CTX_new(mgr->sslmethod());
|
||||
if (!sslctx)
|
||||
return NULL;
|
||||
|
||||
|
||||
sslctx_set_opts(sslctx, mgr);
|
||||
|
||||
//TFE's OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
@@ -953,16 +579,13 @@ static SSL_CTX * down_sslctx_create(struct ssl_mgr* mgr, struct cert* crt)
|
||||
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_new_cb(sslctx, ossl_sessnew_cb);
|
||||
SSL_CTX_sess_set_remove_cb(sslctx, ossl_sessremove_cb);
|
||||
SSL_CTX_sess_set_get_cb(sslctx, ossl_sessget_cb);
|
||||
SSL_CTX_set_session_cache_mode(sslctx, SSL_SESS_CACHE_SERVER |
|
||||
SSL_SESS_CACHE_NO_INTERNAL);
|
||||
SSL_SESS_CACHE_NO_INTERNAL);
|
||||
SSL_CTX_set_session_id_context(sslctx, (void *)(mgr->ssl_session_context),
|
||||
sizeof(mgr->ssl_session_context));
|
||||
#endif /* USE_SSL_SESSION_ID_CONTEXT */
|
||||
sizeof(mgr->ssl_session_context));
|
||||
#ifndef OPENSSL_NO_DH
|
||||
if (mgr->dh)
|
||||
@@ -1000,12 +623,12 @@ static SSL_CTX * down_sslctx_create(struct ssl_mgr* mgr, struct cert* crt)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create new SSL context for the incoming connection, based on the original
|
||||
* destination SSL cert.
|
||||
/*
|
||||
* Create new SSL context for the incoming connection, based on the a designate cert
|
||||
* Returns NULL when openssl error.
|
||||
*/
|
||||
*/
|
||||
void ssl_async_downstream_create(struct future* f, struct ssl_mgr* mgr, struct ssl_chello* hello, evutil_socket_t fd, struct event_base *evbase)
|
||||
{
|
||||
struct cert* crt;
|
||||
struct ssl_stream* p=NULL;
|
||||
if(crt==NULL)
|
||||
@@ -1013,6 +636,7 @@ struct ssl_stream * ssl_downstream_create(struct ssl_mgr* mgr, struct ssl_chello
|
||||
goto error_out;
|
||||
}
|
||||
p=ALLOC(struct ssl_stream, 1);
|
||||
p->ref_mgr=mgr;
|
||||
p->dir=CONN_DIR_DOWNSTREAM;
|
||||
//could be optimized by store the sslctx.
|
||||
SSL_CTX * sslctx = down_sslctx_create(mgr,crt);
|
||||
@@ -1031,7 +655,7 @@ struct ssl_stream * ssl_downstream_create(struct ssl_mgr* mgr, struct ssl_chello
|
||||
}
|
||||
if(mgr->SSL_MODE_RELEASE_BUFFERS==1)
|
||||
{
|
||||
/* lower memory footprint for idle connections */
|
||||
/* 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);
|
||||
@@ -1040,11 +664,125 @@ error_out:
|
||||
free(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Cleanly shut down an SSL socket. Libevent currently has no support for
|
||||
* cleanly shutting down an SSL socket so we work around that by using a
|
||||
* low-level event. This works for recent versions of OpenSSL. OpenSSL
|
||||
* with the older SSL_shutdown() semantics, not exposing WANT_READ/WRITE
|
||||
* may or may not work.
|
||||
*/
|
||||
static struct ssl_shutdown_ctx * ssl_shutdown_ctx_new(struct ssl_stream* s_stream, struct event_base *evbase)
|
||||
{
|
||||
free(stream->sni);
|
||||
stream->sni=NULL;
|
||||
{
|
||||
struct ssl_shutdown_ctx *ctx = ALLOC(struct ssl_shutdown_ctx,1);
|
||||
ctx->evbase = evbase;
|
||||
ctx->s_stream = s_stream;
|
||||
ctx->ev = NULL;
|
||||
ctx->retries = 0;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void ssl_shutdown_ctx_free(struct ssl_shutdown_ctx *ctx)
|
||||
{
|
||||
free(ctx);
|
||||
}
|
||||
static void ssl_stream_free(struct ssl_stream* s_stream)
|
||||
{
|
||||
SSL_free(s_stream->ssl);
|
||||
s_stream->ssl=NULL;
|
||||
free(s_stream->sni);
|
||||
s_stream->sni=NULL;
|
||||
s_stream->ref_mgr=NULL
|
||||
free(s_stream);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* The shutdown socket event handler. This is either
|
||||
* scheduled as a timeout-only event, or as a fd read or
|
||||
* fd write event, depending on whether SSL_shutdown()
|
||||
* indicates it needs read or write on the socket.
|
||||
*/
|
||||
static void
|
||||
pxy_ssl_shutdown_cb(evutil_socket_t fd, UNUSED short what, void *arg)
|
||||
{
|
||||
struct ssl_shutdown_ctx *ctx = (struct ssl_shutdown_ctx *)arg;
|
||||
struct timeval retry_delay = {0, 100};
|
||||
void *logger=ctx->s_stream->ref_mgr->logger;
|
||||
short want = 0;
|
||||
int rv=0, sslerr=0;
|
||||
|
||||
if (ctx->ev) {
|
||||
event_free(ctx->ev);
|
||||
ctx->ev = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the new (post-2008) semantics for SSL_shutdown() on a
|
||||
* non-blocking socket. SSL_shutdown() returns -1 and WANT_READ
|
||||
* if the other end's close notify was not received yet, and
|
||||
* WANT_WRITE it could not write our own close notify.
|
||||
*
|
||||
* This is a good collection of recent and relevant documents:
|
||||
* http://bugs.python.org/issue8108
|
||||
*/
|
||||
rv = SSL_shutdown(ctx->s_stream->ssl);
|
||||
if (rv == 1)
|
||||
goto complete;
|
||||
if (rv != -1) {
|
||||
goto retry;
|
||||
}
|
||||
switch ((sslerr = SSL_get_error(ctx->s_stream->ssl, rv))) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
want = EV_READ;
|
||||
goto retry;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
want = EV_WRITE;
|
||||
goto retry;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
goto retry;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
case SSL_ERROR_SSL:
|
||||
goto complete;
|
||||
default:
|
||||
TFE_LOG_ERROR(logger, "Unhandled SSL_shutdown() "
|
||||
"error %i. Closing fd.\n", sslerr);
|
||||
goto complete;
|
||||
}
|
||||
goto complete;
|
||||
|
||||
retry:
|
||||
if (ctx->retries++ >= MAX_NET_RETRIES) {
|
||||
TFE_LOG_ERROR(logger,"Failed to shutdown SSL connection cleanly: "
|
||||
"Max retries reached. Closing fd.\n");
|
||||
goto complete;
|
||||
}
|
||||
ctx->ev = event_new(ctx->evbase, fd, want, pxy_ssl_shutdown_cb, ctx);
|
||||
if (ctx->ev) {
|
||||
event_add(ctx->ev, &retry_delay);
|
||||
return;
|
||||
}
|
||||
TFE_LOG_ERROR(logger,"Failed to shutdown SSL connection cleanly: "
|
||||
"Cannot create event. Closing fd.\n");
|
||||
|
||||
complete:
|
||||
ssl_stream_free(ctx->s_stream);
|
||||
evutil_closesocket(fd);
|
||||
ssl_shutdown_ctx_free(ctx);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanly shutdown an SSL session on file descriptor fd using low-level
|
||||
* file descriptor readiness events on event base evbase.
|
||||
* Guarantees that SSL and the corresponding SSL_CTX are freed and the
|
||||
* socket is closed, eventually, or in the case of fatal errors, immediately.
|
||||
*/
|
||||
void ssl_stream_free_and_close_fd(struct ssl_stream* s_stream, struct event_base *evbase, evutil_socket_t fd)
|
||||
{
|
||||
struct ssl_shutdown_ctx *sslshutctx=NULL;
|
||||
sslshutctx = ssl_shutdown_ctx_new(s_stream, evbase);
|
||||
pxy_ssl_shutdown_cb(fd, 0, sslshutctx);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user