/*- * SSLsplit - transparent SSL/TLS interception * https://www.roe.ch/SSLsplit * * Copyright (c) 2009-2018, Daniel Roethlisberger . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "opts.h" #include "sys.h" #include "compat.h" #include #include #include #include #include #include #include #include #include #include "cfgparser.h" #include "util.h" extern "C" { #include #include } #include struct tfe_config * tfe_config_new() { struct tfe_config * __config; __config = (struct tfe_config *) malloc(sizeof(struct tfe_config)); memset(__config, 0, sizeof(struct tfe_config)); __config->sslcomp = 1; __config->sslmethod = SSLv23_method; return __config; } #define __TFE_STRING_MAX 4096 static int __proxyspec_parse_each_entry(tfe_config * cfg, TfeConfigParser & ini_config_object, const std::string & str_proxyspec) { const std::string str_section = "proxyspec:" + str_proxyspec; const char * cstr_str_section = str_section.c_str(); std::string str_listen_addr; std::string str_listen_port; std::string str_natengine; std::string str_protocol; str_listen_addr = ini_config_object.GetValue(str_section, "listen_addr"); str_listen_port = ini_config_object.GetValue(str_section, "listen_port"); str_natengine = ini_config_object.GetValue(str_section, "natengine"); str_protocol = ini_config_object.GetValue(str_section, "protocol"); struct proxyspec * __spec = (proxyspec *) malloc(sizeof(proxyspec)); memset(__spec, 0, sizeof(proxyspec)); if (str_protocol == "http") { __spec->ssl = 0; __spec->http = 1; __spec->upgrade = 0; } else if (str_protocol == "https") { __spec->ssl = 1; __spec->http = 1; __spec->upgrade = 0; } else if (str_protocol == "ssl") { __spec->ssl = 1; __spec->http = 0; __spec->upgrade = 0; } else if (str_protocol == "autossl") { __spec->ssl = 0; __spec->http = 0; __spec->upgrade = 1; } __spec->natengine = strdup(str_natengine.c_str()); assert(__spec->natengine != nullptr); int ret = sys_sockaddr_parse(&__spec->listen_addr, &__spec->listen_addrlen, str_listen_addr.c_str(), str_listen_port.c_str(), AF_INET, EVUTIL_AI_PASSIVE); if (ret < 0) { throw cfg_invalid_format("", "", "", ""); } log_dbg_printf("Proxyspec %s: Listen Address %s, Port %s, Protocol %s\n", str_proxyspec.c_str(), str_listen_addr.c_str(), str_listen_port.c_str(), str_protocol.c_str()); /* 插入到Spec链表头部 */ __spec->next = cfg->spec; cfg->spec = __spec; return 0; } static int __ssl_protocol_load_from_file(tfe_config * cfg, TfeConfigParser & cfg_parser) { bool __en_tls_1_0 = true; bool __en_tls_1_1 = true; bool __en_tls_1_2 = true; bool __en_ssl_v2 = true; bool __en_ssl_v3 = true; __en_tls_1_0 = cfg_parser.GetValueWithDefault("ssl_protocol", "tls_v1_0", __en_tls_1_0); __en_tls_1_1 = cfg_parser.GetValueWithDefault("ssl_protocol", "tls_v1_1", __en_tls_1_1); __en_tls_1_2 = cfg_parser.GetValueWithDefault("ssl_protocol", "tls_v1_2", __en_tls_1_2); __en_ssl_v2 = cfg_parser.GetValueWithDefault("ssl_protocol", "ssl_v2", __en_ssl_v2); __en_ssl_v3 = cfg_parser.GetValueWithDefault("ssl_protocol", "ssl_v3", __en_ssl_v3); if (!__en_tls_1_0) cfg->no_tls10 = 1; if (!__en_tls_1_1) cfg->no_tls11 = 1; if (!__en_tls_1_2) cfg->no_tls12 = 1; if (!__en_ssl_v2) cfg->no_ssl2 = 1; if (!__en_ssl_v3) cfg->no_ssl3 = 1; return 0; } static int __maatframe_load_from_file(tfe_config * cfg, TfeConfigParser & cfg_parser) { if (cfg->maat_config == nullptr) { cfg->maat_config = new tfe_maat_config; } tfe_maat_config * __maat_config = cfg->maat_config; /* Maat配置加载来源 */ __maat_config->cfg_load_from = (tfe_maat_config::cfg_load_from_t) cfg_parser.GetValue("maat", "pz_source_mode"); switch (__maat_config->cfg_load_from) { case tfe_maat_config::LOAD_FROM_JSON_FILE: __maat_config->str_json_file_path = cfg_parser.GetValue("maat", "pz_json_file"); break; case tfe_maat_config::LOAD_FROM_REDIS_SERVER: __maat_config->str_redis_addr = cfg_parser.GetValue("maat", "pz_redis_server"); __maat_config->redir_port = cfg_parser.GetValue("maat", "pz_redir_port"); __maat_config->redis_db_index = cfg_parser.GetValue("maat", "pz_redir_db_index"); break; case tfe_maat_config::LOAD_FROM_LOAD_IRIS: __maat_config->str_full_cfg_dir = cfg_parser.GetValue("maat", "pz_iris_full_dir"); __maat_config->str_inc_cfg_dir = cfg_parser.GetValue("maat", "pz_iris_inc_dir"); break; default: throw cfg_invalid_format(cfg_parser.Source(), "maat", "pz_source_mode", "invalid source mode"); } /* Maat表定义文件路径 */ __maat_config->str_table_info_file = cfg_parser.GetValue("maat", "table_info_file"); __maat_config->str_log_file_path = cfg_parser.GetValue("maat", "logfile"); __maat_config->str_stat_file = cfg_parser.GetValue("maat", "statfile"); /* scan interval and effective interval */ auto __result_scan_interval_ms = cfg_parser.TryGetValue("maat", "scan_interval_ms"); if (__result_scan_interval_ms.first) { __maat_config->is_effect_interval_ms_set = true; __maat_config->effect_interval_ms = __result_scan_interval_ms.second; } auto __result_effect_interval_ms = cfg_parser.TryGetValue("maat", "effect_interval_ms"); if (__result_effect_interval_ms.first) { __maat_config->is_effect_interval_ms_set = true; __maat_config->effect_interval_ms = __result_effect_interval_ms.second; } log_dbg_printf("Maat pz source mode = %d", __maat_config->cfg_load_from); log_dbg_printf("Maat json file path = %s", __maat_config->str_json_file_path.c_str()); log_dbg_printf("Maat redis server = %s", __maat_config->str_redis_addr.c_str()); log_dbg_printf("Maat iris full path = %s", __maat_config->str_full_cfg_dir.c_str()); log_dbg_printf("Maat iris inc path = %s", __maat_config->str_inc_cfg_dir.c_str()); log_dbg_printf("Maat table info file = %s", __maat_config->str_table_info_file.c_str()); log_dbg_printf("Maat log file = %s", __maat_config->str_log_file_path.c_str()); return 0; } int __maatframe_init(tfe_config * cfg) { tfe_maat_config * __maat_config = cfg->maat_config; /* 创建Logger,该Logger仅供Maat输出日志使用 */ void * logger = MESA_create_runtime_log_handle(__maat_config->str_log_file_path.c_str(), 0); if (logger == nullptr) { throw std::runtime_error(string_format("Failed at creating runtime logger handle for MAAT: file %s", __maat_config->str_log_file_path.c_str())); } void * feather = Maat_feather(128, __maat_config->str_table_info_file.c_str(), logger); Maat_set_feather_opt(feather, MAAT_OPT_INSTANCE_NAME, "Tfe2a", (int) strlen("Tfe2a") + 1); auto __maat_option_setup_helper = [feather](enum MAAT_INIT_OPT opt, const std::string value) { Maat_set_feather_opt(feather, opt, value.c_str(), (int) value.length() + 1); }; switch (__maat_config->cfg_load_from) { case tfe_maat_config::LOAD_FROM_JSON_FILE: __maat_option_setup_helper(MAAT_OPT_JSON_FILE_PATH, __maat_config->str_json_file_path); break; case tfe_maat_config::LOAD_FROM_LOAD_IRIS: __maat_option_setup_helper(MAAT_OPT_FULL_CFG_DIR, __maat_config->str_full_cfg_dir); __maat_option_setup_helper(MAAT_OPT_INC_CFG_DIR, __maat_config->str_inc_cfg_dir); break; case tfe_maat_config::LOAD_FROM_REDIS_SERVER: __maat_option_setup_helper(MAAT_OPT_REDIS_IP, __maat_config->str_redis_addr); Maat_set_feather_opt(feather, MAAT_OPT_REDIS_PORT, &__maat_config->redir_port, sizeof(__maat_config->redir_port)); Maat_set_feather_opt(feather, MAAT_OPT_REDIS_INDEX, &__maat_config->redis_db_index, sizeof(__maat_config->redis_db_index)); break; default:assert(0); } __maat_option_setup_helper(MAAT_OPT_STAT_FILE_PATH, __maat_config->str_stat_file); Maat_set_feather_opt(feather, MAAT_OPT_STAT_ON, nullptr, 0); Maat_set_feather_opt(feather, MAAT_OPT_PERF_ON, nullptr, 0); if (__maat_config->is_effect_interval_ms_set) { unsigned int __value = __maat_config->effect_interval_ms; Maat_set_feather_opt(feather, MAAT_OPT_EFFECT_INVERVAL_MS, &__value, sizeof(__value)); } if (__maat_config->is_scan_interval_ms_set) { unsigned int __value = __maat_config->scan_interval_ms; Maat_set_feather_opt(feather, MAAT_OPT_SCANDIR_INTERVAL_MS, &__value, sizeof(__value)); } Maat_initiate_feather(feather); if (feather == nullptr) { MESA_destroy_runtime_log_handle(logger); throw std::runtime_error(string_format("Failed at initiate maat feather.")); } g_tfe_instance->maat_feather = feather; g_tfe_instance->maat_logger = logger; return 0; } static int __forge_socket_load_from_file(tfe_config * cfg, TfeConfigParser & cfg_parser) { cfg->forgesocket_config = new tfe_forgesocket_config; auto * forgesocket_config = cfg->forgesocket_config; forgesocket_config->en_forgesocket = cfg_parser.GetValueWithDefault("forgesocket", "enable", false); if (forgesocket_config->en_forgesocket) { forgesocket_config->str_unix_domain_file = cfg_parser.GetValue("forgesocket", "unix_domain_file"); } return 0; } void tfe_config_load_from_file(tfe_config * cfg, const char * c_str_file) { TfeConfigParser __config_parser(c_str_file); try { /* 读取ProxySpec列表 */ std::string str_proxyspec_symbol = __config_parser.GetValue("proxyspec", "symbol"); char * __buffer_proxyspec = strdup(str_proxyspec_symbol.c_str()); /* 拆分列表,根据列表读取详细配置 */ for (const char * __str_token = strtok(__buffer_proxyspec, ","); __str_token != nullptr; __str_token = strtok(nullptr, ",")) { __proxyspec_parse_each_entry(cfg, __config_parser, std::string(__str_token)); } /* 解析SSL/TLS版本功能开关 */ __ssl_protocol_load_from_file(cfg, __config_parser); /* maat参数配置 */ __maatframe_load_from_file(cfg, __config_parser); /* forge socket */ __forge_socket_load_from_file(cfg, __config_parser); } catch (cfg_invalid_format & e) { log_err_printf("Invalid format config entries in file %s, section %s, entry %s.", e.file.c_str(), e.section.c_str(), e.item.c_str()); goto __errout; } catch (cfg_lost_entry & e) { log_err_printf("Required config entries is not existed in file %s, section %s, entry %s.", e.file.c_str(), e.section.c_str(), e.item.c_str()); goto __errout; } return; __errout: exit(EXIT_FAILURE); } void tfe_config_parse_args(tfe_config * tfe, int argc, char ** argv) { return; } void tfe_config_free(tfe_config * opts) { if (opts->key) EVP_PKEY_free(opts->key); if (opts->dh) DH_free(opts->dh); if (opts->ecdhcurve) free(opts->ecdhcurve); if (opts->spec) proxyspec_free(opts->spec); if (opts->ciphers) free(opts->ciphers); if (opts->tgcrtdir) free(opts->tgcrtdir); if (opts->crlurl) free(opts->crlurl); if (opts->dropuser) free(opts->dropuser); if (opts->dropgroup) free(opts->dropgroup); if (opts->jaildir) free(opts->jaildir); if (opts->pidfile) free(opts->pidfile); if (opts->connectlog) free(opts->connectlog); if (opts->contentlog) free(opts->contentlog); if (opts->certgendir) free(opts->certgendir); if (opts->contentlog_basedir) free(opts->contentlog_basedir); if (opts->masterkeylog) free(opts->masterkeylog); memset(opts, 0, sizeof(tfe_config)); free(opts); } /* * Return 1 if opts_t contains a proxyspec that (eventually) uses SSL/TLS, * 0 otherwise. When 0, it is safe to assume that no SSL/TLS operations * will take place with this configuration. */ int tfe_config_has_ssl_spec(tfe_config * opts) { proxyspec * p = opts->spec; while (p) { if (p->ssl || p->upgrade) return 1; p = p->next; } return 0; } /* * Return 1 if opts_t contains a proxyspec with dns, 0 otherwise. */ int tfe_config_has_dns_spec(tfe_config * opts) { proxyspec * p = opts->spec; while (p) { if (p->dns) return 1; p = p->next; } return 0; } /* * Parse SSL proto string in optarg and look up the corresponding SSL method. * Calls exit() on failure. */ void tfe_config_proto_force(tfe_config * opts, const char * optarg, const char * argv0) { #if OPENSSL_VERSION_NUMBER < 0x10100000L if (opts->sslmethod != SSLv23_method) { #else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ if (opts->sslversion) { #endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ fprintf(stderr, "%s: cannot use -r multiple times\n", argv0); exit(EXIT_FAILURE); } #if OPENSSL_VERSION_NUMBER < 0x10100000L #ifdef HAVE_SSLV2 if (!strcmp(optarg, "ssl2")) { opts->sslmethod = SSLv2_method; } else #endif /* HAVE_SSLV2 */ #ifdef HAVE_SSLV3 if (!strcmp(optarg, "ssl3")) { opts->sslmethod = SSLv3_method; } else #endif /* HAVE_SSLV3 */ #ifdef HAVE_TLSV10 if (!strcmp(optarg, "tls10") || !strcmp(optarg, "tls1")) { opts->sslmethod = TLSv1_method; } else #endif /* HAVE_TLSV10 */ #ifdef HAVE_TLSV11 if (!strcmp(optarg, "tls11")) { opts->sslmethod = TLSv1_1_method; } else #endif /* HAVE_TLSV11 */ #ifdef HAVE_TLSV12 if (!strcmp(optarg, "tls12")) { opts->sslmethod = TLSv1_2_method; } else #endif /* HAVE_TLSV12 */ #else /* 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. */ #ifdef HAVE_SSLV3 if (!strcmp(optarg, "ssl3")) { opts->sslversion = SSL3_VERSION; } else #endif /* HAVE_SSLV3 */ #ifdef HAVE_TLSV10 if (!strcmp(optarg, "tls10") || !strcmp(optarg, "tls1")) { opts->sslversion = TLS1_VERSION; } else #endif /* HAVE_TLSV10 */ #ifdef HAVE_TLSV11 if (!strcmp(optarg, "tls11")) { opts->sslversion = TLS1_1_VERSION; } else #endif /* HAVE_TLSV11 */ #ifdef HAVE_TLSV12 if (!strcmp(optarg, "tls12")) { opts->sslversion = TLS1_2_VERSION; } else #endif /* HAVE_TLSV12 */ #endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ { fprintf(stderr, "%s: Unsupported SSL/TLS protocol '%s'\n", argv0, optarg); exit(EXIT_FAILURE); } } /* * Parse SSL proto string in optarg and set the corresponding no_foo bit. * Calls exit() on failure. */ void tfe_config_proto_disable(tfe_config * opts, const char * optarg, const char * argv0) { #ifdef HAVE_SSLV2 if (!strcmp(optarg, "ssl2")) { opts->no_ssl2 = 1; } else #endif /* HAVE_SSLV2 */ #ifdef HAVE_SSLV3 if (!strcmp(optarg, "ssl3")) { opts->no_ssl3 = 1; } else #endif /* HAVE_SSLV3 */ #ifdef HAVE_TLSV10 if (!strcmp(optarg, "tls10") || !strcmp(optarg, "tls1")) { opts->no_tls10 = 1; } else #endif /* HAVE_TLSV10 */ #ifdef HAVE_TLSV11 if (!strcmp(optarg, "tls11")) { opts->no_tls11 = 1; } else #endif /* HAVE_TLSV11 */ #ifdef HAVE_TLSV12 if (!strcmp(optarg, "tls12")) { opts->no_tls12 = 1; } else #endif /* HAVE_TLSV12 */ { fprintf(stderr, "%s: Unsupported SSL/TLS protocol '%s'\n", argv0, optarg); exit(EXIT_FAILURE); } } /* * Dump the SSL/TLS protocol related configuration to the debug log. */ void tfe_config_proto_dbg_dump(tfe_config * opts) { log_dbg_printf("SSL/TLS protocol: %s%s%s%s%s%s\n", #if OPENSSL_VERSION_NUMBER < 0x10100000L #ifdef HAVE_SSLV2 (opts->sslmethod == SSLv2_method) ? "ssl2" : #endif /* HAVE_SSLV2 */ #ifdef HAVE_SSLV3 (opts->sslmethod == SSLv3_method) ? "ssl3" : #endif /* HAVE_SSLV3 */ #ifdef HAVE_TLSV10 (opts->sslmethod == TLSv1_method) ? "tls10" : #endif /* HAVE_TLSV10 */ #ifdef HAVE_TLSV11 (opts->sslmethod == TLSv1_1_method) ? "tls11" : #endif /* HAVE_TLSV11 */ #ifdef HAVE_TLSV12 (opts->sslmethod == TLSv1_2_method) ? "tls12" : #endif /* HAVE_TLSV12 */ #else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ #ifdef HAVE_SSLV3 (opts->sslversion == SSL3_VERSION) ? "ssl3" : #endif /* HAVE_SSLV3 */ #ifdef HAVE_TLSV10 (opts->sslversion == TLS1_VERSION) ? "tls10" : #endif /* HAVE_TLSV10 */ #ifdef HAVE_TLSV11 (opts->sslversion == TLS1_1_VERSION) ? "tls11" : #endif /* HAVE_TLSV11 */ #ifdef HAVE_TLSV12 (opts->sslversion == TLS1_2_VERSION) ? "tls12" : #endif /* HAVE_TLSV12 */ #endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ "negotiate", #ifdef HAVE_SSLV2 opts->no_ssl2 ? " -ssl2" : #endif /* HAVE_SSLV2 */ "", #ifdef HAVE_SSLV3 opts->no_ssl3 ? " -ssl3" : #endif /* HAVE_SSLV3 */ "", #ifdef HAVE_TLSV10 opts->no_tls10 ? " -tls10" : #endif /* HAVE_TLSV10 */ "", #ifdef HAVE_TLSV11 opts->no_tls11 ? " -tls11" : #endif /* HAVE_TLSV11 */ "", #ifdef HAVE_TLSV12 opts->no_tls12 ? " -tls12" : #endif /* HAVE_TLSV12 */ ""); } /* * Clear and free a proxy spec. */ void proxyspec_free(proxyspec * spec) { do { proxyspec * next = spec->next; if (spec->natengine) free(spec->natengine); memset(spec, 0, sizeof(proxyspec)); free(spec); spec = next; } while (spec); } /* * Return text representation of proxy spec for display to the user. * Returned string must be freed by caller. */ char * proxyspec_str(proxyspec * spec) { char * s; char * lhbuf, * lpbuf; char * cbuf = NULL; if (sys_sockaddr_str((struct sockaddr *) &spec->listen_addr, spec->listen_addrlen, &lhbuf, &lpbuf) != 0) { return NULL; } if (spec->connect_addrlen) { char * chbuf, * cpbuf; if (sys_sockaddr_str((struct sockaddr *) &spec->connect_addr, spec->connect_addrlen, &chbuf, &cpbuf) != 0) { return NULL; } if (asprintf(&cbuf, "[%s]:%s", chbuf, cpbuf) < 0) { return NULL; } free(chbuf); free(cpbuf); } if (spec->sni_port) { if (asprintf(&cbuf, "sni %i", spec->sni_port) < 0) { return NULL; } } if (asprintf(&s, "[%s]:%s %s%s%s %s", lhbuf, lpbuf, (spec->ssl ? "ssl" : "tcp"), (spec->upgrade ? "|upgrade" : ""), (spec->http ? "|http" : ""), (spec->natengine ? spec->natengine : cbuf)) < 0) { s = NULL; } free(lhbuf); free(lpbuf); if (cbuf) free(cbuf); return s; } /* vim: set noet ft=c: */