LCOV - code coverage report
Current view: top level - feature/control - control_hs.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 144 172 83.7 %
Date: 2021-11-24 03:28:48 Functions: 5 5 100.0 %

          Line data    Source code
       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           9 : parse_private_key_from_control_port(const char *client_privkey_str,
      27             :                                     curve25519_secret_key_t *privkey,
      28             :                                     control_connection_t *conn)
      29             : {
      30           9 :   int retval = -1;
      31           9 :   smartlist_t *key_args = smartlist_new();
      32             : 
      33           9 :   tor_assert(privkey);
      34             : 
      35           9 :   smartlist_split_string(key_args, client_privkey_str, ":",
      36             :                          SPLIT_IGNORE_BLANK, 0);
      37           9 :   if (smartlist_len(key_args) != 2) {
      38           0 :     control_printf_endreply(conn, 512, "Invalid key type/blob");
      39           0 :     goto err;
      40             :   }
      41             : 
      42           9 :   const char *key_type = smartlist_get(key_args, 0);
      43           9 :   const char *key_blob = smartlist_get(key_args, 1);
      44             : 
      45           9 :   if (strcasecmp(key_type, "x25519")) {
      46           1 :     control_printf_endreply(conn, 552,
      47             :                             "Unrecognized key type \"%s\"", key_type);
      48           1 :     goto err;
      49             :   }
      50             : 
      51           8 :   if (base64_decode((char*)privkey->secret_key, sizeof(privkey->secret_key),
      52             :                     key_blob,
      53             :                     strlen(key_blob)) != sizeof(privkey->secret_key)) {
      54           1 :     control_printf_endreply(conn, 512, "Failed to decode x25519 private key");
      55           1 :     goto err;
      56             :   }
      57             : 
      58           7 :   if (fast_mem_is_zero((const char*)privkey->secret_key,
      59             :                        sizeof(privkey->secret_key))) {
      60           1 :     control_printf_endreply(conn, 553,
      61             :                             "Invalid private key \"%s\"", key_blob);
      62           1 :     goto err;
      63             :   }
      64             : 
      65             :   retval = 0;
      66             : 
      67           9 :  err:
      68          27 :   SMARTLIST_FOREACH(key_args, char *, c, tor_free(c));
      69           9 :   smartlist_free(key_args);
      70           9 :   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          10 : handle_control_onion_client_auth_add(control_connection_t *conn,
      87             :                                      const control_cmd_args_t *args)
      88             : {
      89          10 :   int retval = -1;
      90          10 :   smartlist_t *flags = smartlist_new();
      91          10 :   hs_client_service_authorization_t *creds = NULL;
      92             : 
      93          10 :   tor_assert(args);
      94             : 
      95          10 :   int argc = smartlist_len(args->args);
      96             :   /* We need at least 'HSAddress' and 'PrivateKeyBlob' */
      97          10 :   if (argc < 2) {
      98           0 :     control_printf_endreply(conn, 512,
      99             :                             "Incomplete ONION_CLIENT_AUTH_ADD command");
     100           0 :     goto err;
     101             :   }
     102             : 
     103          10 :   creds = tor_malloc_zero(sizeof(hs_client_service_authorization_t));
     104             : 
     105          10 :   const char *hsaddress = smartlist_get(args->args, 0);
     106          10 :   if (!hs_address_is_valid(hsaddress)) {
     107           1 :     control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",hsaddress);
     108           1 :     goto err;
     109             :   }
     110           9 :   strlcpy(creds->onion_address, hsaddress, sizeof(creds->onion_address));
     111             : 
     112             :   /* Parse the client private key */
     113           9 :   const char *client_privkey = smartlist_get(args->args, 1);
     114           9 :   if (parse_private_key_from_control_port(client_privkey,
     115             :                                           &creds->enc_seckey, conn) < 0) {
     116           3 :     goto err;
     117             :   }
     118             : 
     119             :   /* Now let's parse the remaining arguments (variable size) */
     120          11 :   for (const config_line_t *line = args->kwargs; line; line = line->next) {
     121           5 :     if (!strcasecmpstart(line->key, "Flags")) {
     122           3 :       smartlist_split_string(flags, line->value, ",", SPLIT_IGNORE_BLANK, 0);
     123           3 :       if (smartlist_len(flags) < 1) {
     124           0 :         control_write_endreply(conn, 512, "Invalid 'Flags' argument");
     125           0 :         goto err;
     126             :       }
     127           6 :       SMARTLIST_FOREACH_BEGIN(flags, const char *, flag) {
     128           3 :         if (!strcasecmp(flag, "Permanent")) {
     129           3 :           creds->flags |= CLIENT_AUTH_FLAG_IS_PERMANENT;
     130             :         } else {
     131           0 :           control_printf_endreply(conn, 512, "Invalid 'Flags' argument: %s",
     132             :                                   escaped(flag));
     133           0 :           goto err;
     134             :         }
     135           3 :       } SMARTLIST_FOREACH_END(flag);
     136             :     }
     137           5 :     if (!strcasecmp(line->key, "ClientName")) {
     138           1 :       if (strlen(line->value) > REND_CLIENTNAME_MAX_LEN) {
     139           0 :         control_printf_endreply(conn, 512, "ClientName longer than %d chars",
     140             :                                 REND_CLIENTNAME_MAX_LEN);
     141             :       }
     142           1 :       creds->client_name = tor_strdup(line->value);
     143             :     }
     144             :   }
     145             : 
     146           6 :   hs_client_register_auth_status_t register_status;
     147             :   /* Register the credential (register func takes ownership of cred.) */
     148           6 :   register_status = hs_client_register_auth_credentials(creds);
     149           6 :   switch (register_status) {
     150           0 :   case REGISTER_FAIL_BAD_ADDRESS:
     151             :     /* It's a bug because the service addr has already been validated above */
     152           0 :     control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"", hsaddress);
     153           0 :     break;
     154           1 :   case REGISTER_FAIL_PERMANENT_STORAGE:
     155           1 :     control_printf_endreply(conn, 553, "Unable to store creds for \"%s\"",
     156             :                             hsaddress);
     157           1 :     break;
     158           1 :   case REGISTER_SUCCESS_ALREADY_EXISTS:
     159           1 :     control_printf_endreply(conn, 251,"Client for onion existed and replaced");
     160           1 :     break;
     161           0 :   case REGISTER_SUCCESS_AND_DECRYPTED:
     162           0 :     control_printf_endreply(conn, 252,"Registered client and decrypted desc");
     163           0 :     break;
     164           4 :   case REGISTER_SUCCESS:
     165           4 :     control_printf_endreply(conn, 250, "OK");
     166           4 :     break;
     167           0 :   default:
     168           0 :     tor_assert_nonfatal_unreached();
     169             :   }
     170             : 
     171           6 :   retval = 0;
     172           6 :   goto done;
     173             : 
     174           4 :  err:
     175           4 :   client_service_authorization_free(creds);
     176             : 
     177          10 :  done:
     178          13 :   SMARTLIST_FOREACH(flags, char *, s, tor_free(s));
     179          10 :   smartlist_free(flags);
     180          10 :   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           6 : handle_control_onion_client_auth_remove(control_connection_t *conn,
     195             :                                         const control_cmd_args_t *args)
     196             : {
     197           6 :   int retval = -1;
     198             : 
     199           6 :   tor_assert(args);
     200             : 
     201           6 :   int argc = smartlist_len(args->args);
     202           6 :   if (argc < 1) {
     203           0 :     control_printf_endreply(conn, 512,
     204             :                             "Incomplete ONION_CLIENT_AUTH_REMOVE command");
     205           0 :     goto err;
     206             :   }
     207             : 
     208           6 :   const char *hsaddress = smartlist_get(args->args, 0);
     209           6 :   if (!hs_address_is_valid(hsaddress)) {
     210           1 :     control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",hsaddress);
     211           1 :     goto err;
     212             :   }
     213             : 
     214           5 :   hs_client_removal_auth_status_t removal_status;
     215           5 :   removal_status = hs_client_remove_auth_credentials(hsaddress);
     216           5 :   switch (removal_status) {
     217           0 :   case REMOVAL_BAD_ADDRESS:
     218             :     /* It's a bug because the service addr has already been validated above */
     219           0 :     control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",hsaddress);
     220           0 :     break;
     221           1 :   case REMOVAL_SUCCESS_NOT_FOUND:
     222           1 :     control_printf_endreply(conn, 251, "No credentials for \"%s\"",hsaddress);
     223           1 :     break;
     224           4 :   case REMOVAL_SUCCESS:
     225           4 :     control_printf_endreply(conn, 250, "OK");
     226           4 :     break;
     227           0 :   default:
     228           0 :     tor_assert_nonfatal_unreached();
     229             :   }
     230             : 
     231             :   retval = 0;
     232             : 
     233           6 :  err:
     234           6 :   return retval;
     235             : }
     236             : 
     237             : /** Helper: Return a newly allocated string with the encoding of client
     238             :  *  authorization credentials */
     239             : static char *
     240           4 : encode_client_auth_cred_for_control_port(
     241             :                                        hs_client_service_authorization_t *cred)
     242             : {
     243           4 :   smartlist_t *control_line = smartlist_new();
     244           4 :   char x25519_b64[128];
     245           4 :   char *msg_str = NULL;
     246             : 
     247           4 :   tor_assert(cred);
     248             : 
     249           4 :   if (base64_encode(x25519_b64, sizeof(x25519_b64),
     250           4 :                     (char *)cred->enc_seckey.secret_key,
     251             :                     sizeof(cred->enc_seckey.secret_key), 0) < 0) {
     252           0 :     tor_assert_nonfatal_unreached();
     253           0 :     goto err;
     254             :   }
     255             : 
     256           4 :   smartlist_add_asprintf(control_line, "CLIENT %s x25519:%s",
     257           4 :                          cred->onion_address, x25519_b64);
     258             : 
     259           4 :   if (cred->flags) { /* flags are also optional */
     260           0 :     if (cred->flags & CLIENT_AUTH_FLAG_IS_PERMANENT) {
     261           0 :       smartlist_add_asprintf(control_line, " Flags=Permanent");
     262             :     }
     263             :   }
     264             : 
     265           4 :   if (cred->client_name) {
     266           1 :     smartlist_add_asprintf(control_line, " ClientName=%s", cred->client_name);
     267             :   }
     268             : 
     269             :   /* Join all the components into a single string */
     270           4 :   msg_str = smartlist_join_strings(control_line, "", 0, NULL);
     271             : 
     272           4 :  err:
     273           9 :   SMARTLIST_FOREACH(control_line, char *, cp, tor_free(cp));
     274           4 :   smartlist_free(control_line);
     275             : 
     276           4 :   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           4 : handle_control_onion_client_auth_view(control_connection_t *conn,
     291             :                                       const control_cmd_args_t *args)
     292             : {
     293           4 :   int retval = -1;
     294           4 :   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           4 :   smartlist_t *creds_str_list = smartlist_new();
     298             : 
     299           4 :   tor_assert(args);
     300             : 
     301           4 :   int argc = smartlist_len(args->args);
     302           4 :   if (argc >= 1) {
     303           2 :     hsaddress = smartlist_get(args->args, 0);
     304           2 :     if (!hs_address_is_valid(hsaddress)) {
     305           1 :       control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",
     306             :                               hsaddress);
     307           1 :       goto err;
     308             :     }
     309             :   }
     310             : 
     311           1 :   if (hsaddress) {
     312           1 :     control_printf_midreply(conn, 250, "ONION_CLIENT_AUTH_VIEW %s", hsaddress);
     313             :   } else {
     314           2 :     control_printf_midreply(conn, 250, "ONION_CLIENT_AUTH_VIEW");
     315             :   }
     316             : 
     317             :   /* Create an iterator out of the digest256map */
     318           3 :   digest256map_t *client_auths = get_hs_client_auths_map();
     319           3 :   digest256map_iter_t *itr = digest256map_iter_init(client_auths);
     320           9 :   while (!digest256map_iter_done(itr)) {
     321           6 :     const uint8_t *service_pubkey;
     322           6 :     void *valp;
     323           6 :     digest256map_iter_get(itr, &service_pubkey, &valp);
     324           6 :     tor_assert(valp);
     325           6 :     hs_client_service_authorization_t *cred = valp;
     326             : 
     327             :     /* If a specific HS address was requested, only print creds for that one */
     328           6 :     if (hsaddress && strcmp(cred->onion_address, hsaddress)) {
     329           2 :       itr = digest256map_iter_next(client_auths, itr);
     330           2 :       continue;
     331             :     }
     332             : 
     333           4 :     char *encoding_str = encode_client_auth_cred_for_control_port(cred);
     334           4 :     tor_assert_nonfatal(encoding_str);
     335           4 :     smartlist_add(creds_str_list, encoding_str);
     336             : 
     337           4 :     itr = digest256map_iter_next(client_auths, itr);
     338             :   }
     339             : 
     340             :   /* We got everything: Now sort the strings and print them */
     341           3 :   smartlist_sort_strings(creds_str_list);
     342           7 :   SMARTLIST_FOREACH_BEGIN(creds_str_list, char *, c) {
     343           4 :     control_printf_midreply(conn, 250, "%s", c);
     344           4 :   } SMARTLIST_FOREACH_END(c);
     345             : 
     346           3 :   send_control_done(conn);
     347             : 
     348           3 :   retval = 0;
     349             : 
     350           4 :  err:
     351           8 :   SMARTLIST_FOREACH(creds_str_list, char *, cp, tor_free(cp));
     352           4 :   smartlist_free(creds_str_list);
     353           4 :   return retval;
     354             : }

Generated by: LCOV version 1.14