diff --git a/common/include/ssl_stream.h b/common/include/ssl_stream.h index 8c66892..74d6fd7 100644 --- a/common/include/ssl_stream.h +++ b/common/include/ssl_stream.h @@ -17,7 +17,7 @@ enum SSL_STREAM_OPT SSL_STREAM_OPT_IS_CT_CERT, //0:FALSE, 1:TRUE. SSL_STREAM_OPT_IS_MUTUAL_AUTH, //0:FALSE, 1:TRUE. SSL_STREAM_OPT_PINNING_STATUS, //0:FALSE, 1:TRUE. - SSL_STREAM_OPT_APP_STATUS, //0:FALSE, 1:TRUE. + SSL_STREAM_OPT_JA3_PINNING_STATUS, //0:FALSE, 1:TRUE. SSL_STREAM_OPT_HAS_PROTOCOL_ERRORS, //0:FALSE, 1:TRUE. SSL_STREAM_OPT_NO_VERIFY_SELF_SIGNED, //VALUE is an interger, SIZE=sizeof(int). 1:ON, 0:OFF. DEFAULT:0. SSL_STREAM_OPT_NO_VERIFY_COMMON_NAME, //VALUE is an interger, SIZE=sizeof(int). 1:ON, 0:OFF. DEFAULT:1. @@ -31,6 +31,12 @@ enum SSL_STREAM_OPT SSL_STREAM_OPT_SNI, //VALUE is string SSL_STREAM_OPT_ADDR //VALUE is string }; +enum ssl_ja3_pinning_status +{ + JA3_PINNING_STATUS_UNKNOWN = -1, + JA3_PINNING_STATUS_NOT_PINNING = 0, + JA3_PINNING_STATUS_IS_PINNING = 1, +}; int sslver_str2num(const char * version_str); //s_stream must be upstream. diff --git a/conf/tfe/tfe.conf b/conf/tfe/tfe.conf index 7a1a621..bb2c35f 100644 --- a/conf/tfe/tfe.conf +++ b/conf/tfe/tfe.conf @@ -36,6 +36,7 @@ watchdog_port=2476 [ssl] ssl_ja3_debug=0 +ssl_ja3_table=PXY_SSL_FINGERPRINT # ssl version Not available, configured via TSG website # ssl_max_version=tls13 # ssl_min_version=ssl3 @@ -63,7 +64,6 @@ service_cache_slots=4194304 service_cache_expire_seconds=300 service_cache_fail_as_pinning_cnt=4 service_cache_fail_as_proto_err_cnt=5 -service_cache_succ_as_app_not_pinning_cnt=0 service_cache_fail_time_window=30 # cert diff --git a/platform/include/internal/ssl_service_cache.h b/platform/include/internal/ssl_service_cache.h index 1a3d2ff..d569fce 100644 --- a/platform/include/internal/ssl_service_cache.h +++ b/platform/include/internal/ssl_service_cache.h @@ -9,11 +9,12 @@ #define PINNING_ST_NOT_PINNING 0 #define PINNING_ST_PINNING 1 #define PINNING_ST_MAYBE_PINNING 2 + struct ssl_service_status { char cli_pinning_status; - char is_app_not_pinning; - char is_ev; + char ja3_pinning_status; + char is_ev; char is_ct; char is_mutual_auth; char has_protocol_errors; @@ -32,15 +33,13 @@ struct ssl_service_cache { MESA_htable_handle cli_st_hash; MESA_htable_handle srv_st_hash; - MESA_htable_handle app_st_hash; struct ssl_service_cache_statistics stat; unsigned int fail_as_cli_pinning_count; unsigned int fail_as_proto_err_count; unsigned int fail_time_window; - unsigned int succ_as_app_not_pinning_count; }; -struct ssl_service_cache* ssl_service_cache_create(unsigned int slot_size, unsigned int expire_seconds, int fail_as_pinning_cnt, int fail_as_proto_err_cnt, int succ_as_app_not_pinning_cnt, int fail_time_win); +struct ssl_service_cache* ssl_service_cache_create(unsigned int slot_size, unsigned int expire_seconds, int fail_as_pinning_cnt, int fail_as_proto_err_cnt, int fail_time_win, char *ja3_table_name); void ssl_service_cache_destroy(struct ssl_service_cache* cache); int ssl_service_cache_read(struct ssl_service_cache *svc_cache, const struct ssl_chello *chello, const struct tfe_stream *tcp_stream, struct ssl_service_status *result); diff --git a/platform/src/proxy.cpp b/platform/src/proxy.cpp index f683415..4f1dc2e 100644 --- a/platform/src/proxy.cpp +++ b/platform/src/proxy.cpp @@ -589,7 +589,11 @@ int main(int argc, char * argv[]) g_default_proxy->key_keeper_handler = key_keeper_init(main_profile, "key_keeper", g_default_logger); CHECK_OR_EXIT(g_default_proxy->key_keeper_handler, "Failed at init Key keeper. Exit."); - /* SSL INIT */ + /* RESOURCE INIT */ + ret = tfe_bussiness_resouce_init(); + CHECK_OR_EXIT(ret == 0, "TFE bussiness resource init failed. Exit."); + + /* SSL INIT */ g_default_proxy->ssl_mgr_handler = ssl_manager_init(main_profile, "ssl", g_default_proxy->evbase, g_default_proxy->key_keeper_handler, g_default_logger); CHECK_OR_EXIT(g_default_proxy->ssl_mgr_handler, "Failed at init SSL manager. Exit."); @@ -612,10 +616,6 @@ int main(int argc, char * argv[]) g_default_proxy->scm_sender = sender_scm_init(main_profile, "kni", g_default_logger); CHECK_OR_EXIT(g_default_proxy->scm_sender != NULL, "Failed at creating scm sender, Exit."); - /* RESOURCE INIT */ - ret = tfe_bussiness_resouce_init(); - CHECK_OR_EXIT(ret == 0, "TFE bussiness resource init failed. Exit."); - /* PLUGIN INIT */ unsigned int plugin_iterator = 0; for (struct tfe_plugin * plugin_iter = tfe_plugin_iterate(&plugin_iterator); diff --git a/platform/src/ssl_service_cache.cpp b/platform/src/ssl_service_cache.cpp index 1327a16..28205c0 100644 --- a/platform/src/ssl_service_cache.cpp +++ b/platform/src/ssl_service_cache.cpp @@ -1,8 +1,154 @@ #include -#include -#include -#include -#include +#include +#include +#include + +struct ssl_ja3_enforcer +{ + Maat_feather_t maat; + int table_id; +}; + +struct ssl_svc_ja3 +{ + char ja3_hash[33]; + int fingerprint_id; + int pinning_state; + int is_valid; + int ref_cnt; +}; + +static struct ssl_ja3_enforcer g_static_enforcer = {0}; + +static void ssl_svc_ja3_param_dup_cb(int table_id, MAAT_PLUGIN_EX_DATA *to, MAAT_PLUGIN_EX_DATA *from, long argl, void *argp) +{ + struct ssl_svc_ja3 *param = (struct ssl_svc_ja3 *)*from; + if (param) + { + __sync_add_and_fetch(&(param->ref_cnt), 1); + *to = param; + } + else + { + *to = NULL; + } + return; +} + +static void ssl_svc_ja3_param_new_cb(int table_id, const char *key, const char *table_line, MAAT_PLUGIN_EX_DATA *ad, long argl, void *argp) +{ + int is_valid = 0; + int pinning_state = 0; + int fingerprint_id = 0; + char ja3_hash[33] = {0}; + + if (sscanf(table_line, "%d\t%s\t%d\t%d", &fingerprint_id, ja3_hash, &pinning_state, &is_valid) != 4) + { + TFE_LOG_ERROR(g_default_logger, "Invalid JA3 policy: %s", table_line); + return; + } + + struct ssl_svc_ja3 *param = ALLOC(struct ssl_svc_ja3, 1); + param->fingerprint_id = fingerprint_id; + memcpy(param->ja3_hash, ja3_hash, 32); + param->pinning_state = pinning_state; + param->is_valid = is_valid; + param->ref_cnt = 1; + + *ad = param; + TFE_LOG_INFO(g_default_logger, "Add JA3 policy: id:%d, ja3_hash:%s, pinning_state:%d, is_valid:%d, ref_cnt:%d", + param->fingerprint_id, param->ja3_hash, param->pinning_state, param->is_valid, param->ref_cnt); +} + +static void ssl_svc_ja3_param_free_cb(int table_id, MAAT_PLUGIN_EX_DATA *ad, long argl, void *argp) +{ + struct ssl_svc_ja3 *param = (struct ssl_svc_ja3 *)*ad; + if (param == NULL) + { + return; + } + + if ((__sync_sub_and_fetch(¶m->ref_cnt, 1) == 0)) + { + TFE_LOG_INFO(g_default_logger, "Del JA3 policy: id:%d, ja3_hash:%s, pinning_state:%d, is_valid:%d, ref_cnt:%d", + param->fingerprint_id, param->ja3_hash, param->pinning_state, param->is_valid, param->ref_cnt); + free(param); + *ad = NULL; + } +} + +static void ssl_svc_ja3_param_free(struct ssl_svc_ja3 *param) +{ + ssl_svc_ja3_param_free_cb(0, (void **)¶m, 0, NULL); + return; +} + +static int ssl_svc_ja3_init(const char *table_name) +{ + g_static_enforcer.maat = (Maat_feather_t)tfe_bussiness_resouce_get(STATIC_MAAT); + g_static_enforcer.table_id = Maat_table_register(g_static_enforcer.maat, table_name); + if (g_static_enforcer.table_id < 0) + { + TFE_LOG_ERROR(g_default_logger, "Maat table %s register failed.", table_name); + return 0; + } + int ret = Maat_plugin_EX_register(g_static_enforcer.maat, + g_static_enforcer.table_id, + ssl_svc_ja3_param_new_cb, + ssl_svc_ja3_param_free_cb, + ssl_svc_ja3_param_dup_cb, + NULL, + 0, + &g_static_enforcer); + if (ret < 0) + { + TFE_LOG_ERROR(g_default_logger, "failed at Maat_plugin_EX_register(%s), table_id = %d, ret = %d", + table_name, g_static_enforcer.table_id, ret); + return 0; + } + + return 1; +} + +enum ssl_ja3_pinning_status ssl_svc_ja3_scan(char *ja3_hash, char *addr_str) +{ + enum ssl_ja3_pinning_status ret = JA3_PINNING_STATUS_UNKNOWN; + struct ssl_svc_ja3 *param = NULL; + param = (struct ssl_svc_ja3 *)Maat_plugin_get_EX_data(g_static_enforcer.maat, g_static_enforcer.table_id, ja3_hash); + if (param == NULL) + { + ret = JA3_PINNING_STATUS_UNKNOWN; + goto end; + } + TFE_LOG_INFO(g_default_logger, "Hit JA3 policy: id:%d, ja3_hash:%s, pinning_state:%d, is_valid:%d, ref_cnt:%d, addr:%s", + param->fingerprint_id, param->ja3_hash, param->pinning_state, param->is_valid, param->ref_cnt, addr_str); + + if (!param->is_valid) + { + ret = JA3_PINNING_STATUS_UNKNOWN; + goto end; + } + + // 1 - pinning + if (param->pinning_state) + { + ret = JA3_PINNING_STATUS_IS_PINNING; + } + // 0 - not pinning + else + { + ret = JA3_PINNING_STATUS_NOT_PINNING; + } + +end: + if (param) + { + ssl_svc_ja3_param_free(param); + param = NULL; + } + + return ret; +} struct ssl_svc_client_st { @@ -20,11 +166,6 @@ struct ssl_svc_server_st long long ct_st_switched; struct ssl_service_cache* ref_svc_cache; }; -struct ssl_svc_app_st -{ - unsigned int down_ssl_success_cnt; - struct ssl_service_cache* ref_svc_cache; -}; struct ssl_service_write_args { struct ssl_service_cache* cache; @@ -66,16 +207,6 @@ static void ssl_svc_free_server_st(void * data) free(p); return; } -static void ssl_svc_free_app_st(void* data) -{ - struct ssl_svc_app_st* p= (struct ssl_svc_app_st*)data; - struct ssl_service_cache* svc_cache=p->ref_svc_cache; - if(p->down_ssl_success_cnt>svc_cache->succ_as_app_not_pinning_count) - { - svc_cache->stat.app_not_pinning_cnt--; - } - free(p); -} static size_t ssl_svc_server_st_mk_key(const struct ssl_chello* chello, const struct tfe_stream_addr * addr, char* key_buff, size_t sz) { size_t key_len=0; @@ -289,45 +420,6 @@ static long srv_st_write_cb(void * data, const uchar * key, uint size, void * us // assert(srv_st->ev_st_switched<2&&srv_st->ct_st_switched<2); return 1; } -static long app_st_read_cb(void * data, const uchar * key, uint size, void * user_arg) -{ - struct ssl_svc_app_st* app_st=(struct ssl_svc_app_st*)data; - struct ssl_service_status* result=(struct ssl_service_status*)user_arg; - if (app_st == NULL) - { - return 0; - } - if(app_st->down_ssl_success_cnt>app_st->ref_svc_cache->succ_as_app_not_pinning_count) - { - result->is_app_not_pinning=1; - } - return 1; - -} -static long app_st_write_cb(void * data, const uchar * key, uint size, void * user_arg) -{ - struct ssl_svc_app_st* app_st=(struct ssl_svc_app_st*)data; - struct ssl_service_write_args* args=(struct ssl_service_write_args*)user_arg; - const struct ssl_service_status* status=args->status; - struct ssl_service_cache* cache=args->cache; - UNUSED int ret = 0; - if(app_st==NULL) - { - app_st=ALLOC(struct ssl_svc_app_st, 1); - app_st->ref_svc_cache=cache; - ret = MESA_htable_add(cache->app_st_hash, key, size, app_st); - assert(ret >= 0); - } - if(status->is_app_not_pinning) - { - app_st->down_ssl_success_cnt++; - } - if(app_st->down_ssl_success_cnt>cache->succ_as_app_not_pinning_count) - { - cache->stat.app_not_pinning_cnt++; - } - return 1; -} int ssl_service_cache_read(struct ssl_service_cache *svc_cache, const struct ssl_chello *chello, const struct tfe_stream *tcp_stream, struct ssl_service_status *result) { @@ -356,16 +448,28 @@ int ssl_service_cache_read(struct ssl_service_cache *svc_cache, const struct ssl TFE_LOG_DEBUG(g_default_logger, "server table, hash:%s, found:%d, sni:%s, addr:%s, ct:%d, ev:%d", hash_key, svr_st_cb_ret, chello->sni, addr_str, result->is_ct, result->is_ev); - memset(hash_key, 0, sizeof(hash_key)); - temp_key_sz = ssl_svc_app_st_mk_key(chello, tcp_stream, temp_key, sizeof(temp_key)); - hash_key_sz = tfe_hexdump(hash_key, (unsigned char *)temp_key, temp_key_sz) - hash_key; - MESA_htable_search_cb(svc_cache->app_st_hash, hash_key, (unsigned int) hash_key_sz, app_st_read_cb, result, &app_st_cb_ret); - TFE_LOG_DEBUG(g_default_logger, "app table, hash:%s, found:%d, sni:%s, addr:%s, app_not_pinning:%d", - hash_key, app_st_cb_ret, chello->sni, addr_str, result->is_app_not_pinning); + char ja3_hash[64] = {0}; + uint16_t ja3_len = 0; + result->ja3_pinning_status = JA3_PINNING_STATUS_UNKNOWN; + struct tfe_cmsg *cmsg = tfe_stream_get0_cmsg(tcp_stream); + if (cmsg) + { + int ret = tfe_cmsg_get_value(cmsg, TFE_CMSG_SSL_CLIENT_JA3_FINGERPRINT, (unsigned char *)ja3_hash, sizeof(ja3_hash), &ja3_len); + if (ret == 0) + { + result->ja3_pinning_status = ssl_svc_ja3_scan(ja3_hash, addr_str); + if (result->ja3_pinning_status != JA3_PINNING_STATUS_UNKNOWN) + { + app_st_cb_ret = 1; + } + } + } + TFE_LOG_DEBUG(g_default_logger, "app table, hash:%s, found:%d, sni:%s, addr:%s, ja3_pinning_status:%d", + ja3_hash, app_st_cb_ret, chello->sni, addr_str, result->ja3_pinning_status); free(addr_str); - if(cli_st_cb_ret||svr_st_cb_ret||app_st_cb_ret) + if(cli_st_cb_ret||svr_st_cb_ret||app_st_cb_ret) { return 1; } @@ -407,21 +511,18 @@ void ssl_service_cache_write(struct ssl_service_cache *svc_cache, const struct s hash_key, chello->sni, addr_str, status->is_ct, status->is_ev); MESA_htable_search_cb(svc_cache->srv_st_hash, hash_key, (unsigned int) hash_key_sz, srv_st_write_cb, &write_args, &svr_st_cb_ret); } - if(status->is_app_not_pinning) - { - memset(hash_key, 0, sizeof(hash_key)); - temp_key_sz = ssl_svc_app_st_mk_key(chello, tcp_stream, temp_key, sizeof(temp_key)); - hash_key_sz = tfe_hexdump(hash_key, (unsigned char *)temp_key, temp_key_sz) - hash_key; - TFE_LOG_DEBUG(g_default_logger, "app table, hash:%s, sni:%s, addr:%s, app_not_pinning:%d", - hash_key, chello->sni, addr_str, status->is_app_not_pinning); - MESA_htable_search_cb(svc_cache->app_st_hash, hash_key, (unsigned int) hash_key_sz, app_st_write_cb, &write_args, &svr_st_cb_ret); - } free(addr_str); } -struct ssl_service_cache* ssl_service_cache_create(unsigned int slot_size, unsigned int expire_seconds, int fail_as_pinning_cnt, int fail_as_proto_err_cnt, int succ_as_app_not_pinning_cnt, int fail_time_win) +struct ssl_service_cache *ssl_service_cache_create(unsigned int slot_size, unsigned int expire_seconds, int fail_as_pinning_cnt, int fail_as_proto_err_cnt, int fail_time_win +, char *ja3_table_name) { - struct ssl_service_cache * cache = ALLOC(struct ssl_service_cache, 1); + if (ssl_svc_ja3_init(ja3_table_name) == 0) + { + return NULL; + } + + struct ssl_service_cache * cache = ALLOC(struct ssl_service_cache, 1); unsigned max_num = slot_size * 4; UNUSED int ret = 0; MESA_htable_handle htable=NULL, saved[3]; @@ -429,9 +530,8 @@ struct ssl_service_cache* ssl_service_cache_create(unsigned int slot_size, unsig cache->fail_as_cli_pinning_count=fail_as_pinning_cnt; cache->fail_as_proto_err_count=fail_as_proto_err_cnt; cache->fail_time_window=fail_time_win; - cache->succ_as_app_not_pinning_count = succ_as_app_not_pinning_cnt; - void (*free_func[])(void *)={ssl_svc_free_client_st, ssl_svc_free_server_st, ssl_svc_free_app_st}; - for(i=0; i<3; i++) + void (*free_func[])(void *)={ssl_svc_free_client_st, ssl_svc_free_server_st}; + for(i=0; i<2; i++) { htable = MESA_htable_born(); opt_val=0; @@ -442,21 +542,10 @@ struct ssl_service_cache* ssl_service_cache_create(unsigned int slot_size, unsig ret = MESA_htable_set_opt(htable, MHO_MUTEX_NUM, &opt_val, sizeof(opt_val)); 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)); - // for app table - if (i == 2) - { - unsigned int app_expire_seconds = 2 * expire_seconds; - ret = MESA_htable_set_opt(htable, MHO_EXPIRE_TIME, &app_expire_seconds, sizeof(app_expire_seconds)); - opt_val = HASH_ELIMINATE_ALGO_LRU; - ret = MESA_htable_set_opt(htable, MHO_ELIMIMINATE_TYPE, &opt_val, sizeof(int)); - } // for client table and server table - else - { - ret = MESA_htable_set_opt(htable, MHO_EXPIRE_TIME, &expire_seconds, sizeof(expire_seconds)); - opt_val = HASH_ELIMINATE_ALGO_FIFO; - ret = MESA_htable_set_opt(htable, MHO_ELIMIMINATE_TYPE, &opt_val, sizeof(int)); - } + ret = MESA_htable_set_opt(htable, MHO_EXPIRE_TIME, &expire_seconds, sizeof(expire_seconds)); + opt_val = HASH_ELIMINATE_ALGO_FIFO; + ret = MESA_htable_set_opt(htable, MHO_ELIMIMINATE_TYPE, &opt_val, sizeof(int)); ret = MESA_htable_set_opt(htable, MHO_CBFUN_DATA_FREE, (void*)free_func[i], sizeof(free_func[i])); @@ -467,8 +556,8 @@ struct ssl_service_cache* ssl_service_cache_create(unsigned int slot_size, unsig } cache->cli_st_hash=saved[0]; cache->srv_st_hash=saved[1]; - cache->app_st_hash=saved[2]; - return cache; + + return cache; } void ssl_service_cache_destroy(struct ssl_service_cache* cache) { @@ -476,8 +565,6 @@ void ssl_service_cache_destroy(struct ssl_service_cache* cache) cache->cli_st_hash=NULL; MESA_htable_destroy(cache->srv_st_hash, NULL); cache->srv_st_hash=NULL; - MESA_htable_destroy(cache->app_st_hash, NULL); - cache->app_st_hash=NULL; free(cache); return; } @@ -485,6 +572,4 @@ void ssl_service_cache_stat(struct ssl_service_cache* svc_cache, struct ssl_serv { *result=svc_cache->stat; return; -} - - +} \ No newline at end of file diff --git a/platform/src/ssl_stream.cpp b/platform/src/ssl_stream.cpp index ff7893c..32c4aa1 100644 --- a/platform/src/ssl_stream.cpp +++ b/platform/src/ssl_stream.cpp @@ -625,11 +625,11 @@ struct ssl_mgr * ssl_manager_init(const char * ini_profile, const char * section unsigned int stek_group_num = 0; unsigned int stek_rotation_time = 0; - struct ssl_mgr * mgr = ALLOC(struct ssl_mgr, 1); int ret = 0; - char version_str[TFE_SYMBOL_MAX] = {}; + char ja3_table_name[TFE_STRING_MAX] = {0}; + char version_str[TFE_SYMBOL_MAX] = {}; mgr->logger = logger; mgr->ev_base_gc=ev_base_gc; @@ -710,18 +710,21 @@ struct ssl_mgr * ssl_manager_init(const char * ini_profile, const char * section &(mgr->svc_fail_as_pinning_cnt), 4); MESA_load_profile_uint_def(ini_profile, section, "service_cache_fail_as_proto_err_cnt", &(mgr->svc_fail_as_proto_err_cnt), 5); - MESA_load_profile_uint_def(ini_profile, section, "service_cache_succ_as_app_not_pinning_cnt", - &(mgr->svc_succ_as_app_not_pinning_cnt), 0); MESA_load_profile_uint_def(ini_profile, section, "service_cache_fail_time_window", &(mgr->svc_cnt_time_window), 30); - mgr->svc_cache=ssl_service_cache_create(mgr->svc_cache_slots, mgr->svc_expire_seconds, + MESA_load_profile_string_def(ini_profile, section, "ssl_ja3_table", ja3_table_name, sizeof(ja3_table_name), "PXY_SSL_FINGERPRINT"); + mgr->svc_cache=ssl_service_cache_create(mgr->svc_cache_slots, mgr->svc_expire_seconds, mgr->svc_fail_as_pinning_cnt, mgr->svc_fail_as_proto_err_cnt, - mgr->svc_succ_as_app_not_pinning_cnt, - mgr->svc_cnt_time_window); + mgr->svc_cnt_time_window, ja3_table_name); + if (mgr->svc_cache == NULL) + { + TFE_LOG_ERROR(logger, "Failed to create service cache"); + goto error_out; + } - mgr->key_keeper = key_keeper; + mgr->key_keeper = key_keeper; MESA_load_profile_uint_def(ini_profile, section, "trusted_cert_load_local", &(mgr->trusted_cert_load_local), 1); @@ -1424,7 +1427,7 @@ static void peek_chello_on_succ(future_result_t * result, void * user) if(ret==1) { addr_string=tfe_stream_addr_to_str(s_stream->tcp_stream->addr); - TFE_LOG_DEBUG(ctx->mgr->logger, "%s %s service status pinning:%d, mauth:%d, err:%d, ct:%d, ev:%d, app_not_pinning:%d", + TFE_LOG_DEBUG(ctx->mgr->logger, "%s %s service status pinning:%d, mauth:%d, err:%d, ct:%d, ev:%d, ja3_pinning_status:%d", addr_string, chello->sni, svc_status->cli_pinning_status, @@ -1432,14 +1435,26 @@ static void peek_chello_on_succ(future_result_t * result, void * user) svc_status->has_protocol_errors, svc_status->is_ct, svc_status->is_ev, - svc_status->is_app_not_pinning); + svc_status->ja3_pinning_status); free(addr_string); addr_string=NULL; } - if (svc_status->is_app_not_pinning) - ssl_stream_set_cmsg_integer(s_stream, TFE_CMSG_SSL_PINNING_STATE, PINNING_ST_NOT_PINNING); - else - ssl_stream_set_cmsg_integer(s_stream, TFE_CMSG_SSL_PINNING_STATE, svc_status->cli_pinning_status); + switch (svc_status->ja3_pinning_status) + { + case JA3_PINNING_STATUS_NOT_PINNING: + ctx->mgr->svc_cache->stat.app_not_pinning_cnt++; + ssl_stream_set_cmsg_integer(s_stream, TFE_CMSG_SSL_PINNING_STATE, PINNING_ST_NOT_PINNING); + break; + case JA3_PINNING_STATUS_IS_PINNING: + ssl_stream_set_cmsg_integer(s_stream, TFE_CMSG_SSL_PINNING_STATE, PINNING_ST_PINNING); + break; + case JA3_PINNING_STATUS_UNKNOWN: + /* fall through */ + default: + ssl_stream_set_cmsg_integer(s_stream, TFE_CMSG_SSL_PINNING_STATE, svc_status->cli_pinning_status); + break; + } + if(ctx->mgr->on_new_upstream_cb) { s_stream->up_parts.action=ctx->mgr->on_new_upstream_cb(s_stream, ctx->mgr->upstream_cb_param); @@ -1931,10 +1946,6 @@ static void ssl_client_connected_eventcb(struct bufferevent * bev, short events, } s_stream->negotiated_version=SSL_version(s_stream->ssl); ssl_stream_set_cmsg_string(s_stream, TFE_CMSG_SSL_CLIENT_SIDE_VERSION, SSL_get_version(s_stream->ssl)); - // struct ssl_service_status svc_status; - // memset(&svc_status, 0, sizeof(svc_status)); - // svc_status.is_app_not_pinning=1; - // ssl_service_cache_write(mgr->svc_cache, s_upstream->client_hello, s_stream->tcp_stream, &svc_status); promise_success(p, ctx); } @@ -2037,13 +2048,6 @@ void ssl_stream_free(struct ssl_stream * s_stream, struct event_base * evbase, s { size_t rx_offset_this_time = 0; int ret = tfe_stream_info_get(s_stream->tcp_stream, INFO_FROM_UPSTREAM_RX_OFFSET, &rx_offset_this_time, sizeof(rx_offset_this_time)); - if (ret >= 0 && rx_offset_this_time > 0) - { - struct ssl_service_status svc_status; - memset(&svc_status, 0, sizeof(svc_status)); - svc_status.is_app_not_pinning = 1; - ssl_service_cache_write(s_stream->mgr->svc_cache, s_stream->up_parts.client_hello, s_stream->tcp_stream, &svc_status); - } const char * sni = (s_stream->up_parts.client_hello && s_stream->up_parts.client_hello->sni) ? s_stream->up_parts.client_hello->sni : "null"; TFE_LOG_DEBUG(g_default_logger, "ssl up stream close, rx_offset:%d, sni:%s", rx_offset_this_time, sni); } @@ -2170,8 +2174,8 @@ int ssl_stream_get_integer_opt(struct ssl_stream *upstream, enum SSL_STREAM_OPT case SSL_STREAM_OPT_PINNING_STATUS: *opt_val=svc->cli_pinning_status; break; - case SSL_STREAM_OPT_APP_STATUS: - *opt_val=svc->is_app_not_pinning; + case SSL_STREAM_OPT_JA3_PINNING_STATUS: + *opt_val=svc->ja3_pinning_status; break; case SSL_STREAM_OPT_HAS_PROTOCOL_ERRORS: *opt_val=svc->has_protocol_errors; diff --git a/plugin/business/ssl-policy/src/ssl_policy.cpp b/plugin/business/ssl-policy/src/ssl_policy.cpp index b01db0f..40b0df7 100644 --- a/plugin/business/ssl-policy/src/ssl_policy.cpp +++ b/plugin/business/ssl-policy/src/ssl_policy.cpp @@ -18,24 +18,6 @@ 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 bypass_protocol_errors; - 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 allow_http2; - int mirror_client_version; - int decrypt_mirror_enabled; - int mirror_profile_id; - */ int decryption_profile_id; }; @@ -136,67 +118,6 @@ void intercept_param_new_cb(int table_id, const char* key, const char* table_lin } } - /* - exclusions=cJSON_GetObjectItem(json, "dynamic_bypass"); - 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, "mutual_authentication"); - if(item && item->type==cJSON_Number) param->bypass_mutual_auth=item->valueint; - item=cJSON_GetObjectItem(exclusions, "cert_pinning"); - if(item && item->type==cJSON_Number) param->bypass_pinning=item->valueint; - item=cJSON_GetObjectItem(exclusions, "protocol_errors"); - if(item && item->type==cJSON_Number) param->bypass_protocol_errors=item->valueint; - - } - cert_verify=cJSON_GetObjectItem(json, "certificate_checks"); - 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(cert_verify, "fail_action"); - if(item && item->type==cJSON_String) - { - if(0==strcasecmp(item->valuestring, "Fail-Close")) - { - param->block_fake_cert=1; - } - } - } - ssl_ver=cJSON_GetObjectItem(json, "protocol_version"); - if(ssl_ver) - { - item=cJSON_GetObjectItem(ssl_ver, "mirror_client"); - if(item && item->type==cJSON_Number) param->mirror_client_version=item->valueint; - if(!param->mirror_client_version) - { - item=cJSON_GetObjectItem(ssl_ver, "min"); - if(item && item->type==cJSON_String) param->ssl_min_version=sslver_str2num(item->valuestring); - item=cJSON_GetObjectItem(ssl_ver, "max"); - if(item && item->type==cJSON_String) param->ssl_max_version=sslver_str2num(item->valuestring); - if(param->ssl_min_version<0||param->ssl_max_version<0) - { - param->mirror_client_version=1; - TFE_LOG_ERROR(enforcer->logger, "Invalid intercept parameter: ssl version = %s", item->valuestring); - } - } - item=cJSON_GetObjectItem(ssl_ver, "allow_http2"); - if(item && item->type==cJSON_Number) param->allow_http2=item->valueint; - } - */ item=cJSON_GetObjectItem(json, "decryption"); if(item) { @@ -438,7 +359,7 @@ enum ssl_stream_action ssl_policy_enforce(struct ssl_stream *upstream, void* u_p TFE_LOG_INFO(enforcer->logger, "Failed to get decryption parameter of profile %s.", profile_id_str); return SSL_ACTION_PASSTHROUGH; } - int pinning_staus=0, is_ev=0, is_ct=0, is_mauth=0, has_error=0, app_staus=0; + int pinning_staus=0, is_ev=0, is_ct=0, is_mauth=0, has_error=0, ja3_pinning_status=0; if(!profile_param->mirror_client_version) { ret=ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_PROTOCOL_MIN_VERSION, profile_param->ssl_min_version); @@ -460,7 +381,7 @@ enum ssl_stream_action ssl_policy_enforce(struct ssl_stream *upstream, void* u_p ret=ssl_stream_get_integer_opt(upstream, SSL_STREAM_OPT_PINNING_STATUS, &pinning_staus); assert(ret==0); - ret=ssl_stream_get_integer_opt(upstream, SSL_STREAM_OPT_APP_STATUS, &app_staus); + ret=ssl_stream_get_integer_opt(upstream, SSL_STREAM_OPT_JA3_PINNING_STATUS, &ja3_pinning_status); assert(ret==0); ret=ssl_stream_get_integer_opt(upstream, SSL_STREAM_OPT_IS_EV_CERT, &is_ev); assert(ret==0); @@ -469,23 +390,23 @@ enum ssl_stream_action ssl_policy_enforce(struct ssl_stream *upstream, void* u_p ret=ssl_stream_get_integer_opt(upstream, SSL_STREAM_OPT_HAS_PROTOCOL_ERRORS, &has_error); assert(ret==0); - if ((pinning_staus==1 && app_staus && profile_param->bypass_uninstall_cert_traffic) || - (pinning_staus==1 && !app_staus && profile_param->bypass_pinning) || - (is_mauth && profile_param->bypass_mutual_auth) || - (is_ev && profile_param->bypass_ev_cert) || - (is_ct && profile_param->bypass_ct_cert) || - (has_error && profile_param->bypass_protocol_errors)) - { + if ((pinning_staus == 1 && ja3_pinning_status == JA3_PINNING_STATUS_NOT_PINNING && profile_param->bypass_uninstall_cert_traffic) || + ((pinning_staus == 1 || ja3_pinning_status == JA3_PINNING_STATUS_IS_PINNING) && ja3_pinning_status != JA3_PINNING_STATUS_NOT_PINNING && profile_param->bypass_pinning) || + (is_mauth && profile_param->bypass_mutual_auth) || + (is_ev && profile_param->bypass_ev_cert) || + (is_ct && profile_param->bypass_ct_cert) || + (has_error && profile_param->bypass_protocol_errors)) + { action=SSL_ACTION_PASSTHROUGH; - TFE_LOG_DEBUG(enforcer->logger, "%s %s enforce policy_id %d, action PASSTHROUGH due to uninstall_cert:%d, pinning:%d, mutual_auth:%d, is_ev:%d, is_ct:%d, has_error:%d", - addr_string, sni, policy_param->policy_id, - ((pinning_staus == 1 && app_staus && profile_param->bypass_uninstall_cert_traffic) ? 1 : 0), - ((pinning_staus == 1 && !app_staus && profile_param->bypass_pinning) ? 1 : 0), - ((is_mauth && profile_param->bypass_mutual_auth) ? 1 : 0), - ((is_ev && profile_param->bypass_ev_cert) ? 1 : 0), - ((is_ct && profile_param->bypass_ct_cert) ? 1 : 0), - ((has_error && profile_param->bypass_protocol_errors) ? 1 : 0)); - } + TFE_LOG_DEBUG(enforcer->logger, "%s %s enforce policy_id %d, action PASSTHROUGH due to uninstall_cert:%d, pinning:%d, mutual_auth:%d, is_ev:%d, is_ct:%d, has_error:%d", + addr_string, sni, policy_param->policy_id, + ((pinning_staus == 1 && ja3_pinning_status == JA3_PINNING_STATUS_NOT_PINNING && profile_param->bypass_uninstall_cert_traffic) ? 1 : 0), + (((pinning_staus == 1 || ja3_pinning_status == JA3_PINNING_STATUS_IS_PINNING) && ja3_pinning_status != JA3_PINNING_STATUS_NOT_PINNING && profile_param->bypass_pinning) ? 1 : 0), + ((is_mauth && profile_param->bypass_mutual_auth) ? 1 : 0), + ((is_ev && profile_param->bypass_ev_cert) ? 1 : 0), + ((is_ct && profile_param->bypass_ct_cert) ? 1 : 0), + ((has_error && profile_param->bypass_protocol_errors) ? 1 : 0)); + } else { action=SSL_ACTION_INTERCEPT; diff --git a/resource/pangu/pangu_http.json b/resource/pangu/pangu_http.json index 0c10e89..91c8195 100644 --- a/resource/pangu/pangu_http.json +++ b/resource/pangu/pangu_http.json @@ -258,6 +258,14 @@ "0\t0\t2\t1\t1\t{}\t{\"protocol\":\"SSL\",\"keyring\":765,\"decryption\":0},\"decrypt_mirror\":{\"enable\":0}}\t1\t2", "4\t0\t2\t1\t1\t{}\t{\"protocol\":\"SSL\",\"keyring\":1,\"decryption\":0},\"decrypt_mirror\":{\"enable\":0}}\t1\t2" ] + }, + { + "table_name": "PXY_SSL_FINGERPRINT", + "table_content": [ + "1\t599f223c2c9ee5702f5762913889dc21\t0\t1", + "2\teb149984fc9c44d85ed7f12c90d818be\t1\t0", + "3\te6573e91e6eb777c0933c5b8f97f10cd\t1\t1" + ] } ] } diff --git a/resource/pangu/table_info.conf b/resource/pangu/table_info.conf index e4d8217..99077b0 100644 --- a/resource/pangu/table_info.conf +++ b/resource/pangu/table_info.conf @@ -56,4 +56,5 @@ 36 TSG_SECURITY_SOURCE_LOCATION virtual TSG_OBJ_GEO_LOCATION -- 37 TSG_SECURITY_DESTINATION_LOCATION virtual TSG_OBJ_GEO_LOCATION -- 38 TSG_FIELD_DOH_QNAME virtual ["TSG_OBJ_FQDN","TSG_OBJ_FQDN_CAT"] -- -39 TSG_FIELD_DOH_HOST virtual ["TSG_OBJ_FQDN","TSG_OBJ_FQDN_CAT"] -- \ No newline at end of file +39 TSG_FIELD_DOH_HOST virtual ["TSG_OBJ_FQDN","TSG_OBJ_FQDN_CAT"] -- +40 TSG_JA3_TABLE plugin {"key":2,"valid":4} \ No newline at end of file