#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) { 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) { UNUSED struct ssl_policy_enforcer* enforcer=(struct ssl_policy_enforcer*)u_para; UNUSED int ret=0; int pinning_staus=0, is_ev=0, is_mauth=0; ret=ssl_stream_get_integer_opt(upstream, SSL_STREAM_OPT_PINNING_STATUS, &pinning_staus); assert(ret==1); ret=ssl_stream_get_integer_opt(upstream, SSL_STREAM_OPT_IS_EV_CERT, &is_ev); 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; } else { return SSL_ACTION_INTERCEPT; } }