diff --git a/configure.ac b/configure.ac index a9ea853611..eaef9d2703 100644 --- a/configure.ac +++ b/configure.ac @@ -373,7 +373,7 @@ dnl Tor modules options. These options are namespaced with --disable-module-XXX dnl --- dnl All our modules. -m4_define(MODULES, relay dirauth dircache) +m4_define([MODULES], [relay dirauth dircache pow]) # Some modules are only disabled through another option. For those, we don't # want to print the help in the summary at the end of the configure. Any entry @@ -382,6 +382,9 @@ m4_define(MODULES, relay dirauth dircache) m4_set_add_all([MODULES_WITH_NO_OPTIONS], [dircache]) dnl Relay module. +m4_define([module_option_hints(relay)], + [AS_IF([test "x$value" = x1], [HINT_OPT([--disable-module-relay])], + [HINT_NONE])]) AC_ARG_ENABLE([module-relay], AS_HELP_STRING([--disable-module-relay], [Build tor without the Relay modules: tor can not run as a relay, bridge, or authority. Implies --disable-module-dirauth])) @@ -399,6 +402,9 @@ AM_COND_IF(BUILD_MODULE_DIRCACHE, [Compile with directory cache support])) dnl Directory Authority module. +m4_define([module_option_hints(dirauth)], + [AS_IF([test "x$value" = x1], [HINT_OPT([--disable-module-dirauth])], + [HINT_NONE])]) AC_ARG_ENABLE([module-dirauth], AS_HELP_STRING([--disable-module-dirauth], [Build tor without the Directory Authority module: tor can not run as a directory authority or bridge authority])) @@ -407,6 +413,19 @@ AM_COND_IF(BUILD_MODULE_DIRAUTH, AC_DEFINE([HAVE_MODULE_DIRAUTH], [1], [Compile with Directory Authority feature support])) +dnl Hidden Service Proof-of-Work module. +m4_define([module_option_hints(pow)], + [AS_IF([test "x$value" = x1], [HINT_OPT([--disable-module-pow])], + [AS_IF([test "x$license_option" != "xGPL"], [HINT_OPT([requires --enable-gpl])], + [HINT_NONE])])]) +AC_ARG_ENABLE([module-pow], + AS_HELP_STRING([--disable-module-pow], + [Build tor without proof-of-work denial of service mitigation, normally available when building with --enable-gpl])) +AM_CONDITIONAL(BUILD_MODULE_POW, + [test "x$license_option" = "xGPL" && test "x$enable_module_pow" != "xno"]) +AM_COND_IF(BUILD_MODULE_POW, + AC_DEFINE([HAVE_MODULE_POW], [1], [Compile with proof-of-work support])) + dnl Helper variables. TOR_MODULES_ALL_ENABLED= AC_DEFUN([ADD_MODULE], [ @@ -2733,12 +2752,22 @@ PPRINT_PROP_BOOL([Fragile Hardening (--enable-fragile-hardening, dev only)], $va AS_ECHO PPRINT_SUBTITLE([Modules]) +# Modules have documentation hints indicating how they can be enabled +# or disabled, and those hints can select which version of our message +# to show based on variables at configure-time. +# +# Each "module_option_hints()" macro, if it exists, must +# visit exactly one HINT_* macro using shell conditionals. + m4_foreach_w([mname], MODULES, [ AM_COND_IF(m4_join([], [BUILD_MODULE_], m4_toupper([]mname[])), value=1, value=0) - m4_set_contains([MODULES_WITH_NO_OPTIONS], mname, - PPRINT_PROP_BOOL([mname], $value), - PPRINT_PROP_BOOL([mname (--disable-module-mname)], $value)) + m4_pushdef([HINT_OPT], [PPRINT_PROP_BOOL](mname ($1), [[$value]])) + m4_pushdef([HINT_NONE], [PPRINT_PROP_BOOL](mname, [[$value]])) + m4_ifdef([module_option_hints](mname), + [m4_indir([module_option_hints](mname))], + [HINT_NONE]) + m4_popdef([HINT_OPT], [HINT_NONE]) ] ) diff --git a/src/app/config/config.c b/src/app/config/config.c index cb71d0fb6d..24321b764f 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -88,9 +88,11 @@ #include "feature/control/control.h" #include "feature/control/control_auth.h" #include "feature/control/control_events.h" +#include "feature/dircache/dirserv.h" #include "feature/dirclient/dirclient_modes.h" #include "feature/hibernate/hibernate.h" #include "feature/hs/hs_config.h" +#include "feature/hs/hs_pow.h" #include "feature/metrics/metrics.h" #include "feature/nodelist/dirlist.h" #include "feature/nodelist/networkstatus.h" @@ -2731,11 +2733,19 @@ list_deprecated_options(void) static void list_enabled_modules(void) { - printf("%s: %s\n", "relay", have_module_relay() ? "yes" : "no"); - printf("%s: %s\n", "dirauth", have_module_dirauth() ? "yes" : "no"); - // We don't list dircache, because it cannot be enabled or disabled - // independently from relay. Listing it here would proliferate - // test variants in test_parseconf.sh to no useful purpose. + static const struct { + const char *name; + bool have; + } list[] = { + { "relay", have_module_relay() }, + { "dirauth", have_module_dirauth() }, + { "dircache", have_module_dircache() }, + { "pow", have_module_pow() } + }; + + for (unsigned i = 0; i < sizeof list / sizeof list[0]; i++) { + printf("%s: %s\n", list[i].name, list[i].have ? "yes" : "no"); + } } /** Prints compile-time and runtime library versions. */ diff --git a/src/core/include.am b/src/core/include.am index 7752a7974b..d24e5d5137 100644 --- a/src/core/include.am +++ b/src/core/include.am @@ -17,6 +17,7 @@ if UNITTESTS_ENABLED LIBTOR_APP_TESTING_A_SOURCES += $(MODULE_RELAY_SOURCES) LIBTOR_APP_TESTING_A_SOURCES += $(MODULE_DIRCACHE_SOURCES) LIBTOR_APP_TESTING_A_SOURCES += $(MODULE_DIRAUTH_SOURCES) +LIBTOR_APP_TESTING_A_SOURCES += $(MODULE_POW_SOURCES) src_core_libtor_app_testing_a_SOURCES = $(LIBTOR_APP_TESTING_A_SOURCES) else diff --git a/src/feature/hs/hs_circuit.c b/src/feature/hs/hs_circuit.c index 55b992ee28..f7ab6442b9 100644 --- a/src/feature/hs/hs_circuit.c +++ b/src/feature/hs/hs_circuit.c @@ -1369,7 +1369,7 @@ hs_circ_handle_introduce2(const hs_service_t *service, /* Add the rendezvous request to the priority queue if PoW defenses are * enabled, otherwise rendezvous as usual. */ - if (service->config.has_pow_defenses_enabled) { + if (have_module_pow() && service->config.has_pow_defenses_enabled) { log_notice(LD_REND, "Adding introduction request to pqueue with effort: %u", data.rdv_data.pow_effort); diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c index 56547de7e7..6a404395ea 100644 --- a/src/feature/hs/hs_client.c +++ b/src/feature/hs/hs_client.c @@ -733,7 +733,8 @@ consider_sending_introduce1(origin_circuit_t *intro_circ, /* If the descriptor contains PoW parameters then the service is * expecting a PoW solution in the INTRODUCE cell, which we solve here. */ - if (desc->encrypted_data.pow_params && + if (have_module_pow() && + desc->encrypted_data.pow_params && desc->encrypted_data.pow_params->suggested_effort > 0) { log_debug(LD_REND, "PoW params present in descriptor."); @@ -752,9 +753,11 @@ consider_sending_introduce1(origin_circuit_t *intro_circ, /* send it to the client-side pow cpuworker for solving. */ intro_circ->hs_currently_solving_pow = 1; - pow_queue_work(intro_circ->global_identifier, - rend_circ->global_identifier, - desc->encrypted_data.pow_params); + if (0 != hs_pow_queue_work(intro_circ->global_identifier, + rend_circ->global_identifier, + desc->encrypted_data.pow_params)) { + log_debug(LD_REND, "Failed to enqueue PoW request"); + } /* can't proceed with the intro1 cell yet, so yield back to the * main loop */ diff --git a/src/feature/hs/hs_config.c b/src/feature/hs/hs_config.c index 0f5a8cf49a..296941138b 100644 --- a/src/feature/hs/hs_config.c +++ b/src/feature/hs/hs_config.c @@ -327,6 +327,12 @@ config_validate_service(const hs_service_config_t *config) config->pow_queue_burst, config->pow_queue_rate); goto invalid; } + if (config->has_pow_defenses_enabled && !have_module_pow()) { + log_warn(LD_CONFIG, "Hidden service proof-of-work defenses are enabled " + "in our configuration but this build of tor does not " + "include the required 'pow' module."); + goto invalid; + } /* Valid. */ return 0; diff --git a/src/feature/hs/hs_pow.c b/src/feature/hs/hs_pow.c index 3c02a4851e..8ca121762f 100644 --- a/src/feature/hs/hs_pow.c +++ b/src/feature/hs/hs_pow.c @@ -410,9 +410,9 @@ pow_worker_replyfn(void *work_) * Queue the job of solving the pow in a worker thread. */ int -pow_queue_work(uint32_t intro_circ_identifier, - uint32_t rend_circ_identifier, - const hs_pow_desc_params_t *pow_params) +hs_pow_queue_work(uint32_t intro_circ_identifier, + uint32_t rend_circ_identifier, + const hs_pow_desc_params_t *pow_params) { tor_assert(in_main_thread()); diff --git a/src/feature/hs/hs_pow.h b/src/feature/hs/hs_pow.h index 92ea011b2b..b27bd7441c 100644 --- a/src/feature/hs/hs_pow.h +++ b/src/feature/hs/hs_pow.h @@ -127,6 +127,9 @@ typedef struct hs_pow_solution_t { equix_solution equix_solution; } hs_pow_solution_t; +#ifdef HAVE_MODULE_POW +#define have_module_pow() (1) + /* API */ int hs_pow_solve(const hs_pow_desc_params_t *pow_params, hs_pow_solution_t *pow_solution_out); @@ -137,8 +140,54 @@ int hs_pow_verify(const hs_pow_service_state_t *pow_state, void hs_pow_remove_seed_from_cache(uint32_t seed); void hs_pow_free_service_state(hs_pow_service_state_t *state); -int pow_queue_work(uint32_t intro_circ_identifier, - uint32_t rend_circ_identifier, - const hs_pow_desc_params_t *pow_params); +int hs_pow_queue_work(uint32_t intro_circ_identifier, + uint32_t rend_circ_identifier, + const hs_pow_desc_params_t *pow_params); + +#else /* !defined(HAVE_MODULE_POW) */ +#define have_module_pow() (0) + +static inline int +hs_pow_solve(const hs_pow_desc_params_t *pow_params, + hs_pow_solution_t *pow_solution_out) +{ + (void)pow_params; + (void)pow_solution_out; + return -1; +} + +static inline int +hs_pow_verify(const hs_pow_service_state_t *pow_state, + const hs_pow_solution_t *pow_solution) +{ + (void)pow_state; + (void)pow_solution; + return -1; +} + +static inline void +hs_pow_remove_seed_from_cache(uint32_t seed) +{ + (void)seed; +} + +static inline void +hs_pow_free_service_state(hs_pow_service_state_t *state) +{ + (void)state; +} + +static inline int +hs_pow_queue_work(uint32_t intro_circ_identifier, + uint32_t rend_circ_identifier, + const hs_pow_desc_params_t *pow_params) +{ + (void)intro_circ_identifier; + (void)rend_circ_identifier; + (void)pow_params; + return -1; +} + +#endif /* defined(HAVE_MODULE_POW) */ #endif /* !defined(TOR_HS_POW_H) */ diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c index dd360d3659..a9070024cb 100644 --- a/src/feature/hs/hs_service.c +++ b/src/feature/hs/hs_service.c @@ -2899,7 +2899,7 @@ run_housekeeping_event(time_t now) /* Check if we need to initialize or update PoW parameters, if the * defenses are enabled. */ - if (service->config.has_pow_defenses_enabled) { + if (have_module_pow() && service->config.has_pow_defenses_enabled) { pow_housekeeping(service, now); } @@ -2937,8 +2937,10 @@ run_build_descriptor_event(time_t now) * is useful for newly built descriptors. */ update_all_descriptors_intro_points(now); - /* Update the PoW params if needed. */ - update_all_descriptors_pow_params(now); + if (have_module_pow()) { + /* Update the PoW params if needed. */ + update_all_descriptors_pow_params(now); + } } /** For the given service, launch any intro point circuits that could be diff --git a/src/feature/hs/include.am b/src/feature/hs/include.am index f4966e6c54..b64ab1b41c 100644 --- a/src/feature/hs/include.am +++ b/src/feature/hs/include.am @@ -15,12 +15,19 @@ LIBTOR_APP_A_SOURCES += \ src/feature/hs/hs_intropoint.c \ src/feature/hs/hs_metrics.c \ src/feature/hs/hs_ob.c \ - src/feature/hs/hs_pow.c \ src/feature/hs/hs_service.c \ src/feature/hs/hs_stats.c \ src/feature/hs/hs_sys.c \ src/feature/hs/hs_metrics_entry.c +# Proof of Work module +MODULE_POW_SOURCES = \ + src/feature/hs/hs_pow.c + +if BUILD_MODULE_POW +LIBTOR_APP_A_SOURCES += $(MODULE_POW_SOURCES) +endif + # ADD_C_FILE: INSERT HEADERS HERE. noinst_HEADERS += \ src/feature/hs/hs_cache.h \ diff --git a/src/test/test_parseconf.sh b/src/test/test_parseconf.sh index c02b8b23c0..85a8cbbf0c 100755 --- a/src/test/test_parseconf.sh +++ b/src/test/test_parseconf.sh @@ -98,6 +98,9 @@ # want to encode that knowledge in this test script, so we supply a # separate result file for every combination of disabled modules that # has a different result.) +# +# This logic ignores modules that are not listed by --list-modules +# (dircache) and some that do not currently affect config parsing (pow). umask 077 set -e @@ -197,6 +200,8 @@ echo "This pattern should not match any log messages" \ "$NON_EMPTY" STANDARD_LIBS="libevent\\|openssl\\|zlib" +MODULES_WITHOUT_CONFIG_TESTS="dircache\\|pow" + # Lib names are restricted to [a-z0-9]* at the moment # We don't actually want to support foreign accents here # shellcheck disable=SC2018,SC2019 @@ -229,6 +234,7 @@ TOR_LIBS_ENABLED_SEARCH="$(echo "$TOR_LIBS_ENABLED_SEARCH" | tr ' ' '\n' \ | grep -v '^_*$' | tr '\n' ' ')" TOR_MODULES_DISABLED="$("$TOR_BINARY" --list-modules | grep ': no' \ + | grep -v "$MODULES_WITHOUT_CONFIG_TESTS" \ | cut -d ':' -f1 | sort | tr '\n' '_')" # Remove the last underscore, if there is one TOR_MODULES_DISABLED=${TOR_MODULES_DISABLED%_}