summaryrefslogtreecommitdiff
path: root/src/or/rendservice.c
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2008-09-24 14:44:29 +0000
committerNick Mathewson <nickm@torproject.org>2008-09-24 14:44:29 +0000
commit8bc1536a9e83f68f7aeed34714a8b05deb2aabe9 (patch)
tree52bb1ed6f364e11eef201ed0224ffc46b70e39eb /src/or/rendservice.c
parent982a22a1214f2394c9a8cd2099b749f3e780ab87 (diff)
downloadtor-8bc1536a9e83f68f7aeed34714a8b05deb2aabe9.tar.gz
tor-8bc1536a9e83f68f7aeed34714a8b05deb2aabe9.zip
Add patch 4 from Karsten for proposal 121, slightly modified. Karsten should definitely re-review the bits I changed.
svn:r16955
Diffstat (limited to 'src/or/rendservice.c')
-rw-r--r--src/or/rendservice.c268
1 files changed, 218 insertions, 50 deletions
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 8f224e81de..878fe0227d 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -69,6 +69,11 @@ typedef struct rend_service_t {
* up-to-date. */
time_t next_upload_time; /**< Scheduled next hidden service descriptor
* upload time. */
+ /** Map from digests of diffie-hellman values INTRODUCE2 to time_t of when
+ * they were received; used to prevent replays. */
+ digestmap_t *accepted_intros;
+ /** Time at which we last removed expired values from accepted_intros. */
+ time_t last_cleaned_accepted_intros;
} rend_service_t;
/** A list of rend_service_t's for services run on this OP.
@@ -125,6 +130,8 @@ rend_service_free(rend_service_t *service)
rend_authorized_client_free(c););
smartlist_free(service->clients);
}
+ if (service->accepted_intros)
+ digestmap_free(service->accepted_intros, _tor_free);
tor_free(service);
}
@@ -360,7 +367,7 @@ rend_config_services(or_options_t *options, int validate_only)
if (smartlist_len(type_names_split) < 2) {
log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
"auth-type '%s', but no client names.",
- service->auth_type == 1 ? "basic" : "stealth");
+ service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth");
SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
smartlist_free(type_names_split);
continue;
@@ -423,7 +430,7 @@ rend_config_services(or_options_t *options, int validate_only)
"authorization type '%s'.",
smartlist_len(service->clients),
service->auth_type == REND_BASIC_AUTH ? 512 : 16,
- service->auth_type == 1 ? "basic" : "stealth");
+ service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth");
rend_service_free(service);
return -1;
}
@@ -720,8 +727,10 @@ rend_service_load_keys(void)
tor_free(client_keys_str);
strmap_free(parsed_clients, rend_authorized_client_strmap_item_free);
if (r<0) {
- abort_writing_to_file(open_cfile);
- abort_writing_to_file(open_hfile);
+ if (open_cfile)
+ abort_writing_to_file(open_cfile);
+ if (open_hfile)
+ abort_writing_to_file(open_hfile);
return r;
} else {
finish_writing_to_file(open_cfile);
@@ -764,6 +773,64 @@ rend_service_requires_uptime(rend_service_t *service)
return 0;
}
+/** Check client authorization of a given <b>descriptor_cookie</b> for
+ * <b>service</b>. Return 1 for success and 0 for failure. */
+static int
+rend_check_authorization(rend_service_t *service,
+ const char *descriptor_cookie)
+{
+ rend_authorized_client_t *auth_client = NULL;
+ tor_assert(service);
+ tor_assert(descriptor_cookie);
+ if (!service->clients) {
+ log_warn(LD_BUG, "Can't check authorization for a service that has no "
+ "authorized clients configured.");
+ return 0;
+ }
+
+ /* Look up client authorization by descriptor cookie. */
+ SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *, client, {
+ if (!memcmp(client->descriptor_cookie, descriptor_cookie,
+ REND_DESC_COOKIE_LEN)) {
+ auth_client = client;
+ break;
+ }
+ });
+ if (!auth_client) {
+ char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64];
+ base64_encode(descriptor_cookie_base64, sizeof(descriptor_cookie_base64),
+ descriptor_cookie, REND_DESC_COOKIE_LEN);
+ log_info(LD_REND, "No authorization found for descriptor cookie '%s'! "
+ "Dropping cell!",
+ descriptor_cookie_base64);
+ return 0;
+ }
+
+ /* Allow the request. */
+ log_debug(LD_REND, "Client %s authorized for service %s.",
+ auth_client->client_name, service->service_id);
+ return 1;
+}
+
+/** Remove elements from <b>service</b>'s replay cache that are old enough to
+ * be noticed by timestamp checking. */
+static void
+clean_accepted_intros(rend_service_t *service, time_t now)
+{
+ const time_t cutoff = now - REND_REPLAY_TIME_INTERVAL;
+
+ service->last_cleaned_accepted_intros = now;
+ if (!service->accepted_intros)
+ return;
+
+ DIGESTMAP_FOREACH_MODIFY(service->accepted_intros, digest, time_t *, t) {
+ if (*t < cutoff) {
+ tor_free(t);
+ MAP_DEL_CURRENT(digest);
+ }
+ } DIGESTMAP_FOREACH_END;
+}
+
/******
* Handle cells
******/
@@ -780,7 +847,7 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
char buf[RELAY_PAYLOAD_SIZE];
char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */
rend_service_t *service;
- int r, i;
+ int r, i, v3_shift = 0;
size_t len, keylen;
crypto_dh_env_t *dh = NULL;
origin_circuit_t *launched = NULL;
@@ -791,9 +858,17 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
int reason = END_CIRC_REASON_TORPROTOCOL;
crypto_pk_env_t *intro_key;
char intro_key_digest[DIGEST_LEN];
+ int auth_type;
+ size_t auth_len = 0;
+ char auth_data[REND_DESC_COOKIE_LEN];
+ crypto_digest_env_t *digest = NULL;
+ time_t now = time(NULL);
+ char diffie_hellman_hash[DIGEST_LEN];
+ time_t *access_time;
+ tor_assert(circuit->rend_data);
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
- circuit->rend_pk_digest, REND_SERVICE_ID_LEN);
+ circuit->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN);
log_info(LD_REND, "Received INTRODUCE2 cell for service %s on circ %d.",
escaped(serviceid), circuit->_base.n_circ_id);
@@ -814,7 +889,8 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
/* look up service depending on circuit. */
service = rend_service_get_by_pk_digest_and_version(
- circuit->rend_pk_digest, circuit->rend_desc_version);
+ circuit->rend_data->rend_pk_digest,
+ circuit->rend_data->rend_desc_version);
if (!service) {
log_warn(LD_REND, "Got an INTRODUCE2 cell for an unrecognized service %s.",
escaped(serviceid));
@@ -822,7 +898,7 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
}
/* if descriptor version is 2, use intro key instead of service key. */
- if (circuit->rend_desc_version == 0) {
+ if (circuit->rend_data->rend_desc_version == 0) {
intro_key = service->private_key;
} else {
intro_key = circuit->intro_key;
@@ -854,33 +930,70 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
return -1;
}
len = r;
- if (*buf == 2) {
+ if (*buf == 3) {
+ /* Version 3 INTRODUCE2 cell. */
+ time_t ts = 0, now = time(NULL);
+ v3_shift = 1;
+ auth_type = buf[1];
+ switch (auth_type) {
+ case REND_BASIC_AUTH:
+ /* fall through */
+ case REND_STEALTH_AUTH:
+ auth_len = ntohs(get_uint16(buf+2));
+ if (auth_len != REND_DESC_COOKIE_LEN) {
+ log_info(LD_REND, "Wrong auth data size %d, should be %d.",
+ (int)auth_len, REND_DESC_COOKIE_LEN);
+ return -1;
+ }
+ memcpy(auth_data, buf+4, sizeof(auth_data));
+ v3_shift += 2+REND_DESC_COOKIE_LEN;
+ break;
+ case REND_NO_AUTH:
+ break;
+ default:
+ log_info(LD_REND, "Unknown authorization type '%d'", auth_type);
+ }
+
+ /* Check timestamp. */
+ memcpy((char*)&ts, buf+1+v3_shift, sizeof(uint32_t));
+ v3_shift += 4;
+ ts = ntohl(ts);
+ if ((now - ts) < -1 * REND_REPLAY_TIME_INTERVAL / 2 ||
+ (now - ts) > REND_REPLAY_TIME_INTERVAL / 2) {
+ log_warn(LD_REND, "INTRODUCE2 cell is too %s. Discarding.",
+ (now - ts) < 0 ? "old" : "new");
+ return -1;
+ }
+ }
+ if (*buf == 2 || *buf == 3) {
/* Version 2 INTRODUCE2 cell. */
int klen;
extend_info = tor_malloc_zero(sizeof(extend_info_t));
- tor_addr_from_ipv4n(&extend_info->addr, get_uint32(buf+1));
- extend_info->port = ntohs(get_uint16(buf+5));
- memcpy(extend_info->identity_digest, buf+7, DIGEST_LEN);
+ tor_addr_from_ipv4n(&extend_info->addr, get_uint32(buf+v3_shift+1));
+ extend_info->port = ntohs(get_uint16(buf+v3_shift+5));
+ memcpy(extend_info->identity_digest, buf+v3_shift+7,
+ DIGEST_LEN);
extend_info->nickname[0] = '$';
base16_encode(extend_info->nickname+1, sizeof(extend_info->nickname)-1,
extend_info->identity_digest, DIGEST_LEN);
- klen = ntohs(get_uint16(buf+7+DIGEST_LEN));
- if ((int)len != 7+DIGEST_LEN+2+klen+20+128) {
- log_warn(LD_PROTOCOL, "Bad length %u for version 2 INTRODUCE2 cell.",
- (int)len);
+ klen = ntohs(get_uint16(buf+v3_shift+7+DIGEST_LEN));
+ if ((int)len != v3_shift+7+DIGEST_LEN+2+klen+20+128) {
+ log_warn(LD_PROTOCOL, "Bad length %u for version %d INTRODUCE2 cell.",
+ (int)len, *buf);
reason = END_CIRC_REASON_TORPROTOCOL;
goto err;
}
- extend_info->onion_key = crypto_pk_asn1_decode(buf+7+DIGEST_LEN+2, klen);
+ extend_info->onion_key =
+ crypto_pk_asn1_decode(buf+v3_shift+7+DIGEST_LEN+2, klen);
if (!extend_info->onion_key) {
- log_warn(LD_PROTOCOL,
- "Error decoding onion key in version 2 INTRODUCE2 cell.");
+ log_warn(LD_PROTOCOL, "Error decoding onion key in version %d "
+ "INTRODUCE2 cell.", *buf);
reason = END_CIRC_REASON_TORPROTOCOL;
goto err;
}
- ptr = buf+7+DIGEST_LEN+2+klen;
- len -= 7+DIGEST_LEN+2+klen;
+ ptr = buf+v3_shift+7+DIGEST_LEN+2+klen;
+ len -= v3_shift+7+DIGEST_LEN+2+klen;
} else {
char *rp_nickname;
size_t nickname_field_len;
@@ -932,6 +1045,54 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
r_cookie = ptr;
base16_encode(hexcookie,9,r_cookie,4);
+ /* Determine hash of Diffie-Hellman, part 1 to detect replays. */
+ digest = crypto_new_digest_env();
+ crypto_digest_add_bytes(digest, ptr+REND_COOKIE_LEN, DH_KEY_LEN);
+ crypto_digest_get_digest(digest, diffie_hellman_hash, DIGEST_LEN);
+ crypto_free_digest_env(digest);
+
+ /* Iterate over past requests, remove those which are older than one hour,
+ * and check whether there is one with same Diffie-Hellman, part 1. */
+ if (!service->accepted_intros)
+ service->accepted_intros = digestmap_new();
+
+ access_time = digestmap_get(service->accepted_intros, diffie_hellman_hash);
+ if (access_time != NULL) {
+ log_warn(LD_REND, "Possible replay detected! We received an "
+ "INTRODUCE2 cell with same first part of "
+ "Diffie-Hellman handshake %d seconds ago. Dropping "
+ "cell.",
+ (int) (now - *access_time));
+ goto err;
+ }
+
+ /* Add request to access history, including time and hash of
+ * Diffie-Hellman, part 1. */
+ access_time = tor_malloc(sizeof(time_t));
+ *access_time = now;
+ digestmap_set(service->accepted_intros, diffie_hellman_hash, access_time);
+ if (service->last_cleaned_accepted_intros + REND_REPLAY_TIME_INTERVAL < now)
+ clean_accepted_intros(service, now);
+
+ /* If the service performs client authorization, check included auth data. */
+ if (service->clients) {
+ if (auth_len > 0) {
+ if (rend_check_authorization(service, auth_data)) {
+ log_info(LD_REND, "Authorization data in INTRODUCE2 cell are valid.");
+ } else {
+ log_info(LD_REND, "The authorization data that are contained in "
+ "the INTRODUCE2 cell are invalid. Dropping cell.");
+ reason = END_CIRC_REASON_CONNECTFAILED;
+ goto err;
+ }
+ } else {
+ log_info(LD_REND, "INTRODUCE2 cell does not contain authentication "
+ "data, but we require client authorization. Dropping cell.");
+ reason = END_CIRC_REASON_CONNECTFAILED;
+ goto err;
+ }
+ }
+
/* Try DH handshake... */
dh = crypto_dh_new();
if (!dh || crypto_dh_generate_public(dh)<0) {
@@ -976,12 +1137,14 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
escaped_safe_str(extend_info->nickname), hexcookie, serviceid);
tor_assert(launched->build_state);
/* Fill in the circuit's state. */
- memcpy(launched->rend_pk_digest, circuit->rend_pk_digest,
+ launched->rend_data = tor_malloc_zero(sizeof(rend_data_t));
+ memcpy(launched->rend_data->rend_pk_digest,
+ circuit->rend_data->rend_pk_digest,
DIGEST_LEN);
- memcpy(launched->rend_cookie, r_cookie, REND_COOKIE_LEN);
- strlcpy(launched->rend_query, service->service_id,
- sizeof(launched->rend_query));
- launched->rend_desc_version = service->descriptor_version;
+ memcpy(launched->rend_data->rend_cookie, r_cookie, REND_COOKIE_LEN);
+ strlcpy(launched->rend_data->onion_address, service->service_id,
+ sizeof(launched->rend_data->onion_address));
+ launched->rend_data->rend_desc_version = service->descriptor_version;
launched->build_state->pending_final_cpath = cpath =
tor_malloc_zero(sizeof(crypt_path_t));
cpath->magic = CRYPT_PATH_MAGIC;
@@ -1053,13 +1216,7 @@ rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc)
newstate->pending_final_cpath = oldstate->pending_final_cpath;
oldstate->pending_final_cpath = NULL;
- memcpy(newcirc->rend_query, oldcirc->rend_query,
- REND_SERVICE_ID_LEN_BASE32+1);
- memcpy(newcirc->rend_pk_digest, oldcirc->rend_pk_digest,
- DIGEST_LEN);
- memcpy(newcirc->rend_cookie, oldcirc->rend_cookie,
- REND_COOKIE_LEN);
- newcirc->rend_desc_version = oldcirc->rend_desc_version;
+ newcirc->rend_data = rend_data_dup(oldcirc->rend_data);
}
/** Launch a circuit to serve as an introduction point for the service
@@ -1105,10 +1262,11 @@ rend_service_launch_establish_intro(rend_service_t *service,
intro->extend_info = extend_info_dup(launched->build_state->chosen_exit);
}
- strlcpy(launched->rend_query, service->service_id,
- sizeof(launched->rend_query));
- memcpy(launched->rend_pk_digest, service->pk_digest, DIGEST_LEN);
- launched->rend_desc_version = service->descriptor_version;
+ launched->rend_data = tor_malloc_zero(sizeof(rend_data_t));
+ strlcpy(launched->rend_data->onion_address, service->service_id,
+ sizeof(launched->rend_data->onion_address));
+ memcpy(launched->rend_data->rend_pk_digest, service->pk_digest, DIGEST_LEN);
+ launched->rend_data->rend_desc_version = service->descriptor_version;
if (service->descriptor_version == 2)
launched->intro_key = crypto_pk_dup_key(intro->intro_key);
if (launched->_base.state == CIRCUIT_STATE_OPEN)
@@ -1133,12 +1291,14 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
tor_assert(circuit->_base.purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
tor_assert(circuit->cpath);
+ tor_assert(circuit->rend_data);
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
- circuit->rend_pk_digest, REND_SERVICE_ID_LEN);
+ circuit->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN);
service = rend_service_get_by_pk_digest_and_version(
- circuit->rend_pk_digest, circuit->rend_desc_version);
+ circuit->rend_data->rend_pk_digest,
+ circuit->rend_data->rend_desc_version);
if (!service) {
log_warn(LD_REND, "Unrecognized service ID %s on introduction circuit %d.",
serviceid, circuit->_base.n_circ_id);
@@ -1214,8 +1374,10 @@ rend_service_intro_established(origin_circuit_t *circuit, const char *request,
"received INTRO_ESTABLISHED cell on non-intro circuit.");
goto err;
}
+ tor_assert(circuit->rend_data);
service = rend_service_get_by_pk_digest_and_version(
- circuit->rend_pk_digest, circuit->rend_desc_version);
+ circuit->rend_data->rend_pk_digest,
+ circuit->rend_data->rend_desc_version);
if (!service) {
log_warn(LD_REND, "Unknown service on introduction circuit %d.",
circuit->_base.n_circ_id);
@@ -1225,7 +1387,7 @@ rend_service_intro_established(origin_circuit_t *circuit, const char *request,
circuit->_base.purpose = CIRCUIT_PURPOSE_S_INTRO;
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32 + 1,
- circuit->rend_pk_digest, REND_SERVICE_ID_LEN);
+ circuit->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN);
log_info(LD_REND,
"Received INTRO_ESTABLISHED cell on circuit %d for service %s",
circuit->_base.n_circ_id, serviceid);
@@ -1252,12 +1414,13 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
tor_assert(circuit->_base.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
tor_assert(circuit->cpath);
tor_assert(circuit->build_state);
+ tor_assert(circuit->rend_data);
hop = circuit->build_state->pending_final_cpath;
tor_assert(hop);
- base16_encode(hexcookie,9,circuit->rend_cookie,4);
+ base16_encode(hexcookie,9,circuit->rend_data->rend_cookie,4);
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
- circuit->rend_pk_digest, REND_SERVICE_ID_LEN);
+ circuit->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN);
log_info(LD_REND,
"Done building circuit %d to rendezvous with "
@@ -1265,7 +1428,8 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
circuit->_base.n_circ_id, hexcookie, serviceid);
service = rend_service_get_by_pk_digest_and_version(
- circuit->rend_pk_digest, circuit->rend_desc_version);
+ circuit->rend_data->rend_pk_digest,
+ circuit->rend_data->rend_desc_version);
if (!service) {
log_warn(LD_GENERAL, "Internal error: unrecognized service ID on "
"introduction circuit.");
@@ -1274,7 +1438,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
}
/* All we need to do is send a RELAY_RENDEZVOUS1 cell... */
- memcpy(buf, circuit->rend_cookie, REND_COOKIE_LEN);
+ memcpy(buf, circuit->rend_data->rend_cookie, REND_COOKIE_LEN);
if (crypto_dh_get_public(hop->dh_handshake_state,
buf+REND_COOKIE_LEN, DH_KEY_LEN)<0) {
log_warn(LD_GENERAL,"Couldn't get DH public key.");
@@ -1336,7 +1500,8 @@ find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest,
CIRCUIT_PURPOSE_S_INTRO))) {
if (!memcmp(circ->build_state->chosen_exit->identity_digest,
intro->extend_info->identity_digest, DIGEST_LEN) &&
- circ->rend_desc_version == desc_version) {
+ circ->rend_data &&
+ circ->rend_data->rend_desc_version == desc_version) {
return circ;
}
}
@@ -1346,7 +1511,8 @@ find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest,
CIRCUIT_PURPOSE_S_ESTABLISH_INTRO))) {
if (!memcmp(circ->build_state->chosen_exit->identity_digest,
intro->extend_info->identity_digest, DIGEST_LEN) &&
- circ->rend_desc_version == desc_version) {
+ circ->rend_data &&
+ circ->rend_data->rend_desc_version == desc_version) {
return circ;
}
}
@@ -1827,11 +1993,13 @@ rend_service_set_connection_addr_port(edge_connection_t *conn,
rend_service_port_config_t *chosen_port;
tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
+ tor_assert(circ->rend_data);
log_debug(LD_REND,"beginning to hunt for addr/port");
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
- circ->rend_pk_digest, REND_SERVICE_ID_LEN);
- service = rend_service_get_by_pk_digest_and_version(circ->rend_pk_digest,
- circ->rend_desc_version);
+ circ->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN);
+ service = rend_service_get_by_pk_digest_and_version(
+ circ->rend_data->rend_pk_digest,
+ circ->rend_data->rend_desc_version);
if (!service) {
log_warn(LD_REND, "Couldn't find any service associated with pk %s on "
"rendezvous circuit %d; closing.",