diff --git a/platform/include/internal/proxy.h b/platform/include/internal/proxy.h index f37ef8a..b9195c5 100644 --- a/platform/include/internal/proxy.h +++ b/platform/include/internal/proxy.h @@ -91,6 +91,7 @@ struct tfe_proxy struct tfe_plugin * modules; struct ssl_mgr * ssl_mgr_handler; + struct ssl_policy_enforcer* ssl_ply_enforcer; struct key_keeper * key_keeper_handler; struct kni_acceptor * kni_acceptor_handler; diff --git a/platform/src/ssl_policy.h b/platform/include/internal/ssl_policy.h similarity index 61% rename from platform/src/ssl_policy.h rename to platform/include/internal/ssl_policy.h index 9428323..a380555 100644 --- a/platform/src/ssl_policy.h +++ b/platform/include/internal/ssl_policy.h @@ -1,6 +1,9 @@ #pragma once #include +#include + struct ssl_policy_enforcer; struct ssl_policy_enforcer* ssl_policy_enforcer_create(void); +void ssl_policy_enforcer_init(struct ssl_policy_enforcer* enforcer, Maat_feather_t maat, void* logger); enum ssl_stream_action ssl_policy_enforce(struct ssl_stream *upstream, void* u_para); diff --git a/platform/include/internal/ssl_utils.h b/platform/include/internal/ssl_utils.h index cd6dcf7..7c7e002 100644 --- a/platform/include/internal/ssl_utils.h +++ b/platform/include/internal/ssl_utils.h @@ -122,6 +122,7 @@ void ssl_openssl_version(void); int ssl_init(void); int ssl_reinit(void); void ssl_fini(void); +int sslver_str2num(const char * version_str); char * ssl_sha1_to_str(unsigned char *, int); diff --git a/platform/src/proxy.cpp b/platform/src/proxy.cpp index 1dfc6a8..df67fa7 100644 --- a/platform/src/proxy.cpp +++ b/platform/src/proxy.cpp @@ -38,13 +38,14 @@ #include #include #include +#include static int signals[] = {SIGHUP, SIGPIPE, SIGUSR1}; /* Global Resource */ void * g_default_logger = NULL; struct tfe_proxy * g_default_proxy = NULL; - +extern Maat_feather_t g_business_maat; /* Per thread resource */ thread_local unsigned int __currect_thread_id = 0; thread_local void * __currect_default_logger = NULL; @@ -335,8 +336,6 @@ int tfe_stat_init(struct tfe_proxy * proxy, const char * profile) proxy->fs_handle = fs_handle; return 0; } -extern struct ssl_policy_enforcer* ssl_policy_enforcer_create(void); -extern enum ssl_stream_action ssl_policy_enforce(struct ssl_stream *upstream, void* u_para); int main(int argc, char * argv[]) { @@ -386,8 +385,9 @@ int main(int argc, char * argv[]) /* SSL INIT */ + g_default_proxy->ssl_ply_enforcer = ssl_policy_enforcer_create(); g_default_proxy->ssl_mgr_handler = ssl_manager_init(main_profile, "ssl", g_default_proxy->evbase, g_default_logger, - ssl_policy_enforce, ssl_policy_enforcer_create()); + ssl_policy_enforce, g_default_proxy->ssl_ply_enforcer); CHECK_OR_EXIT(g_default_proxy->ssl_mgr_handler, "Failed at init SSL manager. Exit."); for (size_t i = 0; i < (sizeof(signals) / sizeof(int)); i++) @@ -417,7 +417,8 @@ int main(int argc, char * argv[]) CHECK_OR_EXIT(ret >= 0, "Plugin %s init failed. Exit. ", plugin_iter->symbol); TFE_LOG_INFO(g_default_logger, "Plugin %s initialized. ", plugin_iter->symbol); } - + //ugly here. g_business_maat is available after plugin initiate. + ssl_policy_enforcer_init(g_default_proxy->ssl_ply_enforcer, g_business_maat, g_default_logger); ret = tfe_proxy_work_thread_run(g_default_proxy); CHECK_OR_EXIT(ret == 0, "Failed at creating thread. Exit."); diff --git a/platform/src/ssl_policy.cpp b/platform/src/ssl_policy.cpp index e254c03..cf23366 100644 --- a/platform/src/ssl_policy.cpp +++ b/platform/src/ssl_policy.cpp @@ -1,13 +1,163 @@ #include +#include #include +#include +#include +#include + struct ssl_policy_enforcer { - + Maat_feather_t maat; + int compile_table_id; + void* logger; +}; +struct intercept_param +{ + int policy_id; + int ref_cnt; + int keyring; + int bypass_ev_cert; + int bypass_ct_cert; + int bypass_mutual_auth; + int bypass_pinning; + int no_verify_cn; + int no_verify_issuer; + int no_verify_self_signed; + int no_verify_expry_date; + int block_fake_cert; + int ssl_min_version; + int ssl_max_version; + int mirror_client; + int decrypt_mirror_enabled; + int mirror_profile_id; }; - struct ssl_policy_enforcer* ssl_policy_enforcer_create(void) { - return NULL; + struct ssl_policy_enforcer* enforcer=ALLOC(struct ssl_policy_enforcer, 1); + + return enforcer; +} +void intercept_policy_dup_cb(int table_id, MAAT_PLUGIN_EX_DATA* to, MAAT_PLUGIN_EX_DATA* from, long argl, void* argp) +{ + struct intercept_param* param= (struct intercept_param*) *from; + param->ref_cnt++; + *to = param; + return; +} +void intercept_policy_new_cb(int table_id, const char* key, const char* table_line, MAAT_PLUGIN_EX_DATA* ad, long argl, void* argp) +{ + int ret=0; + size_t intercept_user_region_offset=0, len=0; + char* json_str=NULL; + cJSON *json=NULL, *exclusions=NULL, *cert_verify=NULL, *approach=NULL, *ssl_ver=NULL, *item=NULL; + struct intercept_param* param=NULL; + + struct ssl_policy_enforcer* enforcer=(struct ssl_policy_enforcer*)argp; + ret=Maat_helper_read_column(table_line, 7, &intercept_user_region_offset, &len); + if(ret<0) + { + TFE_LOG_ERROR(enforcer->logger, "Get intercept user region: %s", table_line); + return; + } + json_str=ALLOC(char, len+1); + memcpy(json_str, table_line+intercept_user_region_offset, len); + json=cJSON_Parse(json_str); + if(json==NULL) + { + TFE_LOG_ERROR(enforcer->logger, "Invalid intercept parameter: id = %s", key); + goto error_out; + } + param=ALLOC(struct intercept_param, 1); + param->policy_id=atoi(key); + param->ref_cnt=1; + param->bypass_mutual_auth=1; + param->bypass_pinning=1; + + item=cJSON_GetObjectItem(json, "keyring"); + if(item && item->type==cJSON_Number) param->keyring=item->valueint; + + exclusions=cJSON_GetObjectItem(json, "exclusions"); + if(exclusions) + { + item=cJSON_GetObjectItem(exclusions, "ev_cert"); + if(item && item->type==cJSON_Number) param->bypass_ev_cert=item->valueint; + item=cJSON_GetObjectItem(exclusions, "cert_transparency"); + if(item && item->type==cJSON_Number) param->bypass_ct_cert=item->valueint; + item=cJSON_GetObjectItem(exclusions, "client_cert_req"); + if(item && item->type==cJSON_Number) param->bypass_mutual_auth=item->valueint; + item=cJSON_GetObjectItem(exclusions, "pinning"); + if(item && item->type==cJSON_Number) param->bypass_pinning=item->valueint; + } + cert_verify=cJSON_GetObjectItem(json, "cert_verify"); + if(cert_verify) + { + approach=cJSON_GetObjectItem(cert_verify, "approach"); + if(approach) + { + item=cJSON_GetObjectItem(approach, "cn"); + if(item && item->type==cJSON_Number && item->valueint==0) param->no_verify_cn=1; + item=cJSON_GetObjectItem(approach, "issuer"); + if(item && item->type==cJSON_Number && item->valueint==0) param->no_verify_issuer=1; + item=cJSON_GetObjectItem(approach, "self-signed"); + if(item && item->type==cJSON_Number && item->valueint==0) param->no_verify_self_signed=1; + item=cJSON_GetObjectItem(approach, "expiration"); + if(item && item->type==cJSON_Number && item->valueint==0) param->no_verify_expry_date=1; + } + item=cJSON_GetObjectItem(exclusions, "fail_method"); + if(item && item->type==cJSON_String) + { + if(0==strcasecmp(item->string, "Fail-Close")) + { + param->block_fake_cert=1; + } + } + } + ssl_ver=cJSON_GetObjectItem(json, "ssl_ver"); + if(ssl_ver) + { + item=cJSON_GetObjectItem(ssl_ver, "mirror_client"); + if(item && item->type==cJSON_String) param->mirror_client=item->valueint; + item=cJSON_GetObjectItem(ssl_ver, "min"); + if(item && item->type==cJSON_String) param->ssl_min_version=sslver_str2num(item->string); + item=cJSON_GetObjectItem(ssl_ver, "max"); + if(item && item->type==cJSON_String) param->ssl_max_version=sslver_str2num(item->string); + } + *ad=param; + TFE_LOG_INFO(enforcer->logger, "Add intercept policy: %d", param->policy_id); +error_out: + cJSON_Delete(json); + free(json_str); + return; +} +void intercept_policy_free_cb(int table_id, MAAT_PLUGIN_EX_DATA* ad, long argl, void* argp) +{ + struct ssl_policy_enforcer* enforcer=(struct ssl_policy_enforcer*)argp; + struct intercept_param* param= (struct intercept_param*) *ad; + param->ref_cnt--; + if(param->ref_cnt==0) + { + free(param); + TFE_LOG_INFO(enforcer->logger, "Del intercept policy %d", param->policy_id); + free(*ad); + *ad=NULL; + } +} + +void ssl_policy_enforcer_init(struct ssl_policy_enforcer* enforcer, Maat_feather_t maat, void* logger) +{ + enforcer->maat=maat; + enforcer->logger=logger; + enforcer->compile_table_id=Maat_table_register(enforcer->maat, "PXY_INTERCEPT_COMPILE"); + int ret=Maat_plugin_EX_register(enforcer->maat, + enforcer->compile_table_id, + intercept_policy_new_cb, + intercept_policy_free_cb, + intercept_policy_dup_cb, + NULL, + 0, + enforcer); + assert(ret==1); + return; } enum ssl_stream_action ssl_policy_enforce(struct ssl_stream *upstream, void* u_para) { @@ -20,6 +170,10 @@ enum ssl_stream_action ssl_policy_enforce(struct ssl_stream *upstream, void* u_p assert(ret==1); ret=ssl_stream_get_integer_opt(upstream, SSL_STREAM_OPT_IS_MUTUAL_AUTH, &is_mauth); + ret=ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_NO_VERIFY_EXPIRY_DATE, 1); + ret=ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_PROTOCOL_MIN_VERSION, SSL3_VERSION); + ret=ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_PROTOCOL_MIN_VERSION, TLS1_3_VERSION); + assert(ret=1); if(pinning_staus>0||is_ev||is_mauth) { return SSL_ACTION_PASSTHROUGH; diff --git a/platform/src/ssl_stream.cpp b/platform/src/ssl_stream.cpp index 79394b1..79ca316 100644 --- a/platform/src/ssl_stream.cpp +++ b/platform/src/ssl_stream.cpp @@ -200,7 +200,7 @@ struct ssl_stream struct ssl_upstream_parts up_parts; struct ssl_downstream_parts down_parts; }; - int ssl_min_ver, ssl_max_ver; + int ssl_min_version, ssl_max_version, negotiated_version; const unsigned char* alpn_selected; //reference to SSL_ALPN_HTTP_2/SSL_ALPN_HTTP_1_1 struct ssl_stream* peer; struct __ssl_stream_debug _do_not_use; @@ -393,7 +393,7 @@ void ssl_stat_init(struct ssl_mgr * mgr) return; } -static SSL * downstream_ssl_create(struct ssl_mgr * mgr, struct keyring * crt, const unsigned char* selected_alpn); +static SSL * downstream_ssl_create(struct ssl_mgr * mgr, struct keyring * crt, int negotiated_version, const unsigned char* selected_alpn); static SSL * upstream_ssl_create(struct ssl_mgr * mgr, struct ssl_stream* s_stream, evutil_socket_t fd); static void sslctx_set_opts(SSL_CTX * sslctx, struct ssl_mgr * mgr); @@ -407,26 +407,34 @@ struct ssl_chello * ssl_peek_result_release_chello(future_result_t * result) } struct ssl_stream * ssl_stream_new(struct ssl_mgr * mgr, evutil_socket_t fd, enum tfe_conn_dir dir, - struct ssl_chello * client_hello, struct keyring * kyr, const unsigned char* selected_alpn) + struct ssl_chello * client_hello, struct keyring * kyr, struct ssl_stream * peer) { UNUSED int ret = 0; + const unsigned char* selected_alpn=peer->alpn_selected; + struct ssl_stream * s_stream = ALLOC(struct ssl_stream, 1); s_stream->dir = dir; s_stream->mgr = mgr; s_stream->_do_not_use.fd = fd; assert(ret == 0); + s_stream->ssl_max_version=mgr->ssl_max_version; + s_stream->ssl_min_version=mgr->ssl_min_version; + s_stream->peer=peer; switch (dir) { - case CONN_DIR_DOWNSTREAM: + case CONN_DIR_DOWNSTREAM: + assert(peer!=NULL); ATOMIC_INC(&(s_stream->mgr->stat_val[SSL_DOWN_NEW])); s_stream->down_parts.keyring = kyr; - s_stream->ssl = downstream_ssl_create(mgr, kyr, selected_alpn); + s_stream->ssl = downstream_ssl_create(mgr, kyr, peer->negotiated_version, selected_alpn); break; - case CONN_DIR_UPSTREAM: + case CONN_DIR_UPSTREAM: ATOMIC_INC(&(s_stream->mgr->stat_val[SSL_UP_NEW])); - s_stream->up_parts.verify_param.no_verify_expiry_date=1; - s_stream->up_parts.client_hello = client_hello; + s_stream->up_parts.verify_param.no_verify_cn=1; + s_stream->up_parts.client_hello = client_hello; + s_stream->ssl_min_version=client_hello->min_version.ossl_format; + s_stream->ssl_max_version=client_hello->max_version.ossl_format; s_stream->ssl = upstream_ssl_create(mgr, s_stream, fd); break; default: assert(0); @@ -467,45 +475,7 @@ static void ssl_stream_free(struct ssl_stream * s_stream) return; } -static int sslver_str2num(const char * version_str) -{ - 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 if (!strcmp(version_str, "tls13")) - { - sslversion = TLS1_3_VERSION; - } - - else - { - sslversion = -1; - } - - return sslversion; -} static void log_ssl_master_key(SSL* ssl, int fd, tfe_conn_dir dir, FILE* fp) { char* key_str=NULL; @@ -829,8 +799,8 @@ static SSL * upstream_ssl_create(struct ssl_mgr * mgr, struct ssl_stream* s_stre ret=SSL_CTX_set_cipher_list(sslctx, mgr->default_ciphers); } - if (SSL_CTX_set_min_proto_version(sslctx, mgr->ssl_min_version) == 0 || - SSL_CTX_set_max_proto_version(sslctx, mgr->ssl_max_version) == 0) + if (SSL_CTX_set_min_proto_version(sslctx, s_stream->ssl_min_version) == 0 || + SSL_CTX_set_max_proto_version(sslctx, s_stream->ssl_max_version) == 0) { SSL_CTX_free(sslctx); return NULL; @@ -868,7 +838,7 @@ static SSL * upstream_ssl_create(struct ssl_mgr * mgr, struct ssl_stream* s_stre { /* session resuming based on remote endpoint address and port */ sess = up_session_get(mgr->up_sess_cache, (struct sockaddr *) &addr, addrlen, chello->sni, - SSL_get_min_proto_version(ssl), SSL_get_max_proto_version(ssl)); + s_stream->ssl_min_version, s_stream->ssl_max_version); if (sess) { SSL_set_session(ssl, sess); /* increments sess refcount */ @@ -1171,6 +1141,7 @@ static void ssl_server_connected_eventcb(struct bufferevent * bev, short events, s_stream->alpn_selected=NULL; } } + s_stream->negotiated_version=SSL_version(s_stream->ssl); promise_success(p, ctx); } wrap_ssl_connect_server_ctx_free(ctx); @@ -1479,7 +1450,7 @@ static int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, * Create and set up a new SSL_CTX instance for terminating SSL. * Set up all the necessary callbacks, the keyring, the keyring chain and key. */ -static SSL * downstream_ssl_create(struct ssl_mgr * mgr, struct keyring * crt, const unsigned char* selected_alpn) +static SSL * downstream_ssl_create(struct ssl_mgr * mgr, struct keyring * crt, int negotiated_version, const unsigned char* selected_alpn) { SSL_CTX * sslctx = SSL_CTX_new(mgr->sslmethod()); if (!sslctx) return NULL; @@ -1490,7 +1461,16 @@ static SSL * downstream_ssl_create(struct ssl_mgr * mgr, struct keyring * crt, c SSL_CTX_set_cipher_list(sslctx, mgr->default_ciphers); //TFE's OPENSSL_VERSION_NUMBER >= 0x10100000L - if (mgr->ssl_min_version) + if(negotiated_version) + { + if (SSL_CTX_set_min_proto_version(sslctx, negotiated_version) == 0 || + SSL_CTX_set_max_proto_version(sslctx, negotiated_version) == 0) + { + SSL_CTX_free(sslctx); + return NULL; + } + } + else if(mgr->ssl_min_version) { if (SSL_CTX_set_min_proto_version(sslctx, mgr->ssl_min_version) == 0 || SSL_CTX_set_max_proto_version(sslctx, mgr->ssl_max_version) == 0) @@ -1664,6 +1644,7 @@ static void ssl_client_connected_eventcb(struct bufferevent * bev, short events, { log_ssl_master_key(ctx->downstream->ssl, ctx->fd_downstream, CONN_DIR_DOWNSTREAM, mgr->fp_master_key); } + ctx->downstream->negotiated_version=SSL_version(ctx->downstream->ssl); promise_success(p, ctx); } ssl_connect_client_ctx_free(ctx); @@ -1682,10 +1663,8 @@ void ask_keyring_on_succ(void * result, void * user) kyr = key_keeper_release_keyring(result); //kyr will be freed at ssl downstream closing. clock_gettime(CLOCK_MONOTONIC, &(ctx->start)); - ctx->downstream = ssl_stream_new(mgr, ctx->fd_downstream, CONN_DIR_DOWNSTREAM, - ctx->origin_ssl->up_parts.client_hello, kyr, - ctx->origin_ssl?ctx->origin_ssl->alpn_selected:NULL); - ctx->downstream->peer=ctx->origin_ssl; + ctx->downstream = ssl_stream_new(mgr, ctx->fd_downstream, CONN_DIR_DOWNSTREAM, NULL, + kyr, ctx->origin_ssl); ctx->bev_down = bufferevent_openssl_socket_new(evbase, ctx->fd_downstream, ctx->downstream->ssl, BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_DEFER_CALLBACKS | BEV_OPT_THREADSAFE); bufferevent_openssl_set_allow_dirty_shutdown(ctx->bev_down, 1); @@ -1930,7 +1909,10 @@ int ssl_stream_set_integer_opt(struct ssl_stream *upstream, enum SSL_STREAM_OPT upstream->up_parts.block_fake_cert=opt_val; break; case SSL_STREAM_OPT_PROTOCOL_MIN_VERSION: + upstream->ssl_min_version=opt_val; + break; case SSL_STREAM_OPT_PROTOCOL_MAX_VERSION: + upstream->ssl_max_version=opt_val; break; default: return 0; diff --git a/platform/src/ssl_utils.cpp b/platform/src/ssl_utils.cpp index 3c25292..19e33d3 100644 --- a/platform/src/ssl_utils.cpp +++ b/platform/src/ssl_utils.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -150,6 +151,45 @@ void ssl_openssl_version(void) #endif /* !SSL_OP_TLS_ROLLBACK_BUG */ fprintf(stderr, "\n"); } +int sslver_str2num(const char * version_str) +{ + 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 if (!strcmp(version_str, "tls13")) + { + sslversion = TLS1_3_VERSION; + } + + else + { + sslversion = -1; + } + + return sslversion; +} /* * 1 if OpenSSL has been initialized, 0 if not. When calling a _load() diff --git a/plugin/business/pangu-http/src/pangu_http.cpp b/plugin/business/pangu-http/src/pangu_http.cpp index b783997..6b8703b 100644 --- a/plugin/business/pangu-http/src/pangu_http.cpp +++ b/plugin/business/pangu-http/src/pangu_http.cpp @@ -146,6 +146,9 @@ struct pangu_rt }; struct pangu_rt * g_pangu_rt; +Maat_feather_t g_business_maat; + + #define MAAT_INPUT_JSON 0 #define MAAT_INPUT_REDIS 1 #define MAAT_INPUT_FILE 2 @@ -835,6 +838,7 @@ int pangu_http_init(struct tfe_proxy * proxy) TFE_LOG_INFO(NULL, "Tango Cache Enabled."); } TFE_LOG_INFO(NULL, "Pangu HTTP init success."); + g_business_maat=g_pangu_rt->maat; return 0; error_out: