mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-27 13:53:31 +01:00
Add patch 4 from Karsten for proposal 121, slightly modified. Karsten should definitely re-review the bits I changed.
svn:r16955
This commit is contained in:
parent
982a22a121
commit
8bc1536a9e
11
ChangeLog
11
ChangeLog
@ -1,4 +1,13 @@
|
||||
Changes in version 0.2.1.6-alpha - 2008-09-xx
|
||||
o Major features:
|
||||
- Implement proposal 121: make it possible to build hidden services
|
||||
that only certain clients are allowed to connect to. This is
|
||||
enforced at several points, so that unauthorized clients are
|
||||
unable to send INTRODUCE cells to the service, or even (depending
|
||||
on the type of authentication) to learn introduction points. This
|
||||
feature raises the bar for certain kinds of active attacks against
|
||||
hidden services.
|
||||
|
||||
o Major bugfixes:
|
||||
- Fix a bug when parsing ports in tor_addr_port_parse() that caused
|
||||
Tor to fail to start if you had it configured to use a bridge
|
||||
@ -63,6 +72,8 @@ Changes in version 0.2.1.6-alpha - 2008-09-xx
|
||||
actual mistakes we're making here.
|
||||
- Refactor unit testing logic so that dmalloc can be used sensibly with
|
||||
unit tests to check for memory leaks.
|
||||
- Move all hidden-service related fields from connection and circuit
|
||||
structure to substructures: this way they won't eat so much memory.
|
||||
|
||||
|
||||
Changes in version 0.2.0.31 - 2008-09-03
|
||||
|
28
doc/tor.1.in
28
doc/tor.1.in
@ -472,6 +472,15 @@ used when \fBFascistFirewall\fR is set. This option is deprecated; use
|
||||
ReachableAddresses instead. (Default: 80, 443)
|
||||
.LP
|
||||
.TP
|
||||
\fBHidServAuth \fR\fIonion-address\fR \fIauth-cookie\fP \fIservice-name\fR
|
||||
Client authorization for a hidden service. Valid onion addresses contain 16
|
||||
characters in a-z2-7 plus ".onion", and valid auth cookies contain 22
|
||||
characters in A-Za-z0-9+/. The service name is only used for internal
|
||||
purposes, e.g., for Tor controllers. This option may be used multiple times
|
||||
for different hidden services. If a hidden service uses authorization and
|
||||
this option is not set, the hidden service is not accessible.
|
||||
.LP
|
||||
.TP
|
||||
\fBReachableAddresses \fR\fIADDR\fP[\fB/\fP\fIMASK\fP][:\fIPORT\fP]...\fP
|
||||
A comma-separated list of IP addresses and ports that your firewall allows you
|
||||
to connect to. The format is as
|
||||
@ -1269,6 +1278,18 @@ A list of rendezvous service descriptor versions to publish for the hidden
|
||||
service. Possible version numbers are 0 and 2. (Default: 0, 2)
|
||||
.LP
|
||||
.TP
|
||||
\fBHiddenServiceAuthorizeClient \fR\fIauth-type\fR \fR\fIclient-name\fR,\fIclient-name\fR,\fI...\fP
|
||||
If configured, the hidden service is accessible for authorized clients
|
||||
only. The auth-type can either be 'basic' for a general-purpose
|
||||
authorization protocol or 'stealth' for a less scalable protocol that also
|
||||
hides service activity from unauthorized clients. Only clients that are
|
||||
listed here are authorized to access the hidden service. Valid client names
|
||||
are 1 to 19 characters long and only use characters in A-Za-z0-9+-_
|
||||
(no spaces). If this option is set, the hidden service is not accessible
|
||||
for clients without authorization any more. Generated authorization data
|
||||
can be found in the hostname file.
|
||||
.LP
|
||||
.TP
|
||||
\fBRendPostPeriod \fR\fIN\fR \fBseconds\fR|\fBminutes\fR|\fBhours\fR|\fBdays\fR|\fBweeks\fP
|
||||
Every time the specified period elapses, Tor uploads any rendezvous
|
||||
service descriptors to the directory servers. This information is also
|
||||
@ -1453,10 +1474,17 @@ Only used by authoritative directory servers. Tracks measurements for router me
|
||||
.TP
|
||||
.B \fIHiddenServiceDirectory\fP/hostname
|
||||
The <base32-encoded-fingerprint>.onion domain name for this hidden service.
|
||||
If the hidden service is restricted to authorized clients only, this file
|
||||
also contains authorization data for all clients.
|
||||
.LP
|
||||
.TP
|
||||
.B \fIHiddenServiceDirectory\fP/private_key
|
||||
The private key for this hidden service.
|
||||
.LP
|
||||
.TP
|
||||
.B \fIHiddenServiceDirectory\fP/client_keys
|
||||
Authorization data for a hidden service that is only accessible by authorized
|
||||
clients.
|
||||
.SH SEE ALSO
|
||||
.BR privoxy (1),
|
||||
.BR tsocks (1),
|
||||
|
@ -401,7 +401,8 @@ circuit_free(circuit_t *circ)
|
||||
circuit_free_cpath(ocirc->cpath);
|
||||
if (ocirc->intro_key)
|
||||
crypto_free_pk_env(ocirc->intro_key);
|
||||
|
||||
if (ocirc->rend_data)
|
||||
rend_data_free(ocirc->rend_data);
|
||||
} else {
|
||||
or_circuit_t *ocirc = TO_OR_CIRCUIT(circ);
|
||||
mem = ocirc;
|
||||
@ -720,7 +721,7 @@ circuit_unlink_all_from_or_conn(or_connection_t *conn, int reason)
|
||||
}
|
||||
|
||||
/** Return a circ such that:
|
||||
* - circ-\>rend_query is equal to <b>rend_query</b>, and
|
||||
* - circ-\>rend_data-\>query is equal to <b>rend_query</b>, and
|
||||
* - circ-\>purpose is equal to <b>purpose</b>.
|
||||
*
|
||||
* Return NULL if no such circuit exists.
|
||||
@ -734,9 +735,13 @@ circuit_get_by_rend_query_and_purpose(const char *rend_query, uint8_t purpose)
|
||||
|
||||
for (circ = global_circuitlist; circ; circ = circ->next) {
|
||||
if (!circ->marked_for_close &&
|
||||
circ->purpose == purpose &&
|
||||
!rend_cmp_service_ids(rend_query, TO_ORIGIN_CIRCUIT(circ)->rend_query))
|
||||
return TO_ORIGIN_CIRCUIT(circ);
|
||||
circ->purpose == purpose) {
|
||||
origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
|
||||
if (ocirc->rend_data &&
|
||||
!rend_cmp_service_ids(rend_query,
|
||||
ocirc->rend_data->onion_address))
|
||||
return ocirc;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -764,7 +769,8 @@ circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
|
||||
continue;
|
||||
if (!digest)
|
||||
return TO_ORIGIN_CIRCUIT(circ);
|
||||
else if (!memcmp(TO_ORIGIN_CIRCUIT(circ)->rend_pk_digest,
|
||||
else if (TO_ORIGIN_CIRCUIT(circ)->rend_data &&
|
||||
!memcmp(TO_ORIGIN_CIRCUIT(circ)->rend_data->rend_pk_digest,
|
||||
digest, DIGEST_LEN))
|
||||
return TO_ORIGIN_CIRCUIT(circ);
|
||||
}
|
||||
@ -1020,13 +1026,14 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line,
|
||||
origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
|
||||
tor_assert(circ->state == CIRCUIT_STATE_OPEN);
|
||||
tor_assert(ocirc->build_state->chosen_exit);
|
||||
tor_assert(ocirc->rend_data);
|
||||
/* treat this like getting a nack from it */
|
||||
log_info(LD_REND, "Failed intro circ %s to %s (awaiting ack). "
|
||||
"Removing from descriptor.",
|
||||
safe_str(ocirc->rend_query),
|
||||
safe_str(ocirc->rend_data->onion_address),
|
||||
safe_str(build_state_get_exit_nickname(ocirc->build_state)));
|
||||
rend_client_remove_intro_point(ocirc->build_state->chosen_exit,
|
||||
ocirc->rend_query);
|
||||
ocirc->rend_data);
|
||||
}
|
||||
if (circ->n_conn)
|
||||
connection_or_send_destroy(circ->n_circ_id, circ->n_conn, reason);
|
||||
|
@ -121,8 +121,12 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
|
||||
return 0;
|
||||
}
|
||||
} else { /* not general */
|
||||
if (rend_cmp_service_ids(conn->rend_query,
|
||||
TO_ORIGIN_CIRCUIT(circ)->rend_query)) {
|
||||
origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
|
||||
if ((conn->rend_data && !ocirc->rend_data) ||
|
||||
(!conn->rend_data && ocirc->rend_data) ||
|
||||
(conn->rend_data && ocirc->rend_data &&
|
||||
rend_cmp_service_ids(conn->rend_data->onion_address,
|
||||
ocirc->rend_data->onion_address))) {
|
||||
/* this circ is not for this conn */
|
||||
return 0;
|
||||
}
|
||||
@ -300,7 +304,7 @@ circuit_expire_building(time_t now)
|
||||
/* c_rend_ready circs measure age since timestamp_dirty,
|
||||
* because that's set when they switch purposes
|
||||
*/
|
||||
if (TO_ORIGIN_CIRCUIT(victim)->rend_query[0] ||
|
||||
if (TO_ORIGIN_CIRCUIT(victim)->rend_data ||
|
||||
victim->timestamp_dirty > cutoff)
|
||||
continue;
|
||||
break;
|
||||
@ -1076,18 +1080,24 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
|
||||
|
||||
if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
|
||||
/* need to pick an intro point */
|
||||
extend_info = rend_client_get_random_intro(conn->rend_query);
|
||||
tor_assert(conn->rend_data);
|
||||
extend_info = rend_client_get_random_intro(conn->rend_data);
|
||||
if (!extend_info) {
|
||||
log_info(LD_REND,
|
||||
"No intro points for '%s': refetching service descriptor.",
|
||||
safe_str(conn->rend_query));
|
||||
rend_client_refetch_renddesc(conn->rend_query);
|
||||
rend_client_refetch_v2_renddesc(conn->rend_query);
|
||||
safe_str(conn->rend_data->onion_address));
|
||||
/* Fetch both, v0 and v2 rend descriptors in parallel. Use whichever
|
||||
* arrives first. Exception: When using client authorization, only
|
||||
* fetch v2 descriptors.*/
|
||||
rend_client_refetch_v2_renddesc(conn->rend_data);
|
||||
if (conn->rend_data->auth_type == REND_NO_AUTH)
|
||||
rend_client_refetch_renddesc(conn->rend_data->onion_address);
|
||||
conn->_base.state = AP_CONN_STATE_RENDDESC_WAIT;
|
||||
return 0;
|
||||
}
|
||||
log_info(LD_REND,"Chose '%s' as intro point for '%s'.",
|
||||
extend_info->nickname, safe_str(conn->rend_query));
|
||||
extend_info->nickname,
|
||||
safe_str(conn->rend_data->onion_address));
|
||||
}
|
||||
|
||||
/* If we have specified a particular exit node for our
|
||||
@ -1163,7 +1173,7 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
|
||||
rep_hist_note_used_internal(time(NULL), need_uptime, 1);
|
||||
if (circ) {
|
||||
/* write the service_id into circ */
|
||||
strlcpy(circ->rend_query, conn->rend_query, sizeof(circ->rend_query));
|
||||
circ->rend_data = rend_data_dup(conn->rend_data);
|
||||
if (circ->_base.purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND &&
|
||||
circ->_base.state == CIRCUIT_STATE_OPEN)
|
||||
rend_client_rendcirc_has_opened(circ);
|
||||
|
@ -385,6 +385,8 @@ _connection_free(connection_t *conn)
|
||||
memset(edge_conn->socks_request, 0xcc, sizeof(socks_request_t));
|
||||
tor_free(edge_conn->socks_request);
|
||||
}
|
||||
if (edge_conn->rend_data)
|
||||
rend_data_free(edge_conn->rend_data);
|
||||
}
|
||||
if (conn->type == CONN_TYPE_CONTROL) {
|
||||
control_connection_t *control_conn = TO_CONTROL_CONN(conn);
|
||||
@ -405,6 +407,8 @@ _connection_free(connection_t *conn)
|
||||
}
|
||||
if (dir_conn->cached_dir)
|
||||
cached_dir_decref(dir_conn->cached_dir);
|
||||
if (dir_conn->rend_data)
|
||||
rend_data_free(dir_conn->rend_data);
|
||||
}
|
||||
|
||||
if (conn->s >= 0) {
|
||||
@ -523,21 +527,22 @@ connection_about_to_close_connection(connection_t *conn)
|
||||
* failed: forget about this router, and maybe try again. */
|
||||
connection_dir_request_failed(dir_conn);
|
||||
}
|
||||
if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC) {
|
||||
if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC && dir_conn->rend_data) {
|
||||
/* Give it a try. However, there is no re-fetching for v0 rend
|
||||
* descriptors; if the response is empty or the descriptor is
|
||||
* unusable, close pending connections (unless a v2 request is
|
||||
* still in progress). */
|
||||
rend_client_desc_trynow(dir_conn->rend_query, 0);
|
||||
rend_client_desc_trynow(dir_conn->rend_data->onion_address, 0);
|
||||
}
|
||||
/* If we were trying to fetch a v2 rend desc and did not succeed,
|
||||
* retry as needed. (If a fetch is successful, the connection state
|
||||
* is changed to DIR_PURPOSE_HAS_FETCHED_RENDDESC to mark that
|
||||
* refetching is unnecessary.) */
|
||||
if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC_V2 &&
|
||||
dir_conn->rend_query &&
|
||||
strlen(dir_conn->rend_query) == REND_SERVICE_ID_LEN_BASE32)
|
||||
rend_client_refetch_v2_renddesc(dir_conn->rend_query);
|
||||
dir_conn->rend_data &&
|
||||
strlen(dir_conn->rend_data->onion_address) ==
|
||||
REND_SERVICE_ID_LEN_BASE32)
|
||||
rend_client_refetch_v2_renddesc(dir_conn->rend_data);
|
||||
break;
|
||||
case CONN_TYPE_OR:
|
||||
or_conn = TO_OR_CONN(conn);
|
||||
@ -2565,6 +2570,7 @@ connection_get_by_type_state_rendquery(int type, int state,
|
||||
|
||||
tor_assert(type == CONN_TYPE_DIR ||
|
||||
type == CONN_TYPE_AP || type == CONN_TYPE_EXIT);
|
||||
tor_assert(rendquery);
|
||||
|
||||
SMARTLIST_FOREACH(conns, connection_t *, conn,
|
||||
{
|
||||
@ -2572,12 +2578,16 @@ connection_get_by_type_state_rendquery(int type, int state,
|
||||
!conn->marked_for_close &&
|
||||
(!state || state == conn->state)) {
|
||||
if (type == CONN_TYPE_DIR &&
|
||||
TO_DIR_CONN(conn)->rend_data &&
|
||||
(rendversion < 0 ||
|
||||
rendversion == TO_DIR_CONN(conn)->rend_version) &&
|
||||
!rend_cmp_service_ids(rendquery, TO_DIR_CONN(conn)->rend_query))
|
||||
rendversion == TO_DIR_CONN(conn)->rend_data->rend_desc_version) &&
|
||||
!rend_cmp_service_ids(rendquery,
|
||||
TO_DIR_CONN(conn)->rend_data->onion_address))
|
||||
return conn;
|
||||
else if (CONN_IS_EDGE(conn) &&
|
||||
!rend_cmp_service_ids(rendquery, TO_EDGE_CONN(conn)->rend_query))
|
||||
TO_EDGE_CONN(conn)->rend_data &&
|
||||
!rend_cmp_service_ids(rendquery,
|
||||
TO_EDGE_CONN(conn)->rend_data->onion_address))
|
||||
return conn;
|
||||
}
|
||||
});
|
||||
|
@ -1587,6 +1587,7 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
|
||||
/* it's a hidden-service request */
|
||||
rend_cache_entry_t *entry;
|
||||
int r;
|
||||
rend_service_authorization_t *client_auth;
|
||||
tor_assert(!automap);
|
||||
if (SOCKS_COMMAND_IS_RESOLVE(socks->command)) {
|
||||
/* if it's a resolve request, fail it right now, rather than
|
||||
@ -1608,14 +1609,16 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
|
||||
return -1;
|
||||
}
|
||||
|
||||
strlcpy(conn->rend_query, socks->address, sizeof(conn->rend_query));
|
||||
conn->rend_data = tor_malloc_zero(sizeof(rend_data_t));
|
||||
strlcpy(conn->rend_data->onion_address, socks->address,
|
||||
sizeof(conn->rend_data->onion_address));
|
||||
log_info(LD_REND,"Got a hidden service request for ID '%s'",
|
||||
safe_str(conn->rend_query));
|
||||
safe_str(conn->rend_data->onion_address));
|
||||
/* see if we already have it cached */
|
||||
r = rend_cache_lookup_entry(conn->rend_query, -1, &entry);
|
||||
r = rend_cache_lookup_entry(conn->rend_data->onion_address, -1, &entry);
|
||||
if (r<0) {
|
||||
log_warn(LD_BUG,"Invalid service name '%s'",
|
||||
safe_str(conn->rend_query));
|
||||
safe_str(conn->rend_data->onion_address));
|
||||
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
|
||||
return -1;
|
||||
}
|
||||
@ -1624,14 +1627,26 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
|
||||
* a stable circuit yet, but we know we'll need *something*. */
|
||||
rep_hist_note_used_internal(now, 0, 1);
|
||||
|
||||
/* Look up if we have client authorization for it. */
|
||||
client_auth = rend_client_lookup_service_authorization(
|
||||
conn->rend_data->onion_address);
|
||||
if (client_auth) {
|
||||
log_info(LD_REND, "Using previously configured client authorization "
|
||||
"for hidden service request.");
|
||||
memcpy(conn->rend_data->descriptor_cookie,
|
||||
client_auth->descriptor_cookie, REND_DESC_COOKIE_LEN);
|
||||
conn->rend_data->auth_type = client_auth->auth_type;
|
||||
}
|
||||
if (r==0) {
|
||||
conn->_base.state = AP_CONN_STATE_RENDDESC_WAIT;
|
||||
log_info(LD_REND, "Unknown descriptor %s. Fetching.",
|
||||
safe_str(conn->rend_query));
|
||||
safe_str(conn->rend_data->onion_address));
|
||||
/* Fetch both, v0 and v2 rend descriptors in parallel. Use whichever
|
||||
* arrives first. */
|
||||
rend_client_refetch_v2_renddesc(conn->rend_query);
|
||||
rend_client_refetch_renddesc(conn->rend_query);
|
||||
* arrives first. Exception: When using client authorization, only
|
||||
* fetch v2 descriptors.*/
|
||||
rend_client_refetch_v2_renddesc(conn->rend_data);
|
||||
if (conn->rend_data->auth_type == REND_NO_AUTH)
|
||||
rend_client_refetch_renddesc(conn->rend_data->onion_address);
|
||||
} else { /* r > 0 */
|
||||
/** How long after we receive a hidden service descriptor do we consider
|
||||
* it valid? */
|
||||
@ -1647,11 +1662,13 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
|
||||
} else {
|
||||
conn->_base.state = AP_CONN_STATE_RENDDESC_WAIT;
|
||||
log_info(LD_REND, "Stale descriptor %s. Refetching.",
|
||||
safe_str(conn->rend_query));
|
||||
safe_str(conn->rend_data->onion_address));
|
||||
/* Fetch both, v0 and v2 rend descriptors in parallel. Use whichever
|
||||
* arrives first. */
|
||||
rend_client_refetch_v2_renddesc(conn->rend_query);
|
||||
rend_client_refetch_renddesc(conn->rend_query);
|
||||
* arrives first. Exception: When using client authorization, only
|
||||
* fetch v2 descriptors.*/
|
||||
rend_client_refetch_v2_renddesc(conn->rend_data);
|
||||
if (conn->rend_data->auth_type == REND_NO_AUTH)
|
||||
rend_client_refetch_renddesc(conn->rend_data->onion_address);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@ -2531,8 +2548,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
|
||||
log_info(LD_REND,"begin is for rendezvous. configuring stream.");
|
||||
n_stream->_base.address = tor_strdup("(rendezvous)");
|
||||
n_stream->_base.state = EXIT_CONN_STATE_CONNECTING;
|
||||
strlcpy(n_stream->rend_query, origin_circ->rend_query,
|
||||
sizeof(n_stream->rend_query));
|
||||
n_stream->rend_data = rend_data_dup(origin_circ->rend_data);
|
||||
tor_assert(connection_edge_is_rendezvous_stream(n_stream));
|
||||
assert_circuit_ok(circ);
|
||||
if (rend_service_set_connection_addr_port(n_stream, origin_circ) < 0) {
|
||||
@ -2815,7 +2831,7 @@ int
|
||||
connection_edge_is_rendezvous_stream(edge_connection_t *conn)
|
||||
{
|
||||
tor_assert(conn);
|
||||
if (*conn->rend_query) /* XXX */ /* XXXX Why is this XXX? -NM */
|
||||
if (conn->rend_data) /* XXX */ /* XXXX Why is this XXX? -NM */
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
@ -60,6 +60,22 @@ static void dir_routerdesc_download_failed(smartlist_t *failed,
|
||||
static void note_client_request(int purpose, int compressed, size_t bytes);
|
||||
static int client_likes_consensus(networkstatus_t *v, const char *want_url);
|
||||
|
||||
static void directory_initiate_command_rend(const char *address,
|
||||
const tor_addr_t *addr,
|
||||
uint16_t or_port,
|
||||
uint16_t dir_port,
|
||||
int supports_conditional_consensus,
|
||||
int supports_begindir,
|
||||
const char *digest,
|
||||
uint8_t dir_purpose,
|
||||
uint8_t router_purpose,
|
||||
int anonymized_connection,
|
||||
const char *resource,
|
||||
const char *payload,
|
||||
size_t payload_len,
|
||||
time_t if_modified_since,
|
||||
const rend_data_t *rend_query);
|
||||
|
||||
/********* START VARIABLES **********/
|
||||
|
||||
/** How far in the future do we allow a directory server to tell us it is
|
||||
@ -434,6 +450,48 @@ directory_get_from_all_authorities(uint8_t dir_purpose,
|
||||
});
|
||||
}
|
||||
|
||||
/** Same as directory_initiate_command_routerstatus(), but accepts
|
||||
* rendezvous data to fetch a hidden service descriptor. */
|
||||
void
|
||||
directory_initiate_command_routerstatus_rend(routerstatus_t *status,
|
||||
uint8_t dir_purpose,
|
||||
uint8_t router_purpose,
|
||||
int anonymized_connection,
|
||||
const char *resource,
|
||||
const char *payload,
|
||||
size_t payload_len,
|
||||
time_t if_modified_since,
|
||||
const rend_data_t *rend_query)
|
||||
{
|
||||
routerinfo_t *router;
|
||||
char address_buf[INET_NTOA_BUF_LEN+1];
|
||||
struct in_addr in;
|
||||
const char *address;
|
||||
tor_addr_t addr;
|
||||
router = router_get_by_digest(status->identity_digest);
|
||||
if (!router && anonymized_connection) {
|
||||
log_info(LD_DIR, "Not sending anonymized request to directory '%s'; we "
|
||||
"don't have its router descriptor.", status->nickname);
|
||||
return;
|
||||
} else if (router) {
|
||||
address = router->address;
|
||||
} else {
|
||||
in.s_addr = htonl(status->addr);
|
||||
tor_inet_ntoa(&in, address_buf, sizeof(address_buf));
|
||||
address = address_buf;
|
||||
}
|
||||
tor_addr_from_ipv4h(&addr, status->addr);
|
||||
directory_initiate_command_rend(address, &addr,
|
||||
status->or_port, status->dir_port,
|
||||
status->version_supports_conditional_consensus,
|
||||
status->version_supports_begindir,
|
||||
status->identity_digest,
|
||||
dir_purpose, router_purpose,
|
||||
anonymized_connection, resource,
|
||||
payload, payload_len, if_modified_since,
|
||||
rend_query);
|
||||
}
|
||||
|
||||
/** Launch a new connection to the directory server <b>status</b> to
|
||||
* upload or download a server or rendezvous
|
||||
* descriptor. <b>dir_purpose</b> determines what
|
||||
@ -458,32 +516,11 @@ directory_initiate_command_routerstatus(routerstatus_t *status,
|
||||
size_t payload_len,
|
||||
time_t if_modified_since)
|
||||
{
|
||||
routerinfo_t *router;
|
||||
char address_buf[INET_NTOA_BUF_LEN+1];
|
||||
struct in_addr in;
|
||||
const char *address;
|
||||
tor_addr_t addr;
|
||||
router = router_get_by_digest(status->identity_digest);
|
||||
if (!router && anonymized_connection) {
|
||||
log_info(LD_DIR, "Not sending anonymized request to directory '%s'; we "
|
||||
"don't have its router descriptor.", status->nickname);
|
||||
return;
|
||||
} else if (router) {
|
||||
address = router->address;
|
||||
} else {
|
||||
in.s_addr = htonl(status->addr);
|
||||
tor_inet_ntoa(&in, address_buf, sizeof(address_buf));
|
||||
address = address_buf;
|
||||
}
|
||||
tor_addr_from_ipv4h(&addr, status->addr);
|
||||
directory_initiate_command(address, &addr,
|
||||
status->or_port, status->dir_port,
|
||||
status->version_supports_conditional_consensus,
|
||||
status->version_supports_begindir,
|
||||
status->identity_digest,
|
||||
dir_purpose, router_purpose,
|
||||
anonymized_connection, resource,
|
||||
payload, payload_len, if_modified_since);
|
||||
directory_initiate_command_routerstatus_rend(status, dir_purpose,
|
||||
router_purpose,
|
||||
anonymized_connection, resource,
|
||||
payload, payload_len,
|
||||
if_modified_since, NULL);
|
||||
}
|
||||
|
||||
/** Return true iff <b>conn</b> is the client side of a directory connection
|
||||
@ -667,6 +704,28 @@ directory_initiate_command(const char *address, const tor_addr_t *_addr,
|
||||
int anonymized_connection, const char *resource,
|
||||
const char *payload, size_t payload_len,
|
||||
time_t if_modified_since)
|
||||
{
|
||||
directory_initiate_command_rend(address, _addr, or_port, dir_port,
|
||||
supports_conditional_consensus,
|
||||
supports_begindir, digest, dir_purpose,
|
||||
router_purpose, anonymized_connection,
|
||||
resource, payload, payload_len,
|
||||
if_modified_since, NULL);
|
||||
}
|
||||
|
||||
/** Same as directory_initiate_command(), but accepts rendezvous data to
|
||||
* fetch a hidden service descriptor. */
|
||||
static void
|
||||
directory_initiate_command_rend(const char *address, const tor_addr_t *_addr,
|
||||
uint16_t or_port, uint16_t dir_port,
|
||||
int supports_conditional_consensus,
|
||||
int supports_begindir, const char *digest,
|
||||
uint8_t dir_purpose, uint8_t router_purpose,
|
||||
int anonymized_connection,
|
||||
const char *resource,
|
||||
const char *payload, size_t payload_len,
|
||||
time_t if_modified_since,
|
||||
const rend_data_t *rend_query)
|
||||
{
|
||||
dir_connection_t *conn;
|
||||
or_options_t *options = get_options();
|
||||
@ -705,6 +764,10 @@ directory_initiate_command(const char *address, const tor_addr_t *_addr,
|
||||
/* decide whether we can learn our IP address from this conn */
|
||||
conn->dirconn_direct = !anonymized_connection;
|
||||
|
||||
/* copy rendezvous data, if any */
|
||||
if (rend_query)
|
||||
conn->rend_data = rend_data_dup(rend_query);
|
||||
|
||||
if (!anonymized_connection && !use_begindir) {
|
||||
/* then we want to connect to dirport directly */
|
||||
|
||||
@ -1005,8 +1068,10 @@ directory_send_command(dir_connection_t *conn,
|
||||
/* this must be true or we wouldn't be doing the lookup */
|
||||
tor_assert(strlen(resource) <= REND_SERVICE_ID_LEN_BASE32);
|
||||
/* This breaks the function abstraction. */
|
||||
strlcpy(conn->rend_query, resource, sizeof(conn->rend_query));
|
||||
conn->rend_version = 0;
|
||||
conn->rend_data = tor_malloc_zero(sizeof(rend_data_t));
|
||||
strlcpy(conn->rend_data->onion_address, resource,
|
||||
sizeof(conn->rend_data->onion_address));
|
||||
conn->rend_data->rend_desc_version = 0;
|
||||
|
||||
httpcommand = "GET";
|
||||
/* Request the most recent versioned descriptor. */
|
||||
@ -1019,10 +1084,8 @@ directory_send_command(dir_connection_t *conn,
|
||||
case DIR_PURPOSE_FETCH_RENDDESC_V2:
|
||||
tor_assert(resource);
|
||||
tor_assert(strlen(resource) <= REND_DESC_ID_V2_LEN_BASE32);
|
||||
/* Remember the query to refer to it when a response arrives. */
|
||||
strlcpy(conn->rend_query, payload, sizeof(conn->rend_query));
|
||||
conn->rend_version = 2;
|
||||
payload = NULL;
|
||||
tor_assert(!payload);
|
||||
conn->rend_data->rend_desc_version = 2;
|
||||
httpcommand = "GET";
|
||||
len = strlen(resource) + 32;
|
||||
url = tor_malloc(len);
|
||||
@ -1877,6 +1940,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
|
||||
}
|
||||
|
||||
if (conn->_base.purpose == DIR_PURPOSE_FETCH_RENDDESC) {
|
||||
tor_assert(conn->rend_data);
|
||||
log_info(LD_REND,"Received rendezvous descriptor (size %d, status %d "
|
||||
"(%s))",
|
||||
(int)body_len, status_code, escaped(reason));
|
||||
@ -1892,7 +1956,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
|
||||
} else {
|
||||
/* success. notify pending connections about this. */
|
||||
conn->_base.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC;
|
||||
rend_client_desc_trynow(conn->rend_query, -1);
|
||||
rend_client_desc_trynow(conn->rend_data->onion_address, -1);
|
||||
}
|
||||
break;
|
||||
case 404:
|
||||
@ -1914,12 +1978,13 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
|
||||
}
|
||||
|
||||
if (conn->_base.purpose == DIR_PURPOSE_FETCH_RENDDESC_V2) {
|
||||
tor_assert(conn->rend_data);
|
||||
log_info(LD_REND,"Received rendezvous descriptor (size %d, status %d "
|
||||
"(%s))",
|
||||
(int)body_len, status_code, escaped(reason));
|
||||
switch (status_code) {
|
||||
case 200:
|
||||
switch (rend_cache_store_v2_desc_as_client(body, NULL)) {
|
||||
switch (rend_cache_store_v2_desc_as_client(body, conn->rend_data)) {
|
||||
case -2:
|
||||
log_warn(LD_REND,"Fetching v2 rendezvous descriptor failed. "
|
||||
"Retrying at another directory.");
|
||||
@ -1938,7 +2003,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
|
||||
log_info(LD_REND, "Successfully fetched v2 rendezvous "
|
||||
"descriptor.");
|
||||
conn->_base.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC;
|
||||
rend_client_desc_trynow(conn->rend_query, -1);
|
||||
rend_client_desc_trynow(conn->rend_data->onion_address, -1);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
121
src/or/or.h
121
src/or/or.h
@ -676,6 +676,55 @@ typedef enum {
|
||||
/** Maximum length of authorized client names for a hidden service. */
|
||||
#define REND_CLIENTNAME_MAX_LEN 16
|
||||
|
||||
/** Length of the rendezvous cookie that is used to connect circuits at the
|
||||
* rendezvous point. */
|
||||
#define REND_COOKIE_LEN DIGEST_LEN
|
||||
|
||||
/** Client authorization type that a hidden service performs. */
|
||||
typedef enum rend_auth_type_t {
|
||||
REND_NO_AUTH = 0,
|
||||
REND_BASIC_AUTH = 1,
|
||||
REND_STEALTH_AUTH = 2,
|
||||
} rend_auth_type_t;
|
||||
|
||||
/** Client-side configuration of authorization for a hidden service. */
|
||||
typedef struct rend_service_authorization_t {
|
||||
char descriptor_cookie[REND_DESC_COOKIE_LEN];
|
||||
char onion_address[REND_SERVICE_ADDRESS_LEN+1];
|
||||
rend_auth_type_t auth_type;
|
||||
} rend_service_authorization_t;
|
||||
|
||||
/** Client- and server-side data that is used for hidden service connection
|
||||
* establishment. Not all fields contain data depending on where this struct
|
||||
* is used. */
|
||||
typedef struct rend_data_t {
|
||||
/** Onion address (without the .onion part) that a client requests. */
|
||||
char onion_address[REND_SERVICE_ID_LEN_BASE32+1];
|
||||
|
||||
/** (Optional) descriptor cookie that is used by a client. */
|
||||
char descriptor_cookie[REND_DESC_COOKIE_LEN];
|
||||
|
||||
/** Authorization type for accessing a service used by a client. */
|
||||
rend_auth_type_t auth_type;
|
||||
|
||||
/** Hash of the hidden service's PK used by a service. */
|
||||
char rend_pk_digest[DIGEST_LEN];
|
||||
|
||||
/** Rendezvous cookie used by both, client and service. */
|
||||
char rend_cookie[REND_COOKIE_LEN];
|
||||
|
||||
/** Rendezvous descriptor version that is used by a service. Used to
|
||||
* distinguish introduction and rendezvous points belonging to the same
|
||||
* rendezvous service ID, but different descriptor versions.
|
||||
*/
|
||||
uint8_t rend_desc_version;
|
||||
} rend_data_t;
|
||||
|
||||
/** Time interval for tracking possible replays of INTRODUCE2 cells.
|
||||
* Incoming cells with timestamps half of this interval in the past or
|
||||
* future are dropped immediately. */
|
||||
#define REND_REPLAY_TIME_INTERVAL (60 * 60)
|
||||
|
||||
#define CELL_DIRECTION_IN 1
|
||||
#define CELL_DIRECTION_OUT 2
|
||||
|
||||
@ -1025,7 +1074,7 @@ typedef struct edge_connection_t {
|
||||
uint32_t n_written;
|
||||
|
||||
/** What rendezvous service are we querying for? (AP only) */
|
||||
char rend_query[REND_SERVICE_ID_LEN_BASE32+1];
|
||||
rend_data_t *rend_data;
|
||||
|
||||
/** Number of times we've reassigned this application connection to
|
||||
* a new circuit. We keep track because the timeout is longer if we've
|
||||
@ -1078,11 +1127,8 @@ typedef struct dir_connection_t {
|
||||
/** The zlib object doing on-the-fly compression for spooled data. */
|
||||
tor_zlib_state_t *zlib_state;
|
||||
|
||||
/** What hidden service descriptor are we fetching, if any? */
|
||||
int rend_version;
|
||||
|
||||
/** What rendezvous service are we querying for? */
|
||||
char rend_query[REND_SERVICE_ID_LEN_BASE32+1];
|
||||
rend_data_t *rend_data;
|
||||
|
||||
char identity_digest[DIGEST_LEN]; /**< Hash of the public RSA key for
|
||||
* the directory server's signing key. */
|
||||
@ -1747,7 +1793,6 @@ typedef struct crypt_path_t {
|
||||
CIPHER_KEY_LEN+\
|
||||
DH_KEY_LEN)
|
||||
#define ONIONSKIN_REPLY_LEN (DH_KEY_LEN+DIGEST_LEN)
|
||||
#define REND_COOKIE_LEN DIGEST_LEN
|
||||
|
||||
/** Information used to build a circuit. */
|
||||
typedef struct {
|
||||
@ -1883,28 +1928,8 @@ typedef struct origin_circuit_t {
|
||||
*/
|
||||
crypt_path_t *cpath;
|
||||
|
||||
/** The rend_pk_digest field holds a hash of location-hidden service's
|
||||
* PK if purpose is S_ESTABLISH_INTRO or S_RENDEZVOUSING.
|
||||
*/
|
||||
char rend_pk_digest[DIGEST_LEN];
|
||||
|
||||
/** Holds rendezvous cookie if purpose is C_ESTABLISH_REND. Filled with
|
||||
* zeroes otherwise.
|
||||
*/
|
||||
char rend_cookie[REND_COOKIE_LEN];
|
||||
|
||||
/**
|
||||
* The rend_query field holds the y portion of y.onion (nul-terminated)
|
||||
* if purpose is C_INTRODUCING or C_ESTABLISH_REND, or is a C_GENERAL
|
||||
* for a hidden service, or is S_*.
|
||||
*/
|
||||
char rend_query[REND_SERVICE_ID_LEN_BASE32+1];
|
||||
|
||||
/** Stores the rendezvous descriptor version if purpose is S_*. Used to
|
||||
* distinguish introduction and rendezvous points belonging to the same
|
||||
* rendezvous service ID, but different descriptor versions.
|
||||
*/
|
||||
uint8_t rend_desc_version;
|
||||
/** Holds all rendezvous data on either client or service side. */
|
||||
rend_data_t *rend_data;
|
||||
|
||||
/** How many more relay_early cells can we send on this circuit, according
|
||||
* to the specification? */
|
||||
@ -3179,6 +3204,15 @@ void directory_initiate_command_routerstatus(routerstatus_t *status,
|
||||
const char *payload,
|
||||
size_t payload_len,
|
||||
time_t if_modified_since);
|
||||
void directory_initiate_command_routerstatus_rend(routerstatus_t *status,
|
||||
uint8_t dir_purpose,
|
||||
uint8_t router_purpose,
|
||||
int anonymized_connection,
|
||||
const char *resource,
|
||||
const char *payload,
|
||||
size_t payload_len,
|
||||
time_t if_modified_since,
|
||||
const rend_data_t *rend_query);
|
||||
|
||||
int parse_http_response(const char *headers, int *code, time_t *date,
|
||||
compress_method_t *compression, char **response);
|
||||
@ -3835,39 +3869,25 @@ void rend_client_rendcirc_has_opened(origin_circuit_t *circ);
|
||||
int rend_client_introduction_acked(origin_circuit_t *circ, const char *request,
|
||||
size_t request_len);
|
||||
void rend_client_refetch_renddesc(const char *query);
|
||||
void rend_client_refetch_v2_renddesc(const char *query);
|
||||
void rend_client_refetch_v2_renddesc(const rend_data_t *rend_query);
|
||||
int rend_client_remove_intro_point(extend_info_t *failed_intro,
|
||||
const char *query);
|
||||
const rend_data_t *rend_query);
|
||||
int rend_client_rendezvous_acked(origin_circuit_t *circ, const char *request,
|
||||
size_t request_len);
|
||||
int rend_client_receive_rendezvous(origin_circuit_t *circ, const char *request,
|
||||
size_t request_len);
|
||||
void rend_client_desc_trynow(const char *query, int rend_version);
|
||||
|
||||
extend_info_t *rend_client_get_random_intro(const char *query);
|
||||
extend_info_t *rend_client_get_random_intro(const rend_data_t *rend_query);
|
||||
|
||||
int rend_client_send_introduction(origin_circuit_t *introcirc,
|
||||
origin_circuit_t *rendcirc);
|
||||
|
||||
/** Client authorization type that a hidden service performs. */
|
||||
typedef enum rend_auth_type_t {
|
||||
REND_NO_AUTH = 0,
|
||||
REND_BASIC_AUTH = 1,
|
||||
REND_STEALTH_AUTH = 2,
|
||||
} rend_auth_type_t;
|
||||
|
||||
/** Client-side configuration of authorization for a hidden service. */
|
||||
typedef struct rend_service_authorization_t {
|
||||
char descriptor_cookie[REND_DESC_COOKIE_LEN];
|
||||
char onion_address[REND_SERVICE_ADDRESS_LEN+1];
|
||||
rend_auth_type_t auth_type;
|
||||
} rend_service_authorization_t;
|
||||
|
||||
int rend_parse_service_authorization(or_options_t *options,
|
||||
int validate_only);
|
||||
rend_service_authorization_t *rend_client_lookup_service_authorization(
|
||||
const char *onion_address);
|
||||
void rend_service_authorization_free_all(void);
|
||||
rend_data_t *rend_data_dup(const rend_data_t *request);
|
||||
|
||||
/********************************* rendcommon.c ***************************/
|
||||
|
||||
@ -3910,6 +3930,13 @@ typedef struct rend_service_descriptor_t {
|
||||
smartlist_t *successful_uploads;
|
||||
} rend_service_descriptor_t;
|
||||
|
||||
/** Free all storage associated with <b>data</b> */
|
||||
static INLINE void
|
||||
rend_data_free(rend_data_t *data)
|
||||
{
|
||||
tor_free(data);
|
||||
}
|
||||
|
||||
int rend_cmp_service_ids(const char *one, const char *two);
|
||||
|
||||
void rend_process_relay_cell(circuit_t *circ, int command, size_t length,
|
||||
@ -3947,7 +3974,7 @@ int rend_cache_lookup_entry(const char *query, int version,
|
||||
int rend_cache_lookup_v2_desc_as_dir(const char *query, const char **desc);
|
||||
int rend_cache_store(const char *desc, size_t desc_len, int published);
|
||||
int rend_cache_store_v2_desc_as_client(const char *desc,
|
||||
const char *descriptor_cookie);
|
||||
const rend_data_t *rend_query);
|
||||
int rend_cache_store_v2_desc_as_dir(const char *desc);
|
||||
int rend_cache_size(void);
|
||||
int rend_encode_v2_descriptors(smartlist_t *descs_out,
|
||||
|
@ -31,16 +31,18 @@ static int
|
||||
rend_client_send_establish_rendezvous(origin_circuit_t *circ)
|
||||
{
|
||||
tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND);
|
||||
tor_assert(circ->rend_data);
|
||||
log_info(LD_REND, "Sending an ESTABLISH_RENDEZVOUS cell");
|
||||
|
||||
if (crypto_rand(circ->rend_cookie, REND_COOKIE_LEN) < 0) {
|
||||
if (crypto_rand(circ->rend_data->rend_cookie, REND_COOKIE_LEN) < 0) {
|
||||
log_warn(LD_BUG, "Internal error: Couldn't produce random cookie.");
|
||||
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
|
||||
return -1;
|
||||
}
|
||||
if (relay_send_command_from_edge(0, TO_CIRCUIT(circ),
|
||||
RELAY_COMMAND_ESTABLISH_RENDEZVOUS,
|
||||
circ->rend_cookie, REND_COOKIE_LEN,
|
||||
circ->rend_data->rend_cookie,
|
||||
REND_COOKIE_LEN,
|
||||
circ->cpath->prev)<0) {
|
||||
/* circ is already marked for close */
|
||||
log_warn(LD_GENERAL, "Couldn't send ESTABLISH_RENDEZVOUS cell");
|
||||
@ -58,7 +60,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
|
||||
origin_circuit_t *rendcirc)
|
||||
{
|
||||
size_t payload_len;
|
||||
int r;
|
||||
int r, v3_shift = 0;
|
||||
char payload[RELAY_PAYLOAD_SIZE];
|
||||
char tmp[RELAY_PAYLOAD_SIZE];
|
||||
rend_cache_entry_t *entry;
|
||||
@ -68,13 +70,16 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
|
||||
|
||||
tor_assert(introcirc->_base.purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
|
||||
tor_assert(rendcirc->_base.purpose == CIRCUIT_PURPOSE_C_REND_READY);
|
||||
tor_assert(!rend_cmp_service_ids(introcirc->rend_query,
|
||||
rendcirc->rend_query));
|
||||
tor_assert(introcirc->rend_data);
|
||||
tor_assert(rendcirc->rend_data);
|
||||
tor_assert(!rend_cmp_service_ids(introcirc->rend_data->onion_address,
|
||||
rendcirc->rend_data->onion_address));
|
||||
|
||||
if (rend_cache_lookup_entry(introcirc->rend_query, -1, &entry) < 1) {
|
||||
if (rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1,
|
||||
&entry) < 1) {
|
||||
log_warn(LD_REND,
|
||||
"query %s didn't have valid rend desc in cache. Failing.",
|
||||
escaped_safe_str(introcirc->rend_query));
|
||||
escaped_safe_str(introcirc->rend_data->onion_address));
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -117,27 +122,45 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
|
||||
}
|
||||
}
|
||||
|
||||
/* If version is 3, write (optional) auth data and timestamp. */
|
||||
if (entry->parsed->protocols & (1<<3)) {
|
||||
tmp[0] = 3; /* version 3 of the cell format */
|
||||
tmp[1] = (uint8_t)introcirc->rend_data->auth_type; /* auth type, if any */
|
||||
v3_shift = 1;
|
||||
if (introcirc->rend_data->auth_type != REND_NO_AUTH) {
|
||||
set_uint16(tmp+2, htons(REND_DESC_COOKIE_LEN));
|
||||
memcpy(tmp+4, introcirc->rend_data->descriptor_cookie,
|
||||
REND_DESC_COOKIE_LEN);
|
||||
v3_shift += 2+REND_DESC_COOKIE_LEN;
|
||||
}
|
||||
set_uint32(tmp+v3_shift+1, htonl(time(NULL)));
|
||||
v3_shift += 4;
|
||||
} /* if version 2 only write version number */
|
||||
else if (entry->parsed->protocols & (1<<2)) {
|
||||
tmp[0] = 2; /* version 2 of the cell format */
|
||||
}
|
||||
|
||||
/* write the remaining items into tmp */
|
||||
if (entry->parsed->protocols & (1<<2)) {
|
||||
if (entry->parsed->protocols & (1<<3) || entry->parsed->protocols & (1<<2)) {
|
||||
/* version 2 format */
|
||||
extend_info_t *extend_info = rendcirc->build_state->chosen_exit;
|
||||
int klen;
|
||||
tmp[0] = 2; /* version 2 of the cell format */
|
||||
/* nul pads */
|
||||
set_uint32(tmp+1, tor_addr_to_ipv4h(&extend_info->addr));
|
||||
set_uint16(tmp+5, htons(extend_info->port));
|
||||
memcpy(tmp+7, extend_info->identity_digest, DIGEST_LEN);
|
||||
klen = crypto_pk_asn1_encode(extend_info->onion_key, tmp+7+DIGEST_LEN+2,
|
||||
sizeof(tmp)-(7+DIGEST_LEN+2));
|
||||
set_uint16(tmp+7+DIGEST_LEN, htons(klen));
|
||||
memcpy(tmp+7+DIGEST_LEN+2+klen, rendcirc->rend_cookie,
|
||||
set_uint32(tmp+v3_shift+1, tor_addr_to_ipv4h(&extend_info->addr));
|
||||
set_uint16(tmp+v3_shift+5, htons(extend_info->port));
|
||||
memcpy(tmp+v3_shift+7, extend_info->identity_digest, DIGEST_LEN);
|
||||
klen = crypto_pk_asn1_encode(extend_info->onion_key,
|
||||
tmp+v3_shift+7+DIGEST_LEN+2,
|
||||
sizeof(tmp)-(v3_shift+7+DIGEST_LEN+2));
|
||||
set_uint16(tmp+v3_shift+7+DIGEST_LEN, htons(klen));
|
||||
memcpy(tmp+v3_shift+7+DIGEST_LEN+2+klen, rendcirc->rend_data->rend_cookie,
|
||||
REND_COOKIE_LEN);
|
||||
dh_offset = 7+DIGEST_LEN+2+klen+REND_COOKIE_LEN;
|
||||
dh_offset = v3_shift+7+DIGEST_LEN+2+klen+REND_COOKIE_LEN;
|
||||
} else {
|
||||
/* Version 0. */
|
||||
strncpy(tmp, rendcirc->build_state->chosen_exit->nickname,
|
||||
(MAX_NICKNAME_LEN+1)); /* nul pads */
|
||||
memcpy(tmp+MAX_NICKNAME_LEN+1, rendcirc->rend_cookie,
|
||||
memcpy(tmp+MAX_NICKNAME_LEN+1, rendcirc->rend_data->rend_cookie,
|
||||
REND_COOKIE_LEN);
|
||||
dh_offset = MAX_NICKNAME_LEN+1+REND_COOKIE_LEN;
|
||||
}
|
||||
@ -216,6 +239,7 @@ rend_client_introduction_acked(origin_circuit_t *circ,
|
||||
}
|
||||
|
||||
tor_assert(circ->build_state->chosen_exit);
|
||||
tor_assert(circ->rend_data);
|
||||
|
||||
if (request_len == 0) {
|
||||
/* It's an ACK; the introduction point relayed our introduction request. */
|
||||
@ -224,7 +248,7 @@ rend_client_introduction_acked(origin_circuit_t *circ,
|
||||
*/
|
||||
log_info(LD_REND,"Received ack. Telling rend circ...");
|
||||
rendcirc = circuit_get_by_rend_query_and_purpose(
|
||||
circ->rend_query, CIRCUIT_PURPOSE_C_REND_READY);
|
||||
circ->rend_data->onion_address, CIRCUIT_PURPOSE_C_REND_READY);
|
||||
if (rendcirc) { /* remember the ack */
|
||||
rendcirc->_base.purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED;
|
||||
} else {
|
||||
@ -241,22 +265,22 @@ rend_client_introduction_acked(origin_circuit_t *circ,
|
||||
* If none remain, refetch the service descriptor.
|
||||
*/
|
||||
if (rend_client_remove_intro_point(circ->build_state->chosen_exit,
|
||||
circ->rend_query) > 0) {
|
||||
circ->rend_data) > 0) {
|
||||
/* There are introduction points left. Re-extend the circuit to
|
||||
* another intro point and try again. */
|
||||
extend_info_t *extend_info;
|
||||
int result;
|
||||
extend_info = rend_client_get_random_intro(circ->rend_query);
|
||||
extend_info = rend_client_get_random_intro(circ->rend_data);
|
||||
if (!extend_info) {
|
||||
log_warn(LD_REND, "No introduction points left for %s. Closing.",
|
||||
escaped_safe_str(circ->rend_query));
|
||||
escaped_safe_str(circ->rend_data->onion_address));
|
||||
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
|
||||
return -1;
|
||||
}
|
||||
log_info(LD_REND,
|
||||
"Got nack for %s from %s. Re-extending circ %d, "
|
||||
"this time to %s.",
|
||||
escaped_safe_str(circ->rend_query),
|
||||
escaped_safe_str(circ->rend_data->onion_address),
|
||||
circ->build_state->chosen_exit->nickname, circ->_base.n_circ_id,
|
||||
extend_info->nickname);
|
||||
result = circuit_extend_to_new_exit(circ, extend_info);
|
||||
@ -337,15 +361,15 @@ directory_clean_last_hid_serv_requests(void)
|
||||
* descriptor, return 0, and in case of a failure -1. <b>query</b> is only
|
||||
* passed for pretty log statements. */
|
||||
static int
|
||||
directory_get_from_hs_dir(const char *desc_id, const char *query)
|
||||
directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
|
||||
{
|
||||
smartlist_t *responsible_dirs = smartlist_create();
|
||||
routerstatus_t *hs_dir;
|
||||
char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
|
||||
time_t now = time(NULL);
|
||||
char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64];
|
||||
tor_assert(desc_id);
|
||||
tor_assert(query);
|
||||
tor_assert(strlen(query) == REND_SERVICE_ID_LEN_BASE32);
|
||||
tor_assert(rend_query);
|
||||
/* Determine responsible dirs. Even if we can't get all we want,
|
||||
* work with the ones we have. If it's empty, we'll notice below. */
|
||||
(int) hid_serv_get_responsible_directories(responsible_dirs, desc_id);
|
||||
@ -377,17 +401,33 @@ directory_get_from_hs_dir(const char *desc_id, const char *query)
|
||||
* directory now. */
|
||||
lookup_last_hid_serv_request(hs_dir, desc_id_base32, now, 1);
|
||||
|
||||
/* Send fetch request. (Pass query as payload to write it to the directory
|
||||
* connection so that it can be referred to when the response arrives.) */
|
||||
directory_initiate_command_routerstatus(hs_dir,
|
||||
/* Encode descriptor cookie for logging purposes. */
|
||||
if (rend_query->auth_type != REND_NO_AUTH &&
|
||||
base64_encode(descriptor_cookie_base64, 3*REND_DESC_COOKIE_LEN_BASE64,
|
||||
rend_query->descriptor_cookie, REND_DESC_COOKIE_LEN) < 0) {
|
||||
log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
|
||||
return 0;
|
||||
}
|
||||
/* Remove == signs and newline. */
|
||||
descriptor_cookie_base64[strlen(descriptor_cookie_base64)-3] = '\0';
|
||||
|
||||
/* Send fetch request. (Pass query and possibly descriptor cookie so that
|
||||
* they can be written to the directory connection and be referred to when
|
||||
* the response arrives. */
|
||||
directory_initiate_command_routerstatus_rend(hs_dir,
|
||||
DIR_PURPOSE_FETCH_RENDDESC_V2,
|
||||
ROUTER_PURPOSE_GENERAL,
|
||||
1, desc_id_base32, query, 0, 0);
|
||||
1, desc_id_base32, NULL, 0, 0,
|
||||
rend_query);
|
||||
log_info(LD_REND, "Sending fetch request for v2 descriptor for "
|
||||
"service '%s' with descriptor ID '%s' to hidden "
|
||||
"service directory '%s' on port %d.",
|
||||
safe_str(query), safe_str(desc_id_base32), hs_dir->nickname,
|
||||
hs_dir->dir_port);
|
||||
"service '%s' with descriptor ID '%s', auth type %d, "
|
||||
"and descriptor cookie '%s' to hidden service "
|
||||
"directory '%s' on port %d.",
|
||||
rend_query->onion_address, desc_id_base32,
|
||||
rend_query->auth_type,
|
||||
(rend_query->auth_type == REND_NO_AUTH ? "NULL" :
|
||||
escaped_safe_str(descriptor_cookie_base64)),
|
||||
hs_dir->nickname, hs_dir->dir_port);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -417,14 +457,13 @@ rend_client_refetch_renddesc(const char *query)
|
||||
* <b>query</b>.
|
||||
*/
|
||||
void
|
||||
rend_client_refetch_v2_renddesc(const char *query)
|
||||
rend_client_refetch_v2_renddesc(const rend_data_t *rend_query)
|
||||
{
|
||||
char descriptor_id[DIGEST_LEN];
|
||||
int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS];
|
||||
int i, tries_left;
|
||||
rend_cache_entry_t *e = NULL;
|
||||
tor_assert(query);
|
||||
tor_assert(strlen(query) == REND_SERVICE_ID_LEN_BASE32);
|
||||
tor_assert(rend_query);
|
||||
/* Are we configured to fetch descriptors? */
|
||||
if (!get_options()->FetchHidServDescriptors) {
|
||||
log_warn(LD_REND, "We received an onion address for a v2 rendezvous "
|
||||
@ -432,13 +471,13 @@ rend_client_refetch_v2_renddesc(const char *query)
|
||||
return;
|
||||
}
|
||||
/* Before fetching, check if we already have the descriptor here. */
|
||||
if (rend_cache_lookup_entry(query, -1, &e) > 0) {
|
||||
if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) > 0) {
|
||||
log_info(LD_REND, "We would fetch a v2 rendezvous descriptor, but we "
|
||||
"already have that descriptor here. Not fetching.");
|
||||
return;
|
||||
}
|
||||
log_debug(LD_REND, "Fetching v2 rendezvous descriptor for service %s",
|
||||
safe_str(query));
|
||||
safe_str(rend_query->onion_address));
|
||||
/* Randomly iterate over the replicas until a descriptor can be fetched
|
||||
* from one of the consecutive nodes, or no options are left. */
|
||||
tries_left = REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS;
|
||||
@ -449,13 +488,15 @@ rend_client_refetch_v2_renddesc(const char *query)
|
||||
int chosen_replica = replicas_left_to_try[rand];
|
||||
replicas_left_to_try[rand] = replicas_left_to_try[--tries_left];
|
||||
|
||||
if (rend_compute_v2_desc_id(descriptor_id, query, NULL, time(NULL),
|
||||
chosen_replica) < 0) {
|
||||
if (rend_compute_v2_desc_id(descriptor_id, rend_query->onion_address,
|
||||
rend_query->auth_type == REND_STEALTH_AUTH ?
|
||||
rend_query->descriptor_cookie : NULL,
|
||||
time(NULL), chosen_replica) < 0) {
|
||||
log_warn(LD_REND, "Internal error: Computing v2 rendezvous "
|
||||
"descriptor ID did not succeed.");
|
||||
return;
|
||||
}
|
||||
if (directory_get_from_hs_dir(descriptor_id, query) != 0)
|
||||
if (directory_get_from_hs_dir(descriptor_id, rend_query) != 0)
|
||||
return; /* either success or failure, but we're done */
|
||||
}
|
||||
/* If we come here, there are no hidden service directories left. */
|
||||
@ -463,7 +504,7 @@ rend_client_refetch_v2_renddesc(const char *query)
|
||||
"service directories to fetch descriptors, because "
|
||||
"we already tried them all unsuccessfully.");
|
||||
/* Close pending connections (unless a v0 request is still going on). */
|
||||
rend_client_desc_trynow(query, 2);
|
||||
rend_client_desc_trynow(rend_query->onion_address, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -474,24 +515,28 @@ rend_client_refetch_v2_renddesc(const char *query)
|
||||
* unrecognized, 1 if recognized and some intro points remain.
|
||||
*/
|
||||
int
|
||||
rend_client_remove_intro_point(extend_info_t *failed_intro, const char *query)
|
||||
rend_client_remove_intro_point(extend_info_t *failed_intro,
|
||||
const rend_data_t *rend_query)
|
||||
{
|
||||
int i, r;
|
||||
rend_cache_entry_t *ent;
|
||||
connection_t *conn;
|
||||
|
||||
r = rend_cache_lookup_entry(query, -1, &ent);
|
||||
r = rend_cache_lookup_entry(rend_query->onion_address, -1, &ent);
|
||||
if (r<0) {
|
||||
log_warn(LD_BUG, "Malformed service ID %s.", escaped_safe_str(query));
|
||||
log_warn(LD_BUG, "Malformed service ID %s.",
|
||||
escaped_safe_str(rend_query->onion_address));
|
||||
return -1;
|
||||
}
|
||||
if (r==0) {
|
||||
log_info(LD_REND, "Unknown service %s. Re-fetching descriptor.",
|
||||
escaped_safe_str(query));
|
||||
escaped_safe_str(rend_query->onion_address));
|
||||
/* Fetch both, v0 and v2 rend descriptors in parallel. Use whichever
|
||||
* arrives first. */
|
||||
rend_client_refetch_v2_renddesc(query);
|
||||
rend_client_refetch_renddesc(query);
|
||||
* arrives first. Exception: When using client authorization, only
|
||||
* fetch v2 descriptors.*/
|
||||
rend_client_refetch_v2_renddesc(rend_query);
|
||||
if (rend_query->auth_type == REND_NO_AUTH)
|
||||
rend_client_refetch_renddesc(rend_query->onion_address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -508,22 +553,26 @@ rend_client_remove_intro_point(extend_info_t *failed_intro, const char *query)
|
||||
if (smartlist_len(ent->parsed->intro_nodes) == 0) {
|
||||
log_info(LD_REND,
|
||||
"No more intro points remain for %s. Re-fetching descriptor.",
|
||||
escaped_safe_str(query));
|
||||
escaped_safe_str(rend_query->onion_address));
|
||||
/* Fetch both, v0 and v2 rend descriptors in parallel. Use whichever
|
||||
* arrives first. */
|
||||
rend_client_refetch_v2_renddesc(query);
|
||||
rend_client_refetch_renddesc(query);
|
||||
* arrives first. Exception: When using client authorization, only
|
||||
* fetch v2 descriptors.*/
|
||||
rend_client_refetch_v2_renddesc(rend_query);
|
||||
if (rend_query->auth_type == REND_NO_AUTH)
|
||||
rend_client_refetch_renddesc(rend_query->onion_address);
|
||||
|
||||
/* move all pending streams back to renddesc_wait */
|
||||
while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP,
|
||||
AP_CONN_STATE_CIRCUIT_WAIT, query, -1))) {
|
||||
AP_CONN_STATE_CIRCUIT_WAIT,
|
||||
rend_query->onion_address, -1))) {
|
||||
conn->state = AP_CONN_STATE_RENDDESC_WAIT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
log_info(LD_REND,"%d options left for %s.",
|
||||
smartlist_len(ent->parsed->intro_nodes), escaped_safe_str(query));
|
||||
smartlist_len(ent->parsed->intro_nodes),
|
||||
escaped_safe_str(rend_query->onion_address));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -648,10 +697,13 @@ rend_client_desc_trynow(const char *query, int rend_version)
|
||||
_conn->marked_for_close)
|
||||
continue;
|
||||
conn = TO_EDGE_CONN(_conn);
|
||||
if (rend_cmp_service_ids(query, conn->rend_query))
|
||||
if (!conn->rend_data)
|
||||
continue;
|
||||
if (rend_cmp_service_ids(query, conn->rend_data->onion_address))
|
||||
continue;
|
||||
assert_connection_ok(TO_CONN(conn), now);
|
||||
if (rend_cache_lookup_entry(conn->rend_query, -1, &entry) == 1 &&
|
||||
if (rend_cache_lookup_entry(conn->rend_data->onion_address, -1,
|
||||
&entry) == 1 &&
|
||||
smartlist_len(entry->parsed->intro_nodes) > 0) {
|
||||
/* either this fetch worked, or it failed but there was a
|
||||
* valid entry from before which we should reuse */
|
||||
@ -689,17 +741,17 @@ rend_client_desc_trynow(const char *query, int rend_version)
|
||||
* have been tried and failed.
|
||||
*/
|
||||
extend_info_t *
|
||||
rend_client_get_random_intro(const char *query)
|
||||
rend_client_get_random_intro(const rend_data_t *rend_query)
|
||||
{
|
||||
int i;
|
||||
rend_cache_entry_t *entry;
|
||||
rend_intro_point_t *intro;
|
||||
routerinfo_t *router;
|
||||
|
||||
if (rend_cache_lookup_entry(query, -1, &entry) < 1) {
|
||||
if (rend_cache_lookup_entry(rend_query->onion_address, -1, &entry) < 1) {
|
||||
log_warn(LD_REND,
|
||||
"Query '%s' didn't have valid rend desc in cache. Failing.",
|
||||
safe_str(query));
|
||||
safe_str(rend_query->onion_address));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1243,7 +1243,7 @@ rend_cache_store_v2_desc_as_dir(const char *desc)
|
||||
*/
|
||||
int
|
||||
rend_cache_store_v2_desc_as_client(const char *desc,
|
||||
const char *descriptor_cookie)
|
||||
const rend_data_t *rend_query)
|
||||
{
|
||||
/*XXXX this seems to have a bit of duplicate code with
|
||||
* rend_cache_store_v2_desc_as_dir(). Fix that. */
|
||||
@ -1272,7 +1272,6 @@ rend_cache_store_v2_desc_as_client(const char *desc,
|
||||
rend_cache_entry_t *e;
|
||||
tor_assert(rend_cache);
|
||||
tor_assert(desc);
|
||||
(void) descriptor_cookie; /* We don't use it, yet. */
|
||||
/* Parse the descriptor. */
|
||||
if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
|
||||
&intro_size, &encoded_size,
|
||||
@ -1291,14 +1290,37 @@ rend_cache_store_v2_desc_as_client(const char *desc,
|
||||
}
|
||||
/* Decode/decrypt introduction points. */
|
||||
if (intro_content) {
|
||||
if (rend_query->auth_type != REND_NO_AUTH &&
|
||||
rend_query->descriptor_cookie) {
|
||||
char *ipos_decrypted;
|
||||
size_t ipos_decrypted_size;
|
||||
if (rend_decrypt_introduction_points(&ipos_decrypted,
|
||||
&ipos_decrypted_size,
|
||||
rend_query->descriptor_cookie,
|
||||
intro_content,
|
||||
intro_size) < 0) {
|
||||
log_warn(LD_REND, "Failed to decrypt introduction points. We are "
|
||||
"probably unable to parse the encoded introduction points.");
|
||||
} else {
|
||||
/* Replace encrypted with decrypted introduction points. */
|
||||
log_info(LD_REND, "Successfully decrypted introduction points.");
|
||||
tor_free(intro_content);
|
||||
intro_content = ipos_decrypted;
|
||||
intro_size = ipos_decrypted_size;
|
||||
}
|
||||
}
|
||||
if (rend_parse_introduction_points(parsed, intro_content,
|
||||
intro_size) < 0) {
|
||||
log_warn(LD_PROTOCOL,"Couldn't decode/decrypt introduction points.");
|
||||
rend_service_descriptor_free(parsed);
|
||||
intro_size) <= 0) {
|
||||
log_warn(LD_REND, "Failed to parse introduction points. Either the "
|
||||
"service has published a corrupt descriptor or you have "
|
||||
"provided invalid authorization data.");
|
||||
if (parsed)
|
||||
rend_service_descriptor_free(parsed);
|
||||
tor_free(intro_content);
|
||||
return -2;
|
||||
}
|
||||
} else {
|
||||
log_info(LD_REND, "Descriptor does not contain any introduction points.");
|
||||
parsed->intro_nodes = smartlist_create();
|
||||
}
|
||||
/* We don't need the encoded/encrypted introduction points any longer. */
|
||||
@ -1426,3 +1448,12 @@ rend_cache_size(void)
|
||||
return strmap_size(rend_cache);
|
||||
}
|
||||
|
||||
/** Allocate and return a new rend_data_t with the same
|
||||
* contents as <b>query</b>. */
|
||||
rend_data_t *
|
||||
rend_data_dup(const rend_data_t *data)
|
||||
{
|
||||
tor_assert(data);
|
||||
return tor_memdup(data, sizeof(rend_data_t));
|
||||
}
|
||||
|
||||
|
@ -69,6 +69,11 @@ typedef struct rend_service_t {
|
||||
* up-to-date. */
|
||||
time_t next_upload_time; /**< Scheduled next hidden service descriptor
|
||||
* upload time. */
|
||||
/** Map from digests of diffie-hellman values INTRODUCE2 to time_t of when
|
||||
* 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;
|
||||
} rend_service_t;
|
||||
|
||||
/** A list of rend_service_t's for services run on this OP.
|
||||
@ -125,6 +130,8 @@ rend_service_free(rend_service_t *service)
|
||||
rend_authorized_client_free(c););
|
||||
smartlist_free(service->clients);
|
||||
}
|
||||
if (service->accepted_intros)
|
||||
digestmap_free(service->accepted_intros, _tor_free);
|
||||
tor_free(service);
|
||||
}
|
||||
|
||||
@ -360,7 +367,7 @@ rend_config_services(or_options_t *options, int validate_only)
|
||||
if (smartlist_len(type_names_split) < 2) {
|
||||
log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
|
||||
"auth-type '%s', but no client names.",
|
||||
service->auth_type == 1 ? "basic" : "stealth");
|
||||
service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth");
|
||||
SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
|
||||
smartlist_free(type_names_split);
|
||||
continue;
|
||||
@ -423,7 +430,7 @@ rend_config_services(or_options_t *options, int validate_only)
|
||||
"authorization type '%s'.",
|
||||
smartlist_len(service->clients),
|
||||
service->auth_type == REND_BASIC_AUTH ? 512 : 16,
|
||||
service->auth_type == 1 ? "basic" : "stealth");
|
||||
service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth");
|
||||
rend_service_free(service);
|
||||
return -1;
|
||||
}
|
||||
@ -720,8 +727,10 @@ rend_service_load_keys(void)
|
||||
tor_free(client_keys_str);
|
||||
strmap_free(parsed_clients, rend_authorized_client_strmap_item_free);
|
||||
if (r<0) {
|
||||
abort_writing_to_file(open_cfile);
|
||||
abort_writing_to_file(open_hfile);
|
||||
if (open_cfile)
|
||||
abort_writing_to_file(open_cfile);
|
||||
if (open_hfile)
|
||||
abort_writing_to_file(open_hfile);
|
||||
return r;
|
||||
} else {
|
||||
finish_writing_to_file(open_cfile);
|
||||
@ -764,6 +773,64 @@ rend_service_requires_uptime(rend_service_t *service)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** 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, {
|
||||
if (!memcmp(client->descriptor_cookie, descriptor_cookie,
|
||||
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;
|
||||
}
|
||||
|
||||
/******
|
||||
* Handle cells
|
||||
******/
|
||||
@ -780,7 +847,7 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
|
||||
char buf[RELAY_PAYLOAD_SIZE];
|
||||
char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */
|
||||
rend_service_t *service;
|
||||
int r, i;
|
||||
int r, i, v3_shift = 0;
|
||||
size_t len, keylen;
|
||||
crypto_dh_env_t *dh = NULL;
|
||||
origin_circuit_t *launched = NULL;
|
||||
@ -791,9 +858,17 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
|
||||
int reason = END_CIRC_REASON_TORPROTOCOL;
|
||||
crypto_pk_env_t *intro_key;
|
||||
char intro_key_digest[DIGEST_LEN];
|
||||
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;
|
||||
tor_assert(circuit->rend_data);
|
||||
|
||||
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
|
||||
circuit->rend_pk_digest, REND_SERVICE_ID_LEN);
|
||||
circuit->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN);
|
||||
log_info(LD_REND, "Received INTRODUCE2 cell for service %s on circ %d.",
|
||||
escaped(serviceid), circuit->_base.n_circ_id);
|
||||
|
||||
@ -814,7 +889,8 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
|
||||
|
||||
/* look up service depending on circuit. */
|
||||
service = rend_service_get_by_pk_digest_and_version(
|
||||
circuit->rend_pk_digest, circuit->rend_desc_version);
|
||||
circuit->rend_data->rend_pk_digest,
|
||||
circuit->rend_data->rend_desc_version);
|
||||
if (!service) {
|
||||
log_warn(LD_REND, "Got an INTRODUCE2 cell for an unrecognized service %s.",
|
||||
escaped(serviceid));
|
||||
@ -822,7 +898,7 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
|
||||
}
|
||||
|
||||
/* if descriptor version is 2, use intro key instead of service key. */
|
||||
if (circuit->rend_desc_version == 0) {
|
||||
if (circuit->rend_data->rend_desc_version == 0) {
|
||||
intro_key = service->private_key;
|
||||
} else {
|
||||
intro_key = circuit->intro_key;
|
||||
@ -854,33 +930,70 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
|
||||
return -1;
|
||||
}
|
||||
len = r;
|
||||
if (*buf == 2) {
|
||||
if (*buf == 3) {
|
||||
/* Version 3 INTRODUCE2 cell. */
|
||||
time_t ts = 0, now = time(NULL);
|
||||
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. */
|
||||
memcpy((char*)&ts, buf+1+v3_shift, sizeof(uint32_t));
|
||||
v3_shift += 4;
|
||||
ts = ntohl(ts);
|
||||
if ((now - ts) < -1 * REND_REPLAY_TIME_INTERVAL / 2 ||
|
||||
(now - ts) > REND_REPLAY_TIME_INTERVAL / 2) {
|
||||
log_warn(LD_REND, "INTRODUCE2 cell is too %s. Discarding.",
|
||||
(now - ts) < 0 ? "old" : "new");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (*buf == 2 || *buf == 3) {
|
||||
/* Version 2 INTRODUCE2 cell. */
|
||||
int klen;
|
||||
extend_info = tor_malloc_zero(sizeof(extend_info_t));
|
||||
tor_addr_from_ipv4n(&extend_info->addr, get_uint32(buf+1));
|
||||
extend_info->port = ntohs(get_uint16(buf+5));
|
||||
memcpy(extend_info->identity_digest, buf+7, DIGEST_LEN);
|
||||
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);
|
||||
extend_info->nickname[0] = '$';
|
||||
base16_encode(extend_info->nickname+1, sizeof(extend_info->nickname)-1,
|
||||
extend_info->identity_digest, DIGEST_LEN);
|
||||
|
||||
klen = ntohs(get_uint16(buf+7+DIGEST_LEN));
|
||||
if ((int)len != 7+DIGEST_LEN+2+klen+20+128) {
|
||||
log_warn(LD_PROTOCOL, "Bad length %u for version 2 INTRODUCE2 cell.",
|
||||
(int)len);
|
||||
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);
|
||||
reason = END_CIRC_REASON_TORPROTOCOL;
|
||||
goto err;
|
||||
}
|
||||
extend_info->onion_key = crypto_pk_asn1_decode(buf+7+DIGEST_LEN+2, klen);
|
||||
extend_info->onion_key =
|
||||
crypto_pk_asn1_decode(buf+v3_shift+7+DIGEST_LEN+2, klen);
|
||||
if (!extend_info->onion_key) {
|
||||
log_warn(LD_PROTOCOL,
|
||||
"Error decoding onion key in version 2 INTRODUCE2 cell.");
|
||||
log_warn(LD_PROTOCOL, "Error decoding onion key in version %d "
|
||||
"INTRODUCE2 cell.", *buf);
|
||||
reason = END_CIRC_REASON_TORPROTOCOL;
|
||||
goto err;
|
||||
}
|
||||
ptr = buf+7+DIGEST_LEN+2+klen;
|
||||
len -= 7+DIGEST_LEN+2+klen;
|
||||
ptr = buf+v3_shift+7+DIGEST_LEN+2+klen;
|
||||
len -= v3_shift+7+DIGEST_LEN+2+klen;
|
||||
} else {
|
||||
char *rp_nickname;
|
||||
size_t nickname_field_len;
|
||||
@ -932,6 +1045,54 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
|
||||
r_cookie = ptr;
|
||||
base16_encode(hexcookie,9,r_cookie,4);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Iterate over past requests, remove those which are older than one hour,
|
||||
* and check whether there is one with same Diffie-Hellman, part 1. */
|
||||
if (!service->accepted_intros)
|
||||
service->accepted_intros = digestmap_new();
|
||||
|
||||
access_time = digestmap_get(service->accepted_intros, diffie_hellman_hash);
|
||||
if (access_time != NULL) {
|
||||
log_warn(LD_REND, "Possible replay detected! We received an "
|
||||
"INTRODUCE2 cell with same first part of "
|
||||
"Diffie-Hellman handshake %d seconds ago. Dropping "
|
||||
"cell.",
|
||||
(int) (now - *access_time));
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Add request to access history, including time and hash of
|
||||
* Diffie-Hellman, part 1. */
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try DH handshake... */
|
||||
dh = crypto_dh_new();
|
||||
if (!dh || crypto_dh_generate_public(dh)<0) {
|
||||
@ -976,12 +1137,14 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
|
||||
escaped_safe_str(extend_info->nickname), hexcookie, serviceid);
|
||||
tor_assert(launched->build_state);
|
||||
/* Fill in the circuit's state. */
|
||||
memcpy(launched->rend_pk_digest, circuit->rend_pk_digest,
|
||||
launched->rend_data = tor_malloc_zero(sizeof(rend_data_t));
|
||||
memcpy(launched->rend_data->rend_pk_digest,
|
||||
circuit->rend_data->rend_pk_digest,
|
||||
DIGEST_LEN);
|
||||
memcpy(launched->rend_cookie, r_cookie, REND_COOKIE_LEN);
|
||||
strlcpy(launched->rend_query, service->service_id,
|
||||
sizeof(launched->rend_query));
|
||||
launched->rend_desc_version = service->descriptor_version;
|
||||
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));
|
||||
launched->rend_data->rend_desc_version = service->descriptor_version;
|
||||
launched->build_state->pending_final_cpath = cpath =
|
||||
tor_malloc_zero(sizeof(crypt_path_t));
|
||||
cpath->magic = CRYPT_PATH_MAGIC;
|
||||
@ -1053,13 +1216,7 @@ rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc)
|
||||
newstate->pending_final_cpath = oldstate->pending_final_cpath;
|
||||
oldstate->pending_final_cpath = NULL;
|
||||
|
||||
memcpy(newcirc->rend_query, oldcirc->rend_query,
|
||||
REND_SERVICE_ID_LEN_BASE32+1);
|
||||
memcpy(newcirc->rend_pk_digest, oldcirc->rend_pk_digest,
|
||||
DIGEST_LEN);
|
||||
memcpy(newcirc->rend_cookie, oldcirc->rend_cookie,
|
||||
REND_COOKIE_LEN);
|
||||
newcirc->rend_desc_version = oldcirc->rend_desc_version;
|
||||
newcirc->rend_data = rend_data_dup(oldcirc->rend_data);
|
||||
}
|
||||
|
||||
/** Launch a circuit to serve as an introduction point for the service
|
||||
@ -1105,10 +1262,11 @@ rend_service_launch_establish_intro(rend_service_t *service,
|
||||
intro->extend_info = extend_info_dup(launched->build_state->chosen_exit);
|
||||
}
|
||||
|
||||
strlcpy(launched->rend_query, service->service_id,
|
||||
sizeof(launched->rend_query));
|
||||
memcpy(launched->rend_pk_digest, service->pk_digest, DIGEST_LEN);
|
||||
launched->rend_desc_version = service->descriptor_version;
|
||||
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);
|
||||
launched->rend_data->rend_desc_version = service->descriptor_version;
|
||||
if (service->descriptor_version == 2)
|
||||
launched->intro_key = crypto_pk_dup_key(intro->intro_key);
|
||||
if (launched->_base.state == CIRCUIT_STATE_OPEN)
|
||||
@ -1133,12 +1291,14 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
|
||||
|
||||
tor_assert(circuit->_base.purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
|
||||
tor_assert(circuit->cpath);
|
||||
tor_assert(circuit->rend_data);
|
||||
|
||||
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
|
||||
circuit->rend_pk_digest, REND_SERVICE_ID_LEN);
|
||||
circuit->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN);
|
||||
|
||||
service = rend_service_get_by_pk_digest_and_version(
|
||||
circuit->rend_pk_digest, circuit->rend_desc_version);
|
||||
circuit->rend_data->rend_pk_digest,
|
||||
circuit->rend_data->rend_desc_version);
|
||||
if (!service) {
|
||||
log_warn(LD_REND, "Unrecognized service ID %s on introduction circuit %d.",
|
||||
serviceid, circuit->_base.n_circ_id);
|
||||
@ -1214,8 +1374,10 @@ rend_service_intro_established(origin_circuit_t *circuit, const char *request,
|
||||
"received INTRO_ESTABLISHED cell on non-intro circuit.");
|
||||
goto err;
|
||||
}
|
||||
tor_assert(circuit->rend_data);
|
||||
service = rend_service_get_by_pk_digest_and_version(
|
||||
circuit->rend_pk_digest, circuit->rend_desc_version);
|
||||
circuit->rend_data->rend_pk_digest,
|
||||
circuit->rend_data->rend_desc_version);
|
||||
if (!service) {
|
||||
log_warn(LD_REND, "Unknown service on introduction circuit %d.",
|
||||
circuit->_base.n_circ_id);
|
||||
@ -1225,7 +1387,7 @@ rend_service_intro_established(origin_circuit_t *circuit, const char *request,
|
||||
circuit->_base.purpose = CIRCUIT_PURPOSE_S_INTRO;
|
||||
|
||||
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32 + 1,
|
||||
circuit->rend_pk_digest, REND_SERVICE_ID_LEN);
|
||||
circuit->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN);
|
||||
log_info(LD_REND,
|
||||
"Received INTRO_ESTABLISHED cell on circuit %d for service %s",
|
||||
circuit->_base.n_circ_id, serviceid);
|
||||
@ -1252,12 +1414,13 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
|
||||
tor_assert(circuit->_base.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
|
||||
tor_assert(circuit->cpath);
|
||||
tor_assert(circuit->build_state);
|
||||
tor_assert(circuit->rend_data);
|
||||
hop = circuit->build_state->pending_final_cpath;
|
||||
tor_assert(hop);
|
||||
|
||||
base16_encode(hexcookie,9,circuit->rend_cookie,4);
|
||||
base16_encode(hexcookie,9,circuit->rend_data->rend_cookie,4);
|
||||
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
|
||||
circuit->rend_pk_digest, REND_SERVICE_ID_LEN);
|
||||
circuit->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN);
|
||||
|
||||
log_info(LD_REND,
|
||||
"Done building circuit %d to rendezvous with "
|
||||
@ -1265,7 +1428,8 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
|
||||
circuit->_base.n_circ_id, hexcookie, serviceid);
|
||||
|
||||
service = rend_service_get_by_pk_digest_and_version(
|
||||
circuit->rend_pk_digest, circuit->rend_desc_version);
|
||||
circuit->rend_data->rend_pk_digest,
|
||||
circuit->rend_data->rend_desc_version);
|
||||
if (!service) {
|
||||
log_warn(LD_GENERAL, "Internal error: unrecognized service ID on "
|
||||
"introduction circuit.");
|
||||
@ -1274,7 +1438,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
|
||||
}
|
||||
|
||||
/* All we need to do is send a RELAY_RENDEZVOUS1 cell... */
|
||||
memcpy(buf, circuit->rend_cookie, REND_COOKIE_LEN);
|
||||
memcpy(buf, circuit->rend_data->rend_cookie, REND_COOKIE_LEN);
|
||||
if (crypto_dh_get_public(hop->dh_handshake_state,
|
||||
buf+REND_COOKIE_LEN, DH_KEY_LEN)<0) {
|
||||
log_warn(LD_GENERAL,"Couldn't get DH public key.");
|
||||
@ -1336,7 +1500,8 @@ find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest,
|
||||
CIRCUIT_PURPOSE_S_INTRO))) {
|
||||
if (!memcmp(circ->build_state->chosen_exit->identity_digest,
|
||||
intro->extend_info->identity_digest, DIGEST_LEN) &&
|
||||
circ->rend_desc_version == desc_version) {
|
||||
circ->rend_data &&
|
||||
circ->rend_data->rend_desc_version == desc_version) {
|
||||
return circ;
|
||||
}
|
||||
}
|
||||
@ -1346,7 +1511,8 @@ find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest,
|
||||
CIRCUIT_PURPOSE_S_ESTABLISH_INTRO))) {
|
||||
if (!memcmp(circ->build_state->chosen_exit->identity_digest,
|
||||
intro->extend_info->identity_digest, DIGEST_LEN) &&
|
||||
circ->rend_desc_version == desc_version) {
|
||||
circ->rend_data &&
|
||||
circ->rend_data->rend_desc_version == desc_version) {
|
||||
return circ;
|
||||
}
|
||||
}
|
||||
@ -1827,11 +1993,13 @@ rend_service_set_connection_addr_port(edge_connection_t *conn,
|
||||
rend_service_port_config_t *chosen_port;
|
||||
|
||||
tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
|
||||
tor_assert(circ->rend_data);
|
||||
log_debug(LD_REND,"beginning to hunt for addr/port");
|
||||
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
|
||||
circ->rend_pk_digest, REND_SERVICE_ID_LEN);
|
||||
service = rend_service_get_by_pk_digest_and_version(circ->rend_pk_digest,
|
||||
circ->rend_desc_version);
|
||||
circ->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN);
|
||||
service = rend_service_get_by_pk_digest_and_version(
|
||||
circ->rend_data->rend_pk_digest,
|
||||
circ->rend_data->rend_desc_version);
|
||||
if (!service) {
|
||||
log_warn(LD_REND, "Couldn't find any service associated with pk %s on "
|
||||
"rendezvous circuit %d; closing.",
|
||||
|
Loading…
Reference in New Issue
Block a user