ssl_stream集成新的client hello解析模块。
This commit is contained in:
@@ -70,7 +70,7 @@ struct ssl_mgr
|
||||
unsigned int no_tls12;
|
||||
|
||||
CONST_SSL_METHOD * (* sslmethod)(void); //Parameter of SSL_CTX_new
|
||||
int sslversion;
|
||||
int ssl_min_version, ssl_max_version;
|
||||
char ssl_session_context[8];
|
||||
|
||||
unsigned int cache_slots;
|
||||
@@ -116,17 +116,10 @@ struct ssl_stream
|
||||
struct __ssl_stream_debug _do_not_use;
|
||||
};
|
||||
|
||||
struct ssl_chello
|
||||
{
|
||||
//client hello
|
||||
int version;
|
||||
char * sni;
|
||||
char * cipher_suites;
|
||||
};
|
||||
|
||||
struct peek_client_hello_ctx
|
||||
{
|
||||
struct ssl_chello chello;
|
||||
struct ssl_chello* chello;
|
||||
unsigned char sni_peek_retries; /* max 64 SNI parse retries */
|
||||
struct event * ev;
|
||||
struct event_base * evbase;
|
||||
@@ -203,32 +196,10 @@ static void sslctx_set_opts(SSL_CTX * sslctx, struct ssl_mgr * mgr);
|
||||
|
||||
struct ssl_chello * ssl_peek_result_release_chello(future_result_t * result)
|
||||
{
|
||||
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);
|
||||
copy->cipher_suites = tfe_strdup(p->cipher_suites);
|
||||
copy->version = p->version;
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
void ssl_free_chello(struct ssl_chello * p)
|
||||
{
|
||||
if (p == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
free(p->sni);
|
||||
p->sni = NULL;
|
||||
free(p->cipher_suites);
|
||||
p->cipher_suites = NULL;
|
||||
free(p);
|
||||
return;
|
||||
struct peek_client_hello_ctx* ctx= (struct peek_client_hello_ctx*) result;
|
||||
struct ssl_chello * p = ctx->chello;
|
||||
ctx->chello=NULL;
|
||||
return p;
|
||||
}
|
||||
|
||||
struct ssl_stream * ssl_stream_new(struct ssl_mgr * mgr, evutil_socket_t fd, enum tfe_conn_dir dir,
|
||||
@@ -270,8 +241,8 @@ static void ssl_stream_free(struct ssl_stream * s_stream)
|
||||
break;
|
||||
case CONN_DIR_UPSTREAM:
|
||||
if (s_stream->client_hello != NULL)
|
||||
{
|
||||
ssl_free_chello(s_stream->client_hello);
|
||||
{
|
||||
ssl_chello_free(s_stream->client_hello);
|
||||
s_stream->client_hello = NULL;
|
||||
}
|
||||
break;
|
||||
@@ -369,10 +340,12 @@ struct ssl_mgr * ssl_manager_init(const char * ini_profile, const char * section
|
||||
mgr->logger = logger;
|
||||
mgr->ev_base_gc=ev_base_gc;
|
||||
mgr->fs_handle=fs;
|
||||
MESA_load_profile_string_def(ini_profile, section, "ssl_version", version_str, sizeof(version_str), "tls12");
|
||||
mgr->sslversion = sslver_str2num(version_str);
|
||||
|
||||
if (mgr->sslversion < 0)
|
||||
MESA_load_profile_string_def(ini_profile, section, "ssl_min_version", version_str, sizeof(version_str), "ssl3");
|
||||
mgr->ssl_min_version = sslver_str2num(version_str);
|
||||
MESA_load_profile_string_def(ini_profile, section, "ssl_max_version", version_str, sizeof(version_str), "tls12");
|
||||
mgr->ssl_max_version = sslver_str2num(version_str);
|
||||
|
||||
if (mgr->ssl_min_version < 0)
|
||||
{
|
||||
TFE_LOG_ERROR(logger, "Unsupported SSL/TLS protocol %s", version_str);
|
||||
goto error_out;
|
||||
@@ -479,10 +452,11 @@ void peek_client_hello_ctx_free(struct peek_client_hello_ctx * _ctx)
|
||||
{
|
||||
event_free(_ctx->ev);
|
||||
_ctx->ev = NULL;
|
||||
free(_ctx->chello.sni);
|
||||
_ctx->chello.sni = NULL;
|
||||
free(_ctx->chello.cipher_suites);
|
||||
_ctx->chello.cipher_suites = NULL;
|
||||
if(_ctx->chello!=NULL)
|
||||
{
|
||||
ssl_chello_free(_ctx->chello);
|
||||
_ctx->chello=NULL;
|
||||
}
|
||||
free(_ctx);
|
||||
}
|
||||
|
||||
@@ -497,11 +471,11 @@ static void peek_client_hello_cb(evutil_socket_t fd, short what, void * arg)
|
||||
struct promise * promise = (struct promise *) arg;
|
||||
//use promise_get_ctx instead of promise_dettach_ctx for try more times.
|
||||
struct peek_client_hello_ctx * ctx = (struct peek_client_hello_ctx *) promise_get_ctx(promise);
|
||||
enum chello_parse_result chello_status=CHELLO_PARSE_INVALID_FORMAT;
|
||||
struct ssl_chello* chello=NULL;
|
||||
const char * reason = NULL;
|
||||
unsigned char buf[1024];
|
||||
unsigned char buf[2048];
|
||||
ssize_t n = 0;
|
||||
const unsigned char * chello = NULL;
|
||||
int rv = 0;
|
||||
|
||||
n = recv(fd, buf, sizeof(buf), MSG_PEEK);
|
||||
if (n == -1)
|
||||
@@ -515,49 +489,57 @@ static void peek_client_hello_cb(evutil_socket_t fd, short what, void * arg)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
//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, &(ctx->chello.sni));
|
||||
|
||||
if (rv == 0)
|
||||
chello=ssl_chello_parse(buf,n, &chello_status);
|
||||
switch(chello_status)
|
||||
{
|
||||
promise_dettach_ctx(promise);
|
||||
promise_success(promise, &(ctx->chello));
|
||||
peek_client_hello_ctx_free(ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!chello)
|
||||
case CHELLO_PARSE_SUCCESS:
|
||||
{
|
||||
promise_dettach_ctx(promise);
|
||||
ctx->chello=chello;
|
||||
promise_success(promise, ctx);
|
||||
peek_client_hello_ctx_free(ctx);
|
||||
break;
|
||||
}
|
||||
case CHELLO_PARSE_NOT_ENOUGH_BUFF:
|
||||
{
|
||||
ssl_chello_free(chello);
|
||||
chello=NULL;
|
||||
if (ctx->sni_peek_retries++ > MAX_NET_RETRIES)
|
||||
{
|
||||
TFE_LOG_ERROR(ctx->logger, "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
|
||||
* reading now. We use 25 * 0.2 s = 5 s timeout. */
|
||||
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);
|
||||
break;
|
||||
}
|
||||
case CHELLO_PARSE_INVALID_FORMAT:
|
||||
{
|
||||
ssl_chello_free(chello);
|
||||
chello=NULL;
|
||||
TFE_LOG_ERROR(ctx->logger,
|
||||
"Peeking did not yield a (truncated) ClientHello message, aborting connection\n");
|
||||
reason = "see no client hello";
|
||||
goto failed;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctx->sni_peek_retries++ > MAX_NET_RETRIES)
|
||||
{
|
||||
TFE_LOG_ERROR(ctx->logger, "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
|
||||
* reading now. We use 25 * 0.2 s = 5 s timeout. */
|
||||
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);
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
failed:
|
||||
@@ -589,14 +571,28 @@ static SSL * upstream_ssl_create(struct ssl_mgr * mgr, const struct ssl_chello *
|
||||
SSL_CTX * sslctx = NULL;
|
||||
SSL * ssl = NULL;
|
||||
SSL_SESSION * sess = NULL;
|
||||
|
||||
|
||||
sslctx = SSL_CTX_new(mgr->sslmethod());
|
||||
sslctx_set_opts(sslctx, mgr);
|
||||
|
||||
if (mgr->sslversion)
|
||||
int ret=0;
|
||||
if(chello->cipher_suites!=NULL)
|
||||
{
|
||||
if (SSL_CTX_set_min_proto_version(sslctx, chello->version) == 0 ||
|
||||
SSL_CTX_set_max_proto_version(sslctx, chello->version) == 0)
|
||||
//SSL_CTX_set_cipher_list() and SSL_set_cipher_list() return 1 if any cipher could be selected and 0 on complete failure.
|
||||
ret=SSL_CTX_set_cipher_list(sslctx, chello->cipher_suites);
|
||||
if(ret==0)
|
||||
{
|
||||
TFE_LOG_ERROR(mgr->logger, "SSL_CTX_set_cipher_list %s failed.", chello->cipher_suites);
|
||||
SSL_CTX_set_cipher_list(sslctx, mgr->default_ciphers);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret=SSL_CTX_set_cipher_list(sslctx, mgr->default_ciphers);
|
||||
}
|
||||
if (mgr->ssl_min_version)
|
||||
{
|
||||
if (SSL_CTX_set_min_proto_version(sslctx, MAX(chello->min_version.ossl_format, mgr->ssl_min_version)) == 0 ||
|
||||
SSL_CTX_set_max_proto_version(sslctx, MIN(chello->max_version.ossl_format, mgr->ssl_max_version)) == 0)
|
||||
{
|
||||
SSL_CTX_free(sslctx);
|
||||
return NULL;
|
||||
@@ -623,7 +619,7 @@ static SSL * upstream_ssl_create(struct ssl_mgr * mgr, const struct ssl_chello *
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen = sizeof(struct sockaddr_storage);
|
||||
|
||||
int ret = getpeername(fd, (struct sockaddr *) (&addr), &addrlen);
|
||||
ret = getpeername(fd, (struct sockaddr *) (&addr), &addrlen);
|
||||
assert(ret == 0);
|
||||
|
||||
/* session resuming based on remote endpoint address and port */
|
||||
@@ -1004,7 +1000,6 @@ static void sslctx_set_opts(SSL_CTX * sslctx, struct ssl_mgr * mgr)
|
||||
|
||||
#endif /* SSL_OP_NO_COMPRESSION */
|
||||
|
||||
SSL_CTX_set_cipher_list(sslctx, mgr->default_ciphers);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1019,12 +1014,13 @@ static SSL * downstream_ssl_create(struct ssl_mgr * mgr, struct keyring * crt)
|
||||
SSL * ssl = NULL;
|
||||
int ret = 0;
|
||||
sslctx_set_opts(sslctx, mgr);
|
||||
SSL_CTX_set_cipher_list(sslctx, mgr->default_ciphers);
|
||||
|
||||
//TFE's OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
if (mgr->sslversion)
|
||||
if (mgr->ssl_min_version)
|
||||
{
|
||||
if (SSL_CTX_set_min_proto_version(sslctx, mgr->sslversion) == 0 ||
|
||||
SSL_CTX_set_max_proto_version(sslctx, mgr->sslversion) == 0)
|
||||
if (SSL_CTX_set_min_proto_version(sslctx, mgr->ssl_min_version) == 0 ||
|
||||
SSL_CTX_set_max_proto_version(sslctx, mgr->ssl_min_version) == 0)
|
||||
{
|
||||
SSL_CTX_free(sslctx);
|
||||
return NULL;
|
||||
|
||||
Reference in New Issue
Block a user