From 172b3b2907f2288efe82884f2656731b5b59f66a Mon Sep 17 00:00:00 2001 From: luwenpeng Date: Thu, 16 Apr 2020 16:17:00 +0800 Subject: [PATCH] =?UTF-8?q?TSG-1280=20Proxy=20TFE=E4=BD=BF=E7=94=A8Decrypt?= =?UTF-8?q?ion=20Profile=E8=8E=B7=E5=BE=97=E9=83=A8=E5=88=86=E6=8B=A6?= =?UTF-8?q?=E6=88=AA=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugin/business/ssl-policy/src/ssl_policy.cpp | 284 ++++++++++++++++-- 1 file changed, 259 insertions(+), 25 deletions(-) diff --git a/plugin/business/ssl-policy/src/ssl_policy.cpp b/plugin/business/ssl-policy/src/ssl_policy.cpp index 498ea49..f127557 100644 --- a/plugin/business/ssl-policy/src/ssl_policy.cpp +++ b/plugin/business/ssl-policy/src/ssl_policy.cpp @@ -9,7 +9,8 @@ extern Maat_feather_t g_business_maat; struct ssl_policy_enforcer { Maat_feather_t maat; - int table_id; + int policy_table_id; + int profile_table_id; void* logger; }; struct intercept_param @@ -17,6 +18,7 @@ struct intercept_param int policy_id; int ref_cnt; int keyring; + /* int bypass_ev_cert; int bypass_ct_cert; int bypass_mutual_auth; @@ -33,6 +35,27 @@ struct intercept_param int mirror_client_version; int decrypt_mirror_enabled; int mirror_profile_id; + */ + int decryption_profile_id; +}; + +struct decryption_param +{ + int ref_cnt; + 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; }; void intercept_param_dup_cb(int table_id, MAAT_PLUGIN_EX_DATA* to, MAAT_PLUGIN_EX_DATA* from, long argl, void* argp) @@ -54,7 +77,7 @@ void intercept_param_new_cb(int table_id, const char* key, const char* table_lin 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; + cJSON *json=NULL, *item=NULL; struct intercept_param* param=NULL; struct ssl_policy_enforcer* enforcer=(struct ssl_policy_enforcer*)argp; @@ -87,10 +110,13 @@ void intercept_param_new_cb(int table_id, const char* key, const char* table_lin param=ALLOC(struct intercept_param, 1); param->policy_id=atoi(key); param->ref_cnt=1; + /* param->bypass_mutual_auth=1; param->bypass_pinning=1; param->mirror_client_version=1; + */ param->keyring=1; + param->decryption_profile_id=0; item=cJSON_GetObjectItem(json, "keyring"); if(item) @@ -109,6 +135,7 @@ void intercept_param_new_cb(int table_id, const char* key, const char* table_lin } } + /* exclusions=cJSON_GetObjectItem(json, "dynamic_bypass"); if(exclusions) { @@ -168,8 +195,26 @@ void intercept_param_new_cb(int table_id, const char* key, const char* table_lin 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) + { + if(item->type==cJSON_Number) + { + param->decryption_profile_id=item->valueint; + } + else if(item->type==cJSON_String) + { + param->decryption_profile_id=atoi(item->valuestring); + } + else + { + TFE_LOG_ERROR(enforcer->logger, "Invalid intercept parameter:%d invalid decryption format", param->policy_id); + } + } *ad=param; TFE_LOG_INFO(enforcer->logger, "Add intercept policy: %d", param->policy_id); + TFE_LOG_DEBUG(enforcer->logger, "intercept policy id=%d, key=%s, json=%s", param->policy_id, key, json_str); error_out: cJSON_Delete(json); free(json_str); @@ -196,14 +241,157 @@ void intercept_param_free(struct intercept_param* param) intercept_param_free_cb(0, (void**)¶m, 0, NULL); return; } + +void profile_param_dup_cb(int table_id, MAAT_PLUGIN_EX_DATA* to, MAAT_PLUGIN_EX_DATA* from, long argl, void* argp) +{ + struct decryption_param* param= (struct decryption_param*) *from; + if(param) + { + __sync_add_and_fetch(&(param->ref_cnt), 1); + *to = param; + } + else + { + *to=NULL; + } + return; +} +void profile_param_free_cb(int table_id, MAAT_PLUGIN_EX_DATA* ad, long argl, void* argp) +{ + struct decryption_param* param= (struct decryption_param*) *ad; + if(param==NULL) + { + return; + } + + if ((__sync_sub_and_fetch(¶m->ref_cnt, 1) == 0)) + { + free(param); + *ad=NULL; + } +} +void profile_param_free(struct decryption_param* param) +{ + profile_param_free_cb(0, (void**)¶m, 0, NULL); + return; +} +void profile_param_new_cb(int table_id, const char* key, const char* table_line, MAAT_PLUGIN_EX_DATA* ad, long argl, void* argp) +{ + int i; + int ret=0; + size_t offset[3]={0} , len[3]={0}; + char* json_str[3]={NULL}; + cJSON *json[3]={NULL}, *exclusions=NULL, *cert_verify=NULL, *approach=NULL, *ssl_ver=NULL, *item=NULL; + struct decryption_param* param=NULL; + struct ssl_policy_enforcer* enforcer=(struct ssl_policy_enforcer*)argp; + + for (i=0; i<3; i++) + { + ret=Maat_helper_read_column(table_line, i+3, &offset[i], &len[i]); + if(ret<0) + { + TFE_LOG_ERROR(enforcer->logger, "Get decryption param: %s", table_line); + goto error_out; + } + json_str[i]=ALLOC(char, len[i]+1); + memcpy(json_str[i], table_line+offset[i], len[i]); + json[i]=cJSON_Parse(json_str[i]); + if(json[i]==NULL) + { + TFE_LOG_ERROR(enforcer->logger, "Invalid decryption parameter: %s", table_line); + goto error_out; + } + } + + param=ALLOC(struct decryption_param, 1); + param->ref_cnt=1; + param->bypass_mutual_auth=1; + param->bypass_pinning=1; + param->mirror_client_version=1; + + exclusions=cJSON_GetObjectItem(json[0], "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; + + } + ssl_ver=cJSON_GetObjectItem(json[1], "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; + } + cert_verify=cJSON_GetObjectItem(json[2], "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; + } + } + } + + *ad=param; + TFE_LOG_INFO(enforcer->logger, "decryption profile key=%s, value=%s, %s, %s", key, json_str[0], json_str[1], json_str[2]); +error_out: + for (i=0; i<3; i++) + { + if (json[i]) + cJSON_Delete(json[i]); + if (json_str[i]) + free(json_str[i]); + } + return; +} struct ssl_policy_enforcer* ssl_policy_enforcer_create(void* logger) { struct ssl_policy_enforcer* enforcer=ALLOC(struct ssl_policy_enforcer, 1); enforcer->maat=g_business_maat; enforcer->logger=logger; - enforcer->table_id=Maat_table_register(enforcer->maat, "TSG_SECURITY_COMPILE"); + enforcer->policy_table_id=Maat_table_register(enforcer->maat, "TSG_SECURITY_COMPILE"); + assert(enforcer->policy_table_id >= 0); + enforcer->profile_table_id=Maat_table_register(enforcer->maat, "TSG_PROFILE_DECRYPTION"); + assert(enforcer->profile_table_id >= 0); UNUSED int ret=Maat_plugin_EX_register(enforcer->maat, - enforcer->table_id, + enforcer->policy_table_id, intercept_param_new_cb, intercept_param_free_cb, intercept_param_dup_cb, @@ -211,22 +399,33 @@ struct ssl_policy_enforcer* ssl_policy_enforcer_create(void* logger) 0, enforcer); assert(ret==0); + ret=Maat_plugin_EX_register(enforcer->maat, + enforcer->profile_table_id, + profile_param_new_cb, + profile_param_free_cb, + profile_param_dup_cb, + NULL, + 0, + enforcer); + assert(ret==0); return enforcer; } enum ssl_stream_action ssl_policy_enforce(struct ssl_stream *upstream, void* u_para) { UNUSED struct ssl_policy_enforcer* enforcer=(struct ssl_policy_enforcer*)u_para; - struct intercept_param *param=NULL; + struct intercept_param *policy_param=NULL; + struct decryption_param *profile_param=NULL; enum ssl_stream_action action=SSL_ACTION_PASSTHROUGH; UNUSED int ret=0; int policy_id=0; char policy_id_str[16]={0}; + char profile_id_str[16]={0}; char sni[512], addr_string[512]; ret=ssl_stream_get_integer_opt(upstream, SSL_STREAM_OPT_INTERCEPT_POLICY_ID, &policy_id); assert(ret==0); snprintf(policy_id_str, sizeof(policy_id_str), "%d", policy_id); - param=(struct intercept_param *)Maat_plugin_get_EX_data(enforcer->maat, enforcer->table_id, policy_id_str); - if(param==NULL) + policy_param=(struct intercept_param *)Maat_plugin_get_EX_data(enforcer->maat, enforcer->policy_table_id, policy_id_str); + if(policy_param==NULL) { TFE_LOG_INFO(enforcer->logger, "Failed to get intercept parameter of policy %d.", policy_id); return SSL_ACTION_PASSTHROUGH; @@ -237,25 +436,33 @@ enum ssl_stream_action ssl_policy_enforce(struct ssl_stream *upstream, void* u_p ssl_stream_get_string_opt(upstream, SSL_STREAM_OPT_ADDR, addr_string, sizeof(addr_string)); TFE_LOG_DEBUG(enforcer->logger, "%s %s enforce policy %d", addr_string, sni, policy_id); } - int pinning_staus=0, is_ev=0, is_ct=0, is_mauth=0, has_error=0; - if(!param->mirror_client_version) + + snprintf(profile_id_str, sizeof(profile_id_str), "%u", policy_param->decryption_profile_id); + profile_param=(struct decryption_param *)Maat_plugin_get_EX_data(enforcer->maat, enforcer->profile_table_id, profile_id_str); + if (profile_param==NULL) { - ret=ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_PROTOCOL_MIN_VERSION, param->ssl_min_version); - ret=ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_PROTOCOL_MAX_VERSION, param->ssl_max_version); + TFE_LOG_INFO(enforcer->logger, "Failed to get decryption parameter of profile %s.", profile_id_str); + return SSL_ACTION_PASSTHROUGH; } - if(param->allow_http2) + int pinning_staus=0, is_ev=0, is_ct=0, is_mauth=0, has_error=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); + ret=ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_PROTOCOL_MAX_VERSION, profile_param->ssl_max_version); + } + if(profile_param->allow_http2) { ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_ENABLE_ALPN, 1); } - ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_NO_VERIFY_COMMON_NAME, param->no_verify_cn); - ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_NO_VERIFY_ISSUER, param->no_verify_issuer); - ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_NO_VERIFY_SELF_SIGNED, param->no_verify_self_signed); - ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_NO_VERIFY_EXPIRY_DATE, param->no_verify_expry_date); - if(param->block_fake_cert) + ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_NO_VERIFY_COMMON_NAME, profile_param->no_verify_cn); + ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_NO_VERIFY_ISSUER, profile_param->no_verify_issuer); + ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_NO_VERIFY_SELF_SIGNED, profile_param->no_verify_self_signed); + ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_NO_VERIFY_EXPIRY_DATE, profile_param->no_verify_expry_date); + if(profile_param->block_fake_cert) { ret=ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_BLOCK_FAKE_CERT, 1); } - ret=ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_KEYRING_ID, param->keyring); + ret=ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_KEYRING_ID, policy_param->keyring); ret=ssl_stream_get_integer_opt(upstream, SSL_STREAM_OPT_PINNING_STATUS, &pinning_staus); assert(ret==0); @@ -265,11 +472,11 @@ enum ssl_stream_action ssl_policy_enforce(struct ssl_stream *upstream, void* u_p ret=ssl_stream_get_integer_opt(upstream, SSL_STREAM_OPT_IS_CT_CERT, &is_ct); ret=ssl_stream_get_integer_opt(upstream, SSL_STREAM_OPT_HAS_PROTOCOL_ERRORS, &has_error); assert(ret==0); - if( (pinning_staus==1 && param->bypass_pinning) || - (is_mauth && param->bypass_mutual_auth) || - (is_ev && param->bypass_ev_cert) || - (is_ct && param->bypass_ct_cert) || - (has_error && param->bypass_protocol_errors)) + if( (pinning_staus==1 && 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; } @@ -277,8 +484,35 @@ enum ssl_stream_action ssl_policy_enforce(struct ssl_stream *upstream, void* u_p { action=SSL_ACTION_INTERCEPT; } - intercept_param_free(param); - param=NULL; + + TFE_LOG_DEBUG(enforcer->logger, "%s %s action:%s enforce \"policy_id:%d, keyring:%d, decryption_profile_id:%d, bypass_ct_cert:%d, bypass_ev_cert:%d, bypass_mutual_auth:%s, bypass_pinning:%d, bypass_protocol_errors:%d, " + "no_verify_cn:%d, no_verify_expry_date:%d, no_verify_issuer:%d, no_verify_self_signed:%d, block_fake_cert:%d, ssl_max_version:%d, ssl_min_version:%d, mirror_client_version:%d, allow_http2:%d\"", + addr_string, sni, (action == SSL_ACTION_INTERCEPT ? "INTERCEPT" : "PASSTHROUGH"), + policy_param->policy_id, + policy_param->keyring, + policy_param->decryption_profile_id, + + profile_param->bypass_ct_cert, + profile_param->bypass_ev_cert, + profile_param->bypass_mutual_auth, + profile_param->bypass_pinning, + profile_param->bypass_protocol_errors, + + profile_param->no_verify_cn, + profile_param->no_verify_expry_date, + profile_param->no_verify_issuer, + profile_param->no_verify_self_signed, + profile_param->block_fake_cert, + + profile_param->ssl_max_version, + profile_param->ssl_min_version, + profile_param->mirror_client_version, + profile_param->allow_http2 + ); + intercept_param_free(policy_param); + profile_param_free(profile_param); + policy_param=NULL; + profile_param=NULL; return action; }