LCOV - code coverage report
Current view: top level - feature/hs - hs_config.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 203 242 83.9 %
Date: 2021-11-24 03:28:48 Functions: 14 15 93.3 %

          Line data    Source code
       1             : /* Copyright (c) 2017-2021, The Tor Project, Inc. */
       2             : /* See LICENSE for licensing information */
       3             : 
       4             : /**
       5             :  * \file hs_config.c
       6             :  * \brief Implement hidden service configuration subsystem.
       7             :  *
       8             :  * \details
       9             :  *
      10             :  * This file has basically one main entry point: hs_config_service_all(). It
      11             :  * takes the torrc options and configure hidden service from it. In validate
      12             :  * mode, nothing is added to the global service list or keys are not generated
      13             :  * nor loaded.
      14             :  *
      15             :  * A service is configured in two steps. It is first created using the tor
      16             :  * options and then put in a staging list. It will stay there until
      17             :  * hs_service_load_all_keys() is called. That function is responsible to
      18             :  * load/generate the keys for the service in the staging list and if
      19             :  * successful, transferred the service to the main global service list where
      20             :  * at that point it is ready to be used.
      21             :  *
      22             :  * Configuration functions are per-version and there is a main generic one for
      23             :  * every option that is common to all version (config_generic_service).
      24             :  **/
      25             : 
      26             : #include "feature/hs/hs_common.h"
      27             : #include "feature/hs/hs_config.h"
      28             : #include "feature/hs/hs_client.h"
      29             : #include "feature/hs/hs_ob.h"
      30             : #include "feature/hs/hs_service.h"
      31             : #include "lib/encoding/confline.h"
      32             : #include "lib/conf/confdecl.h"
      33             : #include "lib/confmgt/confmgt.h"
      34             : 
      35             : #include "feature/hs/hs_opts_st.h"
      36             : #include "app/config/or_options_st.h"
      37             : 
      38             : /* Declare the table mapping hs options to hs_opts_t */
      39             : #define CONF_CONTEXT TABLE
      40             : #include "feature/hs/hs_options.inc"
      41             : #undef CONF_CONTEXT
      42             : 
      43             : /** Magic number for hs_opts_t. */
      44             : #define HS_OPTS_MAGIC 0x6f6e796e
      45             : 
      46             : static const config_format_t hs_opts_fmt = {
      47             :   .size = sizeof(hs_opts_t),
      48             :   .magic = { "hs_opts_t",
      49             :              HS_OPTS_MAGIC,
      50             :              offsetof(hs_opts_t, magic) },
      51             :   .vars = hs_opts_t_vars,
      52             : };
      53             : 
      54             : /** Global configuration manager to handle HS sections*/
      55             : static config_mgr_t *hs_opts_mgr = NULL;
      56             : 
      57             : /**
      58             :  * Return a configuration manager for the hs_opts_t configuration type.
      59             :  **/
      60             : static const config_mgr_t *
      61         134 : get_hs_opts_mgr(void)
      62             : {
      63         134 :   if (PREDICT_UNLIKELY(hs_opts_mgr == NULL)) {
      64          15 :     hs_opts_mgr = config_mgr_new(&hs_opts_fmt);
      65          15 :     config_mgr_freeze(hs_opts_mgr);
      66             :   }
      67         134 :   return hs_opts_mgr;
      68             : }
      69             : 
      70             : /**
      71             :  * Allocate, initialize, and return a new hs_opts_t.
      72             :  **/
      73             : static hs_opts_t *
      74          35 : hs_opts_new(void)
      75             : {
      76          35 :   const config_mgr_t *mgr = get_hs_opts_mgr();
      77          35 :   hs_opts_t *r = config_new(mgr);
      78          35 :   tor_assert(r);
      79          35 :   config_init(mgr, r);
      80          35 :   return r;
      81             : }
      82             : 
      83             : /**
      84             :  * Free an hs_opts_t.
      85             :  **/
      86             : #define hs_opts_free(opts) \
      87             :   config_free(get_hs_opts_mgr(), (opts))
      88             : 
      89             : /** Using the given list of services, stage them into our global state. Every
      90             :  * service version are handled. This function can remove entries in the given
      91             :  * service_list.
      92             :  *
      93             :  * Staging a service means that we take all services in service_list and we
      94             :  * put them in the staging list (global) which acts as a temporary list that
      95             :  * is used by the service loading key process. In other words, staging a
      96             :  * service puts it in a list to be considered when loading the keys and then
      97             :  * moved to the main global list. */
      98             : static void
      99          10 : stage_services(smartlist_t *service_list)
     100             : {
     101          10 :   tor_assert(service_list);
     102             : 
     103             :   /* This is >= v3 specific. Using the newly configured service list, stage
     104             :    * them into our global state. Every object ownership is lost after. */
     105          10 :   hs_service_stage_services(service_list);
     106          10 : }
     107             : 
     108             : /** Validate the given service against all service in the given list. If the
     109             :  * service is ephemeral, this function ignores it. Services with the same
     110             :  * directory path aren't allowed and will return an error. If a duplicate is
     111             :  * found, 1 is returned else 0 if none found. */
     112             : static int
     113          20 : service_is_duplicate_in_list(const smartlist_t *service_list,
     114             :                              const hs_service_t *service)
     115             : {
     116          20 :   int ret = 0;
     117             : 
     118          20 :   tor_assert(service_list);
     119          20 :   tor_assert(service);
     120             : 
     121             :   /* Ephemeral service don't have a directory configured so no need to check
     122             :    * for a service in the list having the same path. */
     123          20 :   if (service->config.is_ephemeral) {
     124           0 :     goto end;
     125             :   }
     126             : 
     127             :   /* XXX: Validate if we have any service that has the given service dir path.
     128             :    * This has two problems:
     129             :    *
     130             :    * a) It's O(n^2)
     131             :    *
     132             :    * b) We only compare directory paths as strings, so we can't
     133             :    *    detect two distinct paths that specify the same directory
     134             :    *    (which can arise from symlinks, case-insensitivity, bind
     135             :    *    mounts, etc.).
     136             :    *
     137             :    * It also can't detect that two separate Tor instances are trying
     138             :    * to use the same HiddenServiceDir; for that, we would need a
     139             :    * lock file.  But this is enough to detect a simple mistake that
     140             :    * at least one person has actually made. */
     141          21 :   SMARTLIST_FOREACH_BEGIN(service_list, const hs_service_t *, s) {
     142           2 :     if (!strcmp(s->config.directory_path, service->config.directory_path)) {
     143           1 :       log_warn(LD_REND, "Another hidden service is already configured "
     144             :                         "for directory %s",
     145             :                escaped(service->config.directory_path));
     146           1 :       ret = 1;
     147           1 :       goto end;
     148             :     }
     149           1 :   } SMARTLIST_FOREACH_END(s);
     150             : 
     151          19 :  end:
     152          20 :   return ret;
     153             : }
     154             : 
     155             : /** Check whether an integer <b>i</b> is out of bounds (not between <b>low</b>
     156             :  * and <b>high</b> incusive).  If it is, then log a warning about the option
     157             :  * <b>name</b>, and return true. Otherwise return false. */
     158             : static bool
     159         115 : check_value_oob(int i, const char *name, int low, int high)
     160             : {
     161         115 :   if (i < low || i > high) {
     162           4 :     log_warn(LD_CONFIG, "%s must be between %d and %d, not %d.",
     163             :              name, low, high, i);
     164           4 :     return true;
     165             :   }
     166             :   return false;
     167             : }
     168             : 
     169             : /**
     170             :  * Helper: check whether the integer value called <b>name</b> in <b>opts</b>
     171             :  * is out-of-bounds.
     172             :  **/
     173             : #define CHECK_OOB(opts, name, low, high)      \
     174             :   check_value_oob((opts)->name, #name, (low), (high))
     175             : 
     176             : /** Helper function: Given a configuration option and its value, parse the
     177             :  * value as a hs_circuit_id_protocol_t. On success, ok is set to 1 and ret is
     178             :  * the parse value. On error, ok is set to 0 and the "none"
     179             :  * hs_circuit_id_protocol_t is returned. This function logs on error. */
     180             : static hs_circuit_id_protocol_t
     181           0 : helper_parse_circuit_id_protocol(const char *key, const char *value, int *ok)
     182             : {
     183           0 :   tor_assert(value);
     184           0 :   tor_assert(ok);
     185             : 
     186           0 :   hs_circuit_id_protocol_t ret = HS_CIRCUIT_ID_PROTOCOL_NONE;
     187           0 :   *ok = 0;
     188             : 
     189           0 :   if (! strcasecmp(value, "haproxy")) {
     190           0 :     *ok = 1;
     191           0 :     ret = HS_CIRCUIT_ID_PROTOCOL_HAPROXY;
     192           0 :   } else if (! strcasecmp(value, "none")) {
     193           0 :     *ok = 1;
     194           0 :     ret = HS_CIRCUIT_ID_PROTOCOL_NONE;
     195             :   } else {
     196           0 :     log_warn(LD_CONFIG, "%s must be 'haproxy' or 'none'.", key);
     197           0 :     goto err;
     198             :   }
     199             : 
     200           0 :  err:
     201           0 :   return ret;
     202             : }
     203             : 
     204             : /** Return the service version by trying to learn it from the key on disk if
     205             :  * any. If nothing is found, the current service configured version is
     206             :  * returned. */
     207             : static int
     208          11 : config_learn_service_version(hs_service_t *service)
     209             : {
     210          11 :   int version;
     211             : 
     212          11 :   tor_assert(service);
     213             : 
     214          11 :   version = hs_service_get_version_from_key(service);
     215          11 :   if (version < 0) {
     216          11 :     version = service->config.version;
     217             :   }
     218             : 
     219          11 :   return version;
     220             : }
     221             : 
     222             : /**
     223             :  * Header key indicating the start of a new hidden service configuration
     224             :  * block.
     225             :  **/
     226             : static const char SECTION_HEADER[] = "HiddenServiceDir";
     227             : 
     228             : /** Return true iff the given options starting at line_ for a hidden service
     229             :  * contains at least one invalid option. Each hidden service option don't
     230             :  * apply to all versions so this function can find out. The line_ MUST start
     231             :  * right after the HiddenServiceDir line of this service.
     232             :  *
     233             :  * This is mainly for usability so we can inform the user of any invalid
     234             :  * option for the hidden service version instead of silently ignoring. */
     235             : static int
     236          25 : config_has_invalid_options(const config_line_t *line_,
     237             :                            const hs_service_t *service)
     238             : {
     239          25 :   int ret = 0;
     240          25 :   const char **optlist;
     241          25 :   const config_line_t *line;
     242             : 
     243          25 :   tor_assert(service);
     244          25 :   tor_assert(service->config.version <= HS_VERSION_MAX);
     245             : 
     246             :   /* List of options that a v3 service doesn't support thus must exclude from
     247             :    * its configuration. */
     248          25 :   const char *opts_exclude_v3[] = {
     249             :     "HiddenServiceAuthorizeClient",
     250             :     NULL /* End marker. */
     251             :   };
     252             : 
     253             :   /* Defining the size explicitly allows us to take advantage of the compiler
     254             :    * which warns us if we ever bump the max version but forget to grow this
     255             :    * array. The plus one is because we have a version 0 :). */
     256          25 :   struct {
     257             :     const char **list;
     258          25 :   } exclude_lists[HS_VERSION_MAX + 1] = {
     259             :     { NULL }, /* v0. */
     260             :     { NULL }, /* v1. */
     261             :     { NULL }, /* v2. */
     262             :     { opts_exclude_v3 }, /* v3. */
     263             :   };
     264             : 
     265          25 :   optlist = exclude_lists[service->config.version].list;
     266          25 :   if (optlist == NULL) {
     267             :     /* No exclude options to look at for this version. */
     268           0 :     goto end;
     269             :   }
     270          50 :   for (int i = 0; optlist[i]; i++) {
     271             :     const char *opt = optlist[i];
     272          94 :     for (line = line_; line; line = line->next) {
     273          69 :       if (!strcasecmp(line->key, SECTION_HEADER)) {
     274             :         /* We just hit the next hidden service, stop right now.
     275             :          * (This shouldn't be possible, now that we have partitioned the list
     276             :          * into sections.) */
     277           0 :         tor_assert_nonfatal_unreached();
     278           0 :         goto end;
     279             :       }
     280          69 :       if (!strcasecmp(line->key, opt)) {
     281           0 :         log_warn(LD_CONFIG, "Hidden service option %s is incompatible with "
     282             :                             "version %" PRIu32 " of service in %s",
     283             :                  opt, service->config.version,
     284             :                  service->config.directory_path);
     285           0 :         ret = 1;
     286             :         /* Continue the loop so we can find all possible options. */
     287           0 :         continue;
     288             :       }
     289             :     }
     290             :   }
     291          25 :  end:
     292          25 :   return ret;
     293             : }
     294             : 
     295             : /** Validate service configuration. This is used when loading the configuration
     296             :  * and once we've setup a service object, it's config object is passed to this
     297             :  * function for further validation. This does not validate service key
     298             :  * material. Return 0 if valid else -1 if invalid. */
     299             : static int
     300          22 : config_validate_service(const hs_service_config_t *config)
     301             : {
     302          22 :   tor_assert(config);
     303             : 
     304             :   /* Amount of ports validation. */
     305          22 :   if (!config->ports || smartlist_len(config->ports) == 0) {
     306           1 :     log_warn(LD_CONFIG, "Hidden service (%s) with no ports configured.",
     307             :              escaped(config->directory_path));
     308           1 :     goto invalid;
     309             :   }
     310             : 
     311             :   /* DoS validation values. */
     312          21 :   if (config->has_dos_defense_enabled &&
     313           2 :       (config->intro_dos_burst_per_sec < config->intro_dos_rate_per_sec)) {
     314           1 :     log_warn(LD_CONFIG, "Hidden service DoS defenses burst (%" PRIu32 ") can "
     315             :                         "not be smaller than the rate value (%" PRIu32 ").",
     316             :              config->intro_dos_burst_per_sec, config->intro_dos_rate_per_sec);
     317           1 :     goto invalid;
     318             :   }
     319             : 
     320             :   /* Valid. */
     321             :   return 0;
     322             :  invalid:
     323             :   return -1;
     324             : }
     325             : 
     326             : /** Configuration function for a version 3 service. The given service
     327             :  * object must be already allocated and passed through
     328             :  * config_generic_service() prior to calling this function.
     329             :  *
     330             :  * Return 0 on success else a negative value. */
     331             : static int
     332          25 : config_service_v3(const hs_opts_t *hs_opts,
     333             :                   hs_service_config_t *config)
     334             : {
     335          25 :   tor_assert(config);
     336          25 :   tor_assert(hs_opts);
     337             : 
     338             :   /* Number of introduction points. */
     339          25 :   if (CHECK_OOB(hs_opts, HiddenServiceNumIntroductionPoints,
     340             :                 NUM_INTRO_POINTS_DEFAULT,
     341             :                 HS_CONFIG_V3_MAX_INTRO_POINTS)) {
     342           2 :     goto err;
     343             :   }
     344          23 :   config->num_intro_points = hs_opts->HiddenServiceNumIntroductionPoints;
     345             : 
     346             :   /* Circuit ID export setting. */
     347          23 :   if (hs_opts->HiddenServiceExportCircuitID) {
     348           0 :     int ok;
     349           0 :     config->circuit_id_protocol =
     350           0 :       helper_parse_circuit_id_protocol("HiddenServcieExportCircuitID",
     351             :                                        hs_opts->HiddenServiceExportCircuitID,
     352             :                                        &ok);
     353           0 :     if (!ok) {
     354           0 :       goto err;
     355             :     }
     356             :   }
     357             : 
     358             :   /* Is the DoS defense enabled? */
     359          23 :   config->has_dos_defense_enabled =
     360          23 :     hs_opts->HiddenServiceEnableIntroDoSDefense;
     361             : 
     362             :   /* Rate for DoS defense */
     363          23 :   if (CHECK_OOB(hs_opts, HiddenServiceEnableIntroDoSRatePerSec,
     364             :                  HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MIN,
     365             :                  HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MAX)) {
     366           0 :     goto err;
     367             :   }
     368          23 :   config->intro_dos_rate_per_sec =
     369          23 :     hs_opts->HiddenServiceEnableIntroDoSRatePerSec;
     370          23 :   log_info(LD_REND, "Service INTRO2 DoS defenses rate set to: %" PRIu32,
     371             :            config->intro_dos_rate_per_sec);
     372             : 
     373          23 :   if (CHECK_OOB(hs_opts, HiddenServiceEnableIntroDoSBurstPerSec,
     374             :                 HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MIN,
     375             :                 HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MAX)) {
     376           0 :     goto err;
     377             :   }
     378          23 :   config->intro_dos_burst_per_sec =
     379          23 :     hs_opts->HiddenServiceEnableIntroDoSBurstPerSec;
     380          23 :   log_info(LD_REND, "Service INTRO2 DoS defenses burst set to: %" PRIu32,
     381             :            config->intro_dos_burst_per_sec);
     382             : 
     383             :   /* Is this an onionbalance instance? */
     384          23 :   if (hs_opts->HiddenServiceOnionBalanceInstance) {
     385             :     /* Option is enabled, parse config file. */
     386           2 :     if (! hs_ob_parse_config_file(config)) {
     387           1 :       goto err;
     388             :     }
     389             :   }
     390             : 
     391             :   /* We do not load the key material for the service at this stage. This is
     392             :    * done later once tor can confirm that it is in a running state. */
     393             : 
     394             :   /* We are about to return a fully configured service so do one last pass of
     395             :    * validation at it. */
     396          22 :   if (config_validate_service(config) < 0) {
     397           2 :     goto err;
     398             :   }
     399             : 
     400             :   return 0;
     401             :  err:
     402             :   return -1;
     403             : }
     404             : 
     405             : /** Configure a service using the given options in hs_opts and options. This is
     406             :  * called for any service regardless of its version which means that all
     407             :  * directives in this function are generic to any service version. This
     408             :  * function will also check the validity of the service directory path.
     409             :  *
     410             :  * The line_ must be pointing to the directive directly after a
     411             :  * HiddenServiceDir. That way, when hitting the next HiddenServiceDir line or
     412             :  * reaching the end of the list of lines, we know that we have to stop looking
     413             :  * for more options.
     414             :  *
     415             :  * Return 0 on success else -1. */
     416             : static int
     417          29 : config_generic_service(const hs_opts_t *hs_opts,
     418             :                        const or_options_t *options,
     419             :                        hs_service_t *service)
     420             : {
     421          29 :   hs_service_config_t *config;
     422             : 
     423          29 :   tor_assert(hs_opts);
     424          29 :   tor_assert(options);
     425          29 :   tor_assert(service);
     426             : 
     427             :   /* Makes thing easier. */
     428          29 :   config = &service->config;
     429             : 
     430             :   /* Directory where the service's keys are stored. */
     431          29 :   tor_assert(hs_opts->HiddenServiceDir);
     432          29 :   config->directory_path = tor_strdup(hs_opts->HiddenServiceDir);
     433          29 :   log_info(LD_CONFIG, "%s=%s. Configuring...",
     434             :            SECTION_HEADER, escaped(config->directory_path));
     435             : 
     436             :   /* Protocol version for the service. */
     437          29 :   if (hs_opts->HiddenServiceVersion == -1) {
     438             :     /* No value was set; stay with the default. */
     439          18 :   } else if (hs_opts->HiddenServiceVersion == 2) {
     440           0 :     log_warn(LD_CONFIG, "Onion services version 2 are obsolete. Please see "
     441             :                         "https://blog.torproject.org/v2-deprecation-timeline "
     442             :                         "for more details and for instructions on how to "
     443             :                         "transition to version 3.");
     444           0 :     goto err;
     445          18 :   } else if (CHECK_OOB(hs_opts, HiddenServiceVersion,
     446             :                        HS_VERSION_MIN, HS_VERSION_MAX)) {
     447           1 :     goto err;
     448             :   } else {
     449          17 :     config->hs_version_explicitly_set = 1;
     450          17 :     config->version = hs_opts->HiddenServiceVersion;
     451             :   }
     452             : 
     453             :   /* Virtual port. */
     454          28 :   for (const config_line_t *portline = hs_opts->HiddenServicePort;
     455          59 :        portline; portline = portline->next) {
     456          33 :     char *err_msg = NULL;
     457             :     /* XXX: Can we rename this? */
     458          33 :     hs_port_config_t *portcfg =
     459          33 :       hs_parse_port_config(portline->value, " ", &err_msg);
     460          33 :     if (!portcfg) {
     461           2 :       if (err_msg) {
     462           2 :         log_warn(LD_CONFIG, "%s", err_msg);
     463             :       }
     464           2 :       tor_free(err_msg);
     465           2 :       goto err;
     466             :     }
     467          31 :     tor_assert(!err_msg);
     468          31 :     smartlist_add(config->ports, portcfg);
     469          31 :     log_info(LD_CONFIG, "HiddenServicePort=%s for %s",
     470             :              portline->value, escaped(config->directory_path));
     471             :   }
     472             : 
     473             :   /* Do we allow unknown ports? */
     474          26 :   config->allow_unknown_ports = hs_opts->HiddenServiceAllowUnknownPorts;
     475             : 
     476             :   /* Directory group readable. */
     477          26 :   config->dir_group_readable = hs_opts->HiddenServiceDirGroupReadable;
     478             : 
     479             :   /* Maximum streams per circuit. */
     480          26 :   if (CHECK_OOB(hs_opts, HiddenServiceMaxStreams,
     481             :                 0, HS_CONFIG_MAX_STREAMS_PER_RDV_CIRCUIT)) {
     482           1 :     goto err;
     483             :   }
     484          25 :   config->max_streams_per_rdv_circuit = hs_opts->HiddenServiceMaxStreams;
     485             : 
     486             :   /* Maximum amount of streams before we close the circuit. */
     487          25 :   config->max_streams_close_circuit =
     488          25 :     hs_opts->HiddenServiceMaxStreamsCloseCircuit;
     489             : 
     490             :   /* Check if we are configured in non anonymous mode meaning every service
     491             :    * becomes a single onion service. */
     492          25 :   if (hs_service_non_anonymous_mode_enabled(options)) {
     493           1 :     config->is_single_onion = 1;
     494             :   }
     495             : 
     496             :   /* Success */
     497             :   return 0;
     498             :  err:
     499             :   return -1;
     500             : }
     501             : 
     502             : /** Configure a service using the given line and options. This function will
     503             :  * call the corresponding configuration function for a specific service
     504             :  * version and validate the service against the other ones. On success, add
     505             :  * the service to the given list and return 0. On error, nothing is added to
     506             :  * the list and a negative value is returned. */
     507             : static int
     508          35 : config_service(config_line_t *line, const or_options_t *options,
     509             :                smartlist_t *service_list)
     510             : {
     511          35 :   int ret;
     512          35 :   hs_service_t *service = NULL;
     513          35 :   hs_opts_t *hs_opts = NULL;
     514          35 :   char *msg = NULL;
     515             : 
     516          35 :   tor_assert(line);
     517          35 :   tor_assert(options);
     518          35 :   tor_assert(service_list);
     519             : 
     520             :   /* We have a new hidden service. */
     521          35 :   service = hs_service_new(options);
     522             : 
     523             :   /* Try to validate and parse the configuration lines into 'hs_opts' */
     524          35 :   hs_opts = hs_opts_new();
     525          35 :   ret = config_assign(get_hs_opts_mgr(), hs_opts, line, 0, &msg);
     526          35 :   if (ret < 0) {
     527           6 :     log_warn(LD_REND, "Can't parse configuration for onion service: %s", msg);
     528           6 :     goto err;
     529             :   }
     530          29 :   tor_assert_nonfatal(msg == NULL);
     531          29 :   validation_status_t vs = config_validate(get_hs_opts_mgr(), NULL,
     532             :                                            hs_opts, &msg);
     533          29 :   if (vs < 0) {
     534           0 :     log_warn(LD_REND, "Bad configuration for onion service: %s", msg);
     535           0 :     goto err;
     536             :   }
     537          29 :   tor_assert_nonfatal(msg == NULL);
     538             : 
     539             :   /* We'll configure that service as a generic one and then pass it to a
     540             :    * specific function according to the configured version number. */
     541          29 :   if (config_generic_service(hs_opts, options, service) < 0) {
     542           4 :     goto err;
     543             :   }
     544             : 
     545          25 :   tor_assert(service->config.version <= HS_VERSION_MAX);
     546             : 
     547             :   /* Check permission on service directory that was just parsed. And this must
     548             :    * be done regardless of the service version. Do not ask for the directory
     549             :    * to be created, this is done when the keys are loaded because we could be
     550             :    * in validation mode right now. */
     551          25 :   if (hs_check_service_private_dir(options->User,
     552          25 :                                    service->config.directory_path,
     553          25 :                                    service->config.dir_group_readable,
     554             :                                    0) < 0) {
     555           0 :     goto err;
     556             :   }
     557             : 
     558             :   /* We'll try to learn the service version here by loading the key(s) if
     559             :    * present and we did not set HiddenServiceVersion. Depending on the key
     560             :    * format, we can figure out the service version. */
     561          25 :   if (!service->config.hs_version_explicitly_set) {
     562          11 :     service->config.version = config_learn_service_version(service);
     563             :   }
     564             : 
     565             :   /* We make sure that this set of options for a service are valid. */
     566          25 :   if (config_has_invalid_options(line->next, service)) {
     567           0 :     goto err;
     568             :   }
     569             : 
     570             :   /* Different functions are in charge of specific options for a version. We
     571             :    * start just after the service directory line so once we hit another
     572             :    * directory line, the function knows that it has to stop parsing. */
     573          25 :   switch (service->config.version) {
     574          25 :   case HS_VERSION_THREE:
     575          25 :     ret = config_service_v3(hs_opts, &service->config);
     576          25 :     break;
     577           0 :   default:
     578             :     /* We do validate before if we support the parsed version. */
     579           0 :     tor_assert_nonfatal_unreached();
     580           0 :     goto err;
     581             :   }
     582          25 :   if (ret < 0) {
     583           5 :     goto err;
     584             :   }
     585             : 
     586             :   /* We'll check if this service can be kept depending on the others
     587             :    * configured previously. */
     588          20 :   if (service_is_duplicate_in_list(service_list, service)) {
     589           1 :     goto err;
     590             :   }
     591             : 
     592             :   /* Passes, add it to the given list. */
     593          19 :   smartlist_add(service_list, service);
     594          19 :   hs_opts_free(hs_opts);
     595             : 
     596          19 :   return 0;
     597             : 
     598          16 :  err:
     599          16 :   hs_service_free(service);
     600          16 :   hs_opts_free(hs_opts);
     601          16 :   tor_free(msg);
     602          16 :   return -1;
     603             : }
     604             : 
     605             : /** From a set of <b>options</b>, setup every hidden service found. Return 0 on
     606             :  * success or -1 on failure. If <b>validate_only</b> is set, parse, warn and
     607             :  * return as normal, but don't actually change the configured services. */
     608             : int
     609         460 : hs_config_service_all(const or_options_t *options, int validate_only)
     610             : {
     611         460 :   int ret = -1;
     612         460 :   config_line_t *remaining = NULL;
     613         460 :   smartlist_t *new_service_list = NULL;
     614             : 
     615         460 :   tor_assert(options);
     616             : 
     617             :   /* Newly configured service are put in that list which is then used for
     618             :    * validation and staging for >= v3. */
     619         460 :   new_service_list = smartlist_new();
     620             : 
     621             :   /* We need to start with a HiddenServiceDir line */
     622         460 :   if (options->RendConfigLines &&
     623          35 :       strcasecmp(options->RendConfigLines->key, SECTION_HEADER)) {
     624           2 :     log_warn(LD_CONFIG, "%s with no preceding %s directive",
     625             :              options->RendConfigLines->key, SECTION_HEADER);
     626           2 :     goto err;
     627             :   }
     628             : 
     629         458 :   remaining = config_lines_dup(options->RendConfigLines);
     630         477 :   while (remaining) {
     631          35 :     config_line_t *section = remaining;
     632          35 :     remaining = config_lines_partition(section, SECTION_HEADER);
     633             : 
     634             :     /* Try to configure this service now. On success, it will be added to the
     635             :      * list and validated against the service in that same list. */
     636          35 :     int rv = config_service(section, options, new_service_list);
     637          35 :     config_free_lines(section);
     638          35 :     if (rv < 0) {
     639          16 :       goto err;
     640             :     }
     641             :   }
     642             : 
     643             :   /* In non validation mode, we'll stage those services we just successfully
     644             :    * configured. Service ownership is transferred from the list to the global
     645             :    * state. If any service is invalid, it will be removed from the list and
     646             :    * freed. All versions are handled in that function. */
     647         442 :   if (!validate_only) {
     648          10 :     stage_services(new_service_list);
     649             :   } else {
     650             :     /* We've just validated that we were able to build a clean working list of
     651             :      * services. We don't need those objects anymore. */
     652         444 :     SMARTLIST_FOREACH(new_service_list, hs_service_t *, s,
     653             :                       hs_service_free(s));
     654             :   }
     655             : 
     656             :   /* Success. Note that the service list has no ownership of its content. */
     657         442 :   ret = 0;
     658         442 :   goto end;
     659             : 
     660          18 :  err:
     661          19 :   SMARTLIST_FOREACH(new_service_list, hs_service_t *, s, hs_service_free(s));
     662             : 
     663         460 :  end:
     664         460 :   smartlist_free(new_service_list);
     665             :   /* Tor main should call the free all function on error. */
     666         460 :   return ret;
     667             : }
     668             : 
     669             : /** From a set of <b>options</b>, setup every client authorization found.
     670             :  * Return 0 on success or -1 on failure. If <b>validate_only</b> is set,
     671             :  * parse, warn and return as normal, but don't actually change the
     672             :  * configured state. */
     673             : int
     674         435 : hs_config_client_auth_all(const or_options_t *options, int validate_only)
     675             : {
     676         435 :   int ret = -1;
     677             : 
     678             :   /* Configure v3 authorization. */
     679         435 :   if (hs_config_client_authorization(options, validate_only) < 0) {
     680           0 :     goto done;
     681             :   }
     682             : 
     683             :   /* Success. */
     684             :   ret = 0;
     685         435 :  done:
     686         435 :   return ret;
     687             : }
     688             : 
     689             : /**
     690             :  * Free all resources held by the hs_config.c module.
     691             :  **/
     692             : void
     693         285 : hs_config_free_all(void)
     694             : {
     695         285 :   config_mgr_free(hs_opts_mgr);
     696         285 : }

Generated by: LCOV version 1.14