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; }
/** 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); }
/** 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; }
/** 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; } });
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; }
/* 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); }
/** 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); }
/** 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); }
/** 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))); }
/** 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; } });
/** 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; });
/** 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); }
/** 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); } } }
/** 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; }
/** * 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; }
/** 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; }
/** 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; }
/** 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); }
/** 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); }
/** 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)); }
/** 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)); }
/** 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; }
/** 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; });
/** * 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; }
/** 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); }
/** 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); }
/** 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; }
static INLINE unsigned int node_id_eq(const node_t *node1, const node_t *node2) { return tor_memeq(node1->identity, node2->identity, DIGEST_LEN); }
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); }
/** * 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; }