From f4f7a623a5cdf34294249d39ac097ea0e4302b48 Mon Sep 17 00:00:00 2001 From: zhengchao Date: Tue, 14 May 2019 19:58:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=81=E8=AE=B8=E8=AE=BE=E7=BD=AE=E8=AF=81?= =?UTF-8?q?=E4=B9=A6=E6=A0=A1=E9=AA=8C=E9=80=89=E9=A1=B9=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/include/tfe_stream.h | 6 ++ common/include/tfe_types.h | 15 +++ platform/include/internal/ssl_stream.h | 14 +++ .../internal/ssl_trusted_cert_storage.h | 10 +- platform/src/ssl_stream.cpp | 76 ++++++++----- platform/src/ssl_trusted_cert_storage.cpp | 101 +++++++++++++++--- 6 files changed, 178 insertions(+), 44 deletions(-) diff --git a/common/include/tfe_stream.h b/common/include/tfe_stream.h index 4f40bda..f324145 100644 --- a/common/include/tfe_stream.h +++ b/common/include/tfe_stream.h @@ -68,6 +68,12 @@ enum tfe_stream_close_reason int tfe_stream_action_set_opt(const struct tfe_stream * stream, enum tfe_stream_action_opt type, void * value, size_t size); +enum tfe_stream_opt_level +{ + STREAM_OPT_LEVEL_TCP, + STREAM_OPT_LEVEL_SSL //see tfe_types.h enum SSL_STREAM_OPT +}; +int tfe_stream_set_opt(struct tfe_stream * stream, enum tfe_stream_opt_level level, int type, const void* value, size_t size); /* @return 0 if successful, or -1 if an error occurred */ diff --git a/common/include/tfe_types.h b/common/include/tfe_types.h index 723a94f..167642c 100644 --- a/common/include/tfe_types.h +++ b/common/include/tfe_types.h @@ -159,5 +159,20 @@ char* tfe_string_addr_create_by_fd(int fd, enum tfe_conn_dir dir); char * tfe_stream_addr_to_str(const struct tfe_stream_addr * addr); int tfe_stream_addr_str_split(char* addr_str, const char** sip, const char** sport, const char** dip, const char** dport); +enum SSL_STREAM_OPT +{ + SSL_STREAM_OPT_BYPASS_EV_CERT, //VALUE is an interger, SIZE=sizeof(int). 1:BYPASS, 0:INTERCEPT. DEFAULT:0. + SSL_STREAM_OPT_BYPASS_CT_CERT, //VALUE is an interger, SIZE=sizeof(int). 1:BYPASS, 0:INTERCEPT. DEFAULT:0. + SSL_STREAM_OPT_BYPASS_MUTUAL_AUTH, //VALUE is an interger, SIZE=sizeof(int). 1:BYPASS, 0:BLOCKED. DEFAULT:1. + SSL_STREAM_OPT_BYPASS_CERT_PINNING, //VALUE is an interger, SIZE=sizeof(int). 1:BYPASS, 0:BLOCKED. DEFAULT:1. + SSL_STREAM_OPT_VERIFY_SELF_SIGNED, //VALUE is an interger, SIZE=sizeof(int). 1:ON, 0:OFF. DEFAULT:1. + SSL_STREAM_OPT_VERIFY_COMMON_NAME, //VALUE is an interger, SIZE=sizeof(int). 1:ON, 0:OFF. DEFAULT:1. + SSL_STREAM_OPT_VERIFY_ISSUER, //VALUE is an interger, SIZE=sizeof(int). 1:ON, 0:OFF. DEFAULT:1. + SSL_STREAM_OPT_VERIFY_EXPIRY_DATE, //VALUE is an interger, SIZE=sizeof(int). 1:ON, 0:OFF. DEFAULT:1. + SST_STREAM_OPT_VERIFY_FAIL_ACTION, //VALUE is an interger, SIZE=sizeof(int). 1:PASSTHROUGH, 0:BLOCK. DEFAULT:1. + SSL_STREAM_OPT_PROTOCOL_MIN_VERSION, + SSL_STREAM_OPT_PROTOCOL_MAX_VERSION +}; + diff --git a/platform/include/internal/ssl_stream.h b/platform/include/internal/ssl_stream.h index c0a8e5b..7b29d8c 100644 --- a/platform/include/internal/ssl_stream.h +++ b/platform/include/internal/ssl_stream.h @@ -8,11 +8,23 @@ struct ssl_stream; struct ssl_mgr; +typedef void ssl_stream_new_cb(struct ssl_stream *, void* u_para); +typedef void ssl_stream_free_cb(struct ssl_stream *, void* u_para); + struct ssl_mgr * ssl_manager_init(const char * ini_profile, const char * section, struct event_base * ev_base_gc, void * logger); +//, ssl_stream_new_cb* new_func, ssl_stream_free_cb* free_func, void* u_para); void ssl_manager_destroy(struct ssl_mgr * mgr); void ssl_stream_log_error(struct bufferevent * bev, enum tfe_conn_dir dir, struct ssl_mgr* mgr); +enum ssl_stream_action +{ + SSL_ACTION_PASSTHROUGH, + SSL_ACTION_INTERCEPT, + SSL_ACTION_SHUTDOWN +}; + +enum ssl_stream_action ssl_upstream_create_result_release_action(future_result_t * result); 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 * f, struct ssl_mgr * mgr, evutil_socket_t fd_upstream, @@ -31,4 +43,6 @@ int ssl_manager_add_crl(struct ssl_mgr* mgr, const char* pem_file); int ssl_manager_del_crl(struct ssl_mgr* mgr, const char* pem_file); void ssl_manager_reset_trust_ca(struct ssl_mgr* mgr); +//s_stream must be upstream. +int ssl_stream_set_opt(struct ssl_stream *s_stream, enum SSL_STREAM_OPT type, const void* value, size_t size); diff --git a/platform/include/internal/ssl_trusted_cert_storage.h b/platform/include/internal/ssl_trusted_cert_storage.h index 9bb2986..8ad4598 100644 --- a/platform/include/internal/ssl_trusted_cert_storage.h +++ b/platform/include/internal/ssl_trusted_cert_storage.h @@ -13,8 +13,14 @@ struct cert_store_param struct ssl_trusted_cert_storage; struct ssl_trusted_cert_storage* ssl_trusted_cert_storage_create(const char* pem_bundle, const char* pem_dir, struct cert_store_param* param); void ssl_trusted_cert_storage_destroy(struct ssl_trusted_cert_storage* storage); - -int ssl_trusted_cert_storage_verify_conn(struct ssl_trusted_cert_storage* storage, const SSL * ssl, char* reason, size_t n_reason); +struct cert_verify_param +{ + char no_verify_self_signed; + char no_verify_cn; + char no_verify_issuer; + char no_verify_expiry_date; +}; +int ssl_trusted_cert_storage_verify_conn(struct ssl_trusted_cert_storage* storage, SSL * ssl, const char* hostname, struct cert_verify_param* param, char* reason, size_t n_reason); int ssl_trusted_cert_storage_add(struct ssl_trusted_cert_storage* storage, enum ssl_X509_obj_type type, const char* filename); int ssl_trusted_cert_storage_del(struct ssl_trusted_cert_storage* storage, enum ssl_X509_obj_type type, const char* filename); void ssl_trusted_cert_storage_reset(struct ssl_trusted_cert_storage* storage); diff --git a/platform/src/ssl_stream.cpp b/platform/src/ssl_stream.cpp index 934d001..d6bf71f 100644 --- a/platform/src/ssl_stream.cpp +++ b/platform/src/ssl_stream.cpp @@ -41,7 +41,7 @@ #include #include -int SSL_EX_DATA_IDX_SSLMGR; +static int SSL_EX_DATA_IDX_SSLMGR; int SSL_PEER_CERT_VERIFY_PASSED=1; int SSL_PEER_CERT_VERIFY_FAILED=0; @@ -165,7 +165,27 @@ struct __ssl_stream_debug { evutil_socket_t fd; }; - +struct ssl_bypass +{ + char bypass_ev_cert; + char bypass_ct_cert; + char bypass_mutual_auth; + char bypass_cert_pinning; +}; +struct ssl_upstream_parts +{ + + struct cert_verify_param verify_param; + char verify_failed_action; + + struct ssl_bypass bypass_condition; + struct ssl_chello * client_hello; + int is_server_cert_verify_passed; +}; +struct ssl_downstream_parts +{ + struct keyring * keyring; +}; struct ssl_stream { enum tfe_conn_dir dir; @@ -173,12 +193,11 @@ struct ssl_stream struct ssl_mgr * mgr; union { - struct ssl_chello * client_hello; //dir=upstream, a little weird, which send by downstream client. - struct keyring * keyring; //dir=downstream. + struct ssl_upstream_parts up_parts; + struct ssl_downstream_parts down_parts; }; - const unsigned char* alpn_selected; + const unsigned char* alpn_selected; //reference to SSL_ALPN_HTTP_2/SSL_ALPN_HTTP_1_1 struct __ssl_stream_debug _do_not_use; - int is_peer_cert_verify_passed; }; @@ -395,12 +414,12 @@ struct ssl_stream * ssl_stream_new(struct ssl_mgr * mgr, evutil_socket_t fd, enu case CONN_DIR_DOWNSTREAM: ATOMIC_INC(&(s_stream->mgr->stat_val[SSL_DOWN_NEW])); s_stream->ssl = downstream_ssl_create(mgr, kyr, selected_alpn); - s_stream->keyring = kyr; + s_stream->down_parts.keyring = kyr; break; case CONN_DIR_UPSTREAM: ATOMIC_INC(&(s_stream->mgr->stat_val[SSL_UP_NEW])); s_stream->ssl = upstream_ssl_create(mgr, client_hello, fd); - s_stream->client_hello = client_hello; + s_stream->up_parts.client_hello = client_hello; break; default: assert(0); } @@ -414,18 +433,18 @@ static void ssl_stream_free(struct ssl_stream * s_stream) switch (s_stream->dir) { case CONN_DIR_DOWNSTREAM: - if (s_stream->keyring != NULL) + if (s_stream->down_parts.keyring != NULL) { - key_keeper_free_keyring(s_stream->keyring); - s_stream->keyring = NULL; + key_keeper_free_keyring(s_stream->down_parts.keyring); + s_stream->down_parts.keyring = NULL; } ATOMIC_INC(&(s_stream->mgr->stat_val[SSL_DOWN_CLOSED])); break; case CONN_DIR_UPSTREAM: - if (s_stream->client_hello != NULL) + if (s_stream->up_parts.client_hello != NULL) { - ssl_chello_free(s_stream->client_hello); - s_stream->client_hello = NULL; + ssl_chello_free(s_stream->up_parts.client_hello); + s_stream->up_parts.client_hello = NULL; } ATOMIC_INC(&(s_stream->mgr->stat_val[SSL_UP_CLOSED])); break; @@ -889,7 +908,7 @@ struct bufferevent * ssl_upstream_create_result_release_bev(future_result_t * re const char* ssl_stream_dump_info(struct ssl_stream *stream, char* buffer, size_t sz) { snprintf(buffer, sz, "%s:%s", SSL_get_version(stream->ssl), - stream->dir==CONN_DIR_UPSTREAM ? stream->client_hello->sni:NULL); + stream->dir==CONN_DIR_UPSTREAM ? stream->up_parts.client_hello->sni:NULL); return buffer; } void ssl_stream_log_error(struct bufferevent * bev, enum tfe_conn_dir dir, struct ssl_mgr* mgr) @@ -1010,10 +1029,12 @@ static void ssl_server_connected_eventcb(struct bufferevent * bev, short events, struct ssl_connect_server_ctx * ctx = (struct ssl_connect_server_ctx *) promise_dettach_ctx(p); struct ssl_stream * s_stream = ctx->s_stream; + assert(s_stream->dir==CONN_DIR_UPSTREAM); + struct ssl_upstream_parts* s_upstream=&(s_stream->up_parts); struct ssl_mgr* mgr=s_stream->mgr; SSL_SESSION * ssl_sess = NULL; char error_str[TFE_STRING_MAX]; - const char* sni=s_stream->client_hello->sni?s_stream->client_hello->sni:"null"; + const char* sni=s_upstream->client_hello->sni?s_upstream->client_hello->sni:"null"; long jiffies_ms; char* addr_string=NULL; if (events & BEV_EVENT_ERROR) @@ -1053,14 +1074,14 @@ static void ssl_server_connected_eventcb(struct bufferevent * bev, short events, { if(mgr->no_cert_verify) { - s_stream->is_peer_cert_verify_passed=1; + s_stream->up_parts.is_server_cert_verify_passed=1; } else { - s_stream->is_peer_cert_verify_passed = ssl_trusted_cert_storage_verify_conn(s_stream->mgr->trust_CA_store, - s_stream->ssl, error_str, sizeof(error_str)); + s_stream->up_parts.is_server_cert_verify_passed = ssl_trusted_cert_storage_verify_conn(s_stream->mgr->trust_CA_store, + s_stream->ssl, s_stream->up_parts.client_hello->sni, &(s_stream->up_parts.verify_param), error_str, sizeof(error_str)); } - if(s_stream->is_peer_cert_verify_passed) + if(s_stream->up_parts.is_server_cert_verify_passed) { if(!mgr->no_sesscache) { @@ -1068,21 +1089,21 @@ static void ssl_server_connected_eventcb(struct bufferevent * bev, short events, //The reference count of the SSL_SESSION is not incremented, so no need to free. ssl_sess = SSL_get0_session(s_stream->ssl); up_session_set(mgr->up_sess_cache, (struct sockaddr *)&(ctx->addr), - ctx->addrlen, s_stream->client_hello->sni, ssl_sess); + ctx->addrlen, s_upstream->client_hello->sni, ssl_sess); } } else { ATOMIC_INC(&(mgr->stat_val[SSL_FAKE_CRT])); char* addr_str=tfe_string_addr_create_by_fd(ctx->fd_upstream, CONN_DIR_UPSTREAM); - TFE_LOG_INFO(mgr->logger, "Fake Cert %s %s : %s", addr_str, ctx->s_stream->client_hello->sni, error_str); + TFE_LOG_INFO(mgr->logger, "Fake Cert %s %s : %s", addr_str, s_upstream->client_hello->sni, error_str); free(addr_str); } } else { - //Do not perform cert check on reused session. - s_stream->is_peer_cert_verify_passed=1; + //Do not perform cert verification on reused session. + s_upstream->is_server_cert_verify_passed=1; } if(mgr->log_master_key) { @@ -1523,9 +1544,10 @@ static void ssl_client_connected_eventcb(struct bufferevent * bev, short events, struct ssl_connect_client_ctx * ctx = (struct ssl_connect_client_ctx *) promise_dettach_ctx(p); struct ssl_stream * s_stream = ctx->downstream; + struct ssl_upstream_parts* s_upstream= &(ctx->origin_ssl->up_parts); struct ssl_mgr* mgr=s_stream->mgr; char* addr_string=NULL; - const char* sni=ctx->origin_ssl->client_hello->sni?ctx->origin_ssl->client_hello->sni:"null"; + const char* sni=s_upstream->client_hello->sni?s_upstream->client_hello->sni:"null"; char error_str[TFE_STRING_MAX]={0}; long jiffies_ms=0; if (events & BEV_EVENT_ERROR) @@ -1628,14 +1650,14 @@ void ssl_async_downstream_create(struct future * f, struct ssl_mgr * mgr, struct { ctx->origin_ssl = upstream; ctx->origin_crt = SSL_get_peer_certificate(upstream->ssl); - sni=upstream->client_hello->sni; + sni=upstream->up_parts.client_hello->sni; } struct promise * p = future_to_promise(f); promise_set_ctx(p, ctx, wrap_ssl_connect_client_ctx_free); ctx->f_ask_keyring = future_create("ask_kyr",ask_keyring_on_succ, ask_keyring_on_fail, p); - ctx->is_origin_crt_verify_passed = upstream->is_peer_cert_verify_passed; + ctx->is_origin_crt_verify_passed = upstream->up_parts.is_server_cert_verify_passed; key_keeper_async_ask(ctx->f_ask_keyring, mgr->key_keeper, sni, keyring_id, ctx->origin_crt, ctx->is_origin_crt_verify_passed, evbase, dnsbase); return; diff --git a/platform/src/ssl_trusted_cert_storage.cpp b/platform/src/ssl_trusted_cert_storage.cpp index 33e88fb..dc5bf57 100644 --- a/platform/src/ssl_trusted_cert_storage.cpp +++ b/platform/src/ssl_trusted_cert_storage.cpp @@ -11,6 +11,7 @@ #include #include #include +static int SSL_EX_DATA_IDX_VERIFY_PARAM; struct ssl_X509_object { @@ -183,7 +184,7 @@ struct ssl_trusted_cert_storage* ssl_trusted_cert_storage_create(const char* pem storage->pem_dir=tfe_strdup(pem_dir); storage->hash_table=_create_mesa_htable(); pthread_rwlock_init(&(storage->rwlock), NULL); - + SSL_EX_DATA_IDX_VERIFY_PARAM = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); return storage; @@ -271,9 +272,59 @@ void ssl_trusted_cert_storage_reset(struct ssl_trusted_cert_storage* storage) pthread_rwlock_unlock(&(storage->rwlock)); return; } -int ssl_trusted_cert_storage_verify_conn(struct ssl_trusted_cert_storage* storage, const SSL * ssl, char* reason, size_t n_reason) +static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) + { + int err, ret=0; + SSL* ssl; + struct cert_verify_param* param=NULL; + + if(preverify_ok) + { + return 1; + } + err = X509_STORE_CTX_get_error(ctx); + /* + * Retrieve the pointer to the SSL of the connection currently treated + * and the application specific data stored into the SSL object. + */ + ssl = (SSL*)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + param = (struct cert_verify_param*)SSL_get_ex_data(ssl, SSL_EX_DATA_IDX_VERIFY_PARAM); + switch(err) + { + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + if(param->no_verify_issuer) + { + ret=1; + } + break; + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + if(param->no_verify_self_signed) + { + ret=1; + } + break; + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_CERT_HAS_EXPIRED: + if(param->no_verify_expiry_date) + { + ret=1; + } + break; + case X509_V_ERR_UNABLE_TO_GET_CRL: + case X509_V_ERR_DIFFERENT_CRL_SCOPE: + case X509_V_ERR_CRL_HAS_EXPIRED: + ret=1; + break; + default: + ret=0; + break; + } + return ret; + } + +int ssl_trusted_cert_storage_verify_conn(struct ssl_trusted_cert_storage* storage, SSL * ssl, const char* hostname, struct cert_verify_param* param, char* reason, size_t n_reason) { - int ret = 0, err_code=0; + int ret = 0, err_code=0, host_matched=1; char *subj=NULL, *issuer=NULL; STACK_OF(X509) * cert_chain = SSL_get_peer_cert_chain(ssl); if (cert_chain == NULL) @@ -281,27 +332,46 @@ int ssl_trusted_cert_storage_verify_conn(struct ssl_trusted_cert_storage* storag // The peer certificate chain is not necessarily available after reusing a session, in which case a NULL pointer is returned. return 1; } - X509_STORE_CTX * ctx = X509_STORE_CTX_new(); X509 * cert = sk_X509_value(cert_chain, 0); - + SSL_set_verify(ssl, SSL_VERIFY_PEER, verify_callback); + SSL_set_ex_data(ssl, SSL_EX_DATA_IDX_VERIFY_PARAM, param); + + X509_STORE_CTX * ctx = X509_STORE_CTX_new(); pthread_rwlock_rdlock(&(storage->rwlock)); - ret = X509_STORE_CTX_init(ctx, storage->effective_store, cert, cert_chain); assert(ret == 1); - + + if(!param->no_verify_cn&&!hostname) + { + host_matched=X509_check_host(cert, hostname, strlen(hostname), 0, NULL); + } + else + { + host_matched=1; + } + //If a complete chain can be built and validated this function returns 1, otherwise it return zero or negtive code. ret = X509_verify_cert(ctx); err_code=X509_STORE_CTX_get_error(ctx); - if(ret!=1 && err_code!=X509_V_ERR_UNABLE_TO_GET_CRL - && err_code!=X509_V_ERR_DIFFERENT_CRL_SCOPE - && err_code!=X509_V_ERR_CRL_HAS_EXPIRED) + + if(ret!=1||host_matched!=1) { subj=ssl_x509_subject(cert); issuer=ssl_x509_issuer(cert); - snprintf(reason, n_reason, "%s : subject - %s issuer - %s" - , X509_verify_cert_error_string(err_code) - , subj - , issuer); + if(host_matched!=1) + { + snprintf(reason, n_reason, "%s : subject - %s issuer - %s", + "hostname not matched", + subj, + issuer); + } + else + { + snprintf(reason, n_reason, "%s : subject - %s issuer - %s", + X509_verify_cert_error_string(err_code), + subj, + issuer); + } free(subj); free(issuer); ret=0; @@ -310,8 +380,9 @@ int ssl_trusted_cert_storage_verify_conn(struct ssl_trusted_cert_storage* storag { ret=1; } + X509_STORE_CTX_free(ctx); - pthread_rwlock_unlock(&(storage->rwlock)); + pthread_rwlock_unlock(&(storage->rwlock)); return ret; }