Merge branch 'ticket17238_029_02-resquash'

Conflicts:
	src/or/rendclient.c
	src/or/rendcommon.c
	src/or/routerparse.c
	src/test/test_dir.c
	src/trunnel/ed25519_cert.h
This commit is contained in:
Nick Mathewson 2016-11-04 13:26:37 -04:00
commit c35c43d7d9
44 changed files with 7167 additions and 1181 deletions

View File

@ -64,6 +64,7 @@
#include "connection_or.h" #include "connection_or.h"
#include "control.h" #include "control.h"
#include "main.h" #include "main.h"
#include "hs_common.h"
#include "networkstatus.h" #include "networkstatus.h"
#include "nodelist.h" #include "nodelist.h"
#include "onion.h" #include "onion.h"
@ -1352,9 +1353,11 @@ circuit_get_ready_rend_circ_by_rend_data(const rend_data_t *rend_data)
if (!circ->marked_for_close && if (!circ->marked_for_close &&
circ->purpose == CIRCUIT_PURPOSE_C_REND_READY) { circ->purpose == CIRCUIT_PURPOSE_C_REND_READY) {
origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
if (ocirc->rend_data && if (ocirc->rend_data == NULL) {
!rend_cmp_service_ids(rend_data->onion_address, continue;
ocirc->rend_data->onion_address) && }
if (!rend_cmp_service_ids(rend_data_get_address(rend_data),
rend_data_get_address(ocirc->rend_data)) &&
tor_memeq(ocirc->rend_data->rend_cookie, tor_memeq(ocirc->rend_data->rend_cookie,
rend_data->rend_cookie, rend_data->rend_cookie,
REND_COOKIE_LEN)) REND_COOKIE_LEN))
@ -1366,13 +1369,14 @@ circuit_get_ready_rend_circ_by_rend_data(const rend_data_t *rend_data)
} }
/** Return the first circuit originating here in global_circuitlist after /** Return the first circuit originating here in global_circuitlist after
* <b>start</b> whose purpose is <b>purpose</b>, and where * <b>start</b> whose purpose is <b>purpose</b>, and where <b>digest</b> (if
* <b>digest</b> (if set) matches the rend_pk_digest field. Return NULL if no * set) matches the private key digest of the rend data associated with the
* circuit is found. If <b>start</b> is NULL, begin at the start of the list. * circuit. Return NULL if no circuit is found. If <b>start</b> is NULL,
* begin at the start of the list.
*/ */
origin_circuit_t * origin_circuit_t *
circuit_get_next_by_pk_and_purpose(origin_circuit_t *start, circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
const char *digest, uint8_t purpose) const uint8_t *digest, uint8_t purpose)
{ {
int idx; int idx;
smartlist_t *lst = circuit_get_global_list(); smartlist_t *lst = circuit_get_global_list();
@ -1384,17 +1388,23 @@ circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
for ( ; idx < smartlist_len(lst); ++idx) { for ( ; idx < smartlist_len(lst); ++idx) {
circuit_t *circ = smartlist_get(lst, idx); circuit_t *circ = smartlist_get(lst, idx);
origin_circuit_t *ocirc;
if (circ->marked_for_close) if (circ->marked_for_close)
continue; continue;
if (circ->purpose != purpose) if (circ->purpose != purpose)
continue; continue;
/* At this point we should be able to get a valid origin circuit because
* the origin purpose we are looking for matches this circuit. */
if (BUG(!CIRCUIT_PURPOSE_IS_ORIGIN(circ->purpose))) {
break;
}
ocirc = TO_ORIGIN_CIRCUIT(circ);
if (!digest) if (!digest)
return TO_ORIGIN_CIRCUIT(circ); return ocirc;
else if (TO_ORIGIN_CIRCUIT(circ)->rend_data && if (rend_circuit_pk_digest_eq(ocirc, digest)) {
tor_memeq(TO_ORIGIN_CIRCUIT(circ)->rend_data->rend_pk_digest, return ocirc;
digest, DIGEST_LEN)) }
return TO_ORIGIN_CIRCUIT(circ);
} }
return NULL; return NULL;
} }
@ -1882,7 +1892,7 @@ circuit_about_to_free(circuit_t *circ)
if (orig_reason != END_CIRC_REASON_IP_NOW_REDUNDANT) { if (orig_reason != END_CIRC_REASON_IP_NOW_REDUNDANT) {
/* treat this like getting a nack from it */ /* treat this like getting a nack from it */
log_info(LD_REND, "Failed intro circ %s to %s (awaiting ack). %s", log_info(LD_REND, "Failed intro circ %s to %s (awaiting ack). %s",
safe_str_client(ocirc->rend_data->onion_address), safe_str_client(rend_data_get_address(ocirc->rend_data)),
safe_str_client(build_state_get_exit_nickname(ocirc->build_state)), safe_str_client(build_state_get_exit_nickname(ocirc->build_state)),
timed_out ? "Recording timeout." : "Removing from descriptor."); timed_out ? "Recording timeout." : "Removing from descriptor.");
rend_client_report_intro_point_failure(ocirc->build_state->chosen_exit, rend_client_report_intro_point_failure(ocirc->build_state->chosen_exit,
@ -1899,7 +1909,7 @@ circuit_about_to_free(circuit_t *circ)
log_info(LD_REND, "Failed intro circ %s to %s " log_info(LD_REND, "Failed intro circ %s to %s "
"(building circuit to intro point). " "(building circuit to intro point). "
"Marking intro point as possibly unreachable.", "Marking intro point as possibly unreachable.",
safe_str_client(ocirc->rend_data->onion_address), safe_str_client(rend_data_get_address(ocirc->rend_data)),
safe_str_client(build_state_get_exit_nickname( safe_str_client(build_state_get_exit_nickname(
ocirc->build_state))); ocirc->build_state)));
rend_client_report_intro_point_failure(ocirc->build_state->chosen_exit, rend_client_report_intro_point_failure(ocirc->build_state->chosen_exit,

View File

@ -45,7 +45,7 @@ origin_circuit_t *circuit_get_by_global_id(uint32_t id);
origin_circuit_t *circuit_get_ready_rend_circ_by_rend_data( origin_circuit_t *circuit_get_ready_rend_circ_by_rend_data(
const rend_data_t *rend_data); const rend_data_t *rend_data);
origin_circuit_t *circuit_get_next_by_pk_and_purpose(origin_circuit_t *start, origin_circuit_t *circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
const char *digest, uint8_t purpose); const uint8_t *digest, uint8_t purpose);
or_circuit_t *circuit_get_rendezvous(const uint8_t *cookie); or_circuit_t *circuit_get_rendezvous(const uint8_t *cookie);
or_circuit_t *circuit_get_intro_point(const uint8_t *digest); or_circuit_t *circuit_get_intro_point(const uint8_t *digest);
void circuit_set_rendezvous_cookie(or_circuit_t *circ, const uint8_t *cookie); void circuit_set_rendezvous_cookie(or_circuit_t *circ, const uint8_t *cookie);

View File

@ -40,6 +40,7 @@
#include "connection_edge.h" #include "connection_edge.h"
#include "control.h" #include "control.h"
#include "entrynodes.h" #include "entrynodes.h"
#include "hs_common.h"
#include "nodelist.h" #include "nodelist.h"
#include "networkstatus.h" #include "networkstatus.h"
#include "policies.h" #include "policies.h"
@ -172,8 +173,8 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ,
if ((edge_conn->rend_data && !origin_circ->rend_data) || if ((edge_conn->rend_data && !origin_circ->rend_data) ||
(!edge_conn->rend_data && origin_circ->rend_data) || (!edge_conn->rend_data && origin_circ->rend_data) ||
(edge_conn->rend_data && origin_circ->rend_data && (edge_conn->rend_data && origin_circ->rend_data &&
rend_cmp_service_ids(edge_conn->rend_data->onion_address, rend_cmp_service_ids(rend_data_get_address(edge_conn->rend_data),
origin_circ->rend_data->onion_address))) { rend_data_get_address(origin_circ->rend_data)))) {
/* this circ is not for this conn */ /* this circ is not for this conn */
return 0; return 0;
} }
@ -2036,7 +2037,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
if (!extend_info) { if (!extend_info) {
log_info(LD_REND, log_info(LD_REND,
"No intro points for '%s': re-fetching service descriptor.", "No intro points for '%s': re-fetching service descriptor.",
safe_str_client(rend_data->onion_address)); safe_str_client(rend_data_get_address(rend_data)));
rend_client_refetch_v2_renddesc(rend_data); rend_client_refetch_v2_renddesc(rend_data);
connection_ap_mark_as_non_pending_circuit(conn); connection_ap_mark_as_non_pending_circuit(conn);
ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_RENDDESC_WAIT; ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_RENDDESC_WAIT;
@ -2044,7 +2045,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
} }
log_info(LD_REND,"Chose %s as intro point for '%s'.", log_info(LD_REND,"Chose %s as intro point for '%s'.",
extend_info_describe(extend_info), extend_info_describe(extend_info),
safe_str_client(rend_data->onion_address)); safe_str_client(rend_data_get_address(rend_data)));
} }
/* If we have specified a particular exit node for our /* If we have specified a particular exit node for our

View File

@ -82,6 +82,7 @@
#include "ext_orport.h" #include "ext_orport.h"
#include "geoip.h" #include "geoip.h"
#include "main.h" #include "main.h"
#include "hs_common.h"
#include "nodelist.h" #include "nodelist.h"
#include "policies.h" #include "policies.h"
#include "reasons.h" #include "reasons.h"
@ -4126,12 +4127,12 @@ connection_get_by_type_state_rendquery(int type, int state,
(type == CONN_TYPE_DIR && (type == CONN_TYPE_DIR &&
TO_DIR_CONN(conn)->rend_data && TO_DIR_CONN(conn)->rend_data &&
!rend_cmp_service_ids(rendquery, !rend_cmp_service_ids(rendquery,
TO_DIR_CONN(conn)->rend_data->onion_address)) rend_data_get_address(TO_DIR_CONN(conn)->rend_data)))
|| ||
(CONN_IS_EDGE(conn) && (CONN_IS_EDGE(conn) &&
TO_EDGE_CONN(conn)->rend_data && TO_EDGE_CONN(conn)->rend_data &&
!rend_cmp_service_ids(rendquery, !rend_cmp_service_ids(rendquery,
TO_EDGE_CONN(conn)->rend_data->onion_address)) rend_data_get_address(TO_EDGE_CONN(conn)->rend_data)))
)); ));
} }

View File

@ -75,6 +75,7 @@
#include "directory.h" #include "directory.h"
#include "dirserv.h" #include "dirserv.h"
#include "hibernate.h" #include "hibernate.h"
#include "hs_common.h"
#include "main.h" #include "main.h"
#include "nodelist.h" #include "nodelist.h"
#include "policies.h" #include "policies.h"
@ -1861,21 +1862,22 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
if (rend_data == NULL) { if (rend_data == NULL) {
return -1; return -1;
} }
const char *onion_address = rend_data_get_address(rend_data);
log_info(LD_REND,"Got a hidden service request for ID '%s'", log_info(LD_REND,"Got a hidden service request for ID '%s'",
safe_str_client(rend_data->onion_address)); safe_str_client(onion_address));
/* Lookup the given onion address. If invalid, stop right now. /* Lookup the given onion address. If invalid, stop right now.
* Otherwise, we might have it in the cache or not. */ * Otherwise, we might have it in the cache or not. */
unsigned int refetch_desc = 0; unsigned int refetch_desc = 0;
rend_cache_entry_t *entry = NULL; rend_cache_entry_t *entry = NULL;
const int rend_cache_lookup_result = const int rend_cache_lookup_result =
rend_cache_lookup_entry(rend_data->onion_address, -1, &entry); rend_cache_lookup_entry(onion_address, -1, &entry);
if (rend_cache_lookup_result < 0) { if (rend_cache_lookup_result < 0) {
switch (-rend_cache_lookup_result) { switch (-rend_cache_lookup_result) {
case EINVAL: case EINVAL:
/* We should already have rejected this address! */ /* We should already have rejected this address! */
log_warn(LD_BUG,"Invalid service name '%s'", log_warn(LD_BUG,"Invalid service name '%s'",
safe_str_client(rend_data->onion_address)); safe_str_client(onion_address));
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return -1; return -1;
case ENOENT: case ENOENT:
@ -1901,7 +1903,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
connection_ap_mark_as_non_pending_circuit(conn); connection_ap_mark_as_non_pending_circuit(conn);
base_conn->state = AP_CONN_STATE_RENDDESC_WAIT; base_conn->state = AP_CONN_STATE_RENDDESC_WAIT;
log_info(LD_REND, "Unknown descriptor %s. Fetching.", log_info(LD_REND, "Unknown descriptor %s. Fetching.",
safe_str_client(rend_data->onion_address)); safe_str_client(onion_address));
rend_client_refetch_v2_renddesc(rend_data); rend_client_refetch_v2_renddesc(rend_data);
return 0; return 0;
} }

View File

@ -57,6 +57,7 @@
#include "entrynodes.h" #include "entrynodes.h"
#include "geoip.h" #include "geoip.h"
#include "hibernate.h" #include "hibernate.h"
#include "hs_common.h"
#include "main.h" #include "main.h"
#include "networkstatus.h" #include "networkstatus.h"
#include "nodelist.h" #include "nodelist.h"
@ -2539,7 +2540,7 @@ circuit_describe_status_for_controller(origin_circuit_t *circ)
if (circ->rend_data != NULL) { if (circ->rend_data != NULL) {
smartlist_add_asprintf(descparts, "REND_QUERY=%s", smartlist_add_asprintf(descparts, "REND_QUERY=%s",
circ->rend_data->onion_address); rend_data_get_address(circ->rend_data));
} }
{ {
@ -6856,8 +6857,10 @@ control_event_hs_descriptor_requested(const rend_data_t *rend_query,
send_control_event(EVENT_HS_DESC, send_control_event(EVENT_HS_DESC,
"650 HS_DESC REQUESTED %s %s %s %s\r\n", "650 HS_DESC REQUESTED %s %s %s %s\r\n",
rend_hsaddress_str_or_unknown(rend_query->onion_address), rend_hsaddress_str_or_unknown(
rend_auth_type_to_string(rend_query->auth_type), rend_data_get_address(rend_query)),
rend_auth_type_to_string(
TO_REND_DATA_V2(rend_query)->auth_type),
node_describe_longname_by_id(id_digest), node_describe_longname_by_id(id_digest),
desc_id_base32); desc_id_base32);
} }
@ -6873,11 +6876,12 @@ get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp)
{ {
int replica; int replica;
const char *desc_id = NULL; const char *desc_id = NULL;
const rend_data_v2_t *rend_data_v2 = TO_REND_DATA_V2(rend_data);
/* Possible if the fetch was done using a descriptor ID. This means that /* Possible if the fetch was done using a descriptor ID. This means that
* the HSFETCH command was used. */ * the HSFETCH command was used. */
if (!tor_digest_is_zero(rend_data->desc_id_fetch)) { if (!tor_digest_is_zero(rend_data_v2->desc_id_fetch)) {
desc_id = rend_data->desc_id_fetch; desc_id = rend_data_v2->desc_id_fetch;
goto end; goto end;
} }
@ -6885,7 +6889,7 @@ get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp)
* is the one associated with the HSDir fingerprint. */ * is the one associated with the HSDir fingerprint. */
for (replica = 0; replica < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; for (replica = 0; replica < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS;
replica++) { replica++) {
const char *digest = rend_data->descriptor_id[replica]; const char *digest = rend_data_get_desc_id(rend_data, replica, NULL);
SMARTLIST_FOREACH_BEGIN(rend_data->hsdirs_fp, char *, fingerprint) { SMARTLIST_FOREACH_BEGIN(rend_data->hsdirs_fp, char *, fingerprint) {
if (tor_memcmp(fingerprint, hsdir_fp, DIGEST_LEN) == 0) { if (tor_memcmp(fingerprint, hsdir_fp, DIGEST_LEN) == 0) {
@ -6994,7 +6998,8 @@ control_event_hs_descriptor_receive_end(const char *action,
"650 HS_DESC %s %s %s %s%s%s\r\n", "650 HS_DESC %s %s %s %s%s%s\r\n",
action, action,
rend_hsaddress_str_or_unknown(onion_address), rend_hsaddress_str_or_unknown(onion_address),
rend_auth_type_to_string(rend_data->auth_type), rend_auth_type_to_string(
TO_REND_DATA_V2(rend_data)->auth_type),
node_describe_longname_by_id(id_digest), node_describe_longname_by_id(id_digest),
desc_id_field ? desc_id_field : "", desc_id_field ? desc_id_field : "",
reason_field ? reason_field : ""); reason_field ? reason_field : "");
@ -7091,7 +7096,7 @@ control_event_hs_descriptor_failed(const rend_data_t *rend_data,
return; return;
} }
control_event_hs_descriptor_receive_end("FAILED", control_event_hs_descriptor_receive_end("FAILED",
rend_data->onion_address, rend_data_get_address(rend_data),
rend_data, id_digest, reason); rend_data, id_digest, reason);
} }

View File

@ -3,6 +3,8 @@
* Copyright (c) 2007-2016, The Tor Project, Inc. */ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */ /* See LICENSE for licensing information */
#define DIRECTORY_PRIVATE
#include "or.h" #include "or.h"
#include "backtrace.h" #include "backtrace.h"
#include "buffers.h" #include "buffers.h"
@ -16,6 +18,8 @@
#include "dirvote.h" #include "dirvote.h"
#include "entrynodes.h" #include "entrynodes.h"
#include "geoip.h" #include "geoip.h"
#include "hs_cache.h"
#include "hs_common.h"
#include "main.h" #include "main.h"
#include "microdesc.h" #include "microdesc.h"
#include "networkstatus.h" #include "networkstatus.h"
@ -2385,7 +2389,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
conn->identity_digest, \ conn->identity_digest, \
reason) ) reason) )
#define SEND_HS_DESC_FAILED_CONTENT() ( \ #define SEND_HS_DESC_FAILED_CONTENT() ( \
control_event_hs_descriptor_content(conn->rend_data->onion_address, \ control_event_hs_descriptor_content(rend_data_get_address(conn->rend_data), \
conn->requested_resource, \ conn->requested_resource, \
conn->identity_digest, \ conn->identity_digest, \
NULL) ) NULL) )
@ -2461,7 +2465,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
#define SEND_HS_DESC_UPLOAD_FAILED_EVENT(reason) ( \ #define SEND_HS_DESC_UPLOAD_FAILED_EVENT(reason) ( \
control_event_hs_descriptor_upload_failed( \ control_event_hs_descriptor_upload_failed( \
conn->identity_digest, \ conn->identity_digest, \
conn->rend_data->onion_address, \ rend_data_get_address(conn->rend_data), \
reason) ) reason) )
log_info(LD_REND,"Uploaded rendezvous descriptor (status %d " log_info(LD_REND,"Uploaded rendezvous descriptor (status %d "
"(%s))", "(%s))",
@ -2475,7 +2479,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
"Uploading rendezvous descriptor: finished with status " "Uploading rendezvous descriptor: finished with status "
"200 (%s)", escaped(reason)); "200 (%s)", escaped(reason));
control_event_hs_descriptor_uploaded(conn->identity_digest, control_event_hs_descriptor_uploaded(conn->identity_digest,
conn->rend_data->onion_address); rend_data_get_address(conn->rend_data));
rend_service_desc_has_uploaded(conn->rend_data); rend_service_desc_has_uploaded(conn->rend_data);
break; break;
case 400: case 400:
@ -2586,7 +2590,8 @@ connection_dir_about_to_close(dir_connection_t *dir_conn)
* refetching is unnecessary.) */ * refetching is unnecessary.) */
if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC_V2 && if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC_V2 &&
dir_conn->rend_data && dir_conn->rend_data &&
strlen(dir_conn->rend_data->onion_address) == REND_SERVICE_ID_LEN_BASE32) strlen(rend_data_get_address(dir_conn->rend_data)) ==
REND_SERVICE_ID_LEN_BASE32)
rend_client_refetch_v2_renddesc(dir_conn->rend_data); rend_client_refetch_v2_renddesc(dir_conn->rend_data);
} }
@ -2806,7 +2811,7 @@ static int handle_get_descriptor(dir_connection_t *conn,
const get_handler_args_t *args); const get_handler_args_t *args);
static int handle_get_keys(dir_connection_t *conn, static int handle_get_keys(dir_connection_t *conn,
const get_handler_args_t *args); const get_handler_args_t *args);
static int handle_get_rendezvous2(dir_connection_t *conn, static int handle_get_hs_descriptor_v2(dir_connection_t *conn,
const get_handler_args_t *args); const get_handler_args_t *args);
static int handle_get_robots(dir_connection_t *conn, static int handle_get_robots(dir_connection_t *conn,
const get_handler_args_t *args); const get_handler_args_t *args);
@ -2823,7 +2828,8 @@ static const url_table_ent_t url_table[] = {
{ "/tor/server/", 1, handle_get_descriptor }, { "/tor/server/", 1, handle_get_descriptor },
{ "/tor/extra/", 1, handle_get_descriptor }, { "/tor/extra/", 1, handle_get_descriptor },
{ "/tor/keys/", 1, handle_get_keys }, { "/tor/keys/", 1, handle_get_keys },
{ "/tor/rendezvous2/", 1, handle_get_rendezvous2 }, { "/tor/rendezvous2/", 1, handle_get_hs_descriptor_v2 },
{ "/tor/hs/3/", 1, handle_get_hs_descriptor_v3 },
{ "/tor/robots.txt", 0, handle_get_robots }, { "/tor/robots.txt", 0, handle_get_robots },
{ "/tor/networkstatus-bridges", 0, handle_get_networkstatus_bridges }, { "/tor/networkstatus-bridges", 0, handle_get_networkstatus_bridges },
{ NULL, 0, NULL }, { NULL, 0, NULL },
@ -3391,7 +3397,8 @@ handle_get_keys(dir_connection_t *conn, const get_handler_args_t *args)
/** Helper function for GET /tor/rendezvous2/ /** Helper function for GET /tor/rendezvous2/
*/ */
static int static int
handle_get_rendezvous2(dir_connection_t *conn, const get_handler_args_t *args) handle_get_hs_descriptor_v2(dir_connection_t *conn,
const get_handler_args_t *args)
{ {
const char *url = args->url; const char *url = args->url;
if (connection_dir_is_encrypted(conn)) { if (connection_dir_is_encrypted(conn)) {
@ -3425,6 +3432,50 @@ handle_get_rendezvous2(dir_connection_t *conn, const get_handler_args_t *args)
return 0; return 0;
} }
/** Helper function for GET /tor/hs/3/<z>. Only for version 3.
*/
STATIC int
handle_get_hs_descriptor_v3(dir_connection_t *conn,
const get_handler_args_t *args)
{
int retval;
const char *desc_str = NULL;
const char *pubkey_str = NULL;
const char *url = args->url;
/* Don't serve v3 descriptors if next gen onion service is disabled. */
if (!hs_v3_protocol_is_enabled()) {
/* 404 is used for an unrecognized URL so send back the same. */
write_http_status_line(conn, 404, "Not found");
goto done;
}
/* Reject unencrypted dir connections */
if (!connection_dir_is_encrypted(conn)) {
write_http_status_line(conn, 404, "Not found");
goto done;
}
/* After the path prefix follows the base64 encoded blinded pubkey which we
* use to get the descriptor from the cache. Skip the prefix and get the
* pubkey. */
tor_assert(!strcmpstart(url, "/tor/hs/3/"));
pubkey_str = url + strlen("/tor/hs/3/");
retval = hs_cache_lookup_as_dir(HS_VERSION_THREE,
pubkey_str, &desc_str);
if (retval < 0) {
write_http_status_line(conn, 404, "Not found");
goto done;
}
/* Found requested descriptor! Pass it to this nice client. */
write_http_response_header(conn, strlen(desc_str), 0, 0);
connection_write_to_buf(desc_str, strlen(desc_str), TO_CONN(conn));
done:
return 0;
}
/** Helper function for GET /tor/networkstatus-bridges /** Helper function for GET /tor/networkstatus-bridges
*/ */
static int static int
@ -3480,6 +3531,90 @@ handle_get_robots(dir_connection_t *conn, const get_handler_args_t *args)
return 0; return 0;
} }
/* Given the <b>url</b> from a POST request, try to extract the version number
* using the provided <b>prefix</b>. The version should be after the prefix and
* ending with the seperator "/". For instance:
* /tor/hs/3/publish
*
* On success, <b>end_pos</b> points to the position right after the version
* was found. On error, it is set to NULL.
*
* Return version on success else negative value. */
STATIC int
parse_hs_version_from_post(const char *url, const char *prefix,
const char **end_pos)
{
int ok;
unsigned long version;
const char *start;
char *end = NULL;
tor_assert(url);
tor_assert(prefix);
tor_assert(end_pos);
/* Check if the prefix does start the url. */
if (strcmpstart(url, prefix)) {
goto err;
}
/* Move pointer to the end of the prefix string. */
start = url + strlen(prefix);
/* Try this to be the HS version and if we are still at the separator, next
* will be move to the right value. */
version = tor_parse_long(start, 10, 0, INT_MAX, &ok, &end);
if (!ok) {
goto err;
}
*end_pos = end;
return (int) version;
err:
*end_pos = NULL;
return -1;
}
/* Handle the POST request for a hidden service descripror. The request is in
* <b>url</b>, the body of the request is in <b>body</b>. Return 200 on success
* else return 400 indicating a bad request. */
STATIC int
handle_post_hs_descriptor(const char *url, const char *body)
{
int version;
const char *end_pos;
tor_assert(url);
tor_assert(body);
version = parse_hs_version_from_post(url, "/tor/hs/", &end_pos);
if (version < 0) {
goto err;
}
/* We have a valid version number, now make sure it's a publish request. Use
* the end position just after the version and check for the command. */
if (strcmpstart(end_pos, "/publish")) {
goto err;
}
switch (version) {
case HS_VERSION_THREE:
if (hs_cache_store_as_dir(body) < 0) {
goto err;
}
log_info(LD_REND, "Publish request for HS descriptor handled "
"successfully.");
break;
default:
/* Unsupported version, return a bad request. */
goto err;
}
return 200;
err:
/* Bad request. */
return 400;
}
/** Helper function: called when a dirserver gets a complete HTTP POST /** Helper function: called when a dirserver gets a complete HTTP POST
* request. Look for an uploaded server descriptor or rendezvous * request. Look for an uploaded server descriptor or rendezvous
* service descriptor. On finding one, process it and write a * service descriptor. On finding one, process it and write a
@ -3524,6 +3659,28 @@ directory_handle_command_post(dir_connection_t *conn, const char *headers,
goto done; goto done;
} }
/* Handle HS descriptor publish request. */
/* XXX: This should be disabled with a consensus param until we want to
* the prop224 be deployed and thus use. */
if (connection_dir_is_encrypted(conn) && !strcmpstart(url, "/tor/hs/")) {
const char *msg = "HS descriptor stored successfully.";
/* Don't accept v3 and onward publish request if next gen onion service is
* disabled. */
if (!hs_v3_protocol_is_enabled()) {
/* 404 is used for an unrecognized URL so send back the same. */
write_http_status_line(conn, 404, "Not found");
goto done;
}
/* We most probably have a publish request for an HS descriptor. */
int code = handle_post_hs_descriptor(url, body);
if (code != 200) {
msg = "Invalid HS descriptor. Rejected.";
}
write_http_status_line(conn, code, msg);
goto done;
}
if (!authdir_mode(options)) { if (!authdir_mode(options)) {
/* we just provide cached directories; we don't want to /* we just provide cached directories; we don't want to
* receive anything. */ * receive anything. */

View File

@ -135,8 +135,16 @@ time_t download_status_get_next_attempt_at(const download_status_t *dls);
int purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose, int purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose,
const char *resource); const char *resource);
#ifdef DIRECTORY_PRIVATE
struct get_handler_args_t;
STATIC int handle_get_hs_descriptor_v3(dir_connection_t *conn,
const struct get_handler_args_t *args);
#endif
#ifdef TOR_UNIT_TESTS #ifdef TOR_UNIT_TESTS
/* Used only by directory.c and test_dir.c */ /* Used only by test_dir.c */
STATIC int parse_http_url(const char *headers, char **url); STATIC int parse_http_url(const char *headers, char **url);
STATIC dirinfo_type_t dir_fetch_type(int dir_purpose, int router_purpose, STATIC dirinfo_type_t dir_fetch_type(int dir_purpose, int router_purpose,
@ -150,6 +158,8 @@ STATIC int download_status_schedule_get_delay(download_status_t *dls,
int min_delay, int max_delay, int min_delay, int max_delay,
time_t now); time_t now);
STATIC int handle_post_hs_descriptor(const char *url, const char *body);
STATIC char* authdir_type_to_string(dirinfo_type_t auth); STATIC char* authdir_type_to_string(dirinfo_type_t auth);
STATIC const char * dir_conn_purpose_to_string(int purpose); STATIC const char * dir_conn_purpose_to_string(int purpose);
STATIC int should_use_directory_guards(const or_options_t *options); STATIC int should_use_directory_guards(const or_options_t *options);
@ -161,6 +171,9 @@ STATIC void find_dl_min_and_max_delay(download_status_t *dls,
int *min, int *max); int *min, int *max);
STATIC int next_random_exponential_delay(int delay, int max_delay); STATIC int next_random_exponential_delay(int delay, int max_delay);
STATIC int parse_hs_version_from_post(const char *url, const char *prefix,
const char **end_pos);
#endif #endif
#endif #endif

384
src/or/hs_cache.c Normal file
View File

@ -0,0 +1,384 @@
/* Copyright (c) 2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file hs_cache.c
* \brief Handle hidden service descriptor caches.
**/
/* For unit tests.*/
#define HS_CACHE_PRIVATE
#include "hs_cache.h"
#include "or.h"
#include "config.h"
#include "hs_common.h"
#include "hs_descriptor.h"
#include "rendcache.h"
/* Directory descriptor cache. Map indexed by blinded key. */
static digest256map_t *hs_cache_v3_dir;
/* Remove a given descriptor from our cache. */
static void
remove_v3_desc_as_dir(const hs_cache_dir_descriptor_t *desc)
{
tor_assert(desc);
digest256map_remove(hs_cache_v3_dir, desc->key);
}
/* Store a given descriptor in our cache. */
static void
store_v3_desc_as_dir(hs_cache_dir_descriptor_t *desc)
{
tor_assert(desc);
digest256map_set(hs_cache_v3_dir, desc->key, desc);
}
/* Query our cache and return the entry or NULL if not found. */
static hs_cache_dir_descriptor_t *
lookup_v3_desc_as_dir(const uint8_t *key)
{
tor_assert(key);
return digest256map_get(hs_cache_v3_dir, key);
}
/* Free a directory descriptor object. */
static void
cache_dir_desc_free(hs_cache_dir_descriptor_t *desc)
{
if (desc == NULL) {
return;
}
hs_desc_plaintext_data_free(desc->plaintext_data);
tor_free(desc->encoded_desc);
tor_free(desc);
}
/* Helper function: Use by the free all function using the digest256map
* interface to cache entries. */
static void
cache_dir_desc_free_(void *ptr)
{
hs_cache_dir_descriptor_t *desc = ptr;
cache_dir_desc_free(desc);
}
/* Create a new directory cache descriptor object from a encoded descriptor.
* On success, return the heap-allocated cache object, otherwise return NULL if
* we can't decode the descriptor. */
static hs_cache_dir_descriptor_t *
cache_dir_desc_new(const char *desc)
{
hs_cache_dir_descriptor_t *dir_desc;
tor_assert(desc);
dir_desc = tor_malloc_zero(sizeof(hs_cache_dir_descriptor_t));
dir_desc->plaintext_data =
tor_malloc_zero(sizeof(hs_desc_plaintext_data_t));
dir_desc->encoded_desc = tor_strdup(desc);
if (hs_desc_decode_plaintext(desc, dir_desc->plaintext_data) < 0) {
log_debug(LD_DIR, "Unable to decode descriptor. Rejecting.");
goto err;
}
/* The blinded pubkey is the indexed key. */
dir_desc->key = dir_desc->plaintext_data->blinded_kp.pubkey.pubkey;
dir_desc->created_ts = time(NULL);
return dir_desc;
err:
cache_dir_desc_free(dir_desc);
return NULL;
}
/* Return the size of a cache entry in bytes. */
static size_t
cache_get_entry_size(const hs_cache_dir_descriptor_t *entry)
{
return (sizeof(*entry) + hs_desc_plaintext_obj_size(entry->plaintext_data)
+ strlen(entry->encoded_desc));
}
/* Try to store a valid version 3 descriptor in the directory cache. Return 0
* on success else a negative value is returned indicating that we have a
* newer version in our cache. On error, caller is responsible to free the
* given descriptor desc. */
static int
cache_store_v3_as_dir(hs_cache_dir_descriptor_t *desc)
{
hs_cache_dir_descriptor_t *cache_entry;
tor_assert(desc);
/* Verify if we have an entry in the cache for that key and if yes, check
* if we should replace it? */
cache_entry = lookup_v3_desc_as_dir(desc->key);
if (cache_entry != NULL) {
/* Only replace descriptor if revision-counter is greater than the one
* in our cache */
if (cache_entry->plaintext_data->revision_counter >=
desc->plaintext_data->revision_counter) {
log_info(LD_REND, "Descriptor revision counter in our cache is "
"greater or equal than the one we received. "
"Rejecting!");
goto err;
}
/* We now know that the descriptor we just received is a new one so
* remove the entry we currently have from our cache so we can then
* store the new one. */
remove_v3_desc_as_dir(cache_entry);
cache_dir_desc_free(cache_entry);
rend_cache_decrement_allocation(cache_get_entry_size(cache_entry));
}
/* Store the descriptor we just got. We are sure here that either we
* don't have the entry or we have a newer descriptor and the old one
* has been removed from the cache. */
store_v3_desc_as_dir(desc);
/* Update our total cache size with this entry for the OOM. This uses the
* old HS protocol cache subsystem for which we are tied with. */
rend_cache_increment_allocation(cache_get_entry_size(desc));
/* XXX: Update HS statistics. We should have specific stats for v3. */
return 0;
err:
return -1;
}
/* Using the query which is the base64 encoded blinded key of a version 3
* descriptor, lookup in our directory cache the entry. If found, 1 is
* returned and desc_out is populated with a newly allocated string being the
* encoded descriptor. If not found, 0 is returned and desc_out is untouched.
* On error, a negative value is returned and desc_out is untouched. */
static int
cache_lookup_v3_as_dir(const char *query, const char **desc_out)
{
int found = 0;
ed25519_public_key_t blinded_key;
const hs_cache_dir_descriptor_t *entry;
tor_assert(query);
/* Decode blinded key using the given query value. */
if (ed25519_public_from_base64(&blinded_key, query) < 0) {
log_info(LD_REND, "Unable to decode the v3 HSDir query %s.",
safe_str_client(query));
goto err;
}
entry = lookup_v3_desc_as_dir(blinded_key.pubkey);
if (entry != NULL) {
found = 1;
if (desc_out) {
*desc_out = entry->encoded_desc;
}
}
return found;
err:
return -1;
}
/* Clean the v3 cache by removing any entry that has expired using the
* <b>global_cutoff</b> value. If <b>global_cutoff</b> is 0, the cleaning
* process will use the lifetime found in the plaintext data section. Return
* the number of bytes cleaned. */
STATIC size_t
cache_clean_v3_as_dir(time_t now, time_t global_cutoff)
{
size_t bytes_removed = 0;
/* Code flow error if this ever happens. */
tor_assert(global_cutoff >= 0);
if (!hs_cache_v3_dir) { /* No cache to clean. Just return. */
return 0;
}
DIGEST256MAP_FOREACH_MODIFY(hs_cache_v3_dir, key,
hs_cache_dir_descriptor_t *, entry) {
size_t entry_size;
time_t cutoff = global_cutoff;
if (!cutoff) {
/* Cutoff is the lifetime of the entry found in the descriptor. */
cutoff = now - entry->plaintext_data->lifetime_sec;
}
/* If the entry has been created _after_ the cutoff, not expired so
* continue to the next entry in our v3 cache. */
if (entry->created_ts > cutoff) {
continue;
}
/* Here, our entry has expired, remove and free. */
MAP_DEL_CURRENT(key);
entry_size = cache_get_entry_size(entry);
bytes_removed += entry_size;
/* Entry is not in the cache anymore, destroy it. */
cache_dir_desc_free(entry);
/* Update our cache entry allocation size for the OOM. */
rend_cache_decrement_allocation(entry_size);
/* Logging. */
{
char key_b64[BASE64_DIGEST256_LEN + 1];
base64_encode(key_b64, sizeof(key_b64), (const char *) key,
DIGEST256_LEN, 0);
log_info(LD_REND, "Removing v3 descriptor '%s' from HSDir cache",
safe_str_client(key_b64));
}
} DIGEST256MAP_FOREACH_END;
return bytes_removed;
}
/* Given an encoded descriptor, store it in the directory cache depending on
* which version it is. Return a negative value on error. On success, 0 is
* returned. */
int
hs_cache_store_as_dir(const char *desc)
{
hs_cache_dir_descriptor_t *dir_desc = NULL;
tor_assert(desc);
/* Create a new cache object. This can fail if the descriptor plaintext data
* is unparseable which in this case a log message will be triggered. */
dir_desc = cache_dir_desc_new(desc);
if (dir_desc == NULL) {
goto err;
}
/* Call the right function against the descriptor version. At this point,
* we are sure that the descriptor's version is supported else the
* decoding would have failed. */
switch (dir_desc->plaintext_data->version) {
case HS_VERSION_THREE:
default:
if (cache_store_v3_as_dir(dir_desc) < 0) {
goto err;
}
break;
}
return 0;
err:
cache_dir_desc_free(dir_desc);
return -1;
}
/* Using the query, lookup in our directory cache the entry. If found, 1 is
* returned and desc_out is populated with a newly allocated string being
* the encoded descriptor. If not found, 0 is returned and desc_out is
* untouched. On error, a negative value is returned and desc_out is
* untouched. */
int
hs_cache_lookup_as_dir(uint32_t version, const char *query,
const char **desc_out)
{
int found;
tor_assert(query);
/* This should never be called with an unsupported version. */
tor_assert(hs_desc_is_supported_version(version));
switch (version) {
case HS_VERSION_THREE:
default:
found = cache_lookup_v3_as_dir(query, desc_out);
break;
}
return found;
}
/* Clean all directory caches using the current time now. */
void
hs_cache_clean_as_dir(time_t now)
{
time_t cutoff;
/* Start with v2 cache cleaning. */
cutoff = now - rend_cache_max_entry_lifetime();
rend_cache_clean_v2_descs_as_dir(cutoff);
/* Now, clean the v3 cache. Set the cutoff to 0 telling the cleanup function
* to compute the cutoff by itself using the lifetime value. */
cache_clean_v3_as_dir(now, 0);
}
/* Do a round of OOM cleanup on all directory caches. Return the amount of
* removed bytes. It is possible that the returned value is lower than
* min_remove_bytes if the caches get emptied out so the caller should be
* aware of this. */
size_t
hs_cache_handle_oom(time_t now, size_t min_remove_bytes)
{
time_t k;
size_t bytes_removed = 0;
/* Our OOM handler called with 0 bytes to remove is a code flow error. */
tor_assert(min_remove_bytes != 0);
/* The algorithm is as follow. K is the oldest expected descriptor age.
*
* 1) Deallocate all entries from v2 cache that are older than K hours.
* 1.1) If the amount of remove bytes has been reached, stop.
* 2) Deallocate all entries from v3 cache that are older than K hours
* 2.1) If the amount of remove bytes has been reached, stop.
* 3) Set K = K - RendPostPeriod and repeat process until K is < 0.
*
* This ends up being O(Kn).
*/
/* Set K to the oldest expected age in seconds which is the maximum
* lifetime of a cache entry. We'll use the v2 lifetime because it's much
* bigger than the v3 thus leading to cleaning older descriptors. */
k = rend_cache_max_entry_lifetime();
do {
time_t cutoff;
/* If K becomes negative, it means we've empty the caches so stop and
* return what we were able to cleanup. */
if (k < 0) {
break;
}
/* Compute a cutoff value with K and the current time. */
cutoff = now - k;
/* Start by cleaning the v2 cache with that cutoff. */
bytes_removed += rend_cache_clean_v2_descs_as_dir(cutoff);
if (bytes_removed < min_remove_bytes) {
/* We haven't remove enough bytes so clean v3 cache. */
bytes_removed += cache_clean_v3_as_dir(now, cutoff);
/* Decrement K by a post period to shorten the cutoff. */
k -= get_options()->RendPostPeriod;
}
} while (bytes_removed < min_remove_bytes);
return bytes_removed;
}
/* Initialize the hidden service cache subsystem. */
void
hs_cache_init(void)
{
/* Calling this twice is very wrong code flow. */
tor_assert(!hs_cache_v3_dir);
hs_cache_v3_dir = digest256map_new();
}
/* Cleanup the hidden service cache subsystem. */
void
hs_cache_free_all(void)
{
tor_assert(hs_cache_v3_dir);
digest256map_free(hs_cache_v3_dir, cache_dir_desc_free_);
}

61
src/or/hs_cache.h Normal file
View File

@ -0,0 +1,61 @@
/* Copyright (c) 2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file hs_cache.h
* \brief Header file for hs_cache.c
**/
#ifndef TOR_HS_CACHE_H
#define TOR_HS_CACHE_H
#include <stdint.h>
#include "crypto.h"
#include "crypto_ed25519.h"
#include "hs_common.h"
#include "hs_descriptor.h"
#include "torcert.h"
/* Descriptor representation on the directory side which is a subset of
* information that the HSDir can decode and serve it. */
typedef struct hs_cache_dir_descriptor_t {
/* This object is indexed using the blinded pubkey located in the plaintext
* data which is populated only once the descriptor has been successfully
* decoded and validated. This simply points to that pubkey. */
const uint8_t *key;
/* When does this entry has been created. Used to expire entries. */
time_t created_ts;
/* Descriptor plaintext information. Obviously, we can't decrypt the
* encrypted part of the descriptor. */
hs_desc_plaintext_data_t *plaintext_data;
/* Encoded descriptor which is basically in text form. It's a NUL terminated
* string thus safe to strlen(). */
char *encoded_desc;
} hs_cache_dir_descriptor_t;
/* Public API */
void hs_cache_init(void);
void hs_cache_free_all(void);
void hs_cache_clean_as_dir(time_t now);
size_t hs_cache_handle_oom(time_t now, size_t min_remove_bytes);
/* Store and Lookup function. They are version agnostic that is depending on
* the requested version of the descriptor, it will be re-routed to the
* right function. */
int hs_cache_store_as_dir(const char *desc);
int hs_cache_lookup_as_dir(uint32_t version, const char *query,
const char **desc_out);
#ifdef HS_CACHE_PRIVATE
STATIC size_t cache_clean_v3_as_dir(time_t now, time_t global_cutoff);
#endif /* HS_CACHE_PRIVATE */
#endif /* TOR_HS_CACHE_H */

280
src/or/hs_common.c Normal file
View File

@ -0,0 +1,280 @@
/* Copyright (c) 2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file hs_common.c
* \brief Contains code shared between different HS protocol version as well
* as useful data structures and accessors used by other subsystems.
* The rendcommon.c should only contains code relating to the v2
* protocol.
**/
#include "or.h"
#include "config.h"
#include "networkstatus.h"
#include "hs_common.h"
#include "rendcommon.h"
/* Create a new rend_data_t for a specific given <b>version</b>.
* Return a pointer to the newly allocated data structure. */
static rend_data_t *
rend_data_alloc(uint32_t version)
{
rend_data_t *rend_data = NULL;
switch (version) {
case HS_VERSION_TWO:
{
rend_data_v2_t *v2 = tor_malloc_zero(sizeof(*v2));
v2->base_.version = HS_VERSION_TWO;
v2->base_.hsdirs_fp = smartlist_new();
rend_data = &v2->base_;
break;
}
default:
tor_assert(0);
break;
}
return rend_data;
}
/** Free all storage associated with <b>data</b> */
void
rend_data_free(rend_data_t *data)
{
if (!data) {
return;
}
/* By using our allocation function, this should always be set. */
tor_assert(data->hsdirs_fp);
/* Cleanup the HSDir identity digest. */
SMARTLIST_FOREACH(data->hsdirs_fp, char *, d, tor_free(d));
smartlist_free(data->hsdirs_fp);
/* Depending on the version, cleanup. */
switch (data->version) {
case HS_VERSION_TWO:
{
rend_data_v2_t *v2_data = TO_REND_DATA_V2(data);
tor_free(v2_data);
break;
}
default:
tor_assert(0);
}
}
/* Allocate and return a deep copy of <b>data</b>. */
rend_data_t *
rend_data_dup(const rend_data_t *data)
{
rend_data_t *data_dup = NULL;
smartlist_t *hsdirs_fp = smartlist_new();
tor_assert(data);
tor_assert(data->hsdirs_fp);
SMARTLIST_FOREACH(data->hsdirs_fp, char *, fp,
smartlist_add(hsdirs_fp, tor_memdup(fp, DIGEST_LEN)));
switch (data->version) {
case HS_VERSION_TWO:
{
rend_data_v2_t *v2_data = tor_memdup(TO_REND_DATA_V2(data),
sizeof(*v2_data));
data_dup = &v2_data->base_;
data_dup->hsdirs_fp = hsdirs_fp;
break;
}
default:
tor_assert(0);
break;
}
return data_dup;
}
/* Compute the descriptor ID for each HS descriptor replica and save them. A
* valid onion address must be present in the <b>rend_data</b>.
*
* Return 0 on success else -1. */
static int
compute_desc_id(rend_data_t *rend_data)
{
int ret = 0;
unsigned replica;
time_t now = time(NULL);
tor_assert(rend_data);
switch (rend_data->version) {
case HS_VERSION_TWO:
{
rend_data_v2_t *v2_data = TO_REND_DATA_V2(rend_data);
/* Compute descriptor ID for each replicas. */
for (replica = 0; replica < ARRAY_LENGTH(v2_data->descriptor_id);
replica++) {
ret = rend_compute_v2_desc_id(v2_data->descriptor_id[replica],
v2_data->onion_address,
v2_data->descriptor_cookie,
now, replica);
if (ret < 0) {
goto end;
}
}
break;
}
default:
tor_assert(0);
}
end:
return ret;
}
/* Allocate and initialize a rend_data_t object for a service using the
* provided arguments. All arguments are optional (can be NULL), except from
* <b>onion_address</b> which MUST be set.
*
* Return a valid rend_data_t pointer. This only returns a version 2 object of
* rend_data_t. */
rend_data_t *
rend_data_service_create(const char *onion_address, const char *pk_digest,
const uint8_t *cookie, rend_auth_type_t auth_type)
{
/* Create a rend_data_t object for version 2. */
rend_data_t *rend_data = rend_data_alloc(HS_VERSION_TWO);
rend_data_v2_t *v2= TO_REND_DATA_V2(rend_data);
/* We need at least one else the call is wrong. */
tor_assert(onion_address != NULL);
if (pk_digest) {
memcpy(v2->rend_pk_digest, pk_digest, sizeof(v2->rend_pk_digest));
}
if (cookie) {
memcpy(rend_data->rend_cookie, cookie, sizeof(rend_data->rend_cookie));
}
strlcpy(v2->onion_address, onion_address, sizeof(v2->onion_address));
v2->auth_type = auth_type;
return rend_data;
}
/* Allocate and initialize a rend_data_t object for a client request using
* the given arguments. Either an onion address or a descriptor ID is
* needed. Both can be given but only the onion address will be used to make
* the descriptor fetch.
*
* Return a valid rend_data_t pointer or NULL on error meaning the
* descriptor IDs couldn't be computed from the given data. */
rend_data_t *
rend_data_client_create(const char *onion_address, const char *desc_id,
const char *cookie, rend_auth_type_t auth_type)
{
/* Create a rend_data_t object for version 2. */
rend_data_t *rend_data = rend_data_alloc(HS_VERSION_TWO);
rend_data_v2_t *v2= TO_REND_DATA_V2(rend_data);
/* We need at least one else the call is wrong. */
tor_assert(onion_address != NULL || desc_id != NULL);
if (cookie) {
memcpy(v2->descriptor_cookie, cookie, sizeof(v2->descriptor_cookie));
}
if (desc_id) {
memcpy(v2->desc_id_fetch, desc_id, sizeof(v2->desc_id_fetch));
}
if (onion_address) {
strlcpy(v2->onion_address, onion_address, sizeof(v2->onion_address));
if (compute_desc_id(rend_data) < 0) {
goto error;
}
}
v2->auth_type = auth_type;
return rend_data;
error:
rend_data_free(rend_data);
return NULL;
}
/* Return the onion address from the rend data. Depending on the version,
* the size of the address can vary but it's always NUL terminated. */
const char *
rend_data_get_address(const rend_data_t *rend_data)
{
tor_assert(rend_data);
switch (rend_data->version) {
case HS_VERSION_TWO:
return TO_REND_DATA_V2(rend_data)->onion_address;
default:
/* We should always have a supported version. */
tor_assert(0);
}
}
/* Return the descriptor ID for a specific replica number from the rend
* data. The returned data is a binary digest and depending on the version its
* size can vary. The size of the descriptor ID is put in <b>len_out</b> if
* non NULL. */
const char *
rend_data_get_desc_id(const rend_data_t *rend_data, uint8_t replica,
size_t *len_out)
{
tor_assert(rend_data);
switch (rend_data->version) {
case HS_VERSION_TWO:
tor_assert(replica < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS);
if (len_out) {
*len_out = DIGEST_LEN;
}
return TO_REND_DATA_V2(rend_data)->descriptor_id[replica];
default:
/* We should always have a supported version. */
tor_assert(0);
}
}
/* Return the public key digest using the given <b>rend_data</b>. The size of
* the digest is put in <b>len_out</b> (if set) which can differ depending on
* the version. */
const uint8_t *
rend_data_get_pk_digest(const rend_data_t *rend_data, size_t *len_out)
{
tor_assert(rend_data);
switch (rend_data->version) {
case HS_VERSION_TWO:
{
const rend_data_v2_t *v2_data = TO_REND_DATA_V2(rend_data);
if (len_out) {
*len_out = sizeof(v2_data->rend_pk_digest);
}
return (const uint8_t *) v2_data->rend_pk_digest;
}
default:
/* We should always have a supported version. */
tor_assert(0);
}
}
/* Return true iff the Onion Services protocol version 3 is enabled. This only
* considers the consensus parameter. If the parameter is not found, the
* default is that it's enabled. */
int
hs_v3_protocol_is_enabled(void)
{
/* This consensus param controls if the the onion services version 3 is
* enabled or not which is the first version of the next generation
* (proposal 224). If this option is set to 0, the tor daemon won't support
* the protocol as either a relay, directory, service or client. By default,
* it's enabled if the parameter is not found. */
return networkstatus_get_param(NULL, "EnableOnionServicesV3", 1, 0, 1);
}

39
src/or/hs_common.h Normal file
View File

@ -0,0 +1,39 @@
/* Copyright (c) 2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file hs_common.h
* \brief Header file containing common data for the whole HS subsytem.
**/
#ifndef TOR_HS_COMMON_H
#define TOR_HS_COMMON_H
#include "or.h"
/* Protocol version 2. Use this instead of hardcoding "2" in the code base,
* this adds a clearer semantic to the value when used. */
#define HS_VERSION_TWO 2
/* Version 3 of the protocol (prop224). */
#define HS_VERSION_THREE 3
void rend_data_free(rend_data_t *data);
rend_data_t *rend_data_dup(const rend_data_t *data);
rend_data_t *rend_data_client_create(const char *onion_address,
const char *desc_id,
const char *cookie,
rend_auth_type_t auth_type);
rend_data_t *rend_data_service_create(const char *onion_address,
const char *pk_digest,
const uint8_t *cookie,
rend_auth_type_t auth_type);
const char *rend_data_get_address(const rend_data_t *rend_data);
const char *rend_data_get_desc_id(const rend_data_t *rend_data,
uint8_t replica, size_t *len_out);
const uint8_t *rend_data_get_pk_digest(const rend_data_t *rend_data,
size_t *len_out);
int hs_v3_protocol_is_enabled(void);
#endif /* TOR_HS_COMMON_H */

1939
src/or/hs_descriptor.c Normal file

File diff suppressed because it is too large Load Diff

238
src/or/hs_descriptor.h Normal file
View File

@ -0,0 +1,238 @@
/* Copyright (c) 2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file hs_descriptor.h
* \brief Header file for hs_descriptor.c
**/
#ifndef TOR_HS_DESCRIPTOR_H
#define TOR_HS_DESCRIPTOR_H
#include <stdint.h>
#include "address.h"
#include "container.h"
#include "crypto.h"
#include "crypto_ed25519.h"
#include "torcert.h"
/* The earliest descriptor format version we support. */
#define HS_DESC_SUPPORTED_FORMAT_VERSION_MIN 3
/* The latest descriptor format version we support. */
#define HS_DESC_SUPPORTED_FORMAT_VERSION_MAX 3
/* Maximum lifetime of a descriptor in seconds. The value is set at 12 hours
* which is 720 minutes or 43200 seconds. */
#define HS_DESC_MAX_LIFETIME (12 * 60 * 60)
/* Lifetime of certificate in the descriptor. This defines the lifetime of the
* descriptor signing key and the cross certification cert of that key. */
#define HS_DESC_CERT_LIFETIME (24 * 60 * 60)
/* Length of the salt needed for the encrypted section of a descriptor. */
#define HS_DESC_ENCRYPTED_SALT_LEN 16
/* Length of the secret input needed for the KDF construction which derives
* the encryption key for the encrypted data section of the descriptor. This
* adds up to 68 bytes being the blinded key, hashed subcredential and
* revision counter. */
#define HS_DESC_ENCRYPTED_SECRET_INPUT_LEN \
ED25519_PUBKEY_LEN + DIGEST256_LEN + sizeof(uint64_t)
/* Length of the KDF output value which is the length of the secret key,
* the secret IV and MAC key length which is the length of H() output. */
#define HS_DESC_ENCRYPTED_KDF_OUTPUT_LEN \
CIPHER_KEY_LEN + CIPHER_IV_LEN + DIGEST256_LEN
/* We need to pad the plaintext version of the encrypted data section before
* encryption and it has to be a multiple of this value. */
#define HS_DESC_PLAINTEXT_PADDING_MULTIPLE 128
/* XXX: Let's make sure this makes sense as an upper limit for the padded
* plaintext section. Then we should enforce it as now only an assert will be
* triggered if we are above it. */
/* Once padded, this is the maximum length in bytes for the plaintext. */
#define HS_DESC_PADDED_PLAINTEXT_MAX_LEN 8192
/* Minimum length in bytes of the encrypted portion of the descriptor. */
#define HS_DESC_ENCRYPTED_MIN_LEN \
HS_DESC_ENCRYPTED_SALT_LEN + \
HS_DESC_PLAINTEXT_PADDING_MULTIPLE + DIGEST256_LEN
/* Maximum length in bytes of a full hidden service descriptor. */
#define HS_DESC_MAX_LEN 32768 // XXX justify
/* The minimum amount of fields a descriptor should contain. The parsing of
* the fields are version specific so the only required field, as a generic
* view of a descriptor, is 1 that is the version field. */
#define HS_DESC_PLAINTEXT_MIN_FIELDS 1
/* Type of authentication in the descriptor. */
typedef enum {
HS_DESC_AUTH_PASSWORD = 1,
HS_DESC_AUTH_ED25519 = 2,
} hs_desc_auth_type_t;
/* Type of encryption key in the descriptor. */
typedef enum {
HS_DESC_KEY_TYPE_LEGACY = 1,
HS_DESC_KEY_TYPE_CURVE25519 = 2,
} hs_desc_key_type_t;
/* Link specifier object that contains information on how to extend to the
* relay that is the address, port and handshake type. */
typedef struct hs_desc_link_specifier_t {
/* Indicate the type of link specifier. See trunnel ed25519_cert
* specification. */
uint8_t type;
/* It's either an address/port or a legacy identity fingerprint. */
union {
/* IP address and port of the relay use to extend. */
tor_addr_port_t ap;
/* Legacy identity. A 20-byte SHA1 identity fingerprint. */
uint8_t legacy_id[DIGEST_LEN];
} u;
} hs_desc_link_specifier_t;
/* Introduction point information located in a descriptor. */
typedef struct hs_desc_intro_point_t {
/* Link specifier(s) which details how to extend to the relay. This list
* contains hs_desc_link_specifier_t object. It MUST have at least one. */
smartlist_t *link_specifiers;
/* Authentication key used to establish the introduction point circuit and
* cross-certifies the blinded public key for the replica thus signed by
* the blinded key and in turn signs it. */
tor_cert_t *auth_key_cert;
/* Encryption key type so we know which one to use in the union below. */
hs_desc_key_type_t enc_key_type;
/* Keys are mutually exclusive thus the union. */
union {
/* Encryption key used to encrypt request to hidden service. */
curve25519_keypair_t curve25519;
/* Backward compat: RSA 1024 encryption key for legacy purposes.
* Mutually exclusive with enc_key. */
crypto_pk_t *legacy;
} enc_key;
/* True iff the introduction point has passed the cross certification. Upon
* decoding an intro point, this must be true. */
unsigned int cross_certified : 1;
} hs_desc_intro_point_t;
/* The encrypted data section of a descriptor. Obviously the data in this is
* in plaintext but encrypted once encoded. */
typedef struct hs_desc_encrypted_data_t {
/* Bitfield of CREATE2 cell supported formats. The only currently supported
* format is ntor. */
unsigned int create2_ntor : 1;
/* A list of authentication types that a client must at least support one
* in order to contact the service. Contains NULL terminated strings. */
smartlist_t *auth_types;
/* A list of intro points. Contains hs_desc_intro_point_t objects. */
smartlist_t *intro_points;
} hs_desc_encrypted_data_t;
/* Plaintext data that is unencrypted information of the descriptor. */
typedef struct hs_desc_plaintext_data_t {
/* Version of the descriptor format. Spec specifies this field as a
* positive integer. */
uint32_t version;
/* The lifetime of the descriptor in seconds. */
uint32_t lifetime_sec;
/* Certificate with the short-term ed22519 descriptor signing key for the
* replica which is signed by the blinded public key for that replica. */
tor_cert_t *signing_key_cert;
/* Signing keypair which is used to sign the descriptor. Same public key
* as in the signing key certificate. */
ed25519_keypair_t signing_kp;
/* Blinded keypair used for this descriptor derived from the master
* identity key and generated for a specific replica number. */
ed25519_keypair_t blinded_kp;
/* Revision counter is incremented at each upload, regardless of whether
* the descriptor has changed. This avoids leaking whether the descriptor
* has changed. Spec specifies this as a 8 bytes positive integer. */
uint64_t revision_counter;
/* Decoding only: The base64-decoded encrypted blob from the descriptor */
uint8_t *encrypted_blob;
/* Decoding only: Size of the encrypted_blob */
size_t encrypted_blob_size;
} hs_desc_plaintext_data_t;
/* Service descriptor in its decoded form. */
typedef struct hs_descriptor_t {
/* Contains the plaintext part of the descriptor. */
hs_desc_plaintext_data_t plaintext_data;
/* The following contains what's in the encrypted part of the descriptor.
* It's only encrypted in the encoded version of the descriptor thus the
* data contained in that object is in plaintext. */
hs_desc_encrypted_data_t encrypted_data;
/* Subcredentials of a service, used by the client and service to decrypt
* the encrypted data. */
uint8_t subcredential[DIGEST256_LEN];
} hs_descriptor_t;
/* Return true iff the given descriptor format version is supported. */
static inline int
hs_desc_is_supported_version(uint32_t version)
{
if (version < HS_DESC_SUPPORTED_FORMAT_VERSION_MIN ||
version > HS_DESC_SUPPORTED_FORMAT_VERSION_MAX) {
return 0;
}
return 1;
}
/* Public API. */
void hs_descriptor_free(hs_descriptor_t *desc);
void hs_desc_plaintext_data_free(hs_desc_plaintext_data_t *desc);
void hs_desc_encrypted_data_free(hs_desc_encrypted_data_t *desc);
int hs_desc_encode_descriptor(const hs_descriptor_t *desc,
char **encoded_out);
int hs_desc_decode_descriptor(const char *encoded,
const uint8_t *subcredential,
hs_descriptor_t **desc_out);
int hs_desc_decode_plaintext(const char *encoded,
hs_desc_plaintext_data_t *plaintext);
int hs_desc_decode_encrypted(const hs_descriptor_t *desc,
hs_desc_encrypted_data_t *desc_out);
size_t hs_desc_plaintext_obj_size(const hs_desc_plaintext_data_t *data);
#ifdef HS_DESCRIPTOR_PRIVATE
/* Encoding. */
STATIC int encode_cert(const tor_cert_t *cert, char **cert_str_out);
STATIC char *encode_link_specifiers(const smartlist_t *specs);
STATIC size_t build_plaintext_padding(const char *plaintext,
size_t plaintext_len,
uint8_t **padded_out);
/* Decoding. */
STATIC smartlist_t *decode_link_specifiers(const char *encoded);
STATIC hs_desc_intro_point_t *decode_introduction_point(
const hs_descriptor_t *desc,
const char *text);
STATIC int decode_intro_points(const hs_descriptor_t *desc,
hs_desc_encrypted_data_t *desc_enc,
const char *data);
STATIC int encrypted_data_length_is_valid(size_t len);
STATIC int cert_is_valid(tor_cert_t *cert, uint8_t type,
const char *log_obj_type);
STATIC int desc_sig_is_valid(const char *b64_sig,
const ed25519_keypair_t *signing_kp,
const char *encoded_desc, size_t encoded_len);
#endif /* HS_DESCRIPTOR_PRIVATE */
#endif /* TOR_HS_DESCRIPTOR_H */

View File

@ -48,6 +48,9 @@ LIBTOR_A_SOURCES = \
src/or/entrynodes.c \ src/or/entrynodes.c \
src/or/ext_orport.c \ src/or/ext_orport.c \
src/or/hibernate.c \ src/or/hibernate.c \
src/or/hs_cache.c \
src/or/hs_common.c \
src/or/hs_descriptor.c \
src/or/keypin.c \ src/or/keypin.c \
src/or/main.c \ src/or/main.c \
src/or/microdesc.c \ src/or/microdesc.c \
@ -59,6 +62,7 @@ LIBTOR_A_SOURCES = \
src/or/shared_random.c \ src/or/shared_random.c \
src/or/shared_random_state.c \ src/or/shared_random_state.c \
src/or/transports.c \ src/or/transports.c \
src/or/parsecommon.c \
src/or/periodic.c \ src/or/periodic.c \
src/or/protover.c \ src/or/protover.c \
src/or/policies.c \ src/or/policies.c \
@ -157,6 +161,9 @@ ORHEADERS = \
src/or/geoip.h \ src/or/geoip.h \
src/or/entrynodes.h \ src/or/entrynodes.h \
src/or/hibernate.h \ src/or/hibernate.h \
src/or/hs_cache.h \
src/or/hs_common.h \
src/or/hs_descriptor.h \
src/or/keypin.h \ src/or/keypin.h \
src/or/main.h \ src/or/main.h \
src/or/microdesc.h \ src/or/microdesc.h \
@ -171,6 +178,7 @@ ORHEADERS = \
src/or/shared_random.h \ src/or/shared_random.h \
src/or/shared_random_state.h \ src/or/shared_random_state.h \
src/or/transports.h \ src/or/transports.h \
src/or/parsecommon.h \
src/or/periodic.h \ src/or/periodic.h \
src/or/policies.h \ src/or/policies.h \
src/or/protover.h \ src/or/protover.h \

View File

@ -73,6 +73,7 @@
#include "entrynodes.h" #include "entrynodes.h"
#include "geoip.h" #include "geoip.h"
#include "hibernate.h" #include "hibernate.h"
#include "hs_cache.h"
#include "keypin.h" #include "keypin.h"
#include "main.h" #include "main.h"
#include "microdesc.h" #include "microdesc.h"
@ -1748,7 +1749,7 @@ clean_caches_callback(time_t now, const or_options_t *options)
rep_history_clean(now - options->RephistTrackTime); rep_history_clean(now - options->RephistTrackTime);
rend_cache_clean(now, REND_CACHE_TYPE_CLIENT); rend_cache_clean(now, REND_CACHE_TYPE_CLIENT);
rend_cache_clean(now, REND_CACHE_TYPE_SERVICE); rend_cache_clean(now, REND_CACHE_TYPE_SERVICE);
rend_cache_clean_v2_descs_as_dir(now, 0); hs_cache_clean_as_dir(now);
microdesc_cache_rebuild(NULL, 0); microdesc_cache_rebuild(NULL, 0);
#define CLEAN_CACHES_INTERVAL (30*60) #define CLEAN_CACHES_INTERVAL (30*60)
return CLEAN_CACHES_INTERVAL; return CLEAN_CACHES_INTERVAL;
@ -3094,6 +3095,7 @@ tor_free_all(int postfork)
rend_service_free_all(); rend_service_free_all();
rend_cache_free_all(); rend_cache_free_all();
rend_service_authorization_free_all(); rend_service_authorization_free_all();
hs_cache_free_all();
rep_hist_free_all(); rep_hist_free_all();
dns_free_all(); dns_free_all();
clear_pending_onions(); clear_pending_onions();

View File

@ -114,6 +114,9 @@
#define NON_ANONYMOUS_MODE_ENABLED 1 #define NON_ANONYMOUS_MODE_ENABLED 1
#endif #endif
/** Helper macro: Given a pointer to to.base_, of type from*, return &to. */
#define DOWNCAST(to, ptr) ((to*)SUBTYPE_P(ptr, to, base_))
/** Length of longest allowable configured nickname. */ /** Length of longest allowable configured nickname. */
#define MAX_NICKNAME_LEN 19 #define MAX_NICKNAME_LEN 19
/** Length of a router identity encoded as a hexadecimal digest, plus /** Length of a router identity encoded as a hexadecimal digest, plus
@ -779,6 +782,24 @@ typedef struct rend_service_authorization_t {
* establishment. Not all fields contain data depending on where this struct * establishment. Not all fields contain data depending on where this struct
* is used. */ * is used. */
typedef struct rend_data_t { typedef struct rend_data_t {
/* Hidden service protocol version of this base object. */
uint32_t version;
/** List of HSDir fingerprints on which this request has been sent to. This
* contains binary identity digest of the directory of size DIGEST_LEN. */
smartlist_t *hsdirs_fp;
/** Rendezvous cookie used by both, client and service. */
char rend_cookie[REND_COOKIE_LEN];
/** Number of streams associated with this rendezvous circuit. */
int nr_streams;
} rend_data_t;
typedef struct rend_data_v2_t {
/* Rendezvous base data. */
rend_data_t base_;
/** Onion address (without the .onion part) that a client requests. */ /** Onion address (without the .onion part) that a client requests. */
char onion_address[REND_SERVICE_ID_LEN_BASE32+1]; char onion_address[REND_SERVICE_ID_LEN_BASE32+1];
@ -800,17 +821,16 @@ typedef struct rend_data_t {
/** Hash of the hidden service's PK used by a service. */ /** Hash of the hidden service's PK used by a service. */
char rend_pk_digest[DIGEST_LEN]; char rend_pk_digest[DIGEST_LEN];
} rend_data_v2_t;
/** Rendezvous cookie used by both, client and service. */ /* From a base rend_data_t object <b>d</d>, return the v2 object. */
char rend_cookie[REND_COOKIE_LEN]; static inline
rend_data_v2_t *TO_REND_DATA_V2(const rend_data_t *d)
/** List of HSDir fingerprints on which this request has been sent to. {
* This contains binary identity digest of the directory. */ tor_assert(d);
smartlist_t *hsdirs_fp; tor_assert(d->version == 2);
return DOWNCAST(rend_data_v2_t, d);
/** Number of streams associated with this rendezvous circuit. */ }
int nr_streams;
} rend_data_t;
/** Time interval for tracking replays of DH public keys received in /** Time interval for tracking replays of DH public keys received in
* INTRODUCE2 cells. Used only to avoid launching multiple * INTRODUCE2 cells. Used only to avoid launching multiple
@ -1805,8 +1825,6 @@ typedef struct control_connection_t {
/** Cast a connection_t subtype pointer to a connection_t **/ /** Cast a connection_t subtype pointer to a connection_t **/
#define TO_CONN(c) (&(((c)->base_))) #define TO_CONN(c) (&(((c)->base_)))
/** Helper macro: Given a pointer to to.base_, of type from*, return &to. */
#define DOWNCAST(to, ptr) ((to*)SUBTYPE_P(ptr, to, base_))
/** Cast a entry_connection_t subtype pointer to a edge_connection_t **/ /** Cast a entry_connection_t subtype pointer to a edge_connection_t **/
#define ENTRY_TO_EDGE_CONN(c) (&(((c))->edge_)) #define ENTRY_TO_EDGE_CONN(c) (&(((c))->edge_))

450
src/or/parsecommon.c Normal file
View File

@ -0,0 +1,450 @@
/* Copyright (c) 2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file parsecommon.c
* \brief Common code to parse and validate various type of descriptors.
**/
#include "parsecommon.h"
#include "torlog.h"
#include "util_format.h"
#define MIN_ANNOTATION A_PURPOSE
#define MAX_ANNOTATION A_UNKNOWN_
#define ALLOC_ZERO(sz) memarea_alloc_zero(area,sz)
#define ALLOC(sz) memarea_alloc(area,sz)
#define STRDUP(str) memarea_strdup(area,str)
#define STRNDUP(str,n) memarea_strndup(area,(str),(n))
#define RET_ERR(msg) \
STMT_BEGIN \
if (tok) token_clear(tok); \
tok = ALLOC_ZERO(sizeof(directory_token_t)); \
tok->tp = ERR_; \
tok->error = STRDUP(msg); \
goto done_tokenizing; \
STMT_END
/** Free all resources allocated for <b>tok</b> */
void
token_clear(directory_token_t *tok)
{
if (tok->key)
crypto_pk_free(tok->key);
}
/** Read all tokens from a string between <b>start</b> and <b>end</b>, and add
* them to <b>out</b>. Parse according to the token rules in <b>table</b>.
* Caller must free tokens in <b>out</b>. If <b>end</b> is NULL, use the
* entire string.
*/
int
tokenize_string(memarea_t *area,
const char *start, const char *end, smartlist_t *out,
token_rule_t *table, int flags)
{
const char **s;
directory_token_t *tok = NULL;
int counts[NIL_];
int i;
int first_nonannotation;
int prev_len = smartlist_len(out);
tor_assert(area);
s = &start;
if (!end) {
end = start+strlen(start);
} else {
/* it's only meaningful to check for nuls if we got an end-of-string ptr */
if (memchr(start, '\0', end-start)) {
log_warn(LD_DIR, "parse error: internal NUL character.");
return -1;
}
}
for (i = 0; i < NIL_; ++i)
counts[i] = 0;
SMARTLIST_FOREACH(out, const directory_token_t *, t, ++counts[t->tp]);
while (*s < end && (!tok || tok->tp != EOF_)) {
tok = get_next_token(area, s, end, table);
if (tok->tp == ERR_) {
log_warn(LD_DIR, "parse error: %s", tok->error);
token_clear(tok);
return -1;
}
++counts[tok->tp];
smartlist_add(out, tok);
*s = eat_whitespace_eos(*s, end);
}
if (flags & TS_NOCHECK)
return 0;
if ((flags & TS_ANNOTATIONS_OK)) {
first_nonannotation = -1;
for (i = 0; i < smartlist_len(out); ++i) {
tok = smartlist_get(out, i);
if (tok->tp < MIN_ANNOTATION || tok->tp > MAX_ANNOTATION) {
first_nonannotation = i;
break;
}
}
if (first_nonannotation < 0) {
log_warn(LD_DIR, "parse error: item contains only annotations");
return -1;
}
for (i=first_nonannotation; i < smartlist_len(out); ++i) {
tok = smartlist_get(out, i);
if (tok->tp >= MIN_ANNOTATION && tok->tp <= MAX_ANNOTATION) {
log_warn(LD_DIR, "parse error: Annotations mixed with keywords");
return -1;
}
}
if ((flags & TS_NO_NEW_ANNOTATIONS)) {
if (first_nonannotation != prev_len) {
log_warn(LD_DIR, "parse error: Unexpected annotations.");
return -1;
}
}
} else {
for (i=0; i < smartlist_len(out); ++i) {
tok = smartlist_get(out, i);
if (tok->tp >= MIN_ANNOTATION && tok->tp <= MAX_ANNOTATION) {
log_warn(LD_DIR, "parse error: no annotations allowed.");
return -1;
}
}
first_nonannotation = 0;
}
for (i = 0; table[i].t; ++i) {
if (counts[table[i].v] < table[i].min_cnt) {
log_warn(LD_DIR, "Parse error: missing %s element.", table[i].t);
return -1;
}
if (counts[table[i].v] > table[i].max_cnt) {
log_warn(LD_DIR, "Parse error: too many %s elements.", table[i].t);
return -1;
}
if (table[i].pos & AT_START) {
if (smartlist_len(out) < 1 ||
(tok = smartlist_get(out, first_nonannotation))->tp != table[i].v) {
log_warn(LD_DIR, "Parse error: first item is not %s.", table[i].t);
return -1;
}
}
if (table[i].pos & AT_END) {
if (smartlist_len(out) < 1 ||
(tok = smartlist_get(out, smartlist_len(out)-1))->tp != table[i].v) {
log_warn(LD_DIR, "Parse error: last item is not %s.", table[i].t);
return -1;
}
}
}
return 0;
}
/** Helper: parse space-separated arguments from the string <b>s</b> ending at
* <b>eol</b>, and store them in the args field of <b>tok</b>. Store the
* number of parsed elements into the n_args field of <b>tok</b>. Allocate
* all storage in <b>area</b>. Return the number of arguments parsed, or
* return -1 if there was an insanely high number of arguments. */
static inline int
get_token_arguments(memarea_t *area, directory_token_t *tok,
const char *s, const char *eol)
{
/** Largest number of arguments we'll accept to any token, ever. */
#define MAX_ARGS 512
char *mem = memarea_strndup(area, s, eol-s);
char *cp = mem;
int j = 0;
char *args[MAX_ARGS];
while (*cp) {
if (j == MAX_ARGS)
return -1;
args[j++] = cp;
cp = (char*)find_whitespace(cp);
if (!cp || !*cp)
break; /* End of the line. */
*cp++ = '\0';
cp = (char*)eat_whitespace(cp);
}
tok->n_args = j;
tok->args = memarea_memdup(area, args, j*sizeof(char*));
return j;
#undef MAX_ARGS
}
/** Helper: make sure that the token <b>tok</b> with keyword <b>kwd</b> obeys
* the object syntax of <b>o_syn</b>. Allocate all storage in <b>area</b>.
* Return <b>tok</b> on success, or a new ERR_ token if the token didn't
* conform to the syntax we wanted.
**/
static inline directory_token_t *
token_check_object(memarea_t *area, const char *kwd,
directory_token_t *tok, obj_syntax o_syn)
{
char ebuf[128];
switch (o_syn) {
case NO_OBJ:
/* No object is allowed for this token. */
if (tok->object_body) {
tor_snprintf(ebuf, sizeof(ebuf), "Unexpected object for %s", kwd);
RET_ERR(ebuf);
}
if (tok->key) {
tor_snprintf(ebuf, sizeof(ebuf), "Unexpected public key for %s", kwd);
RET_ERR(ebuf);
}
break;
case NEED_OBJ:
/* There must be a (non-key) object. */
if (!tok->object_body) {
tor_snprintf(ebuf, sizeof(ebuf), "Missing object for %s", kwd);
RET_ERR(ebuf);
}
break;
case NEED_KEY_1024: /* There must be a 1024-bit public key. */
case NEED_SKEY_1024: /* There must be a 1024-bit private key. */
if (tok->key && crypto_pk_num_bits(tok->key) != PK_BYTES*8) {
tor_snprintf(ebuf, sizeof(ebuf), "Wrong size on key for %s: %d bits",
kwd, crypto_pk_num_bits(tok->key));
RET_ERR(ebuf);
}
/* fall through */
case NEED_KEY: /* There must be some kind of key. */
if (!tok->key) {
tor_snprintf(ebuf, sizeof(ebuf), "Missing public key for %s", kwd);
RET_ERR(ebuf);
}
if (o_syn != NEED_SKEY_1024) {
if (crypto_pk_key_is_private(tok->key)) {
tor_snprintf(ebuf, sizeof(ebuf),
"Private key given for %s, which wants a public key", kwd);
RET_ERR(ebuf);
}
} else { /* o_syn == NEED_SKEY_1024 */
if (!crypto_pk_key_is_private(tok->key)) {
tor_snprintf(ebuf, sizeof(ebuf),
"Public key given for %s, which wants a private key", kwd);
RET_ERR(ebuf);
}
}
break;
case OBJ_OK:
/* Anything goes with this token. */
break;
}
done_tokenizing:
return tok;
}
/** Helper function: read the next token from *s, advance *s to the end of the
* token, and return the parsed token. Parse *<b>s</b> according to the list
* of tokens in <b>table</b>.
*/
directory_token_t *
get_next_token(memarea_t *area,
const char **s, const char *eos, token_rule_t *table)
{
/** Reject any object at least this big; it is probably an overflow, an
* attack, a bug, or some other nonsense. */
#define MAX_UNPARSED_OBJECT_SIZE (128*1024)
/** Reject any line at least this big; it is probably an overflow, an
* attack, a bug, or some other nonsense. */
#define MAX_LINE_LENGTH (128*1024)
const char *next, *eol, *obstart;
size_t obname_len;
int i;
directory_token_t *tok;
obj_syntax o_syn = NO_OBJ;
char ebuf[128];
const char *kwd = "";
tor_assert(area);
tok = ALLOC_ZERO(sizeof(directory_token_t));
tok->tp = ERR_;
/* Set *s to first token, eol to end-of-line, next to after first token */
*s = eat_whitespace_eos(*s, eos); /* eat multi-line whitespace */
tor_assert(eos >= *s);
eol = memchr(*s, '\n', eos-*s);
if (!eol)
eol = eos;
if (eol - *s > MAX_LINE_LENGTH) {
RET_ERR("Line far too long");
}
next = find_whitespace_eos(*s, eol);
if (!strcmp_len(*s, "opt", next-*s)) {
/* Skip past an "opt" at the start of the line. */
*s = eat_whitespace_eos_no_nl(next, eol);
next = find_whitespace_eos(*s, eol);
} else if (*s == eos) { /* If no "opt", and end-of-line, line is invalid */
RET_ERR("Unexpected EOF");
}
/* Search the table for the appropriate entry. (I tried a binary search
* instead, but it wasn't any faster.) */
for (i = 0; table[i].t ; ++i) {
if (!strcmp_len(*s, table[i].t, next-*s)) {
/* We've found the keyword. */
kwd = table[i].t;
tok->tp = table[i].v;
o_syn = table[i].os;
*s = eat_whitespace_eos_no_nl(next, eol);
/* We go ahead whether there are arguments or not, so that tok->args is
* always set if we want arguments. */
if (table[i].concat_args) {
/* The keyword takes the line as a single argument */
tok->args = ALLOC(sizeof(char*));
tok->args[0] = STRNDUP(*s,eol-*s); /* Grab everything on line */
tok->n_args = 1;
} else {
/* This keyword takes multiple arguments. */
if (get_token_arguments(area, tok, *s, eol)<0) {
tor_snprintf(ebuf, sizeof(ebuf),"Far too many arguments to %s", kwd);
RET_ERR(ebuf);
}
*s = eol;
}
if (tok->n_args < table[i].min_args) {
tor_snprintf(ebuf, sizeof(ebuf), "Too few arguments to %s", kwd);
RET_ERR(ebuf);
} else if (tok->n_args > table[i].max_args) {
tor_snprintf(ebuf, sizeof(ebuf), "Too many arguments to %s", kwd);
RET_ERR(ebuf);
}
break;
}
}
if (tok->tp == ERR_) {
/* No keyword matched; call it an "K_opt" or "A_unrecognized" */
if (**s == '@')
tok->tp = A_UNKNOWN_;
else
tok->tp = K_OPT;
tok->args = ALLOC(sizeof(char*));
tok->args[0] = STRNDUP(*s, eol-*s);
tok->n_args = 1;
o_syn = OBJ_OK;
}
/* Check whether there's an object present */
*s = eat_whitespace_eos(eol, eos); /* Scan from end of first line */
tor_assert(eos >= *s);
eol = memchr(*s, '\n', eos-*s);
if (!eol || eol-*s<11 || strcmpstart(*s, "-----BEGIN ")) /* No object. */
goto check_object;
obstart = *s; /* Set obstart to start of object spec */
if (*s+16 >= eol || memchr(*s+11,'\0',eol-*s-16) || /* no short lines, */
strcmp_len(eol-5, "-----", 5) || /* nuls or invalid endings */
(eol-*s) > MAX_UNPARSED_OBJECT_SIZE) { /* name too long */
RET_ERR("Malformed object: bad begin line");
}
tok->object_type = STRNDUP(*s+11, eol-*s-16);
obname_len = eol-*s-16; /* store objname length here to avoid a strlen() */
*s = eol+1; /* Set *s to possible start of object data (could be eos) */
/* Go to the end of the object */
next = tor_memstr(*s, eos-*s, "-----END ");
if (!next) {
RET_ERR("Malformed object: missing object end line");
}
tor_assert(eos >= next);
eol = memchr(next, '\n', eos-next);
if (!eol) /* end-of-line marker, or eos if there's no '\n' */
eol = eos;
/* Validate the ending tag, which should be 9 + NAME + 5 + eol */
if ((size_t)(eol-next) != 9+obname_len+5 ||
strcmp_len(next+9, tok->object_type, obname_len) ||
strcmp_len(eol-5, "-----", 5)) {
tor_snprintf(ebuf, sizeof(ebuf), "Malformed object: mismatched end tag %s",
tok->object_type);
ebuf[sizeof(ebuf)-1] = '\0';
RET_ERR(ebuf);
}
if (next - *s > MAX_UNPARSED_OBJECT_SIZE)
RET_ERR("Couldn't parse object: missing footer or object much too big.");
if (!strcmp(tok->object_type, "RSA PUBLIC KEY")) { /* If it's a public key */
tok->key = crypto_pk_new();
if (crypto_pk_read_public_key_from_string(tok->key, obstart, eol-obstart))
RET_ERR("Couldn't parse public key.");
} else if (!strcmp(tok->object_type, "RSA PRIVATE KEY")) { /* private key */
tok->key = crypto_pk_new();
if (crypto_pk_read_private_key_from_string(tok->key, obstart, eol-obstart))
RET_ERR("Couldn't parse private key.");
} else { /* If it's something else, try to base64-decode it */
int r;
tok->object_body = ALLOC(next-*s); /* really, this is too much RAM. */
r = base64_decode(tok->object_body, next-*s, *s, next-*s);
if (r<0)
RET_ERR("Malformed object: bad base64-encoded data");
tok->object_size = r;
}
*s = eol;
check_object:
tok = token_check_object(area, kwd, tok, o_syn);
done_tokenizing:
return tok;
#undef RET_ERR
#undef ALLOC
#undef ALLOC_ZERO
#undef STRDUP
#undef STRNDUP
}
/** Find the first token in <b>s</b> whose keyword is <b>keyword</b>; fail
* with an assert if no such keyword is found.
*/
directory_token_t *
find_by_keyword_(smartlist_t *s, directory_keyword keyword,
const char *keyword_as_string)
{
directory_token_t *tok = find_opt_by_keyword(s, keyword);
if (PREDICT_UNLIKELY(!tok)) {
log_err(LD_BUG, "Missing %s [%d] in directory object that should have "
"been validated. Internal error.", keyword_as_string, (int)keyword);
tor_assert(tok);
}
return tok;
}
/** Find the first token in <b>s</b> whose keyword is <b>keyword</b>; return
* NULL if no such keyword is found.
*/
directory_token_t *
find_opt_by_keyword(smartlist_t *s, directory_keyword keyword)
{
SMARTLIST_FOREACH(s, directory_token_t *, t, if (t->tp == keyword) return t);
return NULL;
}
/** If there are any directory_token_t entries in <b>s</b> whose keyword is
* <b>k</b>, return a newly allocated smartlist_t containing all such entries,
* in the same order in which they occur in <b>s</b>. Otherwise return
* NULL. */
smartlist_t *
find_all_by_keyword(smartlist_t *s, directory_keyword k)
{
smartlist_t *out = NULL;
SMARTLIST_FOREACH(s, directory_token_t *, t,
if (t->tp == k) {
if (!out)
out = smartlist_new();
smartlist_add(out, t);
});
return out;
}

314
src/or/parsecommon.h Normal file
View File

@ -0,0 +1,314 @@
/* Copyright (c) 2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file parsecommon.h
* \brief Header file for parsecommon.c
**/
#ifndef TOR_PARSECOMMON_H
#define TOR_PARSECOMMON_H
#include "container.h"
#include "crypto.h"
#include "memarea.h"
/** Enumeration of possible token types. The ones starting with K_ correspond
* to directory 'keywords'. A_ is for an annotation, R or C is related to
* hidden services, ERR_ is an error in the tokenizing process, EOF_ is an
* end-of-file marker, and NIL_ is used to encode not-a-token.
*/
typedef enum {
K_ACCEPT = 0,
K_ACCEPT6,
K_DIRECTORY_SIGNATURE,
K_RECOMMENDED_SOFTWARE,
K_REJECT,
K_REJECT6,
K_ROUTER,
K_SIGNED_DIRECTORY,
K_SIGNING_KEY,
K_ONION_KEY,
K_ONION_KEY_NTOR,
K_ROUTER_SIGNATURE,
K_PUBLISHED,
K_RUNNING_ROUTERS,
K_ROUTER_STATUS,
K_PLATFORM,
K_PROTO,
K_OPT,
K_BANDWIDTH,
K_CONTACT,
K_NETWORK_STATUS,
K_UPTIME,
K_DIR_SIGNING_KEY,
K_FAMILY,
K_FINGERPRINT,
K_HIBERNATING,
K_READ_HISTORY,
K_WRITE_HISTORY,
K_NETWORK_STATUS_VERSION,
K_DIR_SOURCE,
K_DIR_OPTIONS,
K_CLIENT_VERSIONS,
K_SERVER_VERSIONS,
K_RECOMMENDED_CLIENT_PROTOCOLS,
K_RECOMMENDED_RELAY_PROTOCOLS,
K_REQUIRED_CLIENT_PROTOCOLS,
K_REQUIRED_RELAY_PROTOCOLS,
K_OR_ADDRESS,
K_ID,
K_P,
K_P6,
K_R,
K_A,
K_S,
K_V,
K_W,
K_M,
K_EXTRA_INFO,
K_EXTRA_INFO_DIGEST,
K_CACHES_EXTRA_INFO,
K_HIDDEN_SERVICE_DIR,
K_ALLOW_SINGLE_HOP_EXITS,
K_IPV6_POLICY,
K_ROUTER_SIG_ED25519,
K_IDENTITY_ED25519,
K_MASTER_KEY_ED25519,
K_ONION_KEY_CROSSCERT,
K_NTOR_ONION_KEY_CROSSCERT,
K_DIRREQ_END,
K_DIRREQ_V2_IPS,
K_DIRREQ_V3_IPS,
K_DIRREQ_V2_REQS,
K_DIRREQ_V3_REQS,
K_DIRREQ_V2_SHARE,
K_DIRREQ_V3_SHARE,
K_DIRREQ_V2_RESP,
K_DIRREQ_V3_RESP,
K_DIRREQ_V2_DIR,
K_DIRREQ_V3_DIR,
K_DIRREQ_V2_TUN,
K_DIRREQ_V3_TUN,
K_ENTRY_END,
K_ENTRY_IPS,
K_CELL_END,
K_CELL_PROCESSED,
K_CELL_QUEUED,
K_CELL_TIME,
K_CELL_CIRCS,
K_EXIT_END,
K_EXIT_WRITTEN,
K_EXIT_READ,
K_EXIT_OPENED,
K_DIR_KEY_CERTIFICATE_VERSION,
K_DIR_IDENTITY_KEY,
K_DIR_KEY_PUBLISHED,
K_DIR_KEY_EXPIRES,
K_DIR_KEY_CERTIFICATION,
K_DIR_KEY_CROSSCERT,
K_DIR_ADDRESS,
K_DIR_TUNNELLED,
K_VOTE_STATUS,
K_VALID_AFTER,
K_FRESH_UNTIL,
K_VALID_UNTIL,
K_VOTING_DELAY,
K_KNOWN_FLAGS,
K_PARAMS,
K_BW_WEIGHTS,
K_VOTE_DIGEST,
K_CONSENSUS_DIGEST,
K_ADDITIONAL_DIGEST,
K_ADDITIONAL_SIGNATURE,
K_CONSENSUS_METHODS,
K_CONSENSUS_METHOD,
K_LEGACY_DIR_KEY,
K_DIRECTORY_FOOTER,
K_SIGNING_CERT_ED,
K_SR_FLAG,
K_COMMIT,
K_PREVIOUS_SRV,
K_CURRENT_SRV,
K_PACKAGE,
A_PURPOSE,
A_LAST_LISTED,
A_UNKNOWN_,
R_RENDEZVOUS_SERVICE_DESCRIPTOR,
R_VERSION,
R_PERMANENT_KEY,
R_SECRET_ID_PART,
R_PUBLICATION_TIME,
R_PROTOCOL_VERSIONS,
R_INTRODUCTION_POINTS,
R_SIGNATURE,
R_HS_DESCRIPTOR, /* From version 3, this MUST be generic to all future
descriptor versions thus making it R_. */
R3_DESC_LIFETIME,
R3_DESC_SIGNING_CERT,
R3_REVISION_COUNTER,
R3_ENCRYPTED,
R3_SIGNATURE,
R3_CREATE2_FORMATS,
R3_AUTHENTICATION_REQUIRED,
R3_INTRODUCTION_POINT,
R3_INTRO_AUTH_KEY,
R3_INTRO_ENC_KEY,
R3_INTRO_ENC_KEY_CERTIFICATION,
R_IPO_IDENTIFIER,
R_IPO_IP_ADDRESS,
R_IPO_ONION_PORT,
R_IPO_ONION_KEY,
R_IPO_SERVICE_KEY,
C_CLIENT_NAME,
C_DESCRIPTOR_COOKIE,
C_CLIENT_KEY,
ERR_,
EOF_,
NIL_
} directory_keyword;
/** Structure to hold a single directory token.
*
* We parse a directory by breaking it into "tokens", each consisting
* of a keyword, a line full of arguments, and a binary object. The
* arguments and object are both optional, depending on the keyword
* type.
*
* This structure is only allocated in memareas; do not allocate it on
* the heap, or token_clear() won't work.
*/
typedef struct directory_token_t {
directory_keyword tp; /**< Type of the token. */
int n_args:30; /**< Number of elements in args */
char **args; /**< Array of arguments from keyword line. */
char *object_type; /**< -----BEGIN [object_type]-----*/
size_t object_size; /**< Bytes in object_body */
char *object_body; /**< Contents of object, base64-decoded. */
crypto_pk_t *key; /**< For public keys only. Heap-allocated. */
char *error; /**< For ERR_ tokens only. */
} directory_token_t;
/** We use a table of rules to decide how to parse each token type. */
/** Rules for whether the keyword needs an object. */
typedef enum {
NO_OBJ, /**< No object, ever. */
NEED_OBJ, /**< Object is required. */
NEED_SKEY_1024,/**< Object is required, and must be a 1024 bit private key */
NEED_KEY_1024, /**< Object is required, and must be a 1024 bit public key */
NEED_KEY, /**< Object is required, and must be a public key. */
OBJ_OK, /**< Object is optional. */
} obj_syntax;
#define AT_START 1
#define AT_END 2
#define TS_ANNOTATIONS_OK 1
#define TS_NOCHECK 2
#define TS_NO_NEW_ANNOTATIONS 4
/**
* @name macros for defining token rules
*
* Helper macros to define token tables. 's' is a string, 't' is a
* directory_keyword, 'a' is a trio of argument multiplicities, and 'o' is an
* object syntax.
*/
/**@{*/
/** Appears to indicate the end of a table. */
#define END_OF_TABLE { NULL, NIL_, 0,0,0, NO_OBJ, 0, INT_MAX, 0, 0 }
/** An item with no restrictions: used for obsolete document types */
#define T(s,t,a,o) { s, t, a, o, 0, INT_MAX, 0, 0 }
/** An item with no restrictions on multiplicity or location. */
#define T0N(s,t,a,o) { s, t, a, o, 0, INT_MAX, 0, 0 }
/** An item that must appear exactly once */
#define T1(s,t,a,o) { s, t, a, o, 1, 1, 0, 0 }
/** An item that must appear exactly once, at the start of the document */
#define T1_START(s,t,a,o) { s, t, a, o, 1, 1, AT_START, 0 }
/** An item that must appear exactly once, at the end of the document */
#define T1_END(s,t,a,o) { s, t, a, o, 1, 1, AT_END, 0 }
/** An item that must appear one or more times */
#define T1N(s,t,a,o) { s, t, a, o, 1, INT_MAX, 0, 0 }
/** An item that must appear no more than once */
#define T01(s,t,a,o) { s, t, a, o, 0, 1, 0, 0 }
/** An annotation that must appear no more than once */
#define A01(s,t,a,o) { s, t, a, o, 0, 1, 0, 1 }
/** Argument multiplicity: any number of arguments. */
#define ARGS 0,INT_MAX,0
/** Argument multiplicity: no arguments. */
#define NO_ARGS 0,0,0
/** Argument multiplicity: concatenate all arguments. */
#define CONCAT_ARGS 1,1,1
/** Argument multiplicity: at least <b>n</b> arguments. */
#define GE(n) n,INT_MAX,0
/** Argument multiplicity: exactly <b>n</b> arguments. */
#define EQ(n) n,n,0
/**@}*/
/** Determines the parsing rules for a single token type. */
typedef struct token_rule_t {
/** The string value of the keyword identifying the type of item. */
const char *t;
/** The corresponding directory_keyword enum. */
directory_keyword v;
/** Minimum number of arguments for this item */
int min_args;
/** Maximum number of arguments for this item */
int max_args;
/** If true, we concatenate all arguments for this item into a single
* string. */
int concat_args;
/** Requirements on object syntax for this item. */
obj_syntax os;
/** Lowest number of times this item may appear in a document. */
int min_cnt;
/** Highest number of times this item may appear in a document. */
int max_cnt;
/** One or more of AT_START/AT_END to limit where the item may appear in a
* document. */
int pos;
/** True iff this token is an annotation. */
int is_annotation;
} token_rule_t;
void token_clear(directory_token_t *tok);
int tokenize_string(memarea_t *area,
const char *start, const char *end,
smartlist_t *out,
token_rule_t *table,
int flags);
directory_token_t *get_next_token(memarea_t *area,
const char **s,
const char *eos,
token_rule_t *table);
directory_token_t *find_by_keyword_(smartlist_t *s,
directory_keyword keyword,
const char *keyword_str);
#define find_by_keyword(s, keyword) \
find_by_keyword_((s), (keyword), #keyword)
directory_token_t *find_opt_by_keyword(smartlist_t *s,
directory_keyword keyword);
smartlist_t * find_all_by_keyword(smartlist_t *s, directory_keyword k);
#endif /* TOR_PARSECOMMON_H */

View File

@ -60,6 +60,7 @@
#include "connection_or.h" #include "connection_or.h"
#include "control.h" #include "control.h"
#include "geoip.h" #include "geoip.h"
#include "hs_cache.h"
#include "main.h" #include "main.h"
#include "networkstatus.h" #include "networkstatus.h"
#include "nodelist.h" #include "nodelist.h"
@ -2439,9 +2440,7 @@ cell_queues_check_size(void)
if (rend_cache_total > get_options()->MaxMemInQueues / 5) { if (rend_cache_total > get_options()->MaxMemInQueues / 5) {
const size_t bytes_to_remove = const size_t bytes_to_remove =
rend_cache_total - (size_t)(get_options()->MaxMemInQueues / 10); rend_cache_total - (size_t)(get_options()->MaxMemInQueues / 10);
rend_cache_clean_v2_descs_as_dir(time(NULL), bytes_to_remove); alloc -= hs_cache_handle_oom(time(NULL), bytes_to_remove);
alloc -= rend_cache_total;
alloc += rend_cache_get_total_allocation();
} }
circuits_handle_oom(alloc); circuits_handle_oom(alloc);
return 1; return 1;

View File

@ -86,7 +86,7 @@ rend_cache_get_total_allocation(void)
} }
/** Decrement the total bytes attributed to the rendezvous cache by n. */ /** Decrement the total bytes attributed to the rendezvous cache by n. */
STATIC void void
rend_cache_decrement_allocation(size_t n) rend_cache_decrement_allocation(size_t n)
{ {
static int have_underflowed = 0; static int have_underflowed = 0;
@ -103,7 +103,7 @@ rend_cache_decrement_allocation(size_t n)
} }
/** Increase the total bytes attributed to the rendezvous cache by n. */ /** Increase the total bytes attributed to the rendezvous cache by n. */
STATIC void void
rend_cache_increment_allocation(size_t n) rend_cache_increment_allocation(size_t n)
{ {
static int have_overflowed = 0; static int have_overflowed = 0;
@ -462,19 +462,15 @@ rend_cache_intro_failure_note(rend_intro_point_failure_t failure,
} }
/** Remove all old v2 descriptors and those for which this hidden service /** Remove all old v2 descriptors and those for which this hidden service
* directory is not responsible for any more. * directory is not responsible for any more. The cutoff is the time limit for
* * which we want to keep the cache entry. In other words, any entry created
* If at all possible, remove at least <b>force_remove</b> bytes of data. * before will be removed. */
*/ size_t
void rend_cache_clean_v2_descs_as_dir(time_t cutoff)
rend_cache_clean_v2_descs_as_dir(time_t now, size_t force_remove)
{ {
digestmap_iter_t *iter; digestmap_iter_t *iter;
time_t cutoff = now - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW;
const int LAST_SERVED_CUTOFF_STEP = 1800;
time_t last_served_cutoff = cutoff;
size_t bytes_removed = 0; size_t bytes_removed = 0;
do {
for (iter = digestmap_iter_init(rend_cache_v2_dir); for (iter = digestmap_iter_init(rend_cache_v2_dir);
!digestmap_iter_done(iter); ) { !digestmap_iter_done(iter); ) {
const char *key; const char *key;
@ -482,8 +478,7 @@ rend_cache_clean_v2_descs_as_dir(time_t now, size_t force_remove)
rend_cache_entry_t *ent; rend_cache_entry_t *ent;
digestmap_iter_get(iter, &key, &val); digestmap_iter_get(iter, &key, &val);
ent = val; ent = val;
if (ent->parsed->timestamp < cutoff || if (ent->parsed->timestamp < cutoff) {
ent->last_served < last_served_cutoff) {
char key_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; char key_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
base32_encode(key_base32, sizeof(key_base32), key, DIGEST_LEN); base32_encode(key_base32, sizeof(key_base32), key, DIGEST_LEN);
log_info(LD_REND, "Removing descriptor with ID '%s' from cache", log_info(LD_REND, "Removing descriptor with ID '%s' from cache",
@ -496,11 +491,7 @@ rend_cache_clean_v2_descs_as_dir(time_t now, size_t force_remove)
} }
} }
/* In case we didn't remove enough bytes, advance the cutoff a little. */ return bytes_removed;
last_served_cutoff += LAST_SERVED_CUTOFF_STEP;
if (last_served_cutoff > now)
break;
} while (bytes_removed < force_remove);
} }
/** Lookup in the client cache the given service ID <b>query</b> for /** Lookup in the client cache the given service ID <b>query</b> for
@ -849,6 +840,8 @@ rend_cache_store_v2_desc_as_client(const char *desc,
char want_desc_id[DIGEST_LEN]; char want_desc_id[DIGEST_LEN];
rend_cache_entry_t *e; rend_cache_entry_t *e;
int retval = -1; int retval = -1;
rend_data_v2_t *rend_data = TO_REND_DATA_V2(rend_query);
tor_assert(rend_cache); tor_assert(rend_cache);
tor_assert(desc); tor_assert(desc);
tor_assert(desc_id_base32); tor_assert(desc_id_base32);
@ -874,11 +867,11 @@ rend_cache_store_v2_desc_as_client(const char *desc,
log_warn(LD_REND, "Couldn't compute service ID."); log_warn(LD_REND, "Couldn't compute service ID.");
goto err; goto err;
} }
if (rend_query->onion_address[0] != '\0' && if (rend_data->onion_address[0] != '\0' &&
strcmp(rend_query->onion_address, service_id)) { strcmp(rend_data->onion_address, service_id)) {
log_warn(LD_REND, "Received service descriptor for service ID %s; " log_warn(LD_REND, "Received service descriptor for service ID %s; "
"expected descriptor for service ID %s.", "expected descriptor for service ID %s.",
service_id, safe_str(rend_query->onion_address)); service_id, safe_str(rend_data->onion_address));
goto err; goto err;
} }
if (tor_memneq(desc_id, want_desc_id, DIGEST_LEN)) { if (tor_memneq(desc_id, want_desc_id, DIGEST_LEN)) {
@ -890,14 +883,14 @@ rend_cache_store_v2_desc_as_client(const char *desc,
/* Decode/decrypt introduction points. */ /* Decode/decrypt introduction points. */
if (intro_content && intro_size > 0) { if (intro_content && intro_size > 0) {
int n_intro_points; int n_intro_points;
if (rend_query->auth_type != REND_NO_AUTH && if (rend_data->auth_type != REND_NO_AUTH &&
!tor_mem_is_zero(rend_query->descriptor_cookie, !tor_mem_is_zero(rend_data->descriptor_cookie,
sizeof(rend_query->descriptor_cookie))) { sizeof(rend_data->descriptor_cookie))) {
char *ipos_decrypted = NULL; char *ipos_decrypted = NULL;
size_t ipos_decrypted_size; size_t ipos_decrypted_size;
if (rend_decrypt_introduction_points(&ipos_decrypted, if (rend_decrypt_introduction_points(&ipos_decrypted,
&ipos_decrypted_size, &ipos_decrypted_size,
rend_query->descriptor_cookie, rend_data->descriptor_cookie,
intro_content, intro_content,
intro_size) < 0) { intro_size) < 0) {
log_warn(LD_REND, "Failed to decrypt introduction points. We are " log_warn(LD_REND, "Failed to decrypt introduction points. We are "

View File

@ -53,10 +53,17 @@ typedef enum {
REND_CACHE_TYPE_SERVICE = 2, REND_CACHE_TYPE_SERVICE = 2,
} rend_cache_type_t; } rend_cache_type_t;
/* Return maximum lifetime in seconds of a cache entry. */
static inline time_t
rend_cache_max_entry_lifetime(void)
{
return REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW;
}
void rend_cache_init(void); void rend_cache_init(void);
void rend_cache_clean(time_t now, rend_cache_type_t cache_type); void rend_cache_clean(time_t now, rend_cache_type_t cache_type);
void rend_cache_failure_clean(time_t now); void rend_cache_failure_clean(time_t now);
void rend_cache_clean_v2_descs_as_dir(time_t now, size_t min_to_remove); size_t rend_cache_clean_v2_descs_as_dir(time_t cutoff);
void rend_cache_purge(void); void rend_cache_purge(void);
void rend_cache_free_all(void); void rend_cache_free_all(void);
int rend_cache_lookup_entry(const char *query, int version, int rend_cache_lookup_entry(const char *query, int version,
@ -77,6 +84,8 @@ void rend_cache_intro_failure_note(rend_intro_point_failure_t failure,
const uint8_t *identity, const uint8_t *identity,
const char *service_id); const char *service_id);
void rend_cache_failure_purge(void); void rend_cache_failure_purge(void);
void rend_cache_decrement_allocation(size_t n);
void rend_cache_increment_allocation(size_t n);
#ifdef RENDCACHE_PRIVATE #ifdef RENDCACHE_PRIVATE
@ -89,8 +98,6 @@ STATIC int cache_failure_intro_lookup(const uint8_t *identity,
const char *service_id, const char *service_id,
rend_cache_failure_intro_t rend_cache_failure_intro_t
**intro_entry); **intro_entry);
STATIC void rend_cache_decrement_allocation(size_t n);
STATIC void rend_cache_increment_allocation(size_t n);
STATIC rend_cache_failure_intro_t *rend_cache_failure_intro_entry_new( STATIC rend_cache_failure_intro_t *rend_cache_failure_intro_entry_new(
rend_intro_point_failure_t failure); rend_intro_point_failure_t failure);
STATIC rend_cache_failure_t *rend_cache_failure_entry_new(void); STATIC rend_cache_failure_t *rend_cache_failure_entry_new(void);

View File

@ -16,6 +16,7 @@
#include "connection.h" #include "connection.h"
#include "connection_edge.h" #include "connection_edge.h"
#include "directory.h" #include "directory.h"
#include "hs_common.h"
#include "main.h" #include "main.h"
#include "networkstatus.h" #include "networkstatus.h"
#include "nodelist.h" #include "nodelist.h"
@ -104,7 +105,7 @@ rend_client_reextend_intro_circuit(origin_circuit_t *circ)
if (!extend_info) { if (!extend_info) {
log_warn(LD_REND, log_warn(LD_REND,
"No usable introduction points left for %s. Closing.", "No usable introduction points left for %s. Closing.",
safe_str_client(circ->rend_data->onion_address)); safe_str_client(rend_data_get_address(circ->rend_data)));
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
return -1; return -1;
} }
@ -144,18 +145,19 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
off_t dh_offset; off_t dh_offset;
crypto_pk_t *intro_key = NULL; crypto_pk_t *intro_key = NULL;
int status = 0; int status = 0;
const char *onion_address;
tor_assert(introcirc->base_.purpose == CIRCUIT_PURPOSE_C_INTRODUCING); tor_assert(introcirc->base_.purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
tor_assert(rendcirc->base_.purpose == CIRCUIT_PURPOSE_C_REND_READY); tor_assert(rendcirc->base_.purpose == CIRCUIT_PURPOSE_C_REND_READY);
tor_assert(introcirc->rend_data); tor_assert(introcirc->rend_data);
tor_assert(rendcirc->rend_data); tor_assert(rendcirc->rend_data);
tor_assert(!rend_cmp_service_ids(introcirc->rend_data->onion_address, tor_assert(!rend_cmp_service_ids(rend_data_get_address(introcirc->rend_data),
rendcirc->rend_data->onion_address)); rend_data_get_address(rendcirc->rend_data)));
assert_circ_anonymity_ok(introcirc, options); assert_circ_anonymity_ok(introcirc, options);
assert_circ_anonymity_ok(rendcirc, options); assert_circ_anonymity_ok(rendcirc, options);
onion_address = rend_data_get_address(introcirc->rend_data);
r = rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1, r = rend_cache_lookup_entry(onion_address, -1, &entry);
&entry);
/* An invalid onion address is not possible else we have a big issue. */ /* An invalid onion address is not possible else we have a big issue. */
tor_assert(r != -EINVAL); tor_assert(r != -EINVAL);
if (r < 0 || !rend_client_any_intro_points_usable(entry)) { if (r < 0 || !rend_client_any_intro_points_usable(entry)) {
@ -164,14 +166,13 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
log_info(LD_REND, log_info(LD_REND,
"query %s didn't have valid rend desc in cache. " "query %s didn't have valid rend desc in cache. "
"Refetching descriptor.", "Refetching descriptor.",
safe_str_client(introcirc->rend_data->onion_address)); safe_str_client(onion_address));
rend_client_refetch_v2_renddesc(introcirc->rend_data); rend_client_refetch_v2_renddesc(introcirc->rend_data);
{ {
connection_t *conn; connection_t *conn;
while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP, while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP,
AP_CONN_STATE_CIRCUIT_WAIT, AP_CONN_STATE_CIRCUIT_WAIT, onion_address))) {
introcirc->rend_data->onion_address))) {
connection_ap_mark_as_non_pending_circuit(TO_ENTRY_CONN(conn)); connection_ap_mark_as_non_pending_circuit(TO_ENTRY_CONN(conn));
conn->state = AP_CONN_STATE_RENDDESC_WAIT; conn->state = AP_CONN_STATE_RENDDESC_WAIT;
} }
@ -195,7 +196,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
log_info(LD_REND, "Could not find intro key for %s at %s; we " log_info(LD_REND, "Could not find intro key for %s at %s; we "
"have a v2 rend desc with %d intro points. " "have a v2 rend desc with %d intro points. "
"Trying a different intro point...", "Trying a different intro point...",
safe_str_client(introcirc->rend_data->onion_address), safe_str_client(onion_address),
safe_str_client(extend_info_describe( safe_str_client(extend_info_describe(
introcirc->build_state->chosen_exit)), introcirc->build_state->chosen_exit)),
smartlist_len(entry->parsed->intro_nodes)); smartlist_len(entry->parsed->intro_nodes));
@ -235,11 +236,12 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
/* If version is 3, write (optional) auth data and timestamp. */ /* If version is 3, write (optional) auth data and timestamp. */
if (entry->parsed->protocols & (1<<3)) { if (entry->parsed->protocols & (1<<3)) {
tmp[0] = 3; /* version 3 of the cell format */ tmp[0] = 3; /* version 3 of the cell format */
tmp[1] = (uint8_t)introcirc->rend_data->auth_type; /* auth type, if any */ /* auth type, if any */
tmp[1] = (uint8_t) TO_REND_DATA_V2(introcirc->rend_data)->auth_type;
v3_shift = 1; v3_shift = 1;
if (introcirc->rend_data->auth_type != REND_NO_AUTH) { if (tmp[1] != REND_NO_AUTH) {
set_uint16(tmp+2, htons(REND_DESC_COOKIE_LEN)); set_uint16(tmp+2, htons(REND_DESC_COOKIE_LEN));
memcpy(tmp+4, introcirc->rend_data->descriptor_cookie, memcpy(tmp+4, TO_REND_DATA_V2(introcirc->rend_data)->descriptor_cookie,
REND_DESC_COOKIE_LEN); REND_DESC_COOKIE_LEN);
v3_shift += 2+REND_DESC_COOKIE_LEN; v3_shift += 2+REND_DESC_COOKIE_LEN;
} }
@ -359,7 +361,7 @@ rend_client_rendcirc_has_opened(origin_circuit_t *circ)
* Called to close other intro circuits we launched in parallel. * Called to close other intro circuits we launched in parallel.
*/ */
static void static void
rend_client_close_other_intros(const char *onion_address) rend_client_close_other_intros(const uint8_t *rend_pk_digest)
{ {
/* abort parallel intro circs, if any */ /* abort parallel intro circs, if any */
SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, c) { SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, c) {
@ -368,8 +370,7 @@ rend_client_close_other_intros(const char *onion_address)
!c->marked_for_close && CIRCUIT_IS_ORIGIN(c)) { !c->marked_for_close && CIRCUIT_IS_ORIGIN(c)) {
origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(c); origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(c);
if (oc->rend_data && if (oc->rend_data &&
!rend_cmp_service_ids(onion_address, rend_circuit_pk_digest_eq(oc, rend_pk_digest)) {
oc->rend_data->onion_address)) {
log_info(LD_REND|LD_CIRC, "Closing introduction circuit %d that we " log_info(LD_REND|LD_CIRC, "Closing introduction circuit %d that we "
"built in parallel (Purpose %d).", oc->global_identifier, "built in parallel (Purpose %d).", oc->global_identifier,
c->purpose); c->purpose);
@ -431,7 +432,8 @@ rend_client_introduction_acked(origin_circuit_t *circ,
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_FINISHED); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_FINISHED);
/* close any other intros launched in parallel */ /* close any other intros launched in parallel */
rend_client_close_other_intros(circ->rend_data->onion_address); rend_client_close_other_intros(rend_data_get_pk_digest(circ->rend_data,
NULL));
} else { } else {
/* It's a NAK; the introduction point didn't relay our request. */ /* It's a NAK; the introduction point didn't relay our request. */
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_INTRODUCING); circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_INTRODUCING);
@ -440,7 +442,7 @@ rend_client_introduction_acked(origin_circuit_t *circ,
* If none remain, refetch the service descriptor. * If none remain, refetch the service descriptor.
*/ */
log_info(LD_REND, "Got nack for %s from %s...", log_info(LD_REND, "Got nack for %s from %s...",
safe_str_client(circ->rend_data->onion_address), safe_str_client(rend_data_get_address(circ->rend_data)),
safe_str_client(extend_info_describe(circ->build_state->chosen_exit))); safe_str_client(extend_info_describe(circ->build_state->chosen_exit)));
if (rend_client_report_intro_point_failure(circ->build_state->chosen_exit, if (rend_client_report_intro_point_failure(circ->build_state->chosen_exit,
circ->rend_data, circ->rend_data,
@ -694,13 +696,15 @@ pick_hsdir(const char *desc_id, const char *desc_id_base32)
* in the case that no hidden service directory is left to ask for the * in the case that no hidden service directory is left to ask for the
* descriptor, return 0, and in case of a failure -1. */ * descriptor, return 0, and in case of a failure -1. */
static int static int
directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query, directory_get_from_hs_dir(const char *desc_id,
const rend_data_t *rend_query,
routerstatus_t *rs_hsdir) routerstatus_t *rs_hsdir)
{ {
routerstatus_t *hs_dir = rs_hsdir; routerstatus_t *hs_dir = rs_hsdir;
char *hsdir_fp; char *hsdir_fp;
char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64]; char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64];
const rend_data_v2_t *rend_data;
#ifdef ENABLE_TOR2WEB_MODE #ifdef ENABLE_TOR2WEB_MODE
const int tor2web_mode = get_options()->Tor2webMode; const int tor2web_mode = get_options()->Tor2webMode;
const int how_to_fetch = tor2web_mode ? DIRIND_ONEHOP : DIRIND_ANONYMOUS; const int how_to_fetch = tor2web_mode ? DIRIND_ONEHOP : DIRIND_ANONYMOUS;
@ -709,6 +713,8 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query,
#endif #endif
tor_assert(desc_id); tor_assert(desc_id);
tor_assert(rend_query);
rend_data = TO_REND_DATA_V2(rend_query);
base32_encode(desc_id_base32, sizeof(desc_id_base32), base32_encode(desc_id_base32, sizeof(desc_id_base32),
desc_id, DIGEST_LEN); desc_id, DIGEST_LEN);
@ -731,10 +737,11 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query,
/* Encode descriptor cookie for logging purposes. Also, if the cookie is /* Encode descriptor cookie for logging purposes. Also, if the cookie is
* malformed, no fetch is triggered thus this needs to be done before the * malformed, no fetch is triggered thus this needs to be done before the
* fetch request. */ * fetch request. */
if (rend_query->auth_type != REND_NO_AUTH) { if (rend_data->auth_type != REND_NO_AUTH) {
if (base64_encode(descriptor_cookie_base64, if (base64_encode(descriptor_cookie_base64,
sizeof(descriptor_cookie_base64), sizeof(descriptor_cookie_base64),
rend_query->descriptor_cookie, REND_DESC_COOKIE_LEN, rend_data->descriptor_cookie,
REND_DESC_COOKIE_LEN,
0)<0) { 0)<0) {
log_warn(LD_BUG, "Could not base64-encode descriptor cookie."); log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
return 0; return 0;
@ -760,9 +767,9 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query,
"service '%s' with descriptor ID '%s', auth type %d, " "service '%s' with descriptor ID '%s', auth type %d, "
"and descriptor cookie '%s' to hidden service " "and descriptor cookie '%s' to hidden service "
"directory %s", "directory %s",
rend_query->onion_address, desc_id_base32, rend_data->onion_address, desc_id_base32,
rend_query->auth_type, rend_data->auth_type,
(rend_query->auth_type == REND_NO_AUTH ? "[none]" : (rend_data->auth_type == REND_NO_AUTH ? "[none]" :
escaped_safe_str_client(descriptor_cookie_base64)), escaped_safe_str_client(descriptor_cookie_base64)),
routerstatus_describe(hs_dir)); routerstatus_describe(hs_dir));
control_event_hs_descriptor_requested(rend_query, control_event_hs_descriptor_requested(rend_query,
@ -777,8 +784,8 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query,
* On success, 1 is returned. If no hidden service is left to ask, return 0. * On success, 1 is returned. If no hidden service is left to ask, return 0.
* On error, -1 is returned. */ * On error, -1 is returned. */
static int static int
fetch_v2_desc_by_descid(const char *desc_id, const rend_data_t *rend_query, fetch_v2_desc_by_descid(const char *desc_id,
smartlist_t *hsdirs) const rend_data_t *rend_query, smartlist_t *hsdirs)
{ {
int ret; int ret;
@ -811,13 +818,12 @@ fetch_v2_desc_by_descid(const char *desc_id, const rend_data_t *rend_query,
* On success, 1 is returned. If no hidden service is left to ask, return 0. * On success, 1 is returned. If no hidden service is left to ask, return 0.
* On error, -1 is returned. */ * On error, -1 is returned. */
static int static int
fetch_v2_desc_by_addr(rend_data_t *query, smartlist_t *hsdirs) fetch_v2_desc_by_addr(rend_data_t *rend_query, smartlist_t *hsdirs)
{ {
char descriptor_id[DIGEST_LEN]; char descriptor_id[DIGEST_LEN];
int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS]; int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS];
int i, tries_left, ret; int i, tries_left, ret;
rend_data_v2_t *rend_data = TO_REND_DATA_V2(rend_query);
tor_assert(query);
/* Randomly iterate over the replicas until a descriptor can be fetched /* Randomly iterate over the replicas until a descriptor can be fetched
* from one of the consecutive nodes, or no options are left. */ * from one of the consecutive nodes, or no options are left. */
@ -831,9 +837,10 @@ fetch_v2_desc_by_addr(rend_data_t *query, smartlist_t *hsdirs)
int chosen_replica = replicas_left_to_try[rand_val]; int chosen_replica = replicas_left_to_try[rand_val];
replicas_left_to_try[rand_val] = replicas_left_to_try[--tries_left]; replicas_left_to_try[rand_val] = replicas_left_to_try[--tries_left];
ret = rend_compute_v2_desc_id(descriptor_id, query->onion_address, ret = rend_compute_v2_desc_id(descriptor_id,
query->auth_type == REND_STEALTH_AUTH ? rend_data->onion_address,
query->descriptor_cookie : NULL, rend_data->auth_type == REND_STEALTH_AUTH ?
rend_data->descriptor_cookie : NULL,
time(NULL), chosen_replica); time(NULL), chosen_replica);
if (ret < 0) { if (ret < 0) {
/* Normally, on failure the descriptor_id is untouched but let's be /* Normally, on failure the descriptor_id is untouched but let's be
@ -841,18 +848,18 @@ fetch_v2_desc_by_addr(rend_data_t *query, smartlist_t *hsdirs)
goto end; goto end;
} }
if (tor_memcmp(descriptor_id, query->descriptor_id[chosen_replica], if (tor_memcmp(descriptor_id, rend_data->descriptor_id[chosen_replica],
sizeof(descriptor_id)) != 0) { sizeof(descriptor_id)) != 0) {
/* Not equal from what we currently have so purge the last hid serv /* Not equal from what we currently have so purge the last hid serv
* request cache and update the descriptor ID with the new value. */ * request cache and update the descriptor ID with the new value. */
purge_hid_serv_from_last_hid_serv_requests( purge_hid_serv_from_last_hid_serv_requests(
query->descriptor_id[chosen_replica]); rend_data->descriptor_id[chosen_replica]);
memcpy(query->descriptor_id[chosen_replica], descriptor_id, memcpy(rend_data->descriptor_id[chosen_replica], descriptor_id,
sizeof(query->descriptor_id[chosen_replica])); sizeof(rend_data->descriptor_id[chosen_replica]));
} }
/* Trigger the fetch with the computed descriptor ID. */ /* Trigger the fetch with the computed descriptor ID. */
ret = fetch_v2_desc_by_descid(descriptor_id, query, hsdirs); ret = fetch_v2_desc_by_descid(descriptor_id, rend_query, hsdirs);
if (ret != 0) { if (ret != 0) {
/* Either on success or failure, as long as we tried a fetch we are /* Either on success or failure, as long as we tried a fetch we are
* done here. */ * done here. */
@ -880,16 +887,23 @@ int
rend_client_fetch_v2_desc(rend_data_t *query, smartlist_t *hsdirs) rend_client_fetch_v2_desc(rend_data_t *query, smartlist_t *hsdirs)
{ {
int ret; int ret;
rend_data_v2_t *rend_data;
const char *onion_address;
tor_assert(query); tor_assert(query);
/* Get the version 2 data structure of the query. */
rend_data = TO_REND_DATA_V2(query);
onion_address = rend_data_get_address(query);
/* Depending on what's available in the rend data query object, we will /* Depending on what's available in the rend data query object, we will
* trigger a fetch by HS address or using a descriptor ID. */ * trigger a fetch by HS address or using a descriptor ID. */
if (query->onion_address[0] != '\0') { if (onion_address[0] != '\0') {
ret = fetch_v2_desc_by_addr(query, hsdirs); ret = fetch_v2_desc_by_addr(query, hsdirs);
} else if (!tor_digest_is_zero(query->desc_id_fetch)) { } else if (!tor_digest_is_zero(rend_data->desc_id_fetch)) {
ret = fetch_v2_desc_by_descid(query->desc_id_fetch, query, hsdirs); ret = fetch_v2_desc_by_descid(rend_data->desc_id_fetch, query,
hsdirs);
} else { } else {
/* Query data is invalid. */ /* Query data is invalid. */
ret = -1; ret = -1;
@ -907,10 +921,11 @@ void
rend_client_refetch_v2_renddesc(rend_data_t *rend_query) rend_client_refetch_v2_renddesc(rend_data_t *rend_query)
{ {
rend_cache_entry_t *e = NULL; rend_cache_entry_t *e = NULL;
const char *onion_address = rend_data_get_address(rend_query);
tor_assert(rend_query); tor_assert(rend_query);
/* Before fetching, check if we already have a usable descriptor here. */ /* Before fetching, check if we already have a usable descriptor here. */
if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) == 0 && if (rend_cache_lookup_entry(onion_address, -1, &e) == 0 &&
rend_client_any_intro_points_usable(e)) { rend_client_any_intro_points_usable(e)) {
log_info(LD_REND, "We would fetch a v2 rendezvous descriptor, but we " log_info(LD_REND, "We would fetch a v2 rendezvous descriptor, but we "
"already have a usable descriptor here. Not fetching."); "already have a usable descriptor here. Not fetching.");
@ -923,7 +938,7 @@ rend_client_refetch_v2_renddesc(rend_data_t *rend_query)
return; return;
} }
log_debug(LD_REND, "Fetching v2 rendezvous descriptor for service %s", log_debug(LD_REND, "Fetching v2 rendezvous descriptor for service %s",
safe_str_client(rend_query->onion_address)); safe_str_client(onion_address));
rend_client_fetch_v2_desc(rend_query, NULL); rend_client_fetch_v2_desc(rend_query, NULL);
/* We don't need to look the error code because either on failure or /* We don't need to look the error code because either on failure or
@ -959,7 +974,7 @@ rend_client_cancel_descriptor_fetches(void)
} else { } else {
log_debug(LD_REND, "Marking for close dir conn fetching " log_debug(LD_REND, "Marking for close dir conn fetching "
"rendezvous descriptor for service %s", "rendezvous descriptor for service %s",
safe_str(rd->onion_address)); safe_str(rend_data_get_address(rd)));
} }
connection_mark_for_close(conn); connection_mark_for_close(conn);
} }
@ -989,25 +1004,26 @@ rend_client_cancel_descriptor_fetches(void)
*/ */
int int
rend_client_report_intro_point_failure(extend_info_t *failed_intro, rend_client_report_intro_point_failure(extend_info_t *failed_intro,
rend_data_t *rend_query, rend_data_t *rend_data,
unsigned int failure_type) unsigned int failure_type)
{ {
int i, r; int i, r;
rend_cache_entry_t *ent; rend_cache_entry_t *ent;
connection_t *conn; connection_t *conn;
const char *onion_address = rend_data_get_address(rend_data);
r = rend_cache_lookup_entry(rend_query->onion_address, -1, &ent); r = rend_cache_lookup_entry(onion_address, -1, &ent);
if (r < 0) { if (r < 0) {
/* Either invalid onion address or cache entry not found. */ /* Either invalid onion address or cache entry not found. */
switch (-r) { switch (-r) {
case EINVAL: case EINVAL:
log_warn(LD_BUG, "Malformed service ID %s.", log_warn(LD_BUG, "Malformed service ID %s.",
escaped_safe_str_client(rend_query->onion_address)); escaped_safe_str_client(onion_address));
return -1; return -1;
case ENOENT: case ENOENT:
log_info(LD_REND, "Unknown service %s. Re-fetching descriptor.", log_info(LD_REND, "Unknown service %s. Re-fetching descriptor.",
escaped_safe_str_client(rend_query->onion_address)); escaped_safe_str_client(onion_address));
rend_client_refetch_v2_renddesc(rend_query); rend_client_refetch_v2_renddesc(rend_data);
return 0; return 0;
default: default:
log_warn(LD_BUG, "Unknown cache lookup returned code: %d", r); log_warn(LD_BUG, "Unknown cache lookup returned code: %d", r);
@ -1031,7 +1047,7 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro,
case INTRO_POINT_FAILURE_GENERIC: case INTRO_POINT_FAILURE_GENERIC:
rend_cache_intro_failure_note(failure_type, rend_cache_intro_failure_note(failure_type,
(uint8_t *)failed_intro->identity_digest, (uint8_t *)failed_intro->identity_digest,
rend_query->onion_address); onion_address);
rend_intro_point_free(intro); rend_intro_point_free(intro);
smartlist_del(ent->parsed->intro_nodes, i); smartlist_del(ent->parsed->intro_nodes, i);
break; break;
@ -1049,8 +1065,7 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro,
if (zap_intro_point) { if (zap_intro_point) {
rend_cache_intro_failure_note( rend_cache_intro_failure_note(
failure_type, failure_type,
(uint8_t *) failed_intro->identity_digest, (uint8_t *) failed_intro->identity_digest, onion_address);
rend_query->onion_address);
rend_intro_point_free(intro); rend_intro_point_free(intro);
smartlist_del(ent->parsed->intro_nodes, i); smartlist_del(ent->parsed->intro_nodes, i);
} }
@ -1064,14 +1079,14 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro,
if (! rend_client_any_intro_points_usable(ent)) { if (! rend_client_any_intro_points_usable(ent)) {
log_info(LD_REND, log_info(LD_REND,
"No more intro points remain for %s. Re-fetching descriptor.", "No more intro points remain for %s. Re-fetching descriptor.",
escaped_safe_str_client(rend_query->onion_address)); escaped_safe_str_client(onion_address));
rend_client_refetch_v2_renddesc(rend_query); rend_client_refetch_v2_renddesc(rend_data);
/* move all pending streams back to renddesc_wait */ /* move all pending streams back to renddesc_wait */
/* NOTE: We can now do this faster, if we use pending_entry_connections */ /* NOTE: We can now do this faster, if we use pending_entry_connections */
while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP, while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP,
AP_CONN_STATE_CIRCUIT_WAIT, AP_CONN_STATE_CIRCUIT_WAIT,
rend_query->onion_address))) { onion_address))) {
connection_ap_mark_as_non_pending_circuit(TO_ENTRY_CONN(conn)); connection_ap_mark_as_non_pending_circuit(TO_ENTRY_CONN(conn));
conn->state = AP_CONN_STATE_RENDDESC_WAIT; conn->state = AP_CONN_STATE_RENDDESC_WAIT;
} }
@ -1080,7 +1095,7 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro,
} }
log_info(LD_REND,"%d options left for %s.", log_info(LD_REND,"%d options left for %s.",
smartlist_len(ent->parsed->intro_nodes), smartlist_len(ent->parsed->intro_nodes),
escaped_safe_str_client(rend_query->onion_address)); escaped_safe_str_client(onion_address));
return 1; return 1;
} }
@ -1221,10 +1236,11 @@ rend_client_desc_trynow(const char *query)
rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data; rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data;
if (!rend_data) if (!rend_data)
continue; continue;
if (rend_cmp_service_ids(query, rend_data->onion_address)) const char *onion_address = rend_data_get_address(rend_data);
if (rend_cmp_service_ids(query, onion_address))
continue; continue;
assert_connection_ok(base_conn, now); assert_connection_ok(base_conn, now);
if (rend_cache_lookup_entry(rend_data->onion_address, -1, if (rend_cache_lookup_entry(onion_address, -1,
&entry) == 0 && &entry) == 0 &&
rend_client_any_intro_points_usable(entry)) { rend_client_any_intro_points_usable(entry)) {
/* either this fetch worked, or it failed but there was a /* either this fetch worked, or it failed but there was a
@ -1259,11 +1275,12 @@ rend_client_note_connection_attempt_ended(const rend_data_t *rend_data)
{ {
unsigned int have_onion = 0; unsigned int have_onion = 0;
rend_cache_entry_t *cache_entry = NULL; rend_cache_entry_t *cache_entry = NULL;
const char *onion_address = rend_data_get_address(rend_data);
rend_data_v2_t *rend_data_v2 = TO_REND_DATA_V2(rend_data);
if (*rend_data->onion_address != '\0') { if (onion_address[0] != '\0') {
/* Ignore return value; we find an entry, or we don't. */ /* Ignore return value; we find an entry, or we don't. */
(void) rend_cache_lookup_entry(rend_data->onion_address, -1, (void) rend_cache_lookup_entry(onion_address, -1, &cache_entry);
&cache_entry);
have_onion = 1; have_onion = 1;
} }
@ -1277,17 +1294,17 @@ rend_client_note_connection_attempt_ended(const rend_data_t *rend_data)
/* Remove the HS's entries in last_hid_serv_requests. */ /* Remove the HS's entries in last_hid_serv_requests. */
if (have_onion) { if (have_onion) {
unsigned int replica; unsigned int replica;
for (replica = 0; replica < ARRAY_LENGTH(rend_data->descriptor_id); for (replica = 0; replica < ARRAY_LENGTH(rend_data_v2->descriptor_id);
replica++) { replica++) {
const char *desc_id = rend_data->descriptor_id[replica]; const char *desc_id = rend_data_v2->descriptor_id[replica];
purge_hid_serv_from_last_hid_serv_requests(desc_id); purge_hid_serv_from_last_hid_serv_requests(desc_id);
} }
log_info(LD_REND, "Connection attempt for %s has ended; " log_info(LD_REND, "Connection attempt for %s has ended; "
"cleaning up temporary state.", "cleaning up temporary state.",
safe_str_client(rend_data->onion_address)); safe_str_client(onion_address));
} else { } else {
/* We only have an ID for a fetch. Probably used by HSFETCH. */ /* We only have an ID for a fetch. Probably used by HSFETCH. */
purge_hid_serv_from_last_hid_serv_requests(rend_data->desc_id_fetch); purge_hid_serv_from_last_hid_serv_requests(rend_data_v2->desc_id_fetch);
} }
} }
@ -1301,12 +1318,13 @@ rend_client_get_random_intro(const rend_data_t *rend_query)
int ret; int ret;
extend_info_t *result; extend_info_t *result;
rend_cache_entry_t *entry; rend_cache_entry_t *entry;
const char *onion_address = rend_data_get_address(rend_query);
ret = rend_cache_lookup_entry(rend_query->onion_address, -1, &entry); ret = rend_cache_lookup_entry(onion_address, -1, &entry);
if (ret < 0 || !rend_client_any_intro_points_usable(entry)) { if (ret < 0 || !rend_client_any_intro_points_usable(entry)) {
log_warn(LD_REND, log_warn(LD_REND,
"Query '%s' didn't have valid rend desc in cache. Failing.", "Query '%s' didn't have valid rend desc in cache. Failing.",
safe_str_client(rend_query->onion_address)); safe_str_client(onion_address));
/* XXX: Should we refetch the descriptor here if the IPs are not usable /* XXX: Should we refetch the descriptor here if the IPs are not usable
* anymore ?. */ * anymore ?. */
return NULL; return NULL;

View File

@ -27,7 +27,7 @@ void rend_client_cancel_descriptor_fetches(void);
void rend_client_purge_last_hid_serv_requests(void); void rend_client_purge_last_hid_serv_requests(void);
int rend_client_report_intro_point_failure(extend_info_t *failed_intro, int rend_client_report_intro_point_failure(extend_info_t *failed_intro,
rend_data_t *rend_query, rend_data_t *rend_data,
unsigned int failure_type); unsigned int failure_type);
int rend_client_rendezvous_acked(origin_circuit_t *circ, int rend_client_rendezvous_acked(origin_circuit_t *circ,

View File

@ -12,6 +12,7 @@
#include "circuitbuild.h" #include "circuitbuild.h"
#include "config.h" #include "config.h"
#include "control.h" #include "control.h"
#include "hs_common.h"
#include "rendclient.h" #include "rendclient.h"
#include "rendcommon.h" #include "rendcommon.h"
#include "rendmid.h" #include "rendmid.h"
@ -804,124 +805,6 @@ rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint,
command); command);
} }
/** 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)
{
rend_data_t *data_dup;
tor_assert(data);
data_dup = tor_memdup(data, sizeof(rend_data_t));
data_dup->hsdirs_fp = smartlist_new();
SMARTLIST_FOREACH(data->hsdirs_fp, char *, fp,
smartlist_add(data_dup->hsdirs_fp,
tor_memdup(fp, DIGEST_LEN)));
return data_dup;
}
/** Compute descriptor ID for each replicas and save them. A valid onion
* address must be present in the <b>rend_data</b>.
*
* Return 0 on success else -1. */
static int
compute_desc_id(rend_data_t *rend_data)
{
int ret = 0;
unsigned replica;
time_t now = time(NULL);
tor_assert(rend_data);
/* Compute descriptor ID for each replicas. */
for (replica = 0; replica < ARRAY_LENGTH(rend_data->descriptor_id);
replica++) {
ret = rend_compute_v2_desc_id(rend_data->descriptor_id[replica],
rend_data->onion_address,
rend_data->descriptor_cookie,
now, replica);
if (ret < 0) {
goto end;
}
}
end:
return ret;
}
/** Allocate and initialize a rend_data_t object for a service using the
* given arguments. Only the <b>onion_address</b> is not optional.
*
* Return a valid rend_data_t pointer. */
rend_data_t *
rend_data_service_create(const char *onion_address, const char *pk_digest,
const uint8_t *cookie, rend_auth_type_t auth_type)
{
rend_data_t *rend_data = tor_malloc_zero(sizeof(*rend_data));
/* We need at least one else the call is wrong. */
tor_assert(onion_address != NULL);
if (pk_digest) {
memcpy(rend_data->rend_pk_digest, pk_digest,
sizeof(rend_data->rend_pk_digest));
}
if (cookie) {
memcpy(rend_data->rend_cookie, cookie,
sizeof(rend_data->rend_cookie));
}
strlcpy(rend_data->onion_address, onion_address,
sizeof(rend_data->onion_address));
rend_data->auth_type = auth_type;
/* Won't be used but still need to initialize it for rend_data dup and
* free. */
rend_data->hsdirs_fp = smartlist_new();
return rend_data;
}
/** Allocate and initialize a rend_data_t object for a client request using
* the given arguments. Either an onion address or a descriptor ID is
* needed. Both can be given but only the onion address will be used to make
* the descriptor fetch.
*
* Return a valid rend_data_t pointer or NULL on error meaning the
* descriptor IDs couldn't be computed from the given data. */
rend_data_t *
rend_data_client_create(const char *onion_address, const char *desc_id,
const char *cookie, rend_auth_type_t auth_type)
{
rend_data_t *rend_data = tor_malloc_zero(sizeof(*rend_data));
/* We need at least one else the call is wrong. */
tor_assert(onion_address != NULL || desc_id != NULL);
if (cookie) {
memcpy(rend_data->descriptor_cookie, cookie,
sizeof(rend_data->descriptor_cookie));
}
if (desc_id) {
memcpy(rend_data->desc_id_fetch, desc_id,
sizeof(rend_data->desc_id_fetch));
}
if (onion_address) {
strlcpy(rend_data->onion_address, onion_address,
sizeof(rend_data->onion_address));
if (compute_desc_id(rend_data) < 0) {
goto error;
}
}
rend_data->auth_type = auth_type;
rend_data->hsdirs_fp = smartlist_new();
return rend_data;
error:
rend_data_free(rend_data);
return NULL;
}
/** Determine the routers that are responsible for <b>id</b> (binary) and /** Determine the routers that are responsible for <b>id</b> (binary) and
* add pointers to those routers' routerstatus_t to <b>responsible_dirs</b>. * add pointers to those routers' routerstatus_t to <b>responsible_dirs</b>.
* Return -1 if we're returning an empty smartlist, else return 0. * Return -1 if we're returning an empty smartlist, else return 0.
@ -1116,3 +999,33 @@ assert_circ_anonymity_ok(origin_circuit_t *circ,
} }
} }
/* Return 1 iff the given <b>digest</b> of a permenanent hidden service key is
* equal to the digest in the origin circuit <b>ocirc</b> of its rend data .
* If the rend data doesn't exist, 0 is returned. This function is agnostic to
* the rend data version. */
int
rend_circuit_pk_digest_eq(const origin_circuit_t *ocirc,
const uint8_t *digest)
{
size_t rend_pk_digest_len;
const uint8_t *rend_pk_digest;
tor_assert(ocirc);
tor_assert(digest);
if (ocirc->rend_data == NULL) {
goto no_match;
}
rend_pk_digest = rend_data_get_pk_digest(ocirc->rend_data,
&rend_pk_digest_len);
if (tor_memeq(rend_pk_digest, digest, rend_pk_digest_len)) {
goto match;
}
no_match:
return 0;
match:
return 1;
}

View File

@ -18,19 +18,6 @@ typedef enum rend_intro_point_failure_t {
INTRO_POINT_FAILURE_UNREACHABLE = 2, INTRO_POINT_FAILURE_UNREACHABLE = 2,
} rend_intro_point_failure_t; } rend_intro_point_failure_t;
/** Free all storage associated with <b>data</b> */
static inline void
rend_data_free(rend_data_t *data)
{
if (!data) {
return;
}
/* Cleanup the HSDir identity digest. */
SMARTLIST_FOREACH(data->hsdirs_fp, char *, d, tor_free(d));
smartlist_free(data->hsdirs_fp);
tor_free(data);
}
int rend_cmp_service_ids(const char *one, const char *two); int rend_cmp_service_ids(const char *one, const char *two);
void rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint, void rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint,
@ -60,15 +47,8 @@ void rend_get_descriptor_id_bytes(char *descriptor_id_out,
int hid_serv_get_responsible_directories(smartlist_t *responsible_dirs, int hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
const char *id); const char *id);
rend_data_t *rend_data_dup(const rend_data_t *data); int rend_circuit_pk_digest_eq(const origin_circuit_t *ocirc,
rend_data_t *rend_data_client_create(const char *onion_address, const uint8_t *digest);
const char *desc_id,
const char *cookie,
rend_auth_type_t auth_type);
rend_data_t *rend_data_service_create(const char *onion_address,
const char *pk_digest,
const uint8_t *cookie,
rend_auth_type_t auth_type);
char *rend_auth_encode_cookie(const uint8_t *cookie_in, char *rend_auth_encode_cookie(const uint8_t *cookie_in,
rend_auth_type_t auth_type); rend_auth_type_t auth_type);

View File

@ -17,6 +17,7 @@
#include "config.h" #include "config.h"
#include "control.h" #include "control.h"
#include "directory.h" #include "directory.h"
#include "hs_common.h"
#include "main.h" #include "main.h"
#include "networkstatus.h" #include "networkstatus.h"
#include "nodelist.h" #include "nodelist.h"
@ -761,8 +762,7 @@ rend_config_services(const or_options_t *options, int validate_only)
int keep_it = 0; int keep_it = 0;
tor_assert(oc->rend_data); tor_assert(oc->rend_data);
SMARTLIST_FOREACH(surviving_services, rend_service_t *, ptr, { SMARTLIST_FOREACH(surviving_services, rend_service_t *, ptr, {
if (tor_memeq(ptr->pk_digest, oc->rend_data->rend_pk_digest, if (rend_circuit_pk_digest_eq(oc, (uint8_t *) ptr->pk_digest)) {
DIGEST_LEN)) {
keep_it = 1; keep_it = 1;
break; break;
} }
@ -772,7 +772,7 @@ rend_config_services(const or_options_t *options, int validate_only)
log_info(LD_REND, "Closing intro point %s for service %s.", log_info(LD_REND, "Closing intro point %s for service %s.",
safe_str_client(extend_info_describe( safe_str_client(extend_info_describe(
oc->build_state->chosen_exit)), oc->build_state->chosen_exit)),
oc->rend_data->onion_address); rend_data_get_address(oc->rend_data));
circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED); circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
/* XXXX Is there another reason we should use here? */ /* XXXX Is there another reason we should use here? */
} }
@ -899,12 +899,13 @@ rend_service_del_ephemeral(const char *service_id)
circ->purpose == CIRCUIT_PURPOSE_S_INTRO)) { circ->purpose == CIRCUIT_PURPOSE_S_INTRO)) {
origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ); origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ);
tor_assert(oc->rend_data); tor_assert(oc->rend_data);
if (!tor_memeq(s->pk_digest, oc->rend_data->rend_pk_digest, DIGEST_LEN)) if (!rend_circuit_pk_digest_eq(oc, (uint8_t *) s->pk_digest)) {
continue; continue;
}
log_debug(LD_REND, "Closing intro point %s for service %s.", log_debug(LD_REND, "Closing intro point %s for service %s.",
safe_str_client(extend_info_describe( safe_str_client(extend_info_describe(
oc->build_state->chosen_exit)), oc->build_state->chosen_exit)),
oc->rend_data->onion_address); rend_data_get_address(oc->rend_data));
circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED); circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
} }
} SMARTLIST_FOREACH_END(circ); } SMARTLIST_FOREACH_END(circ);
@ -1649,7 +1650,7 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
const or_options_t *options = get_options(); const or_options_t *options = get_options();
char *err_msg = NULL; char *err_msg = NULL;
int err_msg_severity = LOG_WARN; int err_msg_severity = LOG_WARN;
const char *stage_descr = NULL; const char *stage_descr = NULL, *rend_pk_digest;
int reason = END_CIRC_REASON_TORPROTOCOL; int reason = END_CIRC_REASON_TORPROTOCOL;
/* Service/circuit/key stuff we can learn before parsing */ /* Service/circuit/key stuff we can learn before parsing */
char serviceid[REND_SERVICE_ID_LEN_BASE32+1]; char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
@ -1683,14 +1684,15 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
assert_circ_anonymity_ok(circuit, options); assert_circ_anonymity_ok(circuit, options);
tor_assert(circuit->rend_data); tor_assert(circuit->rend_data);
/* XXX: This is version 2 specific (only one supported). */
rend_pk_digest = (char *) rend_data_get_pk_digest(circuit->rend_data, NULL);
/* We'll use this in a bazillion log messages */ /* We'll use this in a bazillion log messages */
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1, base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
circuit->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN); rend_pk_digest, REND_SERVICE_ID_LEN);
/* look up service depending on circuit. */ /* look up service depending on circuit. */
service = service = rend_service_get_by_pk_digest(rend_pk_digest);
rend_service_get_by_pk_digest(circuit->rend_data->rend_pk_digest);
if (!service) { if (!service) {
log_warn(LD_BUG, log_warn(LD_BUG,
"Internal error: Got an INTRODUCE2 cell on an intro " "Internal error: Got an INTRODUCE2 cell on an intro "
@ -1913,8 +1915,7 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
/* Fill in the circuit's state. */ /* Fill in the circuit's state. */
launched->rend_data = launched->rend_data =
rend_data_service_create(service->service_id, rend_data_service_create(service->service_id, rend_pk_digest,
circuit->rend_data->rend_pk_digest,
parsed_req->rc, service->auth_type); parsed_req->rc, service->auth_type);
launched->build_state->service_pending_final_cpath_ref = launched->build_state->service_pending_final_cpath_ref =
@ -2948,11 +2949,11 @@ count_intro_point_circuits(const rend_service_t *service)
circ->purpose == CIRCUIT_PURPOSE_S_INTRO)) { circ->purpose == CIRCUIT_PURPOSE_S_INTRO)) {
origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ); origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ);
if (oc->rend_data && if (oc->rend_data &&
!rend_cmp_service_ids(service->service_id, rend_circuit_pk_digest_eq(oc, (uint8_t *) service->pk_digest)) {
oc->rend_data->onion_address))
num_ipos++; num_ipos++;
} }
} }
}
SMARTLIST_FOREACH_END(circ); SMARTLIST_FOREACH_END(circ);
return num_ipos; return num_ipos;
} }
@ -2970,17 +2971,19 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
char auth[DIGEST_LEN + 9]; char auth[DIGEST_LEN + 9];
char serviceid[REND_SERVICE_ID_LEN_BASE32+1]; char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
int reason = END_CIRC_REASON_TORPROTOCOL; int reason = END_CIRC_REASON_TORPROTOCOL;
const char *rend_pk_digest;
tor_assert(circuit->base_.purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO); tor_assert(circuit->base_.purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
assert_circ_anonymity_ok(circuit, get_options()); assert_circ_anonymity_ok(circuit, get_options());
tor_assert(circuit->cpath); tor_assert(circuit->cpath);
tor_assert(circuit->rend_data); tor_assert(circuit->rend_data);
/* XXX: This is version 2 specific (only on supported). */
rend_pk_digest = (char *) rend_data_get_pk_digest(circuit->rend_data, NULL);
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1, base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
circuit->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN); rend_pk_digest, REND_SERVICE_ID_LEN);
service = rend_service_get_by_pk_digest( service = rend_service_get_by_pk_digest(rend_pk_digest);
circuit->rend_data->rend_pk_digest);
if (!service) { if (!service) {
log_warn(LD_REND, "Unrecognized service ID %s on introduction circuit %u.", log_warn(LD_REND, "Unrecognized service ID %s on introduction circuit %u.",
safe_str_client(serviceid), (unsigned)circuit->base_.n_circ_id); safe_str_client(serviceid), (unsigned)circuit->base_.n_circ_id);
@ -3021,9 +3024,8 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
circuit_change_purpose(TO_CIRCUIT(circuit), CIRCUIT_PURPOSE_C_GENERAL); circuit_change_purpose(TO_CIRCUIT(circuit), CIRCUIT_PURPOSE_C_GENERAL);
{ {
rend_data_t *rend_data = circuit->rend_data; rend_data_free(circuit->rend_data);
circuit->rend_data = NULL; circuit->rend_data = NULL;
rend_data_free(rend_data);
} }
{ {
crypto_pk_t *intro_key = circuit->intro_key; crypto_pk_t *intro_key = circuit->intro_key;
@ -3106,15 +3108,17 @@ rend_service_intro_established(origin_circuit_t *circuit,
char serviceid[REND_SERVICE_ID_LEN_BASE32+1]; char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
(void) request; (void) request;
(void) request_len; (void) request_len;
tor_assert(circuit->rend_data);
/* XXX: This is version 2 specific (only supported one for now). */
const char *rend_pk_digest =
(char *) rend_data_get_pk_digest(circuit->rend_data, NULL);
if (circuit->base_.purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) { if (circuit->base_.purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) {
log_warn(LD_PROTOCOL, log_warn(LD_PROTOCOL,
"received INTRO_ESTABLISHED cell on non-intro circuit."); "received INTRO_ESTABLISHED cell on non-intro circuit.");
goto err; goto err;
} }
tor_assert(circuit->rend_data); service = rend_service_get_by_pk_digest(rend_pk_digest);
service = rend_service_get_by_pk_digest(
circuit->rend_data->rend_pk_digest);
if (!service) { if (!service) {
log_warn(LD_REND, "Unknown service on introduction circuit %u.", log_warn(LD_REND, "Unknown service on introduction circuit %u.",
(unsigned)circuit->base_.n_circ_id); (unsigned)circuit->base_.n_circ_id);
@ -3137,7 +3141,7 @@ rend_service_intro_established(origin_circuit_t *circuit,
circuit_change_purpose(TO_CIRCUIT(circuit), CIRCUIT_PURPOSE_S_INTRO); circuit_change_purpose(TO_CIRCUIT(circuit), CIRCUIT_PURPOSE_S_INTRO);
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32 + 1, base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32 + 1,
circuit->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN); rend_pk_digest, REND_SERVICE_ID_LEN);
log_info(LD_REND, log_info(LD_REND,
"Received INTRO_ESTABLISHED cell on circuit %u for service %s", "Received INTRO_ESTABLISHED cell on circuit %u for service %s",
(unsigned)circuit->base_.n_circ_id, serviceid); (unsigned)circuit->base_.n_circ_id, serviceid);
@ -3164,6 +3168,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
char serviceid[REND_SERVICE_ID_LEN_BASE32+1]; char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
char hexcookie[9]; char hexcookie[9];
int reason; int reason;
const char *rend_cookie, *rend_pk_digest;
tor_assert(circuit->base_.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND); tor_assert(circuit->base_.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
tor_assert(circuit->cpath); tor_assert(circuit->cpath);
@ -3171,6 +3176,11 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
assert_circ_anonymity_ok(circuit, get_options()); assert_circ_anonymity_ok(circuit, get_options());
tor_assert(circuit->rend_data); tor_assert(circuit->rend_data);
/* XXX: This is version 2 specific (only one supported). */
rend_pk_digest = (char *) rend_data_get_pk_digest(circuit->rend_data,
NULL);
rend_cookie = circuit->rend_data->rend_cookie;
/* Declare the circuit dirty to avoid reuse, and for path-bias */ /* Declare the circuit dirty to avoid reuse, and for path-bias */
if (!circuit->base_.timestamp_dirty) if (!circuit->base_.timestamp_dirty)
circuit->base_.timestamp_dirty = time(NULL); circuit->base_.timestamp_dirty = time(NULL);
@ -3180,9 +3190,9 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
hop = circuit->build_state->service_pending_final_cpath_ref->cpath; hop = circuit->build_state->service_pending_final_cpath_ref->cpath;
base16_encode(hexcookie,9,circuit->rend_data->rend_cookie,4); base16_encode(hexcookie,9, rend_cookie,4);
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1, base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
circuit->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN); rend_pk_digest, REND_SERVICE_ID_LEN);
log_info(LD_REND, log_info(LD_REND,
"Done building circuit %u to rendezvous with " "Done building circuit %u to rendezvous with "
@ -3211,8 +3221,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
circuit->build_state->pending_final_cpath = hop; circuit->build_state->pending_final_cpath = hop;
circuit->build_state->service_pending_final_cpath_ref->cpath = NULL; circuit->build_state->service_pending_final_cpath_ref->cpath = NULL;
service = rend_service_get_by_pk_digest( service = rend_service_get_by_pk_digest(rend_pk_digest);
circuit->rend_data->rend_pk_digest);
if (!service) { if (!service) {
log_warn(LD_GENERAL, "Internal error: unrecognized service ID on " log_warn(LD_GENERAL, "Internal error: unrecognized service ID on "
"rendezvous circuit."); "rendezvous circuit.");
@ -3221,7 +3230,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
} }
/* All we need to do is send a RELAY_RENDEZVOUS1 cell... */ /* All we need to do is send a RELAY_RENDEZVOUS1 cell... */
memcpy(buf, circuit->rend_data->rend_cookie, REND_COOKIE_LEN); memcpy(buf, rend_cookie, REND_COOKIE_LEN);
if (crypto_dh_get_public(hop->rend_dh_handshake_state, if (crypto_dh_get_public(hop->rend_dh_handshake_state,
buf+REND_COOKIE_LEN, DH_KEY_LEN)<0) { buf+REND_COOKIE_LEN, DH_KEY_LEN)<0) {
log_warn(LD_GENERAL,"Couldn't get DH public key."); log_warn(LD_GENERAL,"Couldn't get DH public key.");
@ -3284,8 +3293,8 @@ find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest)
origin_circuit_t *circ = NULL; origin_circuit_t *circ = NULL;
tor_assert(intro); tor_assert(intro);
while ((circ = circuit_get_next_by_pk_and_purpose(circ,pk_digest, while ((circ = circuit_get_next_by_pk_and_purpose(circ,
CIRCUIT_PURPOSE_S_INTRO))) { (uint8_t *) pk_digest, CIRCUIT_PURPOSE_S_INTRO))) {
if (tor_memeq(circ->build_state->chosen_exit->identity_digest, if (tor_memeq(circ->build_state->chosen_exit->identity_digest,
intro->extend_info->identity_digest, DIGEST_LEN) && intro->extend_info->identity_digest, DIGEST_LEN) &&
circ->rend_data) { circ->rend_data) {
@ -3294,7 +3303,8 @@ find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest)
} }
circ = NULL; circ = NULL;
while ((circ = circuit_get_next_by_pk_and_purpose(circ,pk_digest, while ((circ = circuit_get_next_by_pk_and_purpose(circ,
(uint8_t *) pk_digest,
CIRCUIT_PURPOSE_S_ESTABLISH_INTRO))) { CIRCUIT_PURPOSE_S_ESTABLISH_INTRO))) {
if (tor_memeq(circ->build_state->chosen_exit->identity_digest, if (tor_memeq(circ->build_state->chosen_exit->identity_digest,
intro->extend_info->identity_digest, DIGEST_LEN) && intro->extend_info->identity_digest, DIGEST_LEN) &&
@ -3334,7 +3344,7 @@ find_intro_point(origin_circuit_t *circ)
tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO || tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_INTRO); TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_INTRO);
tor_assert(circ->rend_data); tor_assert(circ->rend_data);
serviceid = circ->rend_data->onion_address; serviceid = rend_data_get_address(circ->rend_data);
SMARTLIST_FOREACH(rend_service_list, rend_service_t *, s, SMARTLIST_FOREACH(rend_service_list, rend_service_t *, s,
if (tor_memeq(s->service_id, serviceid, REND_SERVICE_ID_LEN_BASE32)) { if (tor_memeq(s->service_id, serviceid, REND_SERVICE_ID_LEN_BASE32)) {
@ -3719,10 +3729,13 @@ void
rend_service_desc_has_uploaded(const rend_data_t *rend_data) rend_service_desc_has_uploaded(const rend_data_t *rend_data)
{ {
rend_service_t *service; rend_service_t *service;
const char *onion_address;
tor_assert(rend_data); tor_assert(rend_data);
service = rend_service_get_by_service_id(rend_data->onion_address); onion_address = rend_data_get_address(rend_data);
service = rend_service_get_by_service_id(onion_address);
if (service == NULL) { if (service == NULL) {
return; return;
} }
@ -4109,14 +4122,16 @@ rend_service_set_connection_addr_port(edge_connection_t *conn,
smartlist_t *matching_ports; smartlist_t *matching_ports;
rend_service_port_config_t *chosen_port; rend_service_port_config_t *chosen_port;
unsigned int warn_once = 0; unsigned int warn_once = 0;
const char *rend_pk_digest;
tor_assert(circ->base_.purpose == CIRCUIT_PURPOSE_S_REND_JOINED); tor_assert(circ->base_.purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
tor_assert(circ->rend_data); tor_assert(circ->rend_data);
log_debug(LD_REND,"beginning to hunt for addr/port"); log_debug(LD_REND,"beginning to hunt for addr/port");
/* XXX: This is version 2 specific (only one supported). */
rend_pk_digest = (char *) rend_data_get_pk_digest(circ->rend_data, NULL);
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1, base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
circ->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN); rend_pk_digest, REND_SERVICE_ID_LEN);
service = rend_service_get_by_pk_digest( service = rend_service_get_by_pk_digest(rend_pk_digest);
circ->rend_data->rend_pk_digest);
if (!service) { if (!service) {
log_warn(LD_REND, "Couldn't find any service associated with pk %s on " log_warn(LD_REND, "Couldn't find any service associated with pk %s on "
"rendezvous circuit %u; closing.", "rendezvous circuit %u; closing.",

View File

@ -60,6 +60,7 @@
#include "circuitstats.h" #include "circuitstats.h"
#include "dirserv.h" #include "dirserv.h"
#include "dirvote.h" #include "dirvote.h"
#include "parsecommon.h"
#include "policies.h" #include "policies.h"
#include "protover.h" #include "protover.h"
#include "rendcommon.h" #include "rendcommon.h"
@ -81,267 +82,6 @@
/****************************************************************************/ /****************************************************************************/
/** Enumeration of possible token types. The ones starting with K_ correspond
* to directory 'keywords'. A_ is for an annotation, R or C is related to
* hidden services, ERR_ is an error in the tokenizing process, EOF_ is an
* end-of-file marker, and NIL_ is used to encode not-a-token.
*/
typedef enum {
K_ACCEPT = 0,
K_ACCEPT6,
K_DIRECTORY_SIGNATURE,
K_RECOMMENDED_SOFTWARE,
K_REJECT,
K_REJECT6,
K_ROUTER,
K_SIGNED_DIRECTORY,
K_SIGNING_KEY,
K_ONION_KEY,
K_ONION_KEY_NTOR,
K_ROUTER_SIGNATURE,
K_PUBLISHED,
K_RUNNING_ROUTERS,
K_ROUTER_STATUS,
K_PLATFORM,
K_PROTO,
K_OPT,
K_BANDWIDTH,
K_CONTACT,
K_NETWORK_STATUS,
K_UPTIME,
K_DIR_SIGNING_KEY,
K_FAMILY,
K_FINGERPRINT,
K_HIBERNATING,
K_READ_HISTORY,
K_WRITE_HISTORY,
K_NETWORK_STATUS_VERSION,
K_DIR_SOURCE,
K_DIR_OPTIONS,
K_CLIENT_VERSIONS,
K_SERVER_VERSIONS,
K_RECOMMENDED_CLIENT_PROTOCOLS,
K_RECOMMENDED_RELAY_PROTOCOLS,
K_REQUIRED_CLIENT_PROTOCOLS,
K_REQUIRED_RELAY_PROTOCOLS,
K_OR_ADDRESS,
K_ID,
K_P,
K_P6,
K_R,
K_A,
K_S,
K_V,
K_W,
K_M,
K_EXTRA_INFO,
K_EXTRA_INFO_DIGEST,
K_CACHES_EXTRA_INFO,
K_HIDDEN_SERVICE_DIR,
K_ALLOW_SINGLE_HOP_EXITS,
K_IPV6_POLICY,
K_ROUTER_SIG_ED25519,
K_IDENTITY_ED25519,
K_MASTER_KEY_ED25519,
K_ONION_KEY_CROSSCERT,
K_NTOR_ONION_KEY_CROSSCERT,
K_DIRREQ_END,
K_DIRREQ_V2_IPS,
K_DIRREQ_V3_IPS,
K_DIRREQ_V2_REQS,
K_DIRREQ_V3_REQS,
K_DIRREQ_V2_SHARE,
K_DIRREQ_V3_SHARE,
K_DIRREQ_V2_RESP,
K_DIRREQ_V3_RESP,
K_DIRREQ_V2_DIR,
K_DIRREQ_V3_DIR,
K_DIRREQ_V2_TUN,
K_DIRREQ_V3_TUN,
K_ENTRY_END,
K_ENTRY_IPS,
K_CELL_END,
K_CELL_PROCESSED,
K_CELL_QUEUED,
K_CELL_TIME,
K_CELL_CIRCS,
K_EXIT_END,
K_EXIT_WRITTEN,
K_EXIT_READ,
K_EXIT_OPENED,
K_DIR_KEY_CERTIFICATE_VERSION,
K_DIR_IDENTITY_KEY,
K_DIR_KEY_PUBLISHED,
K_DIR_KEY_EXPIRES,
K_DIR_KEY_CERTIFICATION,
K_DIR_KEY_CROSSCERT,
K_DIR_ADDRESS,
K_DIR_TUNNELLED,
K_VOTE_STATUS,
K_VALID_AFTER,
K_FRESH_UNTIL,
K_VALID_UNTIL,
K_VOTING_DELAY,
K_KNOWN_FLAGS,
K_PARAMS,
K_BW_WEIGHTS,
K_VOTE_DIGEST,
K_CONSENSUS_DIGEST,
K_ADDITIONAL_DIGEST,
K_ADDITIONAL_SIGNATURE,
K_CONSENSUS_METHODS,
K_CONSENSUS_METHOD,
K_LEGACY_DIR_KEY,
K_DIRECTORY_FOOTER,
K_SIGNING_CERT_ED,
K_SR_FLAG,
K_COMMIT,
K_PREVIOUS_SRV,
K_CURRENT_SRV,
K_PACKAGE,
A_PURPOSE,
A_LAST_LISTED,
A_UNKNOWN_,
R_RENDEZVOUS_SERVICE_DESCRIPTOR,
R_VERSION,
R_PERMANENT_KEY,
R_SECRET_ID_PART,
R_PUBLICATION_TIME,
R_PROTOCOL_VERSIONS,
R_INTRODUCTION_POINTS,
R_SIGNATURE,
R_IPO_IDENTIFIER,
R_IPO_IP_ADDRESS,
R_IPO_ONION_PORT,
R_IPO_ONION_KEY,
R_IPO_SERVICE_KEY,
C_CLIENT_NAME,
C_DESCRIPTOR_COOKIE,
C_CLIENT_KEY,
ERR_,
EOF_,
NIL_
} directory_keyword;
#define MIN_ANNOTATION A_PURPOSE
#define MAX_ANNOTATION A_UNKNOWN_
/** Structure to hold a single directory token.
*
* We parse a directory by breaking it into "tokens", each consisting
* of a keyword, a line full of arguments, and a binary object. The
* arguments and object are both optional, depending on the keyword
* type.
*
* This structure is only allocated in memareas; do not allocate it on
* the heap, or token_clear() won't work.
*/
typedef struct directory_token_t {
directory_keyword tp; /**< Type of the token. */
int n_args:30; /**< Number of elements in args */
char **args; /**< Array of arguments from keyword line. */
char *object_type; /**< -----BEGIN [object_type]-----*/
size_t object_size; /**< Bytes in object_body */
char *object_body; /**< Contents of object, base64-decoded. */
crypto_pk_t *key; /**< For public keys only. Heap-allocated. */
char *error; /**< For ERR_ tokens only. */
} directory_token_t;
/* ********************************************************************** */
/** We use a table of rules to decide how to parse each token type. */
/** Rules for whether the keyword needs an object. */
typedef enum {
NO_OBJ, /**< No object, ever. */
NEED_OBJ, /**< Object is required. */
NEED_SKEY_1024,/**< Object is required, and must be a 1024 bit private key */
NEED_KEY_1024, /**< Object is required, and must be a 1024 bit public key */
NEED_KEY, /**< Object is required, and must be a public key. */
OBJ_OK, /**< Object is optional. */
} obj_syntax;
#define AT_START 1
#define AT_END 2
/** Determines the parsing rules for a single token type. */
typedef struct token_rule_t {
/** The string value of the keyword identifying the type of item. */
const char *t;
/** The corresponding directory_keyword enum. */
directory_keyword v;
/** Minimum number of arguments for this item */
int min_args;
/** Maximum number of arguments for this item */
int max_args;
/** If true, we concatenate all arguments for this item into a single
* string. */
int concat_args;
/** Requirements on object syntax for this item. */
obj_syntax os;
/** Lowest number of times this item may appear in a document. */
int min_cnt;
/** Highest number of times this item may appear in a document. */
int max_cnt;
/** One or more of AT_START/AT_END to limit where the item may appear in a
* document. */
int pos;
/** True iff this token is an annotation. */
int is_annotation;
} token_rule_t;
/**
* @name macros for defining token rules
*
* Helper macros to define token tables. 's' is a string, 't' is a
* directory_keyword, 'a' is a trio of argument multiplicities, and 'o' is an
* object syntax.
*/
/**@{*/
/** Appears to indicate the end of a table. */
#define END_OF_TABLE { NULL, NIL_, 0,0,0, NO_OBJ, 0, INT_MAX, 0, 0 }
/** An item with no restrictions: used for obsolete document types */
#define T(s,t,a,o) { s, t, a, o, 0, INT_MAX, 0, 0 }
/** An item with no restrictions on multiplicity or location. */
#define T0N(s,t,a,o) { s, t, a, o, 0, INT_MAX, 0, 0 }
/** An item that must appear exactly once */
#define T1(s,t,a,o) { s, t, a, o, 1, 1, 0, 0 }
/** An item that must appear exactly once, at the start of the document */
#define T1_START(s,t,a,o) { s, t, a, o, 1, 1, AT_START, 0 }
/** An item that must appear exactly once, at the end of the document */
#define T1_END(s,t,a,o) { s, t, a, o, 1, 1, AT_END, 0 }
/** An item that must appear one or more times */
#define T1N(s,t,a,o) { s, t, a, o, 1, INT_MAX, 0, 0 }
/** An item that must appear no more than once */
#define T01(s,t,a,o) { s, t, a, o, 0, 1, 0, 0 }
/** An annotation that must appear no more than once */
#define A01(s,t,a,o) { s, t, a, o, 0, 1, 0, 1 }
/** Argument multiplicity: any number of arguments. */
#define ARGS 0,INT_MAX,0
/** Argument multiplicity: no arguments. */
#define NO_ARGS 0,0,0
/** Argument multiplicity: concatenate all arguments. */
#define CONCAT_ARGS 1,1,1
/** Argument multiplicity: at least <b>n</b> arguments. */
#define GE(n) n,INT_MAX,0
/** Argument multiplicity: exactly <b>n</b> arguments. */
#define EQ(n) n,n,0
/**@}*/
/** List of tokens recognized in router descriptors */ /** List of tokens recognized in router descriptors */
static token_rule_t routerdesc_token_table[] = { static token_rule_t routerdesc_token_table[] = {
T0N("reject", K_REJECT, ARGS, NO_OBJ ), T0N("reject", K_REJECT, ARGS, NO_OBJ ),
@ -628,28 +368,8 @@ static int router_get_hashes_impl(const char *s, size_t s_len,
common_digests_t *digests, common_digests_t *digests,
const char *start_str, const char *end_str, const char *start_str, const char *end_str,
char end_char); char end_char);
static void token_clear(directory_token_t *tok);
static smartlist_t *find_all_by_keyword(smartlist_t *s, directory_keyword k);
static smartlist_t *find_all_exitpolicy(smartlist_t *s); static smartlist_t *find_all_exitpolicy(smartlist_t *s);
static directory_token_t *find_by_keyword_(smartlist_t *s,
directory_keyword keyword,
const char *keyword_str);
#define find_by_keyword(s, keyword) find_by_keyword_((s), (keyword), #keyword)
static directory_token_t *find_opt_by_keyword(smartlist_t *s,
directory_keyword keyword);
#define TS_ANNOTATIONS_OK 1
#define TS_NOCHECK 2
#define TS_NO_NEW_ANNOTATIONS 4
static int tokenize_string(memarea_t *area,
const char *start, const char *end,
smartlist_t *out,
token_rule_t *table,
int flags);
static directory_token_t *get_next_token(memarea_t *area,
const char **s,
const char *eos,
token_rule_t *table);
#define CST_CHECK_AUTHORITY (1<<0) #define CST_CHECK_AUTHORITY (1<<0)
#define CST_NO_CHECK_OBJTYPE (1<<1) #define CST_NO_CHECK_OBJTYPE (1<<1)
static int check_signature_token(const char *digest, static int check_signature_token(const char *digest,
@ -4738,445 +4458,6 @@ assert_addr_policy_ok(smartlist_t *lst)
}); });
} }
/*
* Low-level tokenizer for router descriptors and directories.
*/
/** Free all resources allocated for <b>tok</b> */
static void
token_clear(directory_token_t *tok)
{
if (tok->key)
crypto_pk_free(tok->key);
}
#define ALLOC_ZERO(sz) memarea_alloc_zero(area,sz)
#define ALLOC(sz) memarea_alloc(area,sz)
#define STRDUP(str) memarea_strdup(area,str)
#define STRNDUP(str,n) memarea_strndup(area,(str),(n))
#define RET_ERR(msg) \
STMT_BEGIN \
if (tok) token_clear(tok); \
tok = ALLOC_ZERO(sizeof(directory_token_t)); \
tok->tp = ERR_; \
tok->error = STRDUP(msg); \
goto done_tokenizing; \
STMT_END
/** Helper: make sure that the token <b>tok</b> with keyword <b>kwd</b> obeys
* the object syntax of <b>o_syn</b>. Allocate all storage in <b>area</b>.
* Return <b>tok</b> on success, or a new ERR_ token if the token didn't
* conform to the syntax we wanted.
**/
static inline directory_token_t *
token_check_object(memarea_t *area, const char *kwd,
directory_token_t *tok, obj_syntax o_syn)
{
char ebuf[128];
switch (o_syn) {
case NO_OBJ:
/* No object is allowed for this token. */
if (tok->object_body) {
tor_snprintf(ebuf, sizeof(ebuf), "Unexpected object for %s", kwd);
RET_ERR(ebuf);
}
if (tok->key) {
tor_snprintf(ebuf, sizeof(ebuf), "Unexpected public key for %s", kwd);
RET_ERR(ebuf);
}
break;
case NEED_OBJ:
/* There must be a (non-key) object. */
if (!tok->object_body) {
tor_snprintf(ebuf, sizeof(ebuf), "Missing object for %s", kwd);
RET_ERR(ebuf);
}
break;
case NEED_KEY_1024: /* There must be a 1024-bit public key. */
case NEED_SKEY_1024: /* There must be a 1024-bit private key. */
if (tok->key && crypto_pk_num_bits(tok->key) != PK_BYTES*8) {
tor_snprintf(ebuf, sizeof(ebuf), "Wrong size on key for %s: %d bits",
kwd, crypto_pk_num_bits(tok->key));
RET_ERR(ebuf);
}
/* fall through */
case NEED_KEY: /* There must be some kind of key. */
if (!tok->key) {
tor_snprintf(ebuf, sizeof(ebuf), "Missing public key for %s", kwd);
RET_ERR(ebuf);
}
if (o_syn != NEED_SKEY_1024) {
if (crypto_pk_key_is_private(tok->key)) {
tor_snprintf(ebuf, sizeof(ebuf),
"Private key given for %s, which wants a public key", kwd);
RET_ERR(ebuf);
}
} else { /* o_syn == NEED_SKEY_1024 */
if (!crypto_pk_key_is_private(tok->key)) {
tor_snprintf(ebuf, sizeof(ebuf),
"Public key given for %s, which wants a private key", kwd);
RET_ERR(ebuf);
}
}
break;
case OBJ_OK:
/* Anything goes with this token. */
break;
}
done_tokenizing:
return tok;
}
/** Helper: parse space-separated arguments from the string <b>s</b> ending at
* <b>eol</b>, and store them in the args field of <b>tok</b>. Store the
* number of parsed elements into the n_args field of <b>tok</b>. Allocate
* all storage in <b>area</b>. Return the number of arguments parsed, or
* return -1 if there was an insanely high number of arguments. */
static inline int
get_token_arguments(memarea_t *area, directory_token_t *tok,
const char *s, const char *eol)
{
/** Largest number of arguments we'll accept to any token, ever. */
#define MAX_ARGS 512
char *mem = memarea_strndup(area, s, eol-s);
char *cp = mem;
int j = 0;
char *args[MAX_ARGS];
while (*cp) {
if (j == MAX_ARGS)
return -1;
args[j++] = cp;
cp = (char*)find_whitespace(cp);
if (!cp || !*cp)
break; /* End of the line. */
*cp++ = '\0';
cp = (char*)eat_whitespace(cp);
}
tok->n_args = j;
tok->args = memarea_memdup(area, args, j*sizeof(char*));
return j;
#undef MAX_ARGS
}
/** Helper function: read the next token from *s, advance *s to the end of the
* token, and return the parsed token. Parse *<b>s</b> according to the list
* of tokens in <b>table</b>.
*/
static directory_token_t *
get_next_token(memarea_t *area,
const char **s, const char *eos, token_rule_t *table)
{
/** Reject any object at least this big; it is probably an overflow, an
* attack, a bug, or some other nonsense. */
#define MAX_UNPARSED_OBJECT_SIZE (128*1024)
/** Reject any line at least this big; it is probably an overflow, an
* attack, a bug, or some other nonsense. */
#define MAX_LINE_LENGTH (128*1024)
const char *next, *eol, *obstart;
size_t obname_len;
int i;
directory_token_t *tok;
obj_syntax o_syn = NO_OBJ;
char ebuf[128];
const char *kwd = "";
tor_assert(area);
tok = ALLOC_ZERO(sizeof(directory_token_t));
tok->tp = ERR_;
/* Set *s to first token, eol to end-of-line, next to after first token */
*s = eat_whitespace_eos(*s, eos); /* eat multi-line whitespace */
tor_assert(eos >= *s);
eol = memchr(*s, '\n', eos-*s);
if (!eol)
eol = eos;
if (eol - *s > MAX_LINE_LENGTH) {
RET_ERR("Line far too long");
}
next = find_whitespace_eos(*s, eol);
if (!strcmp_len(*s, "opt", next-*s)) {
/* Skip past an "opt" at the start of the line. */
*s = eat_whitespace_eos_no_nl(next, eol);
next = find_whitespace_eos(*s, eol);
} else if (*s == eos) { /* If no "opt", and end-of-line, line is invalid */
RET_ERR("Unexpected EOF");
}
/* Search the table for the appropriate entry. (I tried a binary search
* instead, but it wasn't any faster.) */
for (i = 0; table[i].t ; ++i) {
if (!strcmp_len(*s, table[i].t, next-*s)) {
/* We've found the keyword. */
kwd = table[i].t;
tok->tp = table[i].v;
o_syn = table[i].os;
*s = eat_whitespace_eos_no_nl(next, eol);
/* We go ahead whether there are arguments or not, so that tok->args is
* always set if we want arguments. */
if (table[i].concat_args) {
/* The keyword takes the line as a single argument */
tok->args = ALLOC(sizeof(char*));
tok->args[0] = STRNDUP(*s,eol-*s); /* Grab everything on line */
tok->n_args = 1;
} else {
/* This keyword takes multiple arguments. */
if (get_token_arguments(area, tok, *s, eol)<0) {
tor_snprintf(ebuf, sizeof(ebuf),"Far too many arguments to %s", kwd);
RET_ERR(ebuf);
}
*s = eol;
}
if (tok->n_args < table[i].min_args) {
tor_snprintf(ebuf, sizeof(ebuf), "Too few arguments to %s", kwd);
RET_ERR(ebuf);
} else if (tok->n_args > table[i].max_args) {
tor_snprintf(ebuf, sizeof(ebuf), "Too many arguments to %s", kwd);
RET_ERR(ebuf);
}
break;
}
}
if (tok->tp == ERR_) {
/* No keyword matched; call it an "K_opt" or "A_unrecognized" */
if (**s == '@')
tok->tp = A_UNKNOWN_;
else
tok->tp = K_OPT;
tok->args = ALLOC(sizeof(char*));
tok->args[0] = STRNDUP(*s, eol-*s);
tok->n_args = 1;
o_syn = OBJ_OK;
}
/* Check whether there's an object present */
*s = eat_whitespace_eos(eol, eos); /* Scan from end of first line */
tor_assert(eos >= *s);
eol = memchr(*s, '\n', eos-*s);
if (!eol || eol-*s<11 || strcmpstart(*s, "-----BEGIN ")) /* No object. */
goto check_object;
obstart = *s; /* Set obstart to start of object spec */
if (*s+16 >= eol || memchr(*s+11,'\0',eol-*s-16) || /* no short lines, */
strcmp_len(eol-5, "-----", 5) || /* nuls or invalid endings */
(eol-*s) > MAX_UNPARSED_OBJECT_SIZE) { /* name too long */
RET_ERR("Malformed object: bad begin line");
}
tok->object_type = STRNDUP(*s+11, eol-*s-16);
obname_len = eol-*s-16; /* store objname length here to avoid a strlen() */
*s = eol+1; /* Set *s to possible start of object data (could be eos) */
/* Go to the end of the object */
next = tor_memstr(*s, eos-*s, "-----END ");
if (!next) {
RET_ERR("Malformed object: missing object end line");
}
tor_assert(eos >= next);
eol = memchr(next, '\n', eos-next);
if (!eol) /* end-of-line marker, or eos if there's no '\n' */
eol = eos;
/* Validate the ending tag, which should be 9 + NAME + 5 + eol */
if ((size_t)(eol-next) != 9+obname_len+5 ||
strcmp_len(next+9, tok->object_type, obname_len) ||
strcmp_len(eol-5, "-----", 5)) {
tor_snprintf(ebuf, sizeof(ebuf), "Malformed object: mismatched end tag %s",
tok->object_type);
ebuf[sizeof(ebuf)-1] = '\0';
RET_ERR(ebuf);
}
if (next - *s > MAX_UNPARSED_OBJECT_SIZE)
RET_ERR("Couldn't parse object: missing footer or object much too big.");
if (!strcmp(tok->object_type, "RSA PUBLIC KEY")) { /* If it's a public key */
tok->key = crypto_pk_new();
if (crypto_pk_read_public_key_from_string(tok->key, obstart, eol-obstart))
RET_ERR("Couldn't parse public key.");
} else if (!strcmp(tok->object_type, "RSA PRIVATE KEY")) { /* private key */
tok->key = crypto_pk_new();
if (crypto_pk_read_private_key_from_string(tok->key, obstart, eol-obstart))
RET_ERR("Couldn't parse private key.");
} else { /* If it's something else, try to base64-decode it */
int r;
tok->object_body = ALLOC(next-*s); /* really, this is too much RAM. */
r = base64_decode(tok->object_body, next-*s, *s, next-*s);
if (r<0)
RET_ERR("Malformed object: bad base64-encoded data");
tok->object_size = r;
}
*s = eol;
check_object:
tok = token_check_object(area, kwd, tok, o_syn);
done_tokenizing:
return tok;
#undef RET_ERR
#undef ALLOC
#undef ALLOC_ZERO
#undef STRDUP
#undef STRNDUP
}
/** Read all tokens from a string between <b>start</b> and <b>end</b>, and add
* them to <b>out</b>. Parse according to the token rules in <b>table</b>.
* Caller must free tokens in <b>out</b>. If <b>end</b> is NULL, use the
* entire string.
*/
static int
tokenize_string(memarea_t *area,
const char *start, const char *end, smartlist_t *out,
token_rule_t *table, int flags)
{
const char **s;
directory_token_t *tok = NULL;
int counts[NIL_];
int i;
int first_nonannotation;
int prev_len = smartlist_len(out);
tor_assert(area);
s = &start;
if (!end) {
end = start+strlen(start);
} else {
/* it's only meaningful to check for nuls if we got an end-of-string ptr */
if (memchr(start, '\0', end-start)) {
log_warn(LD_DIR, "parse error: internal NUL character.");
return -1;
}
}
for (i = 0; i < NIL_; ++i)
counts[i] = 0;
SMARTLIST_FOREACH(out, const directory_token_t *, t, ++counts[t->tp]);
while (*s < end && (!tok || tok->tp != EOF_)) {
tok = get_next_token(area, s, end, table);
if (tok->tp == ERR_) {
log_warn(LD_DIR, "parse error: %s", tok->error);
token_clear(tok);
return -1;
}
++counts[tok->tp];
smartlist_add(out, tok);
*s = eat_whitespace_eos(*s, end);
}
if (flags & TS_NOCHECK)
return 0;
if ((flags & TS_ANNOTATIONS_OK)) {
first_nonannotation = -1;
for (i = 0; i < smartlist_len(out); ++i) {
tok = smartlist_get(out, i);
if (tok->tp < MIN_ANNOTATION || tok->tp > MAX_ANNOTATION) {
first_nonannotation = i;
break;
}
}
if (first_nonannotation < 0) {
log_warn(LD_DIR, "parse error: item contains only annotations");
return -1;
}
for (i=first_nonannotation; i < smartlist_len(out); ++i) {
tok = smartlist_get(out, i);
if (tok->tp >= MIN_ANNOTATION && tok->tp <= MAX_ANNOTATION) {
log_warn(LD_DIR, "parse error: Annotations mixed with keywords");
return -1;
}
}
if ((flags & TS_NO_NEW_ANNOTATIONS)) {
if (first_nonannotation != prev_len) {
log_warn(LD_DIR, "parse error: Unexpected annotations.");
return -1;
}
}
} else {
for (i=0; i < smartlist_len(out); ++i) {
tok = smartlist_get(out, i);
if (tok->tp >= MIN_ANNOTATION && tok->tp <= MAX_ANNOTATION) {
log_warn(LD_DIR, "parse error: no annotations allowed.");
return -1;
}
}
first_nonannotation = 0;
}
for (i = 0; table[i].t; ++i) {
if (counts[table[i].v] < table[i].min_cnt) {
log_warn(LD_DIR, "Parse error: missing %s element.", table[i].t);
return -1;
}
if (counts[table[i].v] > table[i].max_cnt) {
log_warn(LD_DIR, "Parse error: too many %s elements.", table[i].t);
return -1;
}
if (table[i].pos & AT_START) {
if (smartlist_len(out) < 1 ||
(tok = smartlist_get(out, first_nonannotation))->tp != table[i].v) {
log_warn(LD_DIR, "Parse error: first item is not %s.", table[i].t);
return -1;
}
}
if (table[i].pos & AT_END) {
if (smartlist_len(out) < 1 ||
(tok = smartlist_get(out, smartlist_len(out)-1))->tp != table[i].v) {
log_warn(LD_DIR, "Parse error: last item is not %s.", table[i].t);
return -1;
}
}
}
return 0;
}
/** Find the first token in <b>s</b> whose keyword is <b>keyword</b>; return
* NULL if no such keyword is found.
*/
static directory_token_t *
find_opt_by_keyword(smartlist_t *s, directory_keyword keyword)
{
SMARTLIST_FOREACH(s, directory_token_t *, t, if (t->tp == keyword) return t);
return NULL;
}
/** Find the first token in <b>s</b> whose keyword is <b>keyword</b>; fail
* with an assert if no such keyword is found.
*/
static directory_token_t *
find_by_keyword_(smartlist_t *s, directory_keyword keyword,
const char *keyword_as_string)
{
directory_token_t *tok = find_opt_by_keyword(s, keyword);
if (PREDICT_UNLIKELY(!tok)) {
log_err(LD_BUG, "Missing %s [%d] in directory object that should have "
"been validated. Internal error.", keyword_as_string, (int)keyword);
tor_assert(tok);
}
return tok;
}
/** If there are any directory_token_t entries in <b>s</b> whose keyword is
* <b>k</b>, return a newly allocated smartlist_t containing all such entries,
* in the same order in which they occur in <b>s</b>. Otherwise return
* NULL. */
static smartlist_t *
find_all_by_keyword(smartlist_t *s, directory_keyword k)
{
smartlist_t *out = NULL;
SMARTLIST_FOREACH(s, directory_token_t *, t,
if (t->tp == k) {
if (!out)
out = smartlist_new();
smartlist_add(out, t);
});
return out;
}
/** Return a newly allocated smartlist of all accept or reject tokens in /** Return a newly allocated smartlist of all accept or reject tokens in
* <b>s</b>. * <b>s</b>.
*/ */

View File

@ -11,7 +11,10 @@
#define CERT_TYPE_ID_SIGNING 0x04 #define CERT_TYPE_ID_SIGNING 0x04
#define CERT_TYPE_SIGNING_LINK 0x05 #define CERT_TYPE_SIGNING_LINK 0x05
#define CERT_TYPE_SIGNING_AUTH 0x06 #define CERT_TYPE_SIGNING_AUTH 0x06
#define CERT_TYPE_SIGNING_HS_DESC 0x08
#define CERT_TYPE_AUTH_HS_IP_KEY 0x09
#define CERT_TYPE_ONION_ID 0x0A #define CERT_TYPE_ONION_ID 0x0A
#define CERT_TYPE_CROSS_HS_IP_KEYS 0x0B
#define CERT_FLAG_INCLUDE_SIGNING_KEY 0x1 #define CERT_FLAG_INCLUDE_SIGNING_KEY 0x1

View File

@ -97,6 +97,8 @@ src_test_test_SOURCES = \
src/test/test_extorport.c \ src/test/test_extorport.c \
src/test/test_hs.c \ src/test/test_hs.c \
src/test/test_handles.c \ src/test/test_handles.c \
src/test/test_hs_cache.c \
src/test/test_hs_descriptor.c \
src/test/test_introduce.c \ src/test/test_introduce.c \
src/test/test_keypin.c \ src/test/test_keypin.c \
src/test/test_link_handshake.c \ src/test/test_link_handshake.c \

View File

@ -1205,6 +1205,8 @@ struct testgroup_t testgroups[] = {
{ "guardfraction/", guardfraction_tests }, { "guardfraction/", guardfraction_tests },
{ "extorport/", extorport_tests }, { "extorport/", extorport_tests },
{ "hs/", hs_tests }, { "hs/", hs_tests },
{ "hs_cache/", hs_cache },
{ "hs_descriptor/", hs_descriptor },
{ "introduce/", introduce_tests }, { "introduce/", introduce_tests },
{ "keypin/", keypin_tests }, { "keypin/", keypin_tests },
{ "link-handshake/", link_handshake_tests }, { "link-handshake/", link_handshake_tests },

View File

@ -198,6 +198,8 @@ extern struct testcase_t entrynodes_tests[];
extern struct testcase_t guardfraction_tests[]; extern struct testcase_t guardfraction_tests[];
extern struct testcase_t extorport_tests[]; extern struct testcase_t extorport_tests[];
extern struct testcase_t hs_tests[]; extern struct testcase_t hs_tests[];
extern struct testcase_t hs_cache[];
extern struct testcase_t hs_descriptor[];
extern struct testcase_t introduce_tests[]; extern struct testcase_t introduce_tests[];
extern struct testcase_t keypin_tests[]; extern struct testcase_t keypin_tests[];
extern struct testcase_t link_handshake_tests[]; extern struct testcase_t link_handshake_tests[];

View File

@ -10,6 +10,7 @@
#include "test.h" #include "test.h"
#include "connection.h" #include "connection.h"
#include "hs_common.h"
#include "main.h" #include "main.h"
#include "microdesc.h" #include "microdesc.h"
#include "networkstatus.h" #include "networkstatus.h"
@ -265,13 +266,9 @@ test_conn_get_rend_setup(const struct testcase_t *tc)
rend_cache_init(); rend_cache_init();
/* TODO: use directory_initiate_command_rend() to do this - maybe? */ /* TODO: use directory_initiate_command_rend() to do this - maybe? */
conn->rend_data = tor_malloc_zero(sizeof(rend_data_t));
tor_assert(strlen(TEST_CONN_REND_ADDR) == REND_SERVICE_ID_LEN_BASE32); tor_assert(strlen(TEST_CONN_REND_ADDR) == REND_SERVICE_ID_LEN_BASE32);
memcpy(conn->rend_data->onion_address, conn->rend_data = rend_data_client_create(TEST_CONN_REND_ADDR, NULL, NULL,
TEST_CONN_REND_ADDR, REND_NO_AUTH);
REND_SERVICE_ID_LEN_BASE32+1);
conn->rend_data->hsdirs_fp = smartlist_new();
assert_connection_ok(&conn->base_, time(NULL)); assert_connection_ok(&conn->base_, time(NULL));
return conn; return conn;
@ -551,7 +548,8 @@ test_conn_get_rend(void *arg)
tt_assert(connection_get_by_type_state_rendquery( tt_assert(connection_get_by_type_state_rendquery(
conn->base_.type, conn->base_.type,
conn->base_.state, conn->base_.state,
conn->rend_data->onion_address) rend_data_get_address(
conn->rend_data))
== TO_CONN(conn)); == TO_CONN(conn));
tt_assert(connection_get_by_type_state_rendquery( tt_assert(connection_get_by_type_state_rendquery(
TEST_CONN_TYPE, TEST_CONN_TYPE,

View File

@ -5740,6 +5740,67 @@ test_dir_assumed_flags(void *arg)
routerstatus_free(rs); routerstatus_free(rs);
} }
static void
test_dir_post_parsing(void *arg)
{
(void) arg;
/* Test the version parsing from an HS descriptor publish request. */
{
const char *end;
const char *prefix = "/tor/hs/";
int version = parse_hs_version_from_post("/tor/hs//publish", prefix, &end);
tt_int_op(version, OP_EQ, -1);
tt_ptr_op(end, OP_EQ, NULL);
version = parse_hs_version_from_post("/tor/hs/a/publish", prefix, &end);
tt_int_op(version, OP_EQ, -1);
tt_ptr_op(end, OP_EQ, NULL);
version = parse_hs_version_from_post("/tor/hs/3/publish", prefix, &end);
tt_int_op(version, OP_EQ, 3);
tt_str_op(end, OP_EQ, "/publish");
version = parse_hs_version_from_post("/tor/hs/42/publish", prefix, &end);
tt_int_op(version, OP_EQ, 42);
tt_str_op(end, OP_EQ, "/publish");
version = parse_hs_version_from_post("/tor/hs/18163/publish", prefix, &end);
tt_int_op(version, OP_EQ, 18163);
tt_str_op(end, OP_EQ, "/publish");
version = parse_hs_version_from_post("JUNKJUNKJUNK", prefix, &end);
tt_int_op(version, OP_EQ, -1);
tt_ptr_op(end, OP_EQ, NULL);
version = parse_hs_version_from_post("/tor/hs/3/publish", "blah", &end);
tt_int_op(version, OP_EQ, -1);
tt_ptr_op(end, OP_EQ, NULL);
/* Missing the '/' at the end of the prefix. */
version = parse_hs_version_from_post("/tor/hs/3/publish", "/tor/hs", &end);
tt_int_op(version, OP_EQ, -1);
tt_ptr_op(end, OP_EQ, NULL);
version = parse_hs_version_from_post("/random/blah/tor/hs/3/publish",
prefix, &end);
tt_int_op(version, OP_EQ, -1);
tt_ptr_op(end, OP_EQ, NULL);
version = parse_hs_version_from_post("/tor/hs/3/publish/random/junk",
prefix, &end);
tt_int_op(version, OP_EQ, 3);
tt_str_op(end, OP_EQ, "/publish/random/junk");
version = parse_hs_version_from_post("/tor/hs/-1/publish", prefix, &end);
tt_int_op(version, OP_EQ, -1);
tt_ptr_op(end, OP_EQ, NULL);
/* INT_MAX */
version = parse_hs_version_from_post("/tor/hs/2147483647/publish",
prefix, &end);
tt_int_op(version, OP_EQ, INT_MAX);
tt_str_op(end, OP_EQ, "/publish");
/* INT_MAX + 1*/
version = parse_hs_version_from_post("/tor/hs/2147483648/publish",
prefix, &end);
tt_int_op(version, OP_EQ, -1);
tt_ptr_op(end, OP_EQ, NULL);
}
done:
;
}
#define DIR_LEGACY(name) \ #define DIR_LEGACY(name) \
{ #name, test_dir_ ## name , TT_FORK, NULL, NULL } { #name, test_dir_ ## name , TT_FORK, NULL, NULL }
@ -5778,6 +5839,7 @@ struct testcase_t dir_tests[] = {
DIR(purpose_needs_anonymity_returns_true_by_default, 0), DIR(purpose_needs_anonymity_returns_true_by_default, 0),
DIR(purpose_needs_anonymity_returns_true_for_sensitive_purpose, 0), DIR(purpose_needs_anonymity_returns_true_for_sensitive_purpose, 0),
DIR(purpose_needs_anonymity_ret_false_for_non_sensitive_conn, 0), DIR(purpose_needs_anonymity_ret_false_for_non_sensitive_conn, 0),
DIR(post_parsing, 0),
DIR(fetch_type, 0), DIR(fetch_type, 0),
DIR(packages, 0), DIR(packages, 0),
DIR(download_status_schedule, 0), DIR(download_status_schedule, 0),

View File

@ -50,19 +50,6 @@ ENABLE_GCC_WARNING(overlength-strings)
#define NS_MODULE dir_handle_get #define NS_MODULE dir_handle_get
static void
connection_write_to_buf_mock(const char *string, size_t len,
connection_t *conn, int zlib)
{
(void) zlib;
tor_assert(string);
tor_assert(conn);
write_to_buf(string, len, conn->outbuf);
}
#define GET(path) "GET " path " HTTP/1.0\r\n\r\n"
#define NOT_FOUND "HTTP/1.0 404 Not found\r\n\r\n" #define NOT_FOUND "HTTP/1.0 404 Not found\r\n\r\n"
#define BAD_REQUEST "HTTP/1.0 400 Bad request\r\n\r\n" #define BAD_REQUEST "HTTP/1.0 400 Bad request\r\n\r\n"
#define SERVER_BUSY "HTTP/1.0 503 Directory busy, try again later\r\n\r\n" #define SERVER_BUSY "HTTP/1.0 503 Directory busy, try again later\r\n\r\n"

View File

@ -12,6 +12,7 @@
#include "routerlist.h" #include "routerlist.h"
#include "nodelist.h" #include "nodelist.h"
#include "buffers.h"
#include "test.h" #include "test.h"
#include "test_helpers.h" #include "test_helpers.h"
@ -92,3 +93,15 @@ helper_setup_fake_routerlist(void)
UNMOCK(router_descriptor_is_older_than); UNMOCK(router_descriptor_is_older_than);
} }
void
connection_write_to_buf_mock(const char *string, size_t len,
connection_t *conn, int zlib)
{
(void) zlib;
tor_assert(string);
tor_assert(conn);
write_to_buf(string, len, conn->outbuf);
}

View File

@ -11,6 +11,10 @@ const char *get_yesterday_date_str(void);
void helper_setup_fake_routerlist(void); void helper_setup_fake_routerlist(void);
#define GET(path) "GET " path " HTTP/1.0\r\n\r\n"
void connection_write_to_buf_mock(const char *string, size_t len,
connection_t *conn, int zlib);
extern const char TEST_DESCRIPTORS[]; extern const char TEST_DESCRIPTORS[];
#endif #endif

View File

@ -14,6 +14,7 @@
#include "test.h" #include "test.h"
#include "control.h" #include "control.h"
#include "config.h" #include "config.h"
#include "hs_common.h"
#include "rendcommon.h" #include "rendcommon.h"
#include "rendservice.h" #include "rendservice.h"
#include "routerset.h" #include "routerset.h"
@ -136,7 +137,7 @@ test_hs_desc_event(void *arg)
#define STR_DESC_ID_BASE32 "hba3gmcgpfivzfhx5rtfqkfdhv65yrj3" #define STR_DESC_ID_BASE32 "hba3gmcgpfivzfhx5rtfqkfdhv65yrj3"
int ret; int ret;
rend_data_t rend_query; rend_data_v2_t rend_query;
const char *expected_msg; const char *expected_msg;
char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
@ -148,11 +149,12 @@ test_hs_desc_event(void *arg)
/* setup rend_query struct */ /* setup rend_query struct */
memset(&rend_query, 0, sizeof(rend_query)); memset(&rend_query, 0, sizeof(rend_query));
rend_query.base_.version = 2;
strncpy(rend_query.onion_address, STR_HS_ADDR, strncpy(rend_query.onion_address, STR_HS_ADDR,
REND_SERVICE_ID_LEN_BASE32+1); REND_SERVICE_ID_LEN_BASE32+1);
rend_query.auth_type = REND_NO_AUTH; rend_query.auth_type = REND_NO_AUTH;
rend_query.hsdirs_fp = smartlist_new(); rend_query.base_.hsdirs_fp = smartlist_new();
smartlist_add(rend_query.hsdirs_fp, tor_memdup(HSDIR_EXIST_ID, smartlist_add(rend_query.base_.hsdirs_fp, tor_memdup(HSDIR_EXIST_ID,
DIGEST_LEN)); DIGEST_LEN));
/* Compute descriptor ID for replica 0, should be STR_DESC_ID_BASE32. */ /* Compute descriptor ID for replica 0, should be STR_DESC_ID_BASE32. */
@ -167,7 +169,7 @@ test_hs_desc_event(void *arg)
sizeof(desc_id_base32)); sizeof(desc_id_base32));
/* test request event */ /* test request event */
control_event_hs_descriptor_requested(&rend_query, HSDIR_EXIST_ID, control_event_hs_descriptor_requested(&rend_query.base_, HSDIR_EXIST_ID,
STR_DESC_ID_BASE32); STR_DESC_ID_BASE32);
expected_msg = "650 HS_DESC REQUESTED "STR_HS_ADDR" NO_AUTH "\ expected_msg = "650 HS_DESC REQUESTED "STR_HS_ADDR" NO_AUTH "\
STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32 "\r\n"; STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32 "\r\n";
@ -178,7 +180,7 @@ test_hs_desc_event(void *arg)
/* test received event */ /* test received event */
rend_query.auth_type = REND_BASIC_AUTH; rend_query.auth_type = REND_BASIC_AUTH;
control_event_hs_descriptor_received(rend_query.onion_address, control_event_hs_descriptor_received(rend_query.onion_address,
&rend_query, HSDIR_EXIST_ID); &rend_query.base_, HSDIR_EXIST_ID);
expected_msg = "650 HS_DESC RECEIVED "STR_HS_ADDR" BASIC_AUTH "\ expected_msg = "650 HS_DESC RECEIVED "STR_HS_ADDR" BASIC_AUTH "\
STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32"\r\n"; STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32"\r\n";
tt_assert(received_msg); tt_assert(received_msg);
@ -187,7 +189,7 @@ test_hs_desc_event(void *arg)
/* test failed event */ /* test failed event */
rend_query.auth_type = REND_STEALTH_AUTH; rend_query.auth_type = REND_STEALTH_AUTH;
control_event_hs_descriptor_failed(&rend_query, control_event_hs_descriptor_failed(&rend_query.base_,
HSDIR_NONE_EXIST_ID, HSDIR_NONE_EXIST_ID,
"QUERY_REJECTED"); "QUERY_REJECTED");
expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" STEALTH_AUTH "\ expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" STEALTH_AUTH "\
@ -198,7 +200,7 @@ test_hs_desc_event(void *arg)
/* test invalid auth type */ /* test invalid auth type */
rend_query.auth_type = 999; rend_query.auth_type = 999;
control_event_hs_descriptor_failed(&rend_query, control_event_hs_descriptor_failed(&rend_query.base_,
HSDIR_EXIST_ID, HSDIR_EXIST_ID,
"QUERY_REJECTED"); "QUERY_REJECTED");
expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" UNKNOWN "\ expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" UNKNOWN "\
@ -221,8 +223,8 @@ test_hs_desc_event(void *arg)
tt_str_op(received_msg, OP_EQ, exp_msg); tt_str_op(received_msg, OP_EQ, exp_msg);
tor_free(received_msg); tor_free(received_msg);
tor_free(exp_msg); tor_free(exp_msg);
SMARTLIST_FOREACH(rend_query.hsdirs_fp, char *, d, tor_free(d)); SMARTLIST_FOREACH(rend_query.base_.hsdirs_fp, char *, d, tor_free(d));
smartlist_free(rend_query.hsdirs_fp); smartlist_free(rend_query.base_.hsdirs_fp);
done: done:
UNMOCK(queue_control_event_string); UNMOCK(queue_control_event_string);
@ -322,42 +324,46 @@ test_hs_rend_data(void *arg)
client = rend_data_client_create(STR_HS_ADDR, desc_id, client_cookie, client = rend_data_client_create(STR_HS_ADDR, desc_id, client_cookie,
REND_NO_AUTH); REND_NO_AUTH);
tt_assert(client); tt_assert(client);
tt_int_op(client->auth_type, ==, REND_NO_AUTH); rend_data_v2_t *client_v2 = TO_REND_DATA_V2(client);
tt_str_op(client->onion_address, OP_EQ, STR_HS_ADDR); tt_int_op(client_v2->auth_type, ==, REND_NO_AUTH);
tt_mem_op(client->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id)); tt_str_op(client_v2->onion_address, OP_EQ, STR_HS_ADDR);
tt_mem_op(client->descriptor_cookie, OP_EQ, client_cookie, tt_mem_op(client_v2->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id));
tt_mem_op(client_v2->descriptor_cookie, OP_EQ, client_cookie,
sizeof(client_cookie)); sizeof(client_cookie));
tt_assert(client->hsdirs_fp); tt_assert(client->hsdirs_fp);
tt_int_op(smartlist_len(client->hsdirs_fp), ==, 0); tt_int_op(smartlist_len(client->hsdirs_fp), ==, 0);
for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
int ret = rend_compute_v2_desc_id(desc_id, client->onion_address, int ret = rend_compute_v2_desc_id(desc_id, client_v2->onion_address,
client->descriptor_cookie, now, rep); client_v2->descriptor_cookie, now, rep);
/* That shouldn't never fail. */ /* That shouldn't never fail. */
tt_int_op(ret, ==, 0); tt_int_op(ret, ==, 0);
tt_mem_op(client->descriptor_id[rep], OP_EQ, desc_id, sizeof(desc_id)); tt_mem_op(client_v2->descriptor_id[rep], OP_EQ, desc_id,
sizeof(desc_id));
} }
/* The rest should be zeroed because this is a client request. */ /* The rest should be zeroed because this is a client request. */
tt_int_op(tor_digest_is_zero(client->rend_pk_digest), ==, 1); tt_int_op(tor_digest_is_zero(client_v2->rend_pk_digest), ==, 1);
tt_int_op(tor_digest_is_zero(client->rend_cookie), ==, 1); tt_int_op(tor_digest_is_zero(client->rend_cookie), ==, 1);
/* Test dup(). */ /* Test dup(). */
client_dup = rend_data_dup(client); client_dup = rend_data_dup(client);
tt_assert(client_dup); tt_assert(client_dup);
tt_int_op(client_dup->auth_type, ==, client->auth_type); rend_data_v2_t *client_dup_v2 = TO_REND_DATA_V2(client_dup);
tt_str_op(client_dup->onion_address, OP_EQ, client->onion_address); tt_int_op(client_dup_v2->auth_type, ==, client_v2->auth_type);
tt_mem_op(client_dup->desc_id_fetch, OP_EQ, client->desc_id_fetch, tt_str_op(client_dup_v2->onion_address, OP_EQ, client_v2->onion_address);
sizeof(client_dup->desc_id_fetch)); tt_mem_op(client_dup_v2->desc_id_fetch, OP_EQ, client_v2->desc_id_fetch,
tt_mem_op(client_dup->descriptor_cookie, OP_EQ, client->descriptor_cookie, sizeof(client_dup_v2->desc_id_fetch));
sizeof(client_dup->descriptor_cookie)); tt_mem_op(client_dup_v2->descriptor_cookie, OP_EQ,
client_v2->descriptor_cookie,
sizeof(client_dup_v2->descriptor_cookie));
tt_assert(client_dup->hsdirs_fp); tt_assert(client_dup->hsdirs_fp);
tt_int_op(smartlist_len(client_dup->hsdirs_fp), ==, 0); tt_int_op(smartlist_len(client_dup->hsdirs_fp), ==, 0);
for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
tt_mem_op(client_dup->descriptor_id[rep], OP_EQ, tt_mem_op(client_dup_v2->descriptor_id[rep], OP_EQ,
client->descriptor_id[rep], DIGEST_LEN); client_v2->descriptor_id[rep], DIGEST_LEN);
} }
/* The rest should be zeroed because this is a client request. */ /* The rest should be zeroed because this is a client request. */
tt_int_op(tor_digest_is_zero(client_dup->rend_pk_digest), ==, 1); tt_int_op(tor_digest_is_zero(client_dup_v2->rend_pk_digest), ==, 1);
tt_int_op(tor_digest_is_zero(client_dup->rend_cookie), ==, 1); tt_int_op(tor_digest_is_zero(client_dup->rend_cookie), ==, 1);
rend_data_free(client); rend_data_free(client);
client = NULL; client = NULL;
@ -373,18 +379,19 @@ test_hs_rend_data(void *arg)
* zeroed out. */ * zeroed out. */
client = rend_data_client_create(NULL, desc_id, NULL, REND_BASIC_AUTH); client = rend_data_client_create(NULL, desc_id, NULL, REND_BASIC_AUTH);
tt_assert(client); tt_assert(client);
tt_int_op(client->auth_type, ==, REND_BASIC_AUTH); client_v2 = TO_REND_DATA_V2(client);
tt_int_op(strlen(client->onion_address), ==, 0); tt_int_op(client_v2->auth_type, ==, REND_BASIC_AUTH);
tt_mem_op(client->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id)); tt_int_op(strlen(client_v2->onion_address), ==, 0);
tt_int_op(tor_mem_is_zero(client->descriptor_cookie, tt_mem_op(client_v2->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id));
sizeof(client->descriptor_cookie)), ==, 1); tt_int_op(tor_mem_is_zero(client_v2->descriptor_cookie,
sizeof(client_v2->descriptor_cookie)), ==, 1);
tt_assert(client->hsdirs_fp); tt_assert(client->hsdirs_fp);
tt_int_op(smartlist_len(client->hsdirs_fp), ==, 0); tt_int_op(smartlist_len(client->hsdirs_fp), ==, 0);
for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
tt_int_op(tor_digest_is_zero(client->descriptor_id[rep]), ==, 1); tt_int_op(tor_digest_is_zero(client_v2->descriptor_id[rep]), ==, 1);
} }
/* The rest should be zeroed because this is a client request. */ /* The rest should be zeroed because this is a client request. */
tt_int_op(tor_digest_is_zero(client->rend_pk_digest), ==, 1); tt_int_op(tor_digest_is_zero(client_v2->rend_pk_digest), ==, 1);
tt_int_op(tor_digest_is_zero(client->rend_cookie), ==, 1); tt_int_op(tor_digest_is_zero(client->rend_cookie), ==, 1);
rend_data_free(client); rend_data_free(client);
client = NULL; client = NULL;
@ -398,37 +405,39 @@ test_hs_rend_data(void *arg)
service = rend_data_service_create(STR_HS_ADDR, rend_pk_digest, service = rend_data_service_create(STR_HS_ADDR, rend_pk_digest,
rend_cookie, REND_NO_AUTH); rend_cookie, REND_NO_AUTH);
tt_assert(service); tt_assert(service);
tt_int_op(service->auth_type, ==, REND_NO_AUTH); rend_data_v2_t *service_v2 = TO_REND_DATA_V2(service);
tt_str_op(service->onion_address, OP_EQ, STR_HS_ADDR); tt_int_op(service_v2->auth_type, ==, REND_NO_AUTH);
tt_mem_op(service->rend_pk_digest, OP_EQ, rend_pk_digest, tt_str_op(service_v2->onion_address, OP_EQ, STR_HS_ADDR);
tt_mem_op(service_v2->rend_pk_digest, OP_EQ, rend_pk_digest,
sizeof(rend_pk_digest)); sizeof(rend_pk_digest));
tt_mem_op(service->rend_cookie, OP_EQ, rend_cookie, sizeof(rend_cookie)); tt_mem_op(service->rend_cookie, OP_EQ, rend_cookie, sizeof(rend_cookie));
tt_assert(service->hsdirs_fp); tt_assert(service->hsdirs_fp);
tt_int_op(smartlist_len(service->hsdirs_fp), ==, 0); tt_int_op(smartlist_len(service->hsdirs_fp), ==, 0);
for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
tt_int_op(tor_digest_is_zero(service->descriptor_id[rep]), ==, 1); tt_int_op(tor_digest_is_zero(service_v2->descriptor_id[rep]), ==, 1);
} }
/* The rest should be zeroed because this is a service request. */ /* The rest should be zeroed because this is a service request. */
tt_int_op(tor_digest_is_zero(service->descriptor_cookie), ==, 1); tt_int_op(tor_digest_is_zero(service_v2->descriptor_cookie), ==, 1);
tt_int_op(tor_digest_is_zero(service->desc_id_fetch), ==, 1); tt_int_op(tor_digest_is_zero(service_v2->desc_id_fetch), ==, 1);
/* Test dup(). */ /* Test dup(). */
service_dup = rend_data_dup(service); service_dup = rend_data_dup(service);
rend_data_v2_t *service_dup_v2 = TO_REND_DATA_V2(service_dup);
tt_assert(service_dup); tt_assert(service_dup);
tt_int_op(service_dup->auth_type, ==, service->auth_type); tt_int_op(service_dup_v2->auth_type, ==, service_v2->auth_type);
tt_str_op(service_dup->onion_address, OP_EQ, service->onion_address); tt_str_op(service_dup_v2->onion_address, OP_EQ, service_v2->onion_address);
tt_mem_op(service_dup->rend_pk_digest, OP_EQ, service->rend_pk_digest, tt_mem_op(service_dup_v2->rend_pk_digest, OP_EQ, service_v2->rend_pk_digest,
sizeof(service_dup->rend_pk_digest)); sizeof(service_dup_v2->rend_pk_digest));
tt_mem_op(service_dup->rend_cookie, OP_EQ, service->rend_cookie, tt_mem_op(service_dup->rend_cookie, OP_EQ, service->rend_cookie,
sizeof(service_dup->rend_cookie)); sizeof(service_dup->rend_cookie));
tt_assert(service_dup->hsdirs_fp); tt_assert(service_dup->hsdirs_fp);
tt_int_op(smartlist_len(service_dup->hsdirs_fp), ==, 0); tt_int_op(smartlist_len(service_dup->hsdirs_fp), ==, 0);
for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
tt_int_op(tor_digest_is_zero(service_dup->descriptor_id[rep]), ==, 1); tt_int_op(tor_digest_is_zero(service_dup_v2->descriptor_id[rep]), ==, 1);
} }
/* The rest should be zeroed because this is a service request. */ /* The rest should be zeroed because this is a service request. */
tt_int_op(tor_digest_is_zero(service_dup->descriptor_cookie), ==, 1); tt_int_op(tor_digest_is_zero(service_dup_v2->descriptor_cookie), ==, 1);
tt_int_op(tor_digest_is_zero(service_dup->desc_id_fetch), ==, 1); tt_int_op(tor_digest_is_zero(service_dup_v2->desc_id_fetch), ==, 1);
done: done:
rend_data_free(service); rend_data_free(service);

479
src/test/test_hs_cache.c Normal file
View File

@ -0,0 +1,479 @@
/* Copyright (c) 2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file test_hs_cache.c
* \brief Test hidden service caches.
*/
#define HS_CACHE_PRIVATE
#include "ed25519_cert.h"
#include "hs_cache.h"
#include "rendcache.h"
#include "directory.h"
#include "connection.h"
#include "test_helpers.h"
#include "test.h"
/* Build an intro point using a blinded key and an address. */
static hs_desc_intro_point_t *
helper_build_intro_point(const ed25519_keypair_t *blinded_kp,
const char *addr)
{
int ret;
ed25519_keypair_t auth_kp;
hs_desc_intro_point_t *intro_point = NULL;
hs_desc_intro_point_t *ip = tor_malloc_zero(sizeof(*ip));
ip->link_specifiers = smartlist_new();
{
hs_desc_link_specifier_t *ls = tor_malloc_zero(sizeof(*ls));
ls->u.ap.port = 9001;
int family = tor_addr_parse(&ls->u.ap.addr, addr);
switch (family) {
case AF_INET:
ls->type = LS_IPV4;
break;
case AF_INET6:
ls->type = LS_IPV6;
break;
default:
/* Stop the test, not suppose to have an error. */
tt_int_op(family, OP_EQ, AF_INET);
}
smartlist_add(ip->link_specifiers, ls);
}
ret = ed25519_keypair_generate(&auth_kp, 0);
tt_int_op(ret, ==, 0);
ip->auth_key_cert = tor_cert_create(blinded_kp, CERT_TYPE_AUTH_HS_IP_KEY,
&auth_kp.pubkey, time(NULL),
HS_DESC_CERT_LIFETIME,
CERT_FLAG_INCLUDE_SIGNING_KEY);
tt_assert(ip->auth_key_cert);
ret = curve25519_keypair_generate(&ip->enc_key.curve25519, 0);
tt_int_op(ret, ==, 0);
ip->enc_key_type = HS_DESC_KEY_TYPE_CURVE25519;
intro_point = ip;
done:
return intro_point;
}
/* Return a valid hs_descriptor_t object. */
static hs_descriptor_t *
helper_build_hs_desc(uint64_t revision_counter, uint32_t lifetime,
ed25519_keypair_t *blinded_kp)
{
int ret;
hs_descriptor_t *descp = NULL, *desc = tor_malloc_zero(sizeof(*desc));
desc->plaintext_data.version = HS_DESC_SUPPORTED_FORMAT_VERSION_MAX;
ret = ed25519_keypair_generate(&desc->plaintext_data.signing_kp, 0);
tt_int_op(ret, ==, 0);
if (blinded_kp) {
memcpy(&desc->plaintext_data.blinded_kp, blinded_kp,
sizeof(ed25519_keypair_t));
} else {
ret = ed25519_keypair_generate(&desc->plaintext_data.blinded_kp, 0);
tt_int_op(ret, ==, 0);
}
desc->plaintext_data.signing_key_cert =
tor_cert_create(&desc->plaintext_data.blinded_kp,
CERT_TYPE_SIGNING_HS_DESC,
&desc->plaintext_data.signing_kp.pubkey, time(NULL),
3600, CERT_FLAG_INCLUDE_SIGNING_KEY);
tt_assert(desc->plaintext_data.signing_key_cert);
desc->plaintext_data.revision_counter = revision_counter;
desc->plaintext_data.lifetime_sec = lifetime;
/* Setup encrypted data section. */
desc->encrypted_data.create2_ntor = 1;
desc->encrypted_data.auth_types = smartlist_new();
smartlist_add(desc->encrypted_data.auth_types, strdup("ed25519"));
desc->encrypted_data.intro_points = smartlist_new();
/* Add an intro point. */
smartlist_add(desc->encrypted_data.intro_points,
helper_build_intro_point(&desc->plaintext_data.blinded_kp,
"1.2.3.4"));
descp = desc;
done:
return descp;
}
/* Static variable used to encoded the HSDir query. */
static char query_b64[256];
/* Build an HSDir query using a ed25519 keypair. */
static const char *
helper_get_hsdir_query(const hs_descriptor_t *desc)
{
ed25519_public_to_base64(query_b64,
&desc->plaintext_data.blinded_kp.pubkey);
return query_b64;
}
static void
init_test(void)
{
/* Always needed. Initialize the subsystem. */
hs_cache_init();
/* We need the v2 cache since our OOM and cache cleanup does poke at it. */
rend_cache_init();
}
static void
test_directory(void *arg)
{
int ret;
size_t oom_size;
char *desc1_str;
const char *desc_out;
hs_descriptor_t *desc1;
(void) arg;
init_test();
/* Generate a valid descriptor with normal values. */
desc1 = helper_build_hs_desc(42, 3 * 60 * 60, NULL);
tt_assert(desc1);
ret = hs_desc_encode_descriptor(desc1, &desc1_str);
tt_int_op(ret, OP_EQ, 0);
/* Very first basic test, should be able to be stored, survive a
* clean, found with a lookup and then cleaned by our OOM. */
{
ret = hs_cache_store_as_dir(desc1_str);
tt_int_op(ret, OP_EQ, 0);
/* Re-add, it should fail since we already have it. */
ret = hs_cache_store_as_dir(desc1_str);
tt_int_op(ret, OP_EQ, -1);
/* Try to clean now which should be fine, there is at worst few seconds
* between the store and this call. */
hs_cache_clean_as_dir(time(NULL));
/* We should find it in our cache. */
ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), &desc_out);
tt_int_op(ret, OP_EQ, 1);
tt_str_op(desc_out, OP_EQ, desc1_str);
/* Tell our OOM to run and to at least remove a byte which will result in
* removing the descriptor from our cache. */
oom_size = hs_cache_handle_oom(time(NULL), 1);
tt_int_op(oom_size, >=, 1);
ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), NULL);
tt_int_op(ret, OP_EQ, 0);
}
/* Store two descriptors and remove the expiring one only. */
{
hs_descriptor_t *desc_zero_lifetime = helper_build_hs_desc(1, 0, NULL);
tt_assert(desc_zero_lifetime);
char *desc_zero_lifetime_str;
ret = hs_desc_encode_descriptor(desc_zero_lifetime,
&desc_zero_lifetime_str);
tt_int_op(ret, OP_EQ, 0);
ret = hs_cache_store_as_dir(desc1_str);
tt_int_op(ret, OP_EQ, 0);
ret = hs_cache_store_as_dir(desc_zero_lifetime_str);
tt_int_op(ret, OP_EQ, 0);
/* This one should clear out our zero lifetime desc. */
hs_cache_clean_as_dir(time(NULL));
/* We should find desc1 in our cache. */
ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), &desc_out);
tt_int_op(ret, OP_EQ, 1);
tt_str_op(desc_out, OP_EQ, desc1_str);
/* We should NOT find our zero lifetime desc in our cache. */
ret = hs_cache_lookup_as_dir(3,
helper_get_hsdir_query(desc_zero_lifetime),
NULL);
tt_int_op(ret, OP_EQ, 0);
/* Cleanup our entire cache. */
oom_size = hs_cache_handle_oom(time(NULL), 1);
tt_int_op(oom_size, >=, 1);
}
/* Throw junk at it. */
{
ret = hs_cache_store_as_dir("blah");
tt_int_op(ret, OP_EQ, -1);
/* Poor attempt at tricking the decoding. */
ret = hs_cache_store_as_dir("hs-descriptor 3\nJUNK");
tt_int_op(ret, OP_EQ, -1);
/* Undecodable base64 query. */
ret = hs_cache_lookup_as_dir(3, "blah", NULL);
tt_int_op(ret, OP_EQ, -1);
/* Decodable base64 query but wrong ed25519 size. */
ret = hs_cache_lookup_as_dir(3, "dW5pY29ybg==", NULL);
tt_int_op(ret, OP_EQ, -1);
}
/* Test descriptor replacement with revision counter. */
{
char *new_desc_str;
/* Add a descriptor. */
ret = hs_cache_store_as_dir(desc1_str);
tt_int_op(ret, OP_EQ, 0);
ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), &desc_out);
tt_int_op(ret, OP_EQ, 1);
/* Bump revision counter. */
desc1->plaintext_data.revision_counter++;
ret = hs_desc_encode_descriptor(desc1, &new_desc_str);
tt_int_op(ret, OP_EQ, 0);
ret = hs_cache_store_as_dir(new_desc_str);
tt_int_op(ret, OP_EQ, 0);
/* Look it up, it should have been replaced. */
ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), &desc_out);
tt_int_op(ret, OP_EQ, 1);
tt_str_op(desc_out, OP_EQ, new_desc_str);
tor_free(new_desc_str);
}
done:
;
}
static void
test_clean_as_dir(void *arg)
{
size_t ret;
char *desc1_str = NULL;
time_t now = time(NULL);
hs_descriptor_t *desc1 = NULL;
(void) arg;
init_test();
/* Generate a valid descriptor with values. */
desc1 = helper_build_hs_desc(42, 3 * 60 * 60, NULL);
tt_assert(desc1);
ret = hs_desc_encode_descriptor(desc1, &desc1_str);
tt_int_op(ret, OP_EQ, 0);
ret = hs_cache_store_as_dir(desc1_str);
tt_int_op(ret, OP_EQ, 0);
/* With the lifetime being 3 hours, a cleanup shouldn't remove it. */
ret = cache_clean_v3_as_dir(now, 0);
tt_int_op(ret, ==, 0);
/* Should be present after clean up. */
ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), NULL);
tt_int_op(ret, OP_EQ, 1);
/* Set a cutoff 100 seconds in the past. It should not remove the entry
* since the entry is still recent enough. */
ret = cache_clean_v3_as_dir(now, now - 100);
tt_int_op(ret, ==, 0);
/* Should be present after clean up. */
ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), NULL);
tt_int_op(ret, OP_EQ, 1);
/* Set a cutoff of 100 seconds in the future. It should remove the entry
* that we've just added since it's not too old for the cutoff. */
ret = cache_clean_v3_as_dir(now, now + 100);
tt_int_op(ret, >, 0);
/* Shouldn't be present after clean up. */
ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), NULL);
tt_int_op(ret, OP_EQ, 0);
done:
hs_descriptor_free(desc1);
tor_free(desc1_str);
}
/* Test helper: Fetch an HS descriptor from an HSDir (for the hidden service
with <b>blinded_key</b>. Return the received descriptor string. */
static char *
helper_fetch_desc_from_hsdir(const ed25519_public_key_t *blinded_key)
{
int retval;
char *received_desc = NULL;
char *hsdir_query_str = NULL;
/* The dir conn we are going to simulate */
dir_connection_t *conn = NULL;
tor_addr_t mock_tor_addr;
/* First extract the blinded public key that we are going to use in our
query, and then build the actual query string. */
{
char hsdir_cache_key[ED25519_BASE64_LEN+1];
retval = ed25519_public_to_base64(hsdir_cache_key,
blinded_key);
tt_int_op(retval, ==, 0);
tor_asprintf(&hsdir_query_str, GET("/tor/hs/3/%s"), hsdir_cache_key);
}
/* Simulate an HTTP GET request to the HSDir */
conn = dir_connection_new(tor_addr_family(&mock_tor_addr));
TO_CONN(conn)->linked = 1;/* Pretend the conn is encrypted :) */
retval = directory_handle_command_get(conn, hsdir_query_str,
NULL, 0);
tt_int_op(retval, OP_EQ, 0);
/* Read the descriptor that the HSDir just served us */
{
char *headers = NULL;
size_t body_used = 0;
fetch_from_buf_http(TO_CONN(conn)->outbuf, &headers, MAX_HEADERS_SIZE,
&received_desc, &body_used, 10000, 0);
}
done:
tor_free(hsdir_query_str);
return received_desc;
}
/* Publish a descriptor to the HSDir, then fetch it. Check that the received
descriptor matches the published one. */
static void
test_upload_and_download_hs_desc(void *arg)
{
int retval;
hs_descriptor_t *published_desc;
char *published_desc_str = NULL;
char *received_desc_str = NULL;
(void) arg;
/* Initialize HSDir cache subsystem */
init_test();
/* Generate a valid descriptor with normal values. */
{
published_desc = helper_build_hs_desc(42, 3 * 60 * 60, NULL);
tt_assert(published_desc);
retval = hs_desc_encode_descriptor(published_desc, &published_desc_str);
tt_int_op(retval, OP_EQ, 0);
}
/* Publish descriptor to the HSDir */
{
retval = handle_post_hs_descriptor("/tor/hs/3/publish",published_desc_str);
tt_int_op(retval, ==, 200);
}
/* Simulate a fetch of the previously published descriptor */
{
const ed25519_public_key_t *blinded_key;
blinded_key = &published_desc->plaintext_data.blinded_kp.pubkey;
received_desc_str = helper_fetch_desc_from_hsdir(blinded_key);
}
/* Verify we received the exact same descriptor we published earlier */
tt_str_op(received_desc_str, OP_EQ, published_desc_str);
done:
tor_free(received_desc_str);
tor_free(published_desc_str);
}
/* Test that HSDirs reject outdated descriptors based on their revision
* counter. Also test that HSDirs correctly replace old descriptors with newer
* descriptors. */
static void
test_hsdir_revision_counter_check(void *arg)
{
int retval;
hs_descriptor_t *published_desc;
char *published_desc_str = NULL;
char *received_desc_str = NULL;
hs_descriptor_t *received_desc = NULL;
(void) arg;
/* Initialize HSDir cache subsystem */
init_test();
/* Generate a valid descriptor with normal values. */
{
published_desc = helper_build_hs_desc(1312, 3 * 60 * 60, NULL);
tt_assert(published_desc);
retval = hs_desc_encode_descriptor(published_desc, &published_desc_str);
tt_int_op(retval, OP_EQ, 0);
}
/* Publish descriptor to the HSDir */
{
retval = handle_post_hs_descriptor("/tor/hs/3/publish",published_desc_str);
tt_int_op(retval, ==, 200);
}
/* Try publishing again with the same revision counter: Should fail. */
{
retval = handle_post_hs_descriptor("/tor/hs/3/publish",published_desc_str);
tt_int_op(retval, ==, 400);
}
/* Fetch the published descriptor and validate the revision counter. */
{
const ed25519_public_key_t *blinded_key;
blinded_key = &published_desc->plaintext_data.blinded_kp.pubkey;
received_desc_str = helper_fetch_desc_from_hsdir(blinded_key);
retval = hs_desc_decode_descriptor(received_desc_str,NULL, &received_desc);
tt_int_op(retval, ==, 0);
tt_assert(received_desc);
/* Check that the revision counter is correct */
tt_int_op(received_desc->plaintext_data.revision_counter, ==, 1312);
}
/* Increment the revision counter and try again. Should work. */
{
published_desc->plaintext_data.revision_counter = 1313;
tor_free(published_desc_str);
retval = hs_desc_encode_descriptor(published_desc, &published_desc_str);
tt_int_op(retval, OP_EQ, 0);
retval = handle_post_hs_descriptor("/tor/hs/3/publish",published_desc_str);
tt_int_op(retval, ==, 200);
}
/* Again, fetch the published descriptor and perform the revision counter
validation. The revision counter must have changed. */
{
const ed25519_public_key_t *blinded_key;
blinded_key = &published_desc->plaintext_data.blinded_kp.pubkey;
received_desc_str = helper_fetch_desc_from_hsdir(blinded_key);
retval = hs_desc_decode_descriptor(received_desc_str,NULL, &received_desc);
tt_int_op(retval, ==, 0);
tt_assert(received_desc);
/* Check that the revision counter is the latest */
tt_int_op(received_desc->plaintext_data.revision_counter, ==, 1313);
}
done:
hs_descriptor_free(published_desc);
hs_descriptor_free(received_desc);
tor_free(received_desc_str);
tor_free(published_desc_str);
}
struct testcase_t hs_cache[] = {
/* Encoding tests. */
{ "directory", test_directory, TT_FORK,
NULL, NULL },
{ "clean_as_dir", test_clean_as_dir, TT_FORK,
NULL, NULL },
{ "hsdir_revision_counter_check", test_hsdir_revision_counter_check, TT_FORK,
NULL, NULL },
{ "upload_and_download_hs_desc", test_upload_and_download_hs_desc, TT_FORK,
NULL, NULL },
END_OF_TESTCASES
};

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@
#include "router.h" #include "router.h"
#include "routerlist.h" #include "routerlist.h"
#include "config.h" #include "config.h"
#include "hs_common.h"
#include <openssl/rsa.h> #include <openssl/rsa.h>
#include "rend_test_helpers.h" #include "rend_test_helpers.h"
#include "log_test_helpers.h" #include "log_test_helpers.h"
@ -24,15 +25,16 @@ static const int TIME_IN_THE_FUTURE = REND_CACHE_MAX_SKEW + 60;
static rend_data_t * static rend_data_t *
mock_rend_data(const char *onion_address) mock_rend_data(const char *onion_address)
{ {
rend_data_t *rend_query = tor_malloc_zero(sizeof(rend_data_t)); rend_data_v2_t *v2_data = tor_malloc_zero(sizeof(*v2_data));
rend_data_t *rend_query = &v2_data->base_;
rend_query->version = 2;
strlcpy(rend_query->onion_address, onion_address, strlcpy(v2_data->onion_address, onion_address,
sizeof(rend_query->onion_address)); sizeof(v2_data->onion_address));
rend_query->auth_type = REND_NO_AUTH; v2_data->auth_type = REND_NO_AUTH;
rend_query->hsdirs_fp = smartlist_new(); rend_query->hsdirs_fp = smartlist_new();
smartlist_add(rend_query->hsdirs_fp, tor_memdup("aaaaaaaaaaaaaaaaaaaaaaaa", smartlist_add(rend_query->hsdirs_fp, tor_memdup("aaaaaaaaaaaaaaaaaaaaaaaa",
DIGEST_LEN)); DIGEST_LEN));
return rend_query; return rend_query;
} }
@ -144,7 +146,8 @@ test_rend_cache_store_v2_desc_as_client(void *data)
// Test mismatch between service ID and onion address // Test mismatch between service ID and onion address
rend_cache_init(); rend_cache_init();
strncpy(mock_rend_query->onion_address, "abc", REND_SERVICE_ID_LEN_BASE32+1); strncpy(TO_REND_DATA_V2(mock_rend_query)->onion_address, "abc",
REND_SERVICE_ID_LEN_BASE32+1);
ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
desc_id_base32, desc_id_base32,
mock_rend_query, NULL); mock_rend_query, NULL);
@ -230,9 +233,9 @@ test_rend_cache_store_v2_desc_as_client(void *data)
generate_desc(RECENT_TIME, &desc_holder, &service_id, 3); generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
mock_rend_query = mock_rend_data(service_id); mock_rend_query = mock_rend_data(service_id);
mock_rend_query->auth_type = REND_BASIC_AUTH; TO_REND_DATA_V2(mock_rend_query)->auth_type = REND_BASIC_AUTH;
client_cookie[0] = 'A'; client_cookie[0] = 'A';
memcpy(mock_rend_query->descriptor_cookie, client_cookie, memcpy(TO_REND_DATA_V2(mock_rend_query)->descriptor_cookie, client_cookie,
REND_DESC_COOKIE_LEN); REND_DESC_COOKIE_LEN);
base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id, base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
DIGEST_LEN); DIGEST_LEN);
@ -250,7 +253,7 @@ test_rend_cache_store_v2_desc_as_client(void *data)
generate_desc(RECENT_TIME, &desc_holder, &service_id, 3); generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
mock_rend_query = mock_rend_data(service_id); mock_rend_query = mock_rend_data(service_id);
mock_rend_query->auth_type = REND_BASIC_AUTH; TO_REND_DATA_V2(mock_rend_query)->auth_type = REND_BASIC_AUTH;
base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id, base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
DIGEST_LEN); DIGEST_LEN);
ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
@ -1078,9 +1081,10 @@ static void
test_rend_cache_clean_v2_descs_as_dir(void *data) test_rend_cache_clean_v2_descs_as_dir(void *data)
{ {
rend_cache_entry_t *e; rend_cache_entry_t *e;
time_t now; time_t now, cutoff;
rend_service_descriptor_t *desc; rend_service_descriptor_t *desc;
now = time(NULL); now = time(NULL);
cutoff = now - (REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW);
const char key[DIGEST_LEN] = "abcde"; const char key[DIGEST_LEN] = "abcde";
(void)data; (void)data;
@ -1088,7 +1092,7 @@ test_rend_cache_clean_v2_descs_as_dir(void *data)
rend_cache_init(); rend_cache_init();
// Test running with an empty cache // Test running with an empty cache
rend_cache_clean_v2_descs_as_dir(now, 0); rend_cache_clean_v2_descs_as_dir(cutoff);
tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0); tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
// Test with only one new entry // Test with only one new entry
@ -1100,38 +1104,15 @@ test_rend_cache_clean_v2_descs_as_dir(void *data)
e->parsed = desc; e->parsed = desc;
digestmap_set(rend_cache_v2_dir, key, e); digestmap_set(rend_cache_v2_dir, key, e);
rend_cache_clean_v2_descs_as_dir(now, 0); /* Set the cutoff to minus 10 seconds. */
rend_cache_clean_v2_descs_as_dir(cutoff - 10);
tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 1); tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 1);
// Test with one old entry // Test with one old entry
desc->timestamp = now - (REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW + 1000); desc->timestamp = cutoff - 1000;
rend_cache_clean_v2_descs_as_dir(now, 0); rend_cache_clean_v2_descs_as_dir(cutoff);
tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0); tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
// Test with one entry that has an old last served
e = tor_malloc_zero(sizeof(rend_cache_entry_t));
e->last_served = now - (REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW + 1000);
desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
desc->timestamp = now;
desc->pk = pk_generate(0);
e->parsed = desc;
digestmap_set(rend_cache_v2_dir, key, e);
rend_cache_clean_v2_descs_as_dir(now, 0);
tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
// Test a run through asking for a large force_remove
e = tor_malloc_zero(sizeof(rend_cache_entry_t));
e->last_served = now;
desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
desc->timestamp = now;
desc->pk = pk_generate(0);
e->parsed = desc;
digestmap_set(rend_cache_v2_dir, key, e);
rend_cache_clean_v2_descs_as_dir(now, 20000);
tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 1);
done: done:
rend_cache_free_all(); rend_cache_free_all();
} }

View File

@ -430,6 +430,597 @@ ed25519_cert_extension_parse(ed25519_cert_extension_t **output, const uint8_t *i
} }
return result; return result;
} }
link_specifier_t *
link_specifier_new(void)
{
link_specifier_t *val = trunnel_calloc(1, sizeof(link_specifier_t));
if (NULL == val)
return NULL;
return val;
}
/** Release all storage held inside 'obj', but do not free 'obj'.
*/
static void
link_specifier_clear(link_specifier_t *obj)
{
(void) obj;
TRUNNEL_DYNARRAY_WIPE(&obj->un_unrecognized);
TRUNNEL_DYNARRAY_CLEAR(&obj->un_unrecognized);
}
void
link_specifier_free(link_specifier_t *obj)
{
if (obj == NULL)
return;
link_specifier_clear(obj);
trunnel_memwipe(obj, sizeof(link_specifier_t));
trunnel_free_(obj);
}
uint8_t
link_specifier_get_ls_type(link_specifier_t *inp)
{
return inp->ls_type;
}
int
link_specifier_set_ls_type(link_specifier_t *inp, uint8_t val)
{
inp->ls_type = val;
return 0;
}
uint8_t
link_specifier_get_ls_len(link_specifier_t *inp)
{
return inp->ls_len;
}
int
link_specifier_set_ls_len(link_specifier_t *inp, uint8_t val)
{
inp->ls_len = val;
return 0;
}
uint32_t
link_specifier_get_un_ipv4_addr(link_specifier_t *inp)
{
return inp->un_ipv4_addr;
}
int
link_specifier_set_un_ipv4_addr(link_specifier_t *inp, uint32_t val)
{
inp->un_ipv4_addr = val;
return 0;
}
uint16_t
link_specifier_get_un_ipv4_port(link_specifier_t *inp)
{
return inp->un_ipv4_port;
}
int
link_specifier_set_un_ipv4_port(link_specifier_t *inp, uint16_t val)
{
inp->un_ipv4_port = val;
return 0;
}
size_t
link_specifier_getlen_un_ipv6_addr(const link_specifier_t *inp)
{
(void)inp; return 16;
}
uint8_t
link_specifier_get_un_ipv6_addr(link_specifier_t *inp, size_t idx)
{
trunnel_assert(idx < 16);
return inp->un_ipv6_addr[idx];
}
uint8_t
link_specifier_getconst_un_ipv6_addr(const link_specifier_t *inp, size_t idx)
{
return link_specifier_get_un_ipv6_addr((link_specifier_t*)inp, idx);
}
int
link_specifier_set_un_ipv6_addr(link_specifier_t *inp, size_t idx, uint8_t elt)
{
trunnel_assert(idx < 16);
inp->un_ipv6_addr[idx] = elt;
return 0;
}
uint8_t *
link_specifier_getarray_un_ipv6_addr(link_specifier_t *inp)
{
return inp->un_ipv6_addr;
}
const uint8_t *
link_specifier_getconstarray_un_ipv6_addr(const link_specifier_t *inp)
{
return (const uint8_t *)link_specifier_getarray_un_ipv6_addr((link_specifier_t*)inp);
}
uint16_t
link_specifier_get_un_ipv6_port(link_specifier_t *inp)
{
return inp->un_ipv6_port;
}
int
link_specifier_set_un_ipv6_port(link_specifier_t *inp, uint16_t val)
{
inp->un_ipv6_port = val;
return 0;
}
size_t
link_specifier_getlen_un_legacy_id(const link_specifier_t *inp)
{
(void)inp; return 20;
}
uint8_t
link_specifier_get_un_legacy_id(link_specifier_t *inp, size_t idx)
{
trunnel_assert(idx < 20);
return inp->un_legacy_id[idx];
}
uint8_t
link_specifier_getconst_un_legacy_id(const link_specifier_t *inp, size_t idx)
{
return link_specifier_get_un_legacy_id((link_specifier_t*)inp, idx);
}
int
link_specifier_set_un_legacy_id(link_specifier_t *inp, size_t idx, uint8_t elt)
{
trunnel_assert(idx < 20);
inp->un_legacy_id[idx] = elt;
return 0;
}
uint8_t *
link_specifier_getarray_un_legacy_id(link_specifier_t *inp)
{
return inp->un_legacy_id;
}
const uint8_t *
link_specifier_getconstarray_un_legacy_id(const link_specifier_t *inp)
{
return (const uint8_t *)link_specifier_getarray_un_legacy_id((link_specifier_t*)inp);
}
size_t
link_specifier_getlen_un_ed25519_id(const link_specifier_t *inp)
{
(void)inp; return 32;
}
uint8_t
link_specifier_get_un_ed25519_id(link_specifier_t *inp, size_t idx)
{
trunnel_assert(idx < 32);
return inp->un_ed25519_id[idx];
}
uint8_t
link_specifier_getconst_un_ed25519_id(const link_specifier_t *inp, size_t idx)
{
return link_specifier_get_un_ed25519_id((link_specifier_t*)inp, idx);
}
int
link_specifier_set_un_ed25519_id(link_specifier_t *inp, size_t idx, uint8_t elt)
{
trunnel_assert(idx < 32);
inp->un_ed25519_id[idx] = elt;
return 0;
}
uint8_t *
link_specifier_getarray_un_ed25519_id(link_specifier_t *inp)
{
return inp->un_ed25519_id;
}
const uint8_t *
link_specifier_getconstarray_un_ed25519_id(const link_specifier_t *inp)
{
return (const uint8_t *)link_specifier_getarray_un_ed25519_id((link_specifier_t*)inp);
}
size_t
link_specifier_getlen_un_unrecognized(const link_specifier_t *inp)
{
return TRUNNEL_DYNARRAY_LEN(&inp->un_unrecognized);
}
uint8_t
link_specifier_get_un_unrecognized(link_specifier_t *inp, size_t idx)
{
return TRUNNEL_DYNARRAY_GET(&inp->un_unrecognized, idx);
}
uint8_t
link_specifier_getconst_un_unrecognized(const link_specifier_t *inp, size_t idx)
{
return link_specifier_get_un_unrecognized((link_specifier_t*)inp, idx);
}
int
link_specifier_set_un_unrecognized(link_specifier_t *inp, size_t idx, uint8_t elt)
{
TRUNNEL_DYNARRAY_SET(&inp->un_unrecognized, idx, elt);
return 0;
}
int
link_specifier_add_un_unrecognized(link_specifier_t *inp, uint8_t elt)
{
TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->un_unrecognized, elt, {});
return 0;
trunnel_alloc_failed:
TRUNNEL_SET_ERROR_CODE(inp);
return -1;
}
uint8_t *
link_specifier_getarray_un_unrecognized(link_specifier_t *inp)
{
return inp->un_unrecognized.elts_;
}
const uint8_t *
link_specifier_getconstarray_un_unrecognized(const link_specifier_t *inp)
{
return (const uint8_t *)link_specifier_getarray_un_unrecognized((link_specifier_t*)inp);
}
int
link_specifier_setlen_un_unrecognized(link_specifier_t *inp, size_t newlen)
{
uint8_t *newptr;
newptr = trunnel_dynarray_setlen(&inp->un_unrecognized.allocated_,
&inp->un_unrecognized.n_, inp->un_unrecognized.elts_, newlen,
sizeof(inp->un_unrecognized.elts_[0]), (trunnel_free_fn_t) NULL,
&inp->trunnel_error_code_);
if (newlen != 0 && newptr == NULL)
goto trunnel_alloc_failed;
inp->un_unrecognized.elts_ = newptr;
return 0;
trunnel_alloc_failed:
TRUNNEL_SET_ERROR_CODE(inp);
return -1;
}
const char *
link_specifier_check(const link_specifier_t *obj)
{
if (obj == NULL)
return "Object was NULL";
if (obj->trunnel_error_code_)
return "A set function failed on this object";
switch (obj->ls_type) {
case LS_IPV4:
break;
case LS_IPV6:
break;
case LS_LEGACY_ID:
break;
case LS_ED25519_ID:
break;
default:
break;
}
return NULL;
}
ssize_t
link_specifier_encoded_len(const link_specifier_t *obj)
{
ssize_t result = 0;
if (NULL != link_specifier_check(obj))
return -1;
/* Length of u8 ls_type */
result += 1;
/* Length of u8 ls_len */
result += 1;
switch (obj->ls_type) {
case LS_IPV4:
/* Length of u32 un_ipv4_addr */
result += 4;
/* Length of u16 un_ipv4_port */
result += 2;
break;
case LS_IPV6:
/* Length of u8 un_ipv6_addr[16] */
result += 16;
/* Length of u16 un_ipv6_port */
result += 2;
break;
case LS_LEGACY_ID:
/* Length of u8 un_legacy_id[20] */
result += 20;
break;
case LS_ED25519_ID:
/* Length of u8 un_ed25519_id[32] */
result += 32;
break;
default:
/* Length of u8 un_unrecognized[] */
result += TRUNNEL_DYNARRAY_LEN(&obj->un_unrecognized);
break;
}
return result;
}
int
link_specifier_clear_errors(link_specifier_t *obj)
{
int r = obj->trunnel_error_code_;
obj->trunnel_error_code_ = 0;
return r;
}
ssize_t
link_specifier_encode(uint8_t *output, const size_t avail, const link_specifier_t *obj)
{
ssize_t result = 0;
size_t written = 0;
uint8_t *ptr = output;
const char *msg;
#ifdef TRUNNEL_CHECK_ENCODED_LEN
const ssize_t encoded_len = link_specifier_encoded_len(obj);
#endif
uint8_t *backptr_ls_len = NULL;
if (NULL != (msg = link_specifier_check(obj)))
goto check_failed;
#ifdef TRUNNEL_CHECK_ENCODED_LEN
trunnel_assert(encoded_len >= 0);
#endif
/* Encode u8 ls_type */
trunnel_assert(written <= avail);
if (avail - written < 1)
goto truncated;
trunnel_set_uint8(ptr, (obj->ls_type));
written += 1; ptr += 1;
/* Encode u8 ls_len */
backptr_ls_len = ptr;
trunnel_assert(written <= avail);
if (avail - written < 1)
goto truncated;
trunnel_set_uint8(ptr, (obj->ls_len));
written += 1; ptr += 1;
{
size_t written_before_union = written;
/* Encode union un[ls_type] */
trunnel_assert(written <= avail);
switch (obj->ls_type) {
case LS_IPV4:
/* Encode u32 un_ipv4_addr */
trunnel_assert(written <= avail);
if (avail - written < 4)
goto truncated;
trunnel_set_uint32(ptr, trunnel_htonl(obj->un_ipv4_addr));
written += 4; ptr += 4;
/* Encode u16 un_ipv4_port */
trunnel_assert(written <= avail);
if (avail - written < 2)
goto truncated;
trunnel_set_uint16(ptr, trunnel_htons(obj->un_ipv4_port));
written += 2; ptr += 2;
break;
case LS_IPV6:
/* Encode u8 un_ipv6_addr[16] */
trunnel_assert(written <= avail);
if (avail - written < 16)
goto truncated;
memcpy(ptr, obj->un_ipv6_addr, 16);
written += 16; ptr += 16;
/* Encode u16 un_ipv6_port */
trunnel_assert(written <= avail);
if (avail - written < 2)
goto truncated;
trunnel_set_uint16(ptr, trunnel_htons(obj->un_ipv6_port));
written += 2; ptr += 2;
break;
case LS_LEGACY_ID:
/* Encode u8 un_legacy_id[20] */
trunnel_assert(written <= avail);
if (avail - written < 20)
goto truncated;
memcpy(ptr, obj->un_legacy_id, 20);
written += 20; ptr += 20;
break;
case LS_ED25519_ID:
/* Encode u8 un_ed25519_id[32] */
trunnel_assert(written <= avail);
if (avail - written < 32)
goto truncated;
memcpy(ptr, obj->un_ed25519_id, 32);
written += 32; ptr += 32;
break;
default:
/* Encode u8 un_unrecognized[] */
{
size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->un_unrecognized);
trunnel_assert(written <= avail);
if (avail - written < elt_len)
goto truncated;
if (elt_len)
memcpy(ptr, obj->un_unrecognized.elts_, elt_len);
written += elt_len; ptr += elt_len;
}
break;
}
/* Write the length field back to ls_len */
trunnel_assert(written >= written_before_union);
#if UINT8_MAX < SIZE_MAX
if (written - written_before_union > UINT8_MAX)
goto check_failed;
#endif
trunnel_set_uint8(backptr_ls_len, (written - written_before_union));
}
trunnel_assert(ptr == output + written);
#ifdef TRUNNEL_CHECK_ENCODED_LEN
{
trunnel_assert(encoded_len >= 0);
trunnel_assert((size_t)encoded_len == written);
}
#endif
return written;
truncated:
result = -2;
goto fail;
check_failed:
(void)msg;
result = -1;
goto fail;
fail:
trunnel_assert(result < 0);
return result;
}
/** As link_specifier_parse(), but do not allocate the output object.
*/
static ssize_t
link_specifier_parse_into(link_specifier_t *obj, const uint8_t *input, const size_t len_in)
{
const uint8_t *ptr = input;
size_t remaining = len_in;
ssize_t result = 0;
(void)result;
/* Parse u8 ls_type */
CHECK_REMAINING(1, truncated);
obj->ls_type = (trunnel_get_uint8(ptr));
remaining -= 1; ptr += 1;
/* Parse u8 ls_len */
CHECK_REMAINING(1, truncated);
obj->ls_len = (trunnel_get_uint8(ptr));
remaining -= 1; ptr += 1;
{
size_t remaining_after;
CHECK_REMAINING(obj->ls_len, truncated);
remaining_after = remaining - obj->ls_len;
remaining = obj->ls_len;
/* Parse union un[ls_type] */
switch (obj->ls_type) {
case LS_IPV4:
/* Parse u32 un_ipv4_addr */
CHECK_REMAINING(4, fail);
obj->un_ipv4_addr = trunnel_ntohl(trunnel_get_uint32(ptr));
remaining -= 4; ptr += 4;
/* Parse u16 un_ipv4_port */
CHECK_REMAINING(2, fail);
obj->un_ipv4_port = trunnel_ntohs(trunnel_get_uint16(ptr));
remaining -= 2; ptr += 2;
break;
case LS_IPV6:
/* Parse u8 un_ipv6_addr[16] */
CHECK_REMAINING(16, fail);
memcpy(obj->un_ipv6_addr, ptr, 16);
remaining -= 16; ptr += 16;
/* Parse u16 un_ipv6_port */
CHECK_REMAINING(2, fail);
obj->un_ipv6_port = trunnel_ntohs(trunnel_get_uint16(ptr));
remaining -= 2; ptr += 2;
break;
case LS_LEGACY_ID:
/* Parse u8 un_legacy_id[20] */
CHECK_REMAINING(20, fail);
memcpy(obj->un_legacy_id, ptr, 20);
remaining -= 20; ptr += 20;
break;
case LS_ED25519_ID:
/* Parse u8 un_ed25519_id[32] */
CHECK_REMAINING(32, fail);
memcpy(obj->un_ed25519_id, ptr, 32);
remaining -= 32; ptr += 32;
break;
default:
/* Parse u8 un_unrecognized[] */
TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->un_unrecognized, remaining, {});
obj->un_unrecognized.n_ = remaining;
if (remaining)
memcpy(obj->un_unrecognized.elts_, ptr, remaining);
ptr += remaining; remaining -= remaining;
break;
}
if (remaining != 0)
goto fail;
remaining = remaining_after;
}
trunnel_assert(ptr + remaining == input + len_in);
return len_in - remaining;
truncated:
return -2;
trunnel_alloc_failed:
return -1;
fail:
result = -1;
return result;
}
ssize_t
link_specifier_parse(link_specifier_t **output, const uint8_t *input, const size_t len_in)
{
ssize_t result;
*output = link_specifier_new();
if (NULL == *output)
return -1;
result = link_specifier_parse_into(*output, input, len_in);
if (result < 0) {
link_specifier_free(*output);
*output = NULL;
}
return result;
}
ed25519_cert_t * ed25519_cert_t *
ed25519_cert_new(void) ed25519_cert_new(void)
{ {
@ -937,3 +1528,293 @@ ed25519_cert_parse(ed25519_cert_t **output, const uint8_t *input, const size_t l
} }
return result; return result;
} }
link_specifier_list_t *
link_specifier_list_new(void)
{
link_specifier_list_t *val = trunnel_calloc(1, sizeof(link_specifier_list_t));
if (NULL == val)
return NULL;
return val;
}
/** Release all storage held inside 'obj', but do not free 'obj'.
*/
static void
link_specifier_list_clear(link_specifier_list_t *obj)
{
(void) obj;
{
unsigned idx;
for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->spec); ++idx) {
link_specifier_free(TRUNNEL_DYNARRAY_GET(&obj->spec, idx));
}
}
TRUNNEL_DYNARRAY_WIPE(&obj->spec);
TRUNNEL_DYNARRAY_CLEAR(&obj->spec);
}
void
link_specifier_list_free(link_specifier_list_t *obj)
{
if (obj == NULL)
return;
link_specifier_list_clear(obj);
trunnel_memwipe(obj, sizeof(link_specifier_list_t));
trunnel_free_(obj);
}
uint8_t
link_specifier_list_get_n_spec(link_specifier_list_t *inp)
{
return inp->n_spec;
}
int
link_specifier_list_set_n_spec(link_specifier_list_t *inp, uint8_t val)
{
inp->n_spec = val;
return 0;
}
size_t
link_specifier_list_getlen_spec(const link_specifier_list_t *inp)
{
return TRUNNEL_DYNARRAY_LEN(&inp->spec);
}
struct link_specifier_st *
link_specifier_list_get_spec(link_specifier_list_t *inp, size_t idx)
{
return TRUNNEL_DYNARRAY_GET(&inp->spec, idx);
}
const struct link_specifier_st *
link_specifier_list_getconst_spec(const link_specifier_list_t *inp, size_t idx)
{
return link_specifier_list_get_spec((link_specifier_list_t*)inp, idx);
}
int
link_specifier_list_set_spec(link_specifier_list_t *inp, size_t idx, struct link_specifier_st * elt)
{
link_specifier_t *oldval = TRUNNEL_DYNARRAY_GET(&inp->spec, idx);
if (oldval && oldval != elt)
link_specifier_free(oldval);
return link_specifier_list_set0_spec(inp, idx, elt);
}
int
link_specifier_list_set0_spec(link_specifier_list_t *inp, size_t idx, struct link_specifier_st * elt)
{
TRUNNEL_DYNARRAY_SET(&inp->spec, idx, elt);
return 0;
}
int
link_specifier_list_add_spec(link_specifier_list_t *inp, struct link_specifier_st * elt)
{
#if SIZE_MAX >= UINT8_MAX
if (inp->spec.n_ == UINT8_MAX)
goto trunnel_alloc_failed;
#endif
TRUNNEL_DYNARRAY_ADD(struct link_specifier_st *, &inp->spec, elt, {});
return 0;
trunnel_alloc_failed:
TRUNNEL_SET_ERROR_CODE(inp);
return -1;
}
struct link_specifier_st * *
link_specifier_list_getarray_spec(link_specifier_list_t *inp)
{
return inp->spec.elts_;
}
const struct link_specifier_st * const *
link_specifier_list_getconstarray_spec(const link_specifier_list_t *inp)
{
return (const struct link_specifier_st * const *)link_specifier_list_getarray_spec((link_specifier_list_t*)inp);
}
int
link_specifier_list_setlen_spec(link_specifier_list_t *inp, size_t newlen)
{
struct link_specifier_st * *newptr;
#if UINT8_MAX < SIZE_MAX
if (newlen > UINT8_MAX)
goto trunnel_alloc_failed;
#endif
newptr = trunnel_dynarray_setlen(&inp->spec.allocated_,
&inp->spec.n_, inp->spec.elts_, newlen,
sizeof(inp->spec.elts_[0]), (trunnel_free_fn_t) link_specifier_free,
&inp->trunnel_error_code_);
if (newlen != 0 && newptr == NULL)
goto trunnel_alloc_failed;
inp->spec.elts_ = newptr;
return 0;
trunnel_alloc_failed:
TRUNNEL_SET_ERROR_CODE(inp);
return -1;
}
const char *
link_specifier_list_check(const link_specifier_list_t *obj)
{
if (obj == NULL)
return "Object was NULL";
if (obj->trunnel_error_code_)
return "A set function failed on this object";
{
const char *msg;
unsigned idx;
for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->spec); ++idx) {
if (NULL != (msg = link_specifier_check(TRUNNEL_DYNARRAY_GET(&obj->spec, idx))))
return msg;
}
}
if (TRUNNEL_DYNARRAY_LEN(&obj->spec) != obj->n_spec)
return "Length mismatch for spec";
return NULL;
}
ssize_t
link_specifier_list_encoded_len(const link_specifier_list_t *obj)
{
ssize_t result = 0;
if (NULL != link_specifier_list_check(obj))
return -1;
/* Length of u8 n_spec */
result += 1;
/* Length of struct link_specifier spec[n_spec] */
{
unsigned idx;
for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->spec); ++idx) {
result += link_specifier_encoded_len(TRUNNEL_DYNARRAY_GET(&obj->spec, idx));
}
}
return result;
}
int
link_specifier_list_clear_errors(link_specifier_list_t *obj)
{
int r = obj->trunnel_error_code_;
obj->trunnel_error_code_ = 0;
return r;
}
ssize_t
link_specifier_list_encode(uint8_t *output, const size_t avail, const link_specifier_list_t *obj)
{
ssize_t result = 0;
size_t written = 0;
uint8_t *ptr = output;
const char *msg;
#ifdef TRUNNEL_CHECK_ENCODED_LEN
const ssize_t encoded_len = link_specifier_list_encoded_len(obj);
#endif
if (NULL != (msg = link_specifier_list_check(obj)))
goto check_failed;
#ifdef TRUNNEL_CHECK_ENCODED_LEN
trunnel_assert(encoded_len >= 0);
#endif
/* Encode u8 n_spec */
trunnel_assert(written <= avail);
if (avail - written < 1)
goto truncated;
trunnel_set_uint8(ptr, (obj->n_spec));
written += 1; ptr += 1;
/* Encode struct link_specifier spec[n_spec] */
{
unsigned idx;
for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->spec); ++idx) {
trunnel_assert(written <= avail);
result = link_specifier_encode(ptr, avail - written, TRUNNEL_DYNARRAY_GET(&obj->spec, idx));
if (result < 0)
goto fail; /* XXXXXXX !*/
written += result; ptr += result;
}
}
trunnel_assert(ptr == output + written);
#ifdef TRUNNEL_CHECK_ENCODED_LEN
{
trunnel_assert(encoded_len >= 0);
trunnel_assert((size_t)encoded_len == written);
}
#endif
return written;
truncated:
result = -2;
goto fail;
check_failed:
(void)msg;
result = -1;
goto fail;
fail:
trunnel_assert(result < 0);
return result;
}
/** As link_specifier_list_parse(), but do not allocate the output
* object.
*/
static ssize_t
link_specifier_list_parse_into(link_specifier_list_t *obj, const uint8_t *input, const size_t len_in)
{
const uint8_t *ptr = input;
size_t remaining = len_in;
ssize_t result = 0;
(void)result;
/* Parse u8 n_spec */
CHECK_REMAINING(1, truncated);
obj->n_spec = (trunnel_get_uint8(ptr));
remaining -= 1; ptr += 1;
/* Parse struct link_specifier spec[n_spec] */
TRUNNEL_DYNARRAY_EXPAND(link_specifier_t *, &obj->spec, obj->n_spec, {});
{
link_specifier_t * elt;
unsigned idx;
for (idx = 0; idx < obj->n_spec; ++idx) {
result = link_specifier_parse(&elt, ptr, remaining);
if (result < 0)
goto relay_fail;
trunnel_assert((size_t)result <= remaining);
remaining -= result; ptr += result;
TRUNNEL_DYNARRAY_ADD(link_specifier_t *, &obj->spec, elt, {link_specifier_free(elt);});
}
}
trunnel_assert(ptr + remaining == input + len_in);
return len_in - remaining;
truncated:
return -2;
relay_fail:
trunnel_assert(result < 0);
return result;
trunnel_alloc_failed:
return -1;
}
ssize_t
link_specifier_list_parse(link_specifier_list_t **output, const uint8_t *input, const size_t len_in)
{
ssize_t result;
*output = link_specifier_list_new();
if (NULL == *output)
return -1;
result = link_specifier_list_parse_into(*output, input, len_in);
if (result < 0) {
link_specifier_list_free(*output);
*output = NULL;
}
return result;
}

View File

@ -10,6 +10,10 @@
#define CERTEXT_SIGNED_WITH_KEY 4 #define CERTEXT_SIGNED_WITH_KEY 4
#define CERTEXT_FLAG_AFFECTS_VALIDATION 1 #define CERTEXT_FLAG_AFFECTS_VALIDATION 1
#define LS_IPV4 0
#define LS_IPV6 1
#define LS_LEGACY_ID 2
#define LS_ED25519_ID 3
#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_ED25519_CERT_EXTENSION) #if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_ED25519_CERT_EXTENSION)
struct ed25519_cert_extension_st { struct ed25519_cert_extension_st {
uint16_t ext_length; uint16_t ext_length;
@ -21,6 +25,21 @@ struct ed25519_cert_extension_st {
}; };
#endif #endif
typedef struct ed25519_cert_extension_st ed25519_cert_extension_t; typedef struct ed25519_cert_extension_st ed25519_cert_extension_t;
#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_LINK_SPECIFIER)
struct link_specifier_st {
uint8_t ls_type;
uint8_t ls_len;
uint32_t un_ipv4_addr;
uint16_t un_ipv4_port;
uint8_t un_ipv6_addr[16];
uint16_t un_ipv6_port;
uint8_t un_legacy_id[20];
uint8_t un_ed25519_id[32];
TRUNNEL_DYNARRAY_HEAD(, uint8_t) un_unrecognized;
uint8_t trunnel_error_code_;
};
#endif
typedef struct link_specifier_st link_specifier_t;
#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_ED25519_CERT) #if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_ED25519_CERT)
struct ed25519_cert_st { struct ed25519_cert_st {
uint8_t version; uint8_t version;
@ -35,6 +54,14 @@ struct ed25519_cert_st {
}; };
#endif #endif
typedef struct ed25519_cert_st ed25519_cert_t; typedef struct ed25519_cert_st ed25519_cert_t;
#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_LINK_SPECIFIER_LIST)
struct link_specifier_list_st {
uint8_t n_spec;
TRUNNEL_DYNARRAY_HEAD(, struct link_specifier_st *) spec;
uint8_t trunnel_error_code_;
};
#endif
typedef struct link_specifier_list_st link_specifier_list_t;
/** Return a newly allocated ed25519_cert_extension with all elements /** Return a newly allocated ed25519_cert_extension with all elements
* set to zero. * set to zero.
*/ */
@ -157,6 +184,196 @@ const uint8_t * ed25519_cert_extension_getconstarray_un_unparsed(const ed25519_
* success; return -1 and set the error code on 'inp' on failure. * success; return -1 and set the error code on 'inp' on failure.
*/ */
int ed25519_cert_extension_setlen_un_unparsed(ed25519_cert_extension_t *inp, size_t newlen); int ed25519_cert_extension_setlen_un_unparsed(ed25519_cert_extension_t *inp, size_t newlen);
/** Return a newly allocated link_specifier with all elements set to
* zero.
*/
link_specifier_t *link_specifier_new(void);
/** Release all storage held by the link_specifier in 'victim'. (Do
* nothing if 'victim' is NULL.)
*/
void link_specifier_free(link_specifier_t *victim);
/** Try to parse a link_specifier from the buffer in 'input', using up
* to 'len_in' bytes from the input buffer. On success, return the
* number of bytes consumed and set *output to the newly allocated
* link_specifier_t. On failure, return -2 if the input appears
* truncated, and -1 if the input is otherwise invalid.
*/
ssize_t link_specifier_parse(link_specifier_t **output, const uint8_t *input, const size_t len_in);
/** Return the number of bytes we expect to need to encode the
* link_specifier in 'obj'. On failure, return a negative value. Note
* that this value may be an overestimate, and can even be an
* underestimate for certain unencodeable objects.
*/
ssize_t link_specifier_encoded_len(const link_specifier_t *obj);
/** Try to encode the link_specifier from 'input' into the buffer at
* 'output', using up to 'avail' bytes of the output buffer. On
* success, return the number of bytes used. On failure, return -2 if
* the buffer was not long enough, and -1 if the input was invalid.
*/
ssize_t link_specifier_encode(uint8_t *output, size_t avail, const link_specifier_t *input);
/** Check whether the internal state of the link_specifier in 'obj' is
* consistent. Return NULL if it is, and a short message if it is not.
*/
const char *link_specifier_check(const link_specifier_t *obj);
/** Clear any errors that were set on the object 'obj' by its setter
* functions. Return true iff errors were cleared.
*/
int link_specifier_clear_errors(link_specifier_t *obj);
/** Return the value of the ls_type field of the link_specifier_t in
* 'inp'
*/
uint8_t link_specifier_get_ls_type(link_specifier_t *inp);
/** Set the value of the ls_type field of the link_specifier_t in
* 'inp' to 'val'. Return 0 on success; return -1 and set the error
* code on 'inp' on failure.
*/
int link_specifier_set_ls_type(link_specifier_t *inp, uint8_t val);
/** Return the value of the ls_len field of the link_specifier_t in
* 'inp'
*/
uint8_t link_specifier_get_ls_len(link_specifier_t *inp);
/** Set the value of the ls_len field of the link_specifier_t in 'inp'
* to 'val'. Return 0 on success; return -1 and set the error code on
* 'inp' on failure.
*/
int link_specifier_set_ls_len(link_specifier_t *inp, uint8_t val);
/** Return the value of the un_ipv4_addr field of the link_specifier_t
* in 'inp'
*/
uint32_t link_specifier_get_un_ipv4_addr(link_specifier_t *inp);
/** Set the value of the un_ipv4_addr field of the link_specifier_t in
* 'inp' to 'val'. Return 0 on success; return -1 and set the error
* code on 'inp' on failure.
*/
int link_specifier_set_un_ipv4_addr(link_specifier_t *inp, uint32_t val);
/** Return the value of the un_ipv4_port field of the link_specifier_t
* in 'inp'
*/
uint16_t link_specifier_get_un_ipv4_port(link_specifier_t *inp);
/** Set the value of the un_ipv4_port field of the link_specifier_t in
* 'inp' to 'val'. Return 0 on success; return -1 and set the error
* code on 'inp' on failure.
*/
int link_specifier_set_un_ipv4_port(link_specifier_t *inp, uint16_t val);
/** Return the (constant) length of the array holding the un_ipv6_addr
* field of the link_specifier_t in 'inp'.
*/
size_t link_specifier_getlen_un_ipv6_addr(const link_specifier_t *inp);
/** Return the element at position 'idx' of the fixed array field
* un_ipv6_addr of the link_specifier_t in 'inp'.
*/
uint8_t link_specifier_get_un_ipv6_addr(link_specifier_t *inp, size_t idx);
/** As link_specifier_get_un_ipv6_addr, but take and return a const
* pointer
*/
uint8_t link_specifier_getconst_un_ipv6_addr(const link_specifier_t *inp, size_t idx);
/** Change the element at position 'idx' of the fixed array field
* un_ipv6_addr of the link_specifier_t in 'inp', so that it will hold
* the value 'elt'.
*/
int link_specifier_set_un_ipv6_addr(link_specifier_t *inp, size_t idx, uint8_t elt);
/** Return a pointer to the 16-element array field un_ipv6_addr of
* 'inp'.
*/
uint8_t * link_specifier_getarray_un_ipv6_addr(link_specifier_t *inp);
/** As link_specifier_get_un_ipv6_addr, but take and return a const
* pointer
*/
const uint8_t * link_specifier_getconstarray_un_ipv6_addr(const link_specifier_t *inp);
/** Return the value of the un_ipv6_port field of the link_specifier_t
* in 'inp'
*/
uint16_t link_specifier_get_un_ipv6_port(link_specifier_t *inp);
/** Set the value of the un_ipv6_port field of the link_specifier_t in
* 'inp' to 'val'. Return 0 on success; return -1 and set the error
* code on 'inp' on failure.
*/
int link_specifier_set_un_ipv6_port(link_specifier_t *inp, uint16_t val);
/** Return the (constant) length of the array holding the un_legacy_id
* field of the link_specifier_t in 'inp'.
*/
size_t link_specifier_getlen_un_legacy_id(const link_specifier_t *inp);
/** Return the element at position 'idx' of the fixed array field
* un_legacy_id of the link_specifier_t in 'inp'.
*/
uint8_t link_specifier_get_un_legacy_id(link_specifier_t *inp, size_t idx);
/** As link_specifier_get_un_legacy_id, but take and return a const
* pointer
*/
uint8_t link_specifier_getconst_un_legacy_id(const link_specifier_t *inp, size_t idx);
/** Change the element at position 'idx' of the fixed array field
* un_legacy_id of the link_specifier_t in 'inp', so that it will hold
* the value 'elt'.
*/
int link_specifier_set_un_legacy_id(link_specifier_t *inp, size_t idx, uint8_t elt);
/** Return a pointer to the 20-element array field un_legacy_id of
* 'inp'.
*/
uint8_t * link_specifier_getarray_un_legacy_id(link_specifier_t *inp);
/** As link_specifier_get_un_legacy_id, but take and return a const
* pointer
*/
const uint8_t * link_specifier_getconstarray_un_legacy_id(const link_specifier_t *inp);
/** Return the (constant) length of the array holding the
* un_ed25519_id field of the link_specifier_t in 'inp'.
*/
size_t link_specifier_getlen_un_ed25519_id(const link_specifier_t *inp);
/** Return the element at position 'idx' of the fixed array field
* un_ed25519_id of the link_specifier_t in 'inp'.
*/
uint8_t link_specifier_get_un_ed25519_id(link_specifier_t *inp, size_t idx);
/** As link_specifier_get_un_ed25519_id, but take and return a const
* pointer
*/
uint8_t link_specifier_getconst_un_ed25519_id(const link_specifier_t *inp, size_t idx);
/** Change the element at position 'idx' of the fixed array field
* un_ed25519_id of the link_specifier_t in 'inp', so that it will
* hold the value 'elt'.
*/
int link_specifier_set_un_ed25519_id(link_specifier_t *inp, size_t idx, uint8_t elt);
/** Return a pointer to the 32-element array field un_ed25519_id of
* 'inp'.
*/
uint8_t * link_specifier_getarray_un_ed25519_id(link_specifier_t *inp);
/** As link_specifier_get_un_ed25519_id, but take and return a const
* pointer
*/
const uint8_t * link_specifier_getconstarray_un_ed25519_id(const link_specifier_t *inp);
/** Return the length of the dynamic array holding the un_unrecognized
* field of the link_specifier_t in 'inp'.
*/
size_t link_specifier_getlen_un_unrecognized(const link_specifier_t *inp);
/** Return the element at position 'idx' of the dynamic array field
* un_unrecognized of the link_specifier_t in 'inp'.
*/
uint8_t link_specifier_get_un_unrecognized(link_specifier_t *inp, size_t idx);
/** As link_specifier_get_un_unrecognized, but take and return a const
* pointer
*/
uint8_t link_specifier_getconst_un_unrecognized(const link_specifier_t *inp, size_t idx);
/** Change the element at position 'idx' of the dynamic array field
* un_unrecognized of the link_specifier_t in 'inp', so that it will
* hold the value 'elt'.
*/
int link_specifier_set_un_unrecognized(link_specifier_t *inp, size_t idx, uint8_t elt);
/** Append a new element 'elt' to the dynamic array field
* un_unrecognized of the link_specifier_t in 'inp'.
*/
int link_specifier_add_un_unrecognized(link_specifier_t *inp, uint8_t elt);
/** Return a pointer to the variable-length array field
* un_unrecognized of 'inp'.
*/
uint8_t * link_specifier_getarray_un_unrecognized(link_specifier_t *inp);
/** As link_specifier_get_un_unrecognized, but take and return a const
* pointer
*/
const uint8_t * link_specifier_getconstarray_un_unrecognized(const link_specifier_t *inp);
/** Change the length of the variable-length array field
* un_unrecognized of 'inp' to 'newlen'.Fill extra elements with 0.
* Return 0 on success; return -1 and set the error code on 'inp' on
* failure.
*/
int link_specifier_setlen_un_unrecognized(link_specifier_t *inp, size_t newlen);
/** Return a newly allocated ed25519_cert with all elements set to /** Return a newly allocated ed25519_cert with all elements set to
* zero. * zero.
*/ */
@ -319,6 +536,89 @@ uint8_t * ed25519_cert_getarray_signature(ed25519_cert_t *inp);
/** As ed25519_cert_get_signature, but take and return a const pointer /** As ed25519_cert_get_signature, but take and return a const pointer
*/ */
const uint8_t * ed25519_cert_getconstarray_signature(const ed25519_cert_t *inp); const uint8_t * ed25519_cert_getconstarray_signature(const ed25519_cert_t *inp);
/** Return a newly allocated link_specifier_list with all elements set
* to zero.
*/
link_specifier_list_t *link_specifier_list_new(void);
/** Release all storage held by the link_specifier_list in 'victim'.
* (Do nothing if 'victim' is NULL.)
*/
void link_specifier_list_free(link_specifier_list_t *victim);
/** Try to parse a link_specifier_list from the buffer in 'input',
* using up to 'len_in' bytes from the input buffer. On success,
* return the number of bytes consumed and set *output to the newly
* allocated link_specifier_list_t. On failure, return -2 if the input
* appears truncated, and -1 if the input is otherwise invalid.
*/
ssize_t link_specifier_list_parse(link_specifier_list_t **output, const uint8_t *input, const size_t len_in);
/** Return the number of bytes we expect to need to encode the
* link_specifier_list in 'obj'. On failure, return a negative value.
* Note that this value may be an overestimate, and can even be an
* underestimate for certain unencodeable objects.
*/
ssize_t link_specifier_list_encoded_len(const link_specifier_list_t *obj);
/** Try to encode the link_specifier_list from 'input' into the buffer
* at 'output', using up to 'avail' bytes of the output buffer. On
* success, return the number of bytes used. On failure, return -2 if
* the buffer was not long enough, and -1 if the input was invalid.
*/
ssize_t link_specifier_list_encode(uint8_t *output, size_t avail, const link_specifier_list_t *input);
/** Check whether the internal state of the link_specifier_list in
* 'obj' is consistent. Return NULL if it is, and a short message if
* it is not.
*/
const char *link_specifier_list_check(const link_specifier_list_t *obj);
/** Clear any errors that were set on the object 'obj' by its setter
* functions. Return true iff errors were cleared.
*/
int link_specifier_list_clear_errors(link_specifier_list_t *obj);
/** Return the value of the n_spec field of the link_specifier_list_t
* in 'inp'
*/
uint8_t link_specifier_list_get_n_spec(link_specifier_list_t *inp);
/** Set the value of the n_spec field of the link_specifier_list_t in
* 'inp' to 'val'. Return 0 on success; return -1 and set the error
* code on 'inp' on failure.
*/
int link_specifier_list_set_n_spec(link_specifier_list_t *inp, uint8_t val);
/** Return the length of the dynamic array holding the spec field of
* the link_specifier_list_t in 'inp'.
*/
size_t link_specifier_list_getlen_spec(const link_specifier_list_t *inp);
/** Return the element at position 'idx' of the dynamic array field
* spec of the link_specifier_list_t in 'inp'.
*/
struct link_specifier_st * link_specifier_list_get_spec(link_specifier_list_t *inp, size_t idx);
/** As link_specifier_list_get_spec, but take and return a const
* pointer
*/
const struct link_specifier_st * link_specifier_list_getconst_spec(const link_specifier_list_t *inp, size_t idx);
/** Change the element at position 'idx' of the dynamic array field
* spec of the link_specifier_list_t in 'inp', so that it will hold
* the value 'elt'. Free the previous value, if any.
*/
int link_specifier_list_set_spec(link_specifier_list_t *inp, size_t idx, struct link_specifier_st * elt);
/** As link_specifier_list_set_spec, but does not free the previous
* value.
*/
int link_specifier_list_set0_spec(link_specifier_list_t *inp, size_t idx, struct link_specifier_st * elt);
/** Append a new element 'elt' to the dynamic array field spec of the
* link_specifier_list_t in 'inp'.
*/
int link_specifier_list_add_spec(link_specifier_list_t *inp, struct link_specifier_st * elt);
/** Return a pointer to the variable-length array field spec of 'inp'.
*/
struct link_specifier_st * * link_specifier_list_getarray_spec(link_specifier_list_t *inp);
/** As link_specifier_list_get_spec, but take and return a const
* pointer
*/
const struct link_specifier_st * const * link_specifier_list_getconstarray_spec(const link_specifier_list_t *inp);
/** Change the length of the variable-length array field spec of 'inp'
* to 'newlen'.Fill extra elements with NULL; free removed elements.
* Return 0 on success; return -1 and set the error code on 'inp' on
* failure.
*/
int link_specifier_list_setlen_spec(link_specifier_list_t *inp, size_t newlen);
#endif #endif

View File

@ -55,6 +55,7 @@ struct auth02_cell {
u8 rand[24]; u8 rand[24];
u8 sig[64]; u8 sig[64];
} }
*/
const LS_IPV4 = 0x00; const LS_IPV4 = 0x00;
const LS_IPV6 = 0x01; const LS_IPV6 = 0x01;
@ -73,4 +74,8 @@ struct link_specifier {
default: u8 unrecognized[]; default: u8 unrecognized[];
}; };
} }
*/
struct link_specifier_list {
u8 n_spec;
struct link_specifier spec[n_spec];
}