/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /* $Id$ */ const char rendcommon_c_id[] = "$Id$"; /** * \file rendcommon.c * \brief Rendezvous implementation: shared code between * introducers, services, clients, and rendezvous points. **/ #include "or.h" /** Return 0 if one and two are the same service ids, else -1 or 1 */ int rend_cmp_service_ids(const char *one, const char *two) { return strcasecmp(one,two); } /** Helper: Release the storage held by the intro key in _ent. */ /*XXXX020 there's also one of these in rendservice.c */ /* Right. But the only alternative to that (which I know) would be to * write it to or.h. Should I do that? -KL */ static void intro_key_free(void *_ent) { crypto_pk_env_t *ent = _ent; crypto_free_pk_env(ent); } /** Free the storage held by the service descriptor desc. */ void rend_service_descriptor_free(rend_service_descriptor_t *desc) { int i; if (desc->pk) crypto_free_pk_env(desc->pk); if (desc->intro_points) { for (i=0; i < desc->n_intro_points; ++i) { tor_free(desc->intro_points[i]); } tor_free(desc->intro_points); } if (desc->intro_point_extend_info) { for (i=0; i < desc->n_intro_points; ++i) { if (desc->intro_point_extend_info[i]) extend_info_free(desc->intro_point_extend_info[i]); } tor_free(desc->intro_point_extend_info); } if (desc->intro_keys) { strmap_free(desc->intro_keys, intro_key_free); } tor_free(desc); } /** Length of the descriptor cookie that is used for versioned hidden * service descriptors. */ #define REND_DESC_COOKIE_LEN 16 /** Length of the replica number that is used to determine the secret ID * part of versioned hidden service descriptors. */ #define REND_REPLICA_LEN 1 /** Compute the descriptor ID for service_id of length * REND_SERVICE_ID_LEN and secret_id_part of length * DIGEST_LEN, and write it to descriptor_id_out of length * DIGEST_LEN. */ void rend_get_descriptor_id_bytes(char *descriptor_id_out, const char *service_id, const char *secret_id_part) { crypto_digest_env_t *digest = crypto_new_digest_env(); crypto_digest_add_bytes(digest, service_id, REND_SERVICE_ID_LEN); crypto_digest_add_bytes(digest, secret_id_part, DIGEST_LEN); crypto_digest_get_digest(digest, descriptor_id_out, DIGEST_LEN); crypto_free_digest_env(digest); } /** Compute the secret ID part for time_period, * a descriptor_cookie of length * REND_DESC_COOKIE_LEN which may also be NULL if no * descriptor_cookie shall be used, and replica, and write it to * secret_id_part of length DIGEST_LEN. */ static void get_secret_id_part_bytes(char *secret_id_part, uint32_t time_period, const char *descriptor_cookie, uint8_t replica) { crypto_digest_env_t *digest = crypto_new_digest_env(); time_period = htonl(time_period); crypto_digest_add_bytes(digest, (char*)&time_period, sizeof(uint32_t)); if (descriptor_cookie) { crypto_digest_add_bytes(digest, descriptor_cookie, REND_DESC_COOKIE_LEN); } crypto_digest_add_bytes(digest, (const char *)&replica, REND_REPLICA_LEN); crypto_digest_get_digest(digest, secret_id_part, DIGEST_LEN); crypto_free_digest_env(digest); } /** Return the time period for time now plus a potentially * intended deviation of one or more periods, based on the first byte * of service_id. */ static uint32_t get_time_period(time_t now, uint8_t deviation, const char *service_id) { /* The time period is the number of REND_TIME_PERIOD_V2_DESC_VALIDITY * intervals that have passed since the epoch, offset slightly so that * each service's time periods start and end at a fraction of that * period based on their first byte. */ return (uint32_t) (now + ((uint8_t) *service_id) * REND_TIME_PERIOD_V2_DESC_VALIDITY / 256) / REND_TIME_PERIOD_V2_DESC_VALIDITY + deviation; } /** Compute the time in seconds that a descriptor that is generated * now for service_id will be valid. */ static uint32_t get_seconds_valid(time_t now, const char *service_id) { uint32_t result = REND_TIME_PERIOD_V2_DESC_VALIDITY - ((uint32_t) (now + ((uint8_t) *service_id) * REND_TIME_PERIOD_V2_DESC_VALIDITY / 256) % REND_TIME_PERIOD_V2_DESC_VALIDITY); return result; } /** Compute the binary desc_id_out (DIGEST_LEN bytes long) for a given * base32-encoded service_id and optional unencoded * descriptor_cookie of length REND_DESC_COOKIE_LEN, * at time now for replica number * replica. desc_id needs to have DIGEST_LEN bytes * free. Return 0 for success, -1 otherwise. */ int rend_compute_v2_desc_id(char *desc_id_out, const char *service_id, const char *descriptor_cookie, time_t now, uint8_t replica) { char service_id_binary[REND_SERVICE_ID_LEN]; char secret_id_part[DIGEST_LEN]; uint32_t time_period; if (!service_id || strlen(service_id) != REND_SERVICE_ID_LEN_BASE32) { log_warn(LD_REND, "Could not compute v2 descriptor ID: " "Illegal service ID: %s", service_id); return -1; } if (replica >= REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS) { log_warn(LD_REND, "Could not compute v2 descriptor ID: " "Replica number out of range: %d", replica); return -1; } /* Convert service ID to binary. */ if (base32_decode(service_id_binary, REND_SERVICE_ID_LEN, service_id, REND_SERVICE_ID_LEN_BASE32) < 0) { log_warn(LD_REND, "Could not compute v2 descriptor ID: " "Illegal characters in service ID: %s", service_id); return -1; } /* Calculate current time-period. */ time_period = get_time_period(now, 0, service_id_binary); /* Calculate secret-id-part = h(time-period + replica). */ get_secret_id_part_bytes(secret_id_part, time_period, descriptor_cookie, replica); /* Calculate descriptor ID. */ rend_get_descriptor_id_bytes(desc_id_out, service_id_binary, secret_id_part); return 0; } /* Encode the introduction points in desc, optionally encrypt them with * an optional descriptor_cookie of length REND_DESC_COOKIE_LEN, * encode it in base64, and write it to a newly allocated string, and write a * pointer to it to *ipos_base64. Return 0 for success, -1 * otherwise. */ static int rend_encode_v2_intro_points(char **ipos_base64, rend_service_descriptor_t *desc, const char *descriptor_cookie) { size_t unenc_len; char *unenc = NULL; size_t unenc_written = 0; int i; int r = -1; /* Assemble unencrypted list of introduction points. */ *ipos_base64 = NULL; unenc_len = desc->n_intro_points * 1000; /* too long, but ok. */ unenc = tor_malloc_zero(unenc_len); for (i = 0; i < desc->n_intro_points; i++) { char id_base32[REND_INTRO_POINT_ID_LEN_BASE32 + 1]; char *onion_key = NULL; size_t onion_key_len; crypto_pk_env_t *intro_key; char *service_key = NULL; char *address = NULL; size_t service_key_len; int res; char hex_digest[HEX_DIGEST_LEN+2]; /* includes $ and NUL. */ /* Obtain extend info with introduction point details. */ extend_info_t *info = desc->intro_point_extend_info[i]; /* Encode introduction point ID. */ base32_encode(id_base32, sizeof(id_base32), info->identity_digest, DIGEST_LEN); /* Encode onion key. */ if (crypto_pk_write_public_key_to_string(info->onion_key, &onion_key, &onion_key_len) < 0) { log_warn(LD_REND, "Could not write onion key."); goto done; } /* Encode intro key. */ hex_digest[0] = '$'; base16_encode(hex_digest+1, HEX_DIGEST_LEN+1, info->identity_digest, DIGEST_LEN); intro_key = strmap_get(desc->intro_keys, hex_digest); if (!intro_key || crypto_pk_write_public_key_to_string(intro_key, &service_key, &service_key_len) < 0) { log_warn(LD_REND, "Could not write intro key."); tor_free(onion_key); goto done; } /* Assemble everything for this introduction point. */ address = tor_dup_addr(info->addr); res = tor_snprintf(unenc + unenc_written, unenc_len - unenc_written, "introduction-point %s\n" "ip-address %s\n" "onion-port %d\n" "onion-key\n%s" "service-key\n%s", id_base32, address, info->port, onion_key, service_key); tor_free(address); tor_free(onion_key); tor_free(service_key); if (res < 0) { log_warn(LD_REND, "Not enough space for writing introduction point " "string."); goto done; } /* Update total number of written bytes for unencrypted intro points. */ unenc_written += res; } /* Finalize unencrypted introduction points. */ if (unenc_len < unenc_written + 2) { log_warn(LD_REND, "Not enough space for finalizing introduction point " "string."); goto done; } unenc[unenc_written++] = '\n'; unenc[unenc_written++] = 0; /* If a descriptor cookie is passed, encrypt introduction points. */ if (descriptor_cookie) { char *enc = tor_malloc_zero(unenc_written + CIPHER_IV_LEN); crypto_cipher_env_t *cipher = crypto_create_init_cipher(descriptor_cookie, 1); int enclen = crypto_cipher_encrypt_with_iv(cipher, enc, unenc_written + CIPHER_IV_LEN, unenc, unenc_written); crypto_free_cipher_env(cipher); if (enclen < 0) { log_warn(LD_REND, "Could not encrypt introduction point string."); tor_free(enc); goto done; } /* Replace original string with the encrypted one. */ tor_free(unenc); unenc = enc; unenc_written = enclen; } /* Base64-encode introduction points. */ *ipos_base64 = tor_malloc_zero(unenc_written * 2); if (base64_encode(*ipos_base64, unenc_written * 2, unenc, unenc_written)<0) { log_warn(LD_REND, "Could not encode introduction point string to " "base64."); goto done; } r = 0; done: if (r<0) tor_free(*ipos_base64); tor_free(unenc); return r; } /** Attempt to parse the given desc_str and return true if this * succeeds, false otherwise. */ static int rend_desc_v2_is_parsable(const char *desc_str) { rend_service_descriptor_t *test_parsed = NULL; char test_desc_id[DIGEST_LEN]; char *test_intro_content = NULL; size_t test_intro_size; size_t test_encoded_size; const char *test_next; int res = rend_parse_v2_service_descriptor(&test_parsed, test_desc_id, &test_intro_content, &test_intro_size, &test_encoded_size, &test_next, desc_str); if (test_parsed) rend_service_descriptor_free(test_parsed); tor_free(test_intro_content); return (res >= 0); } /** Encode a set of new service descriptors for desc at time * now using descriptor_cookie (may be NULL if * introduction points shall not be encrypted) and period (e.g. 0 * for the current period, 1 for the next period, etc.), write the * ASCII-encoded outputs to newly allocated strings and add them to the * existing desc_strs, and write the descriptor IDs to newly * allocated strings and add them to the existing desc_ids; return * the number of seconds that the descriptors will be found under those * desc_ids by clients, or -1 if the encoding was not successful. */ int rend_encode_v2_descriptors(smartlist_t *desc_strs_out, smartlist_t *desc_ids_out, rend_service_descriptor_t *desc, time_t now, const char *descriptor_cookie, uint8_t period) { char service_id[DIGEST_LEN]; uint32_t time_period; char *ipos_base64 = NULL; int k; uint32_t seconds_valid; if (!desc) { log_warn(LD_REND, "Could not encode v2 descriptor: No desc given."); return -1; } /* Obtain service_id from public key. */ crypto_pk_get_digest(desc->pk, service_id); /* Calculate current time-period. */ time_period = get_time_period(now, period, service_id); /* Determine how many seconds the descriptor will be valid. */ seconds_valid = period * REND_TIME_PERIOD_V2_DESC_VALIDITY + get_seconds_valid(now, service_id); /* Assemble, possibly encrypt, and encode introduction points. */ if (desc->n_intro_points > 0 && rend_encode_v2_intro_points(&ipos_base64, desc, descriptor_cookie) < 0) { log_warn(LD_REND, "Encoding of introduction points did not succeed."); return -1; } /* Encode REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS descriptors. */ for (k = 0; k < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; k++) { char secret_id_part[DIGEST_LEN]; char secret_id_part_base32[REND_SECRET_ID_PART_LEN_BASE32 + 1]; char *desc_id; char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; char *permanent_key = NULL; size_t permanent_key_len; char published[ISO_TIME_LEN+1]; int i; char protocol_versions_string[16]; /* max len: "0,1,2,3,4,5,6,7\0" */ size_t protocol_versions_written; size_t desc_len; char *desc_str = NULL; int result = 0; size_t written = 0; char desc_digest[DIGEST_LEN]; /* Calculate secret-id-part = h(time-period + cookie + replica). */ get_secret_id_part_bytes(secret_id_part, time_period, descriptor_cookie, k); base32_encode(secret_id_part_base32, sizeof(secret_id_part_base32), secret_id_part, DIGEST_LEN); /* Calculate descriptor ID. */ desc_id = tor_malloc_zero(DIGEST_LEN); rend_get_descriptor_id_bytes(desc_id, service_id, secret_id_part); smartlist_add(desc_ids_out, desc_id); base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, DIGEST_LEN); /* PEM-encode the public key */ if (crypto_pk_write_public_key_to_string(desc->pk, &permanent_key, &permanent_key_len) < 0) { log_warn(LD_BUG, "Could not write public key to string."); goto err; } /* Encode timestamp. */ format_iso_time(published, desc->timestamp); /* Write protocol-versions bitmask to comma-separated value string. */ protocol_versions_written = 0; for (i = 0; i < 8; i++) { if (desc->protocols & 1 << i) { tor_snprintf(protocol_versions_string + protocol_versions_written, 16 - protocol_versions_written, "%d,", i); protocol_versions_written += 2; } } if (protocol_versions_written) protocol_versions_string[protocol_versions_written - 1] = '\0'; else protocol_versions_string[0]= '\0'; /* Assemble complete descriptor. */ desc_len = 2000 + desc->n_intro_points * 1000; /* far too long, but ok. */ desc_str = tor_malloc_zero(desc_len); result = tor_snprintf(desc_str, desc_len, "rendezvous-service-descriptor %s\n" "version 2\n" "permanent-key\n%s" "secret-id-part %s\n" "publication-time %s\n" "protocol-versions %s\n", desc_id_base32, permanent_key, secret_id_part_base32, published, protocol_versions_string); tor_free(permanent_key); if (result < 0) { log_warn(LD_BUG, "Descriptor ran out of room."); tor_free(desc_str); goto err; } written = result; /* Add introduction points. */ if (ipos_base64) { result = tor_snprintf(desc_str + written, desc_len - written, "introduction-points\n" "-----BEGIN MESSAGE-----\n%s" "-----END MESSAGE-----\n", ipos_base64); if (result < 0) { log_warn(LD_BUG, "could not write introduction points."); tor_free(desc_str); goto err; } written += result; } /* Add signature. */ strlcpy(desc_str + written, "signature\n", desc_len - written); written += strlen(desc_str + written); if (crypto_digest(desc_digest, desc_str, written) < 0) { log_warn(LD_BUG, "could not create digest."); tor_free(desc_str); goto err; } if (router_append_dirobj_signature(desc_str + written, desc_len - written, desc_digest, desc->pk) < 0) { log_warn(LD_BUG, "Couldn't sign desc."); tor_free(desc_str); goto err; } written += strlen(desc_str+written); if (written+2 > desc_len) { log_warn(LD_BUG, "Could not finish desc."); tor_free(desc_str); goto err; } desc_str[written++] = '\n'; desc_str[written++] = 0; /* Check if we can parse our own descriptor. */ if (!rend_desc_v2_is_parsable(desc_str)) { log_warn(LD_BUG, "Could not parse my own descriptor: %s", desc_str); tor_free(desc_str); goto err; } smartlist_add(desc_strs_out, desc_str); } log_info(LD_REND, "Successfully encoded a v2 descriptor and " "confirmed that it is parsable."); goto done; err: SMARTLIST_FOREACH(desc_ids_out, void *, id, tor_free(id)); smartlist_clear(desc_ids_out); SMARTLIST_FOREACH(desc_strs_out, void *, str, tor_free(str)); smartlist_clear(desc_strs_out); seconds_valid = -1; done: tor_free(ipos_base64); return seconds_valid; } /** Encode a service descriptor for desc, and sign it with * key. Store the descriptor in *str_out, and set * *len_out to its length. */ int rend_encode_service_descriptor(rend_service_descriptor_t *desc, crypto_pk_env_t *key, char **str_out, size_t *len_out) { char *cp; char *end; int i; size_t asn1len; size_t buflen = PK_BYTES*2*(desc->n_intro_points+2);/*Too long, but ok*/ cp = *str_out = tor_malloc(buflen); end = cp + PK_BYTES*2*(desc->n_intro_points+1); asn1len = crypto_pk_asn1_encode(desc->pk, cp+2, end-(cp+2)); set_uint16(cp, htons((uint16_t)asn1len)); cp += 2+asn1len; set_uint32(cp, htonl((uint32_t)desc->timestamp)); cp += 4; set_uint16(cp, htons((uint16_t)desc->n_intro_points)); cp += 2; for (i=0; i < desc->n_intro_points; ++i) { char *ipoint = (char*)desc->intro_points[i]; strlcpy(cp, ipoint, buflen-(cp-*str_out)); cp += strlen(ipoint)+1; } note_crypto_pk_op(REND_SERVER); i = crypto_pk_private_sign_digest(key, cp, *str_out, cp-*str_out); if (i<0) { tor_free(*str_out); return -1; } cp += i; *len_out = (size_t)(cp-*str_out); return 0; } /** Parse a service descriptor at str (len bytes). On * success, return a newly alloced service_descriptor_t. On failure, * return NULL. */ rend_service_descriptor_t * rend_parse_service_descriptor(const char *str, size_t len) { rend_service_descriptor_t *result = NULL; int i; size_t keylen, asn1len; const char *end, *cp, *eos; result = tor_malloc_zero(sizeof(rend_service_descriptor_t)); cp = str; end = str+len; if (end-cp<2) goto truncated; result->version = 0; if (end-cp < 2) goto truncated; asn1len = ntohs(get_uint16(cp)); cp += 2; if ((size_t)(end-cp) < asn1len) goto truncated; result->pk = crypto_pk_asn1_decode(cp, asn1len); if (!result->pk) goto truncated; cp += asn1len; if (end-cp < 4) goto truncated; result->timestamp = (time_t) ntohl(get_uint32(cp)); cp += 4; result->protocols = 1<<2; /* always use intro format 2 */ if (end-cp < 2) goto truncated; result->n_intro_points = ntohs(get_uint16(cp)); cp += 2; if (result->n_intro_points != 0) { result->intro_points = tor_malloc_zero(sizeof(char*)*result->n_intro_points); for (i=0;in_intro_points;++i) { if (end-cp < 2) goto truncated; eos = (const char *)memchr(cp,'\0',end-cp); if (!eos) goto truncated; result->intro_points[i] = tor_strdup(cp); cp = eos+1; } } keylen = crypto_pk_keysize(result->pk); tor_assert(end-cp >= 0); if ((size_t)(end-cp) < keylen) goto truncated; if ((size_t)(end-cp) > keylen) { log_warn(LD_PROTOCOL, "Signature is %d bytes too long on service descriptor.", (int)((size_t)(end-cp) - keylen)); goto error; } note_crypto_pk_op(REND_CLIENT); if (crypto_pk_public_checksig_digest(result->pk, (char*)str,cp-str, /* data */ (char*)cp,end-cp /* signature*/ )<0) { log_warn(LD_PROTOCOL, "Bad signature on service descriptor."); goto error; } return result; truncated: log_warn(LD_PROTOCOL, "Truncated service descriptor."); error: rend_service_descriptor_free(result); return NULL; } /** Sets out to the first 10 bytes of the digest of pk, * base32 encoded. NUL-terminates out. (We use this string to * identify services in directory requests and .onion URLs.) */ int rend_get_service_id(crypto_pk_env_t *pk, char *out) { char buf[DIGEST_LEN]; tor_assert(pk); if (crypto_pk_get_digest(pk, buf) < 0) return -1; base32_encode(out, REND_SERVICE_ID_LEN_BASE32+1, buf, REND_SERVICE_ID_LEN); return 0; } /* ==== Rendezvous service descriptor cache. */ /** How old do we let hidden service descriptors get before discarding * them as too old? */ #define REND_CACHE_MAX_AGE (2*24*60*60) /** How wrong do we assume our clock may be when checking whether hidden * services are too old or too new? */ #define REND_CACHE_MAX_SKEW (24*60*60) /** Map from service id (as generated by rend_get_service_id) to * rend_cache_entry_t. */ static strmap_t *rend_cache = NULL; /** Map from descriptor id to rend_cache_entry_t; only for hidden service * directories. */ static digestmap_t *rend_cache_v2_dir = NULL; /** Initializes the service descriptor cache. */ void rend_cache_init(void) { rend_cache = strmap_new(); rend_cache_v2_dir = digestmap_new(); } /** Helper: free storage held by a single service descriptor cache entry. */ static void _rend_cache_entry_free(void *p) { rend_cache_entry_t *e = p; rend_service_descriptor_free(e->parsed); tor_free(e->desc); tor_free(e); } /** Free all storage held by the service descriptor cache. */ void rend_cache_free_all(void) { strmap_free(rend_cache, _rend_cache_entry_free); digestmap_free(rend_cache_v2_dir, _rend_cache_entry_free); rend_cache = NULL; rend_cache_v2_dir = NULL; } /** Removes all old entries from the service descriptor cache. */ void rend_cache_clean(void) { strmap_iter_t *iter; const char *key; void *val; rend_cache_entry_t *ent; time_t cutoff; cutoff = time(NULL) - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW; for (iter = strmap_iter_init(rend_cache); !strmap_iter_done(iter); ) { strmap_iter_get(iter, &key, &val); ent = (rend_cache_entry_t*)val; if (ent->parsed->timestamp < cutoff) { iter = strmap_iter_next_rmv(rend_cache, iter); _rend_cache_entry_free(ent); } else { iter = strmap_iter_next(rend_cache, iter); } } } /** Remove all old v2 descriptors and those for which this hidden service * directory is not responsible for any more. */ void rend_cache_clean_v2_descs_as_dir(void) { digestmap_iter_t *iter; time_t cutoff = time(NULL) - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW; for (iter = digestmap_iter_init(rend_cache_v2_dir); !digestmap_iter_done(iter); ) { const char *key; void *val; rend_cache_entry_t *ent; digestmap_iter_get(iter, &key, &val); ent = val; if (ent->parsed->timestamp < cutoff || !hid_serv_responsible_for_desc_id(key)) { char key_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; base32_encode(key_base32, sizeof(key_base32), key, DIGEST_LEN); log_info(LD_REND, "Removing descriptor with ID '%s' from cache", key_base32); iter = digestmap_iter_next_rmv(rend_cache_v2_dir, iter); _rend_cache_entry_free(ent); } else { iter = digestmap_iter_next(rend_cache_v2_dir, iter); } } } /** Determines whether a is in the interval of b (excluded) and * c (included) in a circular digest ring; returns 1 if this is the * case, and 0 otherwise. */ int rend_id_is_in_interval(const char *a, const char *b, const char *c) { int a_b, b_c, c_a; tor_assert(a); tor_assert(b); tor_assert(c); /* There are five cases in which a is outside the interval ]b,c]: */ a_b = memcmp(a,b,DIGEST_LEN); if (a_b == 0) return 0; /* 1. a == b (b is excluded) */ b_c = memcmp(b,c,DIGEST_LEN); if (b_c == 0) return 0; /* 2. b == c (interval is empty) */ else if (a_b <= 0 && b_c < 0) return 0; /* 3. a b c */ c_a = memcmp(c,a,DIGEST_LEN); if (c_a < 0 && a_b <= 0) return 0; /* 4. c a b */ else if (b_c < 0 && c_a < 0) return 0; /* 5. b c a */ /* In the other cases (a c b; b a c; c b a), a is inside the interval. */ return 1; } /** Return true iff query is a syntactically valid service ID (as * generated by rend_get_service_id). */ int rend_valid_service_id(const char *query) { if (strlen(query) != REND_SERVICE_ID_LEN_BASE32) return 0; if (strspn(query, BASE32_CHARS) != REND_SERVICE_ID_LEN_BASE32) return 0; return 1; } /** If we have a cached rend_cache_entry_t for the service ID query * with version, set *e to that entry and return 1. * Else return 0. If version is nonnegative, only return an entry * in that descriptor format version. Otherwise (if version is * negative), return the most recent format we have. */ int rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e) { char key[REND_SERVICE_ID_LEN_BASE32+2]; /* \0 */ tor_assert(rend_cache); if (!rend_valid_service_id(query)) return -1; *e = NULL; if (version != 0) { tor_snprintf(key, sizeof(key), "2%s", query); *e = strmap_get_lc(rend_cache, key); } if (!*e && version != 2) { tor_snprintf(key, sizeof(key), "0%s", query); *e = strmap_get_lc(rend_cache, key); } if (!*e) return 0; return 1; } /** query is a base-32'ed service id. If it's malformed, return -1. * Else look it up. * - If it is found, point *desc to it, and write its length into * *desc_len, and return 1. * - If it is not found, return 0. * Note: calls to rend_cache_clean or rend_cache_store may invalidate * *desc. */ int rend_cache_lookup_desc(const char *query, int version, const char **desc, size_t *desc_len) { rend_cache_entry_t *e; int r; r = rend_cache_lookup_entry(query,version,&e); if (r <= 0) return r; *desc = e->desc; *desc_len = e->len; return 1; } /** Lookup the v2 service descriptor with base32-encoded desc_id and * copy the pointer to it to *desc. Return 1 on success, 0 on * well-formed-but-not-found, and -1 on failure. */ int rend_cache_lookup_v2_desc_as_dir(const char *desc_id, const char **desc) { rend_cache_entry_t *e; char desc_id_digest[DIGEST_LEN]; tor_assert(rend_cache_v2_dir); if (base32_decode(desc_id_digest, DIGEST_LEN, desc_id, REND_DESC_ID_V2_LEN_BASE32) < 0) { log_warn(LD_REND, "Descriptor ID contains illegal characters: %s", desc_id); return -1; } /* Determine if we are responsible. */ if (hid_serv_responsible_for_desc_id(desc_id_digest) < 0) { log_info(LD_REND, "Could not answer fetch request for v2 descriptor; " "either we are no hidden service directory, or we are " "not responsible for the requested ID."); return -1; } /* Lookup descriptor and return. */ e = digestmap_get(rend_cache_v2_dir, desc_id_digest); if (e) { *desc = e->desc; return 1; } return 0; } /** Parse *desc, calculate its service id, and store it in the cache. * If we have a newer descriptor with the same ID, ignore this one. * If we have an older descriptor with the same ID, replace it. * Return -1 if it's malformed or otherwise rejected; return 0 if * it's the same or older than one we've already got; return 1 if * it's novel. The published flag tells us if we store the descriptor * in our role as directory (1) or if we cache it as client (0). */ int rend_cache_store(const char *desc, size_t desc_len, int published) { rend_cache_entry_t *e; rend_service_descriptor_t *parsed; char query[REND_SERVICE_ID_LEN_BASE32+1]; char key[REND_SERVICE_ID_LEN_BASE32+2]; /* 0\0 */ time_t now; or_options_t *options = get_options(); tor_assert(rend_cache); parsed = rend_parse_service_descriptor(desc,desc_len); if (!parsed) { log_warn(LD_PROTOCOL,"Couldn't parse service descriptor."); return -1; } if (rend_get_service_id(parsed->pk, query)<0) { log_warn(LD_BUG,"Couldn't compute service ID."); rend_service_descriptor_free(parsed); return -1; } tor_snprintf(key, sizeof(key), "0%s", query); now = time(NULL); if (parsed->timestamp < now-REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) { log_fn(LOG_PROTOCOL_WARN, LD_REND, "Service descriptor %s is too old.", safe_str(query)); rend_service_descriptor_free(parsed); return -1; } if (parsed->timestamp > now+REND_CACHE_MAX_SKEW) { log_fn(LOG_PROTOCOL_WARN, LD_REND, "Service descriptor %s is too far in the future.", safe_str(query)); rend_service_descriptor_free(parsed); return -1; } /* report novel publication to statistics */ if (published && options->HSAuthorityRecordStats) { hs_usage_note_publish_total(query, now); } e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, key); if (e && e->parsed->timestamp > parsed->timestamp) { log_info(LD_REND,"We already have a newer service descriptor %s with the " "same ID and version.", safe_str(query)); rend_service_descriptor_free(parsed); return 0; } if (e && e->len == desc_len && !memcmp(desc,e->desc,desc_len)) { log_info(LD_REND,"We already have this service descriptor %s.", safe_str(query)); e->received = time(NULL); rend_service_descriptor_free(parsed); return 0; } if (!e) { e = tor_malloc_zero(sizeof(rend_cache_entry_t)); strmap_set_lc(rend_cache, key, e); /* report novel publication to statistics */ if (published && options->HSAuthorityRecordStats) { hs_usage_note_publish_novel(query, now); } } else { rend_service_descriptor_free(e->parsed); tor_free(e->desc); } e->received = time(NULL); e->parsed = parsed; e->len = desc_len; e->desc = tor_malloc(desc_len); memcpy(e->desc, desc, desc_len); log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.", safe_str(query), (int)desc_len); return 1; } /** Parse the v2 service descriptor(s) in desc and store it/them to the * local rend cache. Don't attempt to decrypt the included list of introduction * points (as we don't have a descriptor cookie for it). * * If we have a newer descriptor with the same ID, ignore this one. * If we have an older descriptor with the same ID, replace it. * Return -1 if it's malformed or otherwise rejected; return 0 if * it's the same or older than one we've already got; return 1 if * it's novel. */ int rend_cache_store_v2_desc_as_dir(const char *desc) { rend_service_descriptor_t *parsed; char desc_id[DIGEST_LEN]; char *intro_content; size_t intro_size; size_t encoded_size; char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; int number_stored = 0; const char *current_desc = desc; const char *next_desc; rend_cache_entry_t *e; time_t now = time(NULL); tor_assert(rend_cache_v2_dir); tor_assert(desc); if (!hid_serv_acting_as_directory()) { /* Cannot store descs, because we are (currently) not acting as * hidden service directory. */ log_info(LD_REND, "Cannot store descs: Not acting as hs dir"); return -1; } while (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content, &intro_size, &encoded_size, &next_desc, current_desc) >= 0) { /* We don't care about the introduction points. */ tor_free(intro_content); /* For pretty log statements. */ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, DIGEST_LEN); /* Is desc ID in the range that we are (directly or indirectly) responsible * for? */ if (!hid_serv_responsible_for_desc_id(desc_id)) { log_info(LD_REND, "Service descriptor with desc ID %s is not in " "interval that we are responsible for.", desc_id_base32); goto skip; } /* Is descriptor too old? */ if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) { log_info(LD_REND, "Service descriptor with desc ID %s is too old.", desc_id_base32); goto skip; } /* Is descriptor too far in the future? */ if (parsed->timestamp > now + REND_CACHE_MAX_SKEW) { log_info(LD_REND, "Service descriptor with desc ID %s is too far in the " "future.", desc_id_base32); goto skip; } /* Do we already have a newer descriptor? */ e = digestmap_get(rend_cache_v2_dir, desc_id); if (e && e->parsed->timestamp > parsed->timestamp) { log_info(LD_REND, "We already have a newer service descriptor with the " "same desc ID %s and version.", desc_id_base32); goto skip; } /* Do we already have this descriptor? */ if (e && !strcmp(desc, e->desc)) { log_info(LD_REND, "We already have this service descriptor with desc " "ID %s.", desc_id_base32); e->received = time(NULL); goto skip; } /* Store received descriptor. */ if (!e) { e = tor_malloc_zero(sizeof(rend_cache_entry_t)); digestmap_set(rend_cache_v2_dir, desc_id, e); } else { rend_service_descriptor_free(e->parsed); tor_free(e->desc); } e->received = time(NULL); e->parsed = parsed; e->desc = tor_strndup(current_desc, encoded_size); e->len = encoded_size; log_info(LD_REND, "Successfully stored service descriptor with desc ID " "'%s' and len %d.", desc_id_base32, (int)encoded_size); number_stored++; goto advance; skip: rend_service_descriptor_free(parsed); advance: /* advance to next descriptor, if available. */ current_desc = next_desc; /* check if there is a next descriptor. */ if (!current_desc || strcmpstart(current_desc, "rendezvous-service-descriptor ")) break; } log_info(LD_REND, "Parsed and added %d descriptor%s.", number_stored, number_stored != 1 ? "s" : ""); return number_stored; } /** Parse the v2 service descriptor in desc, decrypt the included list * of introduction points with descriptor_cookie (which may also be * NULL if decryption is not necessary), and store the descriptor to * the local cache under its version and service id. * * If we have a newer descriptor with the same ID, ignore this one. * If we have an older descriptor with the same ID, replace it. * Return -1 if it's malformed or otherwise rejected; return 0 if * it's the same or older than one we've already got; return 1 if * it's novel. */ int rend_cache_store_v2_desc_as_client(const char *desc, const char *descriptor_cookie) { /*XXXX this seems to have a bit of duplicate code with * rend_cache_store_v2_desc_as_dir(). Fix that. */ /* Though having similar elements, both functions were separated on * purpose: * - dirs don't care about encoded/encrypted introduction points, clients * do. * - dirs store descriptors in a separate cache by descriptor ID, whereas * clients store them by service ID; both caches are different data * structures and have different access methods. * - dirs store a descriptor only if they are responsible for its ID, * clients do so in every way (because they have requested it before). * - dirs can process multiple concatenated descriptors which is required * for replication, whereas clients only accept a single descriptor. * Thus, combining both methods would result in a lot of if statements * which probably would not improve, but worsen code readability. -KL */ rend_service_descriptor_t *parsed = NULL; char desc_id[DIGEST_LEN]; char *intro_content = NULL; size_t intro_size; size_t encoded_size; const char *next_desc; time_t now = time(NULL); char key[REND_SERVICE_ID_LEN_BASE32+2]; char service_id[REND_SERVICE_ID_LEN_BASE32+1]; rend_cache_entry_t *e; tor_assert(rend_cache); tor_assert(desc); /* Parse the descriptor. */ if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content, &intro_size, &encoded_size, &next_desc, desc) < 0) { if (parsed) rend_service_descriptor_free(parsed); tor_free(intro_content); log_warn(LD_REND, "Could not parse descriptor."); return -1; } /* Compute service ID from public key. */ if (rend_get_service_id(parsed->pk, service_id)<0) { log_warn(LD_REND, "Couldn't compute service ID."); rend_service_descriptor_free(parsed); tor_free(intro_content); return -1; } /* Decode/decrypt introduction points. */ if (intro_content) { if (rend_decrypt_introduction_points(parsed, descriptor_cookie, intro_content, intro_size) < 0) { log_warn(LD_PROTOCOL,"Couldn't decode/decrypt introduction points."); rend_service_descriptor_free(parsed); tor_free(intro_content); return -1; } } else { parsed->n_intro_points = 0; } /* We don't need the encoded/encrypted introduction points any longer. */ tor_free(intro_content); /* Is descriptor too old? */ if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) { log_warn(LD_REND, "Service descriptor with service ID %s is too old.", service_id); rend_service_descriptor_free(parsed); return -1; } /* Is descriptor too far in the future? */ if (parsed->timestamp > now + REND_CACHE_MAX_SKEW) { log_warn(LD_REND, "Service descriptor with service ID %s is too far in " "the future.", service_id); rend_service_descriptor_free(parsed); return -1; } /* Do we already have a newer descriptor? */ tor_snprintf(key, sizeof(key), "2%s", service_id); e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, key); if (e && e->parsed->timestamp > parsed->timestamp) { log_info(LD_REND, "We already have a newer service descriptor for " "service ID %s with the same desc ID and version.", service_id); rend_service_descriptor_free(parsed); return 0; } /* Do we already have this descriptor? */ if (e && !strcmp(desc, e->desc)) { log_info(LD_REND,"We already have this service descriptor %s.", service_id); e->received = time(NULL); rend_service_descriptor_free(parsed); return 0; } if (!e) { e = tor_malloc_zero(sizeof(rend_cache_entry_t)); strmap_set_lc(rend_cache, key, e); } else { rend_service_descriptor_free(e->parsed); tor_free(e->desc); } e->received = time(NULL); e->parsed = parsed; e->desc = tor_malloc_zero(encoded_size + 1); strlcpy(e->desc, desc, encoded_size + 1); e->len = encoded_size; log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.", service_id, (int)encoded_size); return 1; } /** Called when we get a rendezvous-related relay cell on circuit * circ. Dispatch on rendezvous relay command. */ void rend_process_relay_cell(circuit_t *circ, int command, size_t length, const char *payload) { or_circuit_t *or_circ = NULL; origin_circuit_t *origin_circ = NULL; int r = -2; if (CIRCUIT_IS_ORIGIN(circ)) origin_circ = TO_ORIGIN_CIRCUIT(circ); else or_circ = TO_OR_CIRCUIT(circ); switch (command) { case RELAY_COMMAND_ESTABLISH_INTRO: if (or_circ) r = rend_mid_establish_intro(or_circ,payload,length); break; case RELAY_COMMAND_ESTABLISH_RENDEZVOUS: if (or_circ) r = rend_mid_establish_rendezvous(or_circ,payload,length); break; case RELAY_COMMAND_INTRODUCE1: if (or_circ) r = rend_mid_introduce(or_circ,payload,length); break; case RELAY_COMMAND_INTRODUCE2: if (origin_circ) r = rend_service_introduce(origin_circ,payload,length); break; case RELAY_COMMAND_INTRODUCE_ACK: if (origin_circ) r = rend_client_introduction_acked(origin_circ,payload,length); break; case RELAY_COMMAND_RENDEZVOUS1: if (or_circ) r = rend_mid_rendezvous(or_circ,payload,length); break; case RELAY_COMMAND_RENDEZVOUS2: if (origin_circ) r = rend_client_receive_rendezvous(origin_circ,payload,length); break; case RELAY_COMMAND_INTRO_ESTABLISHED: if (origin_circ) r = rend_service_intro_established(origin_circ,payload,length); break; case RELAY_COMMAND_RENDEZVOUS_ESTABLISHED: if (origin_circ) r = rend_client_rendezvous_acked(origin_circ,payload,length); break; default: tor_fragile_assert(); } if (r == -2) log_info(LD_PROTOCOL, "Dropping cell (type %d) for wrong circuit type.", command); } /** Return the number of entries in our rendezvous descriptor cache. */ int rend_cache_size(void) { return strmap_size(rend_cache); }