Ejemplo n.º 1
0
static int
mock_signed_digest_equals__yes(const uint8_t *d1, const uint8_t *d2,
                               size_t len)
{
  (void) tor_memeq(d1, d2, len);
  return 1;
}
Ejemplo n.º 2
0
Archivo: onion.c Proyecto: BwRy/Astoria
/** Parse a CREATE, CREATE_FAST, or CREATE2 cell from <b>cell_in</b> into
 * <b>cell_out</b>. Return 0 on success, -1 on failure. (We reject some
 * syntactically valid CREATE2 cells that we can't generate or react to.) */
int
create_cell_parse(create_cell_t *cell_out, const cell_t *cell_in)
{
  switch (cell_in->command) {
  case CELL_CREATE:
    if (tor_memeq(cell_in->payload, NTOR_CREATE_MAGIC, 16)) {
      create_cell_init(cell_out, CELL_CREATE, ONION_HANDSHAKE_TYPE_NTOR,
                       NTOR_ONIONSKIN_LEN, cell_in->payload+16);
    } else {
      create_cell_init(cell_out, CELL_CREATE, ONION_HANDSHAKE_TYPE_TAP,
                       TAP_ONIONSKIN_CHALLENGE_LEN, cell_in->payload);
    }
    break;
  case CELL_CREATE_FAST:
    create_cell_init(cell_out, CELL_CREATE_FAST, ONION_HANDSHAKE_TYPE_FAST,
                     CREATE_FAST_LEN, cell_in->payload);
    break;
  case CELL_CREATE2:
    if (parse_create2_payload(cell_out, cell_in->payload,
                              CELL_PAYLOAD_SIZE) < 0)
      return -1;
    break;
  default:
    return -1;
  }

  return check_create_cell(cell_out, 0);
}
Ejemplo n.º 3
0
/** Return the first circuit originating here in global_circuitlist after
 * <b>start</b> whose purpose is <b>purpose</b>, and where
 * <b>digest</b> (if set) matches the rend_pk_digest field. Return NULL if no
 * circuit is found.  If <b>start</b> is NULL, begin at the start of the list.
 */
origin_circuit_t *
circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
                                   const char *digest, uint8_t purpose)
{
  circuit_t *circ;
  tor_assert(CIRCUIT_PURPOSE_IS_ORIGIN(purpose));
  if (start == NULL)
    circ = global_circuitlist;
  else
    circ = TO_CIRCUIT(start)->next;

  for ( ; circ; circ = circ->next) {
    if (circ->marked_for_close)
      continue;
    if (circ->purpose != purpose)
      continue;
    if (!digest)
      return TO_ORIGIN_CIRCUIT(circ);
    else if (TO_ORIGIN_CIRCUIT(circ)->rend_data &&
             tor_memeq(TO_ORIGIN_CIRCUIT(circ)->rend_data->rend_pk_digest,
                     digest, DIGEST_LEN))
      return TO_ORIGIN_CIRCUIT(circ);
  }
  return NULL;
}
Ejemplo n.º 4
0
/** Called when we're trying to connect an ap conn; sends an INTRODUCE1 cell
 * down introcirc if possible.
 */
int
rend_client_send_introduction(origin_circuit_t *introcirc,
                              origin_circuit_t *rendcirc)
{
  const or_options_t *options = get_options();
  size_t payload_len;
  int r, v3_shift = 0;
  char payload[RELAY_PAYLOAD_SIZE];
  char tmp[RELAY_PAYLOAD_SIZE];
  rend_cache_entry_t *entry = NULL;
  crypt_path_t *cpath;
  off_t dh_offset;
  crypto_pk_t *intro_key = NULL;
  int status = 0;
  const char *onion_address;

  tor_assert(introcirc->base_.purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
  tor_assert(rendcirc->base_.purpose == CIRCUIT_PURPOSE_C_REND_READY);
  tor_assert(introcirc->rend_data);
  tor_assert(rendcirc->rend_data);
  tor_assert(!rend_cmp_service_ids(rend_data_get_address(introcirc->rend_data),
                                  rend_data_get_address(rendcirc->rend_data)));
  assert_circ_anonymity_ok(introcirc, options);
  assert_circ_anonymity_ok(rendcirc, options);
  onion_address = rend_data_get_address(introcirc->rend_data);

  r = rend_cache_lookup_entry(onion_address, -1, &entry);
  /* An invalid onion address is not possible else we have a big issue. */
  tor_assert(r != -EINVAL);
  if (r < 0 || !rend_client_any_intro_points_usable(entry)) {
    /* If the descriptor is not found or the intro points are not usable
     * anymore, trigger a fetch. */
    log_info(LD_REND,
             "query %s didn't have valid rend desc in cache. "
             "Refetching descriptor.",
             safe_str_client(onion_address));
    rend_client_refetch_v2_renddesc(introcirc->rend_data);
    {
      connection_t *conn;

      while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP,
                       AP_CONN_STATE_CIRCUIT_WAIT, onion_address))) {
        connection_ap_mark_as_waiting_for_renddesc(TO_ENTRY_CONN(conn));
      }
    }

    status = -1;
    goto cleanup;
  }

  /* first 20 bytes of payload are the hash of the service's pk */
  intro_key = NULL;
  SMARTLIST_FOREACH(entry->parsed->intro_nodes, rend_intro_point_t *,
                    intro, {
    if (tor_memeq(introcirc->build_state->chosen_exit->identity_digest,
                intro->extend_info->identity_digest, DIGEST_LEN)) {
      intro_key = intro->intro_key;
      break;
    }
  });
Ejemplo n.º 5
0
Archivo: onion.c Proyecto: ageis/tor
static int
extend_cell_from_extend1_cell_body(extend_cell_t *cell_out,
                                   const extend1_cell_body_t *cell)
{
  tor_assert(cell_out);
  tor_assert(cell);
  memset(cell_out, 0, sizeof(*cell_out));
  tor_addr_make_unspec(&cell_out->orport_ipv4.addr);
  tor_addr_make_unspec(&cell_out->orport_ipv6.addr);

  cell_out->cell_type = RELAY_COMMAND_EXTEND;
  tor_addr_from_ipv4h(&cell_out->orport_ipv4.addr, cell->ipv4addr);
  cell_out->orport_ipv4.port = cell->port;
  if (tor_memeq(cell->onionskin, NTOR_CREATE_MAGIC, 16)) {
    cell_out->create_cell.cell_type = CELL_CREATE2;
    cell_out->create_cell.handshake_type = ONION_HANDSHAKE_TYPE_NTOR;
    cell_out->create_cell.handshake_len = NTOR_ONIONSKIN_LEN;
    memcpy(cell_out->create_cell.onionskin, cell->onionskin + 16,
           NTOR_ONIONSKIN_LEN);
  } else {
    cell_out->create_cell.cell_type = CELL_CREATE;
    cell_out->create_cell.handshake_type = ONION_HANDSHAKE_TYPE_TAP;
    cell_out->create_cell.handshake_len = TAP_ONIONSKIN_CHALLENGE_LEN;
    memcpy(cell_out->create_cell.onionskin, cell->onionskin,
           TAP_ONIONSKIN_CHALLENGE_LEN);
  }
  memcpy(cell_out->node_id, cell->identity, DIGEST_LEN);
  return 0;
}
Ejemplo n.º 6
0
/* This is a helper function used by the hash table code (HT_). It returns 1 if
 * two circuits have the same HS token. */
static int
hs_circuits_have_same_token(const circuit_t *first_circuit,
                            const circuit_t *second_circuit)
{
  const hs_token_t *first_token;
  const hs_token_t *second_token;

  tor_assert(first_circuit);
  tor_assert(second_circuit);

  first_token = first_circuit->hs_token;
  second_token = second_circuit->hs_token;

  /* Both circs must have a token */
  if (BUG(!first_token) || BUG(!second_token)) {
    return 0;
  }

  if (first_token->type != second_token->type) {
    return 0;
  }

  if (first_token->token_len != second_token->token_len)
    return 0;

  return tor_memeq(first_token->token,
                   second_token->token,
                   first_token->token_len);
}
Ejemplo n.º 7
0
/** Return true iff cert1 and cert2 are the same cert. */
int
tor_cert_eq(const tor_cert_t *cert1, const tor_cert_t *cert2)
{
  tor_assert(cert1);
  tor_assert(cert2);
  return cert1->encoded_len == cert2->encoded_len &&
    tor_memeq(cert1->encoded, cert2->encoded, cert1->encoded_len);
}
Ejemplo n.º 8
0
/** Return true iff <b>key1</b> and <b>key2</b> are the same public key. */
int
ed25519_pubkey_eq(const ed25519_public_key_t *key1,
                  const ed25519_public_key_t *key2)
{
  tor_assert(key1);
  tor_assert(key2);
  return tor_memeq(key1->pubkey, key2->pubkey, ED25519_PUBKEY_LEN);
}
Ejemplo n.º 9
0
/** Return 1 iff <b>sockaddr1</b> and <b>sockaddr2</b> represent
 * the same IP address and port combination. Otherwise, return 0.
 */
static uint8_t
sockaddr_in6_are_equal(struct sockaddr_in6 *sockaddr1,
                       struct sockaddr_in6 *sockaddr2)
{
   return ((sockaddr1->sin6_family == sockaddr2->sin6_family) &&
           (sockaddr1->sin6_port == sockaddr2->sin6_port) &&
           (tor_memeq(sockaddr1->sin6_addr.s6_addr,
                      sockaddr2->sin6_addr.s6_addr,16)));
}
Ejemplo n.º 10
0
/** Called when we're trying to connect an ap conn; sends an INTRODUCE1 cell
 * down introcirc if possible.
 */
int
rend_client_send_introduction(origin_circuit_t *introcirc,
                              origin_circuit_t *rendcirc)
{
  size_t payload_len;
  int r, v3_shift = 0;
  char payload[RELAY_PAYLOAD_SIZE];
  char tmp[RELAY_PAYLOAD_SIZE];
  rend_cache_entry_t *entry;
  crypt_path_t *cpath;
  off_t dh_offset;
  crypto_pk_t *intro_key = NULL;
  int status = 0;

  tor_assert(introcirc->_base.purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
  tor_assert(rendcirc->_base.purpose == CIRCUIT_PURPOSE_C_REND_READY);
  tor_assert(introcirc->rend_data);
  tor_assert(rendcirc->rend_data);
  tor_assert(!rend_cmp_service_ids(introcirc->rend_data->onion_address,
                                   rendcirc->rend_data->onion_address));
#ifndef NON_ANONYMOUS_MODE_ENABLED
  tor_assert(!(introcirc->build_state->onehop_tunnel));
  tor_assert(!(rendcirc->build_state->onehop_tunnel));
#endif

  if (rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1,
                              &entry) < 1) {
    log_info(LD_REND,
             "query %s didn't have valid rend desc in cache. "
             "Refetching descriptor.",
             safe_str_client(introcirc->rend_data->onion_address));
    rend_client_refetch_v2_renddesc(introcirc->rend_data);
    {
      connection_t *conn;

      while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP,
                       AP_CONN_STATE_CIRCUIT_WAIT,
                       introcirc->rend_data->onion_address))) {
        conn->state = AP_CONN_STATE_RENDDESC_WAIT;
      }
    }

    status = -1;
    goto cleanup;
  }

  /* first 20 bytes of payload are the hash of Bob's pk */
  intro_key = NULL;
  SMARTLIST_FOREACH(entry->parsed->intro_nodes, rend_intro_point_t *,
                    intro, {
    if (tor_memeq(introcirc->build_state->chosen_exit->identity_digest,
                intro->extend_info->identity_digest, DIGEST_LEN)) {
      intro_key = intro->intro_key;
      break;
    }
  });
Ejemplo n.º 11
0
/** Return the dir_server_t for the directory authority whose identity
 * key hashes to <b>digest</b>, or NULL if no such authority is known.
 */
dir_server_t *
router_get_trusteddirserver_by_digest(const char *digest)
{
  if (!trusted_dir_servers)
    return NULL;

  SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ds,
     {
       if (tor_memeq(ds->digest, digest, DIGEST_LEN))
         return ds;
     });
Ejemplo n.º 12
0
/** Given a received RENDEZVOUS2 MAC in <b>mac</b> (of length DIGEST256_LEN),
 *  and the RENDEZVOUS1 key material in <b>hs_ntor_rend_cell_keys</b>, return 1
 *  if the MAC is good, otherwise return 0. */
int
hs_ntor_client_rendezvous2_mac_is_good(
                        const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys,
                        const uint8_t *rcvd_mac)
{
  tor_assert(rcvd_mac);
  tor_assert(hs_ntor_rend_cell_keys);

  return tor_memeq(hs_ntor_rend_cell_keys->rend_cell_auth_mac,
                   rcvd_mac, DIGEST256_LEN);
}
Ejemplo n.º 13
0
/** Log, at severity <b>severity</b>, information about each circuit
 * that is connected to <b>conn</b>.
 */
void
circuit_dump_by_conn(connection_t *conn, int severity)
{
  circuit_t *circ;
  edge_connection_t *tmpconn;

  for (circ=global_circuitlist;circ;circ = circ->next) {
    circid_t n_circ_id = circ->n_circ_id, p_circ_id = 0;
    if (circ->marked_for_close)
      continue;

    if (! CIRCUIT_IS_ORIGIN(circ))
      p_circ_id = TO_OR_CIRCUIT(circ)->p_circ_id;

    if (! CIRCUIT_IS_ORIGIN(circ) && TO_OR_CIRCUIT(circ)->p_conn &&
        TO_CONN(TO_OR_CIRCUIT(circ)->p_conn) == conn)
      circuit_dump_details(severity, circ, conn->conn_array_index, "App-ward",
                           p_circ_id, n_circ_id);
    if (CIRCUIT_IS_ORIGIN(circ)) {
      for (tmpconn=TO_ORIGIN_CIRCUIT(circ)->p_streams; tmpconn;
           tmpconn=tmpconn->next_stream) {
        if (TO_CONN(tmpconn) == conn) {
          circuit_dump_details(severity, circ, conn->conn_array_index,
                               "App-ward", p_circ_id, n_circ_id);
        }
      }
    }
    if (circ->n_conn && TO_CONN(circ->n_conn) == conn)
      circuit_dump_details(severity, circ, conn->conn_array_index, "Exit-ward",
                           n_circ_id, p_circ_id);
    if (! CIRCUIT_IS_ORIGIN(circ)) {
      for (tmpconn=TO_OR_CIRCUIT(circ)->n_streams; tmpconn;
           tmpconn=tmpconn->next_stream) {
        if (TO_CONN(tmpconn) == conn) {
          circuit_dump_details(severity, circ, conn->conn_array_index,
                               "Exit-ward", n_circ_id, p_circ_id);
        }
      }
    }
    if (!circ->n_conn && circ->n_hop &&
        tor_addr_eq(&circ->n_hop->addr, &conn->addr) &&
        circ->n_hop->port == conn->port &&
        conn->type == CONN_TYPE_OR &&
        tor_memeq(TO_OR_CONN(conn)->identity_digest,
                circ->n_hop->identity_digest, DIGEST_LEN)) {
      circuit_dump_details(severity, circ, conn->conn_array_index,
                           (circ->state == CIRCUIT_STATE_OPEN &&
                            !CIRCUIT_IS_ORIGIN(circ)) ?
                             "Endpoint" : "Pending",
                           n_circ_id, p_circ_id);
    }
  }
}
Ejemplo n.º 14
0
/** Return the first OR circuit in the global list whose purpose is
 * <b>purpose</b>, and whose rend_token is the <b>len</b>-byte
 * <b>token</b>. */
static or_circuit_t *
circuit_get_by_rend_token_and_purpose(uint8_t purpose, const char *token,
                                      size_t len)
{
  circuit_t *circ;
  for (circ = global_circuitlist; circ; circ = circ->next) {
    if (! circ->marked_for_close &&
        circ->purpose == purpose &&
        tor_memeq(TO_OR_CIRCUIT(circ)->rend_token, token, len))
      return TO_OR_CIRCUIT(circ);
  }
  return NULL;
}
Ejemplo n.º 15
0
/**
 * Helper: implements keypin_check and keypin_check_and_add.
 */
static int
keypin_check_and_add_impl(const uint8_t *rsa_id_digest,
                          const uint8_t *ed25519_id_key,
                          const int do_not_add,
                          const int replace)
{
  keypin_ent_t search, *ent;
  memset(&search, 0, sizeof(search));
  memcpy(search.rsa_id, rsa_id_digest, sizeof(search.rsa_id));
  memcpy(search.ed25519_key, ed25519_id_key, sizeof(search.ed25519_key));

  /* Search by RSA key digest first */
  ent = HT_FIND(rsamap, &the_rsa_map, &search);
  if (ent) {
    tor_assert(fast_memeq(ent->rsa_id, rsa_id_digest, sizeof(ent->rsa_id)));
    if (tor_memeq(ent->ed25519_key, ed25519_id_key,sizeof(ent->ed25519_key))) {
      return KEYPIN_FOUND; /* Match on both keys. Great. */
    } else {
      if (!replace)
        return KEYPIN_MISMATCH; /* Found RSA with different Ed key */
    }
  }

  /* See if we know a different RSA key for this ed key */
  if (! replace) {
    ent = HT_FIND(edmap, &the_ed_map, &search);
    if (ent) {
      /* If we got here, then the ed key matches and the RSA doesn't */
      tor_assert(fast_memeq(ent->ed25519_key, ed25519_id_key,
                            sizeof(ent->ed25519_key)));
      tor_assert(fast_memneq(ent->rsa_id, rsa_id_digest, sizeof(ent->rsa_id)));
      return KEYPIN_MISMATCH;
    }
  }

  /* Okay, this one is new to us. */
  if (do_not_add)
    return KEYPIN_NOT_FOUND;

  ent = tor_memdup(&search, sizeof(search));
  int r = keypin_add_or_replace_entry_in_map(ent);
  if (! replace) {
    tor_assert(r == 1);
  } else {
    tor_assert(r != 0);
  }
  keypin_journal_append_entry(rsa_id_digest, ed25519_id_key);
  return KEYPIN_ADDED;
}
Ejemplo n.º 16
0
/** Search the map at <b>map</b> for an entry whose key is <b>key</b> (a
 * DIGEST256_LEN-byte key) returning the corresponding value if we found one,
 * and returning <b>dflt_val</b> if the key wasn't found.
 *
 * This operation takes an amount of time dependent only on the length of
 * <b>map</b>, not on the position or presence of <b>key</b> within <b>map</b>.
 */
void *
dimap_search(const di_digest256_map_t *map, const uint8_t *key,
             void *dflt_val)
{
  uintptr_t result = (uintptr_t)dflt_val;

  while (map) {
    uintptr_t r = (uintptr_t) tor_memeq(map->key, key, 32);
    r -= 1; /* Now r is (uintptr_t)-1 if memeq returned false, and
             * 0 if memeq returned true. */

    result &= r;
    result |= ((uintptr_t)(map->val)) & ~r;

    map = map->next;
  }

  return (void *)result;
}
Ejemplo n.º 17
0
/** Return a circ such that
 *  - circ-\>rend_data-\>onion_address is equal to
 *    <b>rend_data</b>-\>onion_address,
 *  - circ-\>rend_data-\>rend_cookie is equal to
 *    <b>rend_data</b>-\>rend_cookie, and
 *  - circ-\>purpose is equal to CIRCUIT_PURPOSE_C_REND_READY.
 *
 * Return NULL if no such circuit exists.
 */
origin_circuit_t *
circuit_get_ready_rend_circ_by_rend_data(const rend_data_t *rend_data)
{
  circuit_t *circ;

  for (circ = global_circuitlist; circ; circ = circ->next) {
    if (!circ->marked_for_close &&
        circ->purpose == CIRCUIT_PURPOSE_C_REND_READY) {
      origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
      if (ocirc->rend_data &&
          !rend_cmp_service_ids(rend_data->onion_address,
                                ocirc->rend_data->onion_address) &&
          tor_memeq(ocirc->rend_data->rend_cookie,
                    rend_data->rend_cookie,
                    REND_COOKIE_LEN))
        return ocirc;
    }
  }
  return NULL;
}
Ejemplo n.º 18
0
/** Run unit tests for fp_pair-to-void* map functions */
static void
test_container_fp_pair_map(void)
{
  fp_pair_map_t *map;
  fp_pair_t fp1, fp2, fp3, fp4, fp5, fp6;
  void *v;
  fp_pair_map_iter_t *iter;
  fp_pair_t k;

  map = fp_pair_map_new();
  test_assert(map);
  test_eq(fp_pair_map_size(map), 0);
  test_assert(fp_pair_map_isempty(map));

  memset(fp1.first, 0x11, DIGEST_LEN);
  memset(fp1.second, 0x12, DIGEST_LEN);
  memset(fp2.first, 0x21, DIGEST_LEN);
  memset(fp2.second, 0x22, DIGEST_LEN);
  memset(fp3.first, 0x31, DIGEST_LEN);
  memset(fp3.second, 0x32, DIGEST_LEN);
  memset(fp4.first, 0x41, DIGEST_LEN);
  memset(fp4.second, 0x42, DIGEST_LEN);
  memset(fp5.first, 0x51, DIGEST_LEN);
  memset(fp5.second, 0x52, DIGEST_LEN);
  memset(fp6.first, 0x61, DIGEST_LEN);
  memset(fp6.second, 0x62, DIGEST_LEN);

  v = fp_pair_map_set(map, &fp1, (void*)99);
  test_eq(v, NULL);
  test_assert(!fp_pair_map_isempty(map));
  v = fp_pair_map_set(map, &fp2, (void*)101);
  test_eq(v, NULL);
  v = fp_pair_map_set(map, &fp1, (void*)100);
  test_eq(v, (void*)99);
  test_eq_ptr(fp_pair_map_get(map, &fp1), (void*)100);
  test_eq_ptr(fp_pair_map_get(map, &fp2), (void*)101);
  test_eq_ptr(fp_pair_map_get(map, &fp3), NULL);
  fp_pair_map_assert_ok(map);

  v = fp_pair_map_remove(map, &fp2);
  fp_pair_map_assert_ok(map);
  test_eq_ptr(v, (void*)101);
  test_eq_ptr(fp_pair_map_get(map, &fp2), NULL);
  test_eq_ptr(fp_pair_map_remove(map, &fp2), NULL);

  fp_pair_map_set(map, &fp2, (void*)101);
  fp_pair_map_set(map, &fp3, (void*)102);
  fp_pair_map_set(map, &fp4, (void*)103);
  test_eq(fp_pair_map_size(map), 4);
  fp_pair_map_assert_ok(map);
  fp_pair_map_set(map, &fp5, (void*)104);
  fp_pair_map_set(map, &fp6, (void*)105);
  fp_pair_map_assert_ok(map);

  /* Test iterator. */
  iter = fp_pair_map_iter_init(map);
  while (!fp_pair_map_iter_done(iter)) {
    fp_pair_map_iter_get(iter, &k, &v);
    test_eq_ptr(v, fp_pair_map_get(map, &k));

    if (tor_memeq(&fp2, &k, sizeof(fp2))) {
      iter = fp_pair_map_iter_next_rmv(map, iter);
    } else {
      iter = fp_pair_map_iter_next(map, iter);
    }
  }

  /* Make sure we removed fp2, but not the others. */
  test_eq_ptr(fp_pair_map_get(map, &fp2), NULL);
  test_eq_ptr(fp_pair_map_get(map, &fp5), (void*)104);

  fp_pair_map_assert_ok(map);
  /* Clean up after ourselves. */
  fp_pair_map_free(map, NULL);
  map = NULL;

 done:
  if (map)
    fp_pair_map_free(map, NULL);
}
Ejemplo n.º 19
0
/** Helper: compares <b>a</b> and </b> for equality for hash-table purposes. */
static INLINE int
microdesc_eq_(microdesc_t *a, microdesc_t *b)
{
  return tor_memeq(a->digest, b->digest, DIGEST256_LEN);
}
Ejemplo n.º 20
0
/** Hashtable helper: compare two keypin table entries and return true iff
 * they have the same ed25519 keys */
static inline int
keypin_ents_eq_ed(const keypin_ent_t *a, const keypin_ent_t *b)
{
  return tor_memeq(a->ed25519_key, b->ed25519_key, sizeof(a->ed25519_key));
}
Ejemplo n.º 21
0
/** Hashtable helper: compare two keypin table entries and return true iff
 * they have the same RSA key IDs. */
static inline int
keypin_ents_eq_rsa(const keypin_ent_t *a, const keypin_ent_t *b)
{
  return tor_memeq(a->rsa_id, b->rsa_id, sizeof(a->rsa_id));
}
Ejemplo n.º 22
0
/** Return a circuit that is open, is CIRCUIT_PURPOSE_C_GENERAL,
 * has a timestamp_dirty value of 0, has flags matching the CIRCLAUNCH_*
 * flags in <b>flags</b>, and if info is defined, does not already use info
 * as any of its hops; or NULL if no circuit fits this description.
 *
 * The <b>purpose</b> argument (currently ignored) refers to the purpose of
 * the circuit we want to create, not the purpose of the circuit we want to
 * cannibalize.
 *
 * If !CIRCLAUNCH_NEED_UPTIME, prefer returning non-uptime circuits.
 */
origin_circuit_t *
circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
                            int flags)
{
  circuit_t *_circ;
  origin_circuit_t *best=NULL;
  int need_uptime = (flags & CIRCLAUNCH_NEED_UPTIME) != 0;
  int need_capacity = (flags & CIRCLAUNCH_NEED_CAPACITY) != 0;
  int internal = (flags & CIRCLAUNCH_IS_INTERNAL) != 0;
  const or_options_t *options = get_options();

  /* Make sure we're not trying to create a onehop circ by
   * cannibalization. */
  tor_assert(!(flags & CIRCLAUNCH_ONEHOP_TUNNEL));

  log_debug(LD_CIRC,
            "Hunting for a circ to cannibalize: purpose %d, uptime %d, "
            "capacity %d, internal %d",
            purpose, need_uptime, need_capacity, internal);

  for (_circ=global_circuitlist; _circ; _circ = _circ->next) {
    if (CIRCUIT_IS_ORIGIN(_circ) &&
        _circ->state == CIRCUIT_STATE_OPEN &&
        !_circ->marked_for_close &&
        _circ->purpose == CIRCUIT_PURPOSE_C_GENERAL &&
        !_circ->timestamp_dirty) {
      origin_circuit_t *circ = TO_ORIGIN_CIRCUIT(_circ);
      if ((!need_uptime || circ->build_state->need_uptime) &&
          (!need_capacity || circ->build_state->need_capacity) &&
          (internal == circ->build_state->is_internal) &&
          circ->remaining_relay_early_cells &&
          circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN &&
          !circ->build_state->onehop_tunnel &&
          !circ->isolation_values_set) {
        if (info) {
          /* need to make sure we don't duplicate hops */
          crypt_path_t *hop = circ->cpath;
          const node_t *ri1 = node_get_by_id(info->identity_digest);
          do {
            const node_t *ri2;
            if (tor_memeq(hop->extend_info->identity_digest,
                        info->identity_digest, DIGEST_LEN))
              goto next;
            if (ri1 &&
                (ri2 = node_get_by_id(hop->extend_info->identity_digest))
                && nodes_in_same_family(ri1, ri2))
              goto next;
            hop=hop->next;
          } while (hop!=circ->cpath);
        }
        if (options->ExcludeNodes) {
          /* Make sure no existing nodes in the circuit are excluded for
           * general use.  (This may be possible if StrictNodes is 0, and we
           * thought we needed to use an otherwise excluded node for, say, a
           * directory operation.) */
          crypt_path_t *hop = circ->cpath;
          do {
            if (routerset_contains_extendinfo(options->ExcludeNodes,
                                              hop->extend_info))
              goto next;
            hop = hop->next;
          } while (hop != circ->cpath);
        }
        if (!best || (best->build_state->need_uptime && !need_uptime))
          best = circ;
      next: ;
      }
    }
  }
  return best;
}
Ejemplo n.º 23
0
/** Called when we get an AUTHENTICATE message.  Check whether the
 * authentication is valid, and if so, update the connection's state to
 * OPEN.  Reply with DONE or ERROR.
 */
int
handle_control_authenticate(control_connection_t *conn, uint32_t len,
                            const char *body)
{
  int used_quoted_string = 0;
  const or_options_t *options = get_options();
  const char *errstr = "Unknown error";
  char *password;
  size_t password_len;
  const char *cp;
  int i;
  int bad_cookie=0, bad_password=0;
  smartlist_t *sl = NULL;

  if (!len) {
    password = tor_strdup("");
    password_len = 0;
  } else if (TOR_ISXDIGIT(body[0])) {
    cp = body;
    while (TOR_ISXDIGIT(*cp))
      ++cp;
    i = (int)(cp - body);
    tor_assert(i>0);
    password_len = i/2;
    password = tor_malloc(password_len + 1);
    if (base16_decode(password, password_len+1, body, i)
                      != (int) password_len) {
      connection_write_str_to_buf(
            "551 Invalid hexadecimal encoding.  Maybe you tried a plain text "
            "password?  If so, the standard requires that you put it in "
            "double quotes.\r\n", conn);
      connection_mark_for_close(TO_CONN(conn));
      tor_free(password);
      return 0;
    }
  } else {
    if (!decode_escaped_string(body, len, &password, &password_len)) {
      connection_write_str_to_buf("551 Invalid quoted string.  You need "
            "to put the password in double quotes.\r\n", conn);
      connection_mark_for_close(TO_CONN(conn));
      return 0;
    }
    used_quoted_string = 1;
  }

  if (conn->safecookie_client_hash != NULL) {
    /* The controller has chosen safe cookie authentication; the only
     * acceptable authentication value is the controller-to-server
     * response. */

    tor_assert(authentication_cookie_is_set);

    if (password_len != DIGEST256_LEN) {
      log_warn(LD_CONTROL,
               "Got safe cookie authentication response with wrong length "
               "(%d)", (int)password_len);
      errstr = "Wrong length for safe cookie response.";
      goto err;
    }

    if (tor_memneq(conn->safecookie_client_hash, password, DIGEST256_LEN)) {
      log_warn(LD_CONTROL,
               "Got incorrect safe cookie authentication response");
      errstr = "Safe cookie response did not match expected value.";
      goto err;
    }

    tor_free(conn->safecookie_client_hash);
    goto ok;
  }

  if (!options->CookieAuthentication && !options->HashedControlPassword &&
      !options->HashedControlSessionPassword) {
    /* if Tor doesn't demand any stronger authentication, then
     * the controller can get in with anything. */
    goto ok;
  }

  if (options->CookieAuthentication) {
    int also_password = options->HashedControlPassword != NULL ||
      options->HashedControlSessionPassword != NULL;
    if (password_len != AUTHENTICATION_COOKIE_LEN) {
      if (!also_password) {
        log_warn(LD_CONTROL, "Got authentication cookie with wrong length "
                 "(%d)", (int)password_len);
        errstr = "Wrong length on authentication cookie.";
        goto err;
      }
      bad_cookie = 1;
    } else if (tor_memneq(authentication_cookie, password, password_len)) {
      if (!also_password) {
        log_warn(LD_CONTROL, "Got mismatched authentication cookie");
        errstr = "Authentication cookie did not match expected value.";
        goto err;
      }
      bad_cookie = 1;
    } else {
      goto ok;
    }
  }

  if (options->HashedControlPassword ||
      options->HashedControlSessionPassword) {
    int bad = 0;
    smartlist_t *sl_tmp;
    char received[DIGEST_LEN];
    int also_cookie = options->CookieAuthentication;
    sl = smartlist_new();
    if (options->HashedControlPassword) {
      sl_tmp = decode_hashed_passwords(options->HashedControlPassword);
      if (!sl_tmp)
        bad = 1;
      else {
        smartlist_add_all(sl, sl_tmp);
        smartlist_free(sl_tmp);
      }
    }
    if (options->HashedControlSessionPassword) {
      sl_tmp = decode_hashed_passwords(options->HashedControlSessionPassword);
      if (!sl_tmp)
        bad = 1;
      else {
        smartlist_add_all(sl, sl_tmp);
        smartlist_free(sl_tmp);
      }
    }
    if (bad) {
      if (!also_cookie) {
        log_warn(LD_BUG,
                 "Couldn't decode HashedControlPassword: invalid base16");
        errstr="Couldn't decode HashedControlPassword value in configuration.";
        goto err;
      }
      bad_password = 1;
      SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
      smartlist_free(sl);
      sl = NULL;
    } else {
      SMARTLIST_FOREACH(sl, char *, expected,
      {
        secret_to_key_rfc2440(received,DIGEST_LEN,
                              password,password_len,expected);
        if (tor_memeq(expected + S2K_RFC2440_SPECIFIER_LEN,
                      received, DIGEST_LEN))
          goto ok;
      });
Ejemplo n.º 24
0
/**
 * Running as a server: load, reload, or refresh our ed25519 keys and
 * certificates, creating and saving new ones as needed.
 *
 * Return -1 on failure; 0 on success if the signing key was not replaced;
 * and 1 on success if the signing key was replaced.
 */
int
load_ed_keys(const or_options_t *options, time_t now)
{
  ed25519_keypair_t *id = NULL;
  ed25519_keypair_t *sign = NULL;
  ed25519_keypair_t *auth = NULL;
  const ed25519_keypair_t *sign_signing_key_with_id = NULL;
  const ed25519_keypair_t *use_signing = NULL;
  const tor_cert_t *check_signing_cert = NULL;
  tor_cert_t *sign_cert = NULL;
  tor_cert_t *auth_cert = NULL;
  int signing_key_changed = 0;

  // It is later than 1972, since otherwise there would be no C compilers.
  // (Try to diagnose #22466.)
  tor_assert_nonfatal(now >= 2 * 365 * 86400);

#define FAIL(msg) do {                          \
    log_warn(LD_OR, (msg));                     \
    goto err;                                   \
  } while (0)
#define SET_KEY(key, newval) do {               \
    if ((key) != (newval))                      \
      ed25519_keypair_free(key);                \
    key = (newval);                             \
  } while (0)
#define SET_CERT(cert, newval) do {             \
    if ((cert) != (newval))                     \
      tor_cert_free(cert);                      \
    cert = (newval);                            \
  } while (0)
#define HAPPENS_SOON(when, interval)            \
  ((when) < now + (interval))
#define EXPIRES_SOON(cert, interval)            \
  (!(cert) || HAPPENS_SOON((cert)->valid_until, (interval)))

  /* XXXX support encrypted identity keys fully */

  /* First try to get the signing key to see how it is. */
  {
    char *fname =
      options_get_keydir_fname(options, "ed25519_signing");
    sign = ed_key_init_from_file(
               fname,
               INIT_ED_KEY_NEEDCERT|
               INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT,
               LOG_INFO,
               NULL, 0, 0, CERT_TYPE_ID_SIGNING, &sign_cert);
    tor_free(fname);
    check_signing_cert = sign_cert;
    use_signing = sign;
  }

  if (use_signing) {
    /* We loaded a signing key with its certificate.  */
    if (! master_signing_key) {
      /* We didn't know one before! */
      signing_key_changed = 1;
    } else if (! ed25519_pubkey_eq(&use_signing->pubkey,
                                   &master_signing_key->pubkey) ||
               ! tor_memeq(use_signing->seckey.seckey,
                           master_signing_key->seckey.seckey,
                           ED25519_SECKEY_LEN)) {
      /* We loaded a different signing key than the one we knew before. */
      signing_key_changed = 1;
    }
  }

  if (!use_signing && master_signing_key) {
    /* We couldn't load a signing key, but we already had one loaded */
    check_signing_cert = signing_key_cert;
    use_signing = master_signing_key;
  }

  const int offline_master =
    options->OfflineMasterKey && options->command != CMD_KEYGEN;
  const int need_new_signing_key =
    NULL == use_signing ||
    EXPIRES_SOON(check_signing_cert, 0) ||
    (options->command == CMD_KEYGEN && ! options->change_key_passphrase);
  const int want_new_signing_key =
    need_new_signing_key ||
    EXPIRES_SOON(check_signing_cert, options->TestingSigningKeySlop);

  /* We can only create a master key if we haven't been told that the
   * master key will always be offline.  Also, if we have a signing key,
   * then we shouldn't make a new master ID key. */
  const int can_make_master_id_key = !offline_master &&
    NULL == use_signing;

  if (need_new_signing_key) {
    log_notice(LD_OR, "It looks like I need to generate and sign a new "
               "medium-term signing key, because %s. To do that, I "
               "need to load%s the permanent master identity key. "
               "If the master identity key was not moved or encrypted "
               "with a passphrase, this will be done automatically and "
               "no further action is required. Otherwise, provide the "
               "necessary data using 'tor --keygen' to do it manually.",
            (NULL == use_signing) ? "I don't have one" :
            EXPIRES_SOON(check_signing_cert, 0) ? "the one I have is expired" :
               "you asked me to make one with --keygen",
            can_make_master_id_key ? " (or create)" : "");
  } else if (want_new_signing_key && !offline_master) {
    log_notice(LD_OR, "It looks like I should try to generate and sign a "
               "new medium-term signing key, because the one I have is "
               "going to expire soon. To do that, I'm going to have to "
               "try to load the permanent master identity key. "
               "If the master identity key was not moved or encrypted "
               "with a passphrase, this will be done automatically and "
               "no further action is required. Otherwise, provide the "
               "necessary data using 'tor --keygen' to do it manually.");
  } else if (want_new_signing_key) {
    log_notice(LD_OR, "It looks like I should try to generate and sign a "
               "new medium-term signing key, because the one I have is "
               "going to expire soon. But OfflineMasterKey is set, so I "
               "won't try to load a permanent master identity key. You "
               "will need to use 'tor --keygen' to make a new signing "
               "key and certificate.");
  }

  {
    uint32_t flags =
      (INIT_ED_KEY_SPLIT|
       INIT_ED_KEY_EXTRA_STRONG|INIT_ED_KEY_NO_REPAIR);
    if (can_make_master_id_key)
      flags |= INIT_ED_KEY_CREATE;
    if (! need_new_signing_key)
      flags |= INIT_ED_KEY_MISSING_SECRET_OK;
    if (! want_new_signing_key || offline_master)
      flags |= INIT_ED_KEY_OMIT_SECRET;
    if (offline_master)
      flags |= INIT_ED_KEY_OFFLINE_SECRET;
    if (options->command == CMD_KEYGEN)
      flags |= INIT_ED_KEY_TRY_ENCRYPTED;

    /* Check/Create the key directory */
    if (create_keys_directory(options) < 0)
      return -1;

    char *fname;
    if (options->master_key_fname) {
      fname = tor_strdup(options->master_key_fname);
      flags |= INIT_ED_KEY_EXPLICIT_FNAME;
    } else {
      fname = options_get_keydir_fname(options, "ed25519_master_id");
    }
    id = ed_key_init_from_file(
             fname,
             flags,
             LOG_WARN, NULL, 0, 0, 0, NULL);
    tor_free(fname);
    if (!id) {
      if (need_new_signing_key) {
        if (offline_master)
          FAIL("Can't load master identity key; OfflineMasterKey is set.");
        else
          FAIL("Missing identity key");
      } else {
        log_warn(LD_OR, "Master public key was absent; inferring from "
                 "public key in signing certificate and saving to disk.");
        tor_assert(check_signing_cert);
        id = tor_malloc_zero(sizeof(*id));
        memcpy(&id->pubkey, &check_signing_cert->signing_key,
               sizeof(ed25519_public_key_t));
        fname = options_get_keydir_fname(options,
                                         "ed25519_master_id_public_key");
        if (ed25519_pubkey_write_to_file(&id->pubkey, fname, "type0") < 0) {
          log_warn(LD_OR, "Error while attempting to write master public key "
                   "to disk");
          tor_free(fname);
          goto err;
        }
        tor_free(fname);
      }
    }
    if (tor_mem_is_zero((char*)id->seckey.seckey, sizeof(id->seckey)))
      sign_signing_key_with_id = NULL;
    else
      sign_signing_key_with_id = id;
  }

  if (master_identity_key &&
      !ed25519_pubkey_eq(&id->pubkey, &master_identity_key->pubkey)) {
    FAIL("Identity key on disk does not match key we loaded earlier!");
  }

  if (need_new_signing_key && NULL == sign_signing_key_with_id)
    FAIL("Can't load master key make a new signing key.");

  if (sign_cert) {
    if (! sign_cert->signing_key_included)
      FAIL("Loaded a signing cert with no key included!");
    if (! ed25519_pubkey_eq(&sign_cert->signing_key, &id->pubkey))
      FAIL("The signing cert we have was not signed with the master key "
           "we loaded!");
    if (tor_cert_checksig(sign_cert, &id->pubkey, 0) < 0) {
      log_warn(LD_OR, "The signing cert we loaded was not signed "
               "correctly: %s!",
               tor_cert_describe_signature_status(sign_cert));
      goto err;
    }
  }

  if (want_new_signing_key && sign_signing_key_with_id) {
    uint32_t flags = (INIT_ED_KEY_CREATE|
                      INIT_ED_KEY_REPLACE|
                      INIT_ED_KEY_EXTRA_STRONG|
                      INIT_ED_KEY_NEEDCERT|
                      INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT);
    char *fname =
      options_get_keydir_fname(options, "ed25519_signing");
    ed25519_keypair_free(sign);
    tor_cert_free(sign_cert);
    sign = ed_key_init_from_file(fname,
                                 flags, LOG_WARN,
                                 sign_signing_key_with_id, now,
                                 options->SigningKeyLifetime,
                                 CERT_TYPE_ID_SIGNING, &sign_cert);
    tor_free(fname);
    if (!sign)
      FAIL("Missing signing key");
    use_signing = sign;
    signing_key_changed = 1;

    tor_assert(sign_cert->signing_key_included);
    tor_assert(ed25519_pubkey_eq(&sign_cert->signing_key, &id->pubkey));
    tor_assert(ed25519_pubkey_eq(&sign_cert->signed_key, &sign->pubkey));
  } else if (want_new_signing_key) {
    static ratelim_t missing_master = RATELIM_INIT(3600);
    log_fn_ratelim(&missing_master, LOG_WARN, LD_OR,
                   "Signing key will expire soon, but I can't load the "
                   "master key to sign a new one!");
  }

  tor_assert(use_signing);

  /* At this point we no longer need our secret identity key.  So wipe
   * it, if we loaded it in the first place. */
  memwipe(id->seckey.seckey, 0, sizeof(id->seckey));

  if (options->command == CMD_KEYGEN)
    goto end;

  if (server_mode(options) &&
      (!rsa_ed_crosscert ||
       HAPPENS_SOON(rsa_ed_crosscert_expiration, 30*86400))) {
    uint8_t *crosscert;
    time_t expiration = now+6*30*86400; /* 6 months in the future. */
    ssize_t crosscert_len = tor_make_rsa_ed25519_crosscert(&id->pubkey,
                                                   get_server_identity_key(),
                                                   expiration,
                                                   &crosscert);
    tor_free(rsa_ed_crosscert);
    rsa_ed_crosscert_len = crosscert_len;
    rsa_ed_crosscert = crosscert;
    rsa_ed_crosscert_expiration = expiration;
  }

  if (!current_auth_key ||
      signing_key_changed ||
      EXPIRES_SOON(auth_key_cert, options->TestingAuthKeySlop)) {
    auth = ed_key_new(use_signing, INIT_ED_KEY_NEEDCERT,
                      now,
                      options->TestingAuthKeyLifetime,
                      CERT_TYPE_SIGNING_AUTH, &auth_cert);

    if (!auth)
      FAIL("Can't create auth key");
  }

  /* We've generated or loaded everything.  Put them in memory. */

 end:
  if (! master_identity_key) {
    SET_KEY(master_identity_key, id);
  } else {
    tor_free(id);
  }
  if (sign) {
    SET_KEY(master_signing_key, sign);
    SET_CERT(signing_key_cert, sign_cert);
  }
  if (auth) {
    SET_KEY(current_auth_key, auth);
    SET_CERT(auth_key_cert, auth_cert);
  }

  return signing_key_changed;
 err:
  ed25519_keypair_free(id);
  ed25519_keypair_free(sign);
  ed25519_keypair_free(auth);
  tor_cert_free(sign_cert);
  tor_cert_free(auth_cert);
  return -1;
}
Ejemplo n.º 25
0
/** Run unit tests for fp_pair-to-void* map functions */
static void
test_container_fp_pair_map(void *arg)
{
  fp_pair_map_t *map;
  fp_pair_t fp1, fp2, fp3, fp4, fp5, fp6;
  void *v;
  fp_pair_map_iter_t *iter;
  fp_pair_t k;
  char *v99 = tor_strdup("99");
  char *v100 = tor_strdup("v100");
  char *v101 = tor_strdup("v101");
  char *v102 = tor_strdup("v102");
  char *v103 = tor_strdup("v103");
  char *v104 = tor_strdup("v104");
  char *v105 = tor_strdup("v105");

  (void)arg;
  map = fp_pair_map_new();
  tt_assert(map);
  tt_int_op(fp_pair_map_size(map),OP_EQ, 0);
  tt_assert(fp_pair_map_isempty(map));

  memset(fp1.first, 0x11, DIGEST_LEN);
  memset(fp1.second, 0x12, DIGEST_LEN);
  memset(fp2.first, 0x21, DIGEST_LEN);
  memset(fp2.second, 0x22, DIGEST_LEN);
  memset(fp3.first, 0x31, DIGEST_LEN);
  memset(fp3.second, 0x32, DIGEST_LEN);
  memset(fp4.first, 0x41, DIGEST_LEN);
  memset(fp4.second, 0x42, DIGEST_LEN);
  memset(fp5.first, 0x51, DIGEST_LEN);
  memset(fp5.second, 0x52, DIGEST_LEN);
  memset(fp6.first, 0x61, DIGEST_LEN);
  memset(fp6.second, 0x62, DIGEST_LEN);

  v = fp_pair_map_set(map, &fp1, v99);
  tt_ptr_op(v, OP_EQ, NULL);
  tt_assert(!fp_pair_map_isempty(map));
  v = fp_pair_map_set(map, &fp2, v101);
  tt_ptr_op(v, OP_EQ, NULL);
  v = fp_pair_map_set(map, &fp1, v100);
  tt_ptr_op(v, OP_EQ, v99);
  tt_ptr_op(fp_pair_map_get(map, &fp1),OP_EQ, v100);
  tt_ptr_op(fp_pair_map_get(map, &fp2),OP_EQ, v101);
  tt_ptr_op(fp_pair_map_get(map, &fp3),OP_EQ, NULL);
  fp_pair_map_assert_ok(map);

  v = fp_pair_map_remove(map, &fp2);
  fp_pair_map_assert_ok(map);
  tt_ptr_op(v,OP_EQ, v101);
  tt_ptr_op(fp_pair_map_get(map, &fp2),OP_EQ, NULL);
  tt_ptr_op(fp_pair_map_remove(map, &fp2),OP_EQ, NULL);

  fp_pair_map_set(map, &fp2, v101);
  fp_pair_map_set(map, &fp3, v102);
  fp_pair_map_set(map, &fp4, v103);
  tt_int_op(fp_pair_map_size(map),OP_EQ, 4);
  fp_pair_map_assert_ok(map);
  fp_pair_map_set(map, &fp5, v104);
  fp_pair_map_set(map, &fp6, v105);
  fp_pair_map_assert_ok(map);

  /* Test iterator. */
  iter = fp_pair_map_iter_init(map);
  while (!fp_pair_map_iter_done(iter)) {
    fp_pair_map_iter_get(iter, &k, &v);
    tt_ptr_op(v,OP_EQ, fp_pair_map_get(map, &k));

    if (tor_memeq(&fp2, &k, sizeof(fp2))) {
      iter = fp_pair_map_iter_next_rmv(map, iter);
    } else {
      iter = fp_pair_map_iter_next(map, iter);
    }
  }

  /* Make sure we removed fp2, but not the others. */
  tt_ptr_op(fp_pair_map_get(map, &fp2),OP_EQ, NULL);
  tt_ptr_op(fp_pair_map_get(map, &fp5),OP_EQ, v104);

  fp_pair_map_assert_ok(map);
  /* Clean up after ourselves. */
  fp_pair_map_free(map, NULL);
  map = NULL;

 done:
  if (map)
    fp_pair_map_free(map, NULL);
  tor_free(v99);
  tor_free(v100);
  tor_free(v101);
  tor_free(v102);
  tor_free(v103);
  tor_free(v104);
  tor_free(v105);
}
Ejemplo n.º 26
0
Archivo: onion.c Proyecto: BwRy/Astoria
/** Parse an EXTEND or EXTEND2 cell (according to <b>command</b>) from the
 * <b>payload_length</b> bytes of <b>payload</b> into <b>cell_out</b>. Return
 * 0 on success, -1 on failure. */
int
extend_cell_parse(extend_cell_t *cell_out, const uint8_t command,
                  const uint8_t *payload, size_t payload_length)
{
  const uint8_t *eop;

  memset(cell_out, 0, sizeof(*cell_out));
  if (payload_length > RELAY_PAYLOAD_SIZE)
    return -1;
  eop = payload + payload_length;

  switch (command) {
  case RELAY_COMMAND_EXTEND:
    {
      if (payload_length != 6 + TAP_ONIONSKIN_CHALLENGE_LEN + DIGEST_LEN)
        return -1;

      cell_out->cell_type = RELAY_COMMAND_EXTEND;
      tor_addr_from_ipv4n(&cell_out->orport_ipv4.addr, get_uint32(payload));
      cell_out->orport_ipv4.port = ntohs(get_uint16(payload+4));
      tor_addr_make_unspec(&cell_out->orport_ipv6.addr);
      if (tor_memeq(payload + 6, NTOR_CREATE_MAGIC, 16)) {
        cell_out->create_cell.cell_type = CELL_CREATE2;
        cell_out->create_cell.handshake_type = ONION_HANDSHAKE_TYPE_NTOR;
        cell_out->create_cell.handshake_len = NTOR_ONIONSKIN_LEN;
        memcpy(cell_out->create_cell.onionskin, payload + 22,
               NTOR_ONIONSKIN_LEN);
      } else {
        cell_out->create_cell.cell_type = CELL_CREATE;
        cell_out->create_cell.handshake_type = ONION_HANDSHAKE_TYPE_TAP;
        cell_out->create_cell.handshake_len = TAP_ONIONSKIN_CHALLENGE_LEN;
        memcpy(cell_out->create_cell.onionskin, payload + 6,
               TAP_ONIONSKIN_CHALLENGE_LEN);
      }
      memcpy(cell_out->node_id, payload + 6 + TAP_ONIONSKIN_CHALLENGE_LEN,
             DIGEST_LEN);
      break;
    }
  case RELAY_COMMAND_EXTEND2:
    {
      uint8_t n_specs, spectype, speclen;
      int i;
      int found_ipv4 = 0, found_ipv6 = 0, found_id = 0;
      tor_addr_make_unspec(&cell_out->orport_ipv4.addr);
      tor_addr_make_unspec(&cell_out->orport_ipv6.addr);

      if (payload_length == 0)
        return -1;

      cell_out->cell_type = RELAY_COMMAND_EXTEND2;
      n_specs = *payload++;
      /* Parse the specifiers. We'll only take the first IPv4 and first IPv6
       * address, and the node ID, and ignore everything else */
      for (i = 0; i < n_specs; ++i) {
        if (eop - payload < 2)
          return -1;
        spectype = payload[0];
        speclen = payload[1];
        payload += 2;
        if (eop - payload < speclen)
          return -1;
        switch (spectype) {
        case SPECTYPE_IPV4:
          if (speclen != 6)
            return -1;
          if (!found_ipv4) {
            tor_addr_from_ipv4n(&cell_out->orport_ipv4.addr,
                                get_uint32(payload));
            cell_out->orport_ipv4.port = ntohs(get_uint16(payload+4));
            found_ipv4 = 1;
          }
          break;
        case SPECTYPE_IPV6:
          if (speclen != 18)
            return -1;
          if (!found_ipv6) {
            tor_addr_from_ipv6_bytes(&cell_out->orport_ipv6.addr,
                                     (const char*)payload);
            cell_out->orport_ipv6.port = ntohs(get_uint16(payload+16));
            found_ipv6 = 1;
          }
          break;
        case SPECTYPE_LEGACY_ID:
          if (speclen != 20)
            return -1;
          if (found_id)
            return -1;
          memcpy(cell_out->node_id, payload, 20);
          found_id = 1;
          break;
        }
        payload += speclen;
      }
      if (!found_id || !found_ipv4)
        return -1;
      if (parse_create2_payload(&cell_out->create_cell,payload,eop-payload)<0)
        return -1;
      break;
    }
  default:
    return -1;
  }

  return check_extend_cell(cell_out);
}
Ejemplo n.º 27
0
/** Helper: write the router-status information in <b>rs</b> into a newly
 * allocated character buffer.  Use the same format as in network-status
 * documents.  If <b>version</b> is non-NULL, add a "v" line for the platform.
 *
 * consensus_method is the current consensus method when format is
 * NS_V3_CONSENSUS or NS_V3_CONSENSUS_MICRODESC. It is ignored for other
 * formats: pass ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD.
 *
 * Return 0 on success, -1 on failure.
 *
 * The format argument has one of the following values:
 *   NS_V2 - Output an entry suitable for a V2 NS opinion document
 *   NS_V3_CONSENSUS - Output the first portion of a V3 NS consensus entry
 *        for consensus_method.
 *   NS_V3_CONSENSUS_MICRODESC - Output the first portion of a V3 microdesc
 *        consensus entry for consensus_method.
 *   NS_V3_VOTE - Output a complete V3 NS vote. If <b>vrs</b> is present,
 *        it contains additional information for the vote.
 *   NS_CONTROL_PORT - Output a NS document for the control port.
 */
char *
routerstatus_format_entry(const routerstatus_t *rs, const char *version,
                          const char *protocols,
                          routerstatus_format_type_t format,
                          int consensus_method,
                          const vote_routerstatus_t *vrs)
{
  char *summary;
  char *result = NULL;

  char published[ISO_TIME_LEN+1];
  char identity64[BASE64_DIGEST_LEN+1];
  char digest64[BASE64_DIGEST_LEN+1];
  smartlist_t *chunks = smartlist_new();

  format_iso_time(published, rs->published_on);
  digest_to_base64(identity64, rs->identity_digest);
  digest_to_base64(digest64, rs->descriptor_digest);

  smartlist_add_asprintf(chunks,
                   "r %s %s %s%s%s %s %d %d\n",
                   rs->nickname,
                   identity64,
                   (format==NS_V3_CONSENSUS_MICRODESC)?"":digest64,
                   (format==NS_V3_CONSENSUS_MICRODESC)?"":" ",
                   published,
                   fmt_addr32(rs->addr),
                   (int)rs->or_port,
                   (int)rs->dir_port);

  /* TODO: Maybe we want to pass in what we need to build the rest of
   * this here, instead of in the caller. Then we could use the
   * networkstatus_type_t values, with an additional control port value
   * added -MP */

  /* V3 microdesc consensuses only have "a" lines in later consensus methods
   */
  if (format == NS_V3_CONSENSUS_MICRODESC &&
      consensus_method < MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS)
    goto done;

  /* Possible "a" line. At most one for now. */
  if (!tor_addr_is_null(&rs->ipv6_addr)) {
    smartlist_add_asprintf(chunks, "a %s\n",
                           fmt_addrport(&rs->ipv6_addr, rs->ipv6_orport));
  }

  if (format == NS_V3_CONSENSUS || format == NS_V3_CONSENSUS_MICRODESC)
    goto done;

  smartlist_add_asprintf(chunks,
                   "s%s%s%s%s%s%s%s%s%s%s%s\n",
                  /* These must stay in alphabetical order. */
                   rs->is_authority?" Authority":"",
                   rs->is_bad_exit?" BadExit":"",
                   rs->is_exit?" Exit":"",
                   rs->is_fast?" Fast":"",
                   rs->is_possible_guard?" Guard":"",
                   rs->is_hs_dir?" HSDir":"",
                   rs->is_flagged_running?" Running":"",
                   rs->is_stable?" Stable":"",
                   rs->is_staledesc?" StaleDesc":"",
                   rs->is_v2_dir?" V2Dir":"",
                   rs->is_valid?" Valid":"");

  /* length of "opt v \n" */
#define V_LINE_OVERHEAD 7
  if (version && strlen(version) < MAX_V_LINE_LEN - V_LINE_OVERHEAD) {
    smartlist_add_asprintf(chunks, "v %s\n", version);
  }
  if (protocols) {
    smartlist_add_asprintf(chunks, "pr %s\n", protocols);
  }

  if (format != NS_V2) {
    const routerinfo_t* desc = router_get_by_id_digest(rs->identity_digest);
    uint32_t bw_kb;

    if (format != NS_CONTROL_PORT) {
      /* Blow up more or less nicely if we didn't get anything or not the
       * thing we expected.
       */
      if (!desc) {
        char id[HEX_DIGEST_LEN+1];
        char dd[HEX_DIGEST_LEN+1];

        base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN);
        base16_encode(dd, sizeof(dd), rs->descriptor_digest, DIGEST_LEN);
        log_warn(LD_BUG, "Cannot get any descriptor for %s "
            "(wanted descriptor %s).",
            id, dd);
        goto err;
      }

      /* This assert could fire for the control port, because
       * it can request NS documents before all descriptors
       * have been fetched. Therefore, we only do this test when
       * format != NS_CONTROL_PORT. */
      if (tor_memneq(desc->cache_info.signed_descriptor_digest,
            rs->descriptor_digest,
            DIGEST_LEN)) {
        char rl_d[HEX_DIGEST_LEN+1];
        char rs_d[HEX_DIGEST_LEN+1];
        char id[HEX_DIGEST_LEN+1];

        base16_encode(rl_d, sizeof(rl_d),
            desc->cache_info.signed_descriptor_digest, DIGEST_LEN);
        base16_encode(rs_d, sizeof(rs_d), rs->descriptor_digest, DIGEST_LEN);
        base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN);
        log_err(LD_BUG, "descriptor digest in routerlist does not match "
            "the one in routerstatus: %s vs %s "
            "(router %s)\n",
            rl_d, rs_d, id);

        tor_assert(tor_memeq(desc->cache_info.signed_descriptor_digest,
              rs->descriptor_digest,
              DIGEST_LEN));
      }
    }

    if (format == NS_CONTROL_PORT && rs->has_bandwidth) {
      bw_kb = rs->bandwidth_kb;
    } else {
      tor_assert(desc);
      bw_kb = router_get_advertised_bandwidth_capped(desc) / 1000;
    }
    smartlist_add_asprintf(chunks,
                     "w Bandwidth=%d", bw_kb);

    if (format == NS_V3_VOTE && vrs && vrs->has_measured_bw) {
      smartlist_add_asprintf(chunks,
                       " Measured=%d", vrs->measured_bw_kb);
    }
    /* Write down guardfraction information if we have it. */
    if (format == NS_V3_VOTE && vrs && vrs->status.has_guardfraction) {
      smartlist_add_asprintf(chunks,
                             " GuardFraction=%d",
                             vrs->status.guardfraction_percentage);
    }

    smartlist_add_strdup(chunks, "\n");

    if (desc) {
      summary = policy_summarize(desc->exit_policy, AF_INET);
      smartlist_add_asprintf(chunks, "p %s\n", summary);
      tor_free(summary);
    }

    if (format == NS_V3_VOTE && vrs) {
      if (tor_mem_is_zero((char*)vrs->ed25519_id, ED25519_PUBKEY_LEN)) {
        smartlist_add_strdup(chunks, "id ed25519 none\n");
      } else {
        char ed_b64[BASE64_DIGEST256_LEN+1];
        digest256_to_base64(ed_b64, (const char*)vrs->ed25519_id);
        smartlist_add_asprintf(chunks, "id ed25519 %s\n", ed_b64);
      }
    }
  }

 done:
  result = smartlist_join_strings(chunks, "", 0, NULL);

 err:
  SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
  smartlist_free(chunks);

  return result;
}
Ejemplo n.º 28
0
static INLINE unsigned int
node_id_eq(const node_t *node1, const node_t *node2)
{
  return tor_memeq(node1->identity, node2->identity, DIGEST_LEN);
}
Ejemplo n.º 29
0
static void
test_router_pick_directory_server_impl(void *arg)
{
  (void)arg;

  networkstatus_t *con_md = NULL;
  char *consensus_text_md = NULL;
  int flags = PDS_IGNORE_FASCISTFIREWALL|PDS_RETRY_IF_NO_SERVERS;
  or_options_t *options = get_options_mutable();
  const routerstatus_t *rs = NULL;
  options->UseMicrodescriptors = 1;
  char *router1_id = NULL, *router2_id = NULL, *router3_id = NULL;
  node_t *node_router1 = NULL, *node_router2 = NULL, *node_router3 = NULL;
  config_line_t *policy_line = NULL;
  time_t now = time(NULL);
  int tmp_dirport1, tmp_dirport3;

  (void)arg;

  MOCK(usable_consensus_flavor, mock_usable_consensus_flavor);

  /* With no consensus, we must be bootstrapping, regardless of time or flavor
   */
  mock_usable_consensus_flavor_value = FLAV_NS;
  tt_assert(networkstatus_consensus_is_bootstrapping(now));
  tt_assert(networkstatus_consensus_is_bootstrapping(now + 2000));
  tt_assert(networkstatus_consensus_is_bootstrapping(now + 2*24*60*60));
  tt_assert(networkstatus_consensus_is_bootstrapping(now - 2*24*60*60));

  mock_usable_consensus_flavor_value = FLAV_MICRODESC;
  tt_assert(networkstatus_consensus_is_bootstrapping(now));
  tt_assert(networkstatus_consensus_is_bootstrapping(now + 2000));
  tt_assert(networkstatus_consensus_is_bootstrapping(now + 2*24*60*60));
  tt_assert(networkstatus_consensus_is_bootstrapping(now - 2*24*60*60));

  /* No consensus available, fail early */
  rs = router_pick_directory_server_impl(V3_DIRINFO, (const int) 0, NULL);
  tt_assert(rs == NULL);

  construct_consensus(&consensus_text_md);
  tt_assert(consensus_text_md);
  con_md = networkstatus_parse_vote_from_string(consensus_text_md, NULL,
                                                NS_TYPE_CONSENSUS);
  tt_assert(con_md);
  tt_int_op(con_md->flavor,==, FLAV_MICRODESC);
  tt_assert(con_md->routerstatus_list);
  tt_int_op(smartlist_len(con_md->routerstatus_list), ==, 3);
  tt_assert(!networkstatus_set_current_consensus_from_ns(con_md,
                                                 "microdesc"));

  /* If the consensus time or flavor doesn't match, we are still
   * bootstrapping */
  mock_usable_consensus_flavor_value = FLAV_NS;
  tt_assert(networkstatus_consensus_is_bootstrapping(now));
  tt_assert(networkstatus_consensus_is_bootstrapping(now + 2000));
  tt_assert(networkstatus_consensus_is_bootstrapping(now + 2*24*60*60));
  tt_assert(networkstatus_consensus_is_bootstrapping(now - 2*24*60*60));

  /* With a valid consensus for the current time and flavor, we stop
   * bootstrapping, even if we have no certificates */
  mock_usable_consensus_flavor_value = FLAV_MICRODESC;
  tt_assert(!networkstatus_consensus_is_bootstrapping(now + 2000));
  tt_assert(!networkstatus_consensus_is_bootstrapping(con_md->valid_after));
  tt_assert(!networkstatus_consensus_is_bootstrapping(con_md->valid_until));
  tt_assert(!networkstatus_consensus_is_bootstrapping(con_md->valid_until
                                                      + 24*60*60));
  /* These times are outside the test validity period */
  tt_assert(networkstatus_consensus_is_bootstrapping(now));
  tt_assert(networkstatus_consensus_is_bootstrapping(now + 2*24*60*60));
  tt_assert(networkstatus_consensus_is_bootstrapping(now - 2*24*60*60));

  nodelist_set_consensus(con_md);
  nodelist_assert_ok();

  rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
  /* We should not fail now we have a consensus and routerstatus_list
   * and nodelist are populated. */
  tt_assert(rs != NULL);

  /* Manipulate the nodes so we get the dir server we expect */
  router1_id = tor_malloc(DIGEST_LEN);
  memset(router1_id, TEST_DIR_ROUTER_ID_1, DIGEST_LEN);
  router2_id = tor_malloc(DIGEST_LEN);
  memset(router2_id, TEST_DIR_ROUTER_ID_2, DIGEST_LEN);
  router3_id = tor_malloc(DIGEST_LEN);
  memset(router3_id, TEST_DIR_ROUTER_ID_3, DIGEST_LEN);

  node_router1 = node_get_mutable_by_id(router1_id);
  node_router2 = node_get_mutable_by_id(router2_id);
  node_router3 = node_get_mutable_by_id(router3_id);

  node_router1->is_possible_guard = 1;

  node_router1->is_running = 0;
  node_router3->is_running = 0;
  rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
  tt_assert(rs != NULL);
  tt_assert(tor_memeq(rs->identity_digest, router2_id, DIGEST_LEN));
  rs = NULL;
  node_router1->is_running = 1;
  node_router3->is_running = 1;

  node_router1->rs->is_v2_dir = 0;
  node_router3->rs->is_v2_dir = 0;
  tmp_dirport1 = node_router1->rs->dir_port;
  tmp_dirport3 = node_router3->rs->dir_port;
  node_router1->rs->dir_port = 0;
  node_router3->rs->dir_port = 0;
  rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
  tt_assert(rs != NULL);
  tt_assert(tor_memeq(rs->identity_digest, router2_id, DIGEST_LEN));
  rs = NULL;
  node_router1->rs->is_v2_dir = 1;
  node_router3->rs->is_v2_dir = 1;
  node_router1->rs->dir_port = tmp_dirport1;
  node_router3->rs->dir_port = tmp_dirport3;

  node_router1->is_valid = 0;
  node_router3->is_valid = 0;
  rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
  tt_assert(rs != NULL);
  tt_assert(tor_memeq(rs->identity_digest, router2_id, DIGEST_LEN));
  rs = NULL;
  node_router1->is_valid = 1;
  node_router3->is_valid = 1;

  flags |= PDS_FOR_GUARD;
  node_router1->using_as_guard = 1;
  node_router2->using_as_guard = 1;
  node_router3->using_as_guard = 1;
  rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
  tt_assert(rs == NULL);
  node_router1->using_as_guard = 0;
  rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
  tt_assert(rs != NULL);
  tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));
  rs = NULL;
  node_router2->using_as_guard = 0;
  node_router3->using_as_guard = 0;

  /* One not valid, one guard. This should leave one remaining */
  node_router1->is_valid = 0;
  node_router2->using_as_guard = 1;
  rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
  tt_assert(rs != NULL);
  tt_assert(tor_memeq(rs->identity_digest, router3_id, DIGEST_LEN));
  rs = NULL;
  node_router1->is_valid = 1;
  node_router2->using_as_guard = 0;

  /* Manipulate overloaded */

  node_router2->rs->last_dir_503_at = now;
  node_router3->rs->last_dir_503_at = now;
  rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
  tt_assert(rs != NULL);
  tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));
  node_router2->rs->last_dir_503_at = 0;
  node_router3->rs->last_dir_503_at = 0;

  /* Set a Fascist firewall */
  flags &= ~ PDS_IGNORE_FASCISTFIREWALL;
  policy_line = tor_malloc_zero(sizeof(config_line_t));
  policy_line->key = tor_strdup("ReachableORAddresses");
  policy_line->value = tor_strdup("accept *:442, reject *:*");
  options->ReachableORAddresses = policy_line;
  policies_parse_from_options(options);

  node_router1->rs->or_port = 444;
  node_router2->rs->or_port = 443;
  node_router3->rs->or_port = 442;
  rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
  tt_assert(rs != NULL);
  tt_assert(tor_memeq(rs->identity_digest, router3_id, DIGEST_LEN));
  node_router1->rs->or_port = 442;
  node_router2->rs->or_port = 443;
  node_router3->rs->or_port = 444;
  rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
  tt_assert(rs != NULL);
  tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));

  /* Fascist firewall and overloaded */
  node_router1->rs->or_port = 442;
  node_router2->rs->or_port = 443;
  node_router3->rs->or_port = 442;
  node_router3->rs->last_dir_503_at = now;
  rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
  tt_assert(rs != NULL);
  tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));
  node_router3->rs->last_dir_503_at = 0;

  /* Fascists against OR and Dir */
  policy_line = tor_malloc_zero(sizeof(config_line_t));
  policy_line->key = tor_strdup("ReachableAddresses");
  policy_line->value = tor_strdup("accept *:80, reject *:*");
  options->ReachableDirAddresses = policy_line;
  policies_parse_from_options(options);
  node_router1->rs->or_port = 442;
  node_router2->rs->or_port = 441;
  node_router3->rs->or_port = 443;
  node_router1->rs->dir_port = 80;
  node_router2->rs->dir_port = 80;
  node_router3->rs->dir_port = 81;
  node_router1->rs->last_dir_503_at = now;
  rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
  tt_assert(rs != NULL);
  tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));
  node_router1->rs->last_dir_503_at = 0;

 done:
  UNMOCK(usable_consensus_flavor);
  if (router1_id)
    tor_free(router1_id);
  if (router2_id)
    tor_free(router2_id);
  if (router3_id)
    tor_free(router3_id);
  if (options->ReachableORAddresses ||
      options->ReachableDirAddresses)
    policies_free_all();
  tor_free(consensus_text_md);
  networkstatus_vote_free(con_md);
}
Ejemplo n.º 30
0
/**
 * Read an ed25519 key and associated certificates from files beginning with
 * <b>fname</b>, with certificate type <b>cert_type</b>.  On failure, return
 * NULL; on success return the keypair.
 *
 * If INIT_ED_KEY_CREATE is set in <b>flags</b>, then create the key (and
 * certificate if requested) if it doesn't exist, and save it to disk.
 *
 * If INIT_ED_KEY_NEEDCERT is set in <b>flags</b>, load/create a certificate
 * too and store it in *<b>cert_out</b>.  Fail if the cert can't be
 * found/created.  To create a certificate, <b>signing_key</b> must be set to
 * the key that should sign it; <b>now</b> to the current time, and
 * <b>lifetime</b> to the lifetime of the key.
 *
 * If INIT_ED_KEY_REPLACE is set in <b>flags</b>, then create and save new key
 * whether we can read the old one or not.
 *
 * If INIT_ED_KEY_EXTRA_STRONG is set in <b>flags</b>, set the extra_strong
 * flag when creating the secret key.
 *
 * If INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT is set in <b>flags</b>, and
 * we create a new certificate, create it with the signing key embedded.
 *
 * If INIT_ED_KEY_SPLIT is set in <b>flags</b>, and we create a new key,
 * store the public key in a separate file from the secret key.
 *
 * If INIT_ED_KEY_MISSING_SECRET_OK is set in <b>flags</b>, and we find a
 * public key file but no secret key file, return successfully anyway.
 *
 * If INIT_ED_KEY_OMIT_SECRET is set in <b>flags</b>, do not try to load a
 * secret key unless no public key is found.  Do not return a secret key. (but
 * create and save one if needed).
 *
 * If INIT_ED_KEY_NO_LOAD_SECRET is set in <b>flags</b>, don't try to load
 * a secret key, no matter what.
 *
 * If INIT_ED_KEY_TRY_ENCRYPTED is set, we look for an encrypted secret key
 * and consider encrypting any new secret key.
 *
 * If INIT_ED_KEY_NO_REPAIR is set, and there is any issue loading the keys
 * from disk _other than their absence_ (full or partial), we do not try to
 * replace them.
 *
 * If INIT_ED_KEY_SUGGEST_KEYGEN is set, have log messages about failures
 * refer to the --keygen option.
 *
 * If INIT_ED_KEY_EXPLICIT_FNAME is set, use the provided file name for the
 * secret key file, encrypted or not.
 */
ed25519_keypair_t *
ed_key_init_from_file(const char *fname, uint32_t flags,
                      int severity,
                      const ed25519_keypair_t *signing_key,
                      time_t now,
                      time_t lifetime,
                      uint8_t cert_type,
                      struct tor_cert_st **cert_out)
{
  char *secret_fname = NULL;
  char *encrypted_secret_fname = NULL;
  char *public_fname = NULL;
  char *cert_fname = NULL;
  const char *loaded_secret_fname = NULL;
  int created_pk = 0, created_sk = 0, created_cert = 0;
  const int try_to_load = ! (flags & INIT_ED_KEY_REPLACE);
  const int encrypt_key = !! (flags & INIT_ED_KEY_TRY_ENCRYPTED);
  const int norepair = !! (flags & INIT_ED_KEY_NO_REPAIR);
  const int split = !! (flags & INIT_ED_KEY_SPLIT);
  const int omit_secret = !! (flags & INIT_ED_KEY_OMIT_SECRET);
  const int offline_secret = !! (flags & INIT_ED_KEY_OFFLINE_SECRET);
  const int explicit_fname = !! (flags & INIT_ED_KEY_EXPLICIT_FNAME);

  /* we don't support setting both of these flags at once. */
  tor_assert((flags & (INIT_ED_KEY_NO_REPAIR|INIT_ED_KEY_NEEDCERT)) !=
                      (INIT_ED_KEY_NO_REPAIR|INIT_ED_KEY_NEEDCERT));

  char tag[8];
  tor_snprintf(tag, sizeof(tag), "type%d", (int)cert_type);

  tor_cert_t *cert = NULL;
  char *got_tag = NULL;
  ed25519_keypair_t *keypair = tor_malloc_zero(sizeof(ed25519_keypair_t));

  if (explicit_fname) {
    secret_fname = tor_strdup(fname);
    encrypted_secret_fname = tor_strdup(fname);
  } else {
    tor_asprintf(&secret_fname, "%s_secret_key", fname);
    tor_asprintf(&encrypted_secret_fname, "%s_secret_key_encrypted", fname);
  }
  tor_asprintf(&public_fname, "%s_public_key", fname);
  tor_asprintf(&cert_fname, "%s_cert", fname);

  /* Try to read the secret key. */
  int have_secret = 0;
  int load_secret = try_to_load &&
    !offline_secret &&
    (!omit_secret || file_status(public_fname)==FN_NOENT);
  if (load_secret) {
    int rv = ed25519_seckey_read_from_file(&keypair->seckey,
                                           &got_tag, secret_fname);
    if (rv == 0) {
      have_secret = 1;
      loaded_secret_fname = secret_fname;
      tor_assert(got_tag);
    } else {
      if (errno != ENOENT && norepair) {
        tor_log(severity, LD_OR, "Unable to read %s: %s", secret_fname,
                strerror(errno));
        goto err;
      }
    }
  }

  /* Should we try for an encrypted key? */
  int have_encrypted_secret_file = 0;
  if (!have_secret && try_to_load && encrypt_key) {
    int r = read_encrypted_secret_key(&keypair->seckey,
                                      encrypted_secret_fname);
    if (r > 0) {
      have_secret = 1;
      have_encrypted_secret_file = 1;
      tor_free(got_tag); /* convince coverity we aren't leaking */
      got_tag = tor_strdup(tag);
      loaded_secret_fname = encrypted_secret_fname;
    } else if (errno != ENOENT && norepair) {
      tor_log(severity, LD_OR, "Unable to read %s: %s",
              encrypted_secret_fname, strerror(errno));
      goto err;
    }
  } else {
    if (try_to_load) {
      /* Check if it's there anyway, so we don't replace it. */
      if (file_status(encrypted_secret_fname) != FN_NOENT)
        have_encrypted_secret_file = 1;
    }
  }

  if (have_secret) {
    if (strcmp(got_tag, tag)) {
      tor_log(severity, LD_OR, "%s has wrong tag", loaded_secret_fname);
      goto err;
    }
    /* Derive the public key */
    if (ed25519_public_key_generate(&keypair->pubkey, &keypair->seckey)<0) {
      tor_log(severity, LD_OR, "%s can't produce a public key",
              loaded_secret_fname);
      goto err;
    }
  }

  /* If we do split keys here, try to read the pubkey. */
  int found_public = 0;
  if (try_to_load && (!have_secret || split)) {
    ed25519_public_key_t pubkey_tmp;
    tor_free(got_tag);
    found_public = ed25519_pubkey_read_from_file(&pubkey_tmp,
                                                 &got_tag, public_fname) == 0;
    if (!found_public && errno != ENOENT && norepair) {
      tor_log(severity, LD_OR, "Unable to read %s: %s", public_fname,
              strerror(errno));
      goto err;
    }
    if (found_public && strcmp(got_tag, tag)) {
      tor_log(severity, LD_OR, "%s has wrong tag", public_fname);
      goto err;
    }
    if (found_public) {
      if (have_secret) {
        /* If we have a secret key and we're reloading the public key,
         * the key must match! */
        if (! ed25519_pubkey_eq(&keypair->pubkey, &pubkey_tmp)) {
          tor_log(severity, LD_OR, "%s does not match %s!  If you are trying "
                  "to restore from backup, make sure you didn't mix up the "
                  "key files. If you are absolutely sure that %s is the right "
                  "key for this relay, delete %s or move it out of the way.",
                  public_fname, loaded_secret_fname,
                  loaded_secret_fname, public_fname);
          goto err;
        }
      } else {
        /* We only have the public key; better use that. */
        tor_assert(split);
        memcpy(&keypair->pubkey, &pubkey_tmp, sizeof(pubkey_tmp));
      }
    } else {
      /* We have no public key file, but we do have a secret key, make the
       * public key file! */
      if (have_secret) {
        if (ed25519_pubkey_write_to_file(&keypair->pubkey, public_fname, tag)
            < 0) {
          tor_log(severity, LD_OR, "Couldn't repair %s", public_fname);
          goto err;
        } else {
          tor_log(LOG_NOTICE, LD_OR,
                  "Found secret key but not %s. Regenerating.",
                  public_fname);
        }
      }
    }
  }

  /* If the secret key is absent and it's not allowed to be, fail. */
  if (!have_secret && found_public &&
      !(flags & INIT_ED_KEY_MISSING_SECRET_OK)) {
    if (have_encrypted_secret_file) {
      tor_log(severity, LD_OR, "We needed to load a secret key from %s, "
              "but it was encrypted. Try 'tor --keygen' instead, so you "
              "can enter the passphrase.",
              secret_fname);
    } else {
      tor_log(severity, LD_OR, "We needed to load a secret key from %s, "
              "but couldn't find it. %s", secret_fname,
              (flags & INIT_ED_KEY_SUGGEST_KEYGEN) ?
              "If you're keeping your master secret key offline, you will "
              "need to run 'tor --keygen' to generate new signing keys." :
              "Did you forget to copy it over when you copied the rest of the "
              "signing key material?");
    }
    goto err;
  }

  /* If it's absent, and we're not supposed to make a new keypair, fail. */
  if (!have_secret && !found_public && !(flags & INIT_ED_KEY_CREATE)) {
    if (split) {
      tor_log(severity, LD_OR, "No key found in %s or %s.",
              secret_fname, public_fname);
    } else {
      tor_log(severity, LD_OR, "No key found in %s.", secret_fname);
    }
    goto err;
  }

  /* If the secret key is absent, but the encrypted key would be present,
   * that's an error */
  if (!have_secret && !found_public && have_encrypted_secret_file) {
    tor_assert(!encrypt_key);
    tor_log(severity, LD_OR, "Found an encrypted secret key, "
            "but not public key file %s!", public_fname);
    goto err;
  }

  /* if it's absent, make a new keypair... */
  if (!have_secret && !found_public) {
    tor_free(keypair);
    keypair = ed_key_new(signing_key, flags, now, lifetime,
                         cert_type, &cert);
    if (!keypair) {
      tor_log(severity, LD_OR, "Couldn't create keypair");
      goto err;
    }
    created_pk = created_sk = created_cert = 1;
  }

  /* Write it to disk if we're supposed to do with a new passphrase, or if
   * we just created it. */
  if (created_sk || (have_secret && get_options()->change_key_passphrase)) {
    if (write_secret_key(&keypair->seckey,
                         encrypt_key,
                         secret_fname, tag, encrypted_secret_fname) < 0
        ||
        (split &&
         ed25519_pubkey_write_to_file(&keypair->pubkey, public_fname, tag) < 0)
        ||
        (cert &&
         crypto_write_tagged_contents_to_file(cert_fname, "ed25519v1-cert",
                                 tag, cert->encoded, cert->encoded_len) < 0)) {
      tor_log(severity, LD_OR, "Couldn't write keys or cert to file.");
      goto err;
    }
    goto done;
  }

  /* If we're not supposed to get a cert, we're done. */
  if (! (flags & INIT_ED_KEY_NEEDCERT))
    goto done;

  /* Read a cert. */
  tor_free(got_tag);
  uint8_t certbuf[256];
  ssize_t cert_body_len = crypto_read_tagged_contents_from_file(
                 cert_fname, "ed25519v1-cert",
                 &got_tag, certbuf, sizeof(certbuf));
  if (cert_body_len >= 0 && !strcmp(got_tag, tag))
    cert = tor_cert_parse(certbuf, cert_body_len);

  /* If we got it, check it to the extent we can. */
  int bad_cert = 0;

  if (! cert) {
    tor_log(severity, LD_OR, "Cert was unparseable");
    bad_cert = 1;
  } else if (!tor_memeq(cert->signed_key.pubkey, keypair->pubkey.pubkey,
                        ED25519_PUBKEY_LEN)) {
    tor_log(severity, LD_OR, "Cert was for wrong key");
    bad_cert = 1;
  } else if (signing_key &&
             tor_cert_checksig(cert, &signing_key->pubkey, now) < 0) {
    tor_log(severity, LD_OR, "Can't check certificate");
    bad_cert = 1;
  } else if (cert->cert_expired) {
    tor_log(severity, LD_OR, "Certificate is expired");
    bad_cert = 1;
  } else if (signing_key && cert->signing_key_included &&
             ! ed25519_pubkey_eq(&signing_key->pubkey, &cert->signing_key)) {
    tor_log(severity, LD_OR, "Certificate signed by unexpectd key!");
    bad_cert = 1;
  }

  if (bad_cert) {
    tor_cert_free(cert);
    cert = NULL;
  }

  /* If we got a cert, we're done. */
  if (cert)
    goto done;

  /* If we didn't get a cert, and we're not supposed to make one, fail. */
  if (!signing_key || !(flags & INIT_ED_KEY_CREATE)) {
    tor_log(severity, LD_OR, "Without signing key, can't create certificate");
    goto err;
  }

  /* We have keys but not a certificate, so make one. */
  uint32_t cert_flags = 0;
  if (flags & INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT)
    cert_flags |= CERT_FLAG_INCLUDE_SIGNING_KEY;
  cert = tor_cert_create(signing_key, cert_type,
                         &keypair->pubkey,
                         now, lifetime,
                         cert_flags);

  if (! cert) {
    tor_log(severity, LD_OR, "Couldn't create certificate");
    goto err;
  }

  /* Write it to disk. */
  created_cert = 1;
  if (crypto_write_tagged_contents_to_file(cert_fname, "ed25519v1-cert",
                             tag, cert->encoded, cert->encoded_len) < 0) {
    tor_log(severity, LD_OR, "Couldn't write cert to disk.");
    goto err;
  }

 done:
  if (cert_out)
    *cert_out = cert;
  else
    tor_cert_free(cert);

  goto cleanup;

 err:
  if (keypair)
    memwipe(keypair, 0, sizeof(*keypair));
  tor_free(keypair);
  tor_cert_free(cert);
  if (cert_out)
    *cert_out = NULL;
  if (created_sk)
    unlink(secret_fname);
  if (created_pk)
    unlink(public_fname);
  if (created_cert)
    unlink(cert_fname);

 cleanup:
  tor_free(encrypted_secret_fname);
  tor_free(secret_fname);
  tor_free(public_fname);
  tor_free(cert_fname);
  tor_free(got_tag);

  return keypair;
}