Commit 68cf666d authored by Nick Mathewson's avatar Nick Mathewson 🦀
Browse files

Fix bug 571: associate certificates with keys, not dirservers, so that we can...

Fix bug 571: associate certificates with keys, not dirservers, so that we can have certificates for dirservers we do not recognize.

svn:r13304
parent c7fe6337
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
Changes in version 0.2.0.19-alpha - 2008-0?-??
  o Minor bugfixes:
    - Directory caches now fetch certificates from all authorities
      listed in a networkstatus consensus, even when they do not
      recognize them.  Fixes bug 571.  Bugfix on 0.2.0.x.


Changes in version 0.2.0.18-alpha - 2008-01-25
+2 −11
Original line number Diff line number Diff line
@@ -601,9 +601,7 @@ connection_dir_download_cert_failed(dir_connection_t *conn, int status)
                                       failed, NULL, 1, 0);
  SMARTLIST_FOREACH(failed, char *, cp,
  {
    trusted_dir_server_t *dir = trusteddirserver_get_by_v3_auth_digest(cp);
    if (dir)
      download_status_failed(&dir->cert_dl_status, status);
    authority_cert_dl_failed(cp, status);
    tor_free(cp);
  });
  smartlist_free(failed);
@@ -2494,14 +2492,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
    smartlist_t *certs = smartlist_create();
    ssize_t len = -1;
    if (!strcmp(url, "/tor/keys/all")) {
      SMARTLIST_FOREACH(router_get_trusted_dir_servers(),
                        trusted_dir_server_t *, ds,
      {
        if (!ds->v3_certs)
          continue;
        SMARTLIST_FOREACH(ds->v3_certs, authority_cert_t *, cert,
                          smartlist_add(certs, cert));
      });
      authority_cert_get_all(certs);
    } else if (!strcmp(url, "/tor/keys/authority")) {
      authority_cert_t *cert = get_my_v3_authority_cert();
      if (cert)
+8 −0
Original line number Diff line number Diff line
@@ -3030,6 +3030,10 @@ int dir_split_resource_into_fingerprints(const char *resource,
char *directory_dump_request_log(void);
int router_supports_extrainfo(const char *identity_digest, int is_authority);

void directory_post_to_hs_dir(smartlist_t *descs, const char *service_id,
                              int seconds_valid);
int directory_get_from_hs_dir(const char *desc_id, const char *query);

time_t download_status_increment_failure(download_status_t *dls,
                                         int status_code, const char *item,
                                         int server, time_t now);
@@ -3813,9 +3817,11 @@ typedef struct trusted_dir_server_t {
  /** What kind of authority is this? (Bitfield.) */
  authority_type_t type;

#if 0
  smartlist_t *v3_certs; /**< V3 key certificates for this authority */
  download_status_t cert_dl_status; /**< Status of downloading this server's
                               * latest certificate. */
#endif
  download_status_t v2_ns_dl_status; /**< Status of downloading this server's
                               * v2 network status. */
  time_t addr_current_at; /**< When was the document that we derived the
@@ -3840,6 +3846,8 @@ authority_cert_t *authority_cert_get_newest_by_id(const char *id_digest);
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,
                                                const char *sk_digest);
void authority_cert_get_all(smartlist_t *certs_out);
void authority_cert_dl_failed(const char *id_digest, int status);
void authority_certs_fetch_missing(networkstatus_vote_t *status, time_t now);
int router_reload_router_list(void);
smartlist_t *router_get_trusted_dir_servers(void);
+114 −67
Original line number Diff line number Diff line
@@ -47,8 +47,16 @@ DECLARE_TYPED_DIGESTMAP_FNS(eimap_, digest_ei_map_t, extrainfo_t)
/** Global list of a trusted_dir_server_t object for each trusted directory
 * server. */
static smartlist_t *trusted_dir_servers = NULL;
/** True iff the key certificate in at least one member of
 * <b>trusted_dir_server_t</b> has changed since we last flushed the

/** DOCDOC */
typedef struct cert_list_t {
  download_status_t dl_status;
  smartlist_t *certs;
} cert_list_t;
/** Map from v3 identity key digest to cert_list_t. */
static digestmap_t *trusted_dir_certs = NULL;
/** True iff any key certificate in at least one member of
 * <b>trusted_dir_certs</b> has changed since we last flushed the
 * certificates to disk. */
static int trusted_dir_servers_certs_changed = 0;

@@ -80,6 +88,22 @@ get_n_authorities(authority_type_t type)

#define get_n_v2_authorities() get_n_authorities(V2_AUTHORITY)

/** DOCDOC */
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));
    cl->certs = smartlist_create();
    digestmap_set(trusted_dir_certs, id_digest, cl);
  }
  return cl;
}

/** Reload the cached v3 key certificates from the cached-certs file in
 * the data directory. Return 0 on success, -1 on failure. */
int
@@ -108,6 +132,7 @@ int
trusted_dirs_load_certs_from_string(const char *contents, int from_store)
{
  trusted_dir_server_t *ds;
  cert_list_t *cl;
  const char *s, *eos;

  for (s = contents; *s; s = eos) {
@@ -117,17 +142,20 @@ trusted_dirs_load_certs_from_string(const char *contents, int from_store)
      break;
    ds = trusteddirserver_get_by_v3_auth_digest(
                                       cert->cache_info.identity_digest);
    if (!ds) {

#if 0
    if (drop_unknown && !ds) {
        log_info(LD_DIR, "Found %s certificate whose key didn't match "
                 "any v3 authority we recognized; skipping.",
                 from_store ? "cached" : "downloaded");
        authority_cert_free(cert);
        continue;
      }
    if (!ds->v3_certs)
      ds->v3_certs = smartlist_create();
    }
#endif
    cl = get_cert_list(cert->cache_info.identity_digest);

    SMARTLIST_FOREACH(ds->v3_certs, authority_cert_t *, c,
    SMARTLIST_FOREACH(cl->certs, authority_cert_t *, c,
      {
        if (!memcmp(c->cache_info.signed_descriptor_digest,
                    cert->cache_info.signed_descriptor_digest,
@@ -145,12 +173,19 @@ trusted_dirs_load_certs_from_string(const char *contents, int from_store)
    if (found)
      continue;

    if (ds) {
      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 {
      log_info(LD_DIR, "Adding %s certificate for unrecognized directory "
               "authority with signing key %s",
               from_store ? "cached" : "downloaded",
               hex_str(cert->signing_key_digest,DIGEST_LEN));
    }

    smartlist_add(ds->v3_certs, cert);
    if (cert->cache_info.published_on > ds->addr_current_at) {
    smartlist_add(cl->certs, cert);
    if (ds && cert->cache_info.published_on > ds->addr_current_at) {
      /* Check to see whether we should update our view of the authority's
       * address. */
      if (cert->addr && cert->dir_port &&
@@ -185,23 +220,20 @@ trusted_dirs_flush_certs_to_disk(void)
  char *filename;
  smartlist_t *chunks;

  if (!trusted_dir_servers_certs_changed)
  if (!trusted_dir_servers_certs_changed || !trusted_dir_certs)
    return;

  chunks = smartlist_create();

  SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds,
  {
      if (ds->v3_certs) {
        SMARTLIST_FOREACH(ds->v3_certs, authority_cert_t *, cert,
  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_datadir_fname("cached-certs");
  if (write_chunks_to_file(filename, chunks, 0)) {
    log_warn(LD_FS, "Error writing certificates to disk.");
@@ -221,23 +253,23 @@ static void
trusted_dirs_remove_old_certs(void)
{
#define OLD_CERT_LIFETIME (48*60*60)
  SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds,
    {
  if (!trusted_dir_certs)
    return;

  DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
    authority_cert_t *newest = NULL;
      if (!ds->v3_certs)
        continue;
      SMARTLIST_FOREACH(ds->v3_certs, authority_cert_t *, cert,
    SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
          if (!newest || (cert->cache_info.published_on >
                          newest->cache_info.published_on))
            newest = cert);
      SMARTLIST_FOREACH(ds->v3_certs, authority_cert_t *, cert,
    SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
          if (newest && (newest->cache_info.published_on >
                         cert->cache_info.published_on + OLD_CERT_LIFETIME)) {
            SMARTLIST_DEL_CURRENT(ds->v3_certs, cert);
            SMARTLIST_DEL_CURRENT(cl->certs, cert);
            authority_cert_free(cert);
            trusted_dir_servers_certs_changed = 1;
          });
    });
  } DIGESTMAP_FOREACH_END
#undef OLD_CERT_LIFETIME

  trusted_dirs_flush_certs_to_disk();
@@ -249,11 +281,11 @@ trusted_dirs_remove_old_certs(void)
authority_cert_t *
authority_cert_get_newest_by_id(const char *id_digest)
{
  trusted_dir_server_t *ds = trusteddirserver_get_by_v3_auth_digest(id_digest);
  cert_list_t *cl;
  authority_cert_t *best = NULL;
  if (!ds || !ds->v3_certs)
  if (!trusted_dir_certs || !(cl = digestmap_get(trusted_dir_certs, id_digest)))
    return NULL;
  SMARTLIST_FOREACH(ds->v3_certs, authority_cert_t *, cert,
  SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
  {
    if (!best || cert->cache_info.published_on > best->cache_info.published_on)
      best = cert;
@@ -267,18 +299,16 @@ authority_cert_get_newest_by_id(const char *id_digest)
authority_cert_t *
authority_cert_get_by_sk_digest(const char *sk_digest)
{
  if (!trusted_dir_servers)
  if (!trusted_dir_certs)
    return NULL;
  SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds,
  {
    if (!ds->v3_certs)
      continue;
    SMARTLIST_FOREACH(ds->v3_certs, authority_cert_t *, cert,

  DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
    SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
    {
      if (!memcmp(cert->signing_key_digest, sk_digest, DIGEST_LEN))
        return cert;
    });
  });
  } DIGESTMAP_FOREACH_END
  return NULL;
}

@@ -289,17 +319,40 @@ authority_cert_t *
authority_cert_get_by_digests(const char *id_digest,
                              const char *sk_digest)
{
  trusted_dir_server_t *ds = trusteddirserver_get_by_v3_auth_digest(id_digest);

  if (!ds || !ds->v3_certs)
  cert_list_t *cl;
  if (!trusted_dir_certs || !(cl = digestmap_get(trusted_dir_certs, id_digest)))
    return NULL;
  SMARTLIST_FOREACH(ds->v3_certs, authority_cert_t *, cert,
  SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
    if (!memcmp(cert->signing_key_digest, sk_digest, DIGEST_LEN))
      return cert; );

  return NULL;
}

/** DOCDOC */
void
authority_cert_get_all(smartlist_t *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
}

/** DOCDOC */
void
authority_cert_dl_failed(const char *id_digest, int status)
{
  cert_list_t *cl;
  if (!trusted_dir_certs || !(cl = digestmap_get(trusted_dir_certs, id_digest)))
    return;

  download_status_failed(&cl->dl_status, status);
}

/** How many times will we try to fetch a certificate before giving up? */
#define MAX_CERT_DL_FAILURES 8

@@ -315,6 +368,8 @@ authority_certs_fetch_missing(networkstatus_vote_t *status, time_t now)
  digestmap_t *pending;
  smartlist_t *missing_digests;
  char *resource = NULL;
  cert_list_t *cl;
  const int cache = directory_caches_dir_info(get_options());

  if (should_delay_dir_fetches(get_options()))
    return;
@@ -326,24 +381,22 @@ authority_certs_fetch_missing(networkstatus_vote_t *status, time_t now)
  if (status) {
    SMARTLIST_FOREACH(status->voters, networkstatus_voter_info_t *, voter,
      {
        trusted_dir_server_t *ds
          = trusteddirserver_get_by_v3_auth_digest(voter->identity_digest);
        if (!ds) /* XXXX020 This is wrong!!  If we're a cache, we should
                  * download unrecognized signing keys so we can serve
                  * them. */
          continue;
        if (tor_digest_is_zero(voter->signing_key_digest))
          continue; /* This authority never signed this consensus, so don't
                     * go looking for a cert with key digest 0000000000. */
        if (!cache &&
            !trusteddirserver_get_by_v3_auth_digest(voter->identity_digest))
          continue; /* We are not a cache, and we don't know this authority.*/
        cl = get_cert_list(voter->identity_digest);
        if (authority_cert_get_by_digests(voter->identity_digest,
                                          voter->signing_key_digest)) {
          download_status_reset(&ds->cert_dl_status);
          download_status_reset(&cl->dl_status);
          continue;
        }
        if (download_status_is_ready(&ds->cert_dl_status, now,
        if (download_status_is_ready(&cl->dl_status, now,
                                     MAX_CERT_DL_FAILURES)) {
          log_notice(LD_DIR, "We're missing a certificate from authority %s "
                     "with signing key %s: launching request.", ds->nickname,
          log_notice(LD_DIR, "We're missing a certificate from authority "
                     "with signing key %s: launching request.",
                     hex_str(voter->signing_key_digest, DIGEST_LEN));
          smartlist_add(missing_digests, voter->identity_digest);
        }
@@ -356,19 +409,18 @@ authority_certs_fetch_missing(networkstatus_vote_t *status, time_t now)
        continue;
      if (smartlist_digest_isin(missing_digests, ds->v3_identity_digest))
        continue;
      if (!ds->v3_certs)
        ds->v3_certs = smartlist_create();
      SMARTLIST_FOREACH(ds->v3_certs, authority_cert_t *, cert,
      cl = get_cert_list(ds->v3_identity_digest);
      SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
        {
          if (!ftime_definitely_after(now, cert->expires)) {
            /* It's not expired, and we weren't looking for something to
             * verify a consensus with.  Call it done. */
            download_status_reset(&ds->cert_dl_status);
            download_status_reset(&cl->dl_status);
            found = 1;
            break;
          }
        });
      if (!found && download_status_is_ready(&ds->cert_dl_status, now,
      if (!found && download_status_is_ready(&cl->dl_status, now,
                                             MAX_CERT_DL_FAILURES)) {
        log_notice(LD_DIR, "No current certificate known for authority %s; "
                   "launching request.", ds->nickname);
@@ -3403,11 +3455,6 @@ authority_cert_free(authority_cert_t *cert)
static void
trusted_dir_server_free(trusted_dir_server_t *ds)
{
  if (ds->v3_certs) {
    SMARTLIST_FOREACH(ds->v3_certs, authority_cert_t *, cert,
                      authority_cert_free(cert));
    smartlist_free(ds->v3_certs);
  }
  tor_free(ds->nickname);
  tor_free(ds->description);
  tor_free(ds->address);
+13 −17
Original line number Diff line number Diff line
@@ -1433,14 +1433,13 @@ extrainfo_parse_entry_from_string(const char *s, const char *end,
authority_cert_t *
authority_cert_parse_from_string(const char *s, const char **end_of_string)
{
  authority_cert_t *cert = NULL;
  authority_cert_t *cert = NULL, *old_cert;
  smartlist_t *tokens = NULL;
  char digest[DIGEST_LEN];
  directory_token_t *tok;
  char fp_declared[DIGEST_LEN];
  char *eos;
  size_t len;
  trusted_dir_server_t *ds;
  int found;

  s = eat_whitespace(s);
@@ -1531,22 +1530,19 @@ authority_cert_parse_from_string(const char *s, const char **end_of_string)
  }

  /* If we already have this cert, don't bother checking the signature. */
  ds = trusteddirserver_get_by_v3_auth_digest(
                                     cert->cache_info.identity_digest);
  old_cert = authority_cert_get_by_digests(
                                     cert->cache_info.identity_digest,
                                     cert->signing_key_digest);
  found = 0;
  if (ds && ds->v3_certs) {
    SMARTLIST_FOREACH(ds->v3_certs, authority_cert_t *, c,
      {
  if (old_cert) {
    /* XXXX020 can we just compare signed_descriptor_digest ? */
        if (c->cache_info.signed_descriptor_len == len &&
            c->cache_info.signed_descriptor_body &&
            !memcmp(s, c->cache_info.signed_descriptor_body, len)) {
    if (old_cert->cache_info.signed_descriptor_len == len &&
        old_cert->cache_info.signed_descriptor_body &&
        !memcmp(s, old_cert->cache_info.signed_descriptor_body, len)) {
      log_debug(LD_DIR, "We already checked the signature on this "
                "certificate; no need to do so again.");
      found = 1;
          break;
    }
      });
  }
  if (!found) {
    if (check_signature_token(digest, tok, cert->identity_key, 0,