When downloading certificates, distinguish requesting by identity digest from requesting by ID digest, signing key pair; fixes bug 5595

This commit is contained in:
Andrea Shepard 2013-05-09 04:56:54 -07:00
parent d5bd4a4763
commit fddb814fea
6 changed files with 436 additions and 91 deletions

8
changes/bug5595 Normal file
View File

@ -0,0 +1,8 @@
o Critical bugfixes:
- Distinguish downloading an authority certificate by identity digest from
downloading one by identity digest/signing key digest pair; formerly we
always request them only by identity digest and get the newest one even
when we wanted one with a different signing key. Then we would complain
about being given a certificate we already had, and never get the one we
really wanted. Now we use the "fp-sk/" resource as well as the "fp/"
resource to request the one we want. Fixes bug 5595.

View File

@ -809,13 +809,34 @@ connection_dir_download_cert_failed(dir_connection_t *conn, int status)
if (!conn->requested_resource) if (!conn->requested_resource)
return; return;
failed = smartlist_new(); failed = smartlist_new();
dir_split_resource_into_fingerprints(conn->requested_resource+3, /*
* We have two cases download by fingerprint (resource starts
* with "fp/") or download by fingerprint/signing key pair
* (resource starts with "fp-sk/").
*/
if (!strcmpstart(conn->requested_resource, "fp/")) {
/* Download by fingerprint case */
dir_split_resource_into_fingerprints(conn->requested_resource + 3,
failed, NULL, DSR_HEX); failed, NULL, DSR_HEX);
SMARTLIST_FOREACH(failed, char *, cp, SMARTLIST_FOREACH_BEGIN(failed, char *, cp) {
{ /* Null signing key digest indicates download by fp only */
authority_cert_dl_failed(cp, status); authority_cert_dl_failed(cp, NULL, status);
tor_free(cp); tor_free(cp);
}); } SMARTLIST_FOREACH_END(cp);
} else if (!strcmpstart(conn->requested_resource, "fp-sk/")) {
/* Download by (fp,sk) pairs */
dir_split_resource_into_fingerprint_pairs(conn->requested_resource + 5,
failed);
SMARTLIST_FOREACH_BEGIN(failed, fp_pair_t *, cp) {
authority_cert_dl_failed(cp->first, cp->second, status);
tor_free(cp);
} SMARTLIST_FOREACH_END(cp);
} else {
log_warn(LD_DIR,
"Don't know what to do with failure for cert fetch %s",
conn->requested_resource);
}
smartlist_free(failed); smartlist_free(failed);
update_certificate_downloads(time(NULL)); update_certificate_downloads(time(NULL));
@ -1589,6 +1610,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
conn->_base.purpose == DIR_PURPOSE_FETCH_MICRODESC); conn->_base.purpose == DIR_PURPOSE_FETCH_MICRODESC);
int was_compressed=0; int was_compressed=0;
time_t now = time(NULL); time_t now = time(NULL);
int src_code;
switch (connection_fetch_from_buf_http(TO_CONN(conn), switch (connection_fetch_from_buf_http(TO_CONN(conn),
&headers, MAX_HEADERS_SIZE, &headers, MAX_HEADERS_SIZE,
@ -1857,7 +1879,19 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
} }
log_info(LD_DIR,"Received authority certificates (size %d) from server " log_info(LD_DIR,"Received authority certificates (size %d) from server "
"'%s:%d'", (int)body_len, conn->_base.address, conn->_base.port); "'%s:%d'", (int)body_len, conn->_base.address, conn->_base.port);
if (trusted_dirs_load_certs_from_string(body, 0, 1)<0) { /*
* Tell trusted_dirs_load_certs_from_string() whether it was by fp
* or fp-sk pair.
*/
src_code = -1;
if (!strcmpstart(conn->requested_resource, "fp/")) {
src_code = TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST;
} else if (!strcmpstart(conn->requested_resource, "fp-sk/")) {
src_code = TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_SK_DIGEST;
}
if (src_code != -1) {
if (trusted_dirs_load_certs_from_string(body, src_code, 1)<0) {
log_warn(LD_DIR, "Unable to parse fetched certificates"); log_warn(LD_DIR, "Unable to parse fetched certificates");
/* if we fetched more than one and only some failed, the successful /* if we fetched more than one and only some failed, the successful
* ones got flushed to disk so it's safe to call this on them */ * ones got flushed to disk so it's safe to call this on them */
@ -1866,6 +1900,13 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
directory_info_has_arrived(now, 0); directory_info_has_arrived(now, 0);
log_info(LD_DIR, "Successfully loaded certificates from fetch."); log_info(LD_DIR, "Successfully loaded certificates from fetch.");
} }
} else {
log_warn(LD_DIR,
"Couldn't figure out what to do with fetched certificates for "
"unknown resource %s",
conn->requested_resource);
connection_dir_download_cert_failed(conn, status_code);
}
} }
if (conn->_base.purpose == DIR_PURPOSE_FETCH_STATUS_VOTE) { if (conn->_base.purpose == DIR_PURPOSE_FETCH_STATUS_VOTE) {
const char *msg; const char *msg;

View File

@ -2947,7 +2947,7 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out)
/* Hey, it's a new cert! */ /* Hey, it's a new cert! */
trusted_dirs_load_certs_from_string( trusted_dirs_load_certs_from_string(
vote->cert->cache_info.signed_descriptor_body, vote->cert->cache_info.signed_descriptor_body,
0 /* from_store */, 1 /*flush*/); TRUSTED_DIRS_CERTS_SRC_FROM_VOTE, 1 /*flush*/);
if (!authority_cert_get_by_digests(vote->cert->cache_info.identity_digest, if (!authority_cert_get_by_digests(vote->cert->cache_info.identity_digest,
vote->cert->signing_key_digest)) { vote->cert->signing_key_digest)) {
log_warn(LD_BUG, "We added a cert, but still couldn't find it."); log_warn(LD_BUG, "We added a cert, but still couldn't find it.");

View File

@ -758,7 +758,8 @@ init_keys(void)
if (cert) { /* add my own cert to the list of known certs */ if (cert) { /* add my own cert to the list of known certs */
log_info(LD_DIR, "adding my own v3 cert"); log_info(LD_DIR, "adding my own v3 cert");
if (trusted_dirs_load_certs_from_string( if (trusted_dirs_load_certs_from_string(
cert->cache_info.signed_descriptor_body, 0, 0)<0) { cert->cache_info.signed_descriptor_body,
TRUSTED_DIRS_CERTS_SRC_SELF, 0)<0) {
log_warn(LD_DIR, "Unable to parse my own v3 cert! Failing."); log_warn(LD_DIR, "Unable to parse my own v3 cert! Failing.");
return -1; return -1;
} }

View File

@ -19,6 +19,7 @@
#include "directory.h" #include "directory.h"
#include "dirserv.h" #include "dirserv.h"
#include "dirvote.h" #include "dirvote.h"
#include "fp_pair.h"
#include "geoip.h" #include "geoip.h"
#include "hibernate.h" #include "hibernate.h"
#include "main.h" #include "main.h"
@ -38,6 +39,24 @@
/****************************************************************************/ /****************************************************************************/
DECLARE_TYPED_DIGESTMAP_FNS(sdmap_, digest_sd_map_t, signed_descriptor_t)
DECLARE_TYPED_DIGESTMAP_FNS(rimap_, digest_ri_map_t, routerinfo_t)
DECLARE_TYPED_DIGESTMAP_FNS(eimap_, digest_ei_map_t, extrainfo_t)
DECLARE_TYPED_DIGESTMAP_FNS(dsmap_, digest_ds_map_t, download_status_t)
#define SDMAP_FOREACH(map, keyvar, valvar) \
DIGESTMAP_FOREACH(sdmap_to_digestmap(map), keyvar, signed_descriptor_t *, \
valvar)
#define RIMAP_FOREACH(map, keyvar, valvar) \
DIGESTMAP_FOREACH(rimap_to_digestmap(map), keyvar, routerinfo_t *, valvar)
#define EIMAP_FOREACH(map, keyvar, valvar) \
DIGESTMAP_FOREACH(eimap_to_digestmap(map), keyvar, extrainfo_t *, valvar)
#define DSMAP_FOREACH(map, keyvar, valvar) \
DIGESTMAP_FOREACH(dsmap_to_digestmap(map), keyvar, download_status_t *, \
valvar)
/* Forward declaration for cert_list_t */
typedef struct cert_list_t cert_list_t;
/* static function prototypes */ /* static function prototypes */
static const routerstatus_t *router_pick_directory_server_impl( static const routerstatus_t *router_pick_directory_server_impl(
dirinfo_type_t auth, int flags); dirinfo_type_t auth, int flags);
@ -56,19 +75,14 @@ static const char *signed_descriptor_get_body_impl(
int with_annotations); int with_annotations);
static void list_pending_downloads(digestmap_t *result, static void list_pending_downloads(digestmap_t *result,
int purpose, const char *prefix); int purpose, const char *prefix);
static void list_pending_fpsk_downloads(fp_pair_map_t *result);
static void launch_dummy_descriptor_download_as_needed(time_t now, static void launch_dummy_descriptor_download_as_needed(time_t now,
const or_options_t *options); const or_options_t *options);
static void download_status_reset_by_sk_in_cl(cert_list_t *cl,
DECLARE_TYPED_DIGESTMAP_FNS(sdmap_, digest_sd_map_t, signed_descriptor_t) const char *digest);
DECLARE_TYPED_DIGESTMAP_FNS(rimap_, digest_ri_map_t, routerinfo_t) static int download_status_is_ready_by_sk_in_cl(cert_list_t *cl,
DECLARE_TYPED_DIGESTMAP_FNS(eimap_, digest_ei_map_t, extrainfo_t) const char *digest,
#define SDMAP_FOREACH(map, keyvar, valvar) \ time_t now, int max_failures);
DIGESTMAP_FOREACH(sdmap_to_digestmap(map), keyvar, signed_descriptor_t *, \
valvar)
#define RIMAP_FOREACH(map, keyvar, valvar) \
DIGESTMAP_FOREACH(rimap_to_digestmap(map), keyvar, routerinfo_t *, valvar)
#define EIMAP_FOREACH(map, keyvar, valvar) \
DIGESTMAP_FOREACH(eimap_to_digestmap(map), keyvar, extrainfo_t *, valvar)
/****************************************************************************/ /****************************************************************************/
@ -78,10 +92,17 @@ static smartlist_t *trusted_dir_servers = NULL;
/** List of for a given authority, and download status for latest certificate. /** List of for a given authority, and download status for latest certificate.
*/ */
typedef struct cert_list_t { struct cert_list_t {
download_status_t dl_status; /*
* The keys of download status map are cert->signing_key_digest for pending
* downloads by (identity digest/signing key digest) pair; functions such
* as authority_cert_get_by_digest() already assume these are unique.
*/
struct digest_ds_map_t *dl_status_map;
/* There is also a dlstatus for the download by identity key only */
download_status_t dl_status_by_id;
smartlist_t *certs; smartlist_t *certs;
} cert_list_t; };
/** Map from v3 identity key digest to cert_list_t. */ /** Map from v3 identity key digest to cert_list_t. */
static digestmap_t *trusted_dir_certs = NULL; static digestmap_t *trusted_dir_certs = NULL;
/** True iff any key certificate in at least one member of /** True iff any key certificate in at least one member of
@ -125,6 +146,72 @@ get_n_authorities(dirinfo_type_t type)
return n; return n;
} }
/** Reset the download status of a specified element in a dsmap */
static void
download_status_reset_by_sk_in_cl(cert_list_t *cl, const char *digest)
{
download_status_t *dlstatus = NULL;
tor_assert(cl);
tor_assert(digest);
/* Make sure we have a dsmap */
if (!(cl->dl_status_map)) {
cl->dl_status_map = dsmap_new();
}
/* Look for a download_status_t in the map with this digest */
dlstatus = dsmap_get(cl->dl_status_map, digest);
/* Got one? */
if (!dlstatus) {
/* Insert before we reset */
dlstatus = tor_malloc_zero(sizeof(*dlstatus));
dsmap_set(cl->dl_status_map, digest, dlstatus);
}
tor_assert(dlstatus);
/* Go ahead and reset it */
download_status_reset(dlstatus);
}
/**
* Return true if the download for this signing key digest in cl is ready
* to be re-attempted.
*/
static int
download_status_is_ready_by_sk_in_cl(cert_list_t *cl,
const char *digest,
time_t now, int max_failures)
{
int rv = 0;
download_status_t *dlstatus = NULL;
tor_assert(cl);
tor_assert(digest);
/* Make sure we have a dsmap */
if (!(cl->dl_status_map)) {
cl->dl_status_map = dsmap_new();
}
/* Look for a download_status_t in the map with this digest */
dlstatus = dsmap_get(cl->dl_status_map, digest);
/* Got one? */
if (dlstatus) {
/* Use download_status_is_ready() */
rv = download_status_is_ready(dlstatus, now, max_failures);
} else {
/*
* If we don't know anything about it, return 1, since we haven't
* tried this one before. We need to create a new entry here,
* too.
*/
dlstatus = tor_malloc_zero(sizeof(*dlstatus));
download_status_reset(dlstatus);
dsmap_set(cl->dl_status_map, digest, dlstatus);
rv = 1;
}
return rv;
}
#define get_n_v2_authorities() get_n_authorities(V2_DIRINFO) #define get_n_v2_authorities() get_n_authorities(V2_DIRINFO)
/** Helper: Return the cert_list_t for an authority whose authority ID is /** Helper: Return the cert_list_t for an authority whose authority ID is
@ -138,8 +225,9 @@ get_cert_list(const char *id_digest)
cl = digestmap_get(trusted_dir_certs, id_digest); cl = digestmap_get(trusted_dir_certs, id_digest);
if (!cl) { if (!cl) {
cl = tor_malloc_zero(sizeof(cert_list_t)); cl = tor_malloc_zero(sizeof(cert_list_t));
cl->dl_status.schedule = DL_SCHED_CONSENSUS; cl->dl_status_by_id.schedule = DL_SCHED_CONSENSUS;
cl->certs = smartlist_new(); cl->certs = smartlist_new();
cl->dl_status_map = dsmap_new();
digestmap_set(trusted_dir_certs, id_digest, cl); digestmap_set(trusted_dir_certs, id_digest, cl);
} }
return cl; return cl;
@ -159,7 +247,9 @@ trusted_dirs_reload_certs(void)
tor_free(filename); tor_free(filename);
if (!contents) if (!contents)
return 0; return 0;
r = trusted_dirs_load_certs_from_string(contents, 1, 1); r = trusted_dirs_load_certs_from_string(
contents,
TRUSTED_DIRS_CERTS_SRC_FROM_STORE, 1);
tor_free(contents); tor_free(contents);
return r; return r;
} }
@ -182,17 +272,23 @@ already_have_cert(authority_cert_t *cert)
} }
/** Load a bunch of new key certificates from the string <b>contents</b>. If /** Load a bunch of new key certificates from the string <b>contents</b>. If
* <b>from_store</b> is true, the certificates are from the cache, and we * <b>source</b> is TRUSTED_DIRS_CERTS_SRC_FROM_STORE, the certificates are
* don't need to flush them to disk. If <b>flush</b> is true, we need * from the cache, and we don't need to flush them to disk. If we are a
* to flush any changed certificates to disk now. Return 0 on success, -1 * dirauth loading our own cert, source is TRUSTED_DIRS_CERTS_SRC_SELF.
* if any certs fail to parse. */ * Otherwise, source is download type: TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST
* or TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_SK_DIGEST. If <b>flush</b> is true, we
* need to flush any changed certificates to disk now. Return 0 on success,
* -1 if any certs fail to parse.
*/
int int
trusted_dirs_load_certs_from_string(const char *contents, int from_store, trusted_dirs_load_certs_from_string(const char *contents, int source,
int flush) int flush)
{ {
trusted_dir_server_t *ds; trusted_dir_server_t *ds;
const char *s, *eos; const char *s, *eos;
int failure_code = 0; int failure_code = 0;
int from_store = (source == TRUSTED_DIRS_CERTS_SRC_FROM_STORE);
for (s = contents; *s; s = eos) { for (s = contents; *s; s = eos) {
authority_cert_t *cert = authority_cert_parse_from_string(s, &eos); authority_cert_t *cert = authority_cert_parse_from_string(s, &eos);
@ -229,7 +325,18 @@ trusted_dirs_load_certs_from_string(const char *contents, int from_store,
ds ? ds->nickname : "an old or new authority"); ds ? ds->nickname : "an old or new authority");
} }
authority_cert_dl_failed(cert->cache_info.identity_digest, 404); /*
* This is where we care about the source; authority_cert_dl_failed()
* needs to know whether the download was by fp or (fp,sk) pair to
* twiddle the right bit in the download map.
*/
if (source == TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST) {
authority_cert_dl_failed(cert->cache_info.identity_digest,
NULL, 404);
} else if (source == TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_SK_DIGEST) {
authority_cert_dl_failed(cert->cache_info.identity_digest,
cert->signing_key_digest, 404);
}
} }
authority_cert_free(cert); authority_cert_free(cert);
@ -445,17 +552,53 @@ authority_cert_get_all(smartlist_t *certs_out)
} }
/** Called when an attempt to download a certificate with the authority with /** Called when an attempt to download a certificate with the authority with
* ID <b>id_digest</b> fails with HTTP response code <b>status</b>: remember * ID <b>id_digest</b> and, if not NULL, signed with key signing_key_digest
* the failure, so we don't try again immediately. */ * fails with HTTP response code <b>status</b>: remember the failure, so we
* don't try again immediately. */
void void
authority_cert_dl_failed(const char *id_digest, int status) authority_cert_dl_failed(const char *id_digest,
const char *signing_key_digest, int status)
{ {
cert_list_t *cl; cert_list_t *cl;
download_status_t *dlstatus = NULL;
char id_digest_str[2*DIGEST_LEN+1];
char sk_digest_str[2*DIGEST_LEN+1];
if (!trusted_dir_certs || if (!trusted_dir_certs ||
!(cl = digestmap_get(trusted_dir_certs, id_digest))) !(cl = digestmap_get(trusted_dir_certs, id_digest)))
return; return;
download_status_failed(&cl->dl_status, status); /*
* Are we noting a failed download of the latest cert for the id digest,
* or of a download by (id, signing key) digest pair?
*/
if (!signing_key_digest) {
/* Just by id digest */
download_status_failed(&cl->dl_status_by_id, status);
} else {
/* Reset by (id, signing key) digest pair
*
* Look for a download_status_t in the map with this digest
*/
dlstatus = dsmap_get(cl->dl_status_map, signing_key_digest);
/* Got one? */
if (dlstatus) {
download_status_failed(dlstatus, status);
} else {
/*
* Do this rather than hex_str(), since hex_str clobbers
* old results and we call twice in the param list.
*/
base16_encode(id_digest_str, sizeof(id_digest_str),
id_digest, DIGEST_LEN);
base16_encode(sk_digest_str, sizeof(sk_digest_str),
signing_key_digest, DIGEST_LEN);
log_warn(LD_DIR,
"Got failure for cert fetch with (fp,sk) = (%s,%s), with "
"status %d, but knew nothing about the download.",
id_digest_str, sk_digest_str, status);
}
}
} }
/** Return true iff when we've been getting enough failures when trying to /** Return true iff when we've been getting enough failures when trying to
@ -471,7 +614,7 @@ authority_cert_dl_looks_uncertain(const char *id_digest)
!(cl = digestmap_get(trusted_dir_certs, id_digest))) !(cl = digestmap_get(trusted_dir_certs, id_digest)))
return 0; return 0;
n_failures = download_status_get_n_failures(&cl->dl_status); n_failures = download_status_get_n_failures(&cl->dl_status_by_id);
return n_failures >= N_AUTH_CERT_DL_FAILURES_TO_BUG_USER; return n_failures >= N_AUTH_CERT_DL_FAILURES_TO_BUG_USER;
} }
@ -487,20 +630,38 @@ authority_cert_dl_looks_uncertain(const char *id_digest)
void void
authority_certs_fetch_missing(networkstatus_t *status, time_t now) authority_certs_fetch_missing(networkstatus_t *status, time_t now)
{ {
digestmap_t *pending; /*
* The pending_id digestmap tracks pending certificate downloads by
* identity digest; the pending_cert digestmap tracks pending downloads
* by (identity digest, signing key digest) pairs.
*/
digestmap_t *pending_id;
fp_pair_map_t *pending_cert;
authority_cert_t *cert; authority_cert_t *cert;
smartlist_t *missing_digests; /*
* The missing_id_digests smartlist will hold a list of id digests
* we want to fetch the newest cert for; the missing_cert_digests
* smartlist will hold a list of fp_pair_t with an identity and
* signing key digest.
*/
smartlist_t *missing_cert_digests, *missing_id_digests;
char *resource = NULL; char *resource = NULL;
cert_list_t *cl; cert_list_t *cl;
const int cache = directory_caches_unknown_auth_certs(get_options()); const int cache = directory_caches_unknown_auth_certs(get_options());
fp_pair_t *fp_tmp = NULL;
char id_digest_str[2*DIGEST_LEN+1];
char sk_digest_str[2*DIGEST_LEN+1];
if (should_delay_dir_fetches(get_options())) if (should_delay_dir_fetches(get_options()))
return; return;
pending = digestmap_new(); pending_cert = fp_pair_map_new();
missing_digests = smartlist_new(); pending_id = digestmap_new();
missing_cert_digests = smartlist_new();
missing_id_digests = smartlist_new();
list_pending_downloads(pending, DIR_PURPOSE_FETCH_CERTIFICATE, "fp/"); list_pending_downloads(pending_id, DIR_PURPOSE_FETCH_CERTIFICATE, "fp/");
list_pending_fpsk_downloads(pending_cert);
if (status) { if (status) {
SMARTLIST_FOREACH_BEGIN(status->voters, networkstatus_voter_info_t *, SMARTLIST_FOREACH_BEGIN(status->voters, networkstatus_voter_info_t *,
voter) { voter) {
@ -516,16 +677,43 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
sig->signing_key_digest); sig->signing_key_digest);
if (cert) { if (cert) {
if (now < cert->expires) if (now < cert->expires)
download_status_reset(&cl->dl_status); download_status_reset_by_sk_in_cl(cl, sig->signing_key_digest);
continue; continue;
} }
if (download_status_is_ready(&cl->dl_status, now, if (download_status_is_ready_by_sk_in_cl(
MAX_CERT_DL_FAILURES) && cl, sig->signing_key_digest,
!digestmap_get(pending, voter->identity_digest)) { now, MAX_CERT_DL_FAILURES) &&
log_info(LD_DIR, "We're missing a certificate from authority " !fp_pair_map_get_by_digests(pending_cert,
"with signing key %s: launching request.", voter->identity_digest,
hex_str(sig->signing_key_digest, DIGEST_LEN)); sig->signing_key_digest)) {
smartlist_add(missing_digests, sig->identity_digest); /*
* Do this rather than hex_str(), since hex_str clobbers
* old results and we call twice in the param list.
*/
base16_encode(id_digest_str, sizeof(id_digest_str),
voter->identity_digest, DIGEST_LEN);
base16_encode(sk_digest_str, sizeof(sk_digest_str),
sig->signing_key_digest, DIGEST_LEN);
if (voter->nickname) {
log_info(LD_DIR,
"We're missing a certificate from authority %s "
"(ID digest %s) with signing key %s: "
"launching request.",
voter->nickname, id_digest_str, sk_digest_str);
} else {
log_info(LD_DIR,
"We're missing a certificate from authority ID digest "
"%s with signing key %s: launching request.",
id_digest_str, sk_digest_str);
}
/* Allocate a new fp_pair_t to append */
fp_tmp = tor_malloc(sizeof(*fp_tmp));
memcpy(fp_tmp->first, voter->identity_digest, sizeof(fp_tmp->first));
memcpy(fp_tmp->second, sig->signing_key_digest,
sizeof(fp_tmp->second));
smartlist_add(missing_cert_digests, fp_tmp);
} }
} SMARTLIST_FOREACH_END(sig); } SMARTLIST_FOREACH_END(sig);
} SMARTLIST_FOREACH_END(voter); } SMARTLIST_FOREACH_END(voter);
@ -534,35 +722,40 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
int found = 0; int found = 0;
if (!(ds->type & V3_DIRINFO)) if (!(ds->type & V3_DIRINFO))
continue; continue;
if (smartlist_digest_isin(missing_digests, ds->v3_identity_digest)) if (smartlist_digest_isin(missing_id_digests, ds->v3_identity_digest))
continue; continue;
cl = get_cert_list(ds->v3_identity_digest); cl = get_cert_list(ds->v3_identity_digest);
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert, { SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert, {
if (now < cert->expires) { if (now < cert->expires) {
/* It's not expired, and we weren't looking for something to /* It's not expired, and we weren't looking for something to
* verify a consensus with. Call it done. */ * verify a consensus with. Call it done. */
download_status_reset(&cl->dl_status); download_status_reset(&(cl->dl_status_by_id));
/* No sense trying to download it specifically by signing key hash */
download_status_reset_by_sk_in_cl(cl, cert->signing_key_digest);
found = 1; found = 1;
break; break;
} }
}); });
if (!found && if (!found &&
download_status_is_ready(&cl->dl_status, now,MAX_CERT_DL_FAILURES) && download_status_is_ready(&(cl->dl_status_by_id), now,
!digestmap_get(pending, ds->v3_identity_digest)) { MAX_CERT_DL_FAILURES) &&
log_info(LD_DIR, "No current certificate known for authority %s; " !digestmap_get(pending_id, ds->v3_identity_digest)) {
"launching request.", ds->nickname); log_info(LD_DIR,
smartlist_add(missing_digests, ds->v3_identity_digest); "No current certificate known for authority %s "
"(ID digest %s); launching request.",
ds->nickname, hex_str(ds->v3_identity_digest, DIGEST_LEN));
smartlist_add(missing_id_digests, ds->v3_identity_digest);
} }
} SMARTLIST_FOREACH_END(ds); } SMARTLIST_FOREACH_END(ds);
if (!smartlist_len(missing_digests)) { /* Do downloads by identity digest */
goto done; if (smartlist_len(missing_id_digests) > 0) {
} else {
smartlist_t *fps = smartlist_new(); smartlist_t *fps = smartlist_new();
smartlist_add(fps, tor_strdup("fp/")); smartlist_add(fps, tor_strdup("fp/"));
SMARTLIST_FOREACH(missing_digests, const char *, d, {
SMARTLIST_FOREACH(missing_id_digests, const char *, d, {
char *fp; char *fp;
if (digestmap_get(pending, d)) if (digestmap_get(pending_id, d))
continue; continue;
fp = tor_malloc(HEX_DIGEST_LEN+2); fp = tor_malloc(HEX_DIGEST_LEN+2);
base16_encode(fp, HEX_DIGEST_LEN+1, d, DIGEST_LEN); base16_encode(fp, HEX_DIGEST_LEN+1, d, DIGEST_LEN);
@ -570,24 +763,77 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
fp[HEX_DIGEST_LEN+1] = '\0'; fp[HEX_DIGEST_LEN+1] = '\0';
smartlist_add(fps, fp); smartlist_add(fps, fp);
}); });
if (smartlist_len(fps) == 1) {
/* we didn't add any: they were all pending */ if (smartlist_len(fps) > 1) {
SMARTLIST_FOREACH(fps, char *, cp, tor_free(cp));
smartlist_free(fps);
goto done;
}
resource = smartlist_join_strings(fps, "", 0, NULL); resource = smartlist_join_strings(fps, "", 0, NULL);
resource[strlen(resource)-1] = '\0'; resource[strlen(resource)-1] = '\0';
directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
resource, PDS_RETRY_IF_NO_SERVERS);
tor_free(resource);
}
/* else we didn't add any: they were all pending */
SMARTLIST_FOREACH(fps, char *, cp, tor_free(cp)); SMARTLIST_FOREACH(fps, char *, cp, tor_free(cp));
smartlist_free(fps); smartlist_free(fps);
} }
/* Do downloads by identity digest/signing key pair */
if (smartlist_len(missing_cert_digests) > 0) {
smartlist_t *fp_pairs = smartlist_new();
int need_plus = 0, offset = 0;
smartlist_add(fp_pairs, tor_strdup("fp-sk/"));
SMARTLIST_FOREACH_BEGIN(missing_cert_digests, const fp_pair_t *, d) {
char *fp_pair;
if (fp_pair_map_get(pending_cert, d))
continue;
fp_pair = tor_malloc(2*HEX_DIGEST_LEN+3);
offset = 0;
if (need_plus) {
fp_pair[offset++] = '+';
} else {
/* Prepend a '+' to all but the first in the list */
need_plus = 1;
}
/* Encode the first fingerprint */
base16_encode(fp_pair + offset, HEX_DIGEST_LEN+1,
d->first, DIGEST_LEN);
offset += HEX_DIGEST_LEN;
/* Add a '-' to separate them */
fp_pair[offset++] = '-';
/* Encode the second fingerprint */
base16_encode(fp_pair + offset, HEX_DIGEST_LEN+1,
d->second, DIGEST_LEN);
offset += HEX_DIGEST_LEN;
/* Add a NUL */
fp_pair[offset++] = '\0';
/* Add it to the list of pairs to request */
smartlist_add(fp_pairs, fp_pair);
} SMARTLIST_FOREACH_END(d);
if (smartlist_len(fp_pairs) > 1) {
resource = smartlist_join_strings(fp_pairs, "", 0, NULL);
resource[strlen(resource)-1] = '\0';
directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0, directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
resource, PDS_RETRY_IF_NO_SERVERS); resource, PDS_RETRY_IF_NO_SERVERS);
done:
tor_free(resource); tor_free(resource);
smartlist_free(missing_digests); }
digestmap_free(pending, NULL); /* else they were all pending */
SMARTLIST_FOREACH(fp_pairs, char *, p, tor_free(p));
smartlist_free(fp_pairs);
}
smartlist_free(missing_id_digests);
SMARTLIST_FOREACH(missing_cert_digests, fp_pair_t *, p, tor_free(p));
smartlist_free(missing_cert_digests);
digestmap_free(pending_id, NULL);
fp_pair_map_free(pending_cert, NULL);
} }
/* Router descriptor storage. /* Router descriptor storage.
@ -4283,6 +4529,41 @@ list_pending_microdesc_downloads(digestmap_t *result)
list_pending_downloads(result, DIR_PURPOSE_FETCH_MICRODESC, "d/"); list_pending_downloads(result, DIR_PURPOSE_FETCH_MICRODESC, "d/");
} }
/** For every certificate we are currently downloading by (identity digest,
* signing key digest) pair, set result[fp_pair] to (void *1).
*/
static void
list_pending_fpsk_downloads(fp_pair_map_t *result)
{
const char *pfx = "fp-sk/";
smartlist_t *tmp;
smartlist_t *conns;
const char *resource;
tor_assert(result);
tmp = smartlist_new();
conns = get_connection_array();
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
if (conn->type == CONN_TYPE_DIR &&
conn->purpose == DIR_PURPOSE_FETCH_CERTIFICATE &&
!conn->marked_for_close) {
resource = TO_DIR_CONN(conn)->requested_resource;
if (!strcmpstart(resource, pfx))
dir_split_resource_into_fingerprint_pairs(resource + strlen(pfx),
tmp);
}
} SMARTLIST_FOREACH_END(conn);
SMARTLIST_FOREACH_BEGIN(tmp, fp_pair_t *, fp) {
fp_pair_map_set(result, fp, (void*)1);
tor_free(fp);
} SMARTLIST_FOREACH_END(fp);
smartlist_free(tmp);
}
/** Launch downloads for all the descriptors whose digests or digests256 /** Launch downloads for all the descriptors whose digests or digests256
* are listed as digests[i] for lo <= i < hi. (Lo and hi may be out of * are listed as digests[i] for lo <= i < hi. (Lo and hi may be out of
* range.) If <b>source</b> is given, download from <b>source</b>; * range.) If <b>source</b> is given, download from <b>source</b>;

View File

@ -13,7 +13,20 @@
int get_n_authorities(dirinfo_type_t type); int get_n_authorities(dirinfo_type_t type);
int trusted_dirs_reload_certs(void); int trusted_dirs_reload_certs(void);
int trusted_dirs_load_certs_from_string(const char *contents, int from_store,
/*
* Pass one of these as source to trusted_dirs_load_certs_from_string()
* to indicate whence string originates; this controls error handling
* behavior such as marking downloads as failed.
*/
#define TRUSTED_DIRS_CERTS_SRC_SELF 0
#define TRUSTED_DIRS_CERTS_SRC_FROM_STORE 1
#define TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST 2
#define TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_SK_DIGEST 3
#define TRUSTED_DIRS_CERTS_SRC_FROM_VOTE 4
int trusted_dirs_load_certs_from_string(const char *contents, int source,
int flush); int flush);
void trusted_dirs_flush_certs_to_disk(void); void trusted_dirs_flush_certs_to_disk(void);
authority_cert_t *authority_cert_get_newest_by_id(const char *id_digest); authority_cert_t *authority_cert_get_newest_by_id(const char *id_digest);
@ -21,7 +34,8 @@ authority_cert_t *authority_cert_get_by_sk_digest(const char *sk_digest);
authority_cert_t *authority_cert_get_by_digests(const char *id_digest, authority_cert_t *authority_cert_get_by_digests(const char *id_digest,
const char *sk_digest); const char *sk_digest);
void authority_cert_get_all(smartlist_t *certs_out); void authority_cert_get_all(smartlist_t *certs_out);
void authority_cert_dl_failed(const char *id_digest, int status); void authority_cert_dl_failed(const char *id_digest,
const char *signing_key_digest, int status);
void authority_certs_fetch_missing(networkstatus_t *status, time_t now); void authority_certs_fetch_missing(networkstatus_t *status, time_t now);
int router_reload_router_list(void); int router_reload_router_list(void);
int authority_cert_dl_looks_uncertain(const char *id_digest); int authority_cert_dl_looks_uncertain(const char *id_digest);