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

          Line data    Source code
       1             : /* Copyright (c) 2016-2021, The Tor Project, Inc. */
       2             : /* See LICENSE for licensing information */
       3             : 
       4             : /**
       5             :  * \file hs_intropoint.c
       6             :  * \brief Implement next generation introductions point functionality
       7             :  **/
       8             : 
       9             : #define HS_INTROPOINT_PRIVATE
      10             : 
      11             : #include "core/or/or.h"
      12             : #include "app/config/config.h"
      13             : #include "core/or/channel.h"
      14             : #include "core/or/circuitlist.h"
      15             : #include "core/or/circuituse.h"
      16             : #include "core/or/relay.h"
      17             : #include "feature/rend/rendmid.h"
      18             : #include "feature/stats/rephist.h"
      19             : #include "lib/crypt_ops/crypto_format.h"
      20             : 
      21             : /* Trunnel */
      22             : #include "trunnel/ed25519_cert.h"
      23             : #include "trunnel/hs/cell_common.h"
      24             : #include "trunnel/hs/cell_establish_intro.h"
      25             : #include "trunnel/hs/cell_introduce1.h"
      26             : 
      27             : #include "feature/hs/hs_circuitmap.h"
      28             : #include "feature/hs/hs_common.h"
      29             : #include "feature/hs/hs_config.h"
      30             : #include "feature/hs/hs_descriptor.h"
      31             : #include "feature/hs/hs_dos.h"
      32             : #include "feature/hs/hs_intropoint.h"
      33             : 
      34             : #include "core/or/or_circuit_st.h"
      35             : 
      36             : /** Extract the authentication key from an ESTABLISH_INTRO or INTRODUCE1 using
      37             :  * the given <b>cell_type</b> from <b>cell</b> and place it in
      38             :  * <b>auth_key_out</b>. */
      39             : STATIC void
      40          17 : get_auth_key_from_cell(ed25519_public_key_t *auth_key_out,
      41             :                        unsigned int cell_type, const void *cell)
      42             : {
      43          17 :   size_t auth_key_len;
      44          17 :   const uint8_t *key_array;
      45             : 
      46          17 :   tor_assert(auth_key_out);
      47          17 :   tor_assert(cell);
      48             : 
      49          17 :   switch (cell_type) {
      50          16 :   case RELAY_COMMAND_ESTABLISH_INTRO:
      51             :   {
      52          16 :     const trn_cell_establish_intro_t *c_cell = cell;
      53          16 :     key_array = trn_cell_establish_intro_getconstarray_auth_key(c_cell);
      54          16 :     auth_key_len = trn_cell_establish_intro_getlen_auth_key(c_cell);
      55          16 :     break;
      56             :   }
      57           1 :   case RELAY_COMMAND_INTRODUCE1:
      58             :   {
      59           1 :     const trn_cell_introduce1_t *c_cell = cell;
      60           1 :     key_array = trn_cell_introduce1_getconstarray_auth_key(cell);
      61           1 :     auth_key_len = trn_cell_introduce1_getlen_auth_key(c_cell);
      62           1 :     break;
      63             :   }
      64           0 :   default:
      65             :     /* Getting here is really bad as it means we got a unknown cell type from
      66             :      * this file where every call has an hardcoded value. */
      67             :     tor_assert_unreached(); /* LCOV_EXCL_LINE */
      68             :   }
      69          17 :   tor_assert(key_array);
      70          17 :   tor_assert(auth_key_len == sizeof(auth_key_out->pubkey));
      71          17 :   memcpy(auth_key_out->pubkey, key_array, auth_key_len);
      72          17 : }
      73             : 
      74             : /** We received an ESTABLISH_INTRO <b>cell</b>. Verify its signature and MAC,
      75             :  *  given <b>circuit_key_material</b>. Return 0 on success else -1 on error. */
      76             : STATIC int
      77          11 : verify_establish_intro_cell(const trn_cell_establish_intro_t *cell,
      78             :                             const uint8_t *circuit_key_material,
      79             :                             size_t circuit_key_material_len)
      80             : {
      81             :   /* We only reach this function if the first byte of the cell is 0x02 which
      82             :    * means that auth_key_type is of ed25519 type, hence this check should
      83             :    * always pass. See hs_intro_received_establish_intro().  */
      84          11 :   if (BUG(cell->auth_key_type != TRUNNEL_HS_INTRO_AUTH_KEY_TYPE_ED25519)) {
      85           0 :     return -1;
      86             :   }
      87             : 
      88             :   /* Make sure the auth key length is of the right size for this type. For
      89             :    * EXTRA safety, we check both the size of the array and the length which
      90             :    * must be the same. Safety first!*/
      91          21 :   if (trn_cell_establish_intro_getlen_auth_key(cell) != ED25519_PUBKEY_LEN ||
      92          10 :       trn_cell_establish_intro_get_auth_key_len(cell) != ED25519_PUBKEY_LEN) {
      93           1 :     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
      94             :            "ESTABLISH_INTRO auth key length is invalid");
      95           1 :     return -1;
      96             :   }
      97             : 
      98          10 :   const uint8_t *msg = cell->start_cell;
      99             : 
     100             :   /* Verify the sig */
     101             :   {
     102          10 :     ed25519_signature_t sig_struct;
     103          10 :     const uint8_t *sig_array =
     104          10 :       trn_cell_establish_intro_getconstarray_sig(cell);
     105             : 
     106             :     /* Make sure the signature length is of the right size. For EXTRA safety,
     107             :      * we check both the size of the array and the length which must be the
     108             :      * same. Safety first!*/
     109          19 :     if (trn_cell_establish_intro_getlen_sig(cell) != sizeof(sig_struct.sig) ||
     110           9 :         trn_cell_establish_intro_get_sig_len(cell) != sizeof(sig_struct.sig)) {
     111           1 :       log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
     112             :              "ESTABLISH_INTRO sig len is invalid");
     113           2 :       return -1;
     114             :     }
     115             :     /* We are now sure that sig_len is of the right size. */
     116           9 :     memcpy(sig_struct.sig, sig_array, cell->sig_len);
     117             : 
     118           9 :     ed25519_public_key_t auth_key;
     119           9 :     get_auth_key_from_cell(&auth_key, RELAY_COMMAND_ESTABLISH_INTRO, cell);
     120             : 
     121           9 :     const size_t sig_msg_len = cell->end_sig_fields - msg;
     122           9 :     int sig_mismatch = ed25519_checksig_prefixed(&sig_struct,
     123             :                                                  msg, sig_msg_len,
     124             :                                                  ESTABLISH_INTRO_SIG_PREFIX,
     125             :                                                  &auth_key);
     126           9 :     if (sig_mismatch) {
     127           1 :       log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
     128             :              "ESTABLISH_INTRO signature not as expected");
     129           1 :       return -1;
     130             :     }
     131             :   }
     132             : 
     133             :   /* Verify the MAC */
     134             :   {
     135           8 :     const size_t auth_msg_len = cell->end_mac_fields - msg;
     136           8 :     uint8_t mac[DIGEST256_LEN];
     137           8 :     crypto_mac_sha3_256(mac, sizeof(mac),
     138             :                         circuit_key_material, circuit_key_material_len,
     139             :                         msg, auth_msg_len);
     140           8 :     if (tor_memneq(mac, cell->handshake_mac, sizeof(mac))) {
     141           1 :       log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
     142             :              "ESTABLISH_INTRO handshake_auth not as expected");
     143           1 :       return -1;
     144             :     }
     145             :   }
     146             : 
     147           7 :   return 0;
     148             : }
     149             : 
     150             : /** Send an INTRO_ESTABLISHED cell to <b>circ</b>. */
     151           5 : MOCK_IMPL(int,
     152             : hs_intro_send_intro_established_cell,(or_circuit_t *circ))
     153             : {
     154           5 :   int ret;
     155           5 :   uint8_t *encoded_cell = NULL;
     156           5 :   ssize_t encoded_len, result_len;
     157           5 :   trn_cell_intro_established_t *cell;
     158           5 :   trn_cell_extension_t *ext;
     159             : 
     160           5 :   tor_assert(circ);
     161             : 
     162             :   /* Build the cell payload. */
     163           5 :   cell = trn_cell_intro_established_new();
     164           5 :   ext = trn_cell_extension_new();
     165           5 :   trn_cell_extension_set_num(ext, 0);
     166           5 :   trn_cell_intro_established_set_extensions(cell, ext);
     167             :   /* Encode the cell to binary format. */
     168           5 :   encoded_len = trn_cell_intro_established_encoded_len(cell);
     169           5 :   tor_assert(encoded_len > 0);
     170           5 :   encoded_cell = tor_malloc_zero(encoded_len);
     171           5 :   result_len = trn_cell_intro_established_encode(encoded_cell, encoded_len,
     172             :                                                 cell);
     173           5 :   tor_assert(encoded_len == result_len);
     174             : 
     175           5 :   ret = relay_send_command_from_edge(0, TO_CIRCUIT(circ),
     176             :                                      RELAY_COMMAND_INTRO_ESTABLISHED,
     177             :                                      (char *) encoded_cell, encoded_len,
     178             :                                      NULL);
     179             :   /* On failure, the above function will close the circuit. */
     180           5 :   trn_cell_intro_established_free(cell);
     181           5 :   tor_free(encoded_cell);
     182           5 :   return ret;
     183             : }
     184             : 
     185             : /** Validate the cell DoS extension parameters. Return true iff they've been
     186             :  * bound check and can be used. Else return false. See proposal 305 for
     187             :  * details and reasons about this validation. */
     188             : STATIC bool
     189          10 : cell_dos_extension_parameters_are_valid(uint64_t intro2_rate_per_sec,
     190             :                                         uint64_t intro2_burst_per_sec)
     191             : {
     192          10 :   bool ret = false;
     193             : 
     194             :   /* Check that received value is not below the minimum. Don't check if minimum
     195             :      is set to 0, since the param is a positive value and gcc will complain. */
     196             : #if HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MIN > 0
     197             :   if (intro2_rate_per_sec < HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MIN) {
     198             :     log_fn(LOG_PROTOCOL_WARN, LD_REND,
     199             :            "Intro point DoS defenses rate per second is "
     200             :            "too small. Received value: %" PRIu64, intro2_rate_per_sec);
     201             :     goto end;
     202             :   }
     203             : #endif /* HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MIN > 0 */
     204             : 
     205             :   /* Check that received value is not above maximum */
     206          10 :   if (intro2_rate_per_sec > HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MAX) {
     207           2 :     log_fn(LOG_PROTOCOL_WARN, LD_REND,
     208             :            "Intro point DoS defenses rate per second is "
     209             :            "too big. Received value: %" PRIu64, intro2_rate_per_sec);
     210           2 :     goto end;
     211             :   }
     212             : 
     213             :   /* Check that received value is not below the minimum */
     214             : #if HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MIN > 0
     215             :   if (intro2_burst_per_sec < HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MIN) {
     216             :     log_fn(LOG_PROTOCOL_WARN, LD_REND,
     217             :            "Intro point DoS defenses burst per second is "
     218             :            "too small. Received value: %" PRIu64, intro2_burst_per_sec);
     219             :     goto end;
     220             :   }
     221             : #endif /* HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MIN > 0 */
     222             : 
     223             :   /* Check that received value is not above maximum */
     224           8 :   if (intro2_burst_per_sec > HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MAX) {
     225           1 :     log_fn(LOG_PROTOCOL_WARN, LD_REND,
     226             :            "Intro point DoS defenses burst per second is "
     227             :            "too big. Received value: %" PRIu64, intro2_burst_per_sec);
     228           1 :     goto end;
     229             :   }
     230             : 
     231             :   /* In a rate limiting scenario, burst can never be smaller than the rate. At
     232             :    * best it can be equal. */
     233           7 :   if (intro2_burst_per_sec < intro2_rate_per_sec) {
     234           2 :     log_info(LD_REND, "Intro point DoS defenses burst is smaller than rate. "
     235             :                       "Rate: %" PRIu64 " vs Burst: %" PRIu64,
     236             :              intro2_rate_per_sec, intro2_burst_per_sec);
     237           2 :     goto end;
     238             :   }
     239             : 
     240             :   /* Passing validation. */
     241             :   ret = true;
     242             : 
     243          10 :  end:
     244          10 :   return ret;
     245             : }
     246             : 
     247             : /** Parse the cell DoS extension and apply defenses on the given circuit if
     248             :  * validation passes. If the cell extension is malformed or contains unusable
     249             :  * values, the DoS defenses is disabled on the circuit. */
     250             : static void
     251           5 : handle_establish_intro_cell_dos_extension(
     252             :                                 const trn_cell_extension_field_t *field,
     253             :                                 or_circuit_t *circ)
     254             : {
     255           5 :   ssize_t ret;
     256           5 :   uint64_t intro2_rate_per_sec = 0, intro2_burst_per_sec = 0;
     257           5 :   trn_cell_extension_dos_t *dos = NULL;
     258             : 
     259           5 :   tor_assert(field);
     260           5 :   tor_assert(circ);
     261             : 
     262           5 :   ret = trn_cell_extension_dos_parse(&dos,
     263             :                  trn_cell_extension_field_getconstarray_field(field),
     264             :                  trn_cell_extension_field_getlen_field(field));
     265           5 :   if (ret < 0) {
     266           0 :     goto end;
     267             :   }
     268             : 
     269          15 :   for (size_t i = 0; i < trn_cell_extension_dos_get_n_params(dos); i++) {
     270          10 :     const trn_cell_extension_dos_param_t *param =
     271          10 :       trn_cell_extension_dos_getconst_params(dos, i);
     272          10 :     if (BUG(param == NULL)) {
     273           0 :       goto end;
     274             :     }
     275             : 
     276          10 :     switch (trn_cell_extension_dos_param_get_type(param)) {
     277           5 :     case TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC:
     278           5 :       intro2_rate_per_sec = trn_cell_extension_dos_param_get_value(param);
     279           5 :       break;
     280           5 :     case TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC:
     281           5 :       intro2_burst_per_sec = trn_cell_extension_dos_param_get_value(param);
     282           5 :       break;
     283           0 :     default:
     284           0 :       goto end;
     285             :     }
     286             :   }
     287             : 
     288             :   /* At this point, the extension is valid so any values out of it implies
     289             :    * that it was set explicitly and thus flag the circuit that it should not
     290             :    * look at the consensus for that reason for the defenses' values. */
     291           5 :   circ->introduce2_dos_defense_explicit = 1;
     292             : 
     293             :   /* A value of 0 is valid in the sense that we accept it but we still disable
     294             :    * the defenses so return false. */
     295           5 :   if (intro2_rate_per_sec == 0 || intro2_burst_per_sec == 0) {
     296           2 :     log_info(LD_REND, "Intro point DoS defenses parameter set to 0. "
     297             :                       "Disabling INTRO2 DoS defenses on circuit id %u",
     298             :              circ->p_circ_id);
     299           2 :     circ->introduce2_dos_defense_enabled = 0;
     300           2 :     goto end;
     301             :   }
     302             : 
     303             :   /* If invalid, we disable the defense on the circuit. */
     304           3 :   if (!cell_dos_extension_parameters_are_valid(intro2_rate_per_sec,
     305             :                                                intro2_burst_per_sec)) {
     306           2 :     circ->introduce2_dos_defense_enabled = 0;
     307           2 :     log_info(LD_REND, "Disabling INTRO2 DoS defenses on circuit id %u",
     308             :              circ->p_circ_id);
     309           2 :     goto end;
     310             :   }
     311             : 
     312             :   /* We passed validation, enable defenses and apply rate/burst. */
     313           1 :   circ->introduce2_dos_defense_enabled = 1;
     314             : 
     315             :   /* Initialize the INTRODUCE2 token bucket for the rate limiting. */
     316           1 :   token_bucket_ctr_init(&circ->introduce2_bucket,
     317             :                         (uint32_t) intro2_rate_per_sec,
     318             :                         (uint32_t) intro2_burst_per_sec,
     319           1 :                         (uint32_t) approx_time());
     320           1 :   log_info(LD_REND, "Intro point DoS defenses enabled. Rate is %" PRIu64
     321             :                     " and Burst is %" PRIu64,
     322             :            intro2_rate_per_sec, intro2_burst_per_sec);
     323             : 
     324           5 :  end:
     325           5 :   trn_cell_extension_dos_free(dos);
     326           5 :   return;
     327             : }
     328             : 
     329             : /** Parse every cell extension in the given ESTABLISH_INTRO cell. */
     330             : static void
     331           6 : handle_establish_intro_cell_extensions(
     332             :                             const trn_cell_establish_intro_t *parsed_cell,
     333             :                             or_circuit_t *circ)
     334             : {
     335           6 :   const trn_cell_extension_t *extensions;
     336             : 
     337           6 :   tor_assert(parsed_cell);
     338           6 :   tor_assert(circ);
     339             : 
     340           6 :   extensions = trn_cell_establish_intro_getconst_extensions(parsed_cell);
     341           6 :   if (extensions == NULL) {
     342           0 :     goto end;
     343             :   }
     344             : 
     345             :   /* Go over all extensions. */
     346          11 :   for (size_t idx = 0; idx < trn_cell_extension_get_num(extensions); idx++) {
     347           5 :     const trn_cell_extension_field_t *field =
     348           5 :       trn_cell_extension_getconst_fields(extensions, idx);
     349           5 :     if (BUG(field == NULL)) {
     350             :       /* The number of extensions should match the number of fields. */
     351             :       break;
     352             :     }
     353             : 
     354           5 :     switch (trn_cell_extension_field_get_field_type(field)) {
     355           5 :     case TRUNNEL_CELL_EXTENSION_TYPE_DOS:
     356             :       /* After this, the circuit should be set for DoS defenses. */
     357           5 :       handle_establish_intro_cell_dos_extension(field, circ);
     358           5 :       break;
     359             :     default:
     360             :       /* Unknown extension. Skip over. */
     361             :       break;
     362             :     }
     363             :   }
     364             : 
     365           6 :  end:
     366           6 :   return;
     367             : }
     368             : 
     369             : /** We received an ESTABLISH_INTRO <b>parsed_cell</b> on <b>circ</b>. It's
     370             :  *  well-formed and passed our verifications. Perform appropriate actions to
     371             :  *  establish an intro point. */
     372             : static int
     373           6 : handle_verified_establish_intro_cell(or_circuit_t *circ,
     374             :                                const trn_cell_establish_intro_t *parsed_cell)
     375             : {
     376             :   /* Get the auth key of this intro point */
     377           6 :   ed25519_public_key_t auth_key;
     378           6 :   get_auth_key_from_cell(&auth_key, RELAY_COMMAND_ESTABLISH_INTRO,
     379             :                          parsed_cell);
     380             : 
     381             :   /* Setup INTRODUCE2 defenses on the circuit. Must be done before parsing the
     382             :    * cell extension that can possibly change the defenses' values. */
     383           6 :   hs_dos_setup_default_intro2_defenses(circ);
     384             : 
     385             :   /* Handle cell extension if any. */
     386           6 :   handle_establish_intro_cell_extensions(parsed_cell, circ);
     387             : 
     388             :   /* Then notify the hidden service that the intro point is established by
     389             :      sending an INTRO_ESTABLISHED cell */
     390           6 :   if (hs_intro_send_intro_established_cell(circ)) {
     391           0 :     log_warn(LD_PROTOCOL, "Couldn't send INTRO_ESTABLISHED cell.");
     392           0 :     return -1;
     393             :   }
     394             : 
     395             :   /* Associate intro point auth key with this circuit. */
     396           6 :   hs_circuitmap_register_intro_circ_v3_relay_side(circ, &auth_key);
     397             :   /* Repurpose this circuit into an intro circuit. */
     398           6 :   circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT);
     399             : 
     400           6 :   return 0;
     401             : }
     402             : 
     403             : /** We just received an ESTABLISH_INTRO cell in <b>circ</b> with payload in
     404             :  *  <b>request</b>. Handle it by making <b>circ</b> an intro circuit. Return 0
     405             :  *  if everything went well, or -1 if there were errors. */
     406             : static int
     407          11 : handle_establish_intro(or_circuit_t *circ, const uint8_t *request,
     408             :                        size_t request_len)
     409             : {
     410          11 :   int cell_ok, retval = -1;
     411          11 :   trn_cell_establish_intro_t *parsed_cell = NULL;
     412             : 
     413          11 :   tor_assert(circ);
     414          11 :   tor_assert(request);
     415             : 
     416          11 :   log_info(LD_REND, "Received an ESTABLISH_INTRO request on circuit %" PRIu32,
     417             :            circ->p_circ_id);
     418             : 
     419             :   /* Check that the circuit is in shape to become an intro point */
     420          11 :   if (!hs_intro_circuit_is_suitable_for_establish_intro(circ)) {
     421           1 :     goto err;
     422             :   }
     423             : 
     424             :   /* Parse the cell */
     425          10 :   ssize_t parsing_result = trn_cell_establish_intro_parse(&parsed_cell,
     426             :                                                          request, request_len);
     427          10 :   if (parsing_result < 0) {
     428           0 :     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
     429             :            "Rejecting %s ESTABLISH_INTRO cell.",
     430             :            parsing_result == -1 ? "invalid" : "truncated");
     431           0 :     goto err;
     432             :   }
     433             : 
     434          20 :   cell_ok = verify_establish_intro_cell(parsed_cell,
     435          10 :                                         (uint8_t *) circ->rend_circ_nonce,
     436             :                                         sizeof(circ->rend_circ_nonce));
     437          10 :   if (cell_ok < 0) {
     438           4 :     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
     439             :            "Failed to verify ESTABLISH_INTRO cell.");
     440           4 :     goto err;
     441             :   }
     442             : 
     443             :   /* This cell is legit. Take the appropriate actions. */
     444           6 :   cell_ok = handle_verified_establish_intro_cell(circ, parsed_cell);
     445           6 :   if (cell_ok < 0) {
     446           0 :     goto err;
     447             :   }
     448             : 
     449             :   /* We are done! */
     450           6 :   retval = 0;
     451           6 :   goto done;
     452             : 
     453           5 :  err:
     454             :   /* When sending the intro establish ack, on error the circuit can be marked
     455             :    * as closed so avoid a double close. */
     456           5 :   if (!TO_CIRCUIT(circ)->marked_for_close) {
     457           5 :     circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
     458             :   }
     459             : 
     460           0 :  done:
     461          11 :   trn_cell_establish_intro_free(parsed_cell);
     462          11 :   return retval;
     463             : }
     464             : 
     465             : /** Return True if circuit is suitable for being an intro circuit. */
     466             : static int
     467          17 : circuit_is_suitable_intro_point(const or_circuit_t *circ,
     468             :                                 const char *log_cell_type_str)
     469             : {
     470          17 :   tor_assert(circ);
     471          17 :   tor_assert(log_cell_type_str);
     472             : 
     473             :   /* Basic circuit state sanity checks. */
     474          17 :   if (circ->base_.purpose != CIRCUIT_PURPOSE_OR) {
     475           3 :     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
     476             :            "Rejecting %s on non-OR circuit.", log_cell_type_str);
     477           3 :     return 0;
     478             :   }
     479             : 
     480          14 :   if (circ->base_.n_chan) {
     481           1 :     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
     482             :            "Rejecting %s on non-edge circuit.", log_cell_type_str);
     483           1 :     return 0;
     484             :   }
     485             : 
     486             :   /* Suitable. */
     487             :   return 1;
     488             : }
     489             : 
     490             : /** Return True if circuit is suitable for being service-side intro circuit. */
     491             : int
     492          11 : hs_intro_circuit_is_suitable_for_establish_intro(const or_circuit_t *circ)
     493             : {
     494          11 :   return circuit_is_suitable_intro_point(circ, "ESTABLISH_INTRO");
     495             : }
     496             : 
     497             : /** We just received an ESTABLISH_INTRO cell in <b>circ</b>. Pass it to the
     498             :  * appropriate handler. */
     499             : int
     500          13 : hs_intro_received_establish_intro(or_circuit_t *circ, const uint8_t *request,
     501             :                             size_t request_len)
     502             : {
     503          13 :   tor_assert(circ);
     504          13 :   tor_assert(request);
     505             : 
     506          13 :   if (request_len == 0) {
     507           1 :     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Empty ESTABLISH_INTRO cell.");
     508           1 :     goto err;
     509             :   }
     510             : 
     511             :   /* Using the first byte of the cell, figure out the version of
     512             :    * ESTABLISH_INTRO and pass it to the appropriate cell handler */
     513          12 :   const uint8_t first_byte = request[0];
     514          12 :   switch (first_byte) {
     515           0 :     case TRUNNEL_HS_INTRO_AUTH_KEY_TYPE_LEGACY0:
     516             :     case TRUNNEL_HS_INTRO_AUTH_KEY_TYPE_LEGACY1:
     517             :       /* Likely version 2 onion service which is now obsolete. Avoid a
     518             :        * protocol warning considering they still exists on the network. */
     519           0 :       goto err;
     520          11 :     case TRUNNEL_HS_INTRO_AUTH_KEY_TYPE_ED25519:
     521          11 :       return handle_establish_intro(circ, request, request_len);
     522           1 :     default:
     523           1 :       log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
     524             :              "Unrecognized AUTH_KEY_TYPE %u.", first_byte);
     525           1 :       goto err;
     526             :   }
     527             : 
     528           2 :  err:
     529           2 :   circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
     530           2 :   return -1;
     531             : }
     532             : 
     533             : /** Send an INTRODUCE_ACK cell onto the circuit <b>circ</b> with the status
     534             :  * value in <b>status</b>. Depending on the status, it can be ACK or a NACK.
     535             :  * Return 0 on success else a negative value on error which will close the
     536             :  * circuit. */
     537             : static int
     538           2 : send_introduce_ack_cell(or_circuit_t *circ, uint16_t status)
     539             : {
     540           2 :   int ret = -1;
     541           2 :   uint8_t *encoded_cell = NULL;
     542           2 :   ssize_t encoded_len, result_len;
     543           2 :   trn_cell_introduce_ack_t *cell;
     544           2 :   trn_cell_extension_t *ext;
     545             : 
     546           2 :   tor_assert(circ);
     547             : 
     548             :   /* Setup the INTRODUCE_ACK cell. We have no extensions so the N_EXTENSIONS
     549             :    * field is set to 0 by default with a new object. */
     550           2 :   cell = trn_cell_introduce_ack_new();
     551           2 :   ret = trn_cell_introduce_ack_set_status(cell, status);
     552             :   /* We have no cell extensions in an INTRODUCE_ACK cell. */
     553           2 :   ext = trn_cell_extension_new();
     554           2 :   trn_cell_extension_set_num(ext, 0);
     555           2 :   trn_cell_introduce_ack_set_extensions(cell, ext);
     556             :   /* A wrong status is a very bad code flow error as this value is controlled
     557             :    * by the code in this file and not an external input. This means we use a
     558             :    * code that is not known by the trunnel ABI. */
     559           2 :   tor_assert(ret == 0);
     560             :   /* Encode the payload. We should never fail to get the encoded length. */
     561           2 :   encoded_len = trn_cell_introduce_ack_encoded_len(cell);
     562           2 :   tor_assert(encoded_len > 0);
     563           2 :   encoded_cell = tor_malloc_zero(encoded_len);
     564           2 :   result_len = trn_cell_introduce_ack_encode(encoded_cell, encoded_len, cell);
     565           2 :   tor_assert(encoded_len == result_len);
     566             : 
     567           2 :   ret = relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(circ),
     568             :                                      RELAY_COMMAND_INTRODUCE_ACK,
     569             :                                      (char *) encoded_cell, encoded_len,
     570             :                                      NULL);
     571             :   /* On failure, the above function will close the circuit. */
     572           2 :   trn_cell_introduce_ack_free(cell);
     573           2 :   tor_free(encoded_cell);
     574           2 :   return ret;
     575             : }
     576             : 
     577             : /** Validate a parsed INTRODUCE1 <b>cell</b>. Return 0 if valid or else a
     578             :  * negative value for an invalid cell that should be NACKed. */
     579             : STATIC int
     580          10 : validate_introduce1_parsed_cell(const trn_cell_introduce1_t *cell)
     581             : {
     582          10 :   size_t legacy_key_id_len;
     583          10 :   const uint8_t *legacy_key_id;
     584             : 
     585          10 :   tor_assert(cell);
     586             : 
     587             :   /* This code path SHOULD NEVER be reached if the cell is a legacy type so
     588             :    * safety net here. The legacy ID must be zeroes in this case. */
     589          10 :   legacy_key_id_len = trn_cell_introduce1_getlen_legacy_key_id(cell);
     590          10 :   legacy_key_id = trn_cell_introduce1_getconstarray_legacy_key_id(cell);
     591          10 :   if (BUG(!fast_mem_is_zero((char *) legacy_key_id, legacy_key_id_len))) {
     592           0 :     goto invalid;
     593             :   }
     594             : 
     595             :   /* The auth key of an INTRODUCE1 should be of type ed25519 thus leading to a
     596             :    * known fixed length as well. */
     597          10 :   if (trn_cell_introduce1_get_auth_key_type(cell) !=
     598             :       TRUNNEL_HS_INTRO_AUTH_KEY_TYPE_ED25519) {
     599           1 :     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
     600             :            "Rejecting invalid INTRODUCE1 cell auth key type. "
     601             :            "Responding with NACK.");
     602           1 :     goto invalid;
     603             :   }
     604          16 :   if (trn_cell_introduce1_get_auth_key_len(cell) != ED25519_PUBKEY_LEN ||
     605           7 :       trn_cell_introduce1_getlen_auth_key(cell) != ED25519_PUBKEY_LEN) {
     606           3 :     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
     607             :            "Rejecting invalid INTRODUCE1 cell auth key length. "
     608             :            "Responding with NACK.");
     609           3 :     goto invalid;
     610             :   }
     611           6 :   if (trn_cell_introduce1_getlen_encrypted(cell) == 0) {
     612           1 :     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
     613             :            "Rejecting invalid INTRODUCE1 cell encrypted length. "
     614             :            "Responding with NACK.");
     615           1 :     goto invalid;
     616             :   }
     617             : 
     618             :   return 0;
     619             :  invalid:
     620             :   return -1;
     621             : }
     622             : 
     623             : /** We just received a non legacy INTRODUCE1 cell on <b>client_circ</b> with
     624             :  * the payload in <b>request</b> of size <b>request_len</b>. Return 0 if
     625             :  * everything went well, or -1 if an error occurred. This function is in charge
     626             :  * of sending back an INTRODUCE_ACK cell and will close client_circ on error.
     627             :  */
     628             : STATIC int
     629           2 : handle_introduce1(or_circuit_t *client_circ, const uint8_t *request,
     630             :                   size_t request_len)
     631             : {
     632           2 :   int ret = -1;
     633           2 :   or_circuit_t *service_circ;
     634           2 :   trn_cell_introduce1_t *parsed_cell;
     635           2 :   uint16_t status = TRUNNEL_HS_INTRO_ACK_STATUS_SUCCESS;
     636             : 
     637           2 :   tor_assert(client_circ);
     638           2 :   tor_assert(request);
     639             : 
     640             :   /* Parse cell. Note that we can only parse the non encrypted section for
     641             :    * which we'll use the authentication key to find the service introduction
     642             :    * circuit and relay the cell on it. */
     643           2 :   ssize_t cell_size = trn_cell_introduce1_parse(&parsed_cell, request,
     644             :                                                request_len);
     645           2 :   if (cell_size < 0) {
     646           2 :     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
     647             :            "Rejecting %s INTRODUCE1 cell. Responding with NACK.",
     648             :            cell_size == -1 ? "invalid" : "truncated");
     649             :     /* Inform client that the INTRODUCE1 has a bad format. */
     650           1 :     status = TRUNNEL_HS_INTRO_ACK_STATUS_BAD_FORMAT;
     651           1 :     goto send_ack;
     652             :   }
     653             : 
     654             :   /* Once parsed validate the cell format. */
     655           1 :   if (validate_introduce1_parsed_cell(parsed_cell) < 0) {
     656             :     /* Inform client that the INTRODUCE1 has bad format. */
     657           0 :     status = TRUNNEL_HS_INTRO_ACK_STATUS_BAD_FORMAT;
     658           0 :     goto send_ack;
     659             :   }
     660             : 
     661             :   /* Find introduction circuit through our circuit map. */
     662             :   {
     663           1 :     ed25519_public_key_t auth_key;
     664           1 :     get_auth_key_from_cell(&auth_key, RELAY_COMMAND_INTRODUCE1, parsed_cell);
     665           1 :     service_circ = hs_circuitmap_get_intro_circ_v3_relay_side(&auth_key);
     666           1 :     if (service_circ == NULL) {
     667           0 :       char b64_key[ED25519_BASE64_LEN + 1];
     668           0 :       ed25519_public_to_base64(b64_key, &auth_key);
     669           0 :       log_info(LD_REND, "No intro circuit found for INTRODUCE1 cell "
     670             :                         "with auth key %s from circuit %" PRIu32 ". "
     671             :                         "Responding with NACK.",
     672             :                safe_str(b64_key), client_circ->p_circ_id);
     673             :       /* Inform the client that we don't know the requested service ID. */
     674           0 :       status = TRUNNEL_HS_INTRO_ACK_STATUS_UNKNOWN_ID;
     675           0 :       goto send_ack;
     676             :     }
     677             :   }
     678             : 
     679             :   /* Before sending, lets make sure this cell can be sent on the service
     680             :    * circuit asking the DoS defenses. */
     681           1 :   if (!hs_dos_can_send_intro2(service_circ)) {
     682           0 :     char *msg;
     683           0 :     static ratelim_t rlimit = RATELIM_INIT(5 * 60);
     684           0 :     if ((msg = rate_limit_log(&rlimit, approx_time()))) {
     685           0 :       log_info(LD_PROTOCOL, "Can't relay INTRODUCE1 v3 cell due to DoS "
     686             :                             "limitations. Sending NACK to client.");
     687           0 :       tor_free(msg);
     688             :     }
     689           0 :     status = TRUNNEL_HS_INTRO_ACK_STATUS_UNKNOWN_ID;
     690           0 :     goto send_ack;
     691             :   }
     692             : 
     693             :   /* Relay the cell to the service on its intro circuit with an INTRODUCE2
     694             :    * cell which is the same exact payload. */
     695           1 :   if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(service_circ),
     696             :                                    RELAY_COMMAND_INTRODUCE2,
     697             :                                    (char *) request, request_len, NULL)) {
     698           0 :     log_warn(LD_PROTOCOL, "Unable to send INTRODUCE2 cell to the service.");
     699             :     /* Inform the client that we can't relay the cell. Use the unknown ID
     700             :      * status code since it means that we do not know the service. */
     701           0 :     status = TRUNNEL_HS_INTRO_ACK_STATUS_UNKNOWN_ID;
     702           0 :     goto send_ack;
     703             :   }
     704             : 
     705             :   /* Success! Send an INTRODUCE_ACK success status onto the client circuit. */
     706             :   status = TRUNNEL_HS_INTRO_ACK_STATUS_SUCCESS;
     707             :   ret = 0;
     708             : 
     709           2 :  send_ack:
     710             :   /* Send INTRODUCE_ACK or INTRODUCE_NACK to client */
     711           2 :   if (send_introduce_ack_cell(client_circ, status) < 0) {
     712           0 :     log_warn(LD_PROTOCOL, "Unable to send an INTRODUCE ACK status %d "
     713             :                           "to client.", status);
     714             :     /* Circuit has been closed on failure of transmission. */
     715           0 :     goto done;
     716             :   }
     717           2 :  done:
     718           2 :   trn_cell_introduce1_free(parsed_cell);
     719           2 :   return ret;
     720             : }
     721             : 
     722             : /** Return true iff the circuit <b>circ</b> is suitable for receiving an
     723             :  * INTRODUCE1 cell. */
     724             : STATIC int
     725           6 : circuit_is_suitable_for_introduce1(const or_circuit_t *circ)
     726             : {
     727           6 :   tor_assert(circ);
     728             : 
     729             :   /* Is this circuit an intro point circuit? */
     730           6 :   if (!circuit_is_suitable_intro_point(circ, "INTRODUCE1")) {
     731             :     return 0;
     732             :   }
     733             : 
     734           3 :   if (circ->already_received_introduce1) {
     735           1 :     log_fn(LOG_PROTOCOL_WARN, LD_REND,
     736             :            "Blocking multiple introductions on the same circuit. "
     737             :            "Someone might be trying to attack a hidden service through "
     738             :            "this relay.");
     739           1 :     return 0;
     740             :   }
     741             : 
     742             :   /* Disallow single hop client circuit. */
     743           2 :   if (circ->p_chan && channel_is_client(circ->p_chan)) {
     744           0 :     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
     745             :            "Single hop client was rejected while trying to introduce. "
     746             :            "Closing circuit.");
     747           0 :     return 0;
     748             :   }
     749             : 
     750             :   return 1;
     751             : }
     752             : 
     753             : /** We just received an INTRODUCE1 cell on <b>circ</b>. Figure out which type
     754             :  * it is and pass it to the appropriate handler. Return 0 on success else a
     755             :  * negative value and the circuit is closed. */
     756             : int
     757           2 : hs_intro_received_introduce1(or_circuit_t *circ, const uint8_t *request,
     758             :                              size_t request_len)
     759             : {
     760           2 :   tor_assert(circ);
     761           2 :   tor_assert(request);
     762             : 
     763             :   /* A cell that can't hold a DIGEST_LEN is invalid. */
     764           2 :   if (request_len < DIGEST_LEN) {
     765           1 :     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Invalid INTRODUCE1 cell length.");
     766           1 :     goto err;
     767             :   }
     768             : 
     769             :   /* Make sure we have a circuit that can have an INTRODUCE1 cell on it. */
     770           1 :   if (!circuit_is_suitable_for_introduce1(circ)) {
     771             :     /* We do not send a NACK because the circuit is not suitable for any kind
     772             :      * of response or transmission as it's a violation of the protocol. */
     773           0 :     goto err;
     774             :   }
     775             :   /* Mark the circuit that we got this cell. None are allowed after this as a
     776             :    * DoS mitigation since one circuit with one client can hammer a service. */
     777           1 :   circ->already_received_introduce1 = 1;
     778             : 
     779             :   /* Handle the cell. */
     780           1 :   return handle_introduce1(circ, request, request_len);
     781             : 
     782           1 :  err:
     783           1 :   circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
     784           1 :   return -1;
     785             : }
     786             : 
     787             : /** Clear memory allocated by the given intropoint object ip (but don't free
     788             :  * the object itself). */
     789             : void
     790          24 : hs_intropoint_clear(hs_intropoint_t *ip)
     791             : {
     792          24 :   if (ip == NULL) {
     793             :     return;
     794             :   }
     795          24 :   tor_cert_free(ip->auth_key_cert);
     796          51 :   SMARTLIST_FOREACH(ip->link_specifiers, link_specifier_t *, ls,
     797             :                     link_specifier_free(ls));
     798          24 :   smartlist_free(ip->link_specifiers);
     799          24 :   memset(ip, 0, sizeof(hs_intropoint_t));
     800             : }

Generated by: LCOV version 1.14