diff options
author | David Goulet <dgoulet@torproject.org> | 2017-01-16 13:19:44 -0500 |
---|---|---|
committer | David Goulet <dgoulet@torproject.org> | 2017-07-13 16:49:08 -0400 |
commit | 02e2edeb33224461d1fbb879722c0948171b9688 (patch) | |
tree | 2f70e922c8feff7afa87b2903c4d59c8952340e7 /src/or/hs_config.c | |
parent | b03853b65f109ed6a34ba2924fe3b00d56131ff5 (diff) | |
download | tor-02e2edeb33224461d1fbb879722c0948171b9688.tar.gz tor-02e2edeb33224461d1fbb879722c0948171b9688.zip |
prop224: Add hs_config.{c|h} with a refactoring
Add the hs_config.{c|h} files contains everything that the HS subsystem needs
to load and configure services. Ultimately, it should also contain client
functions such as client authorization.
This comes with a big refactoring of rend_config_services() which has now
changed to only configure a single service and it is stripped down of the
common directives which are now part of the generic handler.
This is ground work for prop224 of course but only touches version 2 services
and add XXX note for version 3.
Signed-off-by: David Goulet <dgoulet@torproject.org>
Diffstat (limited to 'src/or/hs_config.c')
-rw-r--r-- | src/or/hs_config.c | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/src/or/hs_config.c b/src/or/hs_config.c new file mode 100644 index 0000000000..6326e90324 --- /dev/null +++ b/src/or/hs_config.c @@ -0,0 +1,292 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_config.c + * \brief Implement hidden service configuration subsystem. + * + * \details + * + * This file has basically one main entry point: hs_config_service_all(). It + * takes the torrc options and configure hidden service from it. In validate + * mode, nothing is added to the global service list or keys are not generated + * nor loaded. + * + * A service is configured in two steps. It is first created using the tor + * options and then put in a staging list. It will stay there until + * hs_service_load_all_keys() is called. That function is responsible to + * load/generate the keys for the service in the staging list and if + * successful, transfert the service to the main global service list where + * at that point it is ready to be used. + * + * Configuration handlers are per-version (see config_service_handlers[]) and + * there is a main generic one for every option that is common to all version + * (config_generic_service). + **/ + +#define HS_CONFIG_PRIVATE + +#include "hs_common.h" +#include "hs_config.h" +#include "hs_service.h" +#include "rendservice.h" + +/* Configuration handler for a version 3 service. Return 0 on success else a + * negative value. */ +static int +config_service_v3(const config_line_t *line, + const or_options_t *options, int validate_only, + hs_service_t *service) +{ + (void) line; + (void) service; + (void) validate_only; + (void) options; + /* XXX: Configure a v3 service with specific options. */ + /* XXX: Add service to v3 list and pruning on reload. */ + return 0; +} + +/* Configure a service using the given options in line_ and options. This is + * called for any service regardless of its version which means that all + * directives in this function are generic to any service version. This + * function will also check the validity of the service directory path. + * + * The line_ must be pointing to the directive directly after a + * HiddenServiceDir. That way, when hitting the next HiddenServiceDir line or + * reaching the end of the list of lines, we know that we have to stop looking + * for more options. + * + * Return 0 on success else -1. */ +static int +config_generic_service(const config_line_t *line_, + const or_options_t *options, + hs_service_t *service) +{ + int ok, dir_seen = 0; + const config_line_t *line; + hs_service_config_t *config; + + tor_assert(line_); + tor_assert(options); + tor_assert(service); + + /* Makes thing easier. */ + config = &service->config; + memset(config, 0, sizeof(*config)); + + /* The first line starts with HiddenServiceDir so we consider what's next is + * the configuration of the service. */ + for (line = line_; line ; line = line->next) { + /* This indicate that we have a new service to configure. */ + if (!strcasecmp(line->key, "HiddenServiceDir")) { + /* This function only configures one service at a time so if we've + * already seen one, stop right now. */ + if (dir_seen) { + break; + } + /* Ok, we've seen one and we are about to configure it. */ + dir_seen = 1; + config->directory_path = tor_strdup(line->value); + log_info(LD_CONFIG, "HiddenServiceDir=%s. Configuring...", + escaped(config->directory_path)); + continue; + } + if (BUG(!dir_seen)) { + goto err; + } + /* Version of the service. */ + if (!strcasecmp(line->key, "HiddenServiceVersion")) { + service->version = (uint32_t) tor_parse_ulong(line->value, + 10, HS_VERSION_TWO, + HS_VERSION_MAX, + &ok, NULL); + if (!ok) { + log_warn(LD_CONFIG, + "HiddenServiceVersion be between %u and %u, not %s", + HS_VERSION_TWO, HS_VERSION_MAX, line->value); + goto err; + } + log_info(LD_CONFIG, "HiddenServiceVersion=%" PRIu32 " for %s", + service->version, escaped(config->directory_path)); + continue; + } + /* Virtual port. */ + if (!strcasecmp(line->key, "HiddenServicePort")) { + char *err_msg = NULL; + /* XXX: Can we rename this? */ + rend_service_port_config_t *portcfg = + rend_service_parse_port_config(line->value, " ", &err_msg); + if (!portcfg) { + if (err_msg) { + log_warn(LD_CONFIG, "%s", err_msg); + } + tor_free(err_msg); + goto err; + } + tor_assert(!err_msg); + smartlist_add(config->ports, portcfg); + log_info(LD_CONFIG, "HiddenServicePort=%s for %s", + line->value, escaped(config->directory_path)); + continue; + } + /* Do we allow unknown ports. */ + if (!strcasecmp(line->key, "HiddenServiceAllowUnknownPorts")) { + config->allow_unknown_ports = (unsigned int) tor_parse_long(line->value, + 10, 0, 1, + &ok, NULL); + if (!ok) { + log_warn(LD_CONFIG, + "HiddenServiceAllowUnknownPorts should be 0 or 1, not %s", + line->value); + goto err; + } + log_info(LD_CONFIG, + "HiddenServiceAllowUnknownPorts=%u for %s", + config->allow_unknown_ports, escaped(config->directory_path)); + continue; + } + /* Directory group readable. */ + if (!strcasecmp(line->key, "HiddenServiceDirGroupReadable")) { + config->dir_group_readable = (unsigned int) tor_parse_long(line->value, + 10, 0, 1, + &ok, NULL); + if (!ok) { + log_warn(LD_CONFIG, + "HiddenServiceDirGroupReadable should be 0 or 1, not %s", + line->value); + goto err; + } + log_info(LD_CONFIG, + "HiddenServiceDirGroupReadable=%u for %s", + config->dir_group_readable, escaped(config->directory_path)); + continue; + } + /* Maximum streams per circuit. */ + if (!strcasecmp(line->key, "HiddenServiceMaxStreams")) { + config->max_streams_per_rdv_circuit = tor_parse_uint64(line->value, + 10, 0, 65535, + &ok, NULL); + if (!ok) { + log_warn(LD_CONFIG, + "HiddenServiceMaxStreams should be between 0 and %d, not %s", + 65535, line->value); + goto err; + } + log_info(LD_CONFIG, + "HiddenServiceMaxStreams=%" PRIu64 " for %s", + config->max_streams_per_rdv_circuit, + escaped(config->directory_path)); + continue; + } + /* Maximum amount of streams before we close the circuit. */ + if (!strcasecmp(line->key, "HiddenServiceMaxStreamsCloseCircuit")) { + config->max_streams_close_circuit = + (unsigned int) tor_parse_long(line->value, 10, 0, 1, &ok, NULL); + if (!ok) { + log_warn(LD_CONFIG, + "HiddenServiceMaxStreamsCloseCircuit should be 0 or 1, " + "not %s", line->value); + goto err; + } + log_info(LD_CONFIG, + "HiddenServiceMaxStreamsCloseCircuit=%u for %s", + config->max_streams_close_circuit, + escaped(config->directory_path)); + continue; + } + } + + /* Check permission on service directory. */ + if (hs_check_service_private_dir(options->User, config->directory_path, + config->dir_group_readable, 0) < 0) { + goto err; + } + + /* Check if we are configured in non anonymous mode and single hop mode + * meaning every service become single onion. */ + if (rend_service_allow_non_anonymous_connection(options) && + rend_service_non_anonymous_mode_enabled(options)) { + config->is_single_onion = 1; + } + + /* Success */ + return 0; + err: + return -1; +} + +/* Configuration handler indexed by version number. */ +static int + (*config_service_handlers[])(const config_line_t *line, + const or_options_t *options, + int validate_only, + hs_service_t *service) = +{ + NULL, /* v0 */ + NULL, /* v1 */ + rend_config_service, /* v2 */ + config_service_v3, /* v3 */ +}; + +/* From a set of <b>options</b>, setup every hidden service found. Return 0 on + * success or -1 on failure. If <b>validate_only</b> is set, parse, warn and + * return as normal, but don't actually change the configured services. */ +int +hs_config_service_all(const or_options_t *options, int validate_only) +{ + int dir_option_seen = 0; + hs_service_t *service = NULL; + const config_line_t *line; + + tor_assert(options); + + for (line = options->RendConfigLines; line; line = line->next) { + if (!strcasecmp(line->key, "HiddenServiceDir")) { + /* We have a new hidden service. */ + service = hs_service_new(options); + /* We'll configure that service as a generic one and then pass it to the + * specific handler according to the configured version number. */ + if (config_generic_service(line, options, service) < 0) { + goto err; + } + tor_assert(service->version <= HS_VERSION_MAX); + /* The handler is in charge of specific options for a version. We start + * after this service directory line so once we hit another directory + * line, the handler knows that it has to stop. */ + if (config_service_handlers[service->version](line->next, options, + validate_only, + service) < 0) { + goto err; + } + /* Whatever happens, on success we loose the ownership of the service + * object so we nullify the pointer to be safe. */ + service = NULL; + /* Flag that we've seen a directory directive and we'll use that to make + * sure that the torrc options ordering are actually valid. */ + dir_option_seen = 1; + continue; + } + /* The first line must be a directory option else tor is misconfigured. */ + if (!dir_option_seen) { + log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive", + line->key); + goto err; + } + } + + if (!validate_only) { + /* Trigger service pruning which will make sure the just configured + * services end up in the main global list. This is v2 specific. */ + rend_service_prune_list(); + /* XXX: Need the v3 one. */ + } + + /* Success. */ + return 0; + err: + hs_service_free(service); + /* Tor main should call the free all function. */ + return -1; +} + |