LCOV - code coverage report
Current view: top level - feature/hs - hs_ob.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 114 122 93.4 %
Date: 2021-11-24 03:28:48 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /* Copyright (c) 2017-2021, The Tor Project, Inc. */
       2             : /* See LICENSE for licensing information */
       3             : 
       4             : /**
       5             :  * \file hs_ob.c
       6             :  * \brief Implement Onion Balance specific code.
       7             :  **/
       8             : 
       9             : #define HS_OB_PRIVATE
      10             : 
      11             : #include "feature/hs/hs_service.h"
      12             : 
      13             : #include "feature/nodelist/networkstatus.h"
      14             : #include "feature/nodelist/networkstatus_st.h"
      15             : 
      16             : #include "lib/confmgt/confmgt.h"
      17             : #include "lib/encoding/confline.h"
      18             : 
      19             : #include "feature/hs/hs_ob.h"
      20             : 
      21             : /* Options config magic number. */
      22             : #define OB_OPTIONS_MAGIC 0x631DE7EA
      23             : 
      24             : /* Helper macros. */
      25             : #define VAR(varname, conftype, member, initvalue)                          \
      26             :   CONFIG_VAR_ETYPE(ob_options_t, varname, conftype, member, 0, initvalue)
      27             : #define V(member,conftype,initvalue)        \
      28             :   VAR(#member, conftype, member, initvalue)
      29             : 
      30             : /* Dummy instance of ob_options_t, used for type-checking its members with
      31             :  * CONF_CHECK_VAR_TYPE. */
      32             : DUMMY_TYPECHECK_INSTANCE(ob_options_t);
      33             : 
      34             : /* Array of variables for the config file options. */
      35             : static const config_var_t config_vars[] = {
      36             :   V(MasterOnionAddress, LINELIST, NULL),
      37             : 
      38             :   END_OF_CONFIG_VARS
      39             : };
      40             : 
      41             : /* "Extra" variable in the state that receives lines we can't parse. This
      42             :  * lets us preserve options from versions of Tor newer than us. */
      43             : static const struct_member_t config_extra_vars = {
      44             :   .name = "__extra",
      45             :   .type = CONFIG_TYPE_LINELIST,
      46             :   .offset = offsetof(ob_options_t, ExtraLines),
      47             : };
      48             : 
      49             : /* Configuration format of ob_options_t. */
      50             : static const config_format_t config_format = {
      51             :   .size = sizeof(ob_options_t),
      52             :   .magic = {
      53             :      "ob_options_t",
      54             :      OB_OPTIONS_MAGIC,
      55             :      offsetof(ob_options_t, magic_),
      56             :   },
      57             :   .vars = config_vars,
      58             :   .extra = &config_extra_vars,
      59             : };
      60             : 
      61             : /* Global configuration manager for the config file. */
      62             : static config_mgr_t *config_options_mgr = NULL;
      63             : 
      64             : /* Return the configuration manager for the config file. */
      65             : static const config_mgr_t *
      66           8 : get_config_options_mgr(void)
      67             : {
      68           8 :   if (PREDICT_UNLIKELY(config_options_mgr == NULL)) {
      69           2 :     config_options_mgr = config_mgr_new(&config_format);
      70           2 :     config_mgr_freeze(config_options_mgr);
      71             :   }
      72           8 :   return config_options_mgr;
      73             : }
      74             : 
      75             : #define ob_option_free(val) \
      76             :   FREE_AND_NULL(ob_options_t, ob_option_free_, (val))
      77             : 
      78             : /** Helper: Free a config options object. */
      79             : static void
      80           2 : ob_option_free_(ob_options_t *opts)
      81             : {
      82           2 :   if (opts == NULL) {
      83             :     return;
      84             :   }
      85           2 :   config_free(get_config_options_mgr(), opts);
      86             : }
      87             : 
      88             : /** Return an allocated config options object. */
      89             : static ob_options_t *
      90           2 : ob_option_new(void)
      91             : {
      92           2 :   ob_options_t *opts = config_new(get_config_options_mgr());
      93           2 :   config_init(get_config_options_mgr(), opts);
      94           2 :   return opts;
      95             : }
      96             : 
      97             : /** Helper function: From the configuration line value which is an onion
      98             :  * address with the ".onion" extension, find the public key and put it in
      99             :  * pkey_out.
     100             :  *
     101             :  * On success, true is returned. Else, false and pkey is untouched. */
     102             : static bool
     103           3 : get_onion_public_key(const char *value, ed25519_public_key_t *pkey_out)
     104             : {
     105           3 :   char address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
     106             : 
     107           3 :   tor_assert(value);
     108           3 :   tor_assert(pkey_out);
     109             : 
     110           3 :   if (strcmpend(value, ".onion")) {
     111             :     /* Not a .onion extension, bad format. */
     112             :     return false;
     113             :   }
     114             : 
     115             :   /* Length validation. The -1 is because sizeof() counts the NUL byte. */
     116           3 :   if (strlen(value) >
     117             :       (HS_SERVICE_ADDR_LEN_BASE32 + sizeof(".onion") - 1)) {
     118             :     /* Too long, bad format. */
     119             :     return false;
     120             :   }
     121             : 
     122             :   /* We don't want the .onion so we add 2 because size - 1 is copied with
     123             :    * strlcpy() in order to accommodate the NUL byte and sizeof() counts the NUL
     124             :    * byte so we need to remove them from the equation. */
     125           3 :   strlcpy(address, value, strlen(value) - sizeof(".onion") + 2);
     126             : 
     127           3 :   if (hs_parse_address_no_log(address, pkey_out, NULL, NULL, NULL) < 0) {
     128           1 :     return false;
     129             :   }
     130             : 
     131             :   /* Success. */
     132             :   return true;
     133             : }
     134             : 
     135             : /** Parse the given ob options in opts and set the service config object
     136             :  * accordingly.
     137             :  *
     138             :  * Return 1 on success else 0. */
     139             : static int
     140           2 : ob_option_parse(hs_service_config_t *config, const ob_options_t *opts)
     141             : {
     142           2 :   int ret = 0;
     143           2 :   config_line_t *line;
     144             : 
     145           2 :   tor_assert(config);
     146           2 :   tor_assert(opts);
     147             : 
     148           4 :   for (line = opts->MasterOnionAddress; line; line = line->next) {
     149             :     /* Allocate config list if need be. */
     150           3 :     if (!config->ob_master_pubkeys) {
     151           2 :       config->ob_master_pubkeys = smartlist_new();
     152             :     }
     153           3 :     ed25519_public_key_t *pubkey = tor_malloc_zero(sizeof(*pubkey));
     154             : 
     155           3 :     if (!get_onion_public_key(line->value, pubkey)) {
     156           1 :       log_warn(LD_REND, "OnionBalance: MasterOnionAddress %s is invalid",
     157             :                line->value);
     158           1 :       tor_free(pubkey);
     159           1 :       goto end;
     160             :     }
     161           2 :     smartlist_add(config->ob_master_pubkeys, pubkey);
     162           2 :     log_notice(LD_REND, "OnionBalance: MasterOnionAddress %s registered",
     163             :                line->value);
     164             :   }
     165             :   /* Success. */
     166             :   ret = 1;
     167             : 
     168           2 :  end:
     169             :   /* No keys added, we free the list since no list means no onion balance
     170             :    * support for this tor instance. */
     171           2 :   if (smartlist_len(config->ob_master_pubkeys) == 0) {
     172           1 :     smartlist_free(config->ob_master_pubkeys);
     173             :   }
     174           2 :   return ret;
     175             : }
     176             : 
     177             : /** For the given master public key and time period, compute the subcredential
     178             :  * and put them into subcredential. The subcredential parameter needs to be at
     179             :  * least DIGEST256_LEN in size. */
     180             : static void
     181           9 : build_subcredential(const ed25519_public_key_t *pkey, uint64_t tp,
     182             :                     hs_subcredential_t *subcredential)
     183             : {
     184           9 :   ed25519_public_key_t blinded_pubkey;
     185             : 
     186           9 :   tor_assert(pkey);
     187           9 :   tor_assert(subcredential);
     188             : 
     189           9 :   hs_build_blinded_pubkey(pkey, NULL, 0, tp, &blinded_pubkey);
     190           9 :   hs_get_subcredential(pkey, &blinded_pubkey, subcredential);
     191           9 : }
     192             : 
     193             : /*
     194             :  * Public API.
     195             :  */
     196             : 
     197             : /** Return true iff the given service is configured as an onion balance
     198             :  * instance. To satisfy that condition, there must at least be one master
     199             :  * ed25519 public key configured. */
     200             : bool
     201          55 : hs_ob_service_is_instance(const hs_service_t *service)
     202             : {
     203          55 :   if (BUG(service == NULL)) {
     204           0 :     return false;
     205             :   }
     206             : 
     207             :   /* No list, we are not an instance. */
     208          55 :   if (!service->config.ob_master_pubkeys) {
     209             :     return false;
     210             :   }
     211             : 
     212           9 :   return smartlist_len(service->config.ob_master_pubkeys) > 0;
     213             : }
     214             : 
     215             : /** Read and parse the config file at fname on disk. The service config object
     216             :  * is populated with the options if any.
     217             :  *
     218             :  * Return 1 on success else 0. This is to follow the "ok" convention in
     219             :  * hs_config.c. */
     220             : int
     221           2 : hs_ob_parse_config_file(hs_service_config_t *config)
     222             : {
     223           2 :   static const char *fname = "ob_config";
     224           2 :   int ret = 0;
     225           2 :   char *content = NULL, *errmsg = NULL, *config_file_path = NULL;
     226           2 :   ob_options_t *options = NULL;
     227           2 :   config_line_t *lines = NULL;
     228             : 
     229           2 :   tor_assert(config);
     230             : 
     231             :   /* Read file from disk. */
     232           2 :   config_file_path = hs_path_from_filename(config->directory_path, fname);
     233           2 :   content = read_file_to_str(config_file_path, 0, NULL);
     234           2 :   if (!content) {
     235           0 :     log_warn(LD_FS, "OnionBalance: Unable to read config file %s",
     236             :              escaped(config_file_path));
     237           0 :     goto end;
     238             :   }
     239             : 
     240             :   /* Parse lines. */
     241           2 :   if (config_get_lines(content, &lines, 0) < 0) {
     242           0 :     goto end;
     243             :   }
     244             : 
     245           2 :   options = ob_option_new();
     246           2 :   config_assign(get_config_options_mgr(), options, lines, 0, &errmsg);
     247           2 :   if (errmsg) {
     248           0 :     log_warn(LD_REND, "OnionBalance: Unable to parse config file: %s",
     249             :              errmsg);
     250           0 :     tor_free(errmsg);
     251           0 :     goto end;
     252             :   }
     253             : 
     254             :   /* Parse the options and set the service config object with the details. */
     255           2 :   ret = ob_option_parse(config, options);
     256             : 
     257           2 :  end:
     258           2 :   config_free_lines(lines);
     259           2 :   ob_option_free(options);
     260           2 :   tor_free(content);
     261           2 :   tor_free(config_file_path);
     262           2 :   return ret;
     263             : }
     264             : 
     265             : /** Compute all possible subcredentials for every onion master key in the given
     266             :  * service config object. subcredentials_out is allocated and set as an
     267             :  * continuous array containing all possible values.
     268             :  *
     269             :  * On success, return the number of subcredential put in the array which will
     270             :  * correspond to an array of size: n * DIGEST256_LEN where DIGEST256_LEN is the
     271             :  * length of a single subcredential.
     272             :  *
     273             :  * If the given configuration object has no OB master keys configured, 0 is
     274             :  * returned and subcredentials_out is set to NULL.
     275             :  *
     276             :  * Otherwise, this can't fail. */
     277             : STATIC size_t
     278           4 : compute_subcredentials(const hs_service_t *service,
     279             :                        hs_subcredential_t **subcredentials_out)
     280             : {
     281           4 :   unsigned int num_pkeys, idx = 0;
     282           4 :   hs_subcredential_t *subcreds = NULL;
     283           4 :   const int steps[3] = {0, -1, 1};
     284           4 :   const unsigned int num_steps = ARRAY_LENGTH(steps);
     285           4 :   const uint64_t tp = hs_get_time_period_num(0);
     286             : 
     287           4 :   tor_assert(service);
     288           4 :   tor_assert(subcredentials_out);
     289             :   /* Our caller has checked these too */
     290           4 :   tor_assert(service->desc_current);
     291           4 :   tor_assert(service->desc_next);
     292             : 
     293             :   /* Make sure we are an OB instance, or bail out. */
     294           4 :   num_pkeys = smartlist_len(service->config.ob_master_pubkeys);
     295           4 :   if (!num_pkeys) {
     296           1 :     *subcredentials_out = NULL;
     297           1 :     return 0;
     298             :   }
     299             : 
     300             :   /* Time to build all the subcredentials for each time period: two for each
     301             :    * instance descriptor plus three for the onionbalance frontend service: the
     302             :    * previous one (-1), the current one (0) and the next one (1) for each
     303             :    * configured key in order to accommodate client and service consensus skew.
     304             :    *
     305             :    * If the client consensus after_time is at 23:00 but the service one is at
     306             :    * 01:00, the client will be using the previous time period where the
     307             :    * service will think it is the client next time period. Thus why we have
     308             :    * to try them all.
     309             :    *
     310             :    * The normal use case works because the service gets the descriptor object
     311             :    * that corresponds to the intro point's request, and because each
     312             :    * descriptor corresponds to a specific subcredential, we get the right
     313             :    * subcredential out of it, and use that to do the decryption.
     314             :    *
     315             :    * As a slight optimization, statistically, the current time period (0) will
     316             :    * be the one to work first so we'll put them first in the array to maximize
     317             :    * our chance of success. */
     318             : 
     319             :   /* We use a flat array, not a smartlist_t, in order to minimize memory
     320             :    * allocation.
     321             :    *
     322             :    * Size of array is: length of a single subcredential multiplied by the
     323             :    * number of time period we need to compute and finally multiplied by the
     324             :    * total number of keys we are about to process. In other words, for each
     325             :    * key, we allocate 3 subcredential slots. Then in the end we also add two
     326             :    * subcredentials for this instance's active descriptors. */
     327           3 :   subcreds =
     328           3 :     tor_calloc((num_steps * num_pkeys) + 2, sizeof(hs_subcredential_t));
     329             : 
     330             :   /* For each master pubkey we add 3 subcredentials: */
     331          12 :   for (unsigned int i = 0; i < num_steps; i++) {
     332          18 :     SMARTLIST_FOREACH_BEGIN(service->config.ob_master_pubkeys,
     333             :                             const ed25519_public_key_t *, pkey) {
     334           9 :       build_subcredential(pkey, tp + steps[i], &subcreds[idx]);
     335           9 :       idx++;
     336           9 :     } SMARTLIST_FOREACH_END(pkey);
     337             :   }
     338             : 
     339             :   /* And then in the end we add the two subcredentials of the current active
     340             :    * instance descriptors */
     341           3 :   memcpy(&subcreds[idx++], &service->desc_current->desc->subcredential,
     342             :          sizeof(hs_subcredential_t));
     343           3 :   memcpy(&subcreds[idx++], &service->desc_next->desc->subcredential,
     344             :          sizeof(hs_subcredential_t));
     345             : 
     346           3 :   log_info(LD_REND, "Refreshing %u onionbalance keys (TP #%d).",
     347             :              idx, (int)tp);
     348             : 
     349           3 :   *subcredentials_out = subcreds;
     350           3 :   return idx;
     351             : }
     352             : 
     353             : /**
     354             :  *  If we are an Onionbalance instance, refresh our keys.
     355             :  *
     356             :  *  If we are not an Onionbalance instance or we are not ready to do so, this
     357             :  *  is a NOP.
     358             :  *
     359             :  *  This function is called every time we build a new descriptor. That's
     360             :  *  because we want our Onionbalance keys to always use up-to-date
     361             :  *  subcredentials both for the instance (ourselves) and for the onionbalance
     362             :  *  frontend.
     363             :  */
     364             : void
     365          46 : hs_ob_refresh_keys(hs_service_t *service)
     366             : {
     367          46 :   hs_subcredential_t *ob_subcreds = NULL;
     368          46 :   size_t num_subcreds;
     369             : 
     370          46 :   tor_assert(service);
     371             : 
     372             :   /* Don't do any of this if we are not configured as an OB instance */
     373          46 :   if (!hs_ob_service_is_instance(service)) {
     374          44 :     return;
     375             :   }
     376             : 
     377             :   /* We need both service descriptors created to make onionbalance keys.
     378             :    *
     379             :    * That's because we fetch our own (the instance's) subcredentials from our
     380             :    * own descriptors which should always include the latest subcredentials that
     381             :    * clients would use.
     382             :    *
     383             :    * This function is called with each descriptor build, so we will be
     384             :    * eventually be called when both descriptors are created. */
     385           2 :   if (!service->desc_current || !service->desc_next) {
     386             :     return;
     387             :   }
     388             : 
     389             :   /* Get a new set of subcreds */
     390           2 :   num_subcreds = compute_subcredentials(service, &ob_subcreds);
     391           2 :   if (BUG(!num_subcreds)) {
     392           0 :     return;
     393             :   }
     394             : 
     395             :   /* Delete old subcredentials if any */
     396           2 :   if (service->state.ob_subcreds) {
     397           1 :     tor_free(service->state.ob_subcreds);
     398             :   }
     399             : 
     400           2 :   service->state.ob_subcreds = ob_subcreds;
     401           2 :   service->state.n_ob_subcreds = num_subcreds;
     402             : }
     403             : 
     404             : /** Free any memory allocated by the onionblance subsystem. */
     405             : void
     406         278 : hs_ob_free_all(void)
     407             : {
     408         278 :   config_mgr_free(config_options_mgr);
     409         278 : }

Generated by: LCOV version 1.14