diff options
-rw-r--r-- | src/or/control.c | 63 | ||||
-rw-r--r-- | src/or/or.h | 4 | ||||
-rw-r--r-- | src/or/rendclient.c | 276 | ||||
-rw-r--r-- | src/or/rendclient.h | 2 | ||||
-rw-r--r-- | src/or/rendcommon.c | 8 |
5 files changed, 254 insertions, 99 deletions
diff --git a/src/or/control.c b/src/or/control.c index e7d8b5cdef..9258678e02 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -37,6 +37,7 @@ #include "nodelist.h" #include "policies.h" #include "reasons.h" +#include "rendclient.h" #include "rephist.h" #include "router.h" #include "routerlist.h" @@ -3266,6 +3267,7 @@ handle_control_hsfetch(control_connection_t *conn, uint32_t len, (void) len; /* body is nul-terminated; it's safe to ignore the length */ static const char *v2_str = "v2-"; const size_t v2_str_len = strlen(v2_str); + rend_data_t *rend_query = NULL; /* Make sure we have at least one argument, the HSAddress. */ args = getargs_helper("HSFETCH", conn, body, 1, -1); @@ -3273,10 +3275,10 @@ handle_control_hsfetch(control_connection_t *conn, uint32_t len, goto done; } - /* Extract the HS address that should NOT contain the .onion part. */ + /* Extract the first argument (either HSAddress or DescID). */ arg1 = smartlist_get(args, 0); - /* Remove the HS address from the argument list so we can safely iterate - * on all the rest to find optional argument(s). */ + /* Remove it from the argument list so we can safely iterate on all the + * rest to find optional argument(s). */ smartlist_del(args, 0); /* Test if it's an HS address without the .onion part. */ if (strlen(arg1) == REND_SERVICE_ID_LEN_BASE32 && @@ -3287,40 +3289,42 @@ handle_control_hsfetch(control_connection_t *conn, uint32_t len, strlen(arg1 + v2_str_len) == REND_DESC_ID_V2_LEN_BASE32 && base32_decode(digest, sizeof(digest), arg1 + v2_str_len, REND_DESC_ID_V2_LEN_BASE32) == 0) { - /* We have a well formed version 2 descriptor ID. */ - desc_id = arg1 + v2_str_len; + /* We have a well formed version 2 descriptor ID. Keep the decoded value + * of the id. */ + desc_id = digest; } else { connection_printf_to_buf(conn, "552 Unrecognized \"%s\"\r\n", arg1); goto done; } - /* Stores routerstatus_t object for each specified server. */ - hsdirs = smartlist_new(); - /* Skip first argument because it's the HSAddress. */ SMARTLIST_FOREACH_BEGIN(args, char *, arg) { const node_t *node; static const char *opt_server = "SERVER="; if (!strcasecmpstart(arg, opt_server)) { + char id[DIGEST_LEN] = {0}; const char *server; - memset(digest, 0, sizeof(digest)); server = arg + strlen(opt_server); - /* Is the server fingerprint valid?. */ + /* Is the server's fingerprint valid?. */ if (!string_is_hex(server) || strlen(server) != HEX_DIGEST_LEN || - base16_decode(digest, sizeof(digest), server, HEX_DIGEST_LEN)) { + base16_decode(id, sizeof(id), server, HEX_DIGEST_LEN)) { connection_printf_to_buf(conn, "552 Invalid fingerprint \"%s\"\r\n", server); goto done; } - node = node_get_by_id(digest); + node = node_get_by_id(id); if (!node) { connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n", server); goto done; } + if (!hsdirs) { + /* Stores routerstatus_t object for each specified server. */ + hsdirs = smartlist_new(); + } /* Valid server, add it to our local list. */ smartlist_add(hsdirs, node->rs); } else { @@ -3330,21 +3334,44 @@ handle_control_hsfetch(control_connection_t *conn, uint32_t len, } } SMARTLIST_FOREACH_END(arg); - /* XXX: Actually trigger the fetch(es). */ - (void) hsaddress; - (void) desc_id; - (void) hsdirs; + rend_query = tor_malloc_zero(sizeof(*rend_query)); - /* All good, thanks and come again! */ + if (hsaddress) { + strncpy(rend_query->onion_address, hsaddress, + sizeof(rend_query->onion_address)); + } else if (desc_id) { + /* Using a descriptor ID, we force the user to provide at least one + * hsdir server using the SERVER= option. */ + if (!hsdirs || !smartlist_len(hsdirs)) { + connection_printf_to_buf(conn, "552 SERVER= option is required\r\n"); + goto done; + } + memcpy(rend_query->descriptor_id, desc_id, + sizeof(rend_query->descriptor_id)); + } else { + /* We can't get in here because of the first argument check. */ + tor_assert(0); + } + /* We are about to trigger HSDir fetch so send the OK now because after + * that 650 event(s) are possible so better to have the 250 OK before them + * to avoid out of order replies. */ send_control_done(conn); + /* Trigger the fetch using the built rend query and possibly a lit of HS + * directory to use. This function ignores the client cache thus this will + * always send a fetch command. */ + rend_client_fetch_v2_desc(rend_query, hsdirs); + done: + if (hsdirs) { + smartlist_free(hsdirs); + } if (args) { SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); smartlist_free(args); } + tor_free(rend_query); tor_free(arg1); - smartlist_free(hsdirs); return 0; } diff --git a/src/or/or.h b/src/or/or.h index d548aeabb6..39c5ed6023 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -799,6 +799,10 @@ typedef struct rend_data_t { /** Authorization type for accessing a service used by a client. */ rend_auth_type_t auth_type; + /** Descriptor ID for a client request. The control port command HSFETCH + * can use this. */ + char descriptor_id[DIGEST_LEN]; + /** Hash of the hidden service's PK used by a service. */ char rend_pk_digest[DIGEST_LEN]; diff --git a/src/or/rendclient.c b/src/or/rendclient.c index af08494b9c..7941ba7372 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -607,14 +607,12 @@ rend_client_purge_last_hid_serv_requests(void) } } -/** Determine the responsible hidden service directories for <b>desc_id</b> - * and fetch the descriptor with that ID from one of them. Only - * send a request to a hidden service directory that we have not yet tried - * during this attempt to connect to this hidden service; on success, return 1, - * in the case that no hidden service directory is left to ask for the - * descriptor, return 0, and in case of a failure -1. */ -static int -directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query) +/** This returns a good valid hs dir that should be used for the given + * descriptor id. + * + * Return NULL on error else the hsdir node pointer. */ +static routerstatus_t * +pick_hsdir(const char *desc_id) { smartlist_t *responsible_dirs = smartlist_new(); smartlist_t *usable_responsible_dirs = smartlist_new(); @@ -622,49 +620,43 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query) routerstatus_t *hs_dir; char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; time_t now = time(NULL); - char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64]; -#ifdef ENABLE_TOR2WEB_MODE - const int tor2web_mode = options->Tor2webMode; - const int how_to_fetch = tor2web_mode ? DIRIND_ONEHOP : DIRIND_ANONYMOUS; -#else - const int how_to_fetch = DIRIND_ANONYMOUS; -#endif int excluded_some; + tor_assert(desc_id); - tor_assert(rend_query); - /* Determine responsible dirs. Even if we can't get all we want, - * work with the ones we have. If it's empty, we'll notice below. */ - hid_serv_get_responsible_directories(responsible_dirs, desc_id); base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, DIGEST_LEN); - /* Only select those hidden service directories to which we did not send - * a request recently and for which we have a router descriptor here. */ + /* Determine responsible dirs. Even if we can't get all we want, work with + * the ones we have. If it's empty, we'll notice below. */ + hid_serv_get_responsible_directories(responsible_dirs, desc_id); /* Clean request history first. */ directory_clean_last_hid_serv_requests(now); - SMARTLIST_FOREACH(responsible_dirs, routerstatus_t *, dir, { - time_t last = lookup_last_hid_serv_request( - dir, desc_id_base32, 0, 0); - const node_t *node = node_get_by_id(dir->identity_digest); - if (last + REND_HID_SERV_DIR_REQUERY_PERIOD >= now || - !node || !node_has_descriptor(node)) { - SMARTLIST_DEL_CURRENT(responsible_dirs, dir); - continue; - } - if (! routerset_contains_node(options->ExcludeNodes, node)) { - smartlist_add(usable_responsible_dirs, dir); - } - }); + /* Only select those hidden service directories to which we did not send a + * request recently and for which we have a router descriptor here. */ + SMARTLIST_FOREACH_BEGIN(responsible_dirs, routerstatus_t *, dir) { + time_t last = lookup_last_hid_serv_request(dir, desc_id_base32, + 0, 0); + const node_t *node = node_get_by_id(dir->identity_digest); + if (last + REND_HID_SERV_DIR_REQUERY_PERIOD >= now || + !node || !node_has_descriptor(node)) { + SMARTLIST_DEL_CURRENT(responsible_dirs, dir); + continue; + } + if (!routerset_contains_node(options->ExcludeNodes, node)) { + smartlist_add(usable_responsible_dirs, dir); + } + } SMARTLIST_FOREACH_END(dir); excluded_some = smartlist_len(usable_responsible_dirs) < smartlist_len(responsible_dirs); hs_dir = smartlist_choose(usable_responsible_dirs); - if (! hs_dir && ! options->StrictNodes) + if (!hs_dir && !options->StrictNodes) { hs_dir = smartlist_choose(responsible_dirs); + } smartlist_free(responsible_dirs); smartlist_free(usable_responsible_dirs); @@ -677,14 +669,52 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query) "requested hidden service: they are all either down or " "excluded, and StrictNodes is set."); } - return 0; + } else { + /* Remember that we are requesting a descriptor from this hidden service + * directory now. */ + lookup_last_hid_serv_request(hs_dir, desc_id_base32, now, 1); } - /* Remember that we are requesting a descriptor from this hidden service - * directory now. */ - lookup_last_hid_serv_request(hs_dir, desc_id_base32, now, 1); + return hs_dir; +} + +/** Determine the responsible hidden service directories for <b>desc_id</b> + * and fetch the descriptor with that ID from one of them. Only + * send a request to a hidden service directory that we have not yet tried + * during this attempt to connect to this hidden service; on success, return 1, + * in the case that no hidden service directory is left to ask for the + * descriptor, return 0, and in case of a failure -1. */ +static int +directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query, + routerstatus_t *rs_hsdir) +{ + routerstatus_t *hs_dir = rs_hsdir; + char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64]; +#ifdef ENABLE_TOR2WEB_MODE + const int tor2web_mode = get_options()->Tor2webMode; + const int how_to_fetch = tor2web_mode ? DIRIND_ONEHOP : DIRIND_ANONYMOUS; +#else + const int how_to_fetch = DIRIND_ANONYMOUS; +#endif + + tor_assert(desc_id); + + base32_encode(desc_id_base32, sizeof(desc_id_base32), + desc_id, DIGEST_LEN); - /* Encode descriptor cookie for logging purposes. */ + /* Automatically pick an hs dir if none given. */ + if (!rs_hsdir) { + hs_dir = pick_hsdir(desc_id); + if (!hs_dir) { + /* No suitable hs dir can be found, stop right now. */ + return 0; + } + } + + /* Encode descriptor cookie for logging purposes. Also, if the cookie is + * malformed, no fetch is triggered thus this needs to be done before the + * fetch request. */ if (rend_query->auth_type != REND_NO_AUTH) { if (base64_encode(descriptor_cookie_base64, sizeof(descriptor_cookie_base64), @@ -724,16 +754,136 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query) return 1; } +/** Fetch a v2 descriptor using the given descriptor id. If any hsdir(s) are + * given, they will be used instead. + * + * On success, 1 is returned. If no hidden service is left to ask, return 0. + * On error, -1 is returned. */ +static int +fetch_v2_desc_by_descid(const char *desc_id, const rend_data_t *rend_query, + smartlist_t *hsdirs) +{ + int ret; + + tor_assert(rend_query); + + if (!hsdirs) { + ret = directory_get_from_hs_dir(desc_id, rend_query, NULL); + goto end; /* either success or failure, but we're done */ + } + + /* Using the given hsdir list, trigger a fetch on each of them. */ + SMARTLIST_FOREACH_BEGIN(hsdirs, routerstatus_t *, hs_dir) { + /* This should always be a success. */ + ret = directory_get_from_hs_dir(desc_id, rend_query, hs_dir); + tor_assert(ret); + } SMARTLIST_FOREACH_END(hs_dir); + + /* Everything went well. */ + ret = 0; + +end: + return ret; +} + +/** Fetch a v2 descriptor using the onion address in the given query object. + * This will compute the descriptor id for each replicas and fetch it on the + * given hsdir(s) if any or the responsible ones that are choosen + * automatically. + * + * On success, 1 is returned. If no hidden service is left to ask, return 0. + * On error, -1 is returned. */ +static int +fetch_v2_desc_by_addr(const rend_data_t *query, + smartlist_t *hsdirs) +{ + char descriptor_id[DIGEST_LEN]; + int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS]; + int i, tries_left, ret; + + tor_assert(query); + + /* Randomly iterate over the replicas until a descriptor can be fetched + * from one of the consecutive nodes, or no options are left. */ + for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++) { + replicas_left_to_try[i] = i; + } + + tries_left = REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; + while (tries_left > 0) { + int rand = crypto_rand_int(tries_left); + int chosen_replica = replicas_left_to_try[rand]; + replicas_left_to_try[rand] = replicas_left_to_try[--tries_left]; + + ret = rend_compute_v2_desc_id(descriptor_id, query->onion_address, + query->auth_type == REND_STEALTH_AUTH ? + query->descriptor_cookie : NULL, + time(NULL), chosen_replica); + if (ret < 0) { + /* Normally, on failure the descriptor_id is untouched but let's be + * safe in general in case the function changes at some point. */ + goto end; + } + + /* Trigger the fetch with the computed descriptor ID. */ + ret = fetch_v2_desc_by_descid(descriptor_id, query, hsdirs); + if (ret != 0) { + /* Either on success or failure, as long as we tried a fetch we are + * done here. */ + goto end; + } + } + + /* If we come here, there are no hidden service directories left. */ + log_info(LD_REND, "Could not pick one of the responsible hidden " + "service directories to fetch descriptors, because " + "we already tried them all unsuccessfully."); + ret = 0; + +end: + memwipe(descriptor_id, 0, sizeof(descriptor_id)); + return ret; +} + +/** Fetch a v2 descriptor using the given query. If any hsdir are specified, + * use them for the fetch. + * + * On success, 1 is returned. If no hidden service is left to ask, return 0. + * On error, -1 is returned. */ +int +rend_client_fetch_v2_desc(const rend_data_t *query, + smartlist_t *hsdirs) +{ + int ret; + + tor_assert(query); + + /* Depending on what's available in the rend data query object, we will + * trigger a fetch by HS address or using a descriptor ID. */ + + if (query->onion_address[0] != '\0') { + ret = fetch_v2_desc_by_addr(query, hsdirs); + } else if (query->descriptor_id[0] != '\0') { + ret = fetch_v2_desc_by_descid(query->descriptor_id, query, hsdirs); + } else { + /* Query data is invalid. */ + ret = -1; + goto error; + } + +error: + return ret; +} + /** Unless we already have a descriptor for <b>rend_query</b> with at least * one (possibly) working introduction point in it, start a connection to a * hidden service directory to fetch a v2 rendezvous service descriptor. */ void rend_client_refetch_v2_renddesc(const rend_data_t *rend_query) { - char descriptor_id[DIGEST_LEN]; - int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS]; - int i, tries_left; + int ret; rend_cache_entry_t *e = NULL; + tor_assert(rend_query); /* Are we configured to fetch descriptors? */ if (!get_options()->FetchHidServDescriptors) { @@ -750,44 +900,12 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query) } log_debug(LD_REND, "Fetching v2 rendezvous descriptor for service %s", safe_str_client(rend_query->onion_address)); - /* Randomly iterate over the replicas until a descriptor can be fetched - * from one of the consecutive nodes, or no options are left. */ - tries_left = REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; - for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++) - replicas_left_to_try[i] = i; - while (tries_left > 0) { - int rand = crypto_rand_int(tries_left); - int chosen_replica = replicas_left_to_try[rand]; - replicas_left_to_try[rand] = replicas_left_to_try[--tries_left]; - if (rend_compute_v2_desc_id(descriptor_id, rend_query->onion_address, - rend_query->auth_type == REND_STEALTH_AUTH ? - rend_query->descriptor_cookie : NULL, - time(NULL), chosen_replica) < 0) { - log_warn(LD_REND, "Internal error: Computing v2 rendezvous " - "descriptor ID did not succeed."); - /* - * Hmm, can this write anything to descriptor_id and still fail? - * Let's clear it just to be safe. - * - * From here on, any returns should goto done which clears - * descriptor_id so we don't leave key-derived material on the stack. - */ - goto done; - } - if (directory_get_from_hs_dir(descriptor_id, rend_query) != 0) - goto done; /* either success or failure, but we're done */ + ret = rend_client_fetch_v2_desc(rend_query, NULL); + if (ret <= 0) { + /* Close pending connections on error or if no hsdir can be found. */ + rend_client_desc_trynow(rend_query->onion_address); } - /* If we come here, there are no hidden service directories left. */ - log_info(LD_REND, "Could not pick one of the responsible hidden " - "service directories to fetch descriptors, because " - "we already tried them all unsuccessfully."); - /* Close pending connections. */ - rend_client_desc_trynow(rend_query->onion_address); - - done: - memwipe(descriptor_id, 0, sizeof(descriptor_id)); - return; } diff --git a/src/or/rendclient.h b/src/or/rendclient.h index 098c61d0a1..d6fea678d0 100644 --- a/src/or/rendclient.h +++ b/src/or/rendclient.h @@ -20,6 +20,8 @@ int rend_client_introduction_acked(origin_circuit_t *circ, const uint8_t *request, size_t request_len); void rend_client_refetch_v2_renddesc(const rend_data_t *rend_query); +int rend_client_fetch_v2_desc(const rend_data_t *query, + smartlist_t *hsdirs); void rend_client_cancel_descriptor_fetches(void); void rend_client_purge_last_hid_serv_requests(void); diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index 3fea07f52a..3d9cc23af4 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -1174,6 +1174,9 @@ rend_cache_store_v2_desc_as_client(const char *desc, tor_assert(desc); tor_assert(desc_id_base32); memset(want_desc_id, 0, sizeof(want_desc_id)); + if (entry) { + *entry = NULL; + } if (base32_decode(want_desc_id, sizeof(want_desc_id), desc_id_base32, strlen(desc_id_base32)) != 0) { log_warn(LD_BUG, "Couldn't decode base32 %s for descriptor id.", @@ -1192,7 +1195,8 @@ rend_cache_store_v2_desc_as_client(const char *desc, log_warn(LD_REND, "Couldn't compute service ID."); goto err; } - if (strcmp(rend_query->onion_address, service_id)) { + if (rend_query->onion_address[0] != '\0' && + strcmp(rend_query->onion_address, service_id)) { log_warn(LD_REND, "Received service descriptor for service ID %s; " "expected descriptor for service ID %s.", service_id, safe_str(rend_query->onion_address)); @@ -1239,7 +1243,7 @@ rend_cache_store_v2_desc_as_client(const char *desc, "service descriptor for %s. This is probably a (misguided) " "attempt to improve reliability, but it could also be an " "attempt to do a guard enumeration attack. Rejecting.", - safe_str_client(rend_query->onion_address)); + safe_str_client(service_id)); goto err; } |