aboutsummaryrefslogtreecommitdiff
path: root/src/or/routerparse.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/routerparse.c')
-rw-r--r--src/or/routerparse.c346
1 files changed, 346 insertions, 0 deletions
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 10d0436507..98e46c8e4a 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -80,6 +80,21 @@ typedef enum {
A_PURPOSE,
_A_UNKNOWN,
+ R_RENDEZVOUS_SERVICE_DESCRIPTOR,
+ R_VERSION,
+ R_PERMANENT_KEY,
+ R_SECRET_ID_PART,
+ R_PUBLICATION_TIME,
+ R_PROTOCOL_VERSIONS,
+ R_INTRODUCTION_POINTS,
+ R_SIGNATURE,
+
+ R_IPO_IDENTIFIER,
+ R_IPO_IP_ADDRESS,
+ R_IPO_ONION_PORT,
+ R_IPO_ONION_KEY,
+ R_IPO_SERVICE_KEY,
+
_UNRECOGNIZED,
_ERR,
_EOF,
@@ -298,6 +313,31 @@ static token_rule_t dir_key_certificate_table[] = {
END_OF_TABLE
};
+/** List of tokens allowable in rendezvous service descriptors */
+static token_rule_t desc_token_table[] = {
+ T1("rendezvous-service-descriptor", R_RENDEZVOUS_SERVICE_DESCRIPTOR, EQ(1), \
+ NO_OBJ),
+ T1("version", R_VERSION, EQ(1), NO_OBJ),
+ T1("permanent-key", R_PERMANENT_KEY, NO_ARGS, NEED_KEY_1024),
+ T1("secret-id-part", R_SECRET_ID_PART, EQ(1), NO_OBJ),
+ T1("publication-time", R_PUBLICATION_TIME, CONCAT_ARGS, NO_OBJ),
+ T1("protocol-versions", R_PROTOCOL_VERSIONS, EQ(1), NO_OBJ),
+ T1("introduction-points", R_INTRODUCTION_POINTS, NO_ARGS, NEED_OBJ),
+ T1("signature", R_SIGNATURE, NO_ARGS, NEED_OBJ),
+ END_OF_TABLE
+};
+
+/** List of tokens allowed in the (encrypted) list of introduction points of
+ * rendezvous service descriptors */
+static token_rule_t ipo_token_table[] = {
+ T1("introduction-point", R_IPO_IDENTIFIER, EQ(1), NO_OBJ),
+ T1("ip-address", R_IPO_IP_ADDRESS, EQ(1), NO_OBJ),
+ T1("onion-port", R_IPO_ONION_PORT, EQ(1), NO_OBJ),
+ T1("onion-key", R_IPO_ONION_KEY, NO_ARGS, NEED_KEY_1024),
+ T1("service-key", R_IPO_SERVICE_KEY, NO_ARGS, NEED_KEY_1024),
+ END_OF_TABLE
+};
+
static token_rule_t networkstatus_vote_token_table[] = {
T1("network-status-version", K_NETWORK_STATUS_VERSION,
GE(1), NO_OBJ ),
@@ -3125,3 +3165,309 @@ sort_version_list(smartlist_t *versions, int remove_duplicates)
smartlist_uniq(versions, _compare_tor_version_str_ptr, _tor_free);
}
+/** Parse and validate the ASCII-encoded v2 descriptor in <b>desc</b>,
+ * write the parsed descriptor to the newly allocated <b>parsed</b>, the
+ * binary descriptor ID of length DIGEST_LEN to <b>desc_id</b>, the
+ * encrypted introduction points to the newly allocated
+ * <b>intro_points_encrypted</b>, their encrypted size to
+ * <b>intro_points_encrypted_size</b>, the size of the encoded descriptor
+ * to <b>encoded_size</b>, and a pointer to the possibly next
+ * descriptor to <b>next</b>; return 0 for success (including validation)
+ * and -1 for failure.
+ */
+int
+rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
+ char *desc_id_out,
+ char **intro_points_encrypted_out,
+ size_t *intro_points_encrypted_size_out,
+ size_t *encoded_size_out,
+ const char **next_out, const char *desc)
+{
+ rend_service_descriptor_t *result =
+ tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ char desc_hash[DIGEST_LEN];
+ const char *eos;
+ smartlist_t *tokens = smartlist_create();
+ directory_token_t *tok;
+ char secret_id_part[DIGEST_LEN];
+ int i, version;
+ smartlist_t *versions;
+ char public_key_hash[DIGEST_LEN];
+ char test_desc_id[DIGEST_LEN];
+ tor_assert(desc);
+ /* Check if desc starts correctly. */
+ if (strncmp(desc, "rendezvous-service-descriptor ",
+ strlen("rendezvous-service-descriptor "))) {
+ log_info(LD_REND, "Descriptor does not start correctly.");
+ goto err;
+ }
+ /* Compute descriptor hash for later validation. */
+ if (router_get_hash_impl(desc, desc_hash,
+ "rendezvous-service-descriptor ",
+ "\nsignature", '\n') < 0) {
+ log_warn(LD_REND, "Couldn't compute descriptor hash.");
+ goto err;
+ }
+ /* Determine end of string. */
+ eos = strstr(desc, "\nrendezvous-service-descriptor ");
+ if (!eos)
+ eos = desc + strlen(desc);
+ else
+ eos = eos + 1;
+ /* Tokenize descriptor. */
+ if (tokenize_string(desc, eos, tokens, desc_token_table, 0)) {
+ log_warn(LD_REND, "Error tokenizing descriptor.");
+ goto err;
+ }
+ /* Set next to next descriptor, if available. */
+ *next_out = eos;
+ /* Set length of encoded descriptor. */
+ *encoded_size_out = eos - desc;
+ /* Check min allowed length of token list. */
+ if (smartlist_len(tokens) < 8) {
+ log_warn(LD_REND, "Impossibly short descriptor.");
+ goto err;
+ }
+ /* Check whether descriptor starts correctly. */
+ tok = smartlist_get(tokens, 0);
+ if (tok->tp != R_RENDEZVOUS_SERVICE_DESCRIPTOR) {
+ log_warn(LD_REND, "Entry does not start with "
+ "\"rendezvous-service-descriptor\"");
+ goto err;
+ }
+ /* Parse base32-encoded descriptor ID. */
+ tok = find_first_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR);
+ tor_assert(tok);
+ tor_assert(tok->n_args == 1);
+ if (strlen(tok->args[0]) != 32 ||
+ strspn(tok->args[0], BASE32_CHARS) != 32) {
+ log_warn(LD_REND, "Invalid descriptor ID: '%s'", tok->args[0]);
+ goto err;
+ }
+ if (base32_decode(desc_id_out, DIGEST_LEN,
+ tok->args[0], REND_DESC_ID_V2_BASE32) < 0) {
+ log_warn(LD_REND, "Descriptor ID contains illegal characters: %s",
+ tok->args[0]);
+ goto err;
+ }
+ /* Parse descriptor version. */
+ tok = find_first_by_keyword(tokens, R_VERSION);
+ tor_assert(tok);
+ tor_assert(tok->n_args == 1);
+ result->version = atoi(tok->args[0]);
+ if (result->version < 2) {
+ log_warn(LD_REND, "Wrong descriptor version: %d", result->version);
+ goto err;
+ }
+ /* Parse public key. */
+ tok = find_first_by_keyword(tokens, R_PERMANENT_KEY);
+ tor_assert(tok);
+ result->pk = tok->key;
+ tok->key = NULL; /* Prevent free */
+ /* Parse secret ID part. */
+ tok = find_first_by_keyword(tokens, R_SECRET_ID_PART);
+ tor_assert(tok);
+ tor_assert(tok->n_args == 1);
+ if (strlen(tok->args[0]) != 32 ||
+ strspn(tok->args[0], BASE32_CHARS) != 32) {
+ log_warn(LD_REND, "Invalid secret ID part: '%s'", tok->args[0]);
+ goto err;
+ }
+ if (base32_decode(secret_id_part, DIGEST_LEN, tok->args[0], 32) < 0) {
+ log_warn(LD_REND, "Secret ID part contains illegal characters: %s",
+ tok->args[0]);
+ goto err;
+ }
+ /* Parse publication time -- up-to-date check is done when storing the
+ * descriptor. */
+ tok = find_first_by_keyword(tokens, R_PUBLICATION_TIME);
+ tor_assert(tok);
+ tor_assert(tok->n_args == 1);
+ if (parse_iso_time(tok->args[0], &result->timestamp) < 0) {
+ log_warn(LD_REND, "Invalid publication time: '%s'", tok->args[0]);
+ goto err;
+ }
+ /* Parse protocol versions. */
+ tok = find_first_by_keyword(tokens, R_PROTOCOL_VERSIONS);
+ tor_assert(tok);
+ tor_assert(tok->n_args == 1);
+ versions = smartlist_create();
+ smartlist_split_string(versions, tok->args[0], ",",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ for (i = 0; i < smartlist_len(versions); i++) {
+ version = atoi(smartlist_get(versions, i));
+ result->protocols |= 1 << version;
+ }
+ smartlist_free(versions);
+ /* Parse encrypted introduction points. Don't verify. */
+ tok = find_first_by_keyword(tokens, R_INTRODUCTION_POINTS);
+ tor_assert(tok);
+ *intro_points_encrypted_out = tor_malloc_zero(tok->object_size);
+ memcpy(*intro_points_encrypted_out, tok->object_body, tok->object_size);
+ *intro_points_encrypted_size_out = tok->object_size;
+ /* Parse and verify signature. */
+ tok = find_first_by_keyword(tokens, R_SIGNATURE);
+ tor_assert(tok);
+ note_crypto_pk_op(VERIFY_RTR);
+ if (check_signature_token(desc_hash, tok, result->pk, 0,
+ "v2 rendezvous service descriptor") < 0)
+ goto err;
+ /* Verify that descriptor ID belongs to public key and secret ID part. */
+ crypto_pk_get_digest(result->pk, public_key_hash);
+ rend_get_descriptor_id_bytes(test_desc_id, public_key_hash,
+ secret_id_part);
+ if (memcmp(desc_id_out, test_desc_id, DIGEST_LEN)) {
+ log_warn(LD_REND, "Parsed descriptor ID does not match "
+ "computed descriptor ID.");
+ goto err;
+ }
+ goto done;
+ err:
+ if (result)
+ rend_service_descriptor_free(result);
+ result = NULL;
+ done:
+ if (tokens) {
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ smartlist_free(tokens);
+ }
+ *parsed_out = result;
+ if (result)
+ return 0;
+ return -1;
+}
+
+/** Decrypt and decode the introduction points in
+ * <b>intro_points_encrypted</b> of length
+ * <b>intro_points_encrypted_size</b> using <b>descriptor_cookie</b>
+ * (which may also be <b>NULL</b> if no decryption, but only parsing is
+ * required), parse the introduction points, and write the result to
+ * <b>parsed</b>; return the number of successfully parsed introduction
+ * points or -1 in case of a failure.
+ */
+int
+rend_decrypt_introduction_points(rend_service_descriptor_t *parsed,
+ const char *descriptor_cookie,
+ const char *intro_points_encrypted,
+ size_t intro_points_encrypted_size)
+{
+ char *ipos_decrypted;
+ const char **current_ipo;
+ smartlist_t *intropoints;
+ smartlist_t *tokens;
+ int i;
+ directory_token_t *tok;
+ extend_info_t *info;
+ struct in_addr ip;
+ int result;
+ tor_assert(parsed);
+ tor_assert(intro_points_encrypted);
+ tor_assert(intro_points_encrypted_size > 0);
+ /* Decrypt introduction points, if required. */
+ if (descriptor_cookie) {
+ crypto_cipher_env_t *cipher;
+ int unenclen;
+ ipos_decrypted = tor_malloc_zero(intro_points_encrypted_size - 16);
+ cipher = crypto_create_init_cipher(descriptor_cookie, 0);
+ unenclen = crypto_cipher_decrypt_with_iv(cipher, ipos_decrypted,
+ intro_points_encrypted_size - 16,
+ intro_points_encrypted,
+ intro_points_encrypted_size);
+ crypto_free_cipher_env(cipher);
+ if (unenclen < 0) {
+ if (ipos_decrypted) tor_free(ipos_decrypted);
+ return -1;
+ }
+ intro_points_encrypted = ipos_decrypted;
+ intro_points_encrypted_size = unenclen;
+ }
+ /* Consider one intro point after the other. */
+ current_ipo = (const char **)&intro_points_encrypted;
+ intropoints = smartlist_create();
+ tokens = smartlist_create();
+ parsed->intro_keys = strmap_new();
+ while (!strcmpstart(*current_ipo, "introduction-point ")) {
+ /* Determine end of string. */
+ const char *eos = strstr(*current_ipo, "\nintroduction-point ");
+ if (!eos)
+ eos = *current_ipo+strlen(*current_ipo);
+ else
+ eos = eos+1;
+ /* Free tokens and clear token list. */
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ smartlist_clear(tokens);
+ /* Tokenize string. */
+ if (tokenize_string(*current_ipo, eos, tokens, ipo_token_table, 0)) {
+ log_warn(LD_REND, "Error tokenizing introduction point.");
+ goto err;
+ }
+ /* Advance to next introduction point, if available. */
+ *current_ipo = eos;
+ /* Check minimum allowed length of introduction point. */
+ if (smartlist_len(tokens) < 5) {
+ log_warn(LD_REND, "Impossibly short introduction point.");
+ goto err;
+ }
+ /* Allocate new extend info. */
+ info = tor_malloc_zero(sizeof(extend_info_t));
+ /* Parse identifier. */
+ tok = find_first_by_keyword(tokens, R_IPO_IDENTIFIER);
+ tor_assert(tok);
+ if (base32_decode(info->identity_digest, DIGEST_LEN,
+ tok->args[0], 32) < 0) {
+ log_warn(LD_REND, "Identity digest contains illegal characters: %s",
+ tok->args[0]);
+ tor_free(info);
+ goto err;
+ }
+ /* Write identifier to nickname. */
+ info->nickname[0] = '$';
+ base16_encode(info->nickname + 1, sizeof(info->nickname) - 1,
+ info->identity_digest, DIGEST_LEN);
+ /* Parse IP address. */
+ tok = find_first_by_keyword(tokens, R_IPO_IP_ADDRESS);
+ if (tor_inet_aton(tok->args[0], &ip) == 0) {
+ log_warn(LD_REND, "Could not parse IP address.");
+ tor_free(info);
+ goto err;
+ }
+ info->addr = ntohl(ip.s_addr);
+ /* Parse onion port. */
+ tok = find_first_by_keyword(tokens, R_IPO_ONION_PORT);
+ info->port = (uint16_t) atoi(tok->args[0]);
+ /* Parse onion key. */
+ tok = find_first_by_keyword(tokens, R_IPO_ONION_KEY);
+ info->onion_key = tok->key;
+ tok->key = NULL; /* Prevent free */
+ /* Parse service key. */
+ tok = find_first_by_keyword(tokens, R_IPO_SERVICE_KEY);
+ strmap_set(parsed->intro_keys, info->nickname, tok->key);
+ tok->key = NULL; /* Prevent free */
+ /* Add extend info to list of introduction points. */
+ smartlist_add(intropoints, info);
+ }
+ /* Write extend infos to descriptor. */
+ parsed->n_intro_points = smartlist_len(intropoints);
+ parsed->intro_point_extend_info =
+ tor_malloc_zero(sizeof(extend_info_t *) * parsed->n_intro_points);
+ parsed->intro_points =
+ tor_malloc_zero(sizeof(char *) * parsed->n_intro_points);
+ i = 0;
+ SMARTLIST_FOREACH(intropoints, extend_info_t *, ipo, {
+ parsed->intro_points[i] = tor_strdup(ipo->nickname);
+ parsed->intro_point_extend_info[i++] = ipo;
+ });
+ result = parsed->n_intro_points;
+ goto done;
+
+ err:
+ result = -1;
+
+ done:
+ /* Free tokens and clear token list. */
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ smartlist_free(tokens);
+
+ return result;
+}
+