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-03-31 23:54:56 +02:00
|
|
|
/* See LICENSE for licensing information */
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/**
|
|
|
|
* \file rendservice.c
|
|
|
|
* \brief The hidden-service side of rendezvous functionality.
|
|
|
|
**/
|
2004-03-31 23:54:56 +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 11:54:50 +02:00
|
|
|
#include "directory.h"
|
2010-07-23 20:18:55 +02:00
|
|
|
#include "networkstatus.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-22 00:30:17 +02:00
|
|
|
#include "rendservice.h"
|
2010-07-21 16:17:10 +02:00
|
|
|
#include "router.h"
|
2010-07-23 21:53:11 +02:00
|
|
|
#include "relay.h"
|
2010-07-23 22:57:20 +02:00
|
|
|
#include "rephist.h"
|
2010-07-21 17:08:11 +02:00
|
|
|
#include "routerlist.h"
|
2010-07-23 23:23:43 +02:00
|
|
|
#include "routerparse.h"
|
2004-03-31 23:54:56 +02:00
|
|
|
|
2007-12-21 10:28:22 +01:00
|
|
|
static origin_circuit_t *find_intro_circuit(rend_intro_point_t *intro,
|
2009-05-03 01:04:25 +02:00
|
|
|
const char *pk_digest);
|
2004-04-13 19:16:47 +02:00
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Represents the mapping from a virtual port of a rendezvous service to
|
2004-03-31 23:54:56 +02:00
|
|
|
* a real port on some IP.
|
|
|
|
*/
|
|
|
|
typedef struct rend_service_port_config_t {
|
|
|
|
uint16_t virtual_port;
|
|
|
|
uint16_t real_port;
|
2008-08-05 22:08:19 +02:00
|
|
|
tor_addr_t real_addr;
|
2004-03-31 23:54:56 +02:00
|
|
|
} rend_service_port_config_t;
|
|
|
|
|
2011-10-05 02:34:42 +02:00
|
|
|
/** Try to maintain this many intro points per service by default. */
|
|
|
|
#define NUM_INTRO_POINTS_DEFAULT 3
|
|
|
|
/** Maintain no more than this many intro points per hidden service. */
|
|
|
|
#define NUM_INTRO_POINTS_MAX 10
|
2004-04-03 01:01:00 +02:00
|
|
|
|
2004-05-18 03:53:53 +02:00
|
|
|
/** If we can't build our intro circuits, don't retry for this long. */
|
2006-03-13 00:31:16 +01:00
|
|
|
#define INTRO_CIRC_RETRY_PERIOD (60*5)
|
2004-05-18 03:53:53 +02:00
|
|
|
/** Don't try to build more than this many circuits before giving up
|
|
|
|
* for a while.*/
|
2004-06-02 21:44:41 +02:00
|
|
|
#define MAX_INTRO_CIRCS_PER_PERIOD 10
|
2004-10-30 07:04:52 +02:00
|
|
|
/** How many times will a hidden service operator attempt to connect to
|
|
|
|
* a requested rendezvous point before giving up? */
|
2005-03-19 05:38:59 +01:00
|
|
|
#define MAX_REND_FAILURES 30
|
|
|
|
/** How many seconds should we spend trying to connect to a requested
|
|
|
|
* rendezvous point before giving up? */
|
|
|
|
#define MAX_REND_TIMEOUT 30
|
2004-05-18 03:53:53 +02:00
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Represents a single hidden service running at this OP. */
|
2004-03-31 23:54:56 +02:00
|
|
|
typedef struct rend_service_t {
|
2008-02-12 21:20:52 +01:00
|
|
|
/* Fields specified in config file */
|
2004-05-09 18:47:25 +02:00
|
|
|
char *directory; /**< where in the filesystem it stores it */
|
|
|
|
smartlist_t *ports; /**< List of rend_service_port_config_t */
|
2008-08-08 16:36:11 +02:00
|
|
|
rend_auth_type_t auth_type; /**< Client authorization type or 0 if no client
|
|
|
|
* authorization is performed. */
|
|
|
|
smartlist_t *clients; /**< List of rend_authorized_client_t's of
|
2008-08-09 17:13:28 +02:00
|
|
|
* clients that may access our service. Can be NULL
|
2009-05-27 23:55:51 +02:00
|
|
|
* if no client authorization is performed. */
|
2004-03-31 23:54:56 +02:00
|
|
|
/* Other fields */
|
2008-02-12 21:20:52 +01:00
|
|
|
crypto_pk_env_t *private_key; /**< Permanent hidden-service key. */
|
|
|
|
char service_id[REND_SERVICE_ID_LEN_BASE32+1]; /**< Onion address without
|
|
|
|
* '.onion' */
|
|
|
|
char pk_digest[DIGEST_LEN]; /**< Hash of permanent hidden-service key. */
|
2007-12-21 10:28:22 +01:00
|
|
|
smartlist_t *intro_nodes; /**< List of rend_intro_point_t's we have,
|
2004-05-05 23:32:43 +02:00
|
|
|
* or are trying to establish. */
|
2008-02-12 21:20:52 +01:00
|
|
|
time_t intro_period_started; /**< Start of the current period to build
|
|
|
|
* introduction points. */
|
2009-05-03 01:04:25 +02:00
|
|
|
int n_intro_circuits_launched; /**< Count of intro circuits we have
|
2004-05-18 03:53:53 +02:00
|
|
|
* established in this period. */
|
2011-10-05 02:34:42 +02:00
|
|
|
unsigned int n_intro_points_wanted; /**< Number of intro points this
|
|
|
|
* service wants to have open. */
|
2008-02-12 21:20:52 +01:00
|
|
|
rend_service_descriptor_t *desc; /**< Current hidden service descriptor. */
|
|
|
|
time_t desc_is_dirty; /**< Time at which changes to the hidden service
|
|
|
|
* descriptor content occurred, or 0 if it's
|
|
|
|
* up-to-date. */
|
|
|
|
time_t next_upload_time; /**< Scheduled next hidden service descriptor
|
|
|
|
* upload time. */
|
2009-05-27 23:55:51 +02:00
|
|
|
/** Map from digests of Diffie-Hellman values INTRODUCE2 to time_t of when
|
2008-09-24 16:44:29 +02:00
|
|
|
* they were received; used to prevent replays. */
|
|
|
|
digestmap_t *accepted_intros;
|
|
|
|
/** Time at which we last removed expired values from accepted_intros. */
|
|
|
|
time_t last_cleaned_accepted_intros;
|
2004-03-31 23:54:56 +02:00
|
|
|
} rend_service_t;
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** A list of rend_service_t's for services run on this OP.
|
2004-03-31 23:54:56 +02:00
|
|
|
*/
|
|
|
|
static smartlist_t *rend_service_list = NULL;
|
|
|
|
|
2005-08-13 02:22:07 +02:00
|
|
|
/** Return the number of rendezvous services we have configured. */
|
2005-09-30 03:09:52 +02:00
|
|
|
int
|
|
|
|
num_rend_services(void)
|
|
|
|
{
|
2005-08-13 02:22:07 +02:00
|
|
|
if (!rend_service_list)
|
|
|
|
return 0;
|
|
|
|
return smartlist_len(rend_service_list);
|
|
|
|
}
|
|
|
|
|
2008-08-08 16:36:11 +02:00
|
|
|
/** Helper: free storage held by a single service authorized client entry. */
|
|
|
|
static void
|
|
|
|
rend_authorized_client_free(rend_authorized_client_t *client)
|
|
|
|
{
|
2009-09-28 16:37:01 +02:00
|
|
|
if (!client)
|
|
|
|
return;
|
2008-08-08 16:36:11 +02:00
|
|
|
if (client->client_key)
|
|
|
|
crypto_free_pk_env(client->client_key);
|
|
|
|
tor_free(client->client_name);
|
|
|
|
tor_free(client);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Helper for strmap_free. */
|
|
|
|
static void
|
|
|
|
rend_authorized_client_strmap_item_free(void *authorized_client)
|
|
|
|
{
|
|
|
|
rend_authorized_client_free(authorized_client);
|
|
|
|
}
|
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/** Release the storage held by <b>service</b>.
|
2004-04-06 05:44:36 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static void
|
|
|
|
rend_service_free(rend_service_t *service)
|
2004-03-31 23:54:56 +02:00
|
|
|
{
|
2009-09-28 16:37:01 +02:00
|
|
|
if (!service)
|
|
|
|
return;
|
|
|
|
|
2004-04-06 05:44:36 +02:00
|
|
|
tor_free(service->directory);
|
|
|
|
SMARTLIST_FOREACH(service->ports, void*, p, tor_free(p));
|
|
|
|
smartlist_free(service->ports);
|
|
|
|
if (service->private_key)
|
|
|
|
crypto_free_pk_env(service->private_key);
|
2007-12-21 10:28:22 +01:00
|
|
|
if (service->intro_nodes) {
|
|
|
|
SMARTLIST_FOREACH(service->intro_nodes, rend_intro_point_t *, intro,
|
|
|
|
rend_intro_point_free(intro););
|
|
|
|
smartlist_free(service->intro_nodes);
|
|
|
|
}
|
2009-12-12 08:07:59 +01:00
|
|
|
|
|
|
|
rend_service_descriptor_free(service->desc);
|
2008-08-08 16:36:11 +02:00
|
|
|
if (service->clients) {
|
|
|
|
SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *, c,
|
|
|
|
rend_authorized_client_free(c););
|
|
|
|
smartlist_free(service->clients);
|
|
|
|
}
|
2009-12-12 08:07:59 +01:00
|
|
|
digestmap_free(service->accepted_intros, _tor_free);
|
2004-04-06 05:44:36 +02:00
|
|
|
tor_free(service);
|
2004-03-31 23:54:56 +02:00
|
|
|
}
|
|
|
|
|
2005-02-27 23:08:01 +01:00
|
|
|
/** Release all the storage held in rend_service_list.
|
2004-04-06 05:44:36 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
|
|
|
rend_service_free_all(void)
|
2004-03-31 23:54:56 +02:00
|
|
|
{
|
2009-09-28 16:37:01 +02:00
|
|
|
if (!rend_service_list)
|
2004-03-31 23:54:56 +02:00
|
|
|
return;
|
2009-09-28 16:37:01 +02:00
|
|
|
|
2004-04-03 02:58:54 +02:00
|
|
|
SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr,
|
|
|
|
rend_service_free(ptr));
|
2004-03-31 23:54:56 +02:00
|
|
|
smartlist_free(rend_service_list);
|
2005-02-11 00:18:39 +01:00
|
|
|
rend_service_list = NULL;
|
2004-03-31 23:54:56 +02:00
|
|
|
}
|
|
|
|
|
2004-05-10 09:27:29 +02:00
|
|
|
/** Validate <b>service</b> and add it to rend_service_list if possible.
|
2004-04-06 05:44:36 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static void
|
2007-11-29 16:30:32 +01:00
|
|
|
rend_add_service(rend_service_t *service)
|
2004-03-31 23:54:56 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
rend_service_port_config_t *p;
|
|
|
|
|
2007-11-29 16:25:04 +01:00
|
|
|
service->intro_nodes = smartlist_create();
|
2004-04-03 01:01:00 +02:00
|
|
|
|
2008-08-09 17:13:28 +02:00
|
|
|
if (service->auth_type != REND_NO_AUTH &&
|
|
|
|
smartlist_len(service->clients) == 0) {
|
2008-08-08 16:36:11 +02:00
|
|
|
log_warn(LD_CONFIG, "Hidden service with client authorization but no "
|
|
|
|
"clients; ignoring.");
|
|
|
|
rend_service_free(service);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-04-03 02:58:54 +02:00
|
|
|
if (!smartlist_len(service->ports)) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_CONFIG, "Hidden service with no ports configured; ignoring.");
|
2004-03-31 23:54:56 +02:00
|
|
|
rend_service_free(service);
|
|
|
|
} else {
|
2011-05-29 00:54:59 +02:00
|
|
|
int dupe = 0;
|
|
|
|
/* XXX This duplicate check has two problems:
|
|
|
|
*
|
|
|
|
* a) It's O(n^2), but the same comment from the bottom of
|
|
|
|
* rend_config_services() should apply.
|
|
|
|
*
|
|
|
|
* b) We only compare directory paths as strings, so we can't
|
|
|
|
* detect two distinct paths that specify the same directory
|
|
|
|
* (which can arise from symlinks, case-insensitivity, bind
|
|
|
|
* mounts, etc.).
|
|
|
|
*
|
|
|
|
* It also can't detect that two separate Tor instances are trying
|
|
|
|
* to use the same HiddenServiceDir; for that, we would need a
|
|
|
|
* lock file. But this is enough to detect a simple mistake that
|
|
|
|
* at least one person has actually made.
|
|
|
|
*/
|
|
|
|
SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr,
|
|
|
|
dupe = dupe ||
|
|
|
|
!strcmp(ptr->directory, service->directory));
|
|
|
|
if (dupe) {
|
|
|
|
log_warn(LD_REND, "Another hidden service is already configured for "
|
|
|
|
"directory %s, ignoring.", service->directory);
|
|
|
|
rend_service_free(service);
|
|
|
|
return;
|
|
|
|
}
|
2004-03-31 23:54:56 +02:00
|
|
|
smartlist_add(rend_service_list, service);
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(LD_REND,"Configuring service with directory \"%s\"",
|
|
|
|
service->directory);
|
2004-04-03 02:58:54 +02:00
|
|
|
for (i = 0; i < smartlist_len(service->ports); ++i) {
|
|
|
|
p = smartlist_get(service->ports, i);
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(LD_REND,"Service maps port %d to %s:%d",
|
2008-08-05 22:08:19 +02:00
|
|
|
p->virtual_port, fmt_addr(&p->real_addr), p->real_port);
|
2004-03-31 23:54:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Parses a real-port to virtual-port mapping and returns a new
|
2004-04-06 05:44:36 +02:00
|
|
|
* rend_service_port_config_t.
|
|
|
|
*
|
|
|
|
* The format is: VirtualPort (IP|RealPort|IP:RealPort)?
|
2004-05-10 06:34:48 +02:00
|
|
|
*
|
|
|
|
* IP defaults to 127.0.0.1; RealPort defaults to VirtualPort.
|
2004-03-31 23:54:56 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static rend_service_port_config_t *
|
|
|
|
parse_port_config(const char *string)
|
2004-03-31 23:54:56 +02:00
|
|
|
{
|
2005-08-06 23:35:04 +02:00
|
|
|
smartlist_t *sl;
|
2004-10-12 17:52:09 +02:00
|
|
|
int virtport;
|
2004-10-13 21:53:34 +02:00
|
|
|
int realport;
|
|
|
|
uint16_t p;
|
2008-08-05 22:08:19 +02:00
|
|
|
tor_addr_t addr;
|
2005-08-06 23:35:04 +02:00
|
|
|
const char *addrport;
|
|
|
|
rend_service_port_config_t *result = NULL;
|
|
|
|
|
|
|
|
sl = smartlist_create();
|
2005-12-14 21:40:40 +01:00
|
|
|
smartlist_split_string(sl, string, " ",
|
|
|
|
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
|
2005-08-06 23:35:04 +02:00
|
|
|
if (smartlist_len(sl) < 1 || smartlist_len(sl) > 2) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_CONFIG, "Bad syntax in hidden service port configuration.");
|
2005-08-06 23:35:04 +02:00
|
|
|
goto err;
|
2004-03-31 23:54:56 +02:00
|
|
|
}
|
2005-08-06 23:35:04 +02:00
|
|
|
|
2008-02-06 01:54:47 +01:00
|
|
|
virtport = (int)tor_parse_long(smartlist_get(sl,0), 10, 1, 65535, NULL,NULL);
|
|
|
|
if (!virtport) {
|
|
|
|
log_warn(LD_CONFIG, "Missing or invalid port %s in hidden service port "
|
|
|
|
"configuration", escaped(smartlist_get(sl,0)));
|
2005-08-06 23:35:04 +02:00
|
|
|
goto err;
|
2004-03-31 23:54:56 +02:00
|
|
|
}
|
2005-08-06 23:35:04 +02:00
|
|
|
|
|
|
|
if (smartlist_len(sl) == 1) {
|
2004-03-31 23:54:56 +02:00
|
|
|
/* No addr:port part; use default. */
|
|
|
|
realport = virtport;
|
2008-08-05 22:08:19 +02:00
|
|
|
tor_addr_from_ipv4h(&addr, 0x7F000001u); /* 127.0.0.1 */
|
2004-10-12 17:52:09 +02:00
|
|
|
} else {
|
2005-08-06 23:35:04 +02:00
|
|
|
addrport = smartlist_get(sl,1);
|
|
|
|
if (strchr(addrport, ':') || strchr(addrport, '.')) {
|
2008-08-05 22:08:19 +02:00
|
|
|
if (tor_addr_port_parse(addrport, &addr, &p)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_CONFIG,"Unparseable address in hidden service port "
|
|
|
|
"configuration.");
|
2005-08-06 23:35:04 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
realport = p?p:virtport;
|
|
|
|
} else {
|
|
|
|
/* No addr:port, no addr -- must be port. */
|
2008-02-06 01:54:47 +01:00
|
|
|
realport = (int)tor_parse_long(addrport, 10, 1, 65535, NULL, NULL);
|
|
|
|
if (!realport) {
|
|
|
|
log_warn(LD_CONFIG,"Unparseable or out-of-range port %s in hidden "
|
|
|
|
"service port configuration.", escaped(addrport));
|
2005-08-06 23:35:04 +02:00
|
|
|
goto err;
|
2008-02-06 01:54:47 +01:00
|
|
|
}
|
2008-08-05 22:08:19 +02:00
|
|
|
tor_addr_from_ipv4h(&addr, 0x7F000001u); /* Default to 127.0.0.1 */
|
2004-10-12 21:01:53 +02:00
|
|
|
}
|
2004-03-31 23:54:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
result = tor_malloc(sizeof(rend_service_port_config_t));
|
|
|
|
result->virtual_port = virtport;
|
|
|
|
result->real_port = realport;
|
2008-08-05 22:08:19 +02:00
|
|
|
tor_addr_copy(&result->real_addr, &addr);
|
2005-08-06 23:35:04 +02:00
|
|
|
err:
|
|
|
|
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
|
|
|
|
smartlist_free(sl);
|
2004-03-31 23:54:56 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Set up rend_service_list, based on the values of HiddenServiceDir and
|
2004-05-10 06:34:48 +02:00
|
|
|
* HiddenServicePort in <b>options</b>. Return 0 on success and -1 on
|
2004-11-09 00:12:40 +01:00
|
|
|
* failure. (If <b>validate_only</b> is set, parse, warn and return as
|
|
|
|
* normal, but don't actually change the configured services.)
|
2004-03-31 23:54:56 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
|
|
|
rend_config_services(or_options_t *options, int validate_only)
|
2004-03-31 23:54:56 +02:00
|
|
|
{
|
2005-07-22 23:12:10 +02:00
|
|
|
config_line_t *line;
|
2004-03-31 23:54:56 +02:00
|
|
|
rend_service_t *service = NULL;
|
|
|
|
rend_service_port_config_t *portcfg;
|
2008-12-10 19:16:58 +01:00
|
|
|
smartlist_t *old_service_list = NULL;
|
2004-11-09 00:12:40 +01:00
|
|
|
|
2005-02-11 00:18:39 +01:00
|
|
|
if (!validate_only) {
|
2008-12-10 19:16:58 +01:00
|
|
|
old_service_list = rend_service_list;
|
2005-02-11 00:18:39 +01:00
|
|
|
rend_service_list = smartlist_create();
|
|
|
|
}
|
2004-03-31 23:54:56 +02:00
|
|
|
|
|
|
|
for (line = options->RendConfigLines; line; line = line->next) {
|
|
|
|
if (!strcasecmp(line->key, "HiddenServiceDir")) {
|
2009-09-17 07:58:39 +02:00
|
|
|
if (service) { /* register the one we just finished parsing */
|
2004-11-09 00:12:40 +01:00
|
|
|
if (validate_only)
|
|
|
|
rend_service_free(service);
|
|
|
|
else
|
2007-11-29 16:30:32 +01:00
|
|
|
rend_add_service(service);
|
2004-11-09 00:12:40 +01:00
|
|
|
}
|
2004-03-31 23:54:56 +02:00
|
|
|
service = tor_malloc_zero(sizeof(rend_service_t));
|
|
|
|
service->directory = tor_strdup(line->value);
|
|
|
|
service->ports = smartlist_create();
|
2004-05-18 03:53:53 +02:00
|
|
|
service->intro_period_started = time(NULL);
|
2011-10-05 02:34:42 +02:00
|
|
|
service->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT;
|
2004-04-03 00:06:46 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!service) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive",
|
|
|
|
line->key);
|
2004-04-03 00:06:46 +02:00
|
|
|
rend_service_free(service);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!strcasecmp(line->key, "HiddenServicePort")) {
|
2004-03-31 23:54:56 +02:00
|
|
|
portcfg = parse_port_config(line->value);
|
|
|
|
if (!portcfg) {
|
2004-04-03 04:14:20 +02:00
|
|
|
rend_service_free(service);
|
|
|
|
return -1;
|
2004-03-31 23:54:56 +02:00
|
|
|
}
|
|
|
|
smartlist_add(service->ports, portcfg);
|
2008-08-08 16:36:11 +02:00
|
|
|
} else if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) {
|
|
|
|
/* Parse auth type and comma-separated list of client names and add a
|
|
|
|
* rend_authorized_client_t for each client to the service's list
|
|
|
|
* of authorized clients. */
|
|
|
|
smartlist_t *type_names_split, *clients;
|
|
|
|
const char *authname;
|
2008-08-09 17:13:28 +02:00
|
|
|
int num_clients;
|
|
|
|
if (service->auth_type != REND_NO_AUTH) {
|
2008-08-08 16:36:11 +02:00
|
|
|
log_warn(LD_CONFIG, "Got multiple HiddenServiceAuthorizeClient "
|
|
|
|
"lines for a single service.");
|
|
|
|
rend_service_free(service);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
type_names_split = smartlist_create();
|
2008-08-09 17:13:28 +02:00
|
|
|
smartlist_split_string(type_names_split, line->value, " ", 0, 2);
|
2008-08-08 16:36:11 +02:00
|
|
|
if (smartlist_len(type_names_split) < 1) {
|
|
|
|
log_warn(LD_BUG, "HiddenServiceAuthorizeClient has no value. This "
|
|
|
|
"should have been prevented when parsing the "
|
|
|
|
"configuration.");
|
|
|
|
smartlist_free(type_names_split);
|
|
|
|
rend_service_free(service);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
authname = smartlist_get(type_names_split, 0);
|
2008-08-09 17:13:28 +02:00
|
|
|
if (!strcasecmp(authname, "basic")) {
|
2008-08-08 16:36:11 +02:00
|
|
|
service->auth_type = REND_BASIC_AUTH;
|
2008-08-09 17:13:28 +02:00
|
|
|
} else if (!strcasecmp(authname, "stealth")) {
|
2008-08-08 16:36:11 +02:00
|
|
|
service->auth_type = REND_STEALTH_AUTH;
|
|
|
|
} else {
|
|
|
|
log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
|
2008-08-09 17:13:28 +02:00
|
|
|
"unrecognized auth-type '%s'. Only 'basic' or 'stealth' "
|
|
|
|
"are recognized.",
|
2008-08-08 16:36:11 +02:00
|
|
|
(char *) smartlist_get(type_names_split, 0));
|
|
|
|
SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
|
|
|
|
smartlist_free(type_names_split);
|
|
|
|
rend_service_free(service);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
service->clients = smartlist_create();
|
|
|
|
if (smartlist_len(type_names_split) < 2) {
|
|
|
|
log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
|
2008-08-09 17:13:28 +02:00
|
|
|
"auth-type '%s', but no client names.",
|
2008-09-24 16:44:29 +02:00
|
|
|
service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth");
|
2008-08-08 16:36:11 +02:00
|
|
|
SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
|
|
|
|
smartlist_free(type_names_split);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
clients = smartlist_create();
|
|
|
|
smartlist_split_string(clients, smartlist_get(type_names_split, 1),
|
2008-08-09 17:13:28 +02:00
|
|
|
",", SPLIT_SKIP_SPACE, 0);
|
2008-08-08 16:36:11 +02:00
|
|
|
SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
|
|
|
|
smartlist_free(type_names_split);
|
2008-08-09 17:13:28 +02:00
|
|
|
/* Remove duplicate client names. */
|
|
|
|
num_clients = smartlist_len(clients);
|
|
|
|
smartlist_sort_strings(clients);
|
|
|
|
smartlist_uniq_strings(clients);
|
|
|
|
if (smartlist_len(clients) < num_clients) {
|
|
|
|
log_info(LD_CONFIG, "HiddenServiceAuthorizeClient contains %d "
|
|
|
|
"duplicate client name(s); removing.",
|
|
|
|
num_clients - smartlist_len(clients));
|
|
|
|
num_clients = smartlist_len(clients);
|
|
|
|
}
|
2008-08-08 16:36:11 +02:00
|
|
|
SMARTLIST_FOREACH_BEGIN(clients, const char *, client_name)
|
|
|
|
{
|
|
|
|
rend_authorized_client_t *client;
|
|
|
|
size_t len = strlen(client_name);
|
2008-08-09 17:13:28 +02:00
|
|
|
if (len < 1 || len > REND_CLIENTNAME_MAX_LEN) {
|
2008-08-08 16:36:11 +02:00
|
|
|
log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains an "
|
|
|
|
"illegal client name: '%s'. Length must be "
|
2008-08-09 17:13:28 +02:00
|
|
|
"between 1 and %d characters.",
|
|
|
|
client_name, REND_CLIENTNAME_MAX_LEN);
|
2008-08-08 16:36:11 +02:00
|
|
|
SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp));
|
|
|
|
smartlist_free(clients);
|
|
|
|
rend_service_free(service);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (strspn(client_name, REND_LEGAL_CLIENTNAME_CHARACTERS) != len) {
|
|
|
|
log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains an "
|
|
|
|
"illegal client name: '%s'. Valid "
|
2011-09-10 22:15:52 +02:00
|
|
|
"characters are [A-Za-z0-9+_-].",
|
2008-08-08 16:36:11 +02:00
|
|
|
client_name);
|
|
|
|
SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp));
|
|
|
|
smartlist_free(clients);
|
|
|
|
rend_service_free(service);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
client = tor_malloc_zero(sizeof(rend_authorized_client_t));
|
|
|
|
client->client_name = tor_strdup(client_name);
|
|
|
|
smartlist_add(service->clients, client);
|
|
|
|
log_debug(LD_REND, "Adding client name '%s'", client_name);
|
|
|
|
}
|
|
|
|
SMARTLIST_FOREACH_END(client_name);
|
|
|
|
SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp));
|
|
|
|
smartlist_free(clients);
|
|
|
|
/* Ensure maximum number of clients. */
|
|
|
|
if ((service->auth_type == REND_BASIC_AUTH &&
|
|
|
|
smartlist_len(service->clients) > 512) ||
|
|
|
|
(service->auth_type == REND_STEALTH_AUTH &&
|
|
|
|
smartlist_len(service->clients) > 16)) {
|
|
|
|
log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains %d "
|
|
|
|
"client authorization entries, but only a "
|
|
|
|
"maximum of %d entries is allowed for "
|
2008-08-09 17:13:28 +02:00
|
|
|
"authorization type '%s'.",
|
2008-08-08 16:36:11 +02:00
|
|
|
smartlist_len(service->clients),
|
|
|
|
service->auth_type == REND_BASIC_AUTH ? 512 : 16,
|
2008-09-24 16:44:29 +02:00
|
|
|
service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth");
|
2008-08-08 16:36:11 +02:00
|
|
|
rend_service_free(service);
|
|
|
|
return -1;
|
|
|
|
}
|
2007-10-31 21:48:06 +01:00
|
|
|
} else {
|
|
|
|
tor_assert(!strcasecmp(line->key, "HiddenServiceVersion"));
|
2009-05-02 23:31:58 +02:00
|
|
|
if (strcmp(line->value, "2")) {
|
|
|
|
log_warn(LD_CONFIG,
|
|
|
|
"The only supported HiddenServiceVersion is 2.");
|
|
|
|
rend_service_free(service);
|
|
|
|
return -1;
|
2007-10-31 21:48:06 +01:00
|
|
|
}
|
2004-03-31 23:54:56 +02:00
|
|
|
}
|
|
|
|
}
|
2004-11-09 00:12:40 +01:00
|
|
|
if (service) {
|
|
|
|
if (validate_only)
|
|
|
|
rend_service_free(service);
|
|
|
|
else
|
2007-11-29 16:30:32 +01:00
|
|
|
rend_add_service(service);
|
2004-11-09 00:12:40 +01:00
|
|
|
}
|
2004-03-31 23:54:56 +02:00
|
|
|
|
2008-12-10 19:16:58 +01:00
|
|
|
/* If this is a reload and there were hidden services configured before,
|
|
|
|
* keep the introduction points that are still needed and close the
|
|
|
|
* other ones. */
|
|
|
|
if (old_service_list && !validate_only) {
|
|
|
|
smartlist_t *surviving_services = smartlist_create();
|
|
|
|
circuit_t *circ;
|
|
|
|
|
|
|
|
/* Copy introduction points to new services. */
|
|
|
|
/* XXXX This is O(n^2), but it's only called on reconfigure, so it's
|
|
|
|
* probably ok? */
|
|
|
|
SMARTLIST_FOREACH(rend_service_list, rend_service_t *, new, {
|
|
|
|
SMARTLIST_FOREACH(old_service_list, rend_service_t *, old, {
|
2009-05-02 23:31:58 +02:00
|
|
|
if (!strcmp(old->directory, new->directory)) {
|
2008-12-10 19:16:58 +01:00
|
|
|
smartlist_add_all(new->intro_nodes, old->intro_nodes);
|
|
|
|
smartlist_clear(old->intro_nodes);
|
|
|
|
smartlist_add(surviving_services, old);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
/* Close introduction circuits of services we don't serve anymore. */
|
|
|
|
/* XXXX it would be nicer if we had a nicer abstraction to use here,
|
|
|
|
* so we could just iterate over the list of services to close, but
|
|
|
|
* once again, this isn't critical-path code. */
|
|
|
|
for (circ = _circuit_get_global_list(); circ; circ = circ->next) {
|
|
|
|
if (!circ->marked_for_close &&
|
|
|
|
circ->state == CIRCUIT_STATE_OPEN &&
|
|
|
|
(circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
|
|
|
|
circ->purpose == CIRCUIT_PURPOSE_S_INTRO)) {
|
|
|
|
origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ);
|
|
|
|
int keep_it = 0;
|
|
|
|
tor_assert(oc->rend_data);
|
|
|
|
SMARTLIST_FOREACH(surviving_services, rend_service_t *, ptr, {
|
2011-05-10 22:23:43 +02:00
|
|
|
if (tor_memeq(ptr->pk_digest, oc->rend_data->rend_pk_digest,
|
2009-05-02 23:31:58 +02:00
|
|
|
DIGEST_LEN)) {
|
2008-12-10 19:16:58 +01:00
|
|
|
keep_it = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (keep_it)
|
|
|
|
continue;
|
2009-05-03 00:09:18 +02:00
|
|
|
log_info(LD_REND, "Closing intro point %s for service %s.",
|
2011-05-16 03:58:46 +02:00
|
|
|
safe_str_client(extend_info_describe(
|
|
|
|
oc->build_state->chosen_exit)),
|
2009-05-03 00:09:18 +02:00
|
|
|
oc->rend_data->onion_address);
|
2008-12-10 19:16:58 +01:00
|
|
|
circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
|
|
|
|
/* XXXX Is there another reason we should use here? */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
smartlist_free(surviving_services);
|
|
|
|
SMARTLIST_FOREACH(old_service_list, rend_service_t *, ptr,
|
|
|
|
rend_service_free(ptr));
|
|
|
|
smartlist_free(old_service_list);
|
|
|
|
}
|
|
|
|
|
2004-03-31 23:54:56 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/** Replace the old value of <b>service</b>-\>desc with one that reflects
|
2004-04-03 02:58:54 +02:00
|
|
|
* the other fields in service.
|
2004-04-03 01:01:00 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static void
|
|
|
|
rend_service_update_descriptor(rend_service_t *service)
|
2004-04-03 01:01:00 +02:00
|
|
|
{
|
|
|
|
rend_service_descriptor_t *d;
|
2006-07-23 09:37:35 +02:00
|
|
|
origin_circuit_t *circ;
|
2007-12-21 10:28:22 +01:00
|
|
|
int i;
|
2009-12-12 08:07:59 +01:00
|
|
|
|
|
|
|
rend_service_descriptor_free(service->desc);
|
|
|
|
service->desc = NULL;
|
|
|
|
|
2007-10-28 20:48:16 +01:00
|
|
|
d = service->desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
|
2004-04-03 01:01:00 +02:00
|
|
|
d->pk = crypto_pk_dup_key(service->private_key);
|
|
|
|
d->timestamp = time(NULL);
|
2007-12-21 10:28:22 +01:00
|
|
|
d->intro_nodes = smartlist_create();
|
2008-08-19 17:41:28 +02:00
|
|
|
/* Support intro protocols 2 and 3. */
|
|
|
|
d->protocols = (1 << 2) + (1 << 3);
|
2007-10-28 20:48:16 +01:00
|
|
|
|
2007-12-21 10:28:22 +01:00
|
|
|
for (i = 0; i < smartlist_len(service->intro_nodes); ++i) {
|
|
|
|
rend_intro_point_t *intro_svc = smartlist_get(service->intro_nodes, i);
|
|
|
|
rend_intro_point_t *intro_desc;
|
2009-05-03 01:04:25 +02:00
|
|
|
circ = find_intro_circuit(intro_svc, service->pk_digest);
|
2007-12-21 10:28:22 +01:00
|
|
|
if (!circ || circ->_base.purpose != CIRCUIT_PURPOSE_S_INTRO)
|
2004-09-20 05:07:11 +02:00
|
|
|
continue;
|
2007-12-21 10:28:22 +01:00
|
|
|
|
2011-10-06 00:36:47 +02:00
|
|
|
/* We have an entirely established intro circuit. Publish it in
|
|
|
|
* our descriptor. */
|
2007-12-21 10:28:22 +01:00
|
|
|
intro_desc = tor_malloc_zero(sizeof(rend_intro_point_t));
|
|
|
|
intro_desc->extend_info = extend_info_dup(intro_svc->extend_info);
|
|
|
|
if (intro_svc->intro_key)
|
|
|
|
intro_desc->intro_key = crypto_pk_dup_key(intro_svc->intro_key);
|
|
|
|
smartlist_add(d->intro_nodes, intro_desc);
|
2011-10-06 00:43:02 +02:00
|
|
|
|
|
|
|
if (intro_svc->time_published == -1) {
|
|
|
|
/* We are publishing this intro point in a descriptor for the
|
|
|
|
* first time -- note the current time in the service's copy of
|
|
|
|
* the intro point. */
|
|
|
|
intro_svc->time_published = time(NULL);
|
|
|
|
}
|
2004-04-03 01:01:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-08 16:36:11 +02:00
|
|
|
/** Load and/or generate private keys for all hidden services, possibly
|
|
|
|
* including keys for client authorization. Return 0 on success, -1 on
|
|
|
|
* failure.
|
2004-03-31 23:54:56 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
|
|
|
rend_service_load_keys(void)
|
2004-03-31 23:54:56 +02:00
|
|
|
{
|
2008-08-08 18:45:22 +02:00
|
|
|
int r = 0;
|
2004-03-31 23:54:56 +02:00
|
|
|
char fname[512];
|
2008-08-08 16:36:11 +02:00
|
|
|
char buf[1500];
|
2004-03-31 23:54:56 +02:00
|
|
|
|
2008-08-08 16:36:11 +02:00
|
|
|
SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, s) {
|
2004-03-31 23:54:56 +02:00
|
|
|
if (s->private_key)
|
|
|
|
continue;
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND, "Loading hidden-service keys from \"%s\"",
|
|
|
|
s->directory);
|
2004-04-03 02:58:54 +02:00
|
|
|
|
2004-03-31 23:54:56 +02:00
|
|
|
/* Check/create directory */
|
2011-06-14 18:18:32 +02:00
|
|
|
if (check_private_dir(s->directory, CPD_CREATE, get_options()->User) < 0)
|
2004-03-31 23:54:56 +02:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Load key */
|
2004-04-06 05:44:36 +02:00
|
|
|
if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
|
2007-03-09 22:39:30 +01:00
|
|
|
strlcat(fname,PATH_SEPARATOR"private_key",sizeof(fname))
|
|
|
|
>= sizeof(fname)) {
|
2006-09-30 00:51:47 +02:00
|
|
|
log_warn(LD_CONFIG, "Directory name too long to store key file: \"%s\".",
|
|
|
|
s->directory);
|
2004-03-31 23:54:56 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2007-05-29 19:31:13 +02:00
|
|
|
s->private_key = init_key_from_file(fname, 1, LOG_ERR);
|
2004-03-31 23:54:56 +02:00
|
|
|
if (!s->private_key)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Create service file */
|
|
|
|
if (rend_get_service_id(s->private_key, s->service_id)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
|
2004-03-31 23:54:56 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-04-01 05:34:05 +02:00
|
|
|
if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) {
|
2007-03-04 21:11:46 +01:00
|
|
|
log_warn(LD_BUG, "Couldn't compute hash of public key.");
|
2004-04-01 05:34:05 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-04-06 05:44:36 +02:00
|
|
|
if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
|
2007-03-09 22:39:30 +01:00
|
|
|
strlcat(fname,PATH_SEPARATOR"hostname",sizeof(fname))
|
|
|
|
>= sizeof(fname)) {
|
2006-09-30 00:51:47 +02:00
|
|
|
log_warn(LD_CONFIG, "Directory name too long to store hostname file:"
|
|
|
|
" \"%s\".", s->directory);
|
2004-03-31 23:54:56 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-10-27 08:37:34 +02:00
|
|
|
tor_snprintf(buf, sizeof(buf),"%s.onion\n", s->service_id);
|
2008-08-08 16:36:11 +02:00
|
|
|
if (write_str_to_file(fname,buf,0)<0) {
|
|
|
|
log_warn(LD_CONFIG, "Could not write onion address to hostname file.");
|
2004-03-31 23:54:56 +02:00
|
|
|
return -1;
|
2008-08-08 16:36:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If client authorization is configured, load or generate keys. */
|
2008-08-09 17:13:28 +02:00
|
|
|
if (s->auth_type != REND_NO_AUTH) {
|
2008-08-08 16:36:11 +02:00
|
|
|
char *client_keys_str = NULL;
|
|
|
|
strmap_t *parsed_clients = strmap_new();
|
|
|
|
char cfname[512];
|
|
|
|
FILE *cfile, *hfile;
|
|
|
|
open_file_t *open_cfile = NULL, *open_hfile = NULL;
|
|
|
|
|
|
|
|
/* Load client keys and descriptor cookies, if available. */
|
|
|
|
if (tor_snprintf(cfname, sizeof(cfname), "%s"PATH_SEPARATOR"client_keys",
|
|
|
|
s->directory)<0) {
|
|
|
|
log_warn(LD_CONFIG, "Directory name too long to store client keys "
|
|
|
|
"file: \"%s\".", s->directory);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
client_keys_str = read_file_to_str(cfname, RFTS_IGNORE_MISSING, NULL);
|
|
|
|
if (client_keys_str) {
|
|
|
|
if (rend_parse_client_keys(parsed_clients, client_keys_str) < 0) {
|
|
|
|
log_warn(LD_CONFIG, "Previously stored client_keys file could not "
|
|
|
|
"be parsed.");
|
|
|
|
goto err;
|
|
|
|
} else {
|
|
|
|
log_info(LD_CONFIG, "Parsed %d previously stored client entries.",
|
|
|
|
strmap_size(parsed_clients));
|
|
|
|
tor_free(client_keys_str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepare client_keys and hostname files. */
|
2011-07-19 03:12:31 +02:00
|
|
|
if (!(cfile = start_writing_to_stdio_file(cfname,
|
|
|
|
OPEN_FLAGS_REPLACE | O_TEXT,
|
2008-08-08 16:36:11 +02:00
|
|
|
0600, &open_cfile))) {
|
|
|
|
log_warn(LD_CONFIG, "Could not open client_keys file %s",
|
|
|
|
escaped(cfname));
|
|
|
|
goto err;
|
|
|
|
}
|
2011-07-19 03:12:31 +02:00
|
|
|
if (!(hfile = start_writing_to_stdio_file(fname,
|
|
|
|
OPEN_FLAGS_REPLACE | O_TEXT,
|
2008-08-08 16:36:11 +02:00
|
|
|
0600, &open_hfile))) {
|
|
|
|
log_warn(LD_CONFIG, "Could not open hostname file %s", escaped(fname));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Either use loaded keys for configured clients or generate new
|
|
|
|
* ones if a client is new. */
|
|
|
|
SMARTLIST_FOREACH_BEGIN(s->clients, rend_authorized_client_t *, client)
|
|
|
|
{
|
|
|
|
char desc_cook_out[3*REND_DESC_COOKIE_LEN_BASE64+1];
|
|
|
|
char service_id[16+1];
|
|
|
|
rend_authorized_client_t *parsed =
|
|
|
|
strmap_get(parsed_clients, client->client_name);
|
|
|
|
int written;
|
|
|
|
size_t len;
|
|
|
|
/* Copy descriptor cookie from parsed entry or create new one. */
|
|
|
|
if (parsed) {
|
|
|
|
memcpy(client->descriptor_cookie, parsed->descriptor_cookie,
|
|
|
|
REND_DESC_COOKIE_LEN);
|
|
|
|
} else {
|
|
|
|
crypto_rand(client->descriptor_cookie, REND_DESC_COOKIE_LEN);
|
|
|
|
}
|
|
|
|
if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1,
|
|
|
|
client->descriptor_cookie,
|
|
|
|
REND_DESC_COOKIE_LEN) < 0) {
|
|
|
|
log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
|
|
|
|
strmap_free(parsed_clients, rend_authorized_client_strmap_item_free);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* Copy client key from parsed entry or create new one if required. */
|
|
|
|
if (parsed && parsed->client_key) {
|
|
|
|
client->client_key = crypto_pk_dup_key(parsed->client_key);
|
|
|
|
} else if (s->auth_type == REND_STEALTH_AUTH) {
|
|
|
|
/* Create private key for client. */
|
|
|
|
crypto_pk_env_t *prkey = NULL;
|
|
|
|
if (!(prkey = crypto_new_pk_env())) {
|
|
|
|
log_warn(LD_BUG,"Error constructing client key");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (crypto_pk_generate_key(prkey)) {
|
|
|
|
log_warn(LD_BUG,"Error generating client key");
|
2008-09-05 22:52:15 +02:00
|
|
|
crypto_free_pk_env(prkey);
|
2008-08-08 16:36:11 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (crypto_pk_check_key(prkey) <= 0) {
|
|
|
|
log_warn(LD_BUG,"Generated client key seems invalid");
|
|
|
|
crypto_free_pk_env(prkey);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
client->client_key = prkey;
|
|
|
|
}
|
|
|
|
/* Add entry to client_keys file. */
|
|
|
|
desc_cook_out[strlen(desc_cook_out)-1] = '\0'; /* Remove newline. */
|
|
|
|
written = tor_snprintf(buf, sizeof(buf),
|
|
|
|
"client-name %s\ndescriptor-cookie %s\n",
|
|
|
|
client->client_name, desc_cook_out);
|
|
|
|
if (written < 0) {
|
|
|
|
log_warn(LD_BUG, "Could not write client entry.");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (client->client_key) {
|
2008-09-05 22:52:15 +02:00
|
|
|
char *client_key_out = NULL;
|
2008-08-08 16:36:11 +02:00
|
|
|
crypto_pk_write_private_key_to_string(client->client_key,
|
|
|
|
&client_key_out, &len);
|
|
|
|
if (rend_get_service_id(client->client_key, service_id)<0) {
|
|
|
|
log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
|
2008-09-05 22:52:15 +02:00
|
|
|
tor_free(client_key_out);
|
2008-08-08 16:36:11 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
written = tor_snprintf(buf + written, sizeof(buf) - written,
|
|
|
|
"client-key\n%s", client_key_out);
|
2008-09-05 22:52:15 +02:00
|
|
|
tor_free(client_key_out);
|
2008-08-08 16:36:11 +02:00
|
|
|
if (written < 0) {
|
|
|
|
log_warn(LD_BUG, "Could not write client entry.");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fputs(buf, cfile) < 0) {
|
|
|
|
log_warn(LD_FS, "Could not append client entry to file: %s",
|
|
|
|
strerror(errno));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add line to hostname file. */
|
|
|
|
if (s->auth_type == REND_BASIC_AUTH) {
|
|
|
|
/* Remove == signs (newline has been removed above). */
|
|
|
|
desc_cook_out[strlen(desc_cook_out)-2] = '\0';
|
|
|
|
tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n",
|
|
|
|
s->service_id, desc_cook_out, client->client_name);
|
|
|
|
} else {
|
|
|
|
char extended_desc_cookie[REND_DESC_COOKIE_LEN+1];
|
|
|
|
memcpy(extended_desc_cookie, client->descriptor_cookie,
|
|
|
|
REND_DESC_COOKIE_LEN);
|
2008-08-09 17:13:28 +02:00
|
|
|
extended_desc_cookie[REND_DESC_COOKIE_LEN] =
|
|
|
|
((int)s->auth_type - 1) << 4;
|
2008-08-08 16:36:11 +02:00
|
|
|
if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1,
|
|
|
|
extended_desc_cookie,
|
|
|
|
REND_DESC_COOKIE_LEN+1) < 0) {
|
|
|
|
log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
desc_cook_out[strlen(desc_cook_out)-3] = '\0'; /* Remove A= and
|
|
|
|
newline. */
|
|
|
|
tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n",
|
|
|
|
service_id, desc_cook_out, client->client_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fputs(buf, hfile)<0) {
|
|
|
|
log_warn(LD_FS, "Could not append host entry to file: %s",
|
|
|
|
strerror(errno));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SMARTLIST_FOREACH_END(client);
|
|
|
|
|
|
|
|
goto done;
|
|
|
|
err:
|
|
|
|
r = -1;
|
|
|
|
done:
|
|
|
|
tor_free(client_keys_str);
|
|
|
|
strmap_free(parsed_clients, rend_authorized_client_strmap_item_free);
|
|
|
|
if (r<0) {
|
2008-09-24 16:44:29 +02:00
|
|
|
if (open_cfile)
|
|
|
|
abort_writing_to_file(open_cfile);
|
|
|
|
if (open_hfile)
|
|
|
|
abort_writing_to_file(open_hfile);
|
2008-08-08 16:36:11 +02:00
|
|
|
return r;
|
|
|
|
} else {
|
|
|
|
finish_writing_to_file(open_cfile);
|
|
|
|
finish_writing_to_file(open_hfile);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} SMARTLIST_FOREACH_END(s);
|
|
|
|
return r;
|
2004-03-31 23:54:56 +02:00
|
|
|
}
|
2004-04-01 05:23:28 +02:00
|
|
|
|
2009-05-03 01:04:25 +02:00
|
|
|
/** Return the service whose public key has a digest of <b>digest</b>, or
|
|
|
|
* NULL if no such service exists.
|
2004-04-06 05:44:36 +02:00
|
|
|
*/
|
2004-04-02 23:56:52 +02:00
|
|
|
static rend_service_t *
|
2009-05-03 01:04:25 +02:00
|
|
|
rend_service_get_by_pk_digest(const char* digest)
|
2004-04-01 05:34:05 +02:00
|
|
|
{
|
2004-04-03 02:58:54 +02:00
|
|
|
SMARTLIST_FOREACH(rend_service_list, rend_service_t*, s,
|
2011-05-11 22:23:42 +02:00
|
|
|
if (tor_memeq(s->pk_digest,digest,DIGEST_LEN))
|
2009-05-02 23:31:58 +02:00
|
|
|
return s);
|
2004-04-01 05:34:05 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2005-01-14 05:35:53 +01:00
|
|
|
/** Return 1 if any virtual port in <b>service</b> wants a circuit
|
|
|
|
* to have good uptime. Else return 0.
|
|
|
|
*/
|
|
|
|
static int
|
2005-09-30 03:09:52 +02:00
|
|
|
rend_service_requires_uptime(rend_service_t *service)
|
|
|
|
{
|
2005-01-14 05:35:53 +01:00
|
|
|
int i;
|
|
|
|
rend_service_port_config_t *p;
|
|
|
|
|
|
|
|
for (i=0; i < smartlist_len(service->ports); ++i) {
|
|
|
|
p = smartlist_get(service->ports, i);
|
2005-12-14 21:40:40 +01:00
|
|
|
if (smartlist_string_num_isin(get_options()->LongLivedPorts,
|
|
|
|
p->virtual_port))
|
2005-01-14 05:35:53 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-09-24 16:44:29 +02:00
|
|
|
/** Check client authorization of a given <b>descriptor_cookie</b> for
|
|
|
|
* <b>service</b>. Return 1 for success and 0 for failure. */
|
|
|
|
static int
|
|
|
|
rend_check_authorization(rend_service_t *service,
|
|
|
|
const char *descriptor_cookie)
|
|
|
|
{
|
|
|
|
rend_authorized_client_t *auth_client = NULL;
|
|
|
|
tor_assert(service);
|
|
|
|
tor_assert(descriptor_cookie);
|
|
|
|
if (!service->clients) {
|
|
|
|
log_warn(LD_BUG, "Can't check authorization for a service that has no "
|
|
|
|
"authorized clients configured.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Look up client authorization by descriptor cookie. */
|
|
|
|
SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *, client, {
|
2011-05-10 22:23:43 +02:00
|
|
|
if (tor_memeq(client->descriptor_cookie, descriptor_cookie,
|
2008-09-24 16:44:29 +02:00
|
|
|
REND_DESC_COOKIE_LEN)) {
|
|
|
|
auth_client = client;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (!auth_client) {
|
|
|
|
char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64];
|
|
|
|
base64_encode(descriptor_cookie_base64, sizeof(descriptor_cookie_base64),
|
|
|
|
descriptor_cookie, REND_DESC_COOKIE_LEN);
|
|
|
|
log_info(LD_REND, "No authorization found for descriptor cookie '%s'! "
|
|
|
|
"Dropping cell!",
|
|
|
|
descriptor_cookie_base64);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allow the request. */
|
|
|
|
log_debug(LD_REND, "Client %s authorized for service %s.",
|
|
|
|
auth_client->client_name, service->service_id);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Remove elements from <b>service</b>'s replay cache that are old enough to
|
|
|
|
* be noticed by timestamp checking. */
|
|
|
|
static void
|
|
|
|
clean_accepted_intros(rend_service_t *service, time_t now)
|
|
|
|
{
|
|
|
|
const time_t cutoff = now - REND_REPLAY_TIME_INTERVAL;
|
|
|
|
|
|
|
|
service->last_cleaned_accepted_intros = now;
|
|
|
|
if (!service->accepted_intros)
|
|
|
|
return;
|
|
|
|
|
|
|
|
DIGESTMAP_FOREACH_MODIFY(service->accepted_intros, digest, time_t *, t) {
|
|
|
|
if (*t < cutoff) {
|
|
|
|
tor_free(t);
|
|
|
|
MAP_DEL_CURRENT(digest);
|
|
|
|
}
|
|
|
|
} DIGESTMAP_FOREACH_END;
|
|
|
|
}
|
|
|
|
|
2004-04-01 05:34:05 +02:00
|
|
|
/******
|
|
|
|
* Handle cells
|
|
|
|
******/
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Respond to an INTRODUCE2 cell by launching a circuit to the chosen
|
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
|
|
|
* rendezvous point.
|
2004-04-02 00:21:01 +02:00
|
|
|
*/
|
2009-10-18 00:52:18 +02:00
|
|
|
/* XXX022 this function sure could use some organizing. -RD */
|
2004-04-01 05:34:05 +02:00
|
|
|
int
|
2010-12-14 01:34:01 +01:00
|
|
|
rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
|
2005-12-14 21:40:40 +01:00
|
|
|
size_t request_len)
|
2004-04-01 05:34:05 +02:00
|
|
|
{
|
2005-06-29 23:46:55 +02:00
|
|
|
char *ptr, *r_cookie;
|
|
|
|
extend_info_t *extend_info = NULL;
|
2004-04-01 22:05:57 +02:00
|
|
|
char buf[RELAY_PAYLOAD_SIZE];
|
2004-04-05 23:15:14 +02:00
|
|
|
char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */
|
2004-04-01 05:34:05 +02:00
|
|
|
rend_service_t *service;
|
2008-09-24 16:44:29 +02:00
|
|
|
int r, i, v3_shift = 0;
|
2004-10-14 04:47:09 +02:00
|
|
|
size_t len, keylen;
|
2004-04-01 22:05:57 +02:00
|
|
|
crypto_dh_env_t *dh = NULL;
|
2006-07-23 09:37:35 +02:00
|
|
|
origin_circuit_t *launched = NULL;
|
2004-04-02 00:21:01 +02:00
|
|
|
crypt_path_t *cpath = NULL;
|
2007-11-29 16:25:04 +01:00
|
|
|
char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
|
2004-04-03 02:58:54 +02:00
|
|
|
char hexcookie[9];
|
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
|
|
|
int circ_needs_uptime;
|
2006-10-09 17:47:50 +02:00
|
|
|
int reason = END_CIRC_REASON_TORPROTOCOL;
|
2007-10-31 21:48:06 +01:00
|
|
|
crypto_pk_env_t *intro_key;
|
|
|
|
char intro_key_digest[DIGEST_LEN];
|
2008-09-24 16:44:29 +02:00
|
|
|
int auth_type;
|
|
|
|
size_t auth_len = 0;
|
|
|
|
char auth_data[REND_DESC_COOKIE_LEN];
|
|
|
|
crypto_digest_env_t *digest = NULL;
|
|
|
|
time_t now = time(NULL);
|
|
|
|
char diffie_hellman_hash[DIGEST_LEN];
|
|
|
|
time_t *access_time;
|
2009-10-18 00:52:18 +02:00
|
|
|
or_options_t *options = get_options();
|
|
|
|
|
2008-09-24 16:44:29 +02:00
|
|
|
tor_assert(circuit->rend_data);
|
2004-04-03 02:58:54 +02:00
|
|
|
|
2007-11-29 16:25:04 +01:00
|
|
|
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
|
2008-09-24 16:44:29 +02:00
|
|
|
circuit->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN);
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND, "Received INTRODUCE2 cell for service %s on circ %d.",
|
2006-07-23 09:37:35 +02:00
|
|
|
escaped(serviceid), circuit->_base.n_circ_id);
|
2004-04-01 05:34:05 +02:00
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
if (circuit->_base.purpose != CIRCUIT_PURPOSE_S_INTRO) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL,
|
|
|
|
"Got an INTRODUCE2 over a non-introduction circuit %d.",
|
2006-07-23 09:37:35 +02:00
|
|
|
circuit->_base.n_circ_id);
|
2004-04-01 05:34:05 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2004-04-05 19:28:48 +02:00
|
|
|
/* min key length plus digest length plus nickname length */
|
2004-08-18 07:05:58 +02:00
|
|
|
if (request_len < DIGEST_LEN+REND_COOKIE_LEN+(MAX_NICKNAME_LEN+1)+
|
2004-11-23 00:28:26 +01:00
|
|
|
DH_KEY_LEN+42) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL, "Got a truncated INTRODUCE2 cell on circ %d.",
|
2006-07-23 09:37:35 +02:00
|
|
|
circuit->_base.n_circ_id);
|
2004-04-01 05:34:05 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-10-31 21:48:06 +01:00
|
|
|
/* look up service depending on circuit. */
|
2009-05-03 01:04:25 +02:00
|
|
|
service = rend_service_get_by_pk_digest(
|
|
|
|
circuit->rend_data->rend_pk_digest);
|
2004-04-01 05:34:05 +02:00
|
|
|
if (!service) {
|
2011-10-06 00:55:23 +02:00
|
|
|
log_warn(LD_BUG, "Internal error: Got an INTRODUCE2 cell on an intro "
|
|
|
|
"circ for an unrecognized service %s.",
|
2006-03-06 01:25:39 +01:00
|
|
|
escaped(serviceid));
|
2004-04-01 05:34:05 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2007-10-31 21:48:06 +01:00
|
|
|
|
2009-05-02 23:31:58 +02:00
|
|
|
/* use intro key instead of service key. */
|
|
|
|
intro_key = circuit->intro_key;
|
2007-10-31 21:48:06 +01:00
|
|
|
|
|
|
|
/* first DIGEST_LEN bytes of request is intro or service pk digest */
|
|
|
|
crypto_pk_get_digest(intro_key, intro_key_digest);
|
2011-05-10 22:58:38 +02:00
|
|
|
if (tor_memneq(intro_key_digest, request, DIGEST_LEN)) {
|
2007-11-29 16:25:04 +01:00
|
|
|
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
|
2010-12-14 01:34:01 +01:00
|
|
|
(char*)request, REND_SERVICE_ID_LEN);
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_REND, "Got an INTRODUCE2 cell for the wrong service (%s).",
|
2006-03-06 01:25:39 +01:00
|
|
|
escaped(serviceid));
|
2004-04-01 05:34:05 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-10-31 21:48:06 +01:00
|
|
|
keylen = crypto_pk_keysize(intro_key);
|
2004-04-05 23:15:14 +02:00
|
|
|
if (request_len < keylen+DIGEST_LEN) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL,
|
|
|
|
"PK-encrypted portion of INTRODUCE2 cell was truncated.");
|
2004-04-01 05:34:05 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2011-05-11 03:40:10 +02:00
|
|
|
|
|
|
|
if (!service->accepted_intros)
|
|
|
|
service->accepted_intros = digestmap_new();
|
|
|
|
|
|
|
|
{
|
|
|
|
char pkpart_digest[DIGEST_LEN];
|
|
|
|
/* Check for replay of PK-encrypted portion. It is slightly naughty to
|
|
|
|
use the same digestmap to check for this and for g^x replays, but
|
|
|
|
collisions are tremendously unlikely.
|
|
|
|
*/
|
|
|
|
crypto_digest(pkpart_digest, (char*)request+DIGEST_LEN, keylen);
|
|
|
|
access_time = digestmap_get(service->accepted_intros, pkpart_digest);
|
|
|
|
if (access_time != NULL) {
|
|
|
|
log_warn(LD_REND, "Possible replay detected! We received an "
|
|
|
|
"INTRODUCE2 cell with same PK-encrypted part %d seconds ago. "
|
|
|
|
"Dropping cell.", (int)(now-*access_time));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
access_time = tor_malloc(sizeof(time_t));
|
|
|
|
*access_time = now;
|
|
|
|
digestmap_set(service->accepted_intros, pkpart_digest, access_time);
|
|
|
|
}
|
|
|
|
|
2004-04-01 05:34:05 +02:00
|
|
|
/* Next N bytes is encrypted with service key */
|
2006-10-31 20:17:07 +01:00
|
|
|
note_crypto_pk_op(REND_SERVER);
|
2004-10-14 04:47:09 +02:00
|
|
|
r = crypto_pk_private_hybrid_decrypt(
|
2011-01-13 20:36:41 +01:00
|
|
|
intro_key,buf,sizeof(buf),
|
|
|
|
(char*)(request+DIGEST_LEN),request_len-DIGEST_LEN,
|
2004-05-12 21:30:28 +02:00
|
|
|
PK_PKCS1_OAEP_PADDING,1);
|
2004-10-14 04:47:09 +02:00
|
|
|
if (r<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL, "Couldn't decrypt INTRODUCE2 cell.");
|
2004-04-01 05:34:05 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-10-14 04:47:09 +02:00
|
|
|
len = r;
|
2008-09-24 16:44:29 +02:00
|
|
|
if (*buf == 3) {
|
|
|
|
/* Version 3 INTRODUCE2 cell. */
|
2009-09-03 10:17:08 +02:00
|
|
|
time_t ts = 0;
|
2008-09-24 16:44:29 +02:00
|
|
|
v3_shift = 1;
|
|
|
|
auth_type = buf[1];
|
|
|
|
switch (auth_type) {
|
|
|
|
case REND_BASIC_AUTH:
|
|
|
|
/* fall through */
|
|
|
|
case REND_STEALTH_AUTH:
|
|
|
|
auth_len = ntohs(get_uint16(buf+2));
|
|
|
|
if (auth_len != REND_DESC_COOKIE_LEN) {
|
|
|
|
log_info(LD_REND, "Wrong auth data size %d, should be %d.",
|
|
|
|
(int)auth_len, REND_DESC_COOKIE_LEN);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
memcpy(auth_data, buf+4, sizeof(auth_data));
|
|
|
|
v3_shift += 2+REND_DESC_COOKIE_LEN;
|
|
|
|
break;
|
|
|
|
case REND_NO_AUTH:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
log_info(LD_REND, "Unknown authorization type '%d'", auth_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check timestamp. */
|
2009-09-03 02:36:11 +02:00
|
|
|
ts = ntohl(get_uint32(buf+1+v3_shift));
|
2008-09-24 16:44:29 +02:00
|
|
|
v3_shift += 4;
|
|
|
|
if ((now - ts) < -1 * REND_REPLAY_TIME_INTERVAL / 2 ||
|
|
|
|
(now - ts) > REND_REPLAY_TIME_INTERVAL / 2) {
|
2011-09-11 03:40:09 +02:00
|
|
|
/* This is far more likely to mean that a client's clock is
|
|
|
|
* skewed than that a replay attack is in progress. */
|
|
|
|
log_info(LD_REND, "INTRODUCE2 cell is too %s. Discarding.",
|
2009-09-03 02:36:11 +02:00
|
|
|
(now - ts) < 0 ? "old" : "new");
|
2008-09-24 16:44:29 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (*buf == 2 || *buf == 3) {
|
2005-06-29 23:46:55 +02:00
|
|
|
/* Version 2 INTRODUCE2 cell. */
|
|
|
|
int klen;
|
|
|
|
extend_info = tor_malloc_zero(sizeof(extend_info_t));
|
2008-09-24 16:44:29 +02:00
|
|
|
tor_addr_from_ipv4n(&extend_info->addr, get_uint32(buf+v3_shift+1));
|
|
|
|
extend_info->port = ntohs(get_uint16(buf+v3_shift+5));
|
|
|
|
memcpy(extend_info->identity_digest, buf+v3_shift+7,
|
|
|
|
DIGEST_LEN);
|
2005-06-29 23:46:55 +02:00
|
|
|
extend_info->nickname[0] = '$';
|
|
|
|
base16_encode(extend_info->nickname+1, sizeof(extend_info->nickname)-1,
|
|
|
|
extend_info->identity_digest, DIGEST_LEN);
|
|
|
|
|
2008-09-24 16:44:29 +02:00
|
|
|
klen = ntohs(get_uint16(buf+v3_shift+7+DIGEST_LEN));
|
|
|
|
if ((int)len != v3_shift+7+DIGEST_LEN+2+klen+20+128) {
|
|
|
|
log_warn(LD_PROTOCOL, "Bad length %u for version %d INTRODUCE2 cell.",
|
|
|
|
(int)len, *buf);
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_TORPROTOCOL;
|
2005-06-29 23:46:55 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2008-09-24 16:44:29 +02:00
|
|
|
extend_info->onion_key =
|
|
|
|
crypto_pk_asn1_decode(buf+v3_shift+7+DIGEST_LEN+2, klen);
|
2005-06-29 23:46:55 +02:00
|
|
|
if (!extend_info->onion_key) {
|
2008-09-24 16:44:29 +02:00
|
|
|
log_warn(LD_PROTOCOL, "Error decoding onion key in version %d "
|
|
|
|
"INTRODUCE2 cell.", *buf);
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_TORPROTOCOL;
|
2005-06-29 23:46:55 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2008-09-24 16:44:29 +02:00
|
|
|
ptr = buf+v3_shift+7+DIGEST_LEN+2+klen;
|
|
|
|
len -= v3_shift+7+DIGEST_LEN+2+klen;
|
2004-08-18 06:48:59 +02:00
|
|
|
} else {
|
2005-06-29 23:46:55 +02:00
|
|
|
char *rp_nickname;
|
|
|
|
size_t nickname_field_len;
|
|
|
|
routerinfo_t *router;
|
|
|
|
int version;
|
|
|
|
if (*buf == 1) {
|
|
|
|
rp_nickname = buf+1;
|
|
|
|
nickname_field_len = MAX_HEX_NICKNAME_LEN+1;
|
|
|
|
version = 1;
|
|
|
|
} else {
|
|
|
|
nickname_field_len = MAX_NICKNAME_LEN+1;
|
|
|
|
rp_nickname = buf;
|
|
|
|
version = 0;
|
|
|
|
}
|
|
|
|
ptr=memchr(rp_nickname,0,nickname_field_len);
|
|
|
|
if (!ptr || ptr == rp_nickname) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL,
|
2006-07-15 21:21:30 +02:00
|
|
|
"Couldn't find a nul-padded nickname in INTRODUCE2 cell.");
|
2005-06-29 23:46:55 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if ((version == 0 && !is_legal_nickname(rp_nickname)) ||
|
|
|
|
(version == 1 && !is_legal_nickname_or_hexdigest(rp_nickname))) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL, "Bad nickname in INTRODUCE2 cell.");
|
2005-06-29 23:46:55 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* Okay, now we know that a nickname is at the start of the buffer. */
|
|
|
|
ptr = rp_nickname+nickname_field_len;
|
|
|
|
len -= nickname_field_len;
|
2005-12-14 21:40:40 +01:00
|
|
|
len -= rp_nickname - buf; /* also remove header space used by version, if
|
|
|
|
* any */
|
2005-10-05 00:23:31 +02:00
|
|
|
router = router_get_by_nickname(rp_nickname, 0);
|
2005-06-29 23:46:55 +02:00
|
|
|
if (!router) {
|
2007-09-19 01:34:27 +02:00
|
|
|
log_info(LD_REND, "Couldn't find router %s named in introduce2 cell.",
|
2009-12-15 23:23:36 +01:00
|
|
|
escaped_safe_str_client(rp_nickname));
|
2006-10-09 17:47:50 +02:00
|
|
|
/* XXXX Add a no-such-router reason? */
|
|
|
|
reason = END_CIRC_REASON_TORPROTOCOL;
|
2005-06-29 23:46:55 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
extend_info = extend_info_from_router(router);
|
2004-04-01 05:34:05 +02:00
|
|
|
}
|
2005-06-29 23:46:55 +02:00
|
|
|
|
2004-04-06 05:44:36 +02:00
|
|
|
if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL, "Bad length %u for INTRODUCE2 cell.", (int)len);
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_TORPROTOCOL;
|
2008-01-16 06:27:19 +01:00
|
|
|
goto err;
|
2004-04-01 05:34:05 +02:00
|
|
|
}
|
2005-06-29 23:46:55 +02:00
|
|
|
|
2009-10-18 00:52:18 +02:00
|
|
|
/* Check if we'd refuse to talk to this router */
|
|
|
|
if (options->ExcludeNodes && options->StrictNodes &&
|
|
|
|
routerset_contains_extendinfo(options->ExcludeNodes, extend_info)) {
|
|
|
|
log_warn(LD_REND, "Client asked to rendezvous at a relay that we "
|
|
|
|
"exclude, and StrictNodes is set. Refusing service.");
|
|
|
|
reason = END_CIRC_REASON_INTERNAL; /* XXX might leak why we refused */
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2004-04-01 22:05:57 +02:00
|
|
|
r_cookie = ptr;
|
2004-08-07 03:03:33 +02:00
|
|
|
base16_encode(hexcookie,9,r_cookie,4);
|
2004-04-01 05:34:05 +02:00
|
|
|
|
2008-09-24 16:44:29 +02:00
|
|
|
/* Determine hash of Diffie-Hellman, part 1 to detect replays. */
|
|
|
|
digest = crypto_new_digest_env();
|
|
|
|
crypto_digest_add_bytes(digest, ptr+REND_COOKIE_LEN, DH_KEY_LEN);
|
|
|
|
crypto_digest_get_digest(digest, diffie_hellman_hash, DIGEST_LEN);
|
|
|
|
crypto_free_digest_env(digest);
|
|
|
|
|
2008-09-24 19:33:07 +02:00
|
|
|
/* Check whether there is a past request with the same Diffie-Hellman,
|
|
|
|
* part 1. */
|
2008-09-24 16:44:29 +02:00
|
|
|
access_time = digestmap_get(service->accepted_intros, diffie_hellman_hash);
|
|
|
|
if (access_time != NULL) {
|
2011-09-11 02:54:26 +02:00
|
|
|
/* A Tor client will send a new INTRODUCE1 cell with the same rend
|
|
|
|
* cookie and DH public key as its previous one if its intro circ
|
|
|
|
* times out while in state CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT .
|
|
|
|
* If we received the first INTRODUCE1 cell (the intro-point relay
|
|
|
|
* converts it into an INTRODUCE2 cell), we are already trying to
|
|
|
|
* connect to that rend point (and may have already succeeded);
|
|
|
|
* drop this cell. */
|
|
|
|
log_info(LD_REND, "We received an "
|
2008-09-24 16:44:29 +02:00
|
|
|
"INTRODUCE2 cell with same first part of "
|
|
|
|
"Diffie-Hellman handshake %d seconds ago. Dropping "
|
|
|
|
"cell.",
|
|
|
|
(int) (now - *access_time));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2008-09-24 19:33:07 +02:00
|
|
|
/* Add request to access history, including time and hash of Diffie-Hellman,
|
|
|
|
* part 1, and possibly remove requests from the history that are older than
|
|
|
|
* one hour. */
|
2008-09-24 16:44:29 +02:00
|
|
|
access_time = tor_malloc(sizeof(time_t));
|
|
|
|
*access_time = now;
|
|
|
|
digestmap_set(service->accepted_intros, diffie_hellman_hash, access_time);
|
|
|
|
if (service->last_cleaned_accepted_intros + REND_REPLAY_TIME_INTERVAL < now)
|
|
|
|
clean_accepted_intros(service, now);
|
|
|
|
|
|
|
|
/* If the service performs client authorization, check included auth data. */
|
|
|
|
if (service->clients) {
|
|
|
|
if (auth_len > 0) {
|
|
|
|
if (rend_check_authorization(service, auth_data)) {
|
|
|
|
log_info(LD_REND, "Authorization data in INTRODUCE2 cell are valid.");
|
|
|
|
} else {
|
|
|
|
log_info(LD_REND, "The authorization data that are contained in "
|
|
|
|
"the INTRODUCE2 cell are invalid. Dropping cell.");
|
|
|
|
reason = END_CIRC_REASON_CONNECTFAILED;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log_info(LD_REND, "INTRODUCE2 cell does not contain authentication "
|
|
|
|
"data, but we require client authorization. Dropping cell.");
|
|
|
|
reason = END_CIRC_REASON_CONNECTFAILED;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-04-01 22:05:57 +02:00
|
|
|
/* Try DH handshake... */
|
2011-01-24 22:03:14 +01:00
|
|
|
dh = crypto_dh_new(DH_TYPE_REND);
|
2004-04-01 22:05:57 +02:00
|
|
|
if (!dh || crypto_dh_generate_public(dh)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG,"Internal error: couldn't build DH state "
|
|
|
|
"or generate public key.");
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_INTERNAL;
|
2004-04-01 22:05:57 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2009-10-26 07:47:05 +01:00
|
|
|
if (crypto_dh_compute_secret(LOG_PROTOCOL_WARN, dh, ptr+REND_COOKIE_LEN,
|
|
|
|
DH_KEY_LEN, keys,
|
2004-04-05 23:15:14 +02:00
|
|
|
DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG, "Internal error: couldn't complete DH handshake");
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_INTERNAL;
|
2004-04-01 22:05:57 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
circ_needs_uptime = rend_service_requires_uptime(service);
|
|
|
|
|
|
|
|
/* help predict this next time */
|
2009-09-03 10:17:08 +02:00
|
|
|
rep_hist_note_used_internal(now, circ_needs_uptime, 1);
|
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
|
|
|
|
2004-04-01 22:05:57 +02:00
|
|
|
/* Launch a circuit to alice's chosen rendezvous point.
|
2004-04-01 05:34:05 +02:00
|
|
|
*/
|
2004-11-28 10:05:49 +01:00
|
|
|
for (i=0;i<MAX_REND_FAILURES;i++) {
|
2007-12-23 20:15:22 +01:00
|
|
|
int flags = CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_IS_INTERNAL;
|
|
|
|
if (circ_needs_uptime) flags |= CIRCLAUNCH_NEED_UPTIME;
|
2005-06-29 23:46:55 +02:00
|
|
|
launched = circuit_launch_by_extend_info(
|
2007-12-23 20:15:22 +01:00
|
|
|
CIRCUIT_PURPOSE_S_CONNECT_REND, extend_info, flags);
|
2005-06-29 23:46:55 +02:00
|
|
|
|
2004-10-30 07:04:52 +02:00
|
|
|
if (launched)
|
|
|
|
break;
|
|
|
|
}
|
2004-11-28 10:05:49 +01:00
|
|
|
if (!launched) { /* give up */
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_REND, "Giving up launching first hop of circuit to rendezvous "
|
2006-12-07 21:14:29 +01:00
|
|
|
"point %s for service %s.",
|
2011-05-16 03:58:46 +02:00
|
|
|
safe_str_client(extend_info_describe(extend_info)),
|
2009-09-28 15:08:32 +02:00
|
|
|
serviceid);
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_CONNECTFAILED;
|
2004-10-25 01:09:48 +02:00
|
|
|
goto err;
|
2004-04-01 22:05:57 +02:00
|
|
|
}
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,
|
2006-12-07 21:14:29 +01:00
|
|
|
"Accepted intro; launching circuit to %s "
|
2006-02-13 11:33:00 +01:00
|
|
|
"(cookie %s) for service %s.",
|
2011-05-16 03:58:46 +02:00
|
|
|
safe_str_client(extend_info_describe(extend_info)),
|
2009-09-28 15:08:32 +02:00
|
|
|
hexcookie, serviceid);
|
2004-04-25 22:37:37 +02:00
|
|
|
tor_assert(launched->build_state);
|
2004-04-01 22:05:57 +02:00
|
|
|
/* Fill in the circuit's state. */
|
2008-09-24 16:44:29 +02:00
|
|
|
launched->rend_data = tor_malloc_zero(sizeof(rend_data_t));
|
|
|
|
memcpy(launched->rend_data->rend_pk_digest,
|
|
|
|
circuit->rend_data->rend_pk_digest,
|
2004-04-03 04:40:30 +02:00
|
|
|
DIGEST_LEN);
|
2008-09-24 16:44:29 +02:00
|
|
|
memcpy(launched->rend_data->rend_cookie, r_cookie, REND_COOKIE_LEN);
|
|
|
|
strlcpy(launched->rend_data->onion_address, service->service_id,
|
|
|
|
sizeof(launched->rend_data->onion_address));
|
2004-04-02 00:21:01 +02:00
|
|
|
launched->build_state->pending_final_cpath = cpath =
|
|
|
|
tor_malloc_zero(sizeof(crypt_path_t));
|
2005-03-23 07:21:48 +01:00
|
|
|
cpath->magic = CRYPT_PATH_MAGIC;
|
2009-09-03 10:17:08 +02:00
|
|
|
launched->build_state->expiry_time = now + MAX_REND_TIMEOUT;
|
2004-04-02 00:21:01 +02:00
|
|
|
|
2005-05-03 00:35:18 +02:00
|
|
|
cpath->dh_handshake_state = dh;
|
2004-04-01 22:05:57 +02:00
|
|
|
dh = NULL;
|
2004-04-06 05:44:36 +02:00
|
|
|
if (circuit_init_cpath_crypto(cpath,keys+DIGEST_LEN,1)<0)
|
2004-04-02 00:21:01 +02:00
|
|
|
goto err;
|
2004-04-06 05:44:36 +02:00
|
|
|
memcpy(cpath->handshake_digest, keys, DIGEST_LEN);
|
2005-06-29 23:46:55 +02:00
|
|
|
if (extend_info) extend_info_free(extend_info);
|
2004-04-01 05:34:05 +02:00
|
|
|
|
2011-01-15 17:32:56 +01:00
|
|
|
memset(keys, 0, sizeof(keys));
|
2004-04-01 05:34:05 +02:00
|
|
|
return 0;
|
2004-04-01 22:05:57 +02:00
|
|
|
err:
|
2011-01-15 17:32:56 +01:00
|
|
|
memset(keys, 0, sizeof(keys));
|
2004-04-01 22:05:57 +02:00
|
|
|
if (dh) crypto_dh_free(dh);
|
2006-07-23 09:37:35 +02:00
|
|
|
if (launched)
|
2006-10-09 17:47:50 +02:00
|
|
|
circuit_mark_for_close(TO_CIRCUIT(launched), reason);
|
2005-06-29 23:46:55 +02:00
|
|
|
if (extend_info) extend_info_free(extend_info);
|
2004-04-01 22:05:57 +02:00
|
|
|
return -1;
|
2004-04-01 05:34:05 +02:00
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Called when we fail building a rendezvous circuit at some point other
|
2004-05-05 23:32:43 +02:00
|
|
|
* than the last hop: launches a new circuit to the same rendezvous point.
|
|
|
|
*/
|
2004-04-14 23:40:50 +02:00
|
|
|
void
|
2006-07-23 09:37:35 +02:00
|
|
|
rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc)
|
2004-04-14 23:40:50 +02:00
|
|
|
{
|
2006-07-23 09:37:35 +02:00
|
|
|
origin_circuit_t *newcirc;
|
2004-04-14 23:40:50 +02:00
|
|
|
cpath_build_state_t *newstate, *oldstate;
|
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
tor_assert(oldcirc->_base.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
|
2004-04-14 23:40:50 +02:00
|
|
|
|
|
|
|
if (!oldcirc->build_state ||
|
2005-03-19 05:38:59 +01:00
|
|
|
oldcirc->build_state->failure_count > MAX_REND_FAILURES ||
|
|
|
|
oldcirc->build_state->expiry_time < time(NULL)) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,
|
|
|
|
"Attempt to build circuit to %s for rendezvous has failed "
|
|
|
|
"too many times or expired; giving up.",
|
|
|
|
oldcirc->build_state ?
|
2011-05-16 03:58:46 +02:00
|
|
|
safe_str(extend_info_describe(oldcirc->build_state->chosen_exit))
|
|
|
|
: "*unknown*");
|
2004-04-14 23:40:50 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-10-30 07:04:52 +02:00
|
|
|
oldstate = oldcirc->build_state;
|
|
|
|
tor_assert(oldstate);
|
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
if (oldstate->pending_final_cpath == NULL) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,"Skipping relaunch of circ that failed on its first hop. "
|
|
|
|
"Initiator will retry.");
|
2004-10-30 07:04:52 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-03-06 01:25:39 +01:00
|
|
|
log_info(LD_REND,"Reattempting rendezvous circuit to '%s'",
|
2011-05-16 03:58:46 +02:00
|
|
|
safe_str(extend_info_describe(oldstate->chosen_exit)));
|
2004-04-14 23:40:50 +02:00
|
|
|
|
2007-12-23 20:15:22 +01:00
|
|
|
newcirc = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_CONNECT_REND,
|
|
|
|
oldstate->chosen_exit,
|
|
|
|
CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL);
|
|
|
|
|
2004-04-14 23:40:50 +02:00
|
|
|
if (!newcirc) {
|
2006-03-06 01:25:39 +01:00
|
|
|
log_warn(LD_REND,"Couldn't relaunch rendezvous circuit to '%s'.",
|
2011-05-16 03:58:46 +02:00
|
|
|
safe_str(extend_info_describe(oldstate->chosen_exit)));
|
2004-04-14 23:40:50 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
newstate = newcirc->build_state;
|
2004-10-17 00:14:52 +02:00
|
|
|
tor_assert(newstate);
|
2004-04-14 23:40:50 +02:00
|
|
|
newstate->failure_count = oldstate->failure_count+1;
|
2005-03-19 05:38:59 +01:00
|
|
|
newstate->expiry_time = oldstate->expiry_time;
|
2004-04-14 23:40:50 +02:00
|
|
|
newstate->pending_final_cpath = oldstate->pending_final_cpath;
|
|
|
|
oldstate->pending_final_cpath = NULL;
|
|
|
|
|
2008-09-24 16:44:29 +02:00
|
|
|
newcirc->rend_data = rend_data_dup(oldcirc->rend_data);
|
2004-04-14 23:40:50 +02:00
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Launch a circuit to serve as an introduction point for the service
|
2004-05-10 06:34:48 +02:00
|
|
|
* <b>service</b> at the introduction point <b>nickname</b>
|
2004-04-02 00:21:01 +02:00
|
|
|
*/
|
|
|
|
static int
|
2005-12-14 21:40:40 +01:00
|
|
|
rend_service_launch_establish_intro(rend_service_t *service,
|
2007-12-21 10:28:22 +01:00
|
|
|
rend_intro_point_t *intro)
|
2004-04-02 00:21:01 +02:00
|
|
|
{
|
2006-07-23 09:37:35 +02:00
|
|
|
origin_circuit_t *launched;
|
2004-04-02 00:21:01 +02:00
|
|
|
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,
|
|
|
|
"Launching circuit to introduction point %s for service %s",
|
2011-05-16 03:58:46 +02:00
|
|
|
safe_str_client(extend_info_describe(intro->extend_info)),
|
2007-12-21 10:28:22 +01:00
|
|
|
service->service_id);
|
2004-04-03 02:58:54 +02:00
|
|
|
|
2005-11-25 09:08:56 +01:00
|
|
|
rep_hist_note_used_internal(time(NULL), 1, 0);
|
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
|
|
|
|
2004-05-18 03:53:53 +02:00
|
|
|
++service->n_intro_circuits_launched;
|
2007-12-21 10:28:22 +01:00
|
|
|
launched = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO,
|
2007-12-23 20:15:22 +01:00
|
|
|
intro->extend_info,
|
|
|
|
CIRCLAUNCH_NEED_UPTIME|CIRCLAUNCH_IS_INTERNAL);
|
|
|
|
|
2004-04-02 00:21:01 +02:00
|
|
|
if (!launched) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,
|
2006-12-07 21:14:29 +01:00
|
|
|
"Can't launch circuit to establish introduction at %s.",
|
2011-05-16 03:58:46 +02:00
|
|
|
safe_str_client(extend_info_describe(intro->extend_info)));
|
2004-04-02 00:21:01 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2008-06-17 19:10:03 +02:00
|
|
|
|
2011-05-10 22:58:38 +02:00
|
|
|
if (tor_memneq(intro->extend_info->identity_digest,
|
2008-06-17 19:10:03 +02:00
|
|
|
launched->build_state->chosen_exit->identity_digest, DIGEST_LEN)) {
|
|
|
|
char cann[HEX_DIGEST_LEN+1], orig[HEX_DIGEST_LEN+1];
|
|
|
|
base16_encode(cann, sizeof(cann),
|
|
|
|
launched->build_state->chosen_exit->identity_digest,
|
|
|
|
DIGEST_LEN);
|
|
|
|
base16_encode(orig, sizeof(orig),
|
|
|
|
intro->extend_info->identity_digest, DIGEST_LEN);
|
|
|
|
log_info(LD_REND, "The intro circuit we just cannibalized ends at $%s, "
|
|
|
|
"but we requested an intro circuit to $%s. Updating "
|
|
|
|
"our service.", cann, orig);
|
2008-06-17 21:13:05 +02:00
|
|
|
extend_info_free(intro->extend_info);
|
2008-06-17 19:10:03 +02:00
|
|
|
intro->extend_info = extend_info_dup(launched->build_state->chosen_exit);
|
|
|
|
}
|
|
|
|
|
2008-09-24 16:44:29 +02:00
|
|
|
launched->rend_data = tor_malloc_zero(sizeof(rend_data_t));
|
|
|
|
strlcpy(launched->rend_data->onion_address, service->service_id,
|
|
|
|
sizeof(launched->rend_data->onion_address));
|
|
|
|
memcpy(launched->rend_data->rend_pk_digest, service->pk_digest, DIGEST_LEN);
|
2009-05-02 23:31:58 +02:00
|
|
|
launched->intro_key = crypto_pk_dup_key(intro->intro_key);
|
2006-07-23 09:37:35 +02:00
|
|
|
if (launched->_base.state == CIRCUIT_STATE_OPEN)
|
2005-08-12 03:26:21 +02:00
|
|
|
rend_service_intro_has_opened(launched);
|
2004-04-02 00:21:01 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-10-15 22:59:48 +02:00
|
|
|
/** Return the number of introduction points that are or have been
|
2009-05-03 01:04:25 +02:00
|
|
|
* established for the given service address in <b>query</b>. */
|
2008-10-15 22:59:48 +02:00
|
|
|
static int
|
2009-05-03 01:04:25 +02:00
|
|
|
count_established_intro_points(const char *query)
|
2008-10-15 22:59:48 +02:00
|
|
|
{
|
|
|
|
int num_ipos = 0;
|
|
|
|
circuit_t *circ;
|
|
|
|
for (circ = _circuit_get_global_list(); circ; circ = circ->next) {
|
|
|
|
if (!circ->marked_for_close &&
|
|
|
|
circ->state == CIRCUIT_STATE_OPEN &&
|
|
|
|
(circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
|
|
|
|
circ->purpose == CIRCUIT_PURPOSE_S_INTRO)) {
|
|
|
|
origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ);
|
|
|
|
if (oc->rend_data &&
|
|
|
|
!rend_cmp_service_ids(query, oc->rend_data->onion_address))
|
|
|
|
num_ipos++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return num_ipos;
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Called when we're done building a circuit to an introduction point:
|
2004-04-02 00:21:01 +02:00
|
|
|
* sends a RELAY_ESTABLISH_INTRO cell.
|
|
|
|
*/
|
|
|
|
void
|
2006-07-23 09:37:35 +02:00
|
|
|
rend_service_intro_has_opened(origin_circuit_t *circuit)
|
2004-04-02 00:21:01 +02:00
|
|
|
{
|
|
|
|
rend_service_t *service;
|
2004-10-14 04:47:09 +02:00
|
|
|
size_t len;
|
|
|
|
int r;
|
2004-04-02 00:21:01 +02:00
|
|
|
char buf[RELAY_PAYLOAD_SIZE];
|
2004-04-06 05:44:36 +02:00
|
|
|
char auth[DIGEST_LEN + 9];
|
2007-11-29 16:25:04 +01:00
|
|
|
char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
|
2006-10-09 17:47:50 +02:00
|
|
|
int reason = END_CIRC_REASON_TORPROTOCOL;
|
2007-10-31 21:48:06 +01:00
|
|
|
crypto_pk_env_t *intro_key;
|
2004-04-02 00:21:01 +02:00
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
tor_assert(circuit->_base.purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
|
2004-10-17 00:14:52 +02:00
|
|
|
tor_assert(circuit->cpath);
|
2008-09-24 16:44:29 +02:00
|
|
|
tor_assert(circuit->rend_data);
|
2004-04-03 02:58:54 +02:00
|
|
|
|
2007-11-29 16:25:04 +01:00
|
|
|
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
|
2008-09-24 16:44:29 +02:00
|
|
|
circuit->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN);
|
2004-04-12 07:12:50 +02:00
|
|
|
|
2009-05-03 01:04:25 +02:00
|
|
|
service = rend_service_get_by_pk_digest(
|
|
|
|
circuit->rend_data->rend_pk_digest);
|
2004-04-02 00:21:01 +02:00
|
|
|
if (!service) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_REND, "Unrecognized service ID %s on introduction circuit %d.",
|
2006-07-23 09:37:35 +02:00
|
|
|
serviceid, circuit->_base.n_circ_id);
|
2006-10-17 17:20:00 +02:00
|
|
|
reason = END_CIRC_REASON_NOSUCHSERVICE;
|
2004-04-02 00:21:01 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2008-10-15 22:59:48 +02:00
|
|
|
/* If we already have enough introduction circuits for this service,
|
2011-04-04 02:06:31 +02:00
|
|
|
* redefine this one as a general circuit or close it, depending. */
|
2011-10-05 02:34:42 +02:00
|
|
|
if (count_established_intro_points(serviceid) >
|
|
|
|
(int)service->n_intro_points_wanted) { /* XXX023 remove cast */
|
2011-04-04 02:06:31 +02:00
|
|
|
or_options_t *options = get_options();
|
|
|
|
if (options->ExcludeNodes) {
|
|
|
|
/* XXXX in some future version, we can test whether the transition is
|
|
|
|
allowed or not given the actual nodes in the circuit. But for now,
|
|
|
|
this case, we might as well close the thing. */
|
|
|
|
log_info(LD_CIRC|LD_REND, "We have just finished an introduction "
|
|
|
|
"circuit, but we already have enough. Closing it.");
|
|
|
|
circuit_mark_for_close(TO_CIRCUIT(circuit), END_CIRC_REASON_NONE);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
tor_assert(circuit->build_state->is_internal);
|
|
|
|
log_info(LD_CIRC|LD_REND, "We have just finished an introduction "
|
|
|
|
"circuit, but we already have enough. Redefining purpose to "
|
|
|
|
"general; leaving as internal.");
|
|
|
|
TO_CIRCUIT(circuit)->purpose = CIRCUIT_PURPOSE_C_GENERAL;
|
|
|
|
circuit_has_opened(circuit);
|
|
|
|
return;
|
|
|
|
}
|
2008-10-15 22:59:48 +02:00
|
|
|
}
|
|
|
|
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,
|
|
|
|
"Established circuit %d as introduction point for service %s",
|
2006-07-23 09:37:35 +02:00
|
|
|
circuit->_base.n_circ_id, serviceid);
|
2004-04-03 02:58:54 +02:00
|
|
|
|
2009-05-02 23:31:58 +02:00
|
|
|
/* Use the intro key instead of the service key in ESTABLISH_INTRO. */
|
|
|
|
intro_key = circuit->intro_key;
|
2004-04-02 00:21:01 +02:00
|
|
|
/* Build the payload for a RELAY_ESTABLISH_INTRO cell. */
|
2008-02-20 00:33:02 +01:00
|
|
|
r = crypto_pk_asn1_encode(intro_key, buf+2,
|
|
|
|
RELAY_PAYLOAD_SIZE-2);
|
|
|
|
if (r < 0) {
|
|
|
|
log_warn(LD_BUG, "Internal error; failed to establish intro point.");
|
|
|
|
reason = END_CIRC_REASON_INTERNAL;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
len = r;
|
2004-04-28 22:13:21 +02:00
|
|
|
set_uint16(buf, htons((uint16_t)len));
|
2004-04-02 00:21:01 +02:00
|
|
|
len += 2;
|
2004-04-03 04:40:30 +02:00
|
|
|
memcpy(auth, circuit->cpath->prev->handshake_digest, DIGEST_LEN);
|
|
|
|
memcpy(auth+DIGEST_LEN, "INTRODUCE", 9);
|
2004-11-02 03:28:51 +01:00
|
|
|
if (crypto_digest(buf+len, auth, DIGEST_LEN+9))
|
2004-04-02 00:21:01 +02:00
|
|
|
goto err;
|
|
|
|
len += 20;
|
2006-10-31 20:17:07 +01:00
|
|
|
note_crypto_pk_op(REND_SERVER);
|
2011-01-13 20:36:41 +01:00
|
|
|
r = crypto_pk_private_sign_digest(intro_key, buf+len, sizeof(buf)-len,
|
|
|
|
buf, len);
|
2004-04-02 00:21:01 +02:00
|
|
|
if (r<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG, "Internal error: couldn't sign introduction request.");
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_INTERNAL;
|
2004-04-02 00:21:01 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
len += r;
|
|
|
|
|
2007-03-24 16:57:51 +01:00
|
|
|
if (relay_send_command_from_edge(0, TO_CIRCUIT(circuit),
|
2006-07-23 09:37:35 +02:00
|
|
|
RELAY_COMMAND_ESTABLISH_INTRO,
|
2004-04-02 00:21:01 +02:00
|
|
|
buf, len, circuit->cpath->prev)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_GENERAL,
|
|
|
|
"Couldn't send introduction request for service %s on circuit %d",
|
2006-07-23 09:37:35 +02:00
|
|
|
serviceid, circuit->_base.n_circ_id);
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_INTERNAL;
|
2004-04-02 00:21:01 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
err:
|
2006-10-09 17:47:50 +02:00
|
|
|
circuit_mark_for_close(TO_CIRCUIT(circuit), reason);
|
2004-04-02 00:21:01 +02:00
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Called when we get an INTRO_ESTABLISHED cell; mark the circuit as a
|
2004-05-05 23:32:43 +02:00
|
|
|
* live introduction point, and note that the service descriptor is
|
2009-10-18 00:52:18 +02:00
|
|
|
* now out-of-date. */
|
2004-04-03 06:55:22 +02:00
|
|
|
int
|
2010-12-14 01:34:01 +01:00
|
|
|
rend_service_intro_established(origin_circuit_t *circuit,
|
|
|
|
const uint8_t *request,
|
2005-12-14 21:40:40 +01:00
|
|
|
size_t request_len)
|
2004-04-03 06:55:22 +02:00
|
|
|
{
|
2004-04-13 19:16:47 +02:00
|
|
|
rend_service_t *service;
|
2007-11-29 16:25:04 +01:00
|
|
|
char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
|
2006-06-05 00:42:13 +02:00
|
|
|
(void) request;
|
|
|
|
(void) request_len;
|
2004-04-13 19:16:47 +02:00
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
if (circuit->_base.purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL,
|
|
|
|
"received INTRO_ESTABLISHED cell on non-intro circuit.");
|
2004-04-03 06:55:22 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2008-09-24 16:44:29 +02:00
|
|
|
tor_assert(circuit->rend_data);
|
2009-05-03 01:04:25 +02:00
|
|
|
service = rend_service_get_by_pk_digest(
|
|
|
|
circuit->rend_data->rend_pk_digest);
|
2004-04-13 19:16:47 +02:00
|
|
|
if (!service) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_REND, "Unknown service on introduction circuit %d.",
|
2006-07-23 09:37:35 +02:00
|
|
|
circuit->_base.n_circ_id);
|
2004-04-13 19:16:47 +02:00
|
|
|
goto err;
|
|
|
|
}
|
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
|
|
|
service->desc_is_dirty = time(NULL);
|
2006-07-23 09:37:35 +02:00
|
|
|
circuit->_base.purpose = CIRCUIT_PURPOSE_S_INTRO;
|
2004-04-03 06:55:22 +02:00
|
|
|
|
2007-11-29 16:25:04 +01:00
|
|
|
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32 + 1,
|
2008-09-24 16:44:29 +02:00
|
|
|
circuit->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN);
|
2007-08-11 16:13:25 +02:00
|
|
|
log_info(LD_REND,
|
|
|
|
"Received INTRO_ESTABLISHED cell on circuit %d for service %s",
|
|
|
|
circuit->_base.n_circ_id, serviceid);
|
|
|
|
|
2004-04-03 06:55:22 +02:00
|
|
|
return 0;
|
|
|
|
err:
|
2006-10-09 17:47:27 +02:00
|
|
|
circuit_mark_for_close(TO_CIRCUIT(circuit), END_CIRC_REASON_TORPROTOCOL);
|
2004-04-03 06:55:22 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Called once a circuit to a rendezvous point is established: sends a
|
2004-04-02 00:21:01 +02:00
|
|
|
* RELAY_COMMAND_RENDEZVOUS1 cell.
|
|
|
|
*/
|
|
|
|
void
|
2006-07-23 09:37:35 +02:00
|
|
|
rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
|
2004-04-02 00:21:01 +02:00
|
|
|
{
|
|
|
|
rend_service_t *service;
|
|
|
|
char buf[RELAY_PAYLOAD_SIZE];
|
|
|
|
crypt_path_t *hop;
|
2007-11-29 16:25:04 +01:00
|
|
|
char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
|
2004-04-03 02:58:54 +02:00
|
|
|
char hexcookie[9];
|
2006-10-09 17:47:50 +02:00
|
|
|
int reason;
|
2004-04-02 00:21:01 +02:00
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
tor_assert(circuit->_base.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
|
2004-04-25 22:37:37 +02:00
|
|
|
tor_assert(circuit->cpath);
|
|
|
|
tor_assert(circuit->build_state);
|
2008-09-24 16:44:29 +02:00
|
|
|
tor_assert(circuit->rend_data);
|
2004-04-02 00:21:01 +02:00
|
|
|
hop = circuit->build_state->pending_final_cpath;
|
2004-04-25 22:37:37 +02:00
|
|
|
tor_assert(hop);
|
2004-04-02 00:21:01 +02:00
|
|
|
|
2008-09-24 16:44:29 +02:00
|
|
|
base16_encode(hexcookie,9,circuit->rend_data->rend_cookie,4);
|
2007-11-29 16:25:04 +01:00
|
|
|
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
|
2008-09-24 16:44:29 +02:00
|
|
|
circuit->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN);
|
2004-04-03 02:58:54 +02:00
|
|
|
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,
|
|
|
|
"Done building circuit %d to rendezvous with "
|
|
|
|
"cookie %s for service %s",
|
2006-07-23 09:37:35 +02:00
|
|
|
circuit->_base.n_circ_id, hexcookie, serviceid);
|
2004-04-03 02:58:54 +02:00
|
|
|
|
2009-05-03 01:04:25 +02:00
|
|
|
service = rend_service_get_by_pk_digest(
|
|
|
|
circuit->rend_data->rend_pk_digest);
|
2004-04-02 00:21:01 +02:00
|
|
|
if (!service) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_GENERAL, "Internal error: unrecognized service ID on "
|
|
|
|
"introduction circuit.");
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_INTERNAL;
|
2004-04-02 00:21:01 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* All we need to do is send a RELAY_RENDEZVOUS1 cell... */
|
2008-09-24 16:44:29 +02:00
|
|
|
memcpy(buf, circuit->rend_data->rend_cookie, REND_COOKIE_LEN);
|
2005-05-03 00:35:18 +02:00
|
|
|
if (crypto_dh_get_public(hop->dh_handshake_state,
|
2004-04-02 00:21:01 +02:00
|
|
|
buf+REND_COOKIE_LEN, DH_KEY_LEN)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_GENERAL,"Couldn't get DH public key.");
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_INTERNAL;
|
2004-04-02 00:21:01 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
memcpy(buf+REND_COOKIE_LEN+DH_KEY_LEN, hop->handshake_digest,
|
2004-04-03 04:40:30 +02:00
|
|
|
DIGEST_LEN);
|
2004-04-02 00:21:01 +02:00
|
|
|
|
|
|
|
/* Send the cell */
|
2007-03-24 16:57:51 +01:00
|
|
|
if (relay_send_command_from_edge(0, TO_CIRCUIT(circuit),
|
2006-07-23 09:37:35 +02:00
|
|
|
RELAY_COMMAND_RENDEZVOUS1,
|
2004-04-05 23:39:47 +02:00
|
|
|
buf, REND_COOKIE_LEN+DH_KEY_LEN+DIGEST_LEN,
|
2004-04-02 00:21:01 +02:00
|
|
|
circuit->cpath->prev)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_GENERAL, "Couldn't send RENDEZVOUS1 cell.");
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_INTERNAL;
|
2004-04-02 00:21:01 +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-02 00:21:01 +02:00
|
|
|
/* Append the cpath entry. */
|
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 bob thinks alice has.
|
|
|
|
*/
|
2009-09-17 07:58:39 +02:00
|
|
|
hop->package_window = circuit_initial_package_window();
|
2004-04-06 23:25:11 +02:00
|
|
|
hop->deliver_window = CIRCWINDOW_START;
|
|
|
|
|
2004-04-02 00:21:01 +02:00
|
|
|
onion_append_to_cpath(&circuit->cpath, hop);
|
|
|
|
circuit->build_state->pending_final_cpath = NULL; /* prevent double-free */
|
|
|
|
|
|
|
|
/* Change the circuit purpose. */
|
2006-07-23 09:37:35 +02:00
|
|
|
circuit->_base.purpose = CIRCUIT_PURPOSE_S_REND_JOINED;
|
2004-04-02 00:21:01 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
err:
|
2006-10-09 17:47:50 +02:00
|
|
|
circuit_mark_for_close(TO_CIRCUIT(circuit), reason);
|
2004-04-02 00:21:01 +02:00
|
|
|
}
|
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/*
|
2004-04-01 05:34:05 +02:00
|
|
|
* Manage introduction points
|
2004-05-10 06:34:48 +02:00
|
|
|
*/
|
2004-04-01 05:23:28 +02:00
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Return the (possibly non-open) introduction circuit ending at
|
2009-05-03 00:09:18 +02:00
|
|
|
* <b>intro</b> for the service whose public key is <b>pk_digest</b>.
|
|
|
|
* (<b>desc_version</b> is ignored). Return NULL if no such service is
|
|
|
|
* found.
|
2004-04-06 05:44:36 +02:00
|
|
|
*/
|
2006-07-23 09:37:35 +02:00
|
|
|
static origin_circuit_t *
|
2009-05-03 01:04:25 +02:00
|
|
|
find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest)
|
2004-04-03 06:55:22 +02:00
|
|
|
{
|
2006-07-26 21:05:41 +02:00
|
|
|
origin_circuit_t *circ = NULL;
|
2004-04-03 06:55:22 +02:00
|
|
|
|
2007-12-21 10:28:22 +01:00
|
|
|
tor_assert(intro);
|
2004-04-03 06:55:22 +02:00
|
|
|
while ((circ = circuit_get_next_by_pk_and_purpose(circ,pk_digest,
|
|
|
|
CIRCUIT_PURPOSE_S_INTRO))) {
|
2011-05-10 22:23:43 +02:00
|
|
|
if (tor_memeq(circ->build_state->chosen_exit->identity_digest,
|
2008-06-12 00:46:31 +02:00
|
|
|
intro->extend_info->identity_digest, DIGEST_LEN) &&
|
2009-05-03 00:09:18 +02:00
|
|
|
circ->rend_data) {
|
2006-07-26 21:05:41 +02:00
|
|
|
return circ;
|
2004-04-03 06:55:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
circ = NULL;
|
|
|
|
while ((circ = circuit_get_next_by_pk_and_purpose(circ,pk_digest,
|
|
|
|
CIRCUIT_PURPOSE_S_ESTABLISH_INTRO))) {
|
2011-05-10 22:23:43 +02:00
|
|
|
if (tor_memeq(circ->build_state->chosen_exit->identity_digest,
|
2008-06-12 00:46:31 +02:00
|
|
|
intro->extend_info->identity_digest, DIGEST_LEN) &&
|
2009-05-03 00:09:18 +02:00
|
|
|
circ->rend_data) {
|
2006-07-26 21:05:41 +02:00
|
|
|
return circ;
|
2004-04-03 06:55:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-01-26 01:42:13 +01:00
|
|
|
/** Determine the responsible hidden service directories for the
|
|
|
|
* rend_encoded_v2_service_descriptor_t's in <b>descs</b> and upload them;
|
|
|
|
* <b>service_id</b> and <b>seconds_valid</b> are only passed for logging
|
|
|
|
* purposes. */
|
2008-01-27 02:03:30 +01:00
|
|
|
static void
|
2008-09-09 10:41:58 +02:00
|
|
|
directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
|
|
|
|
smartlist_t *descs, const char *service_id,
|
2008-01-26 01:42:13 +01:00
|
|
|
int seconds_valid)
|
|
|
|
{
|
2008-09-09 10:41:58 +02:00
|
|
|
int i, j, failed_upload = 0;
|
2008-01-26 01:42:13 +01:00
|
|
|
smartlist_t *responsible_dirs = smartlist_create();
|
2008-09-09 10:41:58 +02:00
|
|
|
smartlist_t *successful_uploads = smartlist_create();
|
2008-01-26 01:42:13 +01:00
|
|
|
routerstatus_t *hs_dir;
|
|
|
|
for (i = 0; i < smartlist_len(descs); i++) {
|
|
|
|
rend_encoded_v2_service_descriptor_t *desc = smartlist_get(descs, i);
|
|
|
|
/* Determine responsible dirs. */
|
|
|
|
if (hid_serv_get_responsible_directories(responsible_dirs,
|
|
|
|
desc->desc_id) < 0) {
|
|
|
|
log_warn(LD_REND, "Could not determine the responsible hidden service "
|
|
|
|
"directories to post descriptors to.");
|
|
|
|
smartlist_free(responsible_dirs);
|
2008-09-09 10:41:58 +02:00
|
|
|
smartlist_free(successful_uploads);
|
2008-01-26 01:42:13 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (j = 0; j < smartlist_len(responsible_dirs); j++) {
|
|
|
|
char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
|
2009-10-27 07:09:10 +01:00
|
|
|
char *hs_dir_ip;
|
2008-01-26 01:42:13 +01:00
|
|
|
hs_dir = smartlist_get(responsible_dirs, j);
|
2008-09-09 10:41:58 +02:00
|
|
|
if (smartlist_digest_isin(renddesc->successful_uploads,
|
|
|
|
hs_dir->identity_digest))
|
|
|
|
/* Don't upload descriptor if we succeeded in doing so last time. */
|
|
|
|
continue;
|
|
|
|
if (!router_get_by_digest(hs_dir->identity_digest)) {
|
|
|
|
log_info(LD_REND, "Not sending publish request for v2 descriptor to "
|
2011-05-16 03:58:46 +02:00
|
|
|
"hidden service directory %s; we don't have its "
|
2009-05-27 23:55:51 +02:00
|
|
|
"router descriptor. Queuing for later upload.",
|
2011-05-16 03:58:46 +02:00
|
|
|
safe_str_client(routerstatus_describe(hs_dir)));
|
2008-09-09 10:41:58 +02:00
|
|
|
failed_upload = -1;
|
|
|
|
continue;
|
|
|
|
}
|
2008-01-26 01:42:13 +01:00
|
|
|
/* Send publish request. */
|
|
|
|
directory_initiate_command_routerstatus(hs_dir,
|
|
|
|
DIR_PURPOSE_UPLOAD_RENDDESC_V2,
|
|
|
|
ROUTER_PURPOSE_GENERAL,
|
|
|
|
1, NULL, desc->desc_str,
|
|
|
|
strlen(desc->desc_str), 0);
|
|
|
|
base32_encode(desc_id_base32, sizeof(desc_id_base32),
|
|
|
|
desc->desc_id, DIGEST_LEN);
|
2009-10-27 07:09:10 +01:00
|
|
|
hs_dir_ip = tor_dup_ip(hs_dir->addr);
|
2008-01-26 01:42:13 +01:00
|
|
|
log_info(LD_REND, "Sending publish request for v2 descriptor for "
|
|
|
|
"service '%s' with descriptor ID '%s' with validity "
|
|
|
|
"of %d seconds to hidden service directory '%s' on "
|
2009-10-27 07:09:10 +01:00
|
|
|
"%s:%d.",
|
2009-12-15 23:23:36 +01:00
|
|
|
safe_str_client(service_id),
|
|
|
|
safe_str_client(desc_id_base32),
|
2008-01-26 01:42:13 +01:00
|
|
|
seconds_valid,
|
|
|
|
hs_dir->nickname,
|
2009-10-27 07:09:10 +01:00
|
|
|
hs_dir_ip,
|
|
|
|
hs_dir->or_port);
|
|
|
|
tor_free(hs_dir_ip);
|
2008-09-09 10:41:58 +02:00
|
|
|
/* Remember successful upload to this router for next time. */
|
|
|
|
if (!smartlist_digest_isin(successful_uploads, hs_dir->identity_digest))
|
|
|
|
smartlist_add(successful_uploads, hs_dir->identity_digest);
|
2008-01-26 01:42:13 +01:00
|
|
|
}
|
|
|
|
smartlist_clear(responsible_dirs);
|
|
|
|
}
|
2008-09-09 10:41:58 +02:00
|
|
|
if (!failed_upload) {
|
|
|
|
if (renddesc->successful_uploads) {
|
|
|
|
SMARTLIST_FOREACH(renddesc->successful_uploads, char *, c, tor_free(c););
|
|
|
|
smartlist_free(renddesc->successful_uploads);
|
2009-03-18 15:35:24 +01:00
|
|
|
renddesc->successful_uploads = NULL;
|
2008-09-09 10:41:58 +02:00
|
|
|
}
|
2008-09-09 11:21:21 +02:00
|
|
|
renddesc->all_uploads_performed = 1;
|
2008-09-09 10:41:58 +02:00
|
|
|
} else {
|
|
|
|
/* Remember which routers worked this time, so that we don't upload the
|
|
|
|
* descriptor to them again. */
|
|
|
|
if (!renddesc->successful_uploads)
|
|
|
|
renddesc->successful_uploads = smartlist_create();
|
2009-03-18 15:35:24 +01:00
|
|
|
SMARTLIST_FOREACH(successful_uploads, const char *, c, {
|
2008-09-09 10:41:58 +02:00
|
|
|
if (!smartlist_digest_isin(renddesc->successful_uploads, c)) {
|
2009-03-18 15:35:24 +01:00
|
|
|
char *hsdir_id = tor_memdup(c, DIGEST_LEN);
|
2008-09-09 10:41:58 +02:00
|
|
|
smartlist_add(renddesc->successful_uploads, hsdir_id);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2008-01-26 01:42:13 +01:00
|
|
|
smartlist_free(responsible_dirs);
|
2008-09-09 10:41:58 +02:00
|
|
|
smartlist_free(successful_uploads);
|
2008-01-26 01:42:13 +01:00
|
|
|
}
|
|
|
|
|
2009-05-02 23:31:58 +02:00
|
|
|
/** Encode and sign an up-to-date service descriptor for <b>service</b>,
|
|
|
|
* and upload it/them to the responsible hidden service directories.
|
2004-05-05 23:32:43 +02:00
|
|
|
*/
|
2004-04-13 19:16:47 +02:00
|
|
|
static void
|
2007-09-18 23:17:45 +02:00
|
|
|
upload_service_descriptor(rend_service_t *service)
|
2004-04-13 19:16:47 +02:00
|
|
|
{
|
2007-10-31 21:48:06 +01:00
|
|
|
time_t now = time(NULL);
|
|
|
|
int rendpostperiod;
|
2007-11-29 16:25:04 +01:00
|
|
|
char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
|
2007-10-31 21:48:06 +01:00
|
|
|
int uploaded = 0;
|
2004-04-13 19:16:47 +02:00
|
|
|
|
2007-10-31 21:48:06 +01:00
|
|
|
rendpostperiod = get_options()->RendPostPeriod;
|
|
|
|
|
2009-05-02 23:31:58 +02:00
|
|
|
/* Upload descriptor? */
|
|
|
|
if (get_options()->PublishHidServDescriptors) {
|
2008-02-05 22:39:29 +01:00
|
|
|
networkstatus_t *c = networkstatus_get_latest_consensus();
|
2008-01-24 23:46:29 +01:00
|
|
|
if (c && smartlist_len(c->routerstatus_list) > 0) {
|
2008-08-19 17:41:28 +02:00
|
|
|
int seconds_valid, i, j, num_descs;
|
2007-12-15 21:28:09 +01:00
|
|
|
smartlist_t *descs = smartlist_create();
|
2008-08-19 17:41:28 +02:00
|
|
|
smartlist_t *client_cookies = smartlist_create();
|
|
|
|
/* Either upload a single descriptor (including replicas) or one
|
|
|
|
* descriptor for each authorized client in case of authorization
|
|
|
|
* type 'stealth'. */
|
|
|
|
num_descs = service->auth_type == REND_STEALTH_AUTH ?
|
|
|
|
smartlist_len(service->clients) : 1;
|
|
|
|
for (j = 0; j < num_descs; j++) {
|
|
|
|
crypto_pk_env_t *client_key = NULL;
|
|
|
|
rend_authorized_client_t *client = NULL;
|
|
|
|
smartlist_clear(client_cookies);
|
|
|
|
switch (service->auth_type) {
|
|
|
|
case REND_NO_AUTH:
|
|
|
|
/* Do nothing here. */
|
|
|
|
break;
|
|
|
|
case REND_BASIC_AUTH:
|
|
|
|
SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *,
|
|
|
|
cl, smartlist_add(client_cookies, cl->descriptor_cookie));
|
|
|
|
break;
|
|
|
|
case REND_STEALTH_AUTH:
|
|
|
|
client = smartlist_get(service->clients, j);
|
|
|
|
client_key = client->client_key;
|
|
|
|
smartlist_add(client_cookies, client->descriptor_cookie);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Encode the current descriptor. */
|
2007-12-15 21:28:09 +01:00
|
|
|
seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
|
2008-08-19 17:41:28 +02:00
|
|
|
now, 0,
|
|
|
|
service->auth_type,
|
|
|
|
client_key,
|
|
|
|
client_cookies);
|
2007-10-31 21:48:06 +01:00
|
|
|
if (seconds_valid < 0) {
|
|
|
|
log_warn(LD_BUG, "Internal error: couldn't encode service "
|
|
|
|
"descriptor; not uploading.");
|
2007-12-15 21:50:56 +01:00
|
|
|
smartlist_free(descs);
|
2008-08-19 17:41:28 +02:00
|
|
|
smartlist_free(client_cookies);
|
2007-10-31 21:48:06 +01:00
|
|
|
return;
|
|
|
|
}
|
2008-08-19 17:41:28 +02:00
|
|
|
/* Post the current descriptors to the hidden service directories. */
|
|
|
|
rend_get_service_id(service->desc->pk, serviceid);
|
|
|
|
log_info(LD_REND, "Sending publish request for hidden service %s",
|
|
|
|
serviceid);
|
2008-09-09 10:41:58 +02:00
|
|
|
directory_post_to_hs_dir(service->desc, descs, serviceid,
|
|
|
|
seconds_valid);
|
2007-10-31 21:48:06 +01:00
|
|
|
/* Free memory for descriptors. */
|
2007-12-15 21:50:56 +01:00
|
|
|
for (i = 0; i < smartlist_len(descs); i++)
|
2007-12-15 21:28:09 +01:00
|
|
|
rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
|
2008-08-19 17:41:28 +02:00
|
|
|
smartlist_clear(descs);
|
|
|
|
/* Update next upload time. */
|
|
|
|
if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS
|
|
|
|
> rendpostperiod)
|
|
|
|
service->next_upload_time = now + rendpostperiod;
|
|
|
|
else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS)
|
|
|
|
service->next_upload_time = now + seconds_valid + 1;
|
|
|
|
else
|
|
|
|
service->next_upload_time = now + seconds_valid -
|
|
|
|
REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1;
|
|
|
|
/* Post also the next descriptors, if necessary. */
|
|
|
|
if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) {
|
|
|
|
seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
|
|
|
|
now, 1,
|
|
|
|
service->auth_type,
|
|
|
|
client_key,
|
|
|
|
client_cookies);
|
|
|
|
if (seconds_valid < 0) {
|
|
|
|
log_warn(LD_BUG, "Internal error: couldn't encode service "
|
|
|
|
"descriptor; not uploading.");
|
|
|
|
smartlist_free(descs);
|
|
|
|
smartlist_free(client_cookies);
|
|
|
|
return;
|
|
|
|
}
|
2008-09-09 10:41:58 +02:00
|
|
|
directory_post_to_hs_dir(service->desc, descs, serviceid,
|
|
|
|
seconds_valid);
|
2008-08-19 17:41:28 +02:00
|
|
|
/* Free memory for descriptors. */
|
|
|
|
for (i = 0; i < smartlist_len(descs); i++)
|
|
|
|
rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
|
|
|
|
smartlist_clear(descs);
|
|
|
|
}
|
2007-10-31 21:48:06 +01:00
|
|
|
}
|
2007-12-15 21:50:56 +01:00
|
|
|
smartlist_free(descs);
|
2008-08-19 17:41:28 +02:00
|
|
|
smartlist_free(client_cookies);
|
2007-10-31 21:48:06 +01:00
|
|
|
uploaded = 1;
|
|
|
|
log_info(LD_REND, "Successfully uploaded v2 rend descriptors!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If not uploaded, try again in one minute. */
|
|
|
|
if (!uploaded)
|
|
|
|
service->next_upload_time = now + 60;
|
2004-04-13 19:16:47 +02:00
|
|
|
|
2007-10-31 21:48:06 +01:00
|
|
|
/* Unmark dirty flag of this service. */
|
2004-04-13 19:16:47 +02:00
|
|
|
service->desc_is_dirty = 0;
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** For every service, check how many intro points it currently has, and:
|
2004-04-03 01:01:00 +02:00
|
|
|
* - Pick new intro points as necessary.
|
|
|
|
* - Launch circuits to any new intro points.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
|
|
|
rend_services_introduce(void)
|
|
|
|
{
|
2004-04-02 00:21:01 +02:00
|
|
|
int i,j,r;
|
2004-04-01 05:23:28 +02:00
|
|
|
routerinfo_t *router;
|
2004-04-02 00:21:01 +02:00
|
|
|
rend_service_t *service;
|
2007-12-21 10:28:22 +01:00
|
|
|
rend_intro_point_t *intro;
|
2004-04-13 19:16:47 +02:00
|
|
|
int changed, prev_intro_nodes;
|
2011-10-05 02:34:42 +02:00
|
|
|
unsigned int n_intro_points_to_open;
|
2008-08-05 00:42:27 +02:00
|
|
|
smartlist_t *intro_routers;
|
2004-05-18 03:53:53 +02:00
|
|
|
time_t now;
|
2008-07-18 20:36:32 +02:00
|
|
|
or_options_t *options = get_options();
|
2004-04-01 05:23:28 +02:00
|
|
|
|
2004-04-06 23:19:59 +02:00
|
|
|
intro_routers = smartlist_create();
|
2004-05-18 03:53:53 +02:00
|
|
|
now = time(NULL);
|
2004-04-01 05:23:28 +02:00
|
|
|
|
2005-01-14 05:35:53 +01:00
|
|
|
for (i=0; i < smartlist_len(rend_service_list); ++i) {
|
2004-04-06 23:19:59 +02:00
|
|
|
smartlist_clear(intro_routers);
|
2004-04-03 02:58:54 +02:00
|
|
|
service = smartlist_get(rend_service_list, i);
|
|
|
|
|
2004-04-25 22:37:37 +02:00
|
|
|
tor_assert(service);
|
2004-04-03 01:01:00 +02:00
|
|
|
changed = 0;
|
2004-05-18 03:53:53 +02:00
|
|
|
if (now > service->intro_period_started+INTRO_CIRC_RETRY_PERIOD) {
|
|
|
|
/* One period has elapsed; we can try building circuits again. */
|
|
|
|
service->intro_period_started = now;
|
|
|
|
service->n_intro_circuits_launched = 0;
|
2004-11-23 00:28:26 +01:00
|
|
|
} else if (service->n_intro_circuits_launched >=
|
|
|
|
MAX_INTRO_CIRCS_PER_PERIOD) {
|
2004-05-18 03:53:53 +02:00
|
|
|
/* We have failed too many times in this period; wait for the next
|
|
|
|
* one before we try again. */
|
|
|
|
continue;
|
|
|
|
}
|
2004-04-03 01:01:00 +02:00
|
|
|
|
2005-12-14 21:40:40 +01:00
|
|
|
/* Find out which introduction points we have in progress for this
|
|
|
|
service. */
|
2005-01-14 05:35:53 +01:00
|
|
|
for (j=0; j < smartlist_len(service->intro_nodes); ++j) {
|
2004-04-16 13:48:03 +02:00
|
|
|
intro = smartlist_get(service->intro_nodes, j);
|
2007-12-21 10:28:22 +01:00
|
|
|
router = router_get_by_digest(intro->extend_info->identity_digest);
|
2009-05-03 01:04:25 +02:00
|
|
|
if (!router || !find_intro_circuit(intro, service->pk_digest)) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,"Giving up on %s as intro point for %s.",
|
2011-05-16 03:58:46 +02:00
|
|
|
safe_str_client(extend_info_describe(intro->extend_info)),
|
|
|
|
safe_str_client(service->service_id));
|
2008-07-10 23:02:01 +02:00
|
|
|
if (service->desc) {
|
|
|
|
SMARTLIST_FOREACH(service->desc->intro_nodes, rend_intro_point_t *,
|
|
|
|
dintro, {
|
2011-05-10 22:23:43 +02:00
|
|
|
if (tor_memeq(dintro->extend_info->identity_digest,
|
2008-07-10 23:02:01 +02:00
|
|
|
intro->extend_info->identity_digest, DIGEST_LEN)) {
|
|
|
|
log_info(LD_REND, "The intro point we are giving up on was "
|
|
|
|
"included in the last published descriptor. "
|
|
|
|
"Marking current descriptor as dirty.");
|
|
|
|
service->desc_is_dirty = now;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2007-12-21 10:28:22 +01:00
|
|
|
rend_intro_point_free(intro);
|
2004-04-03 06:55:22 +02:00
|
|
|
smartlist_del(service->intro_nodes,j--);
|
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
|
|
|
changed = 1;
|
2004-04-03 01:01:00 +02:00
|
|
|
}
|
2008-08-05 01:35:12 +02:00
|
|
|
if (router)
|
|
|
|
smartlist_add(intro_routers, router);
|
2004-04-03 01:01:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* We have enough intro points, and the intro points we thought we had were
|
|
|
|
* all connected.
|
|
|
|
*/
|
2011-10-05 02:34:42 +02:00
|
|
|
if (!changed && (smartlist_len(service->intro_nodes) >=
|
|
|
|
(int)service->n_intro_points_wanted)) { /*XXX023 remove cast*/
|
2004-05-18 03:53:53 +02:00
|
|
|
/* We have all our intro points! Start a fresh period and reset the
|
|
|
|
* circuit count. */
|
|
|
|
service->intro_period_started = now;
|
|
|
|
service->n_intro_circuits_launched = 0;
|
2004-04-03 01:01:00 +02:00
|
|
|
continue;
|
2004-05-18 03:53:53 +02:00
|
|
|
}
|
2004-04-03 01:01:00 +02:00
|
|
|
|
|
|
|
/* Remember how many introduction circuits we started with. */
|
2004-04-03 02:58:54 +02:00
|
|
|
prev_intro_nodes = smartlist_len(service->intro_nodes);
|
2008-10-15 23:12:51 +02:00
|
|
|
/* We have enough directory information to start establishing our
|
2011-10-05 02:34:42 +02:00
|
|
|
* intro points. We want to end up with n_intro_points_wanted
|
|
|
|
* intro points, but if we're just starting, we launch two extra
|
|
|
|
* circuits and use the first n_intro_points_wanted that complete.
|
2008-10-15 23:12:51 +02:00
|
|
|
*
|
|
|
|
* The ones after the first three will be converted to 'general'
|
|
|
|
* internal circuits in rend_service_intro_has_opened(), and then
|
|
|
|
* we'll drop them from the list of intro points next time we
|
|
|
|
* go through the above "find out which introduction points we have
|
|
|
|
* in progress" loop. */
|
2011-10-05 02:34:42 +02:00
|
|
|
n_intro_points_to_open = (service->n_intro_points_wanted +
|
|
|
|
(prev_intro_nodes == 0 ? 2 : 0));
|
|
|
|
for (j=prev_intro_nodes; j < (int)n_intro_points_to_open; ++j) { /* XXXX remove cast */
|
2008-07-24 11:22:34 +02:00
|
|
|
router_crn_flags_t flags = CRN_NEED_UPTIME;
|
|
|
|
if (get_options()->_AllowInvalid & ALLOW_INVALID_INTRODUCTION)
|
|
|
|
flags |= CRN_ALLOW_INVALID;
|
2009-09-18 04:45:54 +02:00
|
|
|
router = router_choose_random_node(intro_routers,
|
2008-07-24 11:22:34 +02:00
|
|
|
options->ExcludeNodes, flags);
|
2004-04-03 01:01:00 +02:00
|
|
|
if (!router) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_REND,
|
2011-10-05 02:34:42 +02:00
|
|
|
"Could only establish %d introduction points for %s; "
|
|
|
|
"wanted %u.",
|
|
|
|
smartlist_len(service->intro_nodes), service->service_id,
|
|
|
|
n_intro_points_to_open);
|
2004-04-03 01:01:00 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
changed = 1;
|
2004-04-06 23:19:59 +02:00
|
|
|
smartlist_add(intro_routers, router);
|
2007-12-21 10:28:22 +01:00
|
|
|
intro = tor_malloc_zero(sizeof(rend_intro_point_t));
|
|
|
|
intro->extend_info = extend_info_from_router(router);
|
2009-05-02 23:31:58 +02:00
|
|
|
intro->intro_key = crypto_new_pk_env();
|
|
|
|
tor_assert(!crypto_pk_generate_key(intro->intro_key));
|
2011-10-06 00:43:02 +02:00
|
|
|
intro->time_published = -1;
|
2007-12-21 10:28:22 +01:00
|
|
|
smartlist_add(service->intro_nodes, intro);
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND, "Picked router %s as an intro point for %s.",
|
2011-05-16 03:58:46 +02:00
|
|
|
safe_str_client(router_describe(router)),
|
|
|
|
safe_str_client(service->service_id));
|
2004-04-01 05:23:28 +02:00
|
|
|
}
|
|
|
|
|
2004-04-13 19:16:47 +02:00
|
|
|
/* If there's no need to launch new circuits, stop here. */
|
|
|
|
if (!changed)
|
2004-04-03 01:01:00 +02:00
|
|
|
continue;
|
2004-04-01 05:23:28 +02:00
|
|
|
|
2004-04-03 01:01:00 +02:00
|
|
|
/* Establish new introduction points. */
|
2004-04-03 02:58:54 +02:00
|
|
|
for (j=prev_intro_nodes; j < smartlist_len(service->intro_nodes); ++j) {
|
|
|
|
intro = smartlist_get(service->intro_nodes, j);
|
2004-04-03 01:01:00 +02:00
|
|
|
r = rend_service_launch_establish_intro(service, intro);
|
|
|
|
if (r<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_REND, "Error launching circuit to node %s for service %s.",
|
2011-05-16 03:58:46 +02:00
|
|
|
safe_str_client(extend_info_describe(intro->extend_info)),
|
|
|
|
safe_str_client(service->service_id));
|
2004-04-03 01:01:00 +02:00
|
|
|
}
|
|
|
|
}
|
2004-04-02 00:21:01 +02:00
|
|
|
}
|
2004-04-06 23:19:59 +02:00
|
|
|
smartlist_free(intro_routers);
|
2004-04-01 05:23:28 +02:00
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Regenerate and upload rendezvous service descriptors for all
|
2004-11-15 10:05:54 +01:00
|
|
|
* services, if necessary. If the descriptor has been dirty enough
|
|
|
|
* for long enough, definitely upload; else only upload when the
|
|
|
|
* periodic timeout has expired.
|
|
|
|
*
|
|
|
|
* For the first upload, pick a random time between now and two periods
|
|
|
|
* from now, and pick it independently for each service.
|
2004-05-05 23:32:43 +02:00
|
|
|
*/
|
2004-04-13 19:16:47 +02:00
|
|
|
void
|
2005-06-11 20:52:12 +02:00
|
|
|
rend_consider_services_upload(time_t now)
|
|
|
|
{
|
2004-04-13 19:16:47 +02:00
|
|
|
int i;
|
|
|
|
rend_service_t *service;
|
2004-11-15 10:05:54 +01:00
|
|
|
int rendpostperiod = get_options()->RendPostPeriod;
|
2004-04-13 19:16:47 +02:00
|
|
|
|
2006-02-19 23:02:02 +01:00
|
|
|
if (!get_options()->PublishHidServDescriptors)
|
|
|
|
return;
|
|
|
|
|
2005-01-14 05:35:53 +01:00
|
|
|
for (i=0; i < smartlist_len(rend_service_list); ++i) {
|
2004-04-13 19:16:47 +02:00
|
|
|
service = smartlist_get(rend_service_list, i);
|
2004-11-15 10:05:54 +01:00
|
|
|
if (!service->next_upload_time) { /* never been uploaded yet */
|
2008-06-11 01:31:55 +02:00
|
|
|
/* The fixed lower bound of 30 seconds ensures that the descriptor
|
|
|
|
* is stable before being published. See comment below. */
|
2004-11-15 10:05:54 +01:00
|
|
|
service->next_upload_time =
|
2008-06-11 01:31:55 +02:00
|
|
|
now + 30 + crypto_rand_int(2*rendpostperiod);
|
2004-11-15 10:05:54 +01:00
|
|
|
}
|
|
|
|
if (service->next_upload_time < now ||
|
|
|
|
(service->desc_is_dirty &&
|
2007-03-01 01:41:28 +01:00
|
|
|
service->desc_is_dirty < now-30)) {
|
2004-11-15 10:05:54 +01:00
|
|
|
/* if it's time, or if the directory servers have a wrong service
|
2007-03-01 01:41:28 +01:00
|
|
|
* descriptor and ours has been stable for 30 seconds, upload a
|
2005-08-12 17:05:05 +02:00
|
|
|
* new one of each format. */
|
2008-09-09 10:41:58 +02:00
|
|
|
rend_service_update_descriptor(service);
|
|
|
|
upload_service_descriptor(service);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** True if the list of available router descriptors might have changed so
|
|
|
|
* that we should have a look whether we can republish previously failed
|
|
|
|
* rendezvous service descriptors. */
|
|
|
|
static int consider_republishing_rend_descriptors = 1;
|
|
|
|
|
|
|
|
/** Called when our internal view of the directory has changed, so that we
|
|
|
|
* might have router descriptors of hidden service directories available that
|
|
|
|
* we did not have before. */
|
|
|
|
void
|
|
|
|
rend_hsdir_routers_changed(void)
|
|
|
|
{
|
|
|
|
consider_republishing_rend_descriptors = 1;
|
|
|
|
}
|
|
|
|
|
2008-09-23 11:30:57 +02:00
|
|
|
/** Consider republication of v2 rendezvous service descriptors that failed
|
2008-09-09 10:41:58 +02:00
|
|
|
* previously, but without regenerating descriptor contents.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
rend_consider_descriptor_republication(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
rend_service_t *service;
|
|
|
|
|
|
|
|
if (!consider_republishing_rend_descriptors)
|
|
|
|
return;
|
|
|
|
consider_republishing_rend_descriptors = 0;
|
|
|
|
|
|
|
|
if (!get_options()->PublishHidServDescriptors)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i=0; i < smartlist_len(rend_service_list); ++i) {
|
|
|
|
service = smartlist_get(rend_service_list, i);
|
2009-05-02 23:31:58 +02:00
|
|
|
if (service->desc && !service->desc->all_uploads_performed) {
|
2008-09-09 10:41:58 +02:00
|
|
|
/* If we failed in uploading a descriptor last time, try again *without*
|
|
|
|
* updating the descriptor's contents. */
|
2007-09-18 23:17:45 +02:00
|
|
|
upload_service_descriptor(service);
|
2004-11-15 10:05:54 +01:00
|
|
|
}
|
2004-04-13 19:16:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Log the status of introduction points for all rendezvous services
|
2004-12-01 04:48:14 +01:00
|
|
|
* at log severity <b>severity</b>.
|
2004-05-05 23:32:43 +02:00
|
|
|
*/
|
2004-04-09 22:02:16 +02:00
|
|
|
void
|
|
|
|
rend_service_dump_stats(int severity)
|
|
|
|
{
|
|
|
|
int i,j;
|
|
|
|
rend_service_t *service;
|
2007-12-21 10:28:22 +01:00
|
|
|
rend_intro_point_t *intro;
|
|
|
|
const char *safe_name;
|
2006-07-23 09:37:35 +02:00
|
|
|
origin_circuit_t *circ;
|
2004-04-09 22:02:16 +02:00
|
|
|
|
|
|
|
for (i=0; i < smartlist_len(rend_service_list); ++i) {
|
|
|
|
service = smartlist_get(rend_service_list, i);
|
2005-12-14 21:40:40 +01:00
|
|
|
log(severity, LD_GENERAL, "Service configured in \"%s\":",
|
|
|
|
service->directory);
|
2004-04-09 22:02:16 +02:00
|
|
|
for (j=0; j < smartlist_len(service->intro_nodes); ++j) {
|
2007-12-21 10:28:22 +01:00
|
|
|
intro = smartlist_get(service->intro_nodes, j);
|
2009-12-15 23:23:36 +01:00
|
|
|
safe_name = safe_str_client(intro->extend_info->nickname);
|
2006-12-07 18:04:44 +01:00
|
|
|
|
2009-05-03 01:04:25 +02:00
|
|
|
circ = find_intro_circuit(intro, service->pk_digest);
|
2004-04-09 22:02:16 +02:00
|
|
|
if (!circ) {
|
2006-12-07 18:04:44 +01:00
|
|
|
log(severity, LD_GENERAL, " Intro point %d at %s: no circuit",
|
|
|
|
j, safe_name);
|
2004-04-09 22:02:16 +02:00
|
|
|
continue;
|
|
|
|
}
|
2006-12-07 18:04:44 +01:00
|
|
|
log(severity, LD_GENERAL, " Intro point %d at %s: circuit is %s",
|
|
|
|
j, safe_name, circuit_state_to_string(circ->_base.state));
|
2004-04-09 22:02:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-10 09:27:29 +02:00
|
|
|
/** Given <b>conn</b>, a rendezvous exit stream, look up the hidden service for
|
2004-05-10 06:34:48 +02:00
|
|
|
* 'circ', and look up the port and address based on conn-\>port.
|
|
|
|
* Assign the actual conn-\>addr and conn-\>port. Return -1 if failure,
|
2004-05-05 23:32:43 +02:00
|
|
|
* or 0 for success.
|
2004-04-07 00:05:49 +02:00
|
|
|
*/
|
|
|
|
int
|
2006-07-26 21:07:26 +02:00
|
|
|
rend_service_set_connection_addr_port(edge_connection_t *conn,
|
2006-07-23 09:37:35 +02:00
|
|
|
origin_circuit_t *circ)
|
2004-04-07 00:05:49 +02:00
|
|
|
{
|
|
|
|
rend_service_t *service;
|
2007-11-29 16:25:04 +01:00
|
|
|
char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
|
2007-05-29 20:58:16 +02:00
|
|
|
smartlist_t *matching_ports;
|
|
|
|
rend_service_port_config_t *chosen_port;
|
2004-04-07 00:05:49 +02:00
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
|
2008-09-24 16:44:29 +02:00
|
|
|
tor_assert(circ->rend_data);
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(LD_REND,"beginning to hunt for addr/port");
|
2007-11-29 16:25:04 +01:00
|
|
|
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
|
2008-09-24 16:44:29 +02:00
|
|
|
circ->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN);
|
2009-05-03 01:04:25 +02:00
|
|
|
service = rend_service_get_by_pk_digest(
|
|
|
|
circ->rend_data->rend_pk_digest);
|
2004-04-07 00:05:49 +02:00
|
|
|
if (!service) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_REND, "Couldn't find any service associated with pk %s on "
|
|
|
|
"rendezvous circuit %d; closing.",
|
2006-07-23 09:37:35 +02:00
|
|
|
serviceid, circ->_base.n_circ_id);
|
2004-04-15 03:23:43 +02:00
|
|
|
return -1;
|
2004-04-07 00:05:49 +02:00
|
|
|
}
|
2007-05-29 20:58:16 +02:00
|
|
|
matching_ports = smartlist_create();
|
|
|
|
SMARTLIST_FOREACH(service->ports, rend_service_port_config_t *, p,
|
|
|
|
{
|
2006-07-26 21:07:26 +02:00
|
|
|
if (conn->_base.port == p->virtual_port) {
|
2007-05-29 20:58:16 +02:00
|
|
|
smartlist_add(matching_ports, p);
|
2004-04-07 00:05:49 +02:00
|
|
|
}
|
2007-05-29 20:58:16 +02:00
|
|
|
});
|
|
|
|
chosen_port = smartlist_choose(matching_ports);
|
|
|
|
smartlist_free(matching_ports);
|
|
|
|
if (chosen_port) {
|
2008-08-05 22:08:19 +02:00
|
|
|
tor_addr_copy(&conn->_base.addr, &chosen_port->real_addr);
|
2007-05-29 20:58:16 +02:00
|
|
|
conn->_base.port = chosen_port->real_port;
|
|
|
|
return 0;
|
2004-04-07 00:05:49 +02:00
|
|
|
}
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND, "No virtual port mapping exists for port %d on service %s",
|
2006-07-26 21:07:26 +02:00
|
|
|
conn->_base.port,serviceid);
|
2004-04-07 00:05:49 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2005-06-09 21:03:31 +02:00
|
|
|
|