#include "core/or/or.h"
#include "app/config/config.h"
#include "core/mainloop/connection.h"
#include "core/mainloop/mainloop.h"
#include "core/or/policies.h"
#include "feature/client/bridges.h"
#include "feature/dirauth/authmode.h"
#include "feature/dirclient/dirclient.h"
#include "feature/dirclient/dlstatus.h"
#include "feature/dircommon/directory.h"
#include "feature/dircommon/fp_pair.h"
#include "feature/dirparse/authcert_parse.h"
#include "feature/nodelist/authcert.h"
#include "feature/nodelist/dirlist.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/node_select.h"
#include "feature/nodelist/nodelist.h"
#include "feature/nodelist/routerlist.h"
#include "feature/relay/routermode.h"
#include "core/or/connection_st.h"
#include "feature/dirclient/dir_server_st.h"
#include "feature/dircommon/dir_connection_st.h"
#include "feature/nodelist/authority_cert_st.h"
#include "feature/nodelist/document_signature_st.h"
#include "feature/nodelist/networkstatus_st.h"
#include "feature/nodelist/networkstatus_voter_info_st.h"
#include "feature/nodelist/node_st.h"
DECLARE_TYPED_DIGESTMAP_FNS(dsmap, digest_ds_map_t, download_status_t)
#define DSMAP_FOREACH(map, keyvar, valvar) \
DIGESTMAP_FOREACH(dsmap_to_digestmap(map), keyvar, download_status_t *, \
valvar)
#define dsmap_free(map, fn) MAP_FREE_AND_NULL(dsmap, (map), (fn))
typedef struct cert_list_t cert_list_t;
static void download_status_reset_by_sk_in_cl(cert_list_t *cl,
const char *digest);
static int download_status_is_ready_by_sk_in_cl(cert_list_t *cl,
const char *digest,
time_t now);
static void list_pending_fpsk_downloads(fp_pair_map_t *result);
struct cert_list_t {
struct digest_ds_map_t *dl_status_map;
download_status_t dl_status_by_id;
smartlist_t *certs;
};
static digestmap_t *trusted_dir_certs = NULL;
static int trusted_dir_servers_certs_changed = 0;
static void
download_status_cert_init(download_status_t *dlstatus)
{
dlstatus->schedule = DL_SCHED_CONSENSUS;
dlstatus->want_authority = DL_WANT_ANY_DIRSERVER;
dlstatus->increment_on = DL_SCHED_INCREMENT_FAILURE;
dlstatus->last_backoff_position = 0;
dlstatus->last_delay_used = 0;
download_status_reset(dlstatus);
}
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);
if (!(cl->dl_status_map)) {
cl->dl_status_map = dsmap_new();
}
dlstatus = dsmap_get(cl->dl_status_map, digest);
if (!dlstatus) {
dlstatus = tor_malloc_zero(sizeof(*dlstatus));
dsmap_set(cl->dl_status_map, digest, dlstatus);
download_status_cert_init(dlstatus);
}
tor_assert(dlstatus);
download_status_reset(dlstatus);
}
static int
download_status_is_ready_by_sk_in_cl(cert_list_t *cl,
const char *digest,
time_t now)
{
int rv = 0;
download_status_t *dlstatus = NULL;
tor_assert(cl);
tor_assert(digest);
if (!(cl->dl_status_map)) {
cl->dl_status_map = dsmap_new();
}
dlstatus = dsmap_get(cl->dl_status_map, digest);
if (dlstatus) {
rv = download_status_is_ready(dlstatus, now);
} else {
dlstatus = tor_malloc_zero(sizeof(*dlstatus));
download_status_cert_init(dlstatus);
dsmap_set(cl->dl_status_map, digest, dlstatus);
rv = 1;
}
return rv;
}
static cert_list_t *
get_cert_list(const char *id_digest)
{
cert_list_t *cl;
if (!trusted_dir_certs)
trusted_dir_certs = digestmap_new();
cl = digestmap_get(trusted_dir_certs, id_digest);
if (!cl) {
cl = tor_malloc_zero(sizeof(cert_list_t));
download_status_cert_init(&cl->dl_status_by_id);
cl->certs = smartlist_new();
cl->dl_status_map = dsmap_new();
digestmap_set(trusted_dir_certs, id_digest, cl);
}
return cl;
}
MOCK_IMPL(smartlist_t *,
list_authority_ids_with_downloads, (void))
{
smartlist_t *ids = smartlist_new();
digestmap_iter_t *i;
const char *digest;
char *tmp;
void *cl;
if (trusted_dir_certs) {
for (i = digestmap_iter_init(trusted_dir_certs);
!(digestmap_iter_done(i));
i = digestmap_iter_next(trusted_dir_certs, i)) {
digestmap_iter_get(i, &digest, &cl);
tmp = tor_malloc(DIGEST_LEN);
memcpy(tmp, digest, DIGEST_LEN);
smartlist_add(ids, tmp);
}
}
return ids;
}
MOCK_IMPL(download_status_t *,
id_only_download_status_for_authority_id, (const char *digest))
{
download_status_t *dl = NULL;
cert_list_t *cl;
if (trusted_dir_certs) {
cl = digestmap_get(trusted_dir_certs, digest);
if (cl) {
dl = &(cl->dl_status_by_id);
}
}
return dl;
}
MOCK_IMPL(smartlist_t *,
list_sk_digests_for_authority_id, (const char *digest))
{
smartlist_t *sks = NULL;
cert_list_t *cl;
dsmap_iter_t *i;
const char *sk_digest;
char *tmp;
download_status_t *dl;
if (trusted_dir_certs) {
cl = digestmap_get(trusted_dir_certs, digest);
if (cl) {
sks = smartlist_new();
if (cl->dl_status_map) {
for (i = dsmap_iter_init(cl->dl_status_map);
!(dsmap_iter_done(i));
i = dsmap_iter_next(cl->dl_status_map, i)) {
dsmap_iter_get(i, &sk_digest, &dl);
tmp = tor_malloc(DIGEST_LEN);
memcpy(tmp, sk_digest, DIGEST_LEN);
smartlist_add(sks, tmp);
}
}
}
}
return sks;
}
MOCK_IMPL(download_status_t *,
download_status_for_authority_id_and_sk,(const char *id_digest,
const char *sk_digest))
{
download_status_t *dl = NULL;
cert_list_t *cl = NULL;
if (trusted_dir_certs) {
cl = digestmap_get(trusted_dir_certs, id_digest);
if (cl && cl->dl_status_map) {
dl = dsmap_get(cl->dl_status_map, sk_digest);
}
}
return dl;
}
#define cert_list_free(val) \
FREE_AND_NULL(cert_list_t, cert_list_free_, (val))
static void
cert_list_free_(cert_list_t *cl)
{
if (!cl)
return;
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
authority_cert_free(cert));
smartlist_free(cl->certs);
dsmap_free(cl->dl_status_map, tor_free_);
tor_free(cl);
}
static void
cert_list_free_void(void *cl)
{
cert_list_free_(cl);
}
int
trusted_dirs_reload_certs(void)
{
char *filename;
char *contents;
int r;
filename = get_cachedir_fname("cached-certs");
contents = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL);
tor_free(filename);
if (!contents)
return 0;
r = trusted_dirs_load_certs_from_string(
contents,
TRUSTED_DIRS_CERTS_SRC_FROM_STORE, 1, NULL);
tor_free(contents);
return r;
}
static inline int
already_have_cert(authority_cert_t *cert)
{
cert_list_t *cl = get_cert_list(cert->cache_info.identity_digest);
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, c,
{
if (tor_memeq(c->cache_info.signed_descriptor_digest,
cert->cache_info.signed_descriptor_digest,
DIGEST_LEN))
return 1;
});
return 0;
}
int
trusted_dirs_load_certs_from_string(const char *contents, int source,
int flush, const char *source_dir)
{
dir_server_t *ds;
const char *s, *eos;
int failure_code = 0;
int from_store = (source == TRUSTED_DIRS_CERTS_SRC_FROM_STORE);
int added_trusted_cert = 0;
for (s = contents; *s; s = eos) {
authority_cert_t *cert = authority_cert_parse_from_string(s, strlen(s),
&eos);
cert_list_t *cl;
if (!cert) {
failure_code = -1;
break;
}
ds = trusteddirserver_get_by_v3_auth_digest(
cert->cache_info.identity_digest);
log_debug(LD_DIR, "Parsed certificate for %s",
ds ? ds->nickname : "unknown authority");
if (already_have_cert(cert)) {
log_info(LD_DIR, "Skipping %s certificate for %s that we "
"already have.",
from_store ? "cached" : "downloaded",
ds ? ds->nickname : "an old or new authority");
if (!from_store) {
if (authdir_mode(get_options())) {
log_warn(LD_DIR,
"Got a certificate for %s, but we already have it. "
"Maybe they haven't updated it. Waiting for a while.",
ds ? ds->nickname : "an old or new authority");
} else {
log_info(LD_DIR,
"Got a certificate for %s, but we already have it. "
"Maybe they haven't updated it. Waiting for a while.",
ds ? ds->nickname : "an old or new authority");
}
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);
continue;
}
if (ds) {
added_trusted_cert = 1;
log_info(LD_DIR, "Adding %s certificate for directory authority %s with "
"signing key %s", from_store ? "cached" : "downloaded",
ds->nickname, hex_str(cert->signing_key_digest,DIGEST_LEN));
} else {
int adding = we_want_to_fetch_unknown_auth_certs(get_options());
log_info(LD_DIR, "%s %s certificate for unrecognized directory "
"authority with signing key %s",
adding ? "Adding" : "Not adding",
from_store ? "cached" : "downloaded",
hex_str(cert->signing_key_digest,DIGEST_LEN));
if (!adding) {
authority_cert_free(cert);
continue;
}
}
cl = get_cert_list(cert->cache_info.identity_digest);
smartlist_add(cl->certs, cert);
if (ds && cert->cache_info.published_on > ds->addr_current_at) {
if (!tor_addr_is_null(&cert->ipv4_addr) && cert->ipv4_dirport &&
(!tor_addr_eq(&ds->ipv4_addr, &cert->ipv4_addr) ||
ds->ipv4_dirport != cert->ipv4_dirport)) {
log_notice(LD_DIR, "Updating address for directory authority %s "
"from %s:%"PRIu16" to %s:%"PRIu16" based on certificate.",
ds->nickname, ds->address, ds->ipv4_dirport,
fmt_addr(&cert->ipv4_addr), cert->ipv4_dirport);
tor_addr_copy(&ds->ipv4_addr, &cert->ipv4_addr);
ds->ipv4_dirport = cert->ipv4_dirport;
}
ds->addr_current_at = cert->cache_info.published_on;
}
if (!from_store)
trusted_dir_servers_certs_changed = 1;
}
if (flush)
trusted_dirs_flush_certs_to_disk();
if (failure_code == 0 && added_trusted_cert) {
networkstatus_note_certs_arrived(source_dir);
} else {
networkstatus_note_certs_arrived(NULL);
}
return failure_code;
}
void
trusted_dirs_flush_certs_to_disk(void)
{
char *filename;
smartlist_t *chunks;
if (!trusted_dir_servers_certs_changed || !trusted_dir_certs)
return;
chunks = smartlist_new();
DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
{
sized_chunk_t *c = tor_malloc(sizeof(sized_chunk_t));
c->bytes = cert->cache_info.signed_descriptor_body;
c->len = cert->cache_info.signed_descriptor_len;
smartlist_add(chunks, c);
});
} DIGESTMAP_FOREACH_END;
filename = get_cachedir_fname("cached-certs");
if (write_chunks_to_file(filename, chunks, 0, 0)) {
log_warn(LD_FS, "Error writing certificates to disk.");
}
tor_free(filename);
SMARTLIST_FOREACH(chunks, sized_chunk_t *, c, tor_free(c));
smartlist_free(chunks);
trusted_dir_servers_certs_changed = 0;
}
static int
compare_certs_by_pubdates(const void **_a, const void **_b)
{
const authority_cert_t *cert1 = *_a, *cert2=*_b;
if (cert1->cache_info.published_on < cert2->cache_info.published_on)
return -1;
else if (cert1->cache_info.published_on > cert2->cache_info.published_on)
return 1;
else
return 0;
}
void
trusted_dirs_remove_old_certs(void)
{
time_t now = time(NULL);
#define DEAD_CERT_LIFETIME (2*24*60*60)
#define SUPERSEDED_CERT_LIFETIME (2*24*60*60)
if (!trusted_dir_certs)
return;
DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
smartlist_sort(cl->certs, compare_certs_by_pubdates);
SMARTLIST_FOREACH_BEGIN(cl->certs, authority_cert_t *, cert) {
if (cert_sl_idx == smartlist_len(cl->certs) - 1) {
continue;
}
authority_cert_t *next_cert = smartlist_get(cl->certs, cert_sl_idx+1);
const time_t next_cert_published = next_cert->cache_info.published_on;
if (next_cert_published > now) {
break;
}
int should_remove = 0;
if (cert->expires + DEAD_CERT_LIFETIME < now) {
should_remove = 1;
} else if (next_cert_published + SUPERSEDED_CERT_LIFETIME < now) {
should_remove = 1;
}
if (should_remove) {
SMARTLIST_DEL_CURRENT_KEEPORDER(cl->certs, cert);
authority_cert_free(cert);
trusted_dir_servers_certs_changed = 1;
}
} SMARTLIST_FOREACH_END(cert);
} DIGESTMAP_FOREACH_END;
#undef DEAD_CERT_LIFETIME
#undef OLD_CERT_LIFETIME
trusted_dirs_flush_certs_to_disk();
}
authority_cert_t *
authority_cert_get_newest_by_id(const char *id_digest)
{
cert_list_t *cl;
authority_cert_t *best = NULL;
if (!trusted_dir_certs ||
!(cl = digestmap_get(trusted_dir_certs, id_digest)))
return NULL;
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
{
if (!best || cert->cache_info.published_on > best->cache_info.published_on)
best = cert;
});
return best;
}
authority_cert_t *
authority_cert_get_by_sk_digest(const char *sk_digest)
{
authority_cert_t *c;
if (!trusted_dir_certs)
return NULL;
if ((c = get_my_v3_authority_cert()) &&
tor_memeq(c->signing_key_digest, sk_digest, DIGEST_LEN))
return c;
if ((c = get_my_v3_legacy_cert()) &&
tor_memeq(c->signing_key_digest, sk_digest, DIGEST_LEN))
return c;
DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
{
if (tor_memeq(cert->signing_key_digest, sk_digest, DIGEST_LEN))
return cert;
});
} DIGESTMAP_FOREACH_END;
return NULL;
}
authority_cert_t *
authority_cert_get_by_digests(const char *id_digest,
const char *sk_digest)
{
cert_list_t *cl;
if (!trusted_dir_certs ||
!(cl = digestmap_get(trusted_dir_certs, id_digest)))
return NULL;
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
if (tor_memeq(cert->signing_key_digest, sk_digest, DIGEST_LEN))
return cert; );
return NULL;
}
void
authority_cert_get_all(smartlist_t *certs_out)
{
tor_assert(certs_out);
if (!trusted_dir_certs)
return;
DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, c,
smartlist_add(certs_out, c));
} DIGESTMAP_FOREACH_END;
}
void
authority_cert_dl_failed(const char *id_digest,
const char *signing_key_digest, int status)
{
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 ||
!(cl = digestmap_get(trusted_dir_certs, id_digest)))
return;
if (!signing_key_digest) {
download_status_failed(&cl->dl_status_by_id, status);
} else {
dlstatus = dsmap_get(cl->dl_status_map, signing_key_digest);
if (dlstatus) {
download_status_failed(dlstatus, status);
} else {
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_BUG,
"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);
}
}
}
static const char *BAD_SIGNING_KEYS[] = {
"09CD84F751FD6E955E0F8ADB497D5401470D697E", "0E7E9C07F0969D0468AD741E172A6109DC289F3C", "57B85409891D3FB32137F642FDEDF8B7F8CDFDCD", "87326329007AF781F587AF5B594E540B2B6C7630", "98CC82342DE8D298CF99D3F1A396475901E0D38E", "9904B52336713A5ADCB13E4FB14DC919E0D45571", "9DCD8E3F1DD1597E2AD476BBA28A1A89F3095227", "A61682F34B9BB9694AC98491FE1ABBFE61923941", "B59F6E99C575113650C99F1C425BA7B20A8C071D", "D27178388FA75B96D37FA36E0B015227DDDBDA51", NULL,
};
int
authority_cert_is_denylisted(const authority_cert_t *cert)
{
char hex_digest[HEX_DIGEST_LEN+1];
int i;
base16_encode(hex_digest, sizeof(hex_digest),
cert->signing_key_digest, sizeof(cert->signing_key_digest));
for (i = 0; BAD_SIGNING_KEYS[i]; ++i) {
if (!strcasecmp(hex_digest, BAD_SIGNING_KEYS[i])) {
return 1;
}
}
return 0;
}
int
authority_cert_dl_looks_uncertain(const char *id_digest)
{
#define N_AUTH_CERT_DL_FAILURES_TO_BUG_USER 2
cert_list_t *cl;
int n_failures;
if (!trusted_dir_certs ||
!(cl = digestmap_get(trusted_dir_certs, id_digest)))
return 0;
n_failures = download_status_get_n_failures(&cl->dl_status_by_id);
return n_failures >= N_AUTH_CERT_DL_FAILURES_TO_BUG_USER;
}
static void
authority_certs_fetch_resource_impl(const char *resource,
const char *dir_hint,
const node_t *node,
const routerstatus_t *rs)
{
const or_options_t *options = get_options();
int get_via_tor = purpose_needs_anonymity(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
resource);
if (options->UseBridges) {
if (node && !node_is_a_configured_bridge(node)) {
get_via_tor = 1;
} else if (!node) {
get_via_tor = 1;
}
}
const dir_indirection_t indirection = get_via_tor ? DIRIND_ANONYMOUS
: DIRIND_ONEHOP;
directory_request_t *req = NULL;
if (options->UseBridges && node && node->ri && !get_via_tor) {
tor_addr_port_t or_ap;
reachable_addr_choose_from_node(node, FIREWALL_OR_CONNECTION, 0,
&or_ap);
req = directory_request_new(DIR_PURPOSE_FETCH_CERTIFICATE);
directory_request_set_or_addr_port(req, &or_ap);
if (dir_hint)
directory_request_set_directory_id_digest(req, dir_hint);
} else if (rs) {
req = directory_request_new(DIR_PURPOSE_FETCH_CERTIFICATE);
directory_request_set_routerstatus(req, rs);
}
if (req) {
directory_request_set_indirection(req, indirection);
directory_request_set_resource(req, resource);
directory_initiate_request(req);
directory_request_free(req);
return;
}
directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
resource, PDS_RETRY_IF_NO_SERVERS,
DL_WANT_ANY_DIRSERVER);
}
void
authority_certs_fetch_missing(networkstatus_t *status, time_t now,
const char *dir_hint)
{
digestmap_t *pending_id;
fp_pair_map_t *pending_cert;
smartlist_t *missing_cert_digests, *missing_id_digests;
char *resource = NULL;
cert_list_t *cl;
const or_options_t *options = get_options();
const int keep_unknown = we_want_to_fetch_unknown_auth_certs(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(options, NULL))
return;
pending_cert = fp_pair_map_new();
pending_id = digestmap_new();
missing_cert_digests = smartlist_new();
missing_id_digests = smartlist_new();
list_pending_downloads(pending_id, NULL,
DIR_PURPOSE_FETCH_CERTIFICATE, "fp/");
list_pending_fpsk_downloads(pending_cert);
SMARTLIST_FOREACH_BEGIN(router_get_trusted_dir_servers(),
dir_server_t *, ds) {
int found = 0;
if (!(ds->type & V3_DIRINFO))
continue;
if (smartlist_contains_digest(missing_id_digests,
ds->v3_identity_digest))
continue;
cl = get_cert_list(ds->v3_identity_digest);
SMARTLIST_FOREACH_BEGIN(cl->certs, authority_cert_t *, cert) {
if (now < cert->expires) {
download_status_reset(&(cl->dl_status_by_id));
download_status_reset_by_sk_in_cl(cl, cert->signing_key_digest);
found = 1;
break;
}
} SMARTLIST_FOREACH_END(cert);
if (!found &&
download_status_is_ready(&(cl->dl_status_by_id), now) &&
!digestmap_get(pending_id, ds->v3_identity_digest)) {
log_info(LD_DIR,
"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);
if (status) {
SMARTLIST_FOREACH_BEGIN(status->voters, networkstatus_voter_info_t *,
voter) {
if (!smartlist_len(voter->sigs))
continue;
if (!keep_unknown &&
!trusteddirserver_get_by_v3_auth_digest(voter->identity_digest))
continue;
cl = get_cert_list(voter->identity_digest);
if (smartlist_len(cl->certs) == 0) {
if (digestmap_get(pending_id, voter->identity_digest))
continue;
if (smartlist_contains_digest(missing_id_digests,
voter->identity_digest))
continue;
}
SMARTLIST_FOREACH_BEGIN(voter->sigs, document_signature_t *, sig) {
authority_cert_t *cert =
authority_cert_get_by_digests(voter->identity_digest,
sig->signing_key_digest);
if (cert) {
if (now < cert->expires)
download_status_reset_by_sk_in_cl(cl, sig->signing_key_digest);
continue;
}
if (download_status_is_ready_by_sk_in_cl(
cl, sig->signing_key_digest, now) &&
!fp_pair_map_get_by_digests(pending_cert,
voter->identity_digest,
sig->signing_key_digest)) {
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);
}
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(voter);
}
const node_t *node = NULL;
const routerstatus_t *rs = NULL;
if (dir_hint) {
if (options->UseBridges) {
node = node_get_by_id(dir_hint);
}
rs = router_get_consensus_status_by_id(dir_hint);
if (!rs) {
const dir_server_t *ds = router_get_fallback_dirserver_by_digest(
dir_hint);
if (ds) {
rs = &ds->fake_status;
}
}
if (!node && !rs) {
log_warn(LD_BUG, "Directory %s delivered a consensus, but %s"
"no routerstatus could be found for it.",
options->UseBridges ? "no node and " : "",
hex_str(dir_hint, DIGEST_LEN));
}
}
if (smartlist_len(missing_id_digests) > 0) {
int need_plus = 0;
smartlist_t *fps = smartlist_new();
smartlist_add_strdup(fps, "fp/");
SMARTLIST_FOREACH_BEGIN(missing_id_digests, const char *, d) {
char *fp = NULL;
if (digestmap_get(pending_id, d))
continue;
base16_encode(id_digest_str, sizeof(id_digest_str),
d, DIGEST_LEN);
if (need_plus) {
tor_asprintf(&fp, "+%s", id_digest_str);
} else {
fp = tor_strdup(id_digest_str);
need_plus = 1;
}
smartlist_add(fps, fp);
} SMARTLIST_FOREACH_END(d);
if (smartlist_len(fps) > 1) {
resource = smartlist_join_strings(fps, "", 0, NULL);
authority_certs_fetch_resource_impl(resource, dir_hint, node, rs);
tor_free(resource);
}
SMARTLIST_FOREACH(fps, char *, cp, tor_free(cp));
smartlist_free(fps);
}
if (smartlist_len(missing_cert_digests) > 0) {
int need_plus = 0;
smartlist_t *fp_pairs = smartlist_new();
smartlist_add_strdup(fp_pairs, "fp-sk/");
SMARTLIST_FOREACH_BEGIN(missing_cert_digests, const fp_pair_t *, d) {
char *fp_pair = NULL;
if (fp_pair_map_get(pending_cert, d))
continue;
base16_encode(id_digest_str, sizeof(id_digest_str),
d->first, DIGEST_LEN);
base16_encode(sk_digest_str, sizeof(sk_digest_str),
d->second, DIGEST_LEN);
if (need_plus) {
tor_asprintf(&fp_pair, "+%s-%s", id_digest_str, sk_digest_str);
} else {
tor_asprintf(&fp_pair, "%s-%s", id_digest_str, sk_digest_str);
need_plus = 1;
}
smartlist_add(fp_pairs, fp_pair);
} SMARTLIST_FOREACH_END(d);
if (smartlist_len(fp_pairs) > 1) {
resource = smartlist_join_strings(fp_pairs, "", 0, NULL);
authority_certs_fetch_resource_impl(resource, dir_hint, node, rs);
tor_free(resource);
}
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);
}
void
authcert_free_all(void)
{
if (trusted_dir_certs) {
digestmap_free(trusted_dir_certs, cert_list_free_void);
trusted_dir_certs = NULL;
}
}
void
authority_cert_free_(authority_cert_t *cert)
{
if (!cert)
return;
tor_free(cert->cache_info.signed_descriptor_body);
crypto_pk_free(cert->signing_key);
crypto_pk_free(cert->identity_key);
tor_free(cert);
}
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);
}