#include #include #include #include #include #include #include #include struct ssl_policy_enforcer { struct maat *maat; }; struct decryption_param { uuid_t uuid; int ref_cnt; int bypass_ev_cert; int bypass_ct_cert; int bypass_mutual_auth; int bypass_pinning; int bypass_uninstall_cert_traffic; 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; }; static void profile_param_dup_cb(const char *table_name, void **to, void **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; } static void profile_param_free_cb(const char *table_name, void **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)) { char uuid_str[UUID_STRING_SIZE]; uuid_unparse(param->uuid, uuid_str); TFE_LOG_INFO(g_default_logger, "Del decryption profile: %s", uuid_str); free(param); *ad = NULL; } } static void profile_param_free(struct decryption_param *param) { profile_param_free_cb(NULL, (void **)¶m, 0, NULL); } static void profile_param_new_cb(const char *table_name, const char *key, const char *table_line, void **ad, long argl, void *argp) { cJSON *json = NULL; cJSON *object = NULL; cJSON *exclusions = NULL; cJSON *cert_verify = NULL; cJSON *approach = NULL; cJSON *ssl_ver = NULL; cJSON *item = NULL; struct decryption_param *param = NULL; char *json_str = strdup(table_line); json = cJSON_Parse(json_str); if (json == NULL) { TFE_LOG_ERROR(g_default_logger, "Invalid decryption parameter: (invalid json format) %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; uuid_parse(key, param->uuid); object = cJSON_GetObjectItem(json, "decryption"); if (!object || !cJSON_IsObject(object)) { TFE_LOG_ERROR(g_default_logger, "Invalid decryption parameter: (invalid decryption param) %s", table_line); goto error_out; } exclusions = cJSON_GetObjectItem(object, "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; } item = cJSON_GetObjectItem(exclusions, "trusted_root_cert_is_not_installed_on_client"); if (item && item->type == cJSON_Number) { param->bypass_uninstall_cert_traffic = item->valueint; } } ssl_ver = cJSON_GetObjectItem(object, "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(g_default_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(object, "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(g_default_logger, "Add decryption profile: %s", key); cJSON_Delete(json); free(json_str); return; error_out: if (json) { cJSON_Delete(json); } if (json_str) { free(json_str); } if (param) { free(param); } return; } struct ssl_policy_enforcer *ssl_policy_enforcer_create() { UNUSED int ret = 0; struct ssl_policy_enforcer *enforcer = ALLOC(struct ssl_policy_enforcer, 1); enforcer->maat = tfe_get_maat_handle(); ret = maat_plugin_table_ex_schema_register(enforcer->maat, "DECRYPTION_PROFILE", profile_param_new_cb, profile_param_free_cb, profile_param_dup_cb, 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; enum ssl_stream_action action = SSL_ACTION_PASSTHROUGH; UNUSED int ret = 0; char sni[512]; char addr_string[512]; char rule_uuid_str[UUID_STRING_SIZE]; char decrypted_uuid_str[UUID_STRING_SIZE]; uuid_t rule_uuid; uuid_t decrypted_uuid; uuid_t trusted_keyring_uuid; uuid_t untrusted_keyring_uuid; ssl_stream_get_policy_id(upstream, &rule_uuid); ssl_stream_get_decrypted_profile_id(upstream, &decrypted_uuid); ssl_stream_get_trusted_keyring_profile_id(upstream, &trusted_keyring_uuid); ssl_stream_get_untrusted_keyring_profile_id(upstream, &untrusted_keyring_uuid); ssl_stream_get_string_opt(upstream, SSL_STREAM_OPT_SNI, sni, sizeof(sni)); ssl_stream_get_string_opt(upstream, SSL_STREAM_OPT_ADDR, addr_string, sizeof(addr_string)); uuid_unparse(rule_uuid, rule_uuid_str); uuid_unparse(decrypted_uuid, decrypted_uuid_str); TFE_LOG_DEBUG(g_default_logger, "%s %s enforce policy %s", addr_string, sni, rule_uuid_str); struct decryption_param *profile_param = (struct decryption_param *)maat_plugin_table_get_ex_data(enforcer->maat, "DECRYPTION_PROFILE", (const char *)decrypted_uuid_str, strlen(decrypted_uuid_str)); if (profile_param == NULL) { TFE_LOG_INFO(g_default_logger, "Failed to get decryption parameter of profile %s.", decrypted_uuid_str); ssl_stream_set_cmsg_string(upstream, TFE_CMSG_SSL_PASSTHROUGH_REASON, "Invalid Decryption Param"); return SSL_ACTION_PASSTHROUGH; } int pinning_staus = 0, is_ev = 0, is_ct = 0, is_mauth = 0, has_error = 0, is_app_not_pinning = 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); assert(ret == 1); ret = ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_PROTOCOL_MAX_VERSION, profile_param->ssl_max_version); assert(ret == 1); } if (profile_param->allow_http2) { ret = ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_ENABLE_ALPN, 1); assert(ret == 1); } ret = ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_NO_VERIFY_COMMON_NAME, profile_param->no_verify_cn); assert(ret == 1); ret = ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_NO_VERIFY_ISSUER, profile_param->no_verify_issuer); assert(ret == 1); ret = ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_NO_VERIFY_SELF_SIGNED, profile_param->no_verify_self_signed); assert(ret == 1); ret = ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_NO_VERIFY_EXPIRY_DATE, profile_param->no_verify_expry_date); assert(ret == 1); if (profile_param->block_fake_cert) { ret = ssl_stream_set_integer_opt(upstream, SSL_STREAM_OPT_BLOCK_FAKE_CERT, 1); assert(ret == 1); } ret = ssl_stream_set_uuid_opt(upstream, SSL_STREAM_OPT_KEYRING_FOR_TRUSTED, &trusted_keyring_uuid); assert(ret == 1); ret = ssl_stream_set_uuid_opt(upstream, SSL_STREAM_OPT_KEYRING_FOR_UNTRUSTED, &untrusted_keyring_uuid); assert(ret == 1); 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, &is_app_not_pinning); assert(ret == 0); ret = ssl_stream_get_integer_opt(upstream, SSL_STREAM_OPT_IS_EV_CERT, &is_ev); assert(ret == 0); ret = ssl_stream_get_integer_opt(upstream, SSL_STREAM_OPT_IS_MUTUAL_AUTH, &is_mauth); assert(ret == 0); ret = ssl_stream_get_integer_opt(upstream, SSL_STREAM_OPT_IS_CT_CERT, &is_ct); assert(ret == 0); ret = ssl_stream_get_integer_opt(upstream, SSL_STREAM_OPT_HAS_PROTOCOL_ERRORS, &has_error); assert(ret == 0); if (pinning_staus == 1 && is_app_not_pinning == 1 && profile_param->bypass_uninstall_cert_traffic) { action = SSL_ACTION_PASSTHROUGH; ssl_stream_set_cmsg_string(upstream, TFE_CMSG_SSL_PASSTHROUGH_REASON, "Certificate Not Installed"); TFE_LOG_DEBUG(g_default_logger, "%s %s enforce policy %s, action PASSTHROUGH due to Certificate Not Installed", addr_string, sni, rule_uuid_str); } else if (pinning_staus == 1 && is_app_not_pinning == 0 && profile_param->bypass_pinning) { action = SSL_ACTION_PASSTHROUGH; ssl_stream_set_cmsg_string(upstream, TFE_CMSG_SSL_PASSTHROUGH_REASON, "Certificate Pinning"); TFE_LOG_DEBUG(g_default_logger, "%s %s enforce policy %s, action PASSTHROUGH due to Certificate Pinning", addr_string, sni, rule_uuid_str); } else if (is_mauth && profile_param->bypass_mutual_auth) { action = SSL_ACTION_PASSTHROUGH; ssl_stream_set_cmsg_string(upstream, TFE_CMSG_SSL_PASSTHROUGH_REASON, "Mutual Authentication"); TFE_LOG_DEBUG(g_default_logger, "%s %s enforce policy %s, action PASSTHROUGH due to Mutual Authentication", addr_string, sni, rule_uuid_str); } else if (is_ev && profile_param->bypass_ev_cert) { action = SSL_ACTION_PASSTHROUGH; ssl_stream_set_cmsg_string(upstream, TFE_CMSG_SSL_PASSTHROUGH_REASON, "EV Certificate"); TFE_LOG_DEBUG(g_default_logger, "%s %s enforce policy %s, action PASSTHROUGH due to EV Certificate", addr_string, sni, rule_uuid_str); } else if (is_ct && profile_param->bypass_ct_cert) { action = SSL_ACTION_PASSTHROUGH; ssl_stream_set_cmsg_string(upstream, TFE_CMSG_SSL_PASSTHROUGH_REASON, "Certificate Transparency"); TFE_LOG_DEBUG(g_default_logger, "%s %s enforce policy %s, action PASSTHROUGH due to Certificate Transparency", addr_string, sni, rule_uuid_str); } else if (has_error && profile_param->bypass_protocol_errors) { action = SSL_ACTION_PASSTHROUGH; ssl_stream_set_cmsg_string(upstream, TFE_CMSG_SSL_PASSTHROUGH_REASON, "Protocol Errors"); TFE_LOG_DEBUG(g_default_logger, "%s %s enforce policy %s, action PASSTHROUGH due to Protocol Errors", addr_string, sni, rule_uuid_str); } else { action = SSL_ACTION_INTERCEPT; } profile_param_free(profile_param); profile_param = NULL; return action; }