2007-12-12 22:09:01 +01:00
|
|
|
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
2011-01-03 17:50:39 +01:00
|
|
|
* Copyright (c) 2007-2011, The Tor Project, Inc. */
|
2004-04-03 00:23:15 +02:00
|
|
|
/* See LICENSE for licensing information */
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/**
|
|
|
|
* \file rendclient.c
|
2004-12-01 04:48:14 +01:00
|
|
|
* \brief Client code to access location-hidden services.
|
2004-05-09 18:47:25 +02:00
|
|
|
**/
|
2004-05-05 23:32:43 +02:00
|
|
|
|
2004-04-03 00:23:15 +02:00
|
|
|
#include "or.h"
|
2010-07-22 01:21:00 +02:00
|
|
|
#include "circuitbuild.h"
|
2010-07-22 09:46:23 +02:00
|
|
|
#include "circuitlist.h"
|
2010-07-22 10:03:40 +02:00
|
|
|
#include "circuituse.h"
|
2010-07-22 10:22:51 +02:00
|
|
|
#include "config.h"
|
2010-07-22 10:32:52 +02:00
|
|
|
#include "connection.h"
|
2010-07-22 10:43:02 +02:00
|
|
|
#include "connection_edge.h"
|
2010-07-22 11:54:50 +02:00
|
|
|
#include "directory.h"
|
2010-07-23 19:58:06 +02:00
|
|
|
#include "main.h"
|
Initial conversion to use node_t throughout our codebase.
A node_t is an abstraction over routerstatus_t, routerinfo_t, and
microdesc_t. It should try to present a consistent interface to all
of them. There should be a node_t for a server whenever there is
* A routerinfo_t for it in the routerlist
* A routerstatus_t in the current_consensus.
(note that a microdesc_t alone isn't enough to make a node_t exist,
since microdescriptors aren't usable on their own.)
There are three ways to get a node_t right now: looking it up by ID,
looking it up by nickname, and iterating over the whole list of
microdescriptors.
All (or nearly all) functions that are supposed to return "a router"
-- especially those used in building connections and circuits --
should return a node_t, not a routerinfo_t or a routerstatus_t.
A node_t should hold all the *mutable* flags about a node. This
patch moves the is_foo flags from routerinfo_t into node_t. The
flags in routerstatus_t remain, but they get set from the consensus
and should not change.
Some other highlights of this patch are:
* Looking up routerinfo and routerstatus by nickname is now
unified and based on the "look up a node by nickname" function.
This tries to look only at the values from current consensus,
and not get confused by the routerinfo_t->is_named flag, which
could get set for other weird reasons. This changes the
behavior of how authorities (when acting as clients) deal with
nodes that have been listed by nickname.
* I tried not to artificially increase the size of the diff here
by moving functions around. As a result, some functions that
now operate on nodes are now in the wrong file -- they should
get moved to nodelist.c once this refactoring settles down.
This moving should happen as part of a patch that moves
functions AND NOTHING ELSE.
* Some old code is now left around inside #if 0/1 blocks, and
should get removed once I've verified that I don't want it
sitting around to see how we used to do things.
There are still some unimplemented functions: these are flagged
with "UNIMPLEMENTED_NODELIST()." I'll work on filling in the
implementation here, piece by piece.
I wish this patch could have been smaller, but there did not seem to
be any piece of it that was independent from the rest. Moving flags
forces many functions that once returned routerinfo_t * to return
node_t *, which forces their friends to change, and so on.
2010-09-29 21:00:41 +02:00
|
|
|
#include "nodelist.h"
|
2010-07-23 21:53:11 +02:00
|
|
|
#include "relay.h"
|
2010-07-22 00:13:51 +02:00
|
|
|
#include "rendclient.h"
|
2010-07-21 17:52:54 +02:00
|
|
|
#include "rendcommon.h"
|
2010-07-23 22:57:20 +02:00
|
|
|
#include "rephist.h"
|
2011-05-16 03:58:46 +02:00
|
|
|
#include "router.h"
|
2010-07-21 17:08:11 +02:00
|
|
|
#include "routerlist.h"
|
2004-04-03 00:23:15 +02:00
|
|
|
|
2009-10-18 00:52:18 +02:00
|
|
|
static extend_info_t *rend_client_get_random_intro_impl(
|
2011-04-20 22:49:41 +02:00
|
|
|
const rend_cache_entry_t *rend_query,
|
|
|
|
const int strict, const int warnings);
|
2009-10-18 00:52:18 +02:00
|
|
|
|
2011-06-02 11:57:29 +02:00
|
|
|
/** Purge all potentially remotely-detectable state held in the hidden
|
|
|
|
* service client code. Called on SIGNAL NEWNYM. */
|
|
|
|
void
|
|
|
|
rend_client_purge_state(void)
|
|
|
|
{
|
|
|
|
rend_cache_purge();
|
|
|
|
rend_client_cancel_descriptor_fetches();
|
|
|
|
rend_client_purge_last_hid_serv_requests();
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Called when we've established a circuit to an introduction point:
|
2004-05-05 23:32:43 +02:00
|
|
|
* send the introduction request. */
|
2004-04-03 00:23:15 +02:00
|
|
|
void
|
2006-07-23 09:37:35 +02:00
|
|
|
rend_client_introcirc_has_opened(origin_circuit_t *circ)
|
2004-04-03 00:23:15 +02:00
|
|
|
{
|
2006-07-23 09:37:35 +02:00
|
|
|
tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
|
2004-10-17 00:14:52 +02:00
|
|
|
tor_assert(circ->cpath);
|
2004-04-03 00:23:15 +02:00
|
|
|
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,"introcirc is open");
|
2004-04-05 02:47:48 +02:00
|
|
|
connection_ap_attach_pending();
|
2004-04-03 00:23:15 +02:00
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Send the establish-rendezvous cell along a rendezvous circuit. if
|
2004-05-05 23:32:43 +02:00
|
|
|
* it fails, mark the circ for close and return -1. else return 0.
|
2004-04-05 09:41:31 +02:00
|
|
|
*/
|
2004-05-12 22:58:27 +02:00
|
|
|
static int
|
2006-07-23 09:37:35 +02:00
|
|
|
rend_client_send_establish_rendezvous(origin_circuit_t *circ)
|
2004-04-03 06:22:22 +02:00
|
|
|
{
|
2006-07-23 09:37:35 +02:00
|
|
|
tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND);
|
2008-09-24 16:44:29 +02:00
|
|
|
tor_assert(circ->rend_data);
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND, "Sending an ESTABLISH_RENDEZVOUS cell");
|
2004-04-03 06:22:22 +02:00
|
|
|
|
2008-09-24 16:44:29 +02:00
|
|
|
if (crypto_rand(circ->rend_data->rend_cookie, REND_COOKIE_LEN) < 0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG, "Internal error: Couldn't produce random cookie.");
|
2006-10-09 17:47:27 +02:00
|
|
|
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
|
2004-04-03 06:22:22 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2007-03-24 16:57:51 +01:00
|
|
|
if (relay_send_command_from_edge(0, TO_CIRCUIT(circ),
|
2004-04-03 06:22:22 +02:00
|
|
|
RELAY_COMMAND_ESTABLISH_RENDEZVOUS,
|
2008-09-24 16:44:29 +02:00
|
|
|
circ->rend_data->rend_cookie,
|
|
|
|
REND_COOKIE_LEN,
|
2004-04-03 06:22:22 +02:00
|
|
|
circ->cpath->prev)<0) {
|
2004-04-05 09:41:31 +02:00
|
|
|
/* circ is already marked for close */
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_GENERAL, "Couldn't send ESTABLISH_RENDEZVOUS cell");
|
2004-04-03 06:22:22 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-04-26 11:21:25 +02:00
|
|
|
/** Extend the introduction circuit <b>circ</b> to another valid
|
|
|
|
* introduction point for the hidden service it is trying to connect
|
|
|
|
* to, or mark it and launch a new circuit if we can't extend it.
|
|
|
|
* Return 0 on success. Return -1 and mark the introduction
|
|
|
|
* circuit on failure.
|
|
|
|
*
|
|
|
|
* On failure, the caller is responsible for marking the associated
|
|
|
|
* rendezvous circuit for close. */
|
|
|
|
static int
|
|
|
|
rend_client_reextend_intro_circuit(origin_circuit_t *circ)
|
|
|
|
{
|
|
|
|
extend_info_t *extend_info;
|
|
|
|
int result;
|
|
|
|
extend_info = rend_client_get_random_intro(circ->rend_data);
|
|
|
|
if (!extend_info) {
|
|
|
|
log_warn(LD_REND,
|
|
|
|
"No usable introduction points left for %s. Closing.",
|
|
|
|
safe_str_client(circ->rend_data->onion_address));
|
|
|
|
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (circ->remaining_relay_early_cells) {
|
|
|
|
log_info(LD_REND,
|
|
|
|
"Re-extending circ %d, this time to %s.",
|
2011-05-16 03:58:46 +02:00
|
|
|
circ->_base.n_circ_id,
|
|
|
|
safe_str_client(extend_info_describe(extend_info)));
|
2011-04-26 11:21:25 +02:00
|
|
|
result = circuit_extend_to_new_exit(circ, extend_info);
|
|
|
|
} else {
|
|
|
|
log_info(LD_REND,
|
|
|
|
"Building a new introduction circuit, this time to %s.",
|
2011-05-16 03:58:46 +02:00
|
|
|
safe_str_client(extend_info_describe(extend_info)));
|
2011-04-26 11:21:25 +02:00
|
|
|
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_FINISHED);
|
|
|
|
if (!circuit_launch_by_extend_info(CIRCUIT_PURPOSE_C_INTRODUCING,
|
|
|
|
extend_info,
|
|
|
|
CIRCLAUNCH_IS_INTERNAL)) {
|
|
|
|
log_warn(LD_REND, "Building introduction circuit failed.");
|
|
|
|
result = -1;
|
|
|
|
} else {
|
|
|
|
result = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
extend_info_free(extend_info);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Called when we're trying to connect an ap conn; sends an INTRODUCE1 cell
|
2004-04-06 05:44:36 +02:00
|
|
|
* down introcirc if possible.
|
|
|
|
*/
|
2004-04-05 09:41:31 +02:00
|
|
|
int
|
2006-07-23 09:37:35 +02:00
|
|
|
rend_client_send_introduction(origin_circuit_t *introcirc,
|
|
|
|
origin_circuit_t *rendcirc)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2004-10-14 04:47:09 +02:00
|
|
|
size_t payload_len;
|
2008-09-24 16:44:29 +02:00
|
|
|
int r, v3_shift = 0;
|
2004-04-05 23:15:14 +02:00
|
|
|
char payload[RELAY_PAYLOAD_SIZE];
|
2005-06-29 23:46:55 +02:00
|
|
|
char tmp[RELAY_PAYLOAD_SIZE];
|
2004-04-08 00:41:00 +02:00
|
|
|
rend_cache_entry_t *entry;
|
2004-04-05 22:30:53 +02:00
|
|
|
crypt_path_t *cpath;
|
2005-06-29 23:46:55 +02:00
|
|
|
off_t dh_offset;
|
2009-06-12 02:05:21 +02:00
|
|
|
crypto_pk_env_t *intro_key = NULL;
|
2004-04-05 09:41:31 +02:00
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
tor_assert(introcirc->_base.purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
|
|
|
|
tor_assert(rendcirc->_base.purpose == CIRCUIT_PURPOSE_C_REND_READY);
|
2008-09-24 16:44:29 +02:00
|
|
|
tor_assert(introcirc->rend_data);
|
|
|
|
tor_assert(rendcirc->rend_data);
|
|
|
|
tor_assert(!rend_cmp_service_ids(introcirc->rend_data->onion_address,
|
|
|
|
rendcirc->rend_data->onion_address));
|
2011-06-14 01:12:47 +02:00
|
|
|
tor_assert(!(introcirc->build_state->onehop_tunnel));
|
|
|
|
tor_assert(!(rendcirc->build_state->onehop_tunnel));
|
2004-04-05 09:41:31 +02:00
|
|
|
|
2008-09-24 16:44:29 +02:00
|
|
|
if (rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1,
|
|
|
|
&entry) < 1) {
|
2011-04-25 15:38:35 +02:00
|
|
|
log_info(LD_REND,
|
|
|
|
"query %s didn't have valid rend desc in cache. "
|
|
|
|
"Refetching descriptor.",
|
2011-04-28 19:37:35 +02:00
|
|
|
safe_str_client(introcirc->rend_data->onion_address));
|
2011-04-25 15:38:35 +02:00
|
|
|
rend_client_refetch_v2_renddesc(introcirc->rend_data);
|
|
|
|
{
|
|
|
|
connection_t *conn;
|
|
|
|
|
|
|
|
while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP,
|
|
|
|
AP_CONN_STATE_CIRCUIT_WAIT,
|
2011-04-28 19:00:34 +02:00
|
|
|
introcirc->rend_data->onion_address))) {
|
2011-04-25 15:38:35 +02:00
|
|
|
conn->state = AP_CONN_STATE_RENDDESC_WAIT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
2004-04-05 09:41:31 +02:00
|
|
|
}
|
|
|
|
|
2009-05-27 23:55:51 +02:00
|
|
|
/* first 20 bytes of payload are the hash of Bob's pk */
|
2009-06-12 02:05:21 +02:00
|
|
|
intro_key = NULL;
|
|
|
|
SMARTLIST_FOREACH(entry->parsed->intro_nodes, rend_intro_point_t *,
|
|
|
|
intro, {
|
2011-05-11 22:23:42 +02:00
|
|
|
if (tor_memeq(introcirc->build_state->chosen_exit->identity_digest,
|
2009-06-12 02:05:21 +02:00
|
|
|
intro->extend_info->identity_digest, DIGEST_LEN)) {
|
|
|
|
intro_key = intro->intro_key;
|
|
|
|
break;
|
2007-11-29 16:25:04 +01:00
|
|
|
}
|
2009-06-12 02:05:21 +02:00
|
|
|
});
|
|
|
|
if (!intro_key) {
|
2011-04-26 11:21:25 +02:00
|
|
|
log_info(LD_REND, "Could not find intro key for %s at %s; we "
|
|
|
|
"have a v2 rend desc with %d intro points. "
|
|
|
|
"Trying a different intro point...",
|
|
|
|
safe_str_client(introcirc->rend_data->onion_address),
|
2011-05-16 03:58:46 +02:00
|
|
|
safe_str_client(extend_info_describe(
|
|
|
|
introcirc->build_state->chosen_exit)),
|
2009-07-02 16:20:20 +02:00
|
|
|
smartlist_len(entry->parsed->intro_nodes));
|
2011-04-26 11:21:25 +02:00
|
|
|
|
|
|
|
if (rend_client_reextend_intro_circuit(introcirc)) {
|
|
|
|
goto perm_err;
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
2007-11-01 04:43:02 +01:00
|
|
|
}
|
|
|
|
if (crypto_pk_get_digest(intro_key, payload)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG, "Internal error: couldn't hash public key.");
|
2011-04-25 17:36:02 +02:00
|
|
|
goto perm_err;
|
2004-04-05 09:41:31 +02:00
|
|
|
}
|
|
|
|
|
2004-04-05 22:30:53 +02:00
|
|
|
/* Initialize the pending_final_cpath and start the DH handshake. */
|
2004-04-13 05:19:58 +02:00
|
|
|
cpath = rendcirc->build_state->pending_final_cpath;
|
|
|
|
if (!cpath) {
|
|
|
|
cpath = rendcirc->build_state->pending_final_cpath =
|
|
|
|
tor_malloc_zero(sizeof(crypt_path_t));
|
2005-03-23 07:21:48 +01:00
|
|
|
cpath->magic = CRYPT_PATH_MAGIC;
|
2011-01-24 22:03:14 +01:00
|
|
|
if (!(cpath->dh_handshake_state = crypto_dh_new(DH_TYPE_REND))) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG, "Internal error: couldn't allocate DH.");
|
2011-04-25 17:36:02 +02:00
|
|
|
goto perm_err;
|
2004-04-13 05:19:58 +02:00
|
|
|
}
|
2005-05-03 00:35:18 +02:00
|
|
|
if (crypto_dh_generate_public(cpath->dh_handshake_state)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG, "Internal error: couldn't generate g^x.");
|
2011-04-25 17:36:02 +02:00
|
|
|
goto perm_err;
|
2004-04-13 05:19:58 +02:00
|
|
|
}
|
2004-04-05 22:30:53 +02:00
|
|
|
}
|
|
|
|
|
2008-09-24 16:44:29 +02:00
|
|
|
/* If version is 3, write (optional) auth data and timestamp. */
|
|
|
|
if (entry->parsed->protocols & (1<<3)) {
|
|
|
|
tmp[0] = 3; /* version 3 of the cell format */
|
|
|
|
tmp[1] = (uint8_t)introcirc->rend_data->auth_type; /* auth type, if any */
|
|
|
|
v3_shift = 1;
|
|
|
|
if (introcirc->rend_data->auth_type != REND_NO_AUTH) {
|
|
|
|
set_uint16(tmp+2, htons(REND_DESC_COOKIE_LEN));
|
|
|
|
memcpy(tmp+4, introcirc->rend_data->descriptor_cookie,
|
|
|
|
REND_DESC_COOKIE_LEN);
|
|
|
|
v3_shift += 2+REND_DESC_COOKIE_LEN;
|
|
|
|
}
|
2009-09-01 05:23:47 +02:00
|
|
|
set_uint32(tmp+v3_shift+1, htonl((uint32_t)time(NULL)));
|
2008-09-24 16:44:29 +02:00
|
|
|
v3_shift += 4;
|
|
|
|
} /* if version 2 only write version number */
|
|
|
|
else if (entry->parsed->protocols & (1<<2)) {
|
|
|
|
tmp[0] = 2; /* version 2 of the cell format */
|
|
|
|
}
|
|
|
|
|
2004-04-05 09:41:31 +02:00
|
|
|
/* write the remaining items into tmp */
|
2008-09-24 16:44:29 +02:00
|
|
|
if (entry->parsed->protocols & (1<<3) || entry->parsed->protocols & (1<<2)) {
|
2005-06-29 23:46:55 +02:00
|
|
|
/* version 2 format */
|
|
|
|
extend_info_t *extend_info = rendcirc->build_state->chosen_exit;
|
|
|
|
int klen;
|
|
|
|
/* nul pads */
|
2008-09-24 16:44:29 +02:00
|
|
|
set_uint32(tmp+v3_shift+1, tor_addr_to_ipv4h(&extend_info->addr));
|
|
|
|
set_uint16(tmp+v3_shift+5, htons(extend_info->port));
|
|
|
|
memcpy(tmp+v3_shift+7, extend_info->identity_digest, DIGEST_LEN);
|
|
|
|
klen = crypto_pk_asn1_encode(extend_info->onion_key,
|
|
|
|
tmp+v3_shift+7+DIGEST_LEN+2,
|
|
|
|
sizeof(tmp)-(v3_shift+7+DIGEST_LEN+2));
|
|
|
|
set_uint16(tmp+v3_shift+7+DIGEST_LEN, htons(klen));
|
|
|
|
memcpy(tmp+v3_shift+7+DIGEST_LEN+2+klen, rendcirc->rend_data->rend_cookie,
|
2006-07-23 09:37:35 +02:00
|
|
|
REND_COOKIE_LEN);
|
2008-09-24 16:44:29 +02:00
|
|
|
dh_offset = v3_shift+7+DIGEST_LEN+2+klen+REND_COOKIE_LEN;
|
2005-06-29 23:46:55 +02:00
|
|
|
} else {
|
|
|
|
/* Version 0. */
|
2005-12-14 21:40:40 +01:00
|
|
|
strncpy(tmp, rendcirc->build_state->chosen_exit->nickname,
|
|
|
|
(MAX_NICKNAME_LEN+1)); /* nul pads */
|
2008-09-24 16:44:29 +02:00
|
|
|
memcpy(tmp+MAX_NICKNAME_LEN+1, rendcirc->rend_data->rend_cookie,
|
2006-07-23 09:37:35 +02:00
|
|
|
REND_COOKIE_LEN);
|
2005-06-29 23:46:55 +02:00
|
|
|
dh_offset = MAX_NICKNAME_LEN+1+REND_COOKIE_LEN;
|
|
|
|
}
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
|
2005-06-29 23:46:55 +02:00
|
|
|
if (crypto_dh_get_public(cpath->dh_handshake_state, tmp+dh_offset,
|
2004-04-06 05:44:36 +02:00
|
|
|
DH_KEY_LEN)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG, "Internal error: couldn't extract g^x.");
|
2011-04-25 17:36:02 +02:00
|
|
|
goto perm_err;
|
2004-04-05 22:30:53 +02:00
|
|
|
}
|
2004-04-05 09:41:31 +02:00
|
|
|
|
2006-10-31 20:17:07 +01:00
|
|
|
note_crypto_pk_op(REND_CLIENT);
|
2004-10-24 21:08:07 +02:00
|
|
|
/*XXX maybe give crypto_pk_public_hybrid_encrypt a max_len arg,
|
|
|
|
* to avoid buffer overflows? */
|
2007-11-01 04:43:02 +01:00
|
|
|
r = crypto_pk_public_hybrid_encrypt(intro_key, payload+DIGEST_LEN,
|
2011-01-13 20:36:41 +01:00
|
|
|
sizeof(payload)-DIGEST_LEN,
|
2005-12-14 21:40:40 +01:00
|
|
|
tmp,
|
2006-11-14 02:07:52 +01:00
|
|
|
(int)(dh_offset+DH_KEY_LEN),
|
2004-04-06 22:55:46 +02:00
|
|
|
PK_PKCS1_OAEP_PADDING, 0);
|
2004-04-05 23:15:14 +02:00
|
|
|
if (r<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG,"Internal error: hybrid pk encrypt failed.");
|
2011-04-25 17:36:02 +02:00
|
|
|
goto perm_err;
|
2004-04-05 09:41:31 +02:00
|
|
|
}
|
|
|
|
|
2004-04-06 05:44:36 +02:00
|
|
|
payload_len = DIGEST_LEN + r;
|
2007-05-15 23:17:48 +02:00
|
|
|
tor_assert(payload_len <= RELAY_PAYLOAD_SIZE); /* we overran something */
|
2004-04-05 23:15:14 +02:00
|
|
|
|
2007-08-11 16:13:25 +02:00
|
|
|
log_info(LD_REND, "Sending an INTRODUCE1 cell");
|
2007-03-24 16:57:51 +01:00
|
|
|
if (relay_send_command_from_edge(0, TO_CIRCUIT(introcirc),
|
2004-04-05 09:41:31 +02:00
|
|
|
RELAY_COMMAND_INTRODUCE1,
|
2004-04-05 23:15:14 +02:00
|
|
|
payload, payload_len,
|
2004-04-05 09:41:31 +02:00
|
|
|
introcirc->cpath->prev)<0) {
|
|
|
|
/* introcirc is already marked for close. leave rendcirc alone. */
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG, "Couldn't send INTRODUCE1 cell");
|
2011-04-25 17:36:02 +02:00
|
|
|
return -2;
|
2004-04-05 09:41:31 +02:00
|
|
|
}
|
|
|
|
|
2004-04-13 03:41:39 +02:00
|
|
|
/* Now, we wait for an ACK or NAK on this circuit. */
|
2006-07-23 09:37:35 +02:00
|
|
|
introcirc->_base.purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT;
|
2011-05-29 17:03:41 +02:00
|
|
|
/* Set timestamp_dirty, because circuit_expire_building expects it
|
|
|
|
* to specify when a circuit entered the _C_INTRODUCE_ACK_WAIT
|
|
|
|
* state. */
|
|
|
|
introcirc->_base.timestamp_dirty = time(NULL);
|
2004-04-05 17:17:34 +02:00
|
|
|
|
2004-04-05 09:41:31 +02:00
|
|
|
return 0;
|
2011-05-06 03:53:46 +02:00
|
|
|
perm_err:
|
2011-04-26 11:21:25 +02:00
|
|
|
if (!introcirc->_base.marked_for_close)
|
|
|
|
circuit_mark_for_close(TO_CIRCUIT(introcirc), END_CIRC_REASON_INTERNAL);
|
2006-10-09 17:47:27 +02:00
|
|
|
circuit_mark_for_close(TO_CIRCUIT(rendcirc), END_CIRC_REASON_INTERNAL);
|
2011-04-25 17:36:02 +02:00
|
|
|
return -2;
|
2004-04-05 09:41:31 +02:00
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Called when a rendezvous circuit is open; sends a establish
|
2004-05-05 23:32:43 +02:00
|
|
|
* rendezvous circuit as appropriate. */
|
2004-04-03 00:23:15 +02:00
|
|
|
void
|
2006-07-23 09:37:35 +02:00
|
|
|
rend_client_rendcirc_has_opened(origin_circuit_t *circ)
|
2004-04-03 00:23:15 +02:00
|
|
|
{
|
2006-07-23 09:37:35 +02:00
|
|
|
tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND);
|
2004-04-03 00:23:15 +02:00
|
|
|
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,"rendcirc is open");
|
2004-04-03 05:06:06 +02:00
|
|
|
|
2004-04-05 02:47:48 +02:00
|
|
|
/* generate a rendezvous cookie, store it in circ */
|
|
|
|
if (rend_client_send_establish_rendezvous(circ) < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2004-04-03 05:06:06 +02:00
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Called when get an ACK or a NAK for a REND_INTRODUCE1 cell.
|
2004-04-13 01:33:47 +02:00
|
|
|
*/
|
|
|
|
int
|
2006-07-23 09:37:35 +02:00
|
|
|
rend_client_introduction_acked(origin_circuit_t *circ,
|
2010-12-14 01:34:01 +01:00
|
|
|
const uint8_t *request, size_t request_len)
|
2004-04-13 01:33:47 +02:00
|
|
|
{
|
2006-07-26 21:05:34 +02:00
|
|
|
origin_circuit_t *rendcirc;
|
2006-06-05 00:42:13 +02:00
|
|
|
(void) request; // XXXX Use this.
|
2004-04-13 04:31:52 +02:00
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
if (circ->_base.purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL,
|
|
|
|
"Received REND_INTRODUCE_ACK on unexpected circuit %d.",
|
2006-07-23 09:37:35 +02:00
|
|
|
circ->_base.n_circ_id);
|
2006-10-09 17:47:27 +02:00
|
|
|
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
|
2004-04-13 03:41:39 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2005-06-29 23:46:55 +02:00
|
|
|
tor_assert(circ->build_state->chosen_exit);
|
2011-06-14 01:12:47 +02:00
|
|
|
tor_assert(!(circ->build_state->onehop_tunnel));
|
2008-09-24 16:44:29 +02:00
|
|
|
tor_assert(circ->rend_data);
|
2004-04-13 04:31:52 +02:00
|
|
|
|
2004-04-13 01:33:47 +02:00
|
|
|
if (request_len == 0) {
|
|
|
|
/* It's an ACK; the introduction point relayed our introduction request. */
|
2004-04-14 00:56:24 +02:00
|
|
|
/* Locate the rend circ which is waiting to hear about this ack,
|
|
|
|
* and tell it.
|
|
|
|
*/
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,"Received ack. Telling rend circ...");
|
2004-04-14 00:56:24 +02:00
|
|
|
rendcirc = circuit_get_by_rend_query_and_purpose(
|
2008-09-24 16:44:29 +02:00
|
|
|
circ->rend_data->onion_address, CIRCUIT_PURPOSE_C_REND_READY);
|
2004-11-28 10:05:49 +01:00
|
|
|
if (rendcirc) { /* remember the ack */
|
2011-06-14 01:12:47 +02:00
|
|
|
tor_assert(!(rendcirc->build_state->onehop_tunnel));
|
2006-07-26 21:05:34 +02:00
|
|
|
rendcirc->_base.purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED;
|
2011-05-29 17:03:41 +02:00
|
|
|
/* Set timestamp_dirty, because circuit_expire_building expects
|
|
|
|
* it to specify when a circuit entered the
|
|
|
|
* _C_REND_READY_INTRO_ACKED state. */
|
|
|
|
rendcirc->_base.timestamp_dirty = time(NULL);
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
} else {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,"...Found no rend circ. Dropping on the floor.");
|
2004-04-14 00:56:24 +02:00
|
|
|
}
|
|
|
|
/* close the circuit: we won't need it anymore. */
|
2006-07-23 09:37:35 +02:00
|
|
|
circ->_base.purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACKED;
|
2006-10-17 17:20:00 +02:00
|
|
|
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_FINISHED);
|
2004-04-13 01:33:47 +02:00
|
|
|
} else {
|
|
|
|
/* It's a NAK; the introduction point didn't relay our request. */
|
2006-07-23 09:37:35 +02:00
|
|
|
circ->_base.purpose = CIRCUIT_PURPOSE_C_INTRODUCING;
|
2004-04-18 09:37:16 +02:00
|
|
|
/* Remove this intro point from the set of viable introduction
|
|
|
|
* points. If any remain, extend to a new one and try again.
|
|
|
|
* If none remain, refetch the service descriptor.
|
2004-04-13 03:41:39 +02:00
|
|
|
*/
|
2011-04-26 11:21:25 +02:00
|
|
|
log_info(LD_REND, "Got nack for %s from %s...",
|
2011-05-16 03:58:46 +02:00
|
|
|
safe_str_client(circ->rend_data->onion_address),
|
|
|
|
safe_str_client(extend_info_describe(circ->build_state->chosen_exit)));
|
2005-06-29 23:46:55 +02:00
|
|
|
if (rend_client_remove_intro_point(circ->build_state->chosen_exit,
|
2008-09-24 16:44:29 +02:00
|
|
|
circ->rend_data) > 0) {
|
2005-07-14 10:43:19 +02:00
|
|
|
/* There are introduction points left. Re-extend the circuit to
|
2004-04-13 04:31:52 +02:00
|
|
|
* another intro point and try again. */
|
2011-04-26 11:21:25 +02:00
|
|
|
int result = rend_client_reextend_intro_circuit(circ);
|
|
|
|
/* XXXX If that call failed, should we close the rend circuit,
|
|
|
|
* too? */
|
2005-06-29 23:46:55 +02:00
|
|
|
return result;
|
2004-04-13 04:31:52 +02:00
|
|
|
}
|
2004-04-13 01:33:47 +02:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-01-26 01:42:13 +01:00
|
|
|
/** The period for which a hidden service directory cannot be queried for
|
|
|
|
* the same descriptor ID again. */
|
|
|
|
#define REND_HID_SERV_DIR_REQUERY_PERIOD (15 * 60)
|
|
|
|
|
|
|
|
/** Contains the last request times to hidden service directories for
|
|
|
|
* certain queries; keys are strings consisting of base32-encoded
|
|
|
|
* hidden service directory identities and base32-encoded descriptor IDs;
|
|
|
|
* values are pointers to timestamps of the last requests. */
|
2011-06-02 11:24:18 +02:00
|
|
|
static strmap_t *last_hid_serv_requests_ = NULL;
|
|
|
|
|
|
|
|
/** Returns last_hid_serv_requests_, initializing it to a new strmap if
|
|
|
|
* necessary. */
|
|
|
|
static strmap_t *
|
|
|
|
get_last_hid_serv_requests(void)
|
|
|
|
{
|
|
|
|
if (!last_hid_serv_requests_)
|
|
|
|
last_hid_serv_requests_ = strmap_new();
|
|
|
|
return last_hid_serv_requests_;
|
|
|
|
}
|
2008-01-26 01:42:13 +01:00
|
|
|
|
|
|
|
/** Look up the last request time to hidden service directory <b>hs_dir</b>
|
|
|
|
* for descriptor ID <b>desc_id_base32</b>. If <b>set</b> is non-zero,
|
|
|
|
* assign the current time <b>now</b> and return that. Otherwise, return
|
|
|
|
* the most recent request time, or 0 if no such request has been sent
|
|
|
|
* before. */
|
|
|
|
static time_t
|
|
|
|
lookup_last_hid_serv_request(routerstatus_t *hs_dir,
|
|
|
|
const char *desc_id_base32, time_t now, int set)
|
|
|
|
{
|
|
|
|
char hsdir_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
|
|
|
|
char hsdir_desc_comb_id[2 * REND_DESC_ID_V2_LEN_BASE32 + 1];
|
|
|
|
time_t *last_request_ptr;
|
2011-06-02 11:24:18 +02:00
|
|
|
strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
|
2008-01-26 01:42:13 +01:00
|
|
|
base32_encode(hsdir_id_base32, sizeof(hsdir_id_base32),
|
|
|
|
hs_dir->identity_digest, DIGEST_LEN);
|
|
|
|
tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s",
|
|
|
|
hsdir_id_base32, desc_id_base32);
|
|
|
|
if (set) {
|
2010-02-07 06:30:55 +01:00
|
|
|
time_t *oldptr;
|
|
|
|
last_request_ptr = tor_malloc_zero(sizeof(time_t));
|
2008-01-26 01:42:13 +01:00
|
|
|
*last_request_ptr = now;
|
2010-02-07 06:30:55 +01:00
|
|
|
oldptr = strmap_set(last_hid_serv_requests, hsdir_desc_comb_id,
|
|
|
|
last_request_ptr);
|
|
|
|
tor_free(oldptr);
|
2008-01-26 01:42:13 +01:00
|
|
|
} else
|
|
|
|
last_request_ptr = strmap_get_lc(last_hid_serv_requests,
|
|
|
|
hsdir_desc_comb_id);
|
|
|
|
return (last_request_ptr) ? *last_request_ptr : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Clean the history of request times to hidden service directories, so that
|
|
|
|
* it does not contain requests older than REND_HID_SERV_DIR_REQUERY_PERIOD
|
|
|
|
* seconds any more. */
|
|
|
|
static void
|
2011-02-07 16:43:41 +01:00
|
|
|
directory_clean_last_hid_serv_requests(time_t now)
|
2008-01-26 01:42:13 +01:00
|
|
|
{
|
|
|
|
strmap_iter_t *iter;
|
2011-02-07 16:43:41 +01:00
|
|
|
time_t cutoff = now - REND_HID_SERV_DIR_REQUERY_PERIOD;
|
2011-06-02 11:24:18 +02:00
|
|
|
strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
|
2008-01-26 01:42:13 +01:00
|
|
|
for (iter = strmap_iter_init(last_hid_serv_requests);
|
|
|
|
!strmap_iter_done(iter); ) {
|
|
|
|
const char *key;
|
|
|
|
void *val;
|
|
|
|
time_t *ent;
|
|
|
|
strmap_iter_get(iter, &key, &val);
|
|
|
|
ent = (time_t *) val;
|
|
|
|
if (*ent < cutoff) {
|
|
|
|
iter = strmap_iter_next_rmv(last_hid_serv_requests, iter);
|
|
|
|
tor_free(ent);
|
|
|
|
} else {
|
|
|
|
iter = strmap_iter_next(last_hid_serv_requests, iter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-02 11:46:04 +02:00
|
|
|
/** Purge the history of request times to hidden service directories,
|
|
|
|
* so that future lookups of an HS descriptor will not fail because we
|
|
|
|
* accessed all of the HSDir relays responsible for the descriptor
|
|
|
|
* recently. */
|
|
|
|
void
|
|
|
|
rend_client_purge_last_hid_serv_requests(void)
|
|
|
|
{
|
|
|
|
/* Don't create the table if it doesn't exist yet (and it may very
|
|
|
|
* well not exist if the user hasn't accessed any HSes)... */
|
|
|
|
strmap_t *old_last_hid_serv_requests = last_hid_serv_requests_;
|
|
|
|
/* ... and let get_last_hid_serv_requests re-create it for us if
|
|
|
|
* necessary. */
|
|
|
|
last_hid_serv_requests_ = NULL;
|
|
|
|
|
|
|
|
if (old_last_hid_serv_requests != NULL) {
|
2011-06-02 12:07:09 +02:00
|
|
|
log_info(LD_REND, "Purging client last-HS-desc-request-time table");
|
2011-06-02 11:46:04 +02:00
|
|
|
strmap_free(old_last_hid_serv_requests, _tor_free);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-01-26 01:42:13 +01:00
|
|
|
/** Determine the responsible hidden service directories for <b>desc_id</b>
|
|
|
|
* and fetch the descriptor belonging to that ID from one of them. Only
|
|
|
|
* send a request to hidden service directories that we did not try within
|
|
|
|
* the last REND_HID_SERV_DIR_REQUERY_PERIOD seconds; 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. <b>query</b> is only
|
|
|
|
* passed for pretty log statements. */
|
2008-01-27 02:03:30 +01:00
|
|
|
static int
|
2008-09-24 16:44:29 +02:00
|
|
|
directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
|
2008-01-26 01:42:13 +01:00
|
|
|
{
|
|
|
|
smartlist_t *responsible_dirs = smartlist_create();
|
|
|
|
routerstatus_t *hs_dir;
|
|
|
|
char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
|
|
|
|
time_t now = time(NULL);
|
2008-09-24 16:44:29 +02:00
|
|
|
char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64];
|
2008-01-26 01:42:13 +01:00
|
|
|
tor_assert(desc_id);
|
2008-09-24 16:44:29 +02:00
|
|
|
tor_assert(rend_query);
|
2008-01-26 01:42:13 +01:00
|
|
|
/* 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. */
|
2011-03-09 16:39:04 +01:00
|
|
|
hid_serv_get_responsible_directories(responsible_dirs, desc_id);
|
2008-01-26 01:42:13 +01:00
|
|
|
|
|
|
|
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
|
2008-09-09 10:41:58 +02:00
|
|
|
* a request recently and for which we have a router descriptor here. */
|
2011-02-07 16:43:41 +01:00
|
|
|
|
|
|
|
/* Clean request history first. */
|
|
|
|
directory_clean_last_hid_serv_requests(now);
|
2008-01-26 01:42:13 +01:00
|
|
|
|
|
|
|
SMARTLIST_FOREACH(responsible_dirs, routerstatus_t *, dir, {
|
2011-07-16 00:53:29 +02:00
|
|
|
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);
|
2008-01-26 01:42:13 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
hs_dir = smartlist_choose(responsible_dirs);
|
|
|
|
smartlist_free(responsible_dirs);
|
|
|
|
if (!hs_dir) {
|
|
|
|
log_info(LD_REND, "Could not pick one of the responsible hidden "
|
|
|
|
"service directories, because we requested them all "
|
|
|
|
"recently without success.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2008-09-24 16:44:29 +02:00
|
|
|
/* Encode descriptor cookie for logging purposes. */
|
2008-12-18 05:27:23 +01:00
|
|
|
if (rend_query->auth_type != REND_NO_AUTH) {
|
|
|
|
if (base64_encode(descriptor_cookie_base64,
|
|
|
|
sizeof(descriptor_cookie_base64),
|
|
|
|
rend_query->descriptor_cookie, REND_DESC_COOKIE_LEN)<0) {
|
|
|
|
log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* Remove == signs and newline. */
|
|
|
|
descriptor_cookie_base64[strlen(descriptor_cookie_base64)-3] = '\0';
|
|
|
|
} else {
|
|
|
|
strlcpy(descriptor_cookie_base64, "(none)",
|
|
|
|
sizeof(descriptor_cookie_base64));
|
2008-09-24 16:44:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Send fetch request. (Pass query and possibly descriptor cookie so that
|
|
|
|
* they can be written to the directory connection and be referred to when
|
|
|
|
* the response arrives. */
|
|
|
|
directory_initiate_command_routerstatus_rend(hs_dir,
|
2008-01-26 01:42:13 +01:00
|
|
|
DIR_PURPOSE_FETCH_RENDDESC_V2,
|
|
|
|
ROUTER_PURPOSE_GENERAL,
|
2008-09-24 16:44:29 +02:00
|
|
|
1, desc_id_base32, NULL, 0, 0,
|
|
|
|
rend_query);
|
2008-01-26 01:42:13 +01:00
|
|
|
log_info(LD_REND, "Sending fetch request for v2 descriptor for "
|
2008-09-24 16:44:29 +02:00
|
|
|
"service '%s' with descriptor ID '%s', auth type %d, "
|
|
|
|
"and descriptor cookie '%s' to hidden service "
|
2011-05-16 03:58:46 +02:00
|
|
|
"directory %s",
|
2008-09-24 16:44:29 +02:00
|
|
|
rend_query->onion_address, desc_id_base32,
|
|
|
|
rend_query->auth_type,
|
2008-11-29 12:55:30 +01:00
|
|
|
(rend_query->auth_type == REND_NO_AUTH ? "[none]" :
|
2011-05-16 03:58:46 +02:00
|
|
|
escaped_safe_str_client(descriptor_cookie_base64)),
|
|
|
|
routerstatus_describe(hs_dir));
|
2008-01-26 01:42:13 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-06-13 12:21:58 +02:00
|
|
|
/** 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. */
|
2007-11-01 04:43:02 +01:00
|
|
|
void
|
2008-09-24 16:44:29 +02:00
|
|
|
rend_client_refetch_v2_renddesc(const rend_data_t *rend_query)
|
2007-11-01 04:43:02 +01:00
|
|
|
{
|
|
|
|
char descriptor_id[DIGEST_LEN];
|
2008-01-24 04:28:50 +01:00
|
|
|
int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS];
|
|
|
|
int i, tries_left;
|
2008-02-17 17:47:47 +01:00
|
|
|
rend_cache_entry_t *e = NULL;
|
2008-09-24 16:44:29 +02:00
|
|
|
tor_assert(rend_query);
|
2007-11-01 04:43:02 +01:00
|
|
|
/* Are we configured to fetch descriptors? */
|
|
|
|
if (!get_options()->FetchHidServDescriptors) {
|
|
|
|
log_warn(LD_REND, "We received an onion address for a v2 rendezvous "
|
|
|
|
"service descriptor, but are not fetching service descriptors.");
|
|
|
|
return;
|
|
|
|
}
|
2008-02-17 17:47:47 +01:00
|
|
|
/* Before fetching, check if we already have the descriptor here. */
|
2008-09-24 16:44:29 +02:00
|
|
|
if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) > 0) {
|
2008-02-17 17:47:47 +01:00
|
|
|
log_info(LD_REND, "We would fetch a v2 rendezvous descriptor, but we "
|
|
|
|
"already have that descriptor here. Not fetching.");
|
|
|
|
return;
|
|
|
|
}
|
2007-11-01 04:43:02 +01:00
|
|
|
log_debug(LD_REND, "Fetching v2 rendezvous descriptor for service %s",
|
2009-12-15 23:23:36 +01:00
|
|
|
safe_str_client(rend_query->onion_address));
|
2008-01-24 04:28:50 +01:00
|
|
|
/* 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];
|
|
|
|
|
2008-09-24 16:44:29 +02:00
|
|
|
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) {
|
2008-01-24 04:28:50 +01:00
|
|
|
log_warn(LD_REND, "Internal error: Computing v2 rendezvous "
|
|
|
|
"descriptor ID did not succeed.");
|
|
|
|
return;
|
|
|
|
}
|
2008-09-24 16:44:29 +02:00
|
|
|
if (directory_get_from_hs_dir(descriptor_id, rend_query) != 0)
|
2008-01-24 06:15:50 +01:00
|
|
|
return; /* either success or failure, but we're done */
|
2007-11-01 04:43:02 +01:00
|
|
|
}
|
2008-01-24 04:28:50 +01:00
|
|
|
/* 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.");
|
2009-05-28 22:19:05 +02:00
|
|
|
/* Close pending connections. */
|
|
|
|
rend_client_desc_trynow(rend_query->onion_address);
|
2007-11-01 04:43:02 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-04-20 11:27:58 +02:00
|
|
|
/** Cancel all rendezvous descriptor fetches currently in progress.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
rend_client_cancel_descriptor_fetches(void)
|
|
|
|
{
|
|
|
|
smartlist_t *connection_array = get_connection_array();
|
|
|
|
|
|
|
|
SMARTLIST_FOREACH_BEGIN(connection_array, connection_t *, conn) {
|
|
|
|
if (conn->type == CONN_TYPE_DIR &&
|
|
|
|
(conn->purpose == DIR_PURPOSE_FETCH_RENDDESC ||
|
|
|
|
conn->purpose == DIR_PURPOSE_FETCH_RENDDESC_V2)) {
|
|
|
|
/* It's a rendezvous descriptor fetch in progress -- cancel it
|
|
|
|
* by marking the connection for close.
|
|
|
|
*
|
|
|
|
* Even if this connection has already reached EOF, this is
|
|
|
|
* enough to make sure that if the descriptor hasn't been
|
|
|
|
* processed yet, it won't be. See the end of
|
|
|
|
* connection_handle_read; connection_reached_eof (indirectly)
|
|
|
|
* processes whatever response the connection received. */
|
|
|
|
|
|
|
|
const rend_data_t *rd = (TO_DIR_CONN(conn))->rend_data;
|
|
|
|
if (!rd) {
|
|
|
|
log_warn(LD_BUG | LD_REND,
|
|
|
|
"Marking for close dir conn fetching rendezvous "
|
|
|
|
"descriptor for unknown service!");
|
|
|
|
} else {
|
2011-04-28 19:00:34 +02:00
|
|
|
log_debug(LD_REND, "Marking for close dir conn fetching "
|
2011-04-20 11:27:58 +02:00
|
|
|
"rendezvous descriptor for service %s",
|
|
|
|
safe_str(rd->onion_address));
|
|
|
|
}
|
|
|
|
connection_mark_for_close(conn);
|
|
|
|
}
|
|
|
|
} SMARTLIST_FOREACH_END(conn);
|
|
|
|
}
|
|
|
|
|
2006-01-17 05:16:59 +01:00
|
|
|
/** Remove failed_intro from ent. If ent now has no intro points, or
|
2004-04-18 09:37:16 +02:00
|
|
|
* service is unrecognized, then launch a new renddesc fetch.
|
2011-04-20 11:27:58 +02:00
|
|
|
|
2004-04-18 09:37:16 +02:00
|
|
|
*
|
|
|
|
* Return -1 if error, 0 if no intro points remain or service
|
|
|
|
* unrecognized, 1 if recognized and some intro points remain.
|
|
|
|
*/
|
|
|
|
int
|
2008-09-24 16:44:29 +02:00
|
|
|
rend_client_remove_intro_point(extend_info_t *failed_intro,
|
|
|
|
const rend_data_t *rend_query)
|
2004-04-18 09:37:16 +02:00
|
|
|
{
|
|
|
|
int i, r;
|
|
|
|
rend_cache_entry_t *ent;
|
2005-01-20 00:15:59 +01:00
|
|
|
connection_t *conn;
|
2004-04-18 09:37:16 +02:00
|
|
|
|
2008-09-24 16:44:29 +02:00
|
|
|
r = rend_cache_lookup_entry(rend_query->onion_address, -1, &ent);
|
2004-04-18 09:37:16 +02:00
|
|
|
if (r<0) {
|
2008-09-24 16:44:29 +02:00
|
|
|
log_warn(LD_BUG, "Malformed service ID %s.",
|
2009-12-15 23:23:36 +01:00
|
|
|
escaped_safe_str_client(rend_query->onion_address));
|
2004-04-18 09:37:16 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (r==0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND, "Unknown service %s. Re-fetching descriptor.",
|
2009-12-15 23:23:36 +01:00
|
|
|
escaped_safe_str_client(rend_query->onion_address));
|
2008-09-24 16:44:29 +02:00
|
|
|
rend_client_refetch_v2_renddesc(rend_query);
|
2004-04-18 09:37:16 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-12-21 10:28:22 +01:00
|
|
|
for (i = 0; i < smartlist_len(ent->parsed->intro_nodes); i++) {
|
|
|
|
rend_intro_point_t *intro = smartlist_get(ent->parsed->intro_nodes, i);
|
2011-05-10 22:23:43 +02:00
|
|
|
if (tor_memeq(failed_intro->identity_digest,
|
2007-12-21 10:28:22 +01:00
|
|
|
intro->extend_info->identity_digest, DIGEST_LEN)) {
|
|
|
|
rend_intro_point_free(intro);
|
|
|
|
smartlist_del(ent->parsed->intro_nodes, i);
|
|
|
|
break;
|
2004-04-18 09:37:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-20 22:49:41 +02:00
|
|
|
if (! rend_client_any_intro_points_usable(ent)) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,
|
|
|
|
"No more intro points remain for %s. Re-fetching descriptor.",
|
2009-12-15 23:23:36 +01:00
|
|
|
escaped_safe_str_client(rend_query->onion_address));
|
2008-09-24 16:44:29 +02:00
|
|
|
rend_client_refetch_v2_renddesc(rend_query);
|
2005-01-20 00:15:59 +01:00
|
|
|
|
|
|
|
/* move all pending streams back to renddesc_wait */
|
|
|
|
while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP,
|
2008-09-24 16:44:29 +02:00
|
|
|
AP_CONN_STATE_CIRCUIT_WAIT,
|
2009-05-28 22:19:05 +02:00
|
|
|
rend_query->onion_address))) {
|
2005-01-20 00:15:59 +01:00
|
|
|
conn->state = AP_CONN_STATE_RENDDESC_WAIT;
|
|
|
|
}
|
|
|
|
|
2004-04-18 09:37:16 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,"%d options left for %s.",
|
2008-09-24 16:44:29 +02:00
|
|
|
smartlist_len(ent->parsed->intro_nodes),
|
2009-12-15 23:23:36 +01:00
|
|
|
escaped_safe_str_client(rend_query->onion_address));
|
2004-04-18 09:37:16 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Called when we receive a RENDEZVOUS_ESTABLISHED cell; changes the state of
|
2004-04-06 05:44:36 +02:00
|
|
|
* the circuit to C_REND_READY.
|
|
|
|
*/
|
2004-04-05 02:47:48 +02:00
|
|
|
int
|
2010-12-14 01:34:01 +01:00
|
|
|
rend_client_rendezvous_acked(origin_circuit_t *circ, const uint8_t *request,
|
2005-12-14 21:40:40 +01:00
|
|
|
size_t request_len)
|
2004-04-05 02:47:48 +02:00
|
|
|
{
|
2006-06-05 00:42:13 +02:00
|
|
|
(void) request;
|
|
|
|
(void) request_len;
|
2004-04-05 02:47:48 +02:00
|
|
|
/* we just got an ack for our establish-rendezvous. switch purposes. */
|
2006-07-23 09:37:35 +02:00
|
|
|
if (circ->_base.purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL,"Got a rendezvous ack when we weren't expecting one. "
|
|
|
|
"Closing circ.");
|
2006-10-09 17:47:27 +02:00
|
|
|
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
|
2004-04-05 02:47:48 +02:00
|
|
|
return -1;
|
2004-04-03 05:06:06 +02:00
|
|
|
}
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,"Got rendezvous ack. This circuit is now ready for "
|
|
|
|
"rendezvous.");
|
2006-07-23 09:37:35 +02:00
|
|
|
circ->_base.purpose = CIRCUIT_PURPOSE_C_REND_READY;
|
2011-05-29 17:03:41 +02:00
|
|
|
/* Set timestamp_dirty, because circuit_expire_building expects it
|
|
|
|
* to specify when a circuit entered the _C_REND_READY state. */
|
|
|
|
circ->_base.timestamp_dirty = time(NULL);
|
2011-03-25 21:01:16 +01:00
|
|
|
/* XXXX023 This is a pretty brute-force approach. It'd be better to
|
2008-07-06 20:34:45 +02:00
|
|
|
* attach only the connections that are waiting on this circuit, rather
|
2009-01-06 18:37:22 +01:00
|
|
|
* than trying to attach them all. See comments bug 743. */
|
2008-07-06 20:47:27 +02:00
|
|
|
/* If we already have the introduction circuit built, make sure we send
|
2008-07-06 20:34:45 +02:00
|
|
|
* the INTRODUCE cell _now_ */
|
|
|
|
connection_ap_attach_pending();
|
2004-04-05 02:47:48 +02:00
|
|
|
return 0;
|
2004-04-03 00:23:15 +02:00
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Bob sent us a rendezvous cell; join the circuits. */
|
2004-04-05 09:41:31 +02:00
|
|
|
int
|
2010-12-14 01:34:01 +01:00
|
|
|
rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request,
|
2005-12-14 21:40:40 +01:00
|
|
|
size_t request_len)
|
2004-04-03 00:23:15 +02:00
|
|
|
{
|
2004-04-05 22:53:50 +02:00
|
|
|
crypt_path_t *hop;
|
|
|
|
char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN];
|
2004-04-03 00:23:15 +02:00
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
if ((circ->_base.purpose != CIRCUIT_PURPOSE_C_REND_READY &&
|
|
|
|
circ->_base.purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED)
|
2004-04-14 07:18:21 +02:00
|
|
|
|| !circ->build_state->pending_final_cpath) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL,"Got rendezvous2 cell from hidden service, but not "
|
|
|
|
"expecting it. Closing.");
|
2006-10-09 17:47:27 +02:00
|
|
|
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
|
2004-04-05 09:41:31 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-04-03 00:23:15 +02:00
|
|
|
|
2004-04-05 22:53:50 +02:00
|
|
|
if (request_len != DH_KEY_LEN+DIGEST_LEN) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL,"Incorrect length (%d) on RENDEZVOUS2 cell.",
|
|
|
|
(int)request_len);
|
2004-04-05 22:53:50 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2004-04-03 00:23:15 +02:00
|
|
|
|
2007-08-11 16:13:25 +02:00
|
|
|
log_info(LD_REND,"Got RENDEZVOUS2 cell from hidden service.");
|
|
|
|
|
2004-04-05 22:53:50 +02:00
|
|
|
/* first DH_KEY_LEN bytes are g^y from bob. Finish the dh handshake...*/
|
2004-10-17 00:14:52 +02:00
|
|
|
tor_assert(circ->build_state);
|
|
|
|
tor_assert(circ->build_state->pending_final_cpath);
|
2004-04-05 22:53:50 +02:00
|
|
|
hop = circ->build_state->pending_final_cpath;
|
2005-05-03 00:35:18 +02:00
|
|
|
tor_assert(hop->dh_handshake_state);
|
2010-12-16 04:47:28 +01:00
|
|
|
if (crypto_dh_compute_secret(LOG_PROTOCOL_WARN,
|
|
|
|
hop->dh_handshake_state, (char*)request,
|
2010-12-14 01:34:01 +01:00
|
|
|
DH_KEY_LEN,
|
2004-04-05 22:53:50 +02:00
|
|
|
keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_GENERAL, "Couldn't complete DH handshake.");
|
2004-04-05 22:53:50 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* ... and set up cpath. */
|
|
|
|
if (circuit_init_cpath_crypto(hop, keys+DIGEST_LEN, 0)<0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
/* Check whether the digest is right... */
|
2011-05-10 22:58:38 +02:00
|
|
|
if (tor_memneq(keys, request+DH_KEY_LEN, DIGEST_LEN)) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL, "Incorrect digest of key material.");
|
2004-04-05 22:53:50 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2005-05-03 00:35:18 +02:00
|
|
|
crypto_dh_free(hop->dh_handshake_state);
|
|
|
|
hop->dh_handshake_state = NULL;
|
2004-04-06 22:23:58 +02:00
|
|
|
|
2004-04-05 22:53:50 +02:00
|
|
|
/* All is well. Extend the circuit. */
|
2006-07-23 09:37:35 +02:00
|
|
|
circ->_base.purpose = CIRCUIT_PURPOSE_C_REND_JOINED;
|
2004-04-06 01:40:59 +02:00
|
|
|
hop->state = CPATH_STATE_OPEN;
|
2004-04-06 23:25:11 +02:00
|
|
|
/* set the windows to default. these are the windows
|
|
|
|
* that alice thinks bob has.
|
|
|
|
*/
|
2009-09-15 12:33:33 +02:00
|
|
|
hop->package_window = circuit_initial_package_window();
|
2004-04-06 23:25:11 +02:00
|
|
|
hop->deliver_window = CIRCWINDOW_START;
|
|
|
|
|
2004-04-05 22:53:50 +02:00
|
|
|
onion_append_to_cpath(&circ->cpath, hop);
|
|
|
|
circ->build_state->pending_final_cpath = NULL; /* prevent double-free */
|
2011-03-25 21:01:16 +01:00
|
|
|
/* XXXX023 This is a pretty brute-force approach. It'd be better to
|
2008-07-06 20:34:45 +02:00
|
|
|
* attach only the connections that are waiting on this circuit, rather
|
2009-01-06 18:37:22 +01:00
|
|
|
* than trying to attach them all. See comments bug 743. */
|
2008-07-06 20:34:45 +02:00
|
|
|
connection_ap_attach_pending();
|
2011-01-15 17:32:56 +01:00
|
|
|
memset(keys, 0, sizeof(keys));
|
2004-04-05 09:41:31 +02:00
|
|
|
return 0;
|
2004-04-05 22:53:50 +02:00
|
|
|
err:
|
2011-01-15 17:32:56 +01:00
|
|
|
memset(keys, 0, sizeof(keys));
|
2006-10-09 17:47:27 +02:00
|
|
|
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
|
2004-04-05 22:53:50 +02:00
|
|
|
return -1;
|
2004-04-05 09:41:31 +02:00
|
|
|
}
|
2004-04-03 00:23:15 +02:00
|
|
|
|
2009-05-28 22:19:05 +02:00
|
|
|
/** Find all the apconns in state AP_CONN_STATE_RENDDESC_WAIT that are
|
|
|
|
* waiting on <b>query</b>. If there's a working cache entry here with at
|
|
|
|
* least one intro point, move them to the next state. */
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
2009-05-28 22:19:05 +02:00
|
|
|
rend_client_desc_trynow(const char *query)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2006-07-26 21:07:26 +02:00
|
|
|
edge_connection_t *conn;
|
2004-04-08 00:41:00 +02:00
|
|
|
rend_cache_entry_t *entry;
|
2005-04-27 00:36:00 +02:00
|
|
|
time_t now = time(NULL);
|
2009-05-03 00:09:18 +02:00
|
|
|
|
2007-05-22 17:49:14 +02:00
|
|
|
smartlist_t *conns = get_connection_array();
|
2009-05-28 22:19:05 +02:00
|
|
|
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, _conn) {
|
2007-05-22 17:49:14 +02:00
|
|
|
if (_conn->type != CONN_TYPE_AP ||
|
|
|
|
_conn->state != AP_CONN_STATE_RENDDESC_WAIT ||
|
|
|
|
_conn->marked_for_close)
|
2006-07-26 21:07:26 +02:00
|
|
|
continue;
|
2007-05-22 17:49:14 +02:00
|
|
|
conn = TO_EDGE_CONN(_conn);
|
2008-09-24 16:44:29 +02:00
|
|
|
if (!conn->rend_data)
|
|
|
|
continue;
|
|
|
|
if (rend_cmp_service_ids(query, conn->rend_data->onion_address))
|
2006-01-17 05:16:59 +01:00
|
|
|
continue;
|
2006-07-26 21:07:26 +02:00
|
|
|
assert_connection_ok(TO_CONN(conn), now);
|
2008-09-24 16:44:29 +02:00
|
|
|
if (rend_cache_lookup_entry(conn->rend_data->onion_address, -1,
|
|
|
|
&entry) == 1 &&
|
2011-04-20 22:49:41 +02:00
|
|
|
rend_client_any_intro_points_usable(entry)) {
|
2004-04-08 00:41:00 +02:00
|
|
|
/* either this fetch worked, or it failed but there was a
|
|
|
|
* valid entry from before which we should reuse */
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,"Rend desc is usable. Launching circuits.");
|
2006-07-26 21:07:26 +02:00
|
|
|
conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
|
2005-04-27 00:36:00 +02:00
|
|
|
|
|
|
|
/* restart their timeout values, so they get a fair shake at
|
|
|
|
* connecting to the hidden service. */
|
2006-07-26 21:07:26 +02:00
|
|
|
conn->_base.timestamp_created = now;
|
|
|
|
conn->_base.timestamp_lastread = now;
|
|
|
|
conn->_base.timestamp_lastwritten = now;
|
2005-04-27 00:36:00 +02:00
|
|
|
|
2004-04-03 01:38:26 +02:00
|
|
|
if (connection_ap_handshake_attach_circuit(conn) < 0) {
|
|
|
|
/* it will never work */
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_REND,"Rendezvous attempt failed. Closing.");
|
2008-03-17 04:37:54 +01:00
|
|
|
if (!conn->_base.marked_for_close)
|
|
|
|
connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
|
2004-04-03 01:38:26 +02:00
|
|
|
}
|
2004-04-08 00:41:00 +02:00
|
|
|
} else { /* 404, or fetch didn't get that far */
|
2009-05-28 22:19:05 +02:00
|
|
|
log_notice(LD_REND,"Closing stream for '%s.onion': hidden service is "
|
2009-09-28 15:08:32 +02:00
|
|
|
"unavailable (try again later).",
|
2009-12-15 23:23:36 +01:00
|
|
|
safe_str_client(query));
|
2009-05-28 22:19:05 +02:00
|
|
|
connection_mark_unattached_ap(conn, END_STREAM_REASON_RESOLVEFAILED);
|
2004-04-03 01:38:26 +02:00
|
|
|
}
|
2009-05-28 22:19:05 +02:00
|
|
|
} SMARTLIST_FOREACH_END(_conn);
|
2004-04-03 00:23:15 +02:00
|
|
|
}
|
|
|
|
|
2005-06-29 23:46:55 +02:00
|
|
|
/** Return a newly allocated extend_info_t* for a randomly chosen introduction
|
|
|
|
* point for the named hidden service. Return NULL if all introduction points
|
|
|
|
* have been tried and failed.
|
2004-04-05 02:47:48 +02:00
|
|
|
*/
|
2005-06-29 23:46:55 +02:00
|
|
|
extend_info_t *
|
2008-09-24 16:44:29 +02:00
|
|
|
rend_client_get_random_intro(const rend_data_t *rend_query)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2009-10-18 00:52:18 +02:00
|
|
|
extend_info_t *result;
|
2004-04-08 04:11:49 +02:00
|
|
|
rend_cache_entry_t *entry;
|
2004-04-05 02:47:48 +02:00
|
|
|
|
2008-09-24 16:44:29 +02:00
|
|
|
if (rend_cache_lookup_entry(rend_query->onion_address, -1, &entry) < 1) {
|
2011-04-20 22:49:41 +02:00
|
|
|
log_warn(LD_REND,
|
|
|
|
"Query '%s' didn't have valid rend desc in cache. Failing.",
|
|
|
|
safe_str_client(rend_query->onion_address));
|
2004-04-05 02:47:48 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-10-18 00:52:18 +02:00
|
|
|
/* See if we can get a node that complies with ExcludeNodes */
|
2011-04-20 22:49:41 +02:00
|
|
|
if ((result = rend_client_get_random_intro_impl(entry, 1, 1)))
|
2009-10-18 00:52:18 +02:00
|
|
|
return result;
|
|
|
|
/* If not, and StrictNodes is not set, see if we can return any old node
|
|
|
|
*/
|
|
|
|
if (!get_options()->StrictNodes)
|
2011-04-20 22:49:41 +02:00
|
|
|
return rend_client_get_random_intro_impl(entry, 0, 1);
|
2009-10-18 00:52:18 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** As rend_client_get_random_intro, except assume that StrictNodes is set
|
2011-04-27 19:43:11 +02:00
|
|
|
* iff <b>strict</b> is true. If <b>warnings</b> is false, don't complain
|
|
|
|
* to the user when we're out of nodes, even if StrictNodes is true.
|
2009-10-18 00:52:18 +02:00
|
|
|
*/
|
|
|
|
static extend_info_t *
|
2011-04-20 22:49:41 +02:00
|
|
|
rend_client_get_random_intro_impl(const rend_cache_entry_t *entry,
|
|
|
|
const int strict,
|
|
|
|
const int warnings)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2004-04-05 02:47:48 +02:00
|
|
|
int i;
|
2011-04-20 22:49:41 +02:00
|
|
|
|
2007-12-21 10:28:22 +01:00
|
|
|
rend_intro_point_t *intro;
|
2011-06-14 19:01:38 +02:00
|
|
|
const or_options_t *options = get_options();
|
2009-10-18 00:52:18 +02:00
|
|
|
smartlist_t *usable_nodes;
|
|
|
|
int n_excluded = 0;
|
2004-04-05 02:47:48 +02:00
|
|
|
|
2009-10-18 00:52:18 +02:00
|
|
|
/* We'll keep a separate list of the usable nodes. If this becomes empty,
|
|
|
|
* no nodes are usable. */
|
|
|
|
usable_nodes = smartlist_create();
|
|
|
|
smartlist_add_all(usable_nodes, entry->parsed->intro_nodes);
|
2004-04-05 02:47:48 +02:00
|
|
|
|
2005-06-29 23:46:55 +02:00
|
|
|
again:
|
2009-10-18 00:52:18 +02:00
|
|
|
if (smartlist_len(usable_nodes) == 0) {
|
2011-04-20 22:49:41 +02:00
|
|
|
if (n_excluded && get_options()->StrictNodes && warnings) {
|
2009-10-18 00:52:18 +02:00
|
|
|
/* We only want to warn if StrictNodes is really set. Otherwise
|
|
|
|
* we're just about to retry anyways.
|
|
|
|
*/
|
|
|
|
log_warn(LD_REND, "All introduction points for hidden service are "
|
|
|
|
"at excluded relays, and StrictNodes is set. Skipping.");
|
|
|
|
}
|
|
|
|
smartlist_free(usable_nodes);
|
2005-06-29 23:46:55 +02:00
|
|
|
return NULL;
|
2009-10-18 00:52:18 +02:00
|
|
|
}
|
2004-04-05 02:47:48 +02:00
|
|
|
|
2009-10-18 00:52:18 +02:00
|
|
|
i = crypto_rand_int(smartlist_len(usable_nodes));
|
|
|
|
intro = smartlist_get(usable_nodes, i);
|
2007-12-21 10:28:22 +01:00
|
|
|
/* Do we need to look up the router or is the extend info complete? */
|
|
|
|
if (!intro->extend_info->onion_key) {
|
2010-10-21 17:17:34 +02:00
|
|
|
const node_t *node;
|
2010-10-21 17:08:15 +02:00
|
|
|
if (tor_digest_is_zero(intro->extend_info->identity_digest))
|
2010-10-21 17:17:34 +02:00
|
|
|
node = node_get_by_hex_id(intro->extend_info->nickname);
|
2010-10-21 17:08:15 +02:00
|
|
|
else
|
2010-10-21 17:17:34 +02:00
|
|
|
node = node_get_by_id(intro->extend_info->identity_digest);
|
Initial conversion to use node_t throughout our codebase.
A node_t is an abstraction over routerstatus_t, routerinfo_t, and
microdesc_t. It should try to present a consistent interface to all
of them. There should be a node_t for a server whenever there is
* A routerinfo_t for it in the routerlist
* A routerstatus_t in the current_consensus.
(note that a microdesc_t alone isn't enough to make a node_t exist,
since microdescriptors aren't usable on their own.)
There are three ways to get a node_t right now: looking it up by ID,
looking it up by nickname, and iterating over the whole list of
microdescriptors.
All (or nearly all) functions that are supposed to return "a router"
-- especially those used in building connections and circuits --
should return a node_t, not a routerinfo_t or a routerstatus_t.
A node_t should hold all the *mutable* flags about a node. This
patch moves the is_foo flags from routerinfo_t into node_t. The
flags in routerstatus_t remain, but they get set from the consensus
and should not change.
Some other highlights of this patch are:
* Looking up routerinfo and routerstatus by nickname is now
unified and based on the "look up a node by nickname" function.
This tries to look only at the values from current consensus,
and not get confused by the routerinfo_t->is_named flag, which
could get set for other weird reasons. This changes the
behavior of how authorities (when acting as clients) deal with
nodes that have been listed by nickname.
* I tried not to artificially increase the size of the diff here
by moving functions around. As a result, some functions that
now operate on nodes are now in the wrong file -- they should
get moved to nodelist.c once this refactoring settles down.
This moving should happen as part of a patch that moves
functions AND NOTHING ELSE.
* Some old code is now left around inside #if 0/1 blocks, and
should get removed once I've verified that I don't want it
sitting around to see how we used to do things.
There are still some unimplemented functions: these are flagged
with "UNIMPLEMENTED_NODELIST()." I'll work on filling in the
implementation here, piece by piece.
I wish this patch could have been smaller, but there did not seem to
be any piece of it that was independent from the rest. Moving flags
forces many functions that once returned routerinfo_t * to return
node_t *, which forces their friends to change, and so on.
2010-09-29 21:00:41 +02:00
|
|
|
if (!node) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND, "Unknown router with nickname '%s'; trying another.",
|
2007-12-21 10:28:22 +01:00
|
|
|
intro->extend_info->nickname);
|
2009-10-18 00:52:18 +02:00
|
|
|
smartlist_del(usable_nodes, i);
|
2005-06-29 23:46:55 +02:00
|
|
|
goto again;
|
|
|
|
}
|
2007-12-21 10:28:22 +01:00
|
|
|
extend_info_free(intro->extend_info);
|
Initial conversion to use node_t throughout our codebase.
A node_t is an abstraction over routerstatus_t, routerinfo_t, and
microdesc_t. It should try to present a consistent interface to all
of them. There should be a node_t for a server whenever there is
* A routerinfo_t for it in the routerlist
* A routerstatus_t in the current_consensus.
(note that a microdesc_t alone isn't enough to make a node_t exist,
since microdescriptors aren't usable on their own.)
There are three ways to get a node_t right now: looking it up by ID,
looking it up by nickname, and iterating over the whole list of
microdescriptors.
All (or nearly all) functions that are supposed to return "a router"
-- especially those used in building connections and circuits --
should return a node_t, not a routerinfo_t or a routerstatus_t.
A node_t should hold all the *mutable* flags about a node. This
patch moves the is_foo flags from routerinfo_t into node_t. The
flags in routerstatus_t remain, but they get set from the consensus
and should not change.
Some other highlights of this patch are:
* Looking up routerinfo and routerstatus by nickname is now
unified and based on the "look up a node by nickname" function.
This tries to look only at the values from current consensus,
and not get confused by the routerinfo_t->is_named flag, which
could get set for other weird reasons. This changes the
behavior of how authorities (when acting as clients) deal with
nodes that have been listed by nickname.
* I tried not to artificially increase the size of the diff here
by moving functions around. As a result, some functions that
now operate on nodes are now in the wrong file -- they should
get moved to nodelist.c once this refactoring settles down.
This moving should happen as part of a patch that moves
functions AND NOTHING ELSE.
* Some old code is now left around inside #if 0/1 blocks, and
should get removed once I've verified that I don't want it
sitting around to see how we used to do things.
There are still some unimplemented functions: these are flagged
with "UNIMPLEMENTED_NODELIST()." I'll work on filling in the
implementation here, piece by piece.
I wish this patch could have been smaller, but there did not seem to
be any piece of it that was independent from the rest. Moving flags
forces many functions that once returned routerinfo_t * to return
node_t *, which forces their friends to change, and so on.
2010-09-29 21:00:41 +02:00
|
|
|
intro->extend_info = extend_info_from_node(node);
|
2004-04-08 05:18:03 +02:00
|
|
|
}
|
2009-10-18 00:52:18 +02:00
|
|
|
/* Check if we should refuse to talk to this router. */
|
2011-07-07 17:52:13 +02:00
|
|
|
if (strict &&
|
2009-10-18 00:52:18 +02:00
|
|
|
routerset_contains_extendinfo(options->ExcludeNodes,
|
|
|
|
intro->extend_info)) {
|
|
|
|
n_excluded++;
|
|
|
|
smartlist_del(usable_nodes, i);
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
|
|
|
|
smartlist_free(usable_nodes);
|
2007-12-21 10:28:22 +01:00
|
|
|
return extend_info_dup(intro->extend_info);
|
2004-04-05 02:47:48 +02:00
|
|
|
}
|
2005-06-09 21:03:31 +02:00
|
|
|
|
2011-04-20 22:49:41 +02:00
|
|
|
/** Return true iff any introduction points still listed in <b>entry</b> are
|
|
|
|
* usable. */
|
|
|
|
int
|
|
|
|
rend_client_any_intro_points_usable(const rend_cache_entry_t *entry)
|
|
|
|
{
|
|
|
|
return rend_client_get_random_intro_impl(
|
|
|
|
entry, get_options()->StrictNodes, 0) != NULL;
|
|
|
|
}
|
|
|
|
|
2008-08-12 18:12:26 +02:00
|
|
|
/** Client-side authorizations for hidden services; map of onion address to
|
|
|
|
* rend_service_authorization_t*. */
|
|
|
|
static strmap_t *auth_hid_servs = NULL;
|
|
|
|
|
|
|
|
/** Look up the client-side authorization for the hidden service with
|
|
|
|
* <b>onion_address</b>. Return NULL if no authorization is available for
|
|
|
|
* that address. */
|
|
|
|
rend_service_authorization_t*
|
|
|
|
rend_client_lookup_service_authorization(const char *onion_address)
|
|
|
|
{
|
|
|
|
tor_assert(onion_address);
|
|
|
|
if (!auth_hid_servs) return NULL;
|
|
|
|
return strmap_get(auth_hid_servs, onion_address);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Helper: Free storage held by rend_service_authorization_t. */
|
|
|
|
static void
|
|
|
|
rend_service_authorization_free(rend_service_authorization_t *auth)
|
|
|
|
{
|
|
|
|
tor_free(auth);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Helper for strmap_free. */
|
|
|
|
static void
|
|
|
|
rend_service_authorization_strmap_item_free(void *service_auth)
|
|
|
|
{
|
|
|
|
rend_service_authorization_free(service_auth);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Release all the storage held in auth_hid_servs.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
rend_service_authorization_free_all(void)
|
|
|
|
{
|
|
|
|
if (!auth_hid_servs) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
strmap_free(auth_hid_servs, rend_service_authorization_strmap_item_free);
|
|
|
|
auth_hid_servs = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Parse <b>config_line</b> as a client-side authorization for a hidden
|
|
|
|
* service and add it to the local map of hidden service authorizations.
|
|
|
|
* Return 0 for success and -1 for failure. */
|
|
|
|
int
|
2011-06-14 19:01:38 +02:00
|
|
|
rend_parse_service_authorization(const or_options_t *options,
|
|
|
|
int validate_only)
|
2008-08-12 18:12:26 +02:00
|
|
|
{
|
|
|
|
config_line_t *line;
|
|
|
|
int res = -1;
|
|
|
|
strmap_t *parsed = strmap_new();
|
|
|
|
smartlist_t *sl = smartlist_create();
|
2008-09-05 22:52:15 +02:00
|
|
|
rend_service_authorization_t *auth = NULL;
|
2008-08-12 18:12:26 +02:00
|
|
|
|
|
|
|
for (line = options->HidServAuth; line; line = line->next) {
|
|
|
|
char *onion_address, *descriptor_cookie;
|
|
|
|
char descriptor_cookie_tmp[REND_DESC_COOKIE_LEN+2];
|
|
|
|
char descriptor_cookie_base64ext[REND_DESC_COOKIE_LEN_BASE64+2+1];
|
|
|
|
int auth_type_val = 0;
|
2008-09-05 22:52:15 +02:00
|
|
|
auth = NULL;
|
2008-08-12 18:12:26 +02:00
|
|
|
SMARTLIST_FOREACH(sl, char *, c, tor_free(c););
|
|
|
|
smartlist_clear(sl);
|
|
|
|
smartlist_split_string(sl, line->value, " ",
|
|
|
|
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 3);
|
|
|
|
if (smartlist_len(sl) < 2) {
|
|
|
|
log_warn(LD_CONFIG, "Configuration line does not consist of "
|
|
|
|
"\"onion-address authorization-cookie [service-name]\": "
|
|
|
|
"'%s'", line->value);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
auth = tor_malloc_zero(sizeof(rend_service_authorization_t));
|
|
|
|
/* Parse onion address. */
|
|
|
|
onion_address = smartlist_get(sl, 0);
|
|
|
|
if (strlen(onion_address) != REND_SERVICE_ADDRESS_LEN ||
|
|
|
|
strcmpend(onion_address, ".onion")) {
|
|
|
|
log_warn(LD_CONFIG, "Onion address has wrong format: '%s'",
|
|
|
|
onion_address);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
strlcpy(auth->onion_address, onion_address, REND_SERVICE_ID_LEN_BASE32+1);
|
|
|
|
if (!rend_valid_service_id(auth->onion_address)) {
|
|
|
|
log_warn(LD_CONFIG, "Onion address has wrong format: '%s'",
|
|
|
|
onion_address);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* Parse descriptor cookie. */
|
|
|
|
descriptor_cookie = smartlist_get(sl, 1);
|
|
|
|
if (strlen(descriptor_cookie) != REND_DESC_COOKIE_LEN_BASE64) {
|
|
|
|
log_warn(LD_CONFIG, "Authorization cookie has wrong length: '%s'",
|
|
|
|
descriptor_cookie);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* Add trailing zero bytes (AA) to make base64-decoding happy. */
|
|
|
|
tor_snprintf(descriptor_cookie_base64ext,
|
|
|
|
REND_DESC_COOKIE_LEN_BASE64+2+1,
|
|
|
|
"%sAA", descriptor_cookie);
|
|
|
|
if (base64_decode(descriptor_cookie_tmp, sizeof(descriptor_cookie_tmp),
|
|
|
|
descriptor_cookie_base64ext,
|
|
|
|
strlen(descriptor_cookie_base64ext)) < 0) {
|
|
|
|
log_warn(LD_CONFIG, "Decoding authorization cookie failed: '%s'",
|
|
|
|
descriptor_cookie);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
auth_type_val = (descriptor_cookie_tmp[16] >> 4) + 1;
|
|
|
|
if (auth_type_val < 1 || auth_type_val > 2) {
|
|
|
|
log_warn(LD_CONFIG, "Authorization cookie has unknown authorization "
|
|
|
|
"type encoded.");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
auth->auth_type = auth_type_val == 1 ? REND_BASIC_AUTH : REND_STEALTH_AUTH;
|
|
|
|
memcpy(auth->descriptor_cookie, descriptor_cookie_tmp,
|
|
|
|
REND_DESC_COOKIE_LEN);
|
|
|
|
if (strmap_get(parsed, auth->onion_address)) {
|
|
|
|
log_warn(LD_CONFIG, "Duplicate authorization for the same hidden "
|
|
|
|
"service.");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
strmap_set(parsed, auth->onion_address, auth);
|
2008-09-05 22:52:15 +02:00
|
|
|
auth = NULL;
|
2008-08-12 18:12:26 +02:00
|
|
|
}
|
|
|
|
res = 0;
|
|
|
|
goto done;
|
|
|
|
err:
|
|
|
|
res = -1;
|
|
|
|
done:
|
2009-12-12 08:07:59 +01:00
|
|
|
rend_service_authorization_free(auth);
|
2008-08-12 18:12:26 +02:00
|
|
|
SMARTLIST_FOREACH(sl, char *, c, tor_free(c););
|
|
|
|
smartlist_free(sl);
|
|
|
|
if (!validate_only && res == 0) {
|
|
|
|
rend_service_authorization_free_all();
|
|
|
|
auth_hid_servs = parsed;
|
|
|
|
} else {
|
|
|
|
strmap_free(parsed, rend_service_authorization_strmap_item_free);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|