aboutsummaryrefslogtreecommitdiff
path: root/src/or/routerparse.c
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2014-10-03 10:55:50 -0400
committerNick Mathewson <nickm@torproject.org>2014-10-13 14:30:02 -0400
commita30594605e60d3a581f21c71a7d9e7a062800e3d (patch)
tree0a6900254158758b726724d551e85802935bc08a /src/or/routerparse.c
parentb448ec195dd8687d2d5f363e12fec046eb2d1677 (diff)
downloadtor-a30594605e60d3a581f21c71a7d9e7a062800e3d.tar.gz
tor-a30594605e60d3a581f21c71a7d9e7a062800e3d.zip
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been adding code that makes clients reject directory documents that they previously would have accepted, if those descriptors actually exist. When this happened, the clients would get the document, reject it, and then decide to try downloading it again, ad infinitum. This problem becomes particularly obnoxious with authorities, since if some authorities accept a descriptor that others don't, the ones that don't accept it would go crazy trying to re-fetch it over and over. (See for example ticket #9286.) This patch tries to solve this problem by tracking, if a descriptor isn't parseable, what its digest was, and whether it is invalid because of some flaw that applies to the portion containing the digest. (This excludes RSA signature problems: RSA signatures aren't included in the digest. This means that a directory authority can still put another directory authority into a loop by mentioning a descriptor, and then serving that descriptor with an invalid RSA signatures. But that would also make the misbehaving directory authority get DoSed by the server it's attacking, so it's not much of an issue.) We already have a mechanism to mark something undownloadable with downloadstatus_mark_impossible(); we use that here for microdescriptors, extrainfos, and router descriptors. Unit tests to follow in another patch. Closes ticket #11243.
Diffstat (limited to 'src/or/routerparse.c')
-rw-r--r--src/or/routerparse.c82
1 files changed, 61 insertions, 21 deletions
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 250d1cd062..c7ebba2729 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -911,7 +911,8 @@ find_start_of_next_router_or_extrainfo(const char **s_ptr,
* descriptor in the signed_descriptor_body field of each routerinfo_t. If it
* isn't SAVED_NOWHERE, remember the offset of each descriptor.
*
- * Returns 0 on success and -1 on failure.
+ * Returns 0 on success and -1 on failure. Adds a digest to
+ * <b>invalid_digests_out</b> for every entry that was unparseable or invalid.
*/
int
router_parse_list_from_string(const char **s, const char *eos,
@@ -919,7 +920,8 @@ router_parse_list_from_string(const char **s, const char *eos,
saved_location_t saved_location,
int want_extrainfo,
int allow_annotations,
- const char *prepend_annotations)
+ const char *prepend_annotations,
+ smartlist_t *invalid_digests_out)
{
routerinfo_t *router;
extrainfo_t *extrainfo;
@@ -939,6 +941,9 @@ router_parse_list_from_string(const char **s, const char *eos,
tor_assert(eos >= *s);
while (1) {
+ char raw_digest[DIGEST_LEN];
+ int have_raw_digest = 0;
+ int dl_again = 0;
if (find_start_of_next_router_or_extrainfo(s, eos, &have_extrainfo) < 0)
break;
@@ -955,18 +960,20 @@ router_parse_list_from_string(const char **s, const char *eos,
if (have_extrainfo && want_extrainfo) {
routerlist_t *rl = router_get_routerlist();
+ have_raw_digest = router_get_extrainfo_hash(*s, end-*s, raw_digest) == 0;
extrainfo = extrainfo_parse_entry_from_string(*s, end,
saved_location != SAVED_IN_CACHE,
- rl->identity_map);
+ rl->identity_map, &dl_again);
if (extrainfo) {
signed_desc = &extrainfo->cache_info;
elt = extrainfo;
}
} else if (!have_extrainfo && !want_extrainfo) {
+ have_raw_digest = router_get_router_hash(*s, end-*s, raw_digest) == 0;
router = router_parse_entry_from_string(*s, end,
saved_location != SAVED_IN_CACHE,
allow_annotations,
- prepend_annotations);
+ prepend_annotations, &dl_again);
if (router) {
log_debug(LD_DIR, "Read router '%s', purpose '%s'",
router_describe(router),
@@ -975,6 +982,9 @@ router_parse_list_from_string(const char **s, const char *eos,
elt = router;
}
}
+ if (! elt && ! dl_again && have_raw_digest && invalid_digests_out) {
+ smartlist_add(invalid_digests_out, tor_memdup(raw_digest, DIGEST_LEN));
+ }
if (!elt) {
*s = end;
continue;
@@ -1068,11 +1078,17 @@ find_single_ipv6_orport(const smartlist_t *list,
* around when caching the router.
*
* Only one of allow_annotations and prepend_annotations may be set.
+ *
+ * If <b>can_dl_again_out</b> is provided, set *<b>can_dl_again_out</b> to 1
+ * if it's okay to try to download a descriptor with this same digest again,
+ * and 0 if it isn't. (It might not be okay to download it again if part of
+ * the part covered by the digest is invalid.)
*/
routerinfo_t *
router_parse_entry_from_string(const char *s, const char *end,
int cache_copy, int allow_annotations,
- const char *prepend_annotations)
+ const char *prepend_annotations,
+ int *can_dl_again_out)
{
routerinfo_t *router = NULL;
char digest[128];
@@ -1083,6 +1099,7 @@ router_parse_entry_from_string(const char *s, const char *end,
size_t prepend_len = prepend_annotations ? strlen(prepend_annotations) : 0;
int ok = 1;
memarea_t *area = NULL;
+ int can_dl_again = 0;
tor_assert(!allow_annotations || !prepend_annotations);
@@ -1389,19 +1406,20 @@ router_parse_entry_from_string(const char *s, const char *end,
verified_digests = digestmap_new();
digestmap_set(verified_digests, signed_digest, (void*)(uintptr_t)1);
#endif
- if (check_signature_token(digest, DIGEST_LEN, tok, router->identity_pkey, 0,
- "router descriptor") < 0)
- goto err;
if (!router->or_port) {
log_warn(LD_DIR,"or_port unreadable or 0. Failing.");
goto err;
}
+ can_dl_again = 1;
+ if (check_signature_token(digest, DIGEST_LEN, tok, router->identity_pkey, 0,
+ "router descriptor") < 0)
+ goto err;
+
if (!router->platform) {
router->platform = tor_strdup("<unknown>");
}
-
goto done;
err:
@@ -1418,6 +1436,8 @@ router_parse_entry_from_string(const char *s, const char *end,
DUMP_AREA(area, "routerinfo");
memarea_drop_all(area);
}
+ if (can_dl_again_out)
+ *can_dl_again_out = can_dl_again;
return router;
}
@@ -1426,10 +1446,16 @@ router_parse_entry_from_string(const char *s, const char *end,
* <b>cache_copy</b> is true, make a copy of the extra-info document in the
* cache_info fields of the result. If <b>routermap</b> is provided, use it
* as a map from router identity to routerinfo_t when looking up signing keys.
+ *
+ * If <b>can_dl_again_out</b> is provided, set *<b>can_dl_again_out</b> to 1
+ * if it's okay to try to download an extrainfo with this same digest again,
+ * and 0 if it isn't. (It might not be okay to download it again if part of
+ * the part covered by the digest is invalid.)
*/
extrainfo_t *
extrainfo_parse_entry_from_string(const char *s, const char *end,
- int cache_copy, struct digest_ri_map_t *routermap)
+ int cache_copy, struct digest_ri_map_t *routermap,
+ int *can_dl_again_out)
{
extrainfo_t *extrainfo = NULL;
char digest[128];
@@ -1439,6 +1465,7 @@ extrainfo_parse_entry_from_string(const char *s, const char *end,
routerinfo_t *router = NULL;
memarea_t *area = NULL;
const char *s_dup = s;
+ int can_dl_again = 0;
if (!end) {
end = s + strlen(s);
@@ -1498,6 +1525,8 @@ extrainfo_parse_entry_from_string(const char *s, const char *end,
goto err;
}
+ can_dl_again = 1;
+
if (routermap &&
(router = digestmap_get((digestmap_t*)routermap,
extrainfo->cache_info.identity_digest))) {
@@ -1540,6 +1569,8 @@ extrainfo_parse_entry_from_string(const char *s, const char *end,
DUMP_AREA(area, "extrainfo");
memarea_drop_all(area);
}
+ if (can_dl_again_out)
+ *can_dl_again_out = can_dl_again;
return extrainfo;
}
@@ -4006,12 +4037,15 @@ find_start_of_next_microdesc(const char *s, const char *eos)
* If <b>saved_location</b> isn't SAVED_IN_CACHE, make a local copy of each
* descriptor in the body field of each microdesc_t.
*
- * Return all newly
- * parsed microdescriptors in a newly allocated smartlist_t. */
+ * Return all newly parsed microdescriptors in a newly allocated
+ * smartlist_t. If <b>invalid_disgests_out</b> is provided, add a SHA256
+ * microdesc digest to it for every microdesc that we found to be badly
+ * formed. */
smartlist_t *
microdescs_parse_from_string(const char *s, const char *eos,
int allow_annotations,
- saved_location_t where)
+ saved_location_t where,
+ smartlist_t *invalid_digests_out)
{
smartlist_t *tokens;
smartlist_t *result;
@@ -4033,16 +4067,12 @@ microdescs_parse_from_string(const char *s, const char *eos,
tokens = smartlist_new();
while (s < eos) {
+ int okay = 0;
+
start_of_next_microdesc = find_start_of_next_microdesc(s, eos);
if (!start_of_next_microdesc)
start_of_next_microdesc = eos;
- if (tokenize_string(area, s, start_of_next_microdesc, tokens,
- microdesc_token_table, flags)) {
- log_warn(LD_DIR, "Unparseable microdescriptor");
- goto next;
- }
-
md = tor_malloc_zero(sizeof(microdesc_t));
{
const char *cp = tor_memstr(s, start_of_next_microdesc-s,
@@ -4057,6 +4087,13 @@ microdescs_parse_from_string(const char *s, const char *eos,
md->body = (char*)cp;
md->off = cp - start;
}
+ crypto_digest256(md->digest, md->body, md->bodylen, DIGEST_SHA256);
+
+ if (tokenize_string(area, s, start_of_next_microdesc, tokens,
+ microdesc_token_table, flags)) {
+ log_warn(LD_DIR, "Unparseable microdescriptor");
+ goto next;
+ }
if ((tok = find_opt_by_keyword(tokens, A_LAST_LISTED))) {
if (parse_iso_time(tok->args[0], &md->last_listed)) {
@@ -4113,12 +4150,15 @@ microdescs_parse_from_string(const char *s, const char *eos,
md->ipv6_exit_policy = parse_short_policy(tok->args[0]);
}
- crypto_digest256(md->digest, md->body, md->bodylen, DIGEST_SHA256);
-
smartlist_add(result, md);
+ okay = 1;
md = NULL;
next:
+ if (! okay && invalid_digests_out) {
+ smartlist_add(invalid_digests_out,
+ tor_memdup(md->digest, DIGEST256_LEN));
+ }
microdesc_free(md);
md = NULL;