tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

control_hs.c (11337B)


      1 /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
      2 * Copyright (c) 2019-2021, The Tor Project, Inc. */
      3 /* See LICENSE for licensing information */
      4 
      5 /**
      6 * \file control_hs.c
      7 *
      8 * \brief Implement commands for Tor's control-socket interface that are
      9 *        related to onion services.
     10 **/
     11 
     12 #include "core/or/or.h"
     13 #include "feature/control/control_cmd.h"
     14 #include "feature/control/control_hs.h"
     15 #include "feature/control/control_proto.h"
     16 #include "feature/hs/hs_client.h"
     17 #include "lib/encoding/confline.h"
     18 
     19 #include "feature/control/control_cmd_args_st.h"
     20 
     21 /** Parse the 'KeyType ":" PrivateKey' from <b>client_privkey_str</b> and store
     22 *  it into <b>privkey</b>. Use <b>conn</b> to output any errors if needed.
     23 *
     24 *  Return 0 if all went well, -1 otherwise. */
     25 static int
     26 parse_private_key_from_control_port(const char *client_privkey_str,
     27                                    curve25519_secret_key_t *privkey,
     28                                    control_connection_t *conn)
     29 {
     30  int retval = -1;
     31  smartlist_t *key_args = smartlist_new();
     32 
     33  tor_assert(privkey);
     34 
     35  smartlist_split_string(key_args, client_privkey_str, ":",
     36                         SPLIT_IGNORE_BLANK, 0);
     37  if (smartlist_len(key_args) != 2) {
     38    control_printf_endreply(conn, 512, "Invalid key type/blob");
     39    goto err;
     40  }
     41 
     42  const char *key_type = smartlist_get(key_args, 0);
     43  const char *key_blob = smartlist_get(key_args, 1);
     44 
     45  if (strcasecmp(key_type, "x25519")) {
     46    control_printf_endreply(conn, 552,
     47                            "Unrecognized key type \"%s\"", key_type);
     48    goto err;
     49  }
     50 
     51  if (base64_decode((char*)privkey->secret_key, sizeof(privkey->secret_key),
     52                    key_blob,
     53                    strlen(key_blob)) != sizeof(privkey->secret_key)) {
     54    control_printf_endreply(conn, 512, "Failed to decode x25519 private key");
     55    goto err;
     56  }
     57 
     58  if (fast_mem_is_zero((const char*)privkey->secret_key,
     59                       sizeof(privkey->secret_key))) {
     60    control_printf_endreply(conn, 553,
     61                            "Invalid private key \"%s\"", key_blob);
     62    goto err;
     63  }
     64 
     65  retval = 0;
     66 
     67 err:
     68  SMARTLIST_FOREACH(key_args, char *, c, tor_free(c));
     69  smartlist_free(key_args);
     70  return retval;
     71 }
     72 
     73 /** Syntax details for ONION_CLIENT_AUTH_ADD */
     74 const control_cmd_syntax_t onion_client_auth_add_syntax = {
     75  .max_args = 2,
     76  .accept_keywords = true,
     77 };
     78 
     79 /** Called when we get an ONION_CLIENT_AUTH_ADD command; parse the body, and
     80 *  register the new client-side client auth credentials:
     81 *  "ONION_CLIENT_AUTH_ADD" SP HSAddress
     82 *                          SP KeyType ":" PrivateKeyBlob
     83 *                          [SP "Type=" TYPE] CRLF
     84 */
     85 int
     86 handle_control_onion_client_auth_add(control_connection_t *conn,
     87                                     const control_cmd_args_t *args)
     88 {
     89  int retval = -1;
     90  smartlist_t *flags = smartlist_new();
     91  hs_client_service_authorization_t *creds = NULL;
     92 
     93  tor_assert(args);
     94 
     95  int argc = smartlist_len(args->args);
     96  /* We need at least 'HSAddress' and 'PrivateKeyBlob' */
     97  if (argc < 2) {
     98    control_printf_endreply(conn, 512,
     99                            "Incomplete ONION_CLIENT_AUTH_ADD command");
    100    goto err;
    101  }
    102 
    103  creds = tor_malloc_zero(sizeof(hs_client_service_authorization_t));
    104 
    105  const char *hsaddress = smartlist_get(args->args, 0);
    106  if (!hs_address_is_valid(hsaddress)) {
    107    control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",hsaddress);
    108    goto err;
    109  }
    110  strlcpy(creds->onion_address, hsaddress, sizeof(creds->onion_address));
    111 
    112  /* Parse the client private key */
    113  const char *client_privkey = smartlist_get(args->args, 1);
    114  if (parse_private_key_from_control_port(client_privkey,
    115                                          &creds->enc_seckey, conn) < 0) {
    116    goto err;
    117  }
    118 
    119  /* Now let's parse the remaining arguments (variable size) */
    120  for (const config_line_t *line = args->kwargs; line; line = line->next) {
    121    if (!strcasecmpstart(line->key, "Flags")) {
    122      smartlist_split_string(flags, line->value, ",", SPLIT_IGNORE_BLANK, 0);
    123      if (smartlist_len(flags) < 1) {
    124        control_write_endreply(conn, 512, "Invalid 'Flags' argument");
    125        goto err;
    126      }
    127      SMARTLIST_FOREACH_BEGIN(flags, const char *, flag) {
    128        if (!strcasecmp(flag, "Permanent")) {
    129          creds->flags |= CLIENT_AUTH_FLAG_IS_PERMANENT;
    130        } else {
    131          control_printf_endreply(conn, 512, "Invalid 'Flags' argument: %s",
    132                                  escaped(flag));
    133          goto err;
    134        }
    135      } SMARTLIST_FOREACH_END(flag);
    136    }
    137    if (!strcasecmp(line->key, "ClientName")) {
    138      if (strlen(line->value) > REND_CLIENTNAME_MAX_LEN) {
    139        control_printf_endreply(conn, 512, "ClientName longer than %d chars",
    140                                REND_CLIENTNAME_MAX_LEN);
    141      }
    142      creds->client_name = tor_strdup(line->value);
    143    }
    144  }
    145 
    146  hs_client_register_auth_status_t register_status;
    147  /* Register the credential (register func takes ownership of cred.) */
    148  register_status = hs_client_register_auth_credentials(creds);
    149  switch (register_status) {
    150  case REGISTER_FAIL_BAD_ADDRESS:
    151    /* It's a bug because the service addr has already been validated above */
    152    control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"", hsaddress);
    153    break;
    154  case REGISTER_FAIL_PERMANENT_STORAGE:
    155    control_printf_endreply(conn, 553, "Unable to store creds for \"%s\"",
    156                            hsaddress);
    157    break;
    158  case REGISTER_SUCCESS_ALREADY_EXISTS:
    159    control_printf_endreply(conn, 251,"Client for onion existed and replaced");
    160    break;
    161  case REGISTER_SUCCESS_AND_DECRYPTED:
    162    control_printf_endreply(conn, 252,"Registered client and decrypted desc");
    163    break;
    164  case REGISTER_SUCCESS:
    165    control_printf_endreply(conn, 250, "OK");
    166    break;
    167  default:
    168    tor_assert_nonfatal_unreached();
    169  }
    170 
    171  retval = 0;
    172  goto done;
    173 
    174 err:
    175  client_service_authorization_free(creds);
    176 
    177 done:
    178  SMARTLIST_FOREACH(flags, char *, s, tor_free(s));
    179  smartlist_free(flags);
    180  return retval;
    181 }
    182 
    183 /** Syntax details for ONION_CLIENT_AUTH_REMOVE */
    184 const control_cmd_syntax_t onion_client_auth_remove_syntax = {
    185  .max_args = 1,
    186  .accept_keywords = true,
    187 };
    188 
    189 /** Called when we get an ONION_CLIENT_AUTH_REMOVE command; parse the body, and
    190 *  register the new client-side client auth credentials.
    191 *    "ONION_CLIENT_AUTH_REMOVE" SP HSAddress
    192 */
    193 int
    194 handle_control_onion_client_auth_remove(control_connection_t *conn,
    195                                        const control_cmd_args_t *args)
    196 {
    197  int retval = -1;
    198 
    199  tor_assert(args);
    200 
    201  int argc = smartlist_len(args->args);
    202  if (argc < 1) {
    203    control_printf_endreply(conn, 512,
    204                            "Incomplete ONION_CLIENT_AUTH_REMOVE command");
    205    goto err;
    206  }
    207 
    208  const char *hsaddress = smartlist_get(args->args, 0);
    209  if (!hs_address_is_valid(hsaddress)) {
    210    control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",hsaddress);
    211    goto err;
    212  }
    213 
    214  hs_client_removal_auth_status_t removal_status;
    215  removal_status = hs_client_remove_auth_credentials(hsaddress);
    216  switch (removal_status) {
    217  case REMOVAL_BAD_ADDRESS:
    218    /* It's a bug because the service addr has already been validated above */
    219    control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",hsaddress);
    220    break;
    221  case REMOVAL_SUCCESS_NOT_FOUND:
    222    control_printf_endreply(conn, 251, "No credentials for \"%s\"",hsaddress);
    223    break;
    224  case REMOVAL_SUCCESS:
    225    control_printf_endreply(conn, 250, "OK");
    226    break;
    227  default:
    228    tor_assert_nonfatal_unreached();
    229  }
    230 
    231  retval = 0;
    232 
    233 err:
    234  return retval;
    235 }
    236 
    237 /** Helper: Return a newly allocated string with the encoding of client
    238 *  authorization credentials */
    239 static char *
    240 encode_client_auth_cred_for_control_port(
    241                                       hs_client_service_authorization_t *cred)
    242 {
    243  smartlist_t *control_line = smartlist_new();
    244  char x25519_b64[128];
    245  char *msg_str = NULL;
    246 
    247  tor_assert(cred);
    248 
    249  if (base64_encode(x25519_b64, sizeof(x25519_b64),
    250                    (char *)cred->enc_seckey.secret_key,
    251                    sizeof(cred->enc_seckey.secret_key), 0) < 0) {
    252    tor_assert_nonfatal_unreached();
    253    goto err;
    254  }
    255 
    256  smartlist_add_asprintf(control_line, "CLIENT %s x25519:%s",
    257                         cred->onion_address, x25519_b64);
    258 
    259  if (cred->flags) { /* flags are also optional */
    260    if (cred->flags & CLIENT_AUTH_FLAG_IS_PERMANENT) {
    261      smartlist_add_asprintf(control_line, " Flags=Permanent");
    262    }
    263  }
    264 
    265  if (cred->client_name) {
    266    smartlist_add_asprintf(control_line, " ClientName=%s", cred->client_name);
    267  }
    268 
    269  /* Join all the components into a single string */
    270  msg_str = smartlist_join_strings(control_line, "", 0, NULL);
    271 
    272 err:
    273  SMARTLIST_FOREACH(control_line, char *, cp, tor_free(cp));
    274  smartlist_free(control_line);
    275 
    276  return msg_str;
    277 }
    278 
    279 /** Syntax details for ONION_CLIENT_AUTH_VIEW */
    280 const control_cmd_syntax_t onion_client_auth_view_syntax = {
    281  .max_args = 1,
    282  .accept_keywords = true,
    283 };
    284 
    285 /** Called when we get an ONION_CLIENT_AUTH_VIEW command; parse the body, and
    286 *  register the new client-side client auth credentials.
    287 *        "ONION_CLIENT_AUTH_VIEW" [SP HSAddress] CRLF
    288 */
    289 int
    290 handle_control_onion_client_auth_view(control_connection_t *conn,
    291                                      const control_cmd_args_t *args)
    292 {
    293  int retval = -1;
    294  const char *hsaddress = NULL;
    295  /* We are gonna put all the credential strings into a smartlist, and sort it
    296     before printing, so that we can get a guaranteed order of printing. */
    297  smartlist_t *creds_str_list = smartlist_new();
    298 
    299  tor_assert(args);
    300 
    301  int argc = smartlist_len(args->args);
    302  if (argc >= 1) {
    303    hsaddress = smartlist_get(args->args, 0);
    304    if (!hs_address_is_valid(hsaddress)) {
    305      control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",
    306                              hsaddress);
    307      goto err;
    308    }
    309  }
    310 
    311  if (hsaddress) {
    312    control_printf_midreply(conn, 250, "ONION_CLIENT_AUTH_VIEW %s", hsaddress);
    313  } else {
    314    control_printf_midreply(conn, 250, "ONION_CLIENT_AUTH_VIEW");
    315  }
    316 
    317  /* Create an iterator out of the digest256map */
    318  digest256map_t *client_auths = get_hs_client_auths_map();
    319  digest256map_iter_t *itr = digest256map_iter_init(client_auths);
    320  while (!digest256map_iter_done(itr)) {
    321    const uint8_t *service_pubkey;
    322    void *valp;
    323    digest256map_iter_get(itr, &service_pubkey, &valp);
    324    tor_assert(valp);
    325    hs_client_service_authorization_t *cred = valp;
    326 
    327    /* If a specific HS address was requested, only print creds for that one */
    328    if (hsaddress && strcmp(cred->onion_address, hsaddress)) {
    329      itr = digest256map_iter_next(client_auths, itr);
    330      continue;
    331    }
    332 
    333    char *encoding_str = encode_client_auth_cred_for_control_port(cred);
    334    tor_assert_nonfatal(encoding_str);
    335    smartlist_add(creds_str_list, encoding_str);
    336 
    337    itr = digest256map_iter_next(client_auths, itr);
    338  }
    339 
    340  /* We got everything: Now sort the strings and print them */
    341  smartlist_sort_strings(creds_str_list);
    342  SMARTLIST_FOREACH_BEGIN(creds_str_list, char *, c) {
    343    control_printf_midreply(conn, 250, "%s", c);
    344  } SMARTLIST_FOREACH_END(c);
    345 
    346  send_control_done(conn);
    347 
    348  retval = 0;
    349 
    350 err:
    351  SMARTLIST_FOREACH(creds_str_list, char *, cp, tor_free(cp));
    352  smartlist_free(creds_str_list);
    353  return retval;
    354 }