Merge branch 'tor-github/pr/415' into maint-0.3.5
[tor.git] / src / feature / rend / rendparse.c
blobe2378e340f36924a2a444e4ba4b3e941084ef2a9
1 /* Copyright (c) 2001 Matej Pfajfar.
2 * Copyright (c) 2001-2004, Roger Dingledine.
3 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
4 * Copyright (c) 2007-2018, The Tor Project, Inc. */
5 /* See LICENSE for licensing information */
7 /**
8 * \file rendparse.c
9 * \brief Code to parse and validate v2 hidden service descriptors.
10 **/
12 #include "core/or/or.h"
13 #include "feature/dirparse/parsecommon.h"
14 #include "feature/dirparse/sigcommon.h"
15 #include "feature/rend/rendcommon.h"
16 #include "feature/rend/rendparse.h"
17 #include "lib/memarea/memarea.h"
19 #include "core/or/extend_info_st.h"
20 #include "feature/rend/rend_authorized_client_st.h"
21 #include "feature/rend/rend_intro_point_st.h"
22 #include "feature/rend/rend_service_descriptor_st.h"
24 /** List of tokens recognized in rendezvous service descriptors */
25 static token_rule_t desc_token_table[] = {
26 T1_START("rendezvous-service-descriptor", R_RENDEZVOUS_SERVICE_DESCRIPTOR,
27 EQ(1), NO_OBJ),
28 T1("version", R_VERSION, EQ(1), NO_OBJ),
29 T1("permanent-key", R_PERMANENT_KEY, NO_ARGS, NEED_KEY_1024),
30 T1("secret-id-part", R_SECRET_ID_PART, EQ(1), NO_OBJ),
31 T1("publication-time", R_PUBLICATION_TIME, CONCAT_ARGS, NO_OBJ),
32 T1("protocol-versions", R_PROTOCOL_VERSIONS, EQ(1), NO_OBJ),
33 T01("introduction-points", R_INTRODUCTION_POINTS, NO_ARGS, NEED_OBJ),
34 T1_END("signature", R_SIGNATURE, NO_ARGS, NEED_OBJ),
35 END_OF_TABLE
38 /** List of tokens recognized in the (encrypted) list of introduction points of
39 * rendezvous service descriptors */
40 static token_rule_t ipo_token_table[] = {
41 T1_START("introduction-point", R_IPO_IDENTIFIER, EQ(1), NO_OBJ),
42 T1("ip-address", R_IPO_IP_ADDRESS, EQ(1), NO_OBJ),
43 T1("onion-port", R_IPO_ONION_PORT, EQ(1), NO_OBJ),
44 T1("onion-key", R_IPO_ONION_KEY, NO_ARGS, NEED_KEY_1024),
45 T1("service-key", R_IPO_SERVICE_KEY, NO_ARGS, NEED_KEY_1024),
46 END_OF_TABLE
49 /** List of tokens recognized in the (possibly encrypted) list of introduction
50 * points of rendezvous service descriptors */
51 static token_rule_t client_keys_token_table[] = {
52 T1_START("client-name", C_CLIENT_NAME, CONCAT_ARGS, NO_OBJ),
53 T1("descriptor-cookie", C_DESCRIPTOR_COOKIE, EQ(1), NO_OBJ),
54 T01("client-key", C_CLIENT_KEY, NO_ARGS, NEED_SKEY_1024),
55 END_OF_TABLE
58 /** Parse and validate the ASCII-encoded v2 descriptor in <b>desc</b>,
59 * write the parsed descriptor to the newly allocated *<b>parsed_out</b>, the
60 * binary descriptor ID of length DIGEST_LEN to <b>desc_id_out</b>, the
61 * encrypted introduction points to the newly allocated
62 * *<b>intro_points_encrypted_out</b>, their encrypted size to
63 * *<b>intro_points_encrypted_size_out</b>, the size of the encoded descriptor
64 * to *<b>encoded_size_out</b>, and a pointer to the possibly next
65 * descriptor to *<b>next_out</b>; return 0 for success (including validation)
66 * and -1 for failure.
68 * If <b>as_hsdir</b> is 1, we're parsing this as an HSDir, and we should
69 * be strict about time formats.
71 int
72 rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
73 char *desc_id_out,
74 char **intro_points_encrypted_out,
75 size_t *intro_points_encrypted_size_out,
76 size_t *encoded_size_out,
77 const char **next_out, const char *desc,
78 int as_hsdir)
80 rend_service_descriptor_t *result =
81 tor_malloc_zero(sizeof(rend_service_descriptor_t));
82 char desc_hash[DIGEST_LEN];
83 const char *eos;
84 smartlist_t *tokens = smartlist_new();
85 directory_token_t *tok;
86 char secret_id_part[DIGEST_LEN];
87 int i, version, num_ok=1;
88 smartlist_t *versions;
89 char public_key_hash[DIGEST_LEN];
90 char test_desc_id[DIGEST_LEN];
91 memarea_t *area = NULL;
92 const int strict_time_fmt = as_hsdir;
94 tor_assert(desc);
95 /* Check if desc starts correctly. */
96 if (strcmpstart(desc, "rendezvous-service-descriptor ")) {
97 log_info(LD_REND, "Descriptor does not start correctly.");
98 goto err;
100 /* Compute descriptor hash for later validation. */
101 if (router_get_hash_impl(desc, strlen(desc), desc_hash,
102 "rendezvous-service-descriptor ",
103 "\nsignature", '\n', DIGEST_SHA1) < 0) {
104 log_warn(LD_REND, "Couldn't compute descriptor hash.");
105 goto err;
107 /* Determine end of string. */
108 eos = strstr(desc, "\nrendezvous-service-descriptor ");
109 if (!eos)
110 eos = desc + strlen(desc);
111 else
112 eos = eos + 1;
113 /* Check length. */
114 if (eos-desc > REND_DESC_MAX_SIZE) {
115 /* XXXX+ If we are parsing this descriptor as a server, this
116 * should be a protocol warning. */
117 log_warn(LD_REND, "Descriptor length is %d which exceeds "
118 "maximum rendezvous descriptor size of %d bytes.",
119 (int)(eos-desc), REND_DESC_MAX_SIZE);
120 goto err;
122 /* Tokenize descriptor. */
123 area = memarea_new();
124 if (tokenize_string(area, desc, eos, tokens, desc_token_table, 0)) {
125 log_warn(LD_REND, "Error tokenizing descriptor.");
126 goto err;
128 /* Set next to next descriptor, if available. */
129 *next_out = eos;
130 /* Set length of encoded descriptor. */
131 *encoded_size_out = eos - desc;
132 /* Check min allowed length of token list. */
133 if (smartlist_len(tokens) < 7) {
134 log_warn(LD_REND, "Impossibly short descriptor.");
135 goto err;
137 /* Parse base32-encoded descriptor ID. */
138 tok = find_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR);
139 tor_assert(tok == smartlist_get(tokens, 0));
140 tor_assert(tok->n_args == 1);
141 if (!rend_valid_descriptor_id(tok->args[0])) {
142 log_warn(LD_REND, "Invalid descriptor ID: '%s'", tok->args[0]);
143 goto err;
145 if (base32_decode(desc_id_out, DIGEST_LEN,
146 tok->args[0], REND_DESC_ID_V2_LEN_BASE32) < 0) {
147 log_warn(LD_REND, "Descriptor ID contains illegal characters: %s",
148 tok->args[0]);
149 goto err;
151 /* Parse descriptor version. */
152 tok = find_by_keyword(tokens, R_VERSION);
153 tor_assert(tok->n_args == 1);
154 result->version =
155 (int) tor_parse_long(tok->args[0], 10, 0, INT_MAX, &num_ok, NULL);
156 if (result->version != 2 || !num_ok) {
157 /* If it's <2, it shouldn't be under this format. If the number
158 * is greater than 2, we bumped it because we broke backward
159 * compatibility. See how version numbers in our other formats
160 * work. */
161 log_warn(LD_REND, "Unrecognized descriptor version: %s",
162 escaped(tok->args[0]));
163 goto err;
165 /* Parse public key. */
166 tok = find_by_keyword(tokens, R_PERMANENT_KEY);
167 result->pk = tok->key;
168 tok->key = NULL; /* Prevent free */
169 /* Parse secret ID part. */
170 tok = find_by_keyword(tokens, R_SECRET_ID_PART);
171 tor_assert(tok->n_args == 1);
172 if (strlen(tok->args[0]) != REND_SECRET_ID_PART_LEN_BASE32 ||
173 strspn(tok->args[0], BASE32_CHARS) != REND_SECRET_ID_PART_LEN_BASE32) {
174 log_warn(LD_REND, "Invalid secret ID part: '%s'", tok->args[0]);
175 goto err;
177 if (base32_decode(secret_id_part, DIGEST_LEN, tok->args[0], 32) < 0) {
178 log_warn(LD_REND, "Secret ID part contains illegal characters: %s",
179 tok->args[0]);
180 goto err;
182 /* Parse publication time -- up-to-date check is done when storing the
183 * descriptor. */
184 tok = find_by_keyword(tokens, R_PUBLICATION_TIME);
185 tor_assert(tok->n_args == 1);
186 if (parse_iso_time_(tok->args[0], &result->timestamp,
187 strict_time_fmt, 0) < 0) {
188 log_warn(LD_REND, "Invalid publication time: '%s'", tok->args[0]);
189 goto err;
191 /* Parse protocol versions. */
192 tok = find_by_keyword(tokens, R_PROTOCOL_VERSIONS);
193 tor_assert(tok->n_args == 1);
194 versions = smartlist_new();
195 smartlist_split_string(versions, tok->args[0], ",",
196 SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
197 for (i = 0; i < smartlist_len(versions); i++) {
198 version = (int) tor_parse_long(smartlist_get(versions, i),
199 10, 0, INT_MAX, &num_ok, NULL);
200 if (!num_ok) /* It's a string; let's ignore it. */
201 continue;
202 if (version >= REND_PROTOCOL_VERSION_BITMASK_WIDTH)
203 /* Avoid undefined left-shift behaviour. */
204 continue;
205 result->protocols |= 1 << version;
207 SMARTLIST_FOREACH(versions, char *, cp, tor_free(cp));
208 smartlist_free(versions);
209 /* Parse encrypted introduction points. Don't verify. */
210 tok = find_opt_by_keyword(tokens, R_INTRODUCTION_POINTS);
211 if (tok) {
212 if (strcmp(tok->object_type, "MESSAGE")) {
213 log_warn(LD_DIR, "Bad object type: introduction points should be of "
214 "type MESSAGE");
215 goto err;
217 *intro_points_encrypted_out = tor_memdup(tok->object_body,
218 tok->object_size);
219 *intro_points_encrypted_size_out = tok->object_size;
220 } else {
221 *intro_points_encrypted_out = NULL;
222 *intro_points_encrypted_size_out = 0;
224 /* Parse and verify signature. */
225 tok = find_by_keyword(tokens, R_SIGNATURE);
226 if (check_signature_token(desc_hash, DIGEST_LEN, tok, result->pk, 0,
227 "v2 rendezvous service descriptor") < 0)
228 goto err;
229 /* Verify that descriptor ID belongs to public key and secret ID part. */
230 if (crypto_pk_get_digest(result->pk, public_key_hash) < 0) {
231 log_warn(LD_REND, "Unable to compute rend descriptor public key digest");
232 goto err;
234 rend_get_descriptor_id_bytes(test_desc_id, public_key_hash,
235 secret_id_part);
236 if (tor_memneq(desc_id_out, test_desc_id, DIGEST_LEN)) {
237 log_warn(LD_REND, "Parsed descriptor ID does not match "
238 "computed descriptor ID.");
239 goto err;
241 goto done;
242 err:
243 rend_service_descriptor_free(result);
244 result = NULL;
245 done:
246 if (tokens) {
247 SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
248 smartlist_free(tokens);
250 if (area)
251 memarea_drop_all(area);
252 *parsed_out = result;
253 if (result)
254 return 0;
255 return -1;
258 /** Decrypt the encrypted introduction points in <b>ipos_encrypted</b> of
259 * length <b>ipos_encrypted_size</b> using <b>descriptor_cookie</b> and
260 * write the result to a newly allocated string that is pointed to by
261 * <b>ipos_decrypted</b> and its length to <b>ipos_decrypted_size</b>.
262 * Return 0 if decryption was successful and -1 otherwise. */
264 rend_decrypt_introduction_points(char **ipos_decrypted,
265 size_t *ipos_decrypted_size,
266 const char *descriptor_cookie,
267 const char *ipos_encrypted,
268 size_t ipos_encrypted_size)
270 tor_assert(ipos_encrypted);
271 tor_assert(descriptor_cookie);
272 if (ipos_encrypted_size < 2) {
273 log_warn(LD_REND, "Size of encrypted introduction points is too "
274 "small.");
275 return -1;
277 if (ipos_encrypted[0] == (int)REND_BASIC_AUTH) {
278 char iv[CIPHER_IV_LEN], client_id[REND_BASIC_AUTH_CLIENT_ID_LEN],
279 session_key[CIPHER_KEY_LEN], *dec;
280 int declen, client_blocks;
281 size_t pos = 0, len, client_entries_len;
282 crypto_digest_t *digest;
283 crypto_cipher_t *cipher;
284 client_blocks = (int) ipos_encrypted[1];
285 client_entries_len = client_blocks * REND_BASIC_AUTH_CLIENT_MULTIPLE *
286 REND_BASIC_AUTH_CLIENT_ENTRY_LEN;
287 if (ipos_encrypted_size < 2 + client_entries_len + CIPHER_IV_LEN + 1) {
288 log_warn(LD_REND, "Size of encrypted introduction points is too "
289 "small.");
290 return -1;
292 memcpy(iv, ipos_encrypted + 2 + client_entries_len, CIPHER_IV_LEN);
293 digest = crypto_digest_new();
294 crypto_digest_add_bytes(digest, descriptor_cookie, REND_DESC_COOKIE_LEN);
295 crypto_digest_add_bytes(digest, iv, CIPHER_IV_LEN);
296 crypto_digest_get_digest(digest, client_id,
297 REND_BASIC_AUTH_CLIENT_ID_LEN);
298 crypto_digest_free(digest);
299 for (pos = 2; pos < 2 + client_entries_len;
300 pos += REND_BASIC_AUTH_CLIENT_ENTRY_LEN) {
301 if (tor_memeq(ipos_encrypted + pos, client_id,
302 REND_BASIC_AUTH_CLIENT_ID_LEN)) {
303 /* Attempt to decrypt introduction points. */
304 cipher = crypto_cipher_new(descriptor_cookie);
305 if (crypto_cipher_decrypt(cipher, session_key, ipos_encrypted
306 + pos + REND_BASIC_AUTH_CLIENT_ID_LEN,
307 CIPHER_KEY_LEN) < 0) {
308 log_warn(LD_REND, "Could not decrypt session key for client.");
309 crypto_cipher_free(cipher);
310 return -1;
312 crypto_cipher_free(cipher);
314 len = ipos_encrypted_size - 2 - client_entries_len - CIPHER_IV_LEN;
315 dec = tor_malloc_zero(len + 1);
316 declen = crypto_cipher_decrypt_with_iv(session_key, dec, len,
317 ipos_encrypted + 2 + client_entries_len,
318 ipos_encrypted_size - 2 - client_entries_len);
320 if (declen < 0) {
321 log_warn(LD_REND, "Could not decrypt introduction point string.");
322 tor_free(dec);
323 return -1;
325 if (fast_memcmpstart(dec, declen, "introduction-point ")) {
326 log_warn(LD_REND, "Decrypted introduction points don't "
327 "look like we could parse them.");
328 tor_free(dec);
329 continue;
331 *ipos_decrypted = dec;
332 *ipos_decrypted_size = declen;
333 return 0;
336 log_warn(LD_REND, "Could not decrypt introduction points. Please "
337 "check your authorization for this service!");
338 return -1;
339 } else if (ipos_encrypted[0] == (int)REND_STEALTH_AUTH) {
340 char *dec;
341 int declen;
342 if (ipos_encrypted_size < CIPHER_IV_LEN + 2) {
343 log_warn(LD_REND, "Size of encrypted introduction points is too "
344 "small.");
345 return -1;
347 dec = tor_malloc_zero(ipos_encrypted_size - CIPHER_IV_LEN - 1 + 1);
349 declen = crypto_cipher_decrypt_with_iv(descriptor_cookie, dec,
350 ipos_encrypted_size -
351 CIPHER_IV_LEN - 1,
352 ipos_encrypted + 1,
353 ipos_encrypted_size - 1);
355 if (declen < 0) {
356 log_warn(LD_REND, "Decrypting introduction points failed!");
357 tor_free(dec);
358 return -1;
360 *ipos_decrypted = dec;
361 *ipos_decrypted_size = declen;
362 return 0;
363 } else {
364 log_warn(LD_REND, "Unknown authorization type number: %d",
365 ipos_encrypted[0]);
366 return -1;
370 /** Parse the encoded introduction points in <b>intro_points_encoded</b> of
371 * length <b>intro_points_encoded_size</b> and write the result to the
372 * descriptor in <b>parsed</b>; return the number of successfully parsed
373 * introduction points or -1 in case of a failure. */
375 rend_parse_introduction_points(rend_service_descriptor_t *parsed,
376 const char *intro_points_encoded,
377 size_t intro_points_encoded_size)
379 const char *current_ipo, *end_of_intro_points;
380 smartlist_t *tokens = NULL;
381 directory_token_t *tok;
382 rend_intro_point_t *intro;
383 extend_info_t *info;
384 int result, num_ok=1;
385 memarea_t *area = NULL;
386 tor_assert(parsed);
387 /** Function may only be invoked once. */
388 tor_assert(!parsed->intro_nodes);
389 if (!intro_points_encoded || intro_points_encoded_size == 0) {
390 log_warn(LD_REND, "Empty or zero size introduction point list");
391 goto err;
393 /* Consider one intro point after the other. */
394 current_ipo = intro_points_encoded;
395 end_of_intro_points = intro_points_encoded + intro_points_encoded_size;
396 tokens = smartlist_new();
397 parsed->intro_nodes = smartlist_new();
398 area = memarea_new();
400 while (!fast_memcmpstart(current_ipo, end_of_intro_points-current_ipo,
401 "introduction-point ")) {
402 /* Determine end of string. */
403 const char *eos = tor_memstr(current_ipo, end_of_intro_points-current_ipo,
404 "\nintroduction-point ");
405 if (!eos)
406 eos = end_of_intro_points;
407 else
408 eos = eos+1;
409 tor_assert(eos <= intro_points_encoded+intro_points_encoded_size);
410 /* Free tokens and clear token list. */
411 SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
412 smartlist_clear(tokens);
413 memarea_clear(area);
414 /* Tokenize string. */
415 if (tokenize_string(area, current_ipo, eos, tokens, ipo_token_table, 0)) {
416 log_warn(LD_REND, "Error tokenizing introduction point");
417 goto err;
419 /* Advance to next introduction point, if available. */
420 current_ipo = eos;
421 /* Check minimum allowed length of introduction point. */
422 if (smartlist_len(tokens) < 5) {
423 log_warn(LD_REND, "Impossibly short introduction point.");
424 goto err;
426 /* Allocate new intro point and extend info. */
427 intro = tor_malloc_zero(sizeof(rend_intro_point_t));
428 info = intro->extend_info = tor_malloc_zero(sizeof(extend_info_t));
429 /* Parse identifier. */
430 tok = find_by_keyword(tokens, R_IPO_IDENTIFIER);
431 if (base32_decode(info->identity_digest, DIGEST_LEN,
432 tok->args[0], REND_INTRO_POINT_ID_LEN_BASE32) < 0) {
433 log_warn(LD_REND, "Identity digest contains illegal characters: %s",
434 tok->args[0]);
435 rend_intro_point_free(intro);
436 goto err;
438 /* Write identifier to nickname. */
439 info->nickname[0] = '$';
440 base16_encode(info->nickname + 1, sizeof(info->nickname) - 1,
441 info->identity_digest, DIGEST_LEN);
442 /* Parse IP address. */
443 tok = find_by_keyword(tokens, R_IPO_IP_ADDRESS);
444 if (tor_addr_parse(&info->addr, tok->args[0])<0) {
445 log_warn(LD_REND, "Could not parse introduction point address.");
446 rend_intro_point_free(intro);
447 goto err;
449 if (tor_addr_family(&info->addr) != AF_INET) {
450 log_warn(LD_REND, "Introduction point address was not ipv4.");
451 rend_intro_point_free(intro);
452 goto err;
455 /* Parse onion port. */
456 tok = find_by_keyword(tokens, R_IPO_ONION_PORT);
457 info->port = (uint16_t) tor_parse_long(tok->args[0],10,1,65535,
458 &num_ok,NULL);
459 if (!info->port || !num_ok) {
460 log_warn(LD_REND, "Introduction point onion port %s is invalid",
461 escaped(tok->args[0]));
462 rend_intro_point_free(intro);
463 goto err;
465 /* Parse onion key. */
466 tok = find_by_keyword(tokens, R_IPO_ONION_KEY);
467 if (!crypto_pk_public_exponent_ok(tok->key)) {
468 log_warn(LD_REND,
469 "Introduction point's onion key had invalid exponent.");
470 rend_intro_point_free(intro);
471 goto err;
473 info->onion_key = tok->key;
474 tok->key = NULL; /* Prevent free */
475 /* Parse service key. */
476 tok = find_by_keyword(tokens, R_IPO_SERVICE_KEY);
477 if (!crypto_pk_public_exponent_ok(tok->key)) {
478 log_warn(LD_REND,
479 "Introduction point key had invalid exponent.");
480 rend_intro_point_free(intro);
481 goto err;
483 intro->intro_key = tok->key;
484 tok->key = NULL; /* Prevent free */
485 /* Add extend info to list of introduction points. */
486 smartlist_add(parsed->intro_nodes, intro);
488 result = smartlist_len(parsed->intro_nodes);
489 goto done;
491 err:
492 result = -1;
494 done:
495 /* Free tokens and clear token list. */
496 if (tokens) {
497 SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
498 smartlist_free(tokens);
500 if (area)
501 memarea_drop_all(area);
503 return result;
506 /** Parse the content of a client_key file in <b>ckstr</b> and add
507 * rend_authorized_client_t's for each parsed client to
508 * <b>parsed_clients</b>. Return the number of parsed clients as result
509 * or -1 for failure. */
511 rend_parse_client_keys(strmap_t *parsed_clients, const char *ckstr)
513 int result = -1;
514 smartlist_t *tokens;
515 directory_token_t *tok;
516 const char *current_entry = NULL;
517 memarea_t *area = NULL;
518 char *err_msg = NULL;
519 if (!ckstr || strlen(ckstr) == 0)
520 return -1;
521 tokens = smartlist_new();
522 /* Begin parsing with first entry, skipping comments or whitespace at the
523 * beginning. */
524 area = memarea_new();
525 current_entry = eat_whitespace(ckstr);
526 while (!strcmpstart(current_entry, "client-name ")) {
527 rend_authorized_client_t *parsed_entry;
528 /* Determine end of string. */
529 const char *eos = strstr(current_entry, "\nclient-name ");
530 if (!eos)
531 eos = current_entry + strlen(current_entry);
532 else
533 eos = eos + 1;
534 /* Free tokens and clear token list. */
535 SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
536 smartlist_clear(tokens);
537 memarea_clear(area);
538 /* Tokenize string. */
539 if (tokenize_string(area, current_entry, eos, tokens,
540 client_keys_token_table, 0)) {
541 log_warn(LD_REND, "Error tokenizing client keys file.");
542 goto err;
544 /* Advance to next entry, if available. */
545 current_entry = eos;
546 /* Check minimum allowed length of token list. */
547 if (smartlist_len(tokens) < 2) {
548 log_warn(LD_REND, "Impossibly short client key entry.");
549 goto err;
551 /* Parse client name. */
552 tok = find_by_keyword(tokens, C_CLIENT_NAME);
553 tor_assert(tok == smartlist_get(tokens, 0));
554 tor_assert(tok->n_args == 1);
556 if (!rend_valid_client_name(tok->args[0])) {
557 log_warn(LD_CONFIG, "Illegal client name: %s. (Length must be "
558 "between 1 and %d, and valid characters are "
559 "[A-Za-z0-9+-_].)", tok->args[0], REND_CLIENTNAME_MAX_LEN);
560 goto err;
562 /* Check if client name is duplicate. */
563 if (strmap_get(parsed_clients, tok->args[0])) {
564 log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains a "
565 "duplicate client name: '%s'. Ignoring.", tok->args[0]);
566 goto err;
568 parsed_entry = tor_malloc_zero(sizeof(rend_authorized_client_t));
569 parsed_entry->client_name = tor_strdup(tok->args[0]);
570 strmap_set(parsed_clients, parsed_entry->client_name, parsed_entry);
571 /* Parse client key. */
572 tok = find_opt_by_keyword(tokens, C_CLIENT_KEY);
573 if (tok) {
574 parsed_entry->client_key = tok->key;
575 tok->key = NULL; /* Prevent free */
578 /* Parse descriptor cookie. */
579 tok = find_by_keyword(tokens, C_DESCRIPTOR_COOKIE);
580 tor_assert(tok->n_args == 1);
581 if (rend_auth_decode_cookie(tok->args[0], parsed_entry->descriptor_cookie,
582 NULL, &err_msg) < 0) {
583 tor_assert(err_msg);
584 log_warn(LD_REND, "%s", err_msg);
585 tor_free(err_msg);
586 goto err;
589 result = strmap_size(parsed_clients);
590 goto done;
591 err:
592 result = -1;
593 done:
594 /* Free tokens and clear token list. */
595 SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
596 smartlist_free(tokens);
597 if (area)
598 memarea_drop_all(area);
599 return result;