Tor  0.4.7.0-alpha-dev
control_hs.c
Go to the documentation of this file.
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"
14 #include "feature/control/control_hs.h"
16 #include "feature/hs/hs_client.h"
17 #include "lib/encoding/confline.h"
18 
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,
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 */
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
87  const control_cmd_args_t *args)
88 {
89  int retval = -1;
90  smartlist_t *flags = smartlist_new();
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",
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:
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 */
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
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:
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 *
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) {
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 */
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
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);
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 }
int base64_decode(char *dest, size_t destlen, const char *src, size_t srclen)
Definition: binascii.c:396
int base64_encode(char *dest, size_t destlen, const char *src, size_t srclen, int flags)
Definition: binascii.c:215
Header for confline.c.
Header file for control_cmd.c.
Definition for control_cmd_args_t.
int handle_control_onion_client_auth_view(control_connection_t *conn, const control_cmd_args_t *args)
Definition: control_hs.c:290
static int parse_private_key_from_control_port(const char *client_privkey_str, curve25519_secret_key_t *privkey, control_connection_t *conn)
Definition: control_hs.c:26
int handle_control_onion_client_auth_add(control_connection_t *conn, const control_cmd_args_t *args)
Definition: control_hs.c:86
static char * encode_client_auth_cred_for_control_port(hs_client_service_authorization_t *cred)
Definition: control_hs.c:240
const control_cmd_syntax_t onion_client_auth_add_syntax
Definition: control_hs.c:74
const control_cmd_syntax_t onion_client_auth_remove_syntax
Definition: control_hs.c:184
int handle_control_onion_client_auth_remove(control_connection_t *conn, const control_cmd_args_t *args)
Definition: control_hs.c:194
const control_cmd_syntax_t onion_client_auth_view_syntax
Definition: control_hs.c:280
void control_write_endreply(control_connection_t *conn, int code, const char *s)
void control_printf_midreply(control_connection_t *conn, int code, const char *fmt,...)
void send_control_done(control_connection_t *conn)
void control_printf_endreply(control_connection_t *conn, int code, const char *fmt,...)
Header file for control_proto.c.
const char * escaped(const char *s)
Definition: escape.c:126
digest256map_t * get_hs_client_auths_map(void)
Definition: hs_client.c:1846
hs_client_register_auth_status_t hs_client_register_auth_credentials(hs_client_service_authorization_t *creds)
Definition: hs_client.c:1643
static digest256map_t * client_auths
Definition: hs_client.c:50
hs_client_removal_auth_status_t hs_client_remove_auth_credentials(const char *hsaddress)
Definition: hs_client.c:1812
Header file containing client data for the HS subsystem.
#define CLIENT_AUTH_FLAG_IS_PERMANENT
Definition: hs_client.h:63
int hs_address_is_valid(const char *address)
Definition: hs_common.c:856
#define tor_free(p)
Definition: malloc.h:52
Master header file for Tor-specific functionality.
#define REND_CLIENTNAME_MAX_LEN
Definition: or.h:337
void smartlist_add_asprintf(struct smartlist_t *sl, const char *pattern,...)
Definition: smartlist.c:36
void smartlist_sort_strings(smartlist_t *sl)
Definition: smartlist.c:549
char * smartlist_join_strings(smartlist_t *sl, const char *join, int terminate, size_t *len_out)
Definition: smartlist.c:279
smartlist_t * smartlist_new(void)
void smartlist_add(smartlist_t *sl, void *element)
#define SMARTLIST_FOREACH_BEGIN(sl, type, var)
#define SMARTLIST_FOREACH(sl, type, var, cmd)
int smartlist_split_string(smartlist_t *sl, const char *str, const char *sep, int flags, int max)
struct smartlist_t * args
struct config_line_t * kwargs
unsigned int max_args
Definition: control_cmd.h:46
char onion_address[HS_SERVICE_ADDR_LEN_BASE32+1]
Definition: hs_client.h:72
curve25519_secret_key_t enc_seckey
Definition: hs_client.h:69
#define tor_assert_nonfatal_unreached()
Definition: util_bug.h:176
#define tor_assert(expr)
Definition: util_bug.h:102
int strcasecmpstart(const char *s1, const char *s2)
Definition: util_string.c:225
int fast_mem_is_zero(const char *mem, size_t len)
Definition: util_string.c:74