mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-12-11 21:23:35 +01:00
15a4180a7e
Split "can extend over IPv6" and "has advertised IPv6 ORPort" into separate functions. They currently have the same result, but this may change in 33818 with ExtendAllowIPv6Addresses. Part of 33817.
3566 lines
117 KiB
C
3566 lines
117 KiB
C
/* Copyright (c) 2001 Matej Pfajfar.
|
|
* Copyright (c) 2001-2004, Roger Dingledine.
|
|
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
|
* Copyright (c) 2007-2020, The Tor Project, Inc. */
|
|
/* See LICENSE for licensing information */
|
|
|
|
#define ROUTER_PRIVATE
|
|
|
|
#include "core/or/or.h"
|
|
#include "app/config/config.h"
|
|
#include "app/config/statefile.h"
|
|
#include "app/main/main.h"
|
|
#include "core/mainloop/connection.h"
|
|
#include "core/mainloop/mainloop.h"
|
|
#include "core/mainloop/netstatus.h"
|
|
#include "core/or/policies.h"
|
|
#include "core/or/protover.h"
|
|
#include "feature/client/transports.h"
|
|
#include "feature/control/control_events.h"
|
|
#include "feature/dirauth/process_descs.h"
|
|
#include "feature/dircache/dirserv.h"
|
|
#include "feature/dirclient/dirclient.h"
|
|
#include "feature/dircommon/directory.h"
|
|
#include "feature/dirparse/authcert_parse.h"
|
|
#include "feature/dirparse/routerparse.h"
|
|
#include "feature/dirparse/signing.h"
|
|
#include "feature/hibernate/hibernate.h"
|
|
#include "feature/keymgt/loadkey.h"
|
|
#include "feature/nodelist/authcert.h"
|
|
#include "feature/nodelist/dirlist.h"
|
|
#include "feature/nodelist/networkstatus.h"
|
|
#include "feature/nodelist/nickname.h"
|
|
#include "feature/nodelist/nodefamily.h"
|
|
#include "feature/nodelist/nodelist.h"
|
|
#include "feature/nodelist/routerlist.h"
|
|
#include "feature/nodelist/torcert.h"
|
|
#include "feature/relay/dns.h"
|
|
#include "feature/relay/relay_config.h"
|
|
#include "feature/relay/router.h"
|
|
#include "feature/relay/routerkeys.h"
|
|
#include "feature/relay/routermode.h"
|
|
#include "feature/relay/selftest.h"
|
|
#include "lib/geoip/geoip.h"
|
|
#include "feature/stats/geoip_stats.h"
|
|
#include "feature/stats/rephist.h"
|
|
#include "lib/crypt_ops/crypto_ed25519.h"
|
|
#include "lib/crypt_ops/crypto_format.h"
|
|
#include "lib/crypt_ops/crypto_init.h"
|
|
#include "lib/crypt_ops/crypto_rand.h"
|
|
#include "lib/crypt_ops/crypto_util.h"
|
|
#include "lib/encoding/confline.h"
|
|
#include "lib/osinfo/uname.h"
|
|
#include "lib/tls/tortls.h"
|
|
#include "lib/version/torversion.h"
|
|
|
|
#include "feature/dirauth/authmode.h"
|
|
|
|
#include "app/config/or_state_st.h"
|
|
#include "core/or/port_cfg_st.h"
|
|
#include "feature/dirclient/dir_server_st.h"
|
|
#include "feature/dircommon/dir_connection_st.h"
|
|
#include "feature/nodelist/authority_cert_st.h"
|
|
#include "feature/nodelist/extrainfo_st.h"
|
|
#include "feature/nodelist/networkstatus_st.h"
|
|
#include "feature/nodelist/node_st.h"
|
|
#include "feature/nodelist/routerinfo_st.h"
|
|
#include "feature/nodelist/routerstatus_st.h"
|
|
|
|
/**
|
|
* \file router.c
|
|
* \brief Miscellaneous relay functionality, including RSA key maintenance,
|
|
* generating and uploading server descriptors, picking an address to
|
|
* advertise, and so on.
|
|
*
|
|
* This module handles the job of deciding whether we are a Tor relay, and if
|
|
* so what kind. (Mostly through functions like server_mode() that inspect an
|
|
* or_options_t, but in some cases based on our own capabilities, such as when
|
|
* we are deciding whether to be a directory cache in
|
|
* router_has_bandwidth_to_be_dirserver().)
|
|
*
|
|
* Also in this module are the functions to generate our own routerinfo_t and
|
|
* extrainfo_t, and to encode those to signed strings for upload to the
|
|
* directory authorities.
|
|
*
|
|
* This module also handles key maintenance for RSA and Curve25519-ntor keys,
|
|
* and for our TLS context. (These functions should eventually move to
|
|
* routerkeys.c along with the code that handles Ed25519 keys now.)
|
|
**/
|
|
|
|
/************************************************************/
|
|
|
|
/*****
|
|
* Key management: ORs only.
|
|
*****/
|
|
|
|
/** Private keys for this OR. There is also an SSL key managed by tortls.c.
|
|
*/
|
|
static tor_mutex_t *key_lock=NULL;
|
|
static time_t onionkey_set_at=0; /**< When was onionkey last changed? */
|
|
/** Current private onionskin decryption key: used to decode CREATE cells. */
|
|
static crypto_pk_t *onionkey=NULL;
|
|
/** Previous private onionskin decryption key: used to decode CREATE cells
|
|
* generated by clients that have an older version of our descriptor. */
|
|
static crypto_pk_t *lastonionkey=NULL;
|
|
/** Current private ntor secret key: used to perform the ntor handshake. */
|
|
static curve25519_keypair_t curve25519_onion_key;
|
|
/** Previous private ntor secret key: used to perform the ntor handshake
|
|
* with clients that have an older version of our descriptor. */
|
|
static curve25519_keypair_t last_curve25519_onion_key;
|
|
/** Private server "identity key": used to sign directory info and TLS
|
|
* certificates. Never changes. */
|
|
static crypto_pk_t *server_identitykey=NULL;
|
|
/** Digest of server_identitykey. */
|
|
static char server_identitykey_digest[DIGEST_LEN];
|
|
/** Private client "identity key": used to sign bridges' and clients'
|
|
* outbound TLS certificates. Regenerated on startup and on IP address
|
|
* change. */
|
|
static crypto_pk_t *client_identitykey=NULL;
|
|
/** Signing key used for v3 directory material; only set for authorities. */
|
|
static crypto_pk_t *authority_signing_key = NULL;
|
|
/** Key certificate to authenticate v3 directory material; only set for
|
|
* authorities. */
|
|
static authority_cert_t *authority_key_certificate = NULL;
|
|
|
|
/** For emergency V3 authority key migration: An extra signing key that we use
|
|
* with our old (obsolete) identity key for a while. */
|
|
static crypto_pk_t *legacy_signing_key = NULL;
|
|
/** For emergency V3 authority key migration: An extra certificate to
|
|
* authenticate legacy_signing_key with our obsolete identity key.*/
|
|
static authority_cert_t *legacy_key_certificate = NULL;
|
|
|
|
/* (Note that v3 authorities also have a separate "authority identity key",
|
|
* but this key is never actually loaded by the Tor process. Instead, it's
|
|
* used by tor-gencert to sign new signing keys and make new key
|
|
* certificates. */
|
|
|
|
/** Return a readonly string with human readable description
|
|
* of <b>err</b>.
|
|
*/
|
|
const char *
|
|
routerinfo_err_to_string(int err)
|
|
{
|
|
switch (err) {
|
|
case TOR_ROUTERINFO_ERROR_NO_EXT_ADDR:
|
|
return "No known exit address yet";
|
|
case TOR_ROUTERINFO_ERROR_CANNOT_PARSE:
|
|
return "Cannot parse descriptor";
|
|
case TOR_ROUTERINFO_ERROR_NOT_A_SERVER:
|
|
return "Not running in server mode";
|
|
case TOR_ROUTERINFO_ERROR_DIGEST_FAILED:
|
|
return "Key digest failed";
|
|
case TOR_ROUTERINFO_ERROR_CANNOT_GENERATE:
|
|
return "Cannot generate descriptor";
|
|
case TOR_ROUTERINFO_ERROR_DESC_REBUILDING:
|
|
return "Descriptor still rebuilding - not ready yet";
|
|
case TOR_ROUTERINFO_ERROR_INTERNAL_BUG:
|
|
return "Internal bug, see logs for details";
|
|
}
|
|
|
|
log_warn(LD_BUG, "unknown routerinfo error %d - shouldn't happen", err);
|
|
tor_assert_unreached();
|
|
|
|
return "Unknown error";
|
|
}
|
|
|
|
/** Return true if we expect given error to be transient.
|
|
* Return false otherwise.
|
|
*/
|
|
int
|
|
routerinfo_err_is_transient(int err)
|
|
{
|
|
/**
|
|
* For simplicity, we consider all errors other than
|
|
* "not a server" transient - see discussion on
|
|
* https://trac.torproject.org/projects/tor/ticket/27034
|
|
*/
|
|
return err != TOR_ROUTERINFO_ERROR_NOT_A_SERVER;
|
|
}
|
|
|
|
/** Replace the current onion key with <b>k</b>. Does not affect
|
|
* lastonionkey; to update lastonionkey correctly, call rotate_onion_key().
|
|
*/
|
|
static void
|
|
set_onion_key(crypto_pk_t *k)
|
|
{
|
|
if (onionkey && crypto_pk_eq_keys(onionkey, k)) {
|
|
/* k is already our onion key; free it and return */
|
|
crypto_pk_free(k);
|
|
return;
|
|
}
|
|
tor_mutex_acquire(key_lock);
|
|
crypto_pk_free(onionkey);
|
|
onionkey = k;
|
|
tor_mutex_release(key_lock);
|
|
mark_my_descriptor_dirty("set onion key");
|
|
}
|
|
|
|
/** Return the current onion key. Requires that the onion key has been
|
|
* loaded or generated. */
|
|
MOCK_IMPL(crypto_pk_t *,
|
|
get_onion_key,(void))
|
|
{
|
|
tor_assert(onionkey);
|
|
return onionkey;
|
|
}
|
|
|
|
/** Store a full copy of the current onion key into *<b>key</b>, and a full
|
|
* copy of the most recent onion key into *<b>last</b>. Store NULL into
|
|
* a pointer if the corresponding key does not exist.
|
|
*/
|
|
void
|
|
dup_onion_keys(crypto_pk_t **key, crypto_pk_t **last)
|
|
{
|
|
tor_assert(key);
|
|
tor_assert(last);
|
|
tor_mutex_acquire(key_lock);
|
|
if (onionkey)
|
|
*key = crypto_pk_copy_full(onionkey);
|
|
else
|
|
*key = NULL;
|
|
if (lastonionkey)
|
|
*last = crypto_pk_copy_full(lastonionkey);
|
|
else
|
|
*last = NULL;
|
|
tor_mutex_release(key_lock);
|
|
}
|
|
|
|
/** Expire our old set of onion keys. This is done by setting
|
|
* last_curve25519_onion_key and lastonionkey to all zero's and NULL
|
|
* respectively.
|
|
*
|
|
* This function does not perform any grace period checks for the old onion
|
|
* keys.
|
|
*/
|
|
void
|
|
expire_old_onion_keys(void)
|
|
{
|
|
char *fname = NULL;
|
|
|
|
tor_mutex_acquire(key_lock);
|
|
|
|
/* Free lastonionkey and set it to NULL. */
|
|
if (lastonionkey) {
|
|
crypto_pk_free(lastonionkey);
|
|
lastonionkey = NULL;
|
|
}
|
|
|
|
/* We zero out the keypair. See the fast_mem_is_zero() check made in
|
|
* construct_ntor_key_map() below. */
|
|
memset(&last_curve25519_onion_key, 0, sizeof(last_curve25519_onion_key));
|
|
|
|
tor_mutex_release(key_lock);
|
|
|
|
fname = get_keydir_fname("secret_onion_key.old");
|
|
if (file_status(fname) == FN_FILE) {
|
|
if (tor_unlink(fname) != 0) {
|
|
log_warn(LD_FS, "Couldn't unlink old onion key file %s: %s",
|
|
fname, strerror(errno));
|
|
}
|
|
}
|
|
tor_free(fname);
|
|
|
|
fname = get_keydir_fname("secret_onion_key_ntor.old");
|
|
if (file_status(fname) == FN_FILE) {
|
|
if (tor_unlink(fname) != 0) {
|
|
log_warn(LD_FS, "Couldn't unlink old ntor onion key file %s: %s",
|
|
fname, strerror(errno));
|
|
}
|
|
}
|
|
tor_free(fname);
|
|
}
|
|
|
|
/** Return the current secret onion key for the ntor handshake. Must only
|
|
* be called from the main thread. */
|
|
MOCK_IMPL(STATIC const struct curve25519_keypair_t *,
|
|
get_current_curve25519_keypair,(void))
|
|
{
|
|
return &curve25519_onion_key;
|
|
}
|
|
|
|
/** Return a map from KEYID (the key itself) to keypairs for use in the ntor
|
|
* handshake. Must only be called from the main thread. */
|
|
di_digest256_map_t *
|
|
construct_ntor_key_map(void)
|
|
{
|
|
di_digest256_map_t *m = NULL;
|
|
|
|
const uint8_t *cur_pk = curve25519_onion_key.pubkey.public_key;
|
|
const uint8_t *last_pk = last_curve25519_onion_key.pubkey.public_key;
|
|
|
|
if (!fast_mem_is_zero((const char *)cur_pk, CURVE25519_PUBKEY_LEN)) {
|
|
dimap_add_entry(&m, cur_pk,
|
|
tor_memdup(&curve25519_onion_key,
|
|
sizeof(curve25519_keypair_t)));
|
|
}
|
|
if (!fast_mem_is_zero((const char*)last_pk, CURVE25519_PUBKEY_LEN) &&
|
|
tor_memneq(cur_pk, last_pk, CURVE25519_PUBKEY_LEN)) {
|
|
dimap_add_entry(&m, last_pk,
|
|
tor_memdup(&last_curve25519_onion_key,
|
|
sizeof(curve25519_keypair_t)));
|
|
}
|
|
|
|
return m;
|
|
}
|
|
/** Helper used to deallocate a di_digest256_map_t returned by
|
|
* construct_ntor_key_map. */
|
|
static void
|
|
ntor_key_map_free_helper(void *arg)
|
|
{
|
|
curve25519_keypair_t *k = arg;
|
|
memwipe(k, 0, sizeof(*k));
|
|
tor_free(k);
|
|
}
|
|
/** Release all storage from a keymap returned by construct_ntor_key_map. */
|
|
void
|
|
ntor_key_map_free_(di_digest256_map_t *map)
|
|
{
|
|
if (!map)
|
|
return;
|
|
dimap_free(map, ntor_key_map_free_helper);
|
|
}
|
|
|
|
/** Return the time when the onion key was last set. This is either the time
|
|
* when the process launched, or the time of the most recent key rotation since
|
|
* the process launched.
|
|
*/
|
|
time_t
|
|
get_onion_key_set_at(void)
|
|
{
|
|
return onionkey_set_at;
|
|
}
|
|
|
|
/** Set the current server identity key to <b>k</b>.
|
|
*/
|
|
void
|
|
set_server_identity_key(crypto_pk_t *k)
|
|
{
|
|
crypto_pk_free(server_identitykey);
|
|
server_identitykey = k;
|
|
if (crypto_pk_get_digest(server_identitykey,
|
|
server_identitykey_digest) < 0) {
|
|
log_err(LD_BUG, "Couldn't compute our own identity key digest.");
|
|
tor_assert(0);
|
|
}
|
|
}
|
|
|
|
#ifdef TOR_UNIT_TESTS
|
|
/** Testing only -- set the server's RSA identity digest to
|
|
* be <b>digest</b> */
|
|
void
|
|
set_server_identity_key_digest_testing(const uint8_t *digest)
|
|
{
|
|
memcpy(server_identitykey_digest, digest, DIGEST_LEN);
|
|
}
|
|
#endif /* defined(TOR_UNIT_TESTS) */
|
|
|
|
/** Make sure that we have set up our identity keys to match or not match as
|
|
* appropriate, and die with an assertion if we have not. */
|
|
static void
|
|
assert_identity_keys_ok(void)
|
|
{
|
|
if (1)
|
|
return;
|
|
tor_assert(client_identitykey);
|
|
if (public_server_mode(get_options())) {
|
|
/* assert that we have set the client and server keys to be equal */
|
|
tor_assert(server_identitykey);
|
|
tor_assert(crypto_pk_eq_keys(client_identitykey, server_identitykey));
|
|
} else {
|
|
/* assert that we have set the client and server keys to be unequal */
|
|
if (server_identitykey)
|
|
tor_assert(!crypto_pk_eq_keys(client_identitykey, server_identitykey));
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_MODULE_RELAY
|
|
|
|
/** Returns the current server identity key; requires that the key has
|
|
* been set, and that we are running as a Tor server.
|
|
*/
|
|
MOCK_IMPL(crypto_pk_t *,
|
|
get_server_identity_key,(void))
|
|
{
|
|
tor_assert(server_identitykey);
|
|
tor_assert(server_mode(get_options()));
|
|
assert_identity_keys_ok();
|
|
return server_identitykey;
|
|
}
|
|
|
|
#endif /* defined(HAVE_MODULE_RELAY) */
|
|
|
|
/** Return true iff we are a server and the server identity key
|
|
* has been set. */
|
|
int
|
|
server_identity_key_is_set(void)
|
|
{
|
|
return server_mode(get_options()) && server_identitykey != NULL;
|
|
}
|
|
|
|
/** Set the current client identity key to <b>k</b>.
|
|
*/
|
|
void
|
|
set_client_identity_key(crypto_pk_t *k)
|
|
{
|
|
crypto_pk_free(client_identitykey);
|
|
client_identitykey = k;
|
|
}
|
|
|
|
/** Returns the current client identity key for use on outgoing TLS
|
|
* connections; requires that the key has been set.
|
|
*/
|
|
crypto_pk_t *
|
|
get_tlsclient_identity_key(void)
|
|
{
|
|
tor_assert(client_identitykey);
|
|
assert_identity_keys_ok();
|
|
return client_identitykey;
|
|
}
|
|
|
|
/** Return true iff the client identity key has been set. */
|
|
int
|
|
client_identity_key_is_set(void)
|
|
{
|
|
return client_identitykey != NULL;
|
|
}
|
|
|
|
/** Return the key certificate for this v3 (voting) authority, or NULL
|
|
* if we have no such certificate. */
|
|
MOCK_IMPL(authority_cert_t *,
|
|
get_my_v3_authority_cert, (void))
|
|
{
|
|
return authority_key_certificate;
|
|
}
|
|
|
|
/** Return the v3 signing key for this v3 (voting) authority, or NULL
|
|
* if we have no such key. */
|
|
crypto_pk_t *
|
|
get_my_v3_authority_signing_key(void)
|
|
{
|
|
return authority_signing_key;
|
|
}
|
|
|
|
/** If we're an authority, and we're using a legacy authority identity key for
|
|
* emergency migration purposes, return the certificate associated with that
|
|
* key. */
|
|
authority_cert_t *
|
|
get_my_v3_legacy_cert(void)
|
|
{
|
|
return legacy_key_certificate;
|
|
}
|
|
|
|
/** If we're an authority, and we're using a legacy authority identity key for
|
|
* emergency migration purposes, return that key. */
|
|
crypto_pk_t *
|
|
get_my_v3_legacy_signing_key(void)
|
|
{
|
|
return legacy_signing_key;
|
|
}
|
|
|
|
/** Replace the previous onion key with the current onion key, and generate
|
|
* a new previous onion key. Immediately after calling this function,
|
|
* the OR should:
|
|
* - schedule all previous cpuworkers to shut down _after_ processing
|
|
* pending work. (This will cause fresh cpuworkers to be generated.)
|
|
* - generate and upload a fresh routerinfo.
|
|
*/
|
|
void
|
|
rotate_onion_key(void)
|
|
{
|
|
char *fname, *fname_prev;
|
|
crypto_pk_t *prkey = NULL;
|
|
or_state_t *state = get_or_state();
|
|
curve25519_keypair_t new_curve25519_keypair;
|
|
time_t now;
|
|
fname = get_keydir_fname("secret_onion_key");
|
|
fname_prev = get_keydir_fname("secret_onion_key.old");
|
|
/* There isn't much point replacing an old key with an empty file */
|
|
if (file_status(fname) == FN_FILE) {
|
|
if (replace_file(fname, fname_prev))
|
|
goto error;
|
|
}
|
|
if (!(prkey = crypto_pk_new())) {
|
|
log_err(LD_GENERAL,"Error constructing rotated onion key");
|
|
goto error;
|
|
}
|
|
if (crypto_pk_generate_key(prkey)) {
|
|
log_err(LD_BUG,"Error generating onion key");
|
|
goto error;
|
|
}
|
|
if (crypto_pk_write_private_key_to_filename(prkey, fname)) {
|
|
log_err(LD_FS,"Couldn't write generated onion key to \"%s\".", fname);
|
|
goto error;
|
|
}
|
|
tor_free(fname);
|
|
tor_free(fname_prev);
|
|
fname = get_keydir_fname("secret_onion_key_ntor");
|
|
fname_prev = get_keydir_fname("secret_onion_key_ntor.old");
|
|
if (curve25519_keypair_generate(&new_curve25519_keypair, 1) < 0)
|
|
goto error;
|
|
/* There isn't much point replacing an old key with an empty file */
|
|
if (file_status(fname) == FN_FILE) {
|
|
if (replace_file(fname, fname_prev))
|
|
goto error;
|
|
}
|
|
if (curve25519_keypair_write_to_file(&new_curve25519_keypair, fname,
|
|
"onion") < 0) {
|
|
log_err(LD_FS,"Couldn't write curve25519 onion key to \"%s\".",fname);
|
|
goto error;
|
|
}
|
|
log_info(LD_GENERAL, "Rotating onion key");
|
|
tor_mutex_acquire(key_lock);
|
|
crypto_pk_free(lastonionkey);
|
|
lastonionkey = onionkey;
|
|
onionkey = prkey;
|
|
memcpy(&last_curve25519_onion_key, &curve25519_onion_key,
|
|
sizeof(curve25519_keypair_t));
|
|
memcpy(&curve25519_onion_key, &new_curve25519_keypair,
|
|
sizeof(curve25519_keypair_t));
|
|
now = time(NULL);
|
|
state->LastRotatedOnionKey = onionkey_set_at = now;
|
|
tor_mutex_release(key_lock);
|
|
mark_my_descriptor_dirty("rotated onion key");
|
|
or_state_mark_dirty(state, get_options()->AvoidDiskWrites ? now+3600 : 0);
|
|
goto done;
|
|
error:
|
|
log_warn(LD_GENERAL, "Couldn't rotate onion key.");
|
|
if (prkey)
|
|
crypto_pk_free(prkey);
|
|
done:
|
|
memwipe(&new_curve25519_keypair, 0, sizeof(new_curve25519_keypair));
|
|
tor_free(fname);
|
|
tor_free(fname_prev);
|
|
}
|
|
|
|
/** Log greeting message that points to new relay lifecycle document the
|
|
* first time this function has been called.
|
|
*/
|
|
static void
|
|
log_new_relay_greeting(void)
|
|
{
|
|
static int already_logged = 0;
|
|
|
|
if (already_logged)
|
|
return;
|
|
|
|
tor_log(LOG_NOTICE, LD_GENERAL, "You are running a new relay. "
|
|
"Thanks for helping the Tor network! If you wish to know "
|
|
"what will happen in the upcoming weeks regarding its usage, "
|
|
"have a look at https://blog.torproject.org/blog/lifecycle-of"
|
|
"-a-new-relay");
|
|
|
|
already_logged = 1;
|
|
}
|
|
|
|
/** Load a curve25519 keypair from the file <b>fname</b>, writing it into
|
|
* <b>keys_out</b>. If the file isn't found, or is empty, and <b>generate</b>
|
|
* is true, create a new keypair and write it into the file. If there are
|
|
* errors, log them at level <b>severity</b>. Generate files using <b>tag</b>
|
|
* in their ASCII wrapper. */
|
|
static int
|
|
init_curve25519_keypair_from_file(curve25519_keypair_t *keys_out,
|
|
const char *fname,
|
|
int generate,
|
|
int severity,
|
|
const char *tag)
|
|
{
|
|
switch (file_status(fname)) {
|
|
case FN_DIR:
|
|
case FN_ERROR:
|
|
tor_log(severity, LD_FS,"Can't read key from \"%s\"", fname);
|
|
goto error;
|
|
/* treat empty key files as if the file doesn't exist, and, if generate
|
|
* is set, replace the empty file in curve25519_keypair_write_to_file() */
|
|
case FN_NOENT:
|
|
case FN_EMPTY:
|
|
if (generate) {
|
|
if (!have_lockfile()) {
|
|
if (try_locking(get_options(), 0)<0) {
|
|
/* Make sure that --list-fingerprint only creates new keys
|
|
* if there is no possibility for a deadlock. */
|
|
tor_log(severity, LD_FS, "Another Tor process has locked \"%s\". "
|
|
"Not writing any new keys.", fname);
|
|
/*XXXX The 'other process' might make a key in a second or two;
|
|
* maybe we should wait for it. */
|
|
goto error;
|
|
}
|
|
}
|
|
log_info(LD_GENERAL, "No key found in \"%s\"; generating fresh key.",
|
|
fname);
|
|
if (curve25519_keypair_generate(keys_out, 1) < 0)
|
|
goto error;
|
|
if (curve25519_keypair_write_to_file(keys_out, fname, tag)<0) {
|
|
tor_log(severity, LD_FS,
|
|
"Couldn't write generated key to \"%s\".", fname);
|
|
memwipe(keys_out, 0, sizeof(*keys_out));
|
|
goto error;
|
|
}
|
|
} else {
|
|
log_info(LD_GENERAL, "No key found in \"%s\"", fname);
|
|
}
|
|
return 0;
|
|
case FN_FILE:
|
|
{
|
|
char *tag_in=NULL;
|
|
if (curve25519_keypair_read_from_file(keys_out, &tag_in, fname) < 0) {
|
|
tor_log(severity, LD_GENERAL,"Error loading private key.");
|
|
tor_free(tag_in);
|
|
goto error;
|
|
}
|
|
if (!tag_in || strcmp(tag_in, tag)) {
|
|
tor_log(severity, LD_GENERAL,"Unexpected tag %s on private key.",
|
|
escaped(tag_in));
|
|
tor_free(tag_in);
|
|
goto error;
|
|
}
|
|
tor_free(tag_in);
|
|
return 0;
|
|
}
|
|
default:
|
|
tor_assert(0);
|
|
}
|
|
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
/** Try to load the vote-signing private key and certificate for being a v3
|
|
* directory authority, and make sure they match. If <b>legacy</b>, load a
|
|
* legacy key/cert set for emergency key migration; otherwise load the regular
|
|
* key/cert set. On success, store them into *<b>key_out</b> and
|
|
* *<b>cert_out</b> respectively, and return 0. On failure, return -1. */
|
|
static int
|
|
load_authority_keyset(int legacy, crypto_pk_t **key_out,
|
|
authority_cert_t **cert_out)
|
|
{
|
|
int r = -1;
|
|
char *fname = NULL, *cert = NULL;
|
|
const char *eos = NULL;
|
|
crypto_pk_t *signing_key = NULL;
|
|
authority_cert_t *parsed = NULL;
|
|
|
|
fname = get_keydir_fname(
|
|
legacy ? "legacy_signing_key" : "authority_signing_key");
|
|
signing_key = init_key_from_file(fname, 0, LOG_ERR, NULL);
|
|
if (!signing_key) {
|
|
log_warn(LD_DIR, "No version 3 directory key found in %s", fname);
|
|
goto done;
|
|
}
|
|
tor_free(fname);
|
|
fname = get_keydir_fname(
|
|
legacy ? "legacy_certificate" : "authority_certificate");
|
|
cert = read_file_to_str(fname, 0, NULL);
|
|
if (!cert) {
|
|
log_warn(LD_DIR, "Signing key found, but no certificate found in %s",
|
|
fname);
|
|
goto done;
|
|
}
|
|
parsed = authority_cert_parse_from_string(cert, strlen(cert), &eos);
|
|
if (!parsed) {
|
|
log_warn(LD_DIR, "Unable to parse certificate in %s", fname);
|
|
goto done;
|
|
}
|
|
if (!crypto_pk_eq_keys(signing_key, parsed->signing_key)) {
|
|
log_warn(LD_DIR, "Stored signing key does not match signing key in "
|
|
"certificate");
|
|
goto done;
|
|
}
|
|
|
|
crypto_pk_free(*key_out);
|
|
authority_cert_free(*cert_out);
|
|
|
|
*key_out = signing_key;
|
|
*cert_out = parsed;
|
|
r = 0;
|
|
signing_key = NULL;
|
|
parsed = NULL;
|
|
|
|
done:
|
|
tor_free(fname);
|
|
tor_free(cert);
|
|
crypto_pk_free(signing_key);
|
|
authority_cert_free(parsed);
|
|
return r;
|
|
}
|
|
|
|
/** Load the v3 (voting) authority signing key and certificate, if they are
|
|
* present. Return -1 if anything is missing, mismatched, or unloadable;
|
|
* return 0 on success. */
|
|
static int
|
|
init_v3_authority_keys(void)
|
|
{
|
|
if (load_authority_keyset(0, &authority_signing_key,
|
|
&authority_key_certificate)<0)
|
|
return -1;
|
|
|
|
if (get_options()->V3AuthUseLegacyKey &&
|
|
load_authority_keyset(1, &legacy_signing_key,
|
|
&legacy_key_certificate)<0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** If we're a v3 authority, check whether we have a certificate that's
|
|
* likely to expire soon. Warn if we do, but not too often. */
|
|
void
|
|
v3_authority_check_key_expiry(void)
|
|
{
|
|
time_t now, expires;
|
|
static time_t last_warned = 0;
|
|
int badness, time_left, warn_interval;
|
|
if (!authdir_mode_v3(get_options()) || !authority_key_certificate)
|
|
return;
|
|
|
|
now = time(NULL);
|
|
expires = authority_key_certificate->expires;
|
|
time_left = (int)( expires - now );
|
|
if (time_left <= 0) {
|
|
badness = LOG_ERR;
|
|
warn_interval = 60*60;
|
|
} else if (time_left <= 24*60*60) {
|
|
badness = LOG_WARN;
|
|
warn_interval = 60*60;
|
|
} else if (time_left <= 24*60*60*7) {
|
|
badness = LOG_WARN;
|
|
warn_interval = 24*60*60;
|
|
} else if (time_left <= 24*60*60*30) {
|
|
badness = LOG_WARN;
|
|
warn_interval = 24*60*60*5;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
if (last_warned + warn_interval > now)
|
|
return;
|
|
|
|
if (time_left <= 0) {
|
|
tor_log(badness, LD_DIR, "Your v3 authority certificate has expired."
|
|
" Generate a new one NOW.");
|
|
} else if (time_left <= 24*60*60) {
|
|
tor_log(badness, LD_DIR, "Your v3 authority certificate expires in %d "
|
|
"hours; Generate a new one NOW.", time_left/(60*60));
|
|
} else {
|
|
tor_log(badness, LD_DIR, "Your v3 authority certificate expires in %d "
|
|
"days; Generate a new one soon.", time_left/(24*60*60));
|
|
}
|
|
last_warned = now;
|
|
}
|
|
|
|
/** Get the lifetime of an onion key in days. This value is defined by the
|
|
* network consensus parameter "onion-key-rotation-days". Always returns a
|
|
* value between <b>MIN_ONION_KEY_LIFETIME_DAYS</b> and
|
|
* <b>MAX_ONION_KEY_LIFETIME_DAYS</b>.
|
|
*/
|
|
static int
|
|
get_onion_key_rotation_days_(void)
|
|
{
|
|
return networkstatus_get_param(NULL,
|
|
"onion-key-rotation-days",
|
|
DEFAULT_ONION_KEY_LIFETIME_DAYS,
|
|
MIN_ONION_KEY_LIFETIME_DAYS,
|
|
MAX_ONION_KEY_LIFETIME_DAYS);
|
|
}
|
|
|
|
/** Get the current lifetime of an onion key in seconds. This value is defined
|
|
* by the network consensus parameter "onion-key-rotation-days", but the value
|
|
* is converted to seconds.
|
|
*/
|
|
int
|
|
get_onion_key_lifetime(void)
|
|
{
|
|
return get_onion_key_rotation_days_()*24*60*60;
|
|
}
|
|
|
|
/** Get the grace period of an onion key in seconds. This value is defined by
|
|
* the network consensus parameter "onion-key-grace-period-days", but the value
|
|
* is converted to seconds.
|
|
*/
|
|
int
|
|
get_onion_key_grace_period(void)
|
|
{
|
|
int grace_period;
|
|
grace_period = networkstatus_get_param(NULL,
|
|
"onion-key-grace-period-days",
|
|
DEFAULT_ONION_KEY_GRACE_PERIOD_DAYS,
|
|
MIN_ONION_KEY_GRACE_PERIOD_DAYS,
|
|
get_onion_key_rotation_days_());
|
|
return grace_period*24*60*60;
|
|
}
|
|
|
|
/** Set up Tor's TLS contexts, based on our configuration and keys. Return 0
|
|
* on success, and -1 on failure. */
|
|
int
|
|
router_initialize_tls_context(void)
|
|
{
|
|
unsigned int flags = 0;
|
|
const or_options_t *options = get_options();
|
|
int lifetime = options->SSLKeyLifetime;
|
|
if (public_server_mode(options))
|
|
flags |= TOR_TLS_CTX_IS_PUBLIC_SERVER;
|
|
if (!lifetime) { /* we should guess a good ssl cert lifetime */
|
|
|
|
/* choose between 5 and 365 days, and round to the day */
|
|
unsigned int five_days = 5*24*3600;
|
|
unsigned int one_year = 365*24*3600;
|
|
lifetime = crypto_rand_int_range(five_days, one_year);
|
|
lifetime -= lifetime % (24*3600);
|
|
|
|
if (crypto_rand_int(2)) {
|
|
/* Half the time we expire at midnight, and half the time we expire
|
|
* one second before midnight. (Some CAs wobble their expiry times a
|
|
* bit in practice, perhaps to reduce collision attacks; see ticket
|
|
* 8443 for details about observed certs in the wild.) */
|
|
lifetime--;
|
|
}
|
|
}
|
|
|
|
/* It's ok to pass lifetime in as an unsigned int, since
|
|
* config_parse_interval() checked it. */
|
|
return tor_tls_context_init(flags,
|
|
get_tlsclient_identity_key(),
|
|
server_mode(options) ?
|
|
get_server_identity_key() : NULL,
|
|
(unsigned int)lifetime);
|
|
}
|
|
|
|
/** Compute fingerprint (or hashed fingerprint if hashed is 1) and write
|
|
* it to 'fingerprint' (or 'hashed-fingerprint'). Return 0 on success, or
|
|
* -1 if Tor should die,
|
|
*/
|
|
STATIC int
|
|
router_write_fingerprint(int hashed)
|
|
{
|
|
char *keydir = NULL, *cp = NULL;
|
|
const char *fname = hashed ? "hashed-fingerprint" :
|
|
"fingerprint";
|
|
char fingerprint[FINGERPRINT_LEN+1];
|
|
const or_options_t *options = get_options();
|
|
char *fingerprint_line = NULL;
|
|
int result = -1;
|
|
|
|
keydir = get_datadir_fname(fname);
|
|
log_info(LD_GENERAL,"Dumping %sfingerprint to \"%s\"...",
|
|
hashed ? "hashed " : "", keydir);
|
|
if (!hashed) {
|
|
if (crypto_pk_get_fingerprint(get_server_identity_key(),
|
|
fingerprint, 0) < 0) {
|
|
log_err(LD_GENERAL,"Error computing fingerprint");
|
|
goto done;
|
|
}
|
|
} else {
|
|
if (crypto_pk_get_hashed_fingerprint(get_server_identity_key(),
|
|
fingerprint) < 0) {
|
|
log_err(LD_GENERAL,"Error computing hashed fingerprint");
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
tor_asprintf(&fingerprint_line, "%s %s\n", options->Nickname, fingerprint);
|
|
|
|
/* Check whether we need to write the (hashed-)fingerprint file. */
|
|
|
|
cp = read_file_to_str(keydir, RFTS_IGNORE_MISSING, NULL);
|
|
if (!cp || strcmp(cp, fingerprint_line)) {
|
|
if (write_str_to_file(keydir, fingerprint_line, 0)) {
|
|
log_err(LD_FS, "Error writing %sfingerprint line to file",
|
|
hashed ? "hashed " : "");
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
log_notice(LD_GENERAL, "Your Tor %s identity key fingerprint is '%s %s'",
|
|
hashed ? "bridge's hashed" : "server's", options->Nickname,
|
|
fingerprint);
|
|
|
|
result = 0;
|
|
done:
|
|
tor_free(cp);
|
|
tor_free(keydir);
|
|
tor_free(fingerprint_line);
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
init_keys_common(void)
|
|
{
|
|
if (!key_lock)
|
|
key_lock = tor_mutex_new();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
init_keys_client(void)
|
|
{
|
|
crypto_pk_t *prkey;
|
|
if (init_keys_common() < 0)
|
|
return -1;
|
|
|
|
if (!(prkey = crypto_pk_new()))
|
|
return -1;
|
|
if (crypto_pk_generate_key(prkey)) {
|
|
crypto_pk_free(prkey);
|
|
return -1;
|
|
}
|
|
set_client_identity_key(prkey);
|
|
/* Create a TLS context. */
|
|
if (router_initialize_tls_context() < 0) {
|
|
log_err(LD_GENERAL,"Error creating TLS context for Tor client.");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/** Initialize all OR private keys, and the TLS context, as necessary.
|
|
* On OPs, this only initializes the tls context. Return 0 on success,
|
|
* or -1 if Tor should die.
|
|
*/
|
|
int
|
|
init_keys(void)
|
|
{
|
|
char *keydir;
|
|
const char *mydesc;
|
|
crypto_pk_t *prkey;
|
|
char digest[DIGEST_LEN];
|
|
char v3_digest[DIGEST_LEN];
|
|
const or_options_t *options = get_options();
|
|
dirinfo_type_t type;
|
|
time_t now = time(NULL);
|
|
dir_server_t *ds;
|
|
int v3_digest_set = 0;
|
|
authority_cert_t *cert = NULL;
|
|
|
|
/* OP's don't need persistent keys; just make up an identity and
|
|
* initialize the TLS context. */
|
|
if (!server_mode(options)) {
|
|
return init_keys_client();
|
|
}
|
|
if (init_keys_common() < 0)
|
|
return -1;
|
|
|
|
if (create_keys_directory(options) < 0)
|
|
return -1;
|
|
|
|
/* 1a. Read v3 directory authority key/cert information. */
|
|
memset(v3_digest, 0, sizeof(v3_digest));
|
|
if (authdir_mode_v3(options)) {
|
|
if (init_v3_authority_keys()<0) {
|
|
log_err(LD_GENERAL, "We're configured as a V3 authority, but we "
|
|
"were unable to load our v3 authority keys and certificate! "
|
|
"Use tor-gencert to generate them. Dying.");
|
|
return -1;
|
|
}
|
|
cert = get_my_v3_authority_cert();
|
|
if (cert) {
|
|
if (crypto_pk_get_digest(get_my_v3_authority_cert()->identity_key,
|
|
v3_digest) < 0) {
|
|
log_err(LD_BUG, "Couldn't compute my v3 authority identity key "
|
|
"digest.");
|
|
return -1;
|
|
}
|
|
v3_digest_set = 1;
|
|
}
|
|
}
|
|
|
|
/* 1b. Read identity key. Make it if none is found. */
|
|
keydir = get_keydir_fname("secret_id_key");
|
|
log_info(LD_GENERAL,"Reading/making identity key \"%s\"...",keydir);
|
|
bool created = false;
|
|
prkey = init_key_from_file(keydir, 1, LOG_ERR, &created);
|
|
tor_free(keydir);
|
|
if (!prkey) return -1;
|
|
if (created)
|
|
log_new_relay_greeting();
|
|
set_server_identity_key(prkey);
|
|
|
|
/* 1c. If we are configured as a bridge, generate a client key;
|
|
* otherwise, set the server identity key as our client identity
|
|
* key. */
|
|
if (public_server_mode(options)) {
|
|
set_client_identity_key(crypto_pk_dup_key(prkey)); /* set above */
|
|
} else {
|
|
if (!(prkey = crypto_pk_new()))
|
|
return -1;
|
|
if (crypto_pk_generate_key(prkey)) {
|
|
crypto_pk_free(prkey);
|
|
return -1;
|
|
}
|
|
set_client_identity_key(prkey);
|
|
}
|
|
|
|
/* 1d. Load all ed25519 keys */
|
|
const int new_signing_key = load_ed_keys(options,now);
|
|
if (new_signing_key < 0)
|
|
return -1;
|
|
|
|
/* 2. Read onion key. Make it if none is found. */
|
|
keydir = get_keydir_fname("secret_onion_key");
|
|
log_info(LD_GENERAL,"Reading/making onion key \"%s\"...",keydir);
|
|
prkey = init_key_from_file(keydir, 1, LOG_ERR, &created);
|
|
if (created)
|
|
log_new_relay_greeting();
|
|
tor_free(keydir);
|
|
if (!prkey) return -1;
|
|
set_onion_key(prkey);
|
|
if (options->command == CMD_RUN_TOR) {
|
|
/* only mess with the state file if we're actually running Tor */
|
|
or_state_t *state = get_or_state();
|
|
if (state->LastRotatedOnionKey > 100 && state->LastRotatedOnionKey < now) {
|
|
/* We allow for some parsing slop, but we don't want to risk accepting
|
|
* values in the distant future. If we did, we might never rotate the
|
|
* onion key. */
|
|
onionkey_set_at = state->LastRotatedOnionKey;
|
|
} else {
|
|
/* We have no LastRotatedOnionKey set; either we just created the key
|
|
* or it's a holdover from 0.1.2.4-alpha-dev or earlier. In either case,
|
|
* start the clock ticking now so that we will eventually rotate it even
|
|
* if we don't stay up for the full lifetime of an onion key. */
|
|
state->LastRotatedOnionKey = onionkey_set_at = now;
|
|
or_state_mark_dirty(state, options->AvoidDiskWrites ?
|
|
time(NULL)+3600 : 0);
|
|
}
|
|
}
|
|
|
|
keydir = get_keydir_fname("secret_onion_key.old");
|
|
if (!lastonionkey && file_status(keydir) == FN_FILE) {
|
|
/* Load keys from non-empty files only.
|
|
* Missing old keys won't be replaced with freshly generated keys. */
|
|
prkey = init_key_from_file(keydir, 0, LOG_ERR, 0);
|
|
if (prkey)
|
|
lastonionkey = prkey;
|
|
}
|
|
tor_free(keydir);
|
|
|
|
{
|
|
/* 2b. Load curve25519 onion keys. */
|
|
int r;
|
|
keydir = get_keydir_fname("secret_onion_key_ntor");
|
|
r = init_curve25519_keypair_from_file(&curve25519_onion_key,
|
|
keydir, 1, LOG_ERR, "onion");
|
|
tor_free(keydir);
|
|
if (r<0)
|
|
return -1;
|
|
|
|
keydir = get_keydir_fname("secret_onion_key_ntor.old");
|
|
if (fast_mem_is_zero((const char *)
|
|
last_curve25519_onion_key.pubkey.public_key,
|
|
CURVE25519_PUBKEY_LEN) &&
|
|
file_status(keydir) == FN_FILE) {
|
|
/* Load keys from non-empty files only.
|
|
* Missing old keys won't be replaced with freshly generated keys. */
|
|
init_curve25519_keypair_from_file(&last_curve25519_onion_key,
|
|
keydir, 0, LOG_ERR, "onion");
|
|
}
|
|
tor_free(keydir);
|
|
}
|
|
|
|
/* 3. Initialize link key and TLS context. */
|
|
if (router_initialize_tls_context() < 0) {
|
|
log_err(LD_GENERAL,"Error initializing TLS context");
|
|
return -1;
|
|
}
|
|
|
|
/* 3b. Get an ed25519 link certificate. Note that we need to do this
|
|
* after we set up the TLS context */
|
|
if (generate_ed_link_cert(options, now, new_signing_key > 0) < 0) {
|
|
log_err(LD_GENERAL,"Couldn't make link cert");
|
|
return -1;
|
|
}
|
|
|
|
/* 4. Build our router descriptor. */
|
|
/* Must be called after keys are initialized. */
|
|
mydesc = router_get_my_descriptor();
|
|
if (authdir_mode_v3(options)) {
|
|
const char *m = NULL;
|
|
routerinfo_t *ri;
|
|
/* We need to add our own fingerprint and ed25519 key so it gets
|
|
* recognized. */
|
|
if (dirserv_add_own_fingerprint(get_server_identity_key(),
|
|
get_master_identity_key())) {
|
|
log_err(LD_GENERAL,"Error adding own fingerprint to set of relays");
|
|
return -1;
|
|
}
|
|
if (mydesc) {
|
|
was_router_added_t added;
|
|
ri = router_parse_entry_from_string(mydesc, NULL, 1, 0, NULL, NULL);
|
|
if (!ri) {
|
|
log_err(LD_GENERAL,"Generated a routerinfo we couldn't parse.");
|
|
return -1;
|
|
}
|
|
added = dirserv_add_descriptor(ri, &m, "self");
|
|
if (!WRA_WAS_ADDED(added)) {
|
|
if (!WRA_WAS_OUTDATED(added)) {
|
|
log_err(LD_GENERAL, "Unable to add own descriptor to directory: %s",
|
|
m?m:"<unknown error>");
|
|
return -1;
|
|
} else {
|
|
/* If the descriptor was outdated, that's ok. This can happen
|
|
* when some config options are toggled that affect workers, but
|
|
* we don't really need new keys yet so the descriptor doesn't
|
|
* change and the old one is still fresh. */
|
|
log_info(LD_GENERAL, "Couldn't add own descriptor to directory "
|
|
"after key init: %s This is usually not a problem.",
|
|
m?m:"<unknown error>");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 5. Dump fingerprint and possibly hashed fingerprint to files. */
|
|
if (router_write_fingerprint(0)) {
|
|
log_err(LD_FS, "Error writing fingerprint to file");
|
|
return -1;
|
|
}
|
|
if (!public_server_mode(options) && router_write_fingerprint(1)) {
|
|
log_err(LD_FS, "Error writing hashed fingerprint to file");
|
|
return -1;
|
|
}
|
|
|
|
if (!authdir_mode(options))
|
|
return 0;
|
|
/* 6. [authdirserver only] load approved-routers file */
|
|
if (dirserv_load_fingerprint_file() < 0) {
|
|
log_err(LD_GENERAL,"Error loading fingerprints");
|
|
return -1;
|
|
}
|
|
/* 6b. [authdirserver only] add own key to approved directories. */
|
|
crypto_pk_get_digest(get_server_identity_key(), digest);
|
|
type = ((options->V3AuthoritativeDir ?
|
|
(V3_DIRINFO|MICRODESC_DIRINFO|EXTRAINFO_DIRINFO) : NO_DIRINFO) |
|
|
(options->BridgeAuthoritativeDir ? BRIDGE_DIRINFO : NO_DIRINFO));
|
|
|
|
ds = router_get_trusteddirserver_by_digest(digest);
|
|
if (!ds) {
|
|
ds = trusted_dir_server_new(options->Nickname, NULL,
|
|
router_get_advertised_dir_port(options, 0),
|
|
router_get_advertised_or_port(options),
|
|
NULL,
|
|
digest,
|
|
v3_digest,
|
|
type, 0.0);
|
|
if (!ds) {
|
|
log_err(LD_GENERAL,"We want to be a directory authority, but we "
|
|
"couldn't add ourselves to the authority list. Failing.");
|
|
return -1;
|
|
}
|
|
dir_server_add(ds);
|
|
}
|
|
if (ds->type != type) {
|
|
log_warn(LD_DIR, "Configured authority type does not match authority "
|
|
"type in DirAuthority list. Adjusting. (%d v %d)",
|
|
type, ds->type);
|
|
ds->type = type;
|
|
}
|
|
if (v3_digest_set && (ds->type & V3_DIRINFO) &&
|
|
tor_memneq(v3_digest, ds->v3_identity_digest, DIGEST_LEN)) {
|
|
log_warn(LD_DIR, "V3 identity key does not match identity declared in "
|
|
"DirAuthority line. Adjusting.");
|
|
memcpy(ds->v3_identity_digest, v3_digest, DIGEST_LEN);
|
|
}
|
|
|
|
if (cert) { /* add my own cert to the list of known certs */
|
|
log_info(LD_DIR, "adding my own v3 cert");
|
|
if (trusted_dirs_load_certs_from_string(
|
|
cert->cache_info.signed_descriptor_body,
|
|
TRUSTED_DIRS_CERTS_SRC_SELF, 0,
|
|
NULL)<0) {
|
|
log_warn(LD_DIR, "Unable to parse my own v3 cert! Failing.");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0; /* success */
|
|
}
|
|
|
|
/** The lower threshold of remaining bandwidth required to advertise (or
|
|
* automatically provide) directory services */
|
|
/* XXX Should this be increased? */
|
|
#define MIN_BW_TO_ADVERTISE_DIRSERVER 51200
|
|
|
|
/** Return true iff we have enough configured bandwidth to advertise or
|
|
* automatically provide directory services from cache directory
|
|
* information. */
|
|
int
|
|
router_has_bandwidth_to_be_dirserver(const or_options_t *options)
|
|
{
|
|
if (options->BandwidthRate < MIN_BW_TO_ADVERTISE_DIRSERVER) {
|
|
return 0;
|
|
}
|
|
if (options->RelayBandwidthRate > 0 &&
|
|
options->RelayBandwidthRate < MIN_BW_TO_ADVERTISE_DIRSERVER) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/** Helper: Return 1 if we have sufficient resources for serving directory
|
|
* requests, return 0 otherwise.
|
|
* dir_port is either 0 or the configured DirPort number.
|
|
* If AccountingMax is set less than our advertised bandwidth, then don't
|
|
* serve requests. Likewise, if our advertised bandwidth is less than
|
|
* MIN_BW_TO_ADVERTISE_DIRSERVER, don't bother trying to serve requests.
|
|
*/
|
|
static int
|
|
router_should_be_dirserver(const or_options_t *options, int dir_port)
|
|
{
|
|
static int advertising=1; /* start out assuming we will advertise */
|
|
int new_choice=1;
|
|
const char *reason = NULL;
|
|
|
|
if (accounting_is_enabled(options) &&
|
|
get_options()->AccountingRule != ACCT_IN) {
|
|
/* Don't spend bytes for directory traffic if we could end up hibernating,
|
|
* but allow DirPort otherwise. Some relay operators set AccountingMax
|
|
* because they're confused or to get statistics. Directory traffic has a
|
|
* much larger effect on output than input so there is no reason to turn it
|
|
* off if using AccountingRule in. */
|
|
int interval_length = accounting_get_interval_length();
|
|
uint32_t effective_bw = relay_get_effective_bwrate(options);
|
|
uint64_t acc_bytes;
|
|
if (!interval_length) {
|
|
log_warn(LD_BUG, "An accounting interval is not allowed to be zero "
|
|
"seconds long. Raising to 1.");
|
|
interval_length = 1;
|
|
}
|
|
log_info(LD_GENERAL, "Calculating whether to advertise %s: effective "
|
|
"bwrate: %u, AccountingMax: %"PRIu64", "
|
|
"accounting interval length %d",
|
|
dir_port ? "dirport" : "begindir",
|
|
effective_bw, (options->AccountingMax),
|
|
interval_length);
|
|
|
|
acc_bytes = options->AccountingMax;
|
|
if (get_options()->AccountingRule == ACCT_SUM)
|
|
acc_bytes /= 2;
|
|
if (effective_bw >=
|
|
acc_bytes / interval_length) {
|
|
new_choice = 0;
|
|
reason = "AccountingMax enabled";
|
|
}
|
|
} else if (! router_has_bandwidth_to_be_dirserver(options)) {
|
|
/* if we're advertising a small amount */
|
|
new_choice = 0;
|
|
reason = "BandwidthRate under 50KB";
|
|
}
|
|
|
|
if (advertising != new_choice) {
|
|
if (new_choice == 1) {
|
|
if (dir_port > 0)
|
|
log_notice(LD_DIR, "Advertising DirPort as %d", dir_port);
|
|
else
|
|
log_notice(LD_DIR, "Advertising directory service support");
|
|
} else {
|
|
tor_assert(reason);
|
|
log_notice(LD_DIR, "Not advertising Dir%s (Reason: %s)",
|
|
dir_port ? "Port" : "ectory Service support", reason);
|
|
}
|
|
advertising = new_choice;
|
|
}
|
|
|
|
return advertising;
|
|
}
|
|
|
|
/** Look at a variety of factors, and return 0 if we don't want to
|
|
* advertise the fact that we have a DirPort open or begindir support, else
|
|
* return 1.
|
|
*
|
|
* Where dir_port or supports_tunnelled_dir_requests are not relevant, they
|
|
* must be 0.
|
|
*
|
|
* Log a helpful message if we change our mind about whether to publish.
|
|
*/
|
|
static int
|
|
decide_to_advertise_dir_impl(const or_options_t *options,
|
|
uint16_t dir_port,
|
|
int supports_tunnelled_dir_requests)
|
|
{
|
|
/* Part one: reasons to publish or not publish that aren't
|
|
* worth mentioning to the user, either because they're obvious
|
|
* or because they're normal behavior. */
|
|
|
|
/* short circuit the rest of the function */
|
|
if (!dir_port && !supports_tunnelled_dir_requests)
|
|
return 0;
|
|
if (authdir_mode(options)) /* always publish */
|
|
return 1;
|
|
if (net_is_disabled())
|
|
return 0;
|
|
if (dir_port && !router_get_advertised_dir_port(options, dir_port))
|
|
return 0;
|
|
if (supports_tunnelled_dir_requests &&
|
|
!router_get_advertised_or_port(options))
|
|
return 0;
|
|
|
|
/* Part two: consider config options that could make us choose to
|
|
* publish or not publish that the user might find surprising. */
|
|
return router_should_be_dirserver(options, dir_port);
|
|
}
|
|
|
|
/** Front-end to decide_to_advertise_dir_impl(): return 0 if we don't want to
|
|
* advertise the fact that we have a DirPort open, else return the
|
|
* DirPort we want to advertise.
|
|
*/
|
|
int
|
|
router_should_advertise_dirport(const or_options_t *options, uint16_t dir_port)
|
|
{
|
|
/* supports_tunnelled_dir_requests is not relevant, pass 0 */
|
|
return decide_to_advertise_dir_impl(options, dir_port, 0) ? dir_port : 0;
|
|
}
|
|
|
|
/** Front-end to decide_to_advertise_dir_impl(): return 0 if we don't want to
|
|
* advertise the fact that we support begindir requests, else return 1.
|
|
*/
|
|
static int
|
|
router_should_advertise_begindir(const or_options_t *options,
|
|
int supports_tunnelled_dir_requests)
|
|
{
|
|
/* dir_port is not relevant, pass 0 */
|
|
return decide_to_advertise_dir_impl(options, 0,
|
|
supports_tunnelled_dir_requests);
|
|
}
|
|
|
|
/** Return true iff the combination of options in <b>options</b> and parameters
|
|
* in the consensus mean that we don't want to allow exits from circuits
|
|
* we got from addresses not known to be servers. */
|
|
int
|
|
should_refuse_unknown_exits(const or_options_t *options)
|
|
{
|
|
if (options->RefuseUnknownExits != -1) {
|
|
return options->RefuseUnknownExits;
|
|
} else {
|
|
return networkstatus_get_param(NULL, "refuseunknownexits", 1, 0, 1);
|
|
}
|
|
}
|
|
|
|
/** Decide if we're a publishable server. We are a publishable server if:
|
|
* - We don't have the ClientOnly option set
|
|
* and
|
|
* - We have the PublishServerDescriptor option set to non-empty
|
|
* and
|
|
* - We have ORPort set
|
|
* and
|
|
* - We believe our ORPort and DirPort (if present) are reachable from
|
|
* the outside; or
|
|
* - We believe our ORPort is reachable from the outside, and we can't
|
|
* check our DirPort because the consensus has no exits; or
|
|
* - We are an authoritative directory server.
|
|
*/
|
|
static int
|
|
decide_if_publishable_server(void)
|
|
{
|
|
const or_options_t *options = get_options();
|
|
|
|
if (options->ClientOnly)
|
|
return 0;
|
|
if (options->PublishServerDescriptor_ == NO_DIRINFO)
|
|
return 0;
|
|
if (!server_mode(options))
|
|
return 0;
|
|
if (authdir_mode(options))
|
|
return 1;
|
|
if (!router_get_advertised_or_port(options))
|
|
return 0;
|
|
if (!check_whether_orport_reachable(options))
|
|
return 0;
|
|
if (router_have_consensus_path() == CONSENSUS_PATH_INTERNAL) {
|
|
/* All set: there are no exits in the consensus (maybe this is a tiny
|
|
* test network), so we can't check our DirPort reachability. */
|
|
return 1;
|
|
} else {
|
|
return check_whether_dirport_reachable(options);
|
|
}
|
|
}
|
|
|
|
/** Initiate server descriptor upload as reasonable (if server is publishable,
|
|
* etc). <b>force</b> is as for router_upload_dir_desc_to_dirservers.
|
|
*
|
|
* We need to rebuild the descriptor if it's dirty even if we're not
|
|
* uploading, because our reachability testing *uses* our descriptor to
|
|
* determine what IP address and ports to test.
|
|
*/
|
|
void
|
|
consider_publishable_server(int force)
|
|
{
|
|
int rebuilt;
|
|
|
|
if (!server_mode(get_options()))
|
|
return;
|
|
|
|
rebuilt = router_rebuild_descriptor(0);
|
|
if (decide_if_publishable_server()) {
|
|
set_server_advertised(1);
|
|
if (rebuilt == 0)
|
|
router_upload_dir_desc_to_dirservers(force);
|
|
} else {
|
|
set_server_advertised(0);
|
|
}
|
|
}
|
|
|
|
/** Return the port of the first active listener of type
|
|
* <b>listener_type</b>. Returns 0 if no port is found. */
|
|
/** XXX not a very good interface. it's not reliable when there are
|
|
multiple listeners. */
|
|
uint16_t
|
|
router_get_active_listener_port_by_type_af(int listener_type,
|
|
sa_family_t family)
|
|
{
|
|
/* Iterate all connections, find one of the right kind and return
|
|
the port. Not very sophisticated or fast, but effective. */
|
|
smartlist_t *conns = get_connection_array();
|
|
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
|
|
if (conn->type == listener_type && !conn->marked_for_close &&
|
|
conn->socket_family == family) {
|
|
return conn->port;
|
|
}
|
|
} SMARTLIST_FOREACH_END(conn);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** Return the port that we should advertise as our ORPort; this is either
|
|
* the one configured in the ORPort option, or the one we actually bound to
|
|
* if ORPort is "auto". Returns 0 if no port is found. */
|
|
uint16_t
|
|
router_get_advertised_or_port(const or_options_t *options)
|
|
{
|
|
return router_get_advertised_or_port_by_af(options, AF_INET);
|
|
}
|
|
|
|
/** As router_get_advertised_or_port(), but allows an address family argument.
|
|
*/
|
|
uint16_t
|
|
router_get_advertised_or_port_by_af(const or_options_t *options,
|
|
sa_family_t family)
|
|
{
|
|
int port = get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER,
|
|
family);
|
|
(void)options;
|
|
|
|
/* If the port is in 'auto' mode, we have to use
|
|
router_get_listener_port_by_type(). */
|
|
if (port == CFG_AUTO_PORT)
|
|
return router_get_active_listener_port_by_type_af(CONN_TYPE_OR_LISTENER,
|
|
family);
|
|
|
|
return port;
|
|
}
|
|
|
|
/** As router_get_advertised_or_port(), but returns the IPv6 address and
|
|
* port in ipv6_ap_out, which must not be NULL. Returns a null address and
|
|
* zero port, if no ORPort is found. */
|
|
void
|
|
router_get_advertised_ipv6_or_ap(const or_options_t *options,
|
|
tor_addr_port_t *ipv6_ap_out)
|
|
{
|
|
/* Bug in calling function, we can't return a sensible result, and it
|
|
* shouldn't use the NULL pointer once we return. */
|
|
tor_assert(ipv6_ap_out);
|
|
|
|
/* If there is no valid IPv6 ORPort, return a null address and port. */
|
|
tor_addr_make_null(&ipv6_ap_out->addr, AF_INET6);
|
|
ipv6_ap_out->port = 0;
|
|
|
|
const tor_addr_t *addr = get_first_advertised_addr_by_type_af(
|
|
CONN_TYPE_OR_LISTENER,
|
|
AF_INET6);
|
|
const uint16_t port = router_get_advertised_or_port_by_af(
|
|
options,
|
|
AF_INET6);
|
|
|
|
if (!addr || port == 0) {
|
|
log_debug(LD_CONFIG, "There is no advertised IPv6 ORPort.");
|
|
return;
|
|
}
|
|
|
|
/* If the relay is configured using the default authorities, disallow
|
|
* internal IPs. Otherwise, allow them. For IPv4 ORPorts and DirPorts,
|
|
* this check is done in resolve_my_address(). See #33681. */
|
|
const int default_auth = using_default_dir_authorities(options);
|
|
if (tor_addr_is_internal(addr, 0) && default_auth) {
|
|
log_warn(LD_CONFIG,
|
|
"Unable to use configured IPv6 ORPort \"%s\" in a "
|
|
"descriptor. Skipping it. "
|
|
"Try specifying a globally reachable address explicitly.",
|
|
fmt_addrport(addr, port));
|
|
return;
|
|
}
|
|
|
|
tor_addr_copy(&ipv6_ap_out->addr, addr);
|
|
ipv6_ap_out->port = port;
|
|
}
|
|
|
|
/** Returns true if this router has an advertised IPv6 ORPort. */
|
|
bool
|
|
router_has_advertised_ipv6_orport(const or_options_t *options)
|
|
{
|
|
tor_addr_port_t ipv6_ap;
|
|
router_get_advertised_ipv6_or_ap(options, &ipv6_ap);
|
|
return tor_addr_port_is_valid_ap(&ipv6_ap, 0);
|
|
}
|
|
|
|
/** Returns true if this router has an advertised IPv6 ORPort. */
|
|
MOCK_IMPL(bool,
|
|
router_can_extend_over_ipv6,(const or_options_t *options))
|
|
{
|
|
/* We might add some extra checks here, such as ExtendAllowIPv6Addresses
|
|
* from ticket 33818. */
|
|
return router_has_advertised_ipv6_orport(options);
|
|
}
|
|
|
|
/** Return the port that we should advertise as our DirPort;
|
|
* this is one of three possibilities:
|
|
* The one that is passed as <b>dirport</b> if the DirPort option is 0, or
|
|
* the one configured in the DirPort option,
|
|
* or the one we actually bound to if DirPort is "auto". */
|
|
uint16_t
|
|
router_get_advertised_dir_port(const or_options_t *options, uint16_t dirport)
|
|
{
|
|
int dirport_configured = get_primary_dir_port();
|
|
(void)options;
|
|
|
|
if (!dirport_configured)
|
|
return dirport;
|
|
|
|
if (dirport_configured == CFG_AUTO_PORT)
|
|
return router_get_active_listener_port_by_type_af(CONN_TYPE_DIR_LISTENER,
|
|
AF_INET);
|
|
|
|
return dirport_configured;
|
|
}
|
|
|
|
/*
|
|
* OR descriptor generation.
|
|
*/
|
|
|
|
/** My routerinfo. */
|
|
static routerinfo_t *desc_routerinfo = NULL;
|
|
/** My extrainfo */
|
|
static extrainfo_t *desc_extrainfo = NULL;
|
|
/** Why did we most recently decide to regenerate our descriptor? Used to
|
|
* tell the authorities why we're sending it to them. */
|
|
static const char *desc_gen_reason = "uninitialized reason";
|
|
/** Since when has our descriptor been "clean"? 0 if we need to regenerate it
|
|
* now. */
|
|
STATIC time_t desc_clean_since = 0;
|
|
/** Why did we mark the descriptor dirty? */
|
|
STATIC const char *desc_dirty_reason = "Tor just started";
|
|
/** Boolean: do we need to regenerate the above? */
|
|
static int desc_needs_upload = 0;
|
|
|
|
/** OR only: If <b>force</b> is true, or we haven't uploaded this
|
|
* descriptor successfully yet, try to upload our signed descriptor to
|
|
* all the directory servers we know about.
|
|
*/
|
|
void
|
|
router_upload_dir_desc_to_dirservers(int force)
|
|
{
|
|
const routerinfo_t *ri;
|
|
extrainfo_t *ei;
|
|
char *msg;
|
|
size_t desc_len, extra_len = 0, total_len;
|
|
dirinfo_type_t auth = get_options()->PublishServerDescriptor_;
|
|
|
|
ri = router_get_my_routerinfo();
|
|
if (!ri) {
|
|
log_info(LD_GENERAL, "No descriptor; skipping upload");
|
|
return;
|
|
}
|
|
ei = router_get_my_extrainfo();
|
|
if (auth == NO_DIRINFO)
|
|
return;
|
|
if (!force && !desc_needs_upload)
|
|
return;
|
|
|
|
log_info(LD_OR, "Uploading relay descriptor to directory authorities%s",
|
|
force ? " (forced)" : "");
|
|
|
|
desc_needs_upload = 0;
|
|
|
|
desc_len = ri->cache_info.signed_descriptor_len;
|
|
extra_len = ei ? ei->cache_info.signed_descriptor_len : 0;
|
|
total_len = desc_len + extra_len + 1;
|
|
msg = tor_malloc(total_len);
|
|
memcpy(msg, ri->cache_info.signed_descriptor_body, desc_len);
|
|
if (ei) {
|
|
memcpy(msg+desc_len, ei->cache_info.signed_descriptor_body, extra_len);
|
|
}
|
|
msg[desc_len+extra_len] = 0;
|
|
|
|
directory_post_to_dirservers(DIR_PURPOSE_UPLOAD_DIR,
|
|
(auth & BRIDGE_DIRINFO) ?
|
|
ROUTER_PURPOSE_BRIDGE :
|
|
ROUTER_PURPOSE_GENERAL,
|
|
auth, msg, desc_len, extra_len);
|
|
tor_free(msg);
|
|
}
|
|
|
|
/** OR only: Check whether my exit policy says to allow connection to
|
|
* conn. Return 0 if we accept; non-0 if we reject.
|
|
*/
|
|
int
|
|
router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port)
|
|
{
|
|
const routerinfo_t *me = router_get_my_routerinfo();
|
|
if (!me) /* make sure routerinfo exists */
|
|
return -1;
|
|
|
|
/* make sure it's resolved to something. this way we can't get a
|
|
'maybe' below. */
|
|
if (tor_addr_is_null(addr))
|
|
return -1;
|
|
|
|
/* look at router_get_my_routerinfo()->exit_policy for both the v4 and the
|
|
* v6 policies. The exit_policy field in router_get_my_routerinfo() is a
|
|
* bit unusual, in that it contains IPv6 and IPv6 entries. We don't want to
|
|
* look at router_get_my_routerinfo()->ipv6_exit_policy, since that's a port
|
|
* summary. */
|
|
if ((tor_addr_family(addr) == AF_INET ||
|
|
tor_addr_family(addr) == AF_INET6)) {
|
|
return compare_tor_addr_to_addr_policy(addr, port,
|
|
me->exit_policy) != ADDR_POLICY_ACCEPTED;
|
|
#if 0
|
|
} else if (tor_addr_family(addr) == AF_INET6) {
|
|
return get_options()->IPv6Exit &&
|
|
desc_routerinfo->ipv6_exit_policy &&
|
|
compare_tor_addr_to_short_policy(addr, port,
|
|
me->ipv6_exit_policy) != ADDR_POLICY_ACCEPTED;
|
|
#endif /* 0 */
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/** Return true iff my exit policy is reject *:*. Return -1 if we don't
|
|
* have a descriptor */
|
|
MOCK_IMPL(int,
|
|
router_my_exit_policy_is_reject_star,(void))
|
|
{
|
|
const routerinfo_t *me = router_get_my_routerinfo();
|
|
if (!me) /* make sure routerinfo exists */
|
|
return -1;
|
|
|
|
return me->policy_is_reject_star;
|
|
}
|
|
|
|
/** Return true iff I'm a server and <b>digest</b> is equal to
|
|
* my server identity key digest. */
|
|
int
|
|
router_digest_is_me(const char *digest)
|
|
{
|
|
return (server_identitykey &&
|
|
tor_memeq(server_identitykey_digest, digest, DIGEST_LEN));
|
|
}
|
|
|
|
/** Return my identity digest. */
|
|
const uint8_t *
|
|
router_get_my_id_digest(void)
|
|
{
|
|
return (const uint8_t *)server_identitykey_digest;
|
|
}
|
|
|
|
/** Return true iff I'm a server and <b>digest</b> is equal to
|
|
* my identity digest. */
|
|
int
|
|
router_extrainfo_digest_is_me(const char *digest)
|
|
{
|
|
extrainfo_t *ei = router_get_my_extrainfo();
|
|
if (!ei)
|
|
return 0;
|
|
|
|
return tor_memeq(digest,
|
|
ei->cache_info.signed_descriptor_digest,
|
|
DIGEST_LEN);
|
|
}
|
|
|
|
/** A wrapper around router_digest_is_me(). */
|
|
int
|
|
router_is_me(const routerinfo_t *router)
|
|
{
|
|
return router_digest_is_me(router->cache_info.identity_digest);
|
|
}
|
|
|
|
/** Return a routerinfo for this OR, rebuilding a fresh one if
|
|
* necessary. Return NULL on error, or if called on an OP. */
|
|
MOCK_IMPL(const routerinfo_t *,
|
|
router_get_my_routerinfo,(void))
|
|
{
|
|
return router_get_my_routerinfo_with_err(NULL);
|
|
}
|
|
|
|
/** Return routerinfo of this OR. Rebuild it from
|
|
* scratch if needed. Set <b>*err</b> to 0 on success or to
|
|
* appropriate TOR_ROUTERINFO_ERROR_* value on failure.
|
|
*/
|
|
MOCK_IMPL(const routerinfo_t *,
|
|
router_get_my_routerinfo_with_err,(int *err))
|
|
{
|
|
if (!server_mode(get_options())) {
|
|
if (err)
|
|
*err = TOR_ROUTERINFO_ERROR_NOT_A_SERVER;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if (!desc_clean_since) {
|
|
int rebuild_err = router_rebuild_descriptor(0);
|
|
if (rebuild_err < 0) {
|
|
if (err)
|
|
*err = rebuild_err;
|
|
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (!desc_routerinfo) {
|
|
if (err)
|
|
*err = TOR_ROUTERINFO_ERROR_DESC_REBUILDING;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if (err)
|
|
*err = 0;
|
|
|
|
return desc_routerinfo;
|
|
}
|
|
|
|
/** OR only: Return a signed server descriptor for this OR, rebuilding a fresh
|
|
* one if necessary. Return NULL on error.
|
|
*/
|
|
const char *
|
|
router_get_my_descriptor(void)
|
|
{
|
|
const char *body;
|
|
const routerinfo_t *me = router_get_my_routerinfo();
|
|
if (! me)
|
|
return NULL;
|
|
tor_assert(me->cache_info.saved_location == SAVED_NOWHERE);
|
|
body = signed_descriptor_get_body(&me->cache_info);
|
|
/* Make sure this is nul-terminated. */
|
|
tor_assert(!body[me->cache_info.signed_descriptor_len]);
|
|
log_debug(LD_GENERAL,"my desc is '%s'", body);
|
|
return body;
|
|
}
|
|
|
|
/** Return the extrainfo document for this OR, or NULL if we have none.
|
|
* Rebuilt it (and the server descriptor) if necessary. */
|
|
extrainfo_t *
|
|
router_get_my_extrainfo(void)
|
|
{
|
|
if (!server_mode(get_options()))
|
|
return NULL;
|
|
if (router_rebuild_descriptor(0))
|
|
return NULL;
|
|
return desc_extrainfo;
|
|
}
|
|
|
|
/** Return a human-readable string describing what triggered us to generate
|
|
* our current descriptor, or NULL if we don't know. */
|
|
const char *
|
|
router_get_descriptor_gen_reason(void)
|
|
{
|
|
return desc_gen_reason;
|
|
}
|
|
|
|
static int router_guess_address_from_dir_headers(uint32_t *guess);
|
|
|
|
/** Make a current best guess at our address, either because
|
|
* it's configured in torrc, or because we've learned it from
|
|
* dirserver headers. Place the answer in *<b>addr</b> and return
|
|
* 0 on success, else return -1 if we have no guess.
|
|
*
|
|
* If <b>cache_only</b> is true, just return any cached answers, and
|
|
* don't try to get any new answers.
|
|
*/
|
|
MOCK_IMPL(int,
|
|
router_pick_published_address,(const or_options_t *options, uint32_t *addr,
|
|
int cache_only))
|
|
{
|
|
/* First, check the cached output from resolve_my_address(). */
|
|
*addr = get_last_resolved_addr();
|
|
if (*addr)
|
|
return 0;
|
|
|
|
/* Second, consider doing a resolve attempt right here. */
|
|
if (!cache_only) {
|
|
if (resolve_my_address(LOG_INFO, options, addr, NULL, NULL) >= 0) {
|
|
log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr32(*addr));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Third, check the cached output from router_new_address_suggestion(). */
|
|
if (router_guess_address_from_dir_headers(addr) >= 0)
|
|
return 0;
|
|
|
|
/* We have no useful cached answers. Return failure. */
|
|
return -1;
|
|
}
|
|
|
|
/* Like router_check_descriptor_address_consistency, but specifically for the
|
|
* ORPort or DirPort.
|
|
* listener_type is either CONN_TYPE_OR_LISTENER or CONN_TYPE_DIR_LISTENER. */
|
|
static void
|
|
router_check_descriptor_address_port_consistency(uint32_t ipv4h_desc_addr,
|
|
int listener_type)
|
|
{
|
|
tor_assert(listener_type == CONN_TYPE_OR_LISTENER ||
|
|
listener_type == CONN_TYPE_DIR_LISTENER);
|
|
|
|
/* The first advertised Port may be the magic constant CFG_AUTO_PORT.
|
|
*/
|
|
int port_v4_cfg = get_first_advertised_port_by_type_af(listener_type,
|
|
AF_INET);
|
|
if (port_v4_cfg != 0 &&
|
|
!port_exists_by_type_addr32h_port(listener_type,
|
|
ipv4h_desc_addr, port_v4_cfg, 1)) {
|
|
const tor_addr_t *port_addr = get_first_advertised_addr_by_type_af(
|
|
listener_type,
|
|
AF_INET);
|
|
/* If we're building a descriptor with no advertised address,
|
|
* something is terribly wrong. */
|
|
tor_assert(port_addr);
|
|
|
|
tor_addr_t desc_addr;
|
|
char port_addr_str[TOR_ADDR_BUF_LEN];
|
|
char desc_addr_str[TOR_ADDR_BUF_LEN];
|
|
|
|
tor_addr_to_str(port_addr_str, port_addr, TOR_ADDR_BUF_LEN, 0);
|
|
|
|
tor_addr_from_ipv4h(&desc_addr, ipv4h_desc_addr);
|
|
tor_addr_to_str(desc_addr_str, &desc_addr, TOR_ADDR_BUF_LEN, 0);
|
|
|
|
const char *listener_str = (listener_type == CONN_TYPE_OR_LISTENER ?
|
|
"OR" : "Dir");
|
|
log_warn(LD_CONFIG, "The IPv4 %sPort address %s does not match the "
|
|
"descriptor address %s. If you have a static public IPv4 "
|
|
"address, use 'Address <IPv4>' and 'OutboundBindAddress "
|
|
"<IPv4>'. If you are behind a NAT, use two %sPort lines: "
|
|
"'%sPort <PublicPort> NoListen' and '%sPort <InternalPort> "
|
|
"NoAdvertise'.",
|
|
listener_str, port_addr_str, desc_addr_str, listener_str,
|
|
listener_str, listener_str);
|
|
}
|
|
}
|
|
|
|
/* Tor relays only have one IPv4 address in the descriptor, which is derived
|
|
* from the Address torrc option, or guessed using various methods in
|
|
* router_pick_published_address().
|
|
* Warn the operator if there is no ORPort on the descriptor address
|
|
* ipv4h_desc_addr.
|
|
* Warn the operator if there is no DirPort on the descriptor address.
|
|
* This catches a few common config errors:
|
|
* - operators who expect ORPorts and DirPorts to be advertised on the
|
|
* ports' listen addresses, rather than the torrc Address (or guessed
|
|
* addresses in the absence of an Address config). This includes
|
|
* operators who attempt to put their ORPort and DirPort on different
|
|
* addresses;
|
|
* - discrepancies between guessed addresses and configured listen
|
|
* addresses (when the Address option isn't set).
|
|
* If a listener is listening on all IPv4 addresses, it is assumed that it
|
|
* is listening on the configured Address, and no messages are logged.
|
|
* If an operators has specified NoAdvertise ORPorts in a NAT setting,
|
|
* no messages are logged, unless they have specified other advertised
|
|
* addresses.
|
|
* The message tells operators to configure an ORPort and DirPort that match
|
|
* the Address (using NoListen if needed).
|
|
*/
|
|
static void
|
|
router_check_descriptor_address_consistency(uint32_t ipv4h_desc_addr)
|
|
{
|
|
router_check_descriptor_address_port_consistency(ipv4h_desc_addr,
|
|
CONN_TYPE_OR_LISTENER);
|
|
router_check_descriptor_address_port_consistency(ipv4h_desc_addr,
|
|
CONN_TYPE_DIR_LISTENER);
|
|
}
|
|
|
|
/** A list of nicknames that we've warned about including in our family,
|
|
* for one reason or another. */
|
|
static smartlist_t *warned_family = NULL;
|
|
|
|
/**
|
|
* Return a new smartlist containing the family members configured in
|
|
* <b>options</b>. Warn about invalid or missing entries. Return NULL
|
|
* if this relay should not declare a family.
|
|
**/
|
|
STATIC smartlist_t *
|
|
get_my_declared_family(const or_options_t *options)
|
|
{
|
|
if (!options->MyFamily)
|
|
return NULL;
|
|
|
|
if (options->BridgeRelay)
|
|
return NULL;
|
|
|
|
if (!warned_family)
|
|
warned_family = smartlist_new();
|
|
|
|
smartlist_t *declared_family = smartlist_new();
|
|
config_line_t *family;
|
|
|
|
/* First we try to get the whole family in the form of hexdigests. */
|
|
for (family = options->MyFamily; family; family = family->next) {
|
|
char *name = family->value;
|
|
const node_t *member;
|
|
if (options->Nickname && !strcasecmp(name, options->Nickname))
|
|
continue; /* Don't list ourself by nickname, that's redundant */
|
|
else
|
|
member = node_get_by_nickname(name, 0);
|
|
|
|
if (!member) {
|
|
/* This node doesn't seem to exist, so warn about it if it is not
|
|
* a hexdigest. */
|
|
int is_legal = is_legal_nickname_or_hexdigest(name);
|
|
if (!smartlist_contains_string(warned_family, name) &&
|
|
!is_legal_hexdigest(name)) {
|
|
if (is_legal)
|
|
log_warn(LD_CONFIG,
|
|
"There is a router named %s in my declared family, but "
|
|
"I have no descriptor for it. I'll use the nickname "
|
|
"as is, but this may confuse clients. Please list it "
|
|
"by identity digest instead.", escaped(name));
|
|
else
|
|
log_warn(LD_CONFIG, "There is a router named %s in my declared "
|
|
"family, but that isn't a legal digest or nickname. "
|
|
"Skipping it.", escaped(name));
|
|
smartlist_add_strdup(warned_family, name);
|
|
}
|
|
if (is_legal) {
|
|
smartlist_add_strdup(declared_family, name);
|
|
}
|
|
} else {
|
|
/* List the node by digest. */
|
|
char *fp = tor_malloc(HEX_DIGEST_LEN+2);
|
|
fp[0] = '$';
|
|
base16_encode(fp+1,HEX_DIGEST_LEN+1,
|
|
member->identity, DIGEST_LEN);
|
|
smartlist_add(declared_family, fp);
|
|
|
|
if (! is_legal_hexdigest(name) &&
|
|
!smartlist_contains_string(warned_family, name)) {
|
|
/* Warn if this node was not specified by hexdigest. */
|
|
log_warn(LD_CONFIG, "There is a router named %s in my declared "
|
|
"family, but it wasn't listed by digest. Please consider "
|
|
"saying %s instead, if that's what you meant.",
|
|
escaped(name), fp);
|
|
smartlist_add_strdup(warned_family, name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Now declared_family should have the closest we can come to the
|
|
* identities that the user wanted.
|
|
*
|
|
* Unlike older versions of Tor, we _do_ include our own identity: this
|
|
* helps microdescriptor compression, and helps in-memory compression
|
|
* on clients. */
|
|
nodefamily_t *nf = nodefamily_from_members(declared_family,
|
|
router_get_my_id_digest(),
|
|
NF_WARN_MALFORMED,
|
|
NULL);
|
|
SMARTLIST_FOREACH(declared_family, char *, s, tor_free(s));
|
|
smartlist_free(declared_family);
|
|
if (!nf) {
|
|
return NULL;
|
|
}
|
|
|
|
char *s = nodefamily_format(nf);
|
|
nodefamily_free(nf);
|
|
|
|
smartlist_t *result = smartlist_new();
|
|
smartlist_split_string(result, s, NULL,
|
|
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
|
|
tor_free(s);
|
|
|
|
if (smartlist_len(result) == 1) {
|
|
/* This is a one-element list containing only ourself; instead return
|
|
* nothing */
|
|
const char *singleton = smartlist_get(result, 0);
|
|
bool is_me = false;
|
|
if (singleton[0] == '$') {
|
|
char d[DIGEST_LEN];
|
|
int n = base16_decode(d, sizeof(d), singleton+1, strlen(singleton+1));
|
|
if (n == DIGEST_LEN &&
|
|
fast_memeq(d, router_get_my_id_digest(), DIGEST_LEN)) {
|
|
is_me = true;
|
|
}
|
|
}
|
|
if (!is_me) {
|
|
// LCOV_EXCL_START
|
|
log_warn(LD_BUG, "Found a singleton family list with an element "
|
|
"that wasn't us! Element was %s", escaped(singleton));
|
|
// LCOV_EXCL_STOP
|
|
} else {
|
|
SMARTLIST_FOREACH(result, char *, cp, tor_free(cp));
|
|
smartlist_free(result);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/** Allocate a fresh, unsigned routerinfo for this OR, without any of the
|
|
* fields that depend on the corresponding extrainfo.
|
|
*
|
|
* On success, set ri_out to the new routerinfo, and return 0.
|
|
* Caller is responsible for freeing the generated routerinfo.
|
|
*
|
|
* Returns a negative value and sets ri_out to NULL on temporary error.
|
|
*/
|
|
MOCK_IMPL(STATIC int,
|
|
router_build_fresh_unsigned_routerinfo,(routerinfo_t **ri_out))
|
|
{
|
|
routerinfo_t *ri = NULL;
|
|
uint32_t addr;
|
|
char platform[256];
|
|
int hibernating = we_are_hibernating();
|
|
const or_options_t *options = get_options();
|
|
int result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG;
|
|
|
|
if (BUG(!ri_out)) {
|
|
result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG;
|
|
goto err;
|
|
}
|
|
|
|
if (router_pick_published_address(options, &addr, 0) < 0) {
|
|
log_warn(LD_CONFIG, "Don't know my address while generating descriptor");
|
|
result = TOR_ROUTERINFO_ERROR_NO_EXT_ADDR;
|
|
goto err;
|
|
}
|
|
|
|
/* Log a message if the address in the descriptor doesn't match the ORPort
|
|
* and DirPort addresses configured by the operator. */
|
|
router_check_descriptor_address_consistency(addr);
|
|
|
|
ri = tor_malloc_zero(sizeof(routerinfo_t));
|
|
ri->cache_info.routerlist_index = -1;
|
|
ri->nickname = tor_strdup(options->Nickname);
|
|
ri->addr = addr;
|
|
ri->or_port = router_get_advertised_or_port(options);
|
|
ri->dir_port = router_get_advertised_dir_port(options, 0);
|
|
ri->supports_tunnelled_dir_requests =
|
|
directory_permits_begindir_requests(options);
|
|
ri->cache_info.published_on = time(NULL);
|
|
/* get_onion_key() must invoke from main thread */
|
|
router_set_rsa_onion_pkey(get_onion_key(), &ri->onion_pkey,
|
|
&ri->onion_pkey_len);
|
|
|
|
ri->onion_curve25519_pkey =
|
|
tor_memdup(&get_current_curve25519_keypair()->pubkey,
|
|
sizeof(curve25519_public_key_t));
|
|
|
|
/* For now, at most one IPv6 or-address is being advertised. */
|
|
tor_addr_port_t ipv6_orport;
|
|
router_get_advertised_ipv6_or_ap(options, &ipv6_orport);
|
|
/* If there is no valid IPv6 ORPort, the address and port are null. */
|
|
tor_addr_copy(&ri->ipv6_addr, &ipv6_orport.addr);
|
|
ri->ipv6_orport = ipv6_orport.port;
|
|
|
|
ri->identity_pkey = crypto_pk_dup_key(get_server_identity_key());
|
|
if (BUG(crypto_pk_get_digest(ri->identity_pkey,
|
|
ri->cache_info.identity_digest) < 0)) {
|
|
result = TOR_ROUTERINFO_ERROR_DIGEST_FAILED;
|
|
goto err;
|
|
}
|
|
ri->cache_info.signing_key_cert =
|
|
tor_cert_dup(get_master_signing_key_cert());
|
|
|
|
get_platform_str(platform, sizeof(platform));
|
|
ri->platform = tor_strdup(platform);
|
|
|
|
ri->protocol_list = tor_strdup(protover_get_supported_protocols());
|
|
|
|
/* compute ri->bandwidthrate as the min of various options */
|
|
ri->bandwidthrate = relay_get_effective_bwrate(options);
|
|
|
|
/* and compute ri->bandwidthburst similarly */
|
|
ri->bandwidthburst = relay_get_effective_bwburst(options);
|
|
|
|
/* Report bandwidth, unless we're hibernating or shutting down */
|
|
ri->bandwidthcapacity = hibernating ? 0 : rep_hist_bandwidth_assess();
|
|
|
|
if (dns_seems_to_be_broken() || has_dns_init_failed()) {
|
|
/* DNS is screwed up; don't claim to be an exit. */
|
|
policies_exit_policy_append_reject_star(&ri->exit_policy);
|
|
} else {
|
|
policies_parse_exit_policy_from_options(options,ri->addr,&ri->ipv6_addr,
|
|
&ri->exit_policy);
|
|
}
|
|
ri->policy_is_reject_star =
|
|
policy_is_reject_star(ri->exit_policy, AF_INET, 1) &&
|
|
policy_is_reject_star(ri->exit_policy, AF_INET6, 1);
|
|
|
|
if (options->IPv6Exit) {
|
|
char *p_tmp = policy_summarize(ri->exit_policy, AF_INET6);
|
|
if (p_tmp)
|
|
ri->ipv6_exit_policy = parse_short_policy(p_tmp);
|
|
tor_free(p_tmp);
|
|
}
|
|
|
|
ri->declared_family = get_my_declared_family(options);
|
|
|
|
if (options->BridgeRelay) {
|
|
ri->purpose = ROUTER_PURPOSE_BRIDGE;
|
|
/* Bridges shouldn't be able to send their descriptors unencrypted,
|
|
anyway, since they don't have a DirPort, and always connect to the
|
|
bridge authority anonymously. But just in case they somehow think of
|
|
sending them on an unencrypted connection, don't allow them to try. */
|
|
ri->cache_info.send_unencrypted = 0;
|
|
} else {
|
|
ri->purpose = ROUTER_PURPOSE_GENERAL;
|
|
ri->cache_info.send_unencrypted = 1;
|
|
}
|
|
|
|
goto done;
|
|
|
|
err:
|
|
routerinfo_free(ri);
|
|
*ri_out = NULL;
|
|
return result;
|
|
|
|
done:
|
|
*ri_out = ri;
|
|
return 0;
|
|
}
|
|
|
|
/** Allocate and return a fresh, unsigned extrainfo for this OR, based on the
|
|
* routerinfo ri.
|
|
*
|
|
* Uses options->Nickname to set the nickname, and options->BridgeRelay to set
|
|
* ei->cache_info.send_unencrypted.
|
|
*
|
|
* If ri is NULL, logs a BUG() warning and returns NULL.
|
|
* Caller is responsible for freeing the generated extrainfo.
|
|
*/
|
|
static extrainfo_t *
|
|
router_build_fresh_unsigned_extrainfo(const routerinfo_t *ri)
|
|
{
|
|
extrainfo_t *ei = NULL;
|
|
const or_options_t *options = get_options();
|
|
|
|
if (BUG(!ri))
|
|
return NULL;
|
|
|
|
/* Now generate the extrainfo. */
|
|
ei = tor_malloc_zero(sizeof(extrainfo_t));
|
|
ei->cache_info.is_extrainfo = 1;
|
|
strlcpy(ei->nickname, options->Nickname, sizeof(ei->nickname));
|
|
ei->cache_info.published_on = ri->cache_info.published_on;
|
|
ei->cache_info.signing_key_cert =
|
|
tor_cert_dup(get_master_signing_key_cert());
|
|
|
|
memcpy(ei->cache_info.identity_digest, ri->cache_info.identity_digest,
|
|
DIGEST_LEN);
|
|
|
|
if (options->BridgeRelay) {
|
|
/* See note in router_build_fresh_routerinfo(). */
|
|
ei->cache_info.send_unencrypted = 0;
|
|
} else {
|
|
ei->cache_info.send_unencrypted = 1;
|
|
}
|
|
|
|
return ei;
|
|
}
|
|
|
|
/** Dump the extrainfo descriptor body for ei, sign it, and add the body and
|
|
* signature to ei->cache_info. Note that the extrainfo body is determined by
|
|
* ei, and some additional config and statistics state: see
|
|
* extrainfo_dump_to_string() for details.
|
|
*
|
|
* Return 0 on success, -1 on temporary error.
|
|
* If ei is NULL, logs a BUG() warning and returns -1.
|
|
* On error, ei->cache_info is not modified.
|
|
*/
|
|
static int
|
|
router_dump_and_sign_extrainfo_descriptor_body(extrainfo_t *ei)
|
|
{
|
|
if (BUG(!ei))
|
|
return -1;
|
|
|
|
if (extrainfo_dump_to_string(&ei->cache_info.signed_descriptor_body,
|
|
ei, get_server_identity_key(),
|
|
get_master_signing_keypair()) < 0) {
|
|
log_warn(LD_BUG, "Couldn't generate extra-info descriptor.");
|
|
return -1;
|
|
}
|
|
|
|
ei->cache_info.signed_descriptor_len =
|
|
strlen(ei->cache_info.signed_descriptor_body);
|
|
|
|
router_get_extrainfo_hash(ei->cache_info.signed_descriptor_body,
|
|
ei->cache_info.signed_descriptor_len,
|
|
ei->cache_info.signed_descriptor_digest);
|
|
crypto_digest256((char*) ei->digest256,
|
|
ei->cache_info.signed_descriptor_body,
|
|
ei->cache_info.signed_descriptor_len,
|
|
DIGEST_SHA256);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** Allocate and return a fresh, signed extrainfo for this OR, based on the
|
|
* routerinfo ri.
|
|
*
|
|
* If ri is NULL, logs a BUG() warning and returns NULL.
|
|
* Caller is responsible for freeing the generated extrainfo.
|
|
*/
|
|
STATIC extrainfo_t *
|
|
router_build_fresh_signed_extrainfo(const routerinfo_t *ri)
|
|
{
|
|
int result = -1;
|
|
extrainfo_t *ei = NULL;
|
|
|
|
if (BUG(!ri))
|
|
return NULL;
|
|
|
|
ei = router_build_fresh_unsigned_extrainfo(ri);
|
|
/* router_build_fresh_unsigned_extrainfo() should not fail. */
|
|
if (BUG(!ei))
|
|
goto err;
|
|
|
|
result = router_dump_and_sign_extrainfo_descriptor_body(ei);
|
|
if (result < 0)
|
|
goto err;
|
|
|
|
goto done;
|
|
|
|
err:
|
|
extrainfo_free(ei);
|
|
return NULL;
|
|
|
|
done:
|
|
return ei;
|
|
}
|
|
|
|
/** Set the fields in ri that depend on ei.
|
|
*
|
|
* If ei is NULL, logs a BUG() warning and zeroes the relevant fields.
|
|
*/
|
|
STATIC void
|
|
router_update_routerinfo_from_extrainfo(routerinfo_t *ri,
|
|
const extrainfo_t *ei)
|
|
{
|
|
if (BUG(!ei)) {
|
|
/* Just to be safe, zero ri->cache_info.extra_info_digest here. */
|
|
memset(ri->cache_info.extra_info_digest, 0, DIGEST_LEN);
|
|
memset(ri->cache_info.extra_info_digest256, 0, DIGEST256_LEN);
|
|
return;
|
|
}
|
|
|
|
/* Now finish the router descriptor. */
|
|
memcpy(ri->cache_info.extra_info_digest,
|
|
ei->cache_info.signed_descriptor_digest,
|
|
DIGEST_LEN);
|
|
memcpy(ri->cache_info.extra_info_digest256,
|
|
ei->digest256,
|
|
DIGEST256_LEN);
|
|
}
|
|
|
|
/** Dump the descriptor body for ri, sign it, and add the body and signature to
|
|
* ri->cache_info. Note that the descriptor body is determined by ri, and some
|
|
* additional config and state: see router_dump_router_to_string() for details.
|
|
*
|
|
* Return 0 on success, and a negative value on temporary error.
|
|
* If ri is NULL, logs a BUG() warning and returns a negative value.
|
|
* On error, ri->cache_info is not modified.
|
|
*/
|
|
STATIC int
|
|
router_dump_and_sign_routerinfo_descriptor_body(routerinfo_t *ri)
|
|
{
|
|
if (BUG(!ri))
|
|
return TOR_ROUTERINFO_ERROR_INTERNAL_BUG;
|
|
|
|
if (! (ri->cache_info.signed_descriptor_body =
|
|
router_dump_router_to_string(ri, get_server_identity_key(),
|
|
get_onion_key(),
|
|
get_current_curve25519_keypair(),
|
|
get_master_signing_keypair())) ) {
|
|
log_warn(LD_BUG, "Couldn't generate router descriptor.");
|
|
return TOR_ROUTERINFO_ERROR_CANNOT_GENERATE;
|
|
}
|
|
|
|
ri->cache_info.signed_descriptor_len =
|
|
strlen(ri->cache_info.signed_descriptor_body);
|
|
|
|
router_get_router_hash(ri->cache_info.signed_descriptor_body,
|
|
strlen(ri->cache_info.signed_descriptor_body),
|
|
ri->cache_info.signed_descriptor_digest);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** Build a fresh routerinfo, signed server descriptor, and signed extrainfo
|
|
* document for this OR.
|
|
*
|
|
* Set r to the generated routerinfo, e to the generated extrainfo document.
|
|
* Failure to generate an extra-info document is not an error and is indicated
|
|
* by setting e to NULL.
|
|
* Return 0 on success, and a negative value on temporary error.
|
|
* Caller is responsible for freeing generated documents on success.
|
|
*/
|
|
int
|
|
router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
|
|
{
|
|
int result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG;
|
|
routerinfo_t *ri = NULL;
|
|
extrainfo_t *ei = NULL;
|
|
|
|
if (BUG(!r))
|
|
goto err;
|
|
|
|
if (BUG(!e))
|
|
goto err;
|
|
|
|
result = router_build_fresh_unsigned_routerinfo(&ri);
|
|
if (result < 0) {
|
|
goto err;
|
|
}
|
|
/* If ri is NULL, then result should be negative. So this check should be
|
|
* unreachable. */
|
|
if (BUG(!ri)) {
|
|
result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG;
|
|
goto err;
|
|
}
|
|
|
|
ei = router_build_fresh_signed_extrainfo(ri);
|
|
|
|
/* Failing to create an ei is not an error. */
|
|
if (ei) {
|
|
router_update_routerinfo_from_extrainfo(ri, ei);
|
|
}
|
|
|
|
result = router_dump_and_sign_routerinfo_descriptor_body(ri);
|
|
if (result < 0)
|
|
goto err;
|
|
|
|
if (ei) {
|
|
if (BUG(routerinfo_incompatible_with_extrainfo(ri->identity_pkey, ei,
|
|
&ri->cache_info, NULL))) {
|
|
result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG;
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
goto done;
|
|
|
|
err:
|
|
routerinfo_free(ri);
|
|
extrainfo_free(ei);
|
|
*r = NULL;
|
|
*e = NULL;
|
|
return result;
|
|
|
|
done:
|
|
*r = ri;
|
|
*e = ei;
|
|
return 0;
|
|
}
|
|
|
|
/** If <b>force</b> is true, or our descriptor is out-of-date, rebuild a fresh
|
|
* routerinfo, signed server descriptor, and extra-info document for this OR.
|
|
* Return 0 on success, -1 on temporary error.
|
|
*/
|
|
int
|
|
router_rebuild_descriptor(int force)
|
|
{
|
|
int err = 0;
|
|
routerinfo_t *ri;
|
|
extrainfo_t *ei;
|
|
uint32_t addr;
|
|
const or_options_t *options = get_options();
|
|
|
|
if (desc_clean_since && !force)
|
|
return 0;
|
|
|
|
if (router_pick_published_address(options, &addr, 0) < 0 ||
|
|
router_get_advertised_or_port(options) == 0) {
|
|
/* Stop trying to rebuild our descriptor every second. We'll
|
|
* learn that it's time to try again when ip_address_changed()
|
|
* marks it dirty. */
|
|
desc_clean_since = time(NULL);
|
|
return TOR_ROUTERINFO_ERROR_DESC_REBUILDING;
|
|
}
|
|
|
|
log_info(LD_OR, "Rebuilding relay descriptor%s", force ? " (forced)" : "");
|
|
|
|
err = router_build_fresh_descriptor(&ri, &ei);
|
|
if (err < 0) {
|
|
return err;
|
|
}
|
|
|
|
routerinfo_free(desc_routerinfo);
|
|
desc_routerinfo = ri;
|
|
extrainfo_free(desc_extrainfo);
|
|
desc_extrainfo = ei;
|
|
|
|
desc_clean_since = time(NULL);
|
|
desc_needs_upload = 1;
|
|
desc_gen_reason = desc_dirty_reason;
|
|
if (BUG(desc_gen_reason == NULL)) {
|
|
desc_gen_reason = "descriptor was marked dirty earlier, for no reason.";
|
|
}
|
|
desc_dirty_reason = NULL;
|
|
control_event_my_descriptor_changed();
|
|
return 0;
|
|
}
|
|
|
|
/** If our router descriptor ever goes this long without being regenerated
|
|
* because something changed, we force an immediate regenerate-and-upload. */
|
|
#define FORCE_REGENERATE_DESCRIPTOR_INTERVAL (18*60*60)
|
|
|
|
/** If our router descriptor seems to be missing or unacceptable according
|
|
* to the authorities, regenerate and reupload it _this_ often. */
|
|
#define FAST_RETRY_DESCRIPTOR_INTERVAL (90*60)
|
|
|
|
/** Mark descriptor out of date if it's been "too long" since we last tried
|
|
* to upload one. */
|
|
void
|
|
mark_my_descriptor_dirty_if_too_old(time_t now)
|
|
{
|
|
networkstatus_t *ns;
|
|
const routerstatus_t *rs;
|
|
const char *retry_fast_reason = NULL; /* Set if we should retry frequently */
|
|
const time_t slow_cutoff = now - FORCE_REGENERATE_DESCRIPTOR_INTERVAL;
|
|
const time_t fast_cutoff = now - FAST_RETRY_DESCRIPTOR_INTERVAL;
|
|
|
|
/* If it's already dirty, don't mark it. */
|
|
if (! desc_clean_since)
|
|
return;
|
|
|
|
/* If it's older than FORCE_REGENERATE_DESCRIPTOR_INTERVAL, it's always
|
|
* time to rebuild it. */
|
|
if (desc_clean_since < slow_cutoff) {
|
|
mark_my_descriptor_dirty("time for new descriptor");
|
|
return;
|
|
}
|
|
/* Now we see whether we want to be retrying frequently or no. The
|
|
* rule here is that we'll retry frequently if we aren't listed in the
|
|
* live consensus we have, or if the publication time of the
|
|
* descriptor listed for us in the consensus is very old, or if the
|
|
* consensus lists us as "stale" and we haven't regenerated since the
|
|
* consensus was published. */
|
|
ns = networkstatus_get_live_consensus(now);
|
|
if (ns) {
|
|
rs = networkstatus_vote_find_entry(ns, server_identitykey_digest);
|
|
if (rs == NULL)
|
|
retry_fast_reason = "not listed in consensus";
|
|
else if (rs->published_on < slow_cutoff)
|
|
retry_fast_reason = "version listed in consensus is quite old";
|
|
else if (rs->is_staledesc && ns->valid_after > desc_clean_since)
|
|
retry_fast_reason = "listed as stale in consensus";
|
|
}
|
|
|
|
if (retry_fast_reason && desc_clean_since < fast_cutoff)
|
|
mark_my_descriptor_dirty(retry_fast_reason);
|
|
}
|
|
|
|
/** Call when the current descriptor is out of date. */
|
|
void
|
|
mark_my_descriptor_dirty(const char *reason)
|
|
{
|
|
const or_options_t *options = get_options();
|
|
if (BUG(reason == NULL)) {
|
|
reason = "marked descriptor dirty for unspecified reason";
|
|
}
|
|
if (server_mode(options) && options->PublishServerDescriptor_)
|
|
log_info(LD_OR, "Decided to publish new relay descriptor: %s", reason);
|
|
desc_clean_since = 0;
|
|
if (!desc_dirty_reason)
|
|
desc_dirty_reason = reason;
|
|
}
|
|
|
|
/** How frequently will we republish our descriptor because of large (factor
|
|
* of 2) shifts in estimated bandwidth? Note: We don't use this constant
|
|
* if our previous bandwidth estimate was exactly 0. */
|
|
#define MAX_BANDWIDTH_CHANGE_FREQ (3*60*60)
|
|
|
|
/** Maximum uptime to republish our descriptor because of large shifts in
|
|
* estimated bandwidth. */
|
|
#define MAX_UPTIME_BANDWIDTH_CHANGE (24*60*60)
|
|
|
|
/** By which factor bandwidth shifts have to change to be considered large. */
|
|
#define BANDWIDTH_CHANGE_FACTOR 2
|
|
|
|
/** Check whether bandwidth has changed a lot since the last time we announced
|
|
* bandwidth while the uptime is smaller than MAX_UPTIME_BANDWIDTH_CHANGE.
|
|
* If so, mark our descriptor dirty. */
|
|
void
|
|
check_descriptor_bandwidth_changed(time_t now)
|
|
{
|
|
static time_t last_changed = 0;
|
|
uint64_t prev, cur;
|
|
const int hibernating = we_are_hibernating();
|
|
|
|
/* If the relay uptime is bigger than MAX_UPTIME_BANDWIDTH_CHANGE,
|
|
* the next regularly scheduled descriptor update (18h) will be enough */
|
|
if (get_uptime() > MAX_UPTIME_BANDWIDTH_CHANGE && !hibernating)
|
|
return;
|
|
|
|
const routerinfo_t *my_ri = router_get_my_routerinfo();
|
|
|
|
if (!my_ri)
|
|
return;
|
|
|
|
prev = my_ri->bandwidthcapacity;
|
|
|
|
/* Consider ourselves to have zero bandwidth if we're hibernating or
|
|
* shutting down. */
|
|
cur = hibernating ? 0 : rep_hist_bandwidth_assess();
|
|
|
|
if ((prev != cur && (!prev || !cur)) ||
|
|
cur > (prev * BANDWIDTH_CHANGE_FACTOR) ||
|
|
cur < (prev / BANDWIDTH_CHANGE_FACTOR) ) {
|
|
if (last_changed+MAX_BANDWIDTH_CHANGE_FREQ < now || !prev) {
|
|
log_info(LD_GENERAL,
|
|
"Measured bandwidth has changed; rebuilding descriptor.");
|
|
mark_my_descriptor_dirty("bandwidth has changed");
|
|
last_changed = now;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Note at log level severity that our best guess of address has changed from
|
|
* <b>prev</b> to <b>cur</b>. */
|
|
static void
|
|
log_addr_has_changed(int severity,
|
|
const tor_addr_t *prev,
|
|
const tor_addr_t *cur,
|
|
const char *source)
|
|
{
|
|
char addrbuf_prev[TOR_ADDR_BUF_LEN];
|
|
char addrbuf_cur[TOR_ADDR_BUF_LEN];
|
|
|
|
if (BUG(!server_mode(get_options())))
|
|
return;
|
|
|
|
if (tor_addr_to_str(addrbuf_prev, prev, sizeof(addrbuf_prev), 1) == NULL)
|
|
strlcpy(addrbuf_prev, "???", TOR_ADDR_BUF_LEN);
|
|
if (tor_addr_to_str(addrbuf_cur, cur, sizeof(addrbuf_cur), 1) == NULL)
|
|
strlcpy(addrbuf_cur, "???", TOR_ADDR_BUF_LEN);
|
|
|
|
if (!tor_addr_is_null(prev))
|
|
log_fn(severity, LD_GENERAL,
|
|
"Our IP Address has changed from %s to %s; "
|
|
"rebuilding descriptor (source: %s).",
|
|
addrbuf_prev, addrbuf_cur, source);
|
|
else
|
|
log_notice(LD_GENERAL,
|
|
"Guessed our IP address as %s (source: %s).",
|
|
addrbuf_cur, source);
|
|
}
|
|
|
|
/** Check whether our own address as defined by the Address configuration
|
|
* has changed. This is for routers that get their address from a service
|
|
* like dyndns. If our address has changed, mark our descriptor dirty. */
|
|
void
|
|
check_descriptor_ipaddress_changed(time_t now)
|
|
{
|
|
uint32_t prev, cur;
|
|
const or_options_t *options = get_options();
|
|
const char *method = NULL;
|
|
char *hostname = NULL;
|
|
const routerinfo_t *my_ri = router_get_my_routerinfo();
|
|
|
|
(void) now;
|
|
|
|
if (my_ri == NULL) /* make sure routerinfo exists */
|
|
return;
|
|
|
|
/* XXXX ipv6 */
|
|
prev = my_ri->addr;
|
|
if (resolve_my_address(LOG_INFO, options, &cur, &method, &hostname) < 0) {
|
|
log_info(LD_CONFIG,"options->Address didn't resolve into an IP.");
|
|
return;
|
|
}
|
|
|
|
if (prev != cur) {
|
|
char *source;
|
|
tor_addr_t tmp_prev, tmp_cur;
|
|
|
|
tor_addr_from_ipv4h(&tmp_prev, prev);
|
|
tor_addr_from_ipv4h(&tmp_cur, cur);
|
|
|
|
tor_asprintf(&source, "METHOD=%s%s%s", method,
|
|
hostname ? " HOSTNAME=" : "",
|
|
hostname ? hostname : "");
|
|
|
|
log_addr_has_changed(LOG_NOTICE, &tmp_prev, &tmp_cur, source);
|
|
tor_free(source);
|
|
|
|
ip_address_changed(0);
|
|
}
|
|
|
|
tor_free(hostname);
|
|
}
|
|
|
|
/** The most recently guessed value of our IP address, based on directory
|
|
* headers. */
|
|
static tor_addr_t last_guessed_ip = TOR_ADDR_NULL;
|
|
|
|
/** A directory server <b>d_conn</b> told us our IP address is
|
|
* <b>suggestion</b>.
|
|
* If this address is different from the one we think we are now, and
|
|
* if our computer doesn't actually know its IP address, then switch. */
|
|
void
|
|
router_new_address_suggestion(const char *suggestion,
|
|
const dir_connection_t *d_conn)
|
|
{
|
|
tor_addr_t addr;
|
|
uint32_t cur = 0; /* Current IPv4 address. */
|
|
const or_options_t *options = get_options();
|
|
|
|
/* first, learn what the IP address actually is */
|
|
if (tor_addr_parse(&addr, suggestion) == -1) {
|
|
log_debug(LD_DIR, "Malformed X-Your-Address-Is header %s. Ignoring.",
|
|
escaped(suggestion));
|
|
return;
|
|
}
|
|
|
|
log_debug(LD_DIR, "Got X-Your-Address-Is: %s.", suggestion);
|
|
|
|
if (!server_mode(options)) {
|
|
tor_addr_copy(&last_guessed_ip, &addr);
|
|
return;
|
|
}
|
|
|
|
/* XXXX ipv6 */
|
|
cur = get_last_resolved_addr();
|
|
if (cur ||
|
|
resolve_my_address(LOG_INFO, options, &cur, NULL, NULL) >= 0) {
|
|
/* We're all set -- we already know our address. Great. */
|
|
tor_addr_from_ipv4h(&last_guessed_ip, cur); /* store it in case we
|
|
need it later */
|
|
return;
|
|
}
|
|
if (tor_addr_is_internal(&addr, 0)) {
|
|
/* Don't believe anybody who says our IP is, say, 127.0.0.1. */
|
|
return;
|
|
}
|
|
if (tor_addr_eq(&d_conn->base_.addr, &addr)) {
|
|
/* Don't believe anybody who says our IP is their IP. */
|
|
log_debug(LD_DIR, "A directory server told us our IP address is %s, "
|
|
"but they are just reporting their own IP address. Ignoring.",
|
|
suggestion);
|
|
return;
|
|
}
|
|
|
|
/* Okay. We can't resolve our own address, and X-Your-Address-Is is giving
|
|
* us an answer different from what we had the last time we managed to
|
|
* resolve it. */
|
|
if (!tor_addr_eq(&last_guessed_ip, &addr)) {
|
|
control_event_server_status(LOG_NOTICE,
|
|
"EXTERNAL_ADDRESS ADDRESS=%s METHOD=DIRSERV",
|
|
suggestion);
|
|
log_addr_has_changed(LOG_NOTICE, &last_guessed_ip, &addr,
|
|
d_conn->base_.address);
|
|
ip_address_changed(0);
|
|
tor_addr_copy(&last_guessed_ip, &addr); /* router_rebuild_descriptor()
|
|
will fetch it */
|
|
}
|
|
}
|
|
|
|
/** We failed to resolve our address locally, but we'd like to build
|
|
* a descriptor and publish / test reachability. If we have a guess
|
|
* about our address based on directory headers, answer it and return
|
|
* 0; else return -1. */
|
|
static int
|
|
router_guess_address_from_dir_headers(uint32_t *guess)
|
|
{
|
|
if (!tor_addr_is_null(&last_guessed_ip)) {
|
|
*guess = tor_addr_to_ipv4h(&last_guessed_ip);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/** Set <b>platform</b> (max length <b>len</b>) to a NUL-terminated short
|
|
* string describing the version of Tor and the operating system we're
|
|
* currently running on.
|
|
*/
|
|
STATIC void
|
|
get_platform_str(char *platform, size_t len)
|
|
{
|
|
tor_snprintf(platform, len, "Tor %s on %s",
|
|
get_short_version(), get_uname());
|
|
}
|
|
|
|
/* XXX need to audit this thing and count fenceposts. maybe
|
|
* refactor so we don't have to keep asking if we're
|
|
* near the end of maxlen?
|
|
*/
|
|
#define DEBUG_ROUTER_DUMP_ROUTER_TO_STRING
|
|
|
|
/** OR only: Given a routerinfo for this router, and an identity key to sign
|
|
* with, encode the routerinfo as a signed server descriptor and return a new
|
|
* string encoding the result, or NULL on failure.
|
|
*
|
|
* In addition to the fields in router, this function calls
|
|
* onion_key_lifetime(), get_options(), and we_are_hibernating(), and uses the
|
|
* results to populate some fields in the descriptor.
|
|
*/
|
|
char *
|
|
router_dump_router_to_string(routerinfo_t *router,
|
|
const crypto_pk_t *ident_key,
|
|
const crypto_pk_t *tap_key,
|
|
const curve25519_keypair_t *ntor_keypair,
|
|
const ed25519_keypair_t *signing_keypair)
|
|
{
|
|
char *address = NULL;
|
|
char *onion_pkey = NULL; /* Onion key, PEM-encoded. */
|
|
crypto_pk_t *rsa_pubkey = NULL;
|
|
char *identity_pkey = NULL; /* Identity key, PEM-encoded. */
|
|
char digest[DIGEST256_LEN];
|
|
char published[ISO_TIME_LEN+1];
|
|
char fingerprint[FINGERPRINT_LEN+1];
|
|
char *extra_info_line = NULL;
|
|
size_t onion_pkeylen, identity_pkeylen;
|
|
char *family_line = NULL;
|
|
char *extra_or_address = NULL;
|
|
const or_options_t *options = get_options();
|
|
smartlist_t *chunks = NULL;
|
|
char *output = NULL;
|
|
const int emit_ed_sigs = signing_keypair &&
|
|
router->cache_info.signing_key_cert;
|
|
char *ed_cert_line = NULL;
|
|
char *rsa_tap_cc_line = NULL;
|
|
char *ntor_cc_line = NULL;
|
|
char *proto_line = NULL;
|
|
|
|
/* Make sure the identity key matches the one in the routerinfo. */
|
|
if (!crypto_pk_eq_keys(ident_key, router->identity_pkey)) {
|
|
log_warn(LD_BUG,"Tried to sign a router with a private key that didn't "
|
|
"match router's public key!");
|
|
goto err;
|
|
}
|
|
if (emit_ed_sigs) {
|
|
if (!router->cache_info.signing_key_cert->signing_key_included ||
|
|
!ed25519_pubkey_eq(&router->cache_info.signing_key_cert->signed_key,
|
|
&signing_keypair->pubkey)) {
|
|
log_warn(LD_BUG, "Tried to sign a router descriptor with a mismatched "
|
|
"ed25519 key chain %d",
|
|
router->cache_info.signing_key_cert->signing_key_included);
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
/* record our fingerprint, so we can include it in the descriptor */
|
|
if (crypto_pk_get_fingerprint(router->identity_pkey, fingerprint, 1)<0) {
|
|
log_err(LD_BUG,"Error computing fingerprint");
|
|
goto err;
|
|
}
|
|
|
|
if (emit_ed_sigs) {
|
|
/* Encode ed25519 signing cert */
|
|
char ed_cert_base64[256];
|
|
char ed_fp_base64[ED25519_BASE64_LEN+1];
|
|
if (base64_encode(ed_cert_base64, sizeof(ed_cert_base64),
|
|
(const char*)router->cache_info.signing_key_cert->encoded,
|
|
router->cache_info.signing_key_cert->encoded_len,
|
|
BASE64_ENCODE_MULTILINE) < 0) {
|
|
log_err(LD_BUG,"Couldn't base64-encode signing key certificate!");
|
|
goto err;
|
|
}
|
|
ed25519_public_to_base64(ed_fp_base64,
|
|
&router->cache_info.signing_key_cert->signing_key);
|
|
tor_asprintf(&ed_cert_line, "identity-ed25519\n"
|
|
"-----BEGIN ED25519 CERT-----\n"
|
|
"%s"
|
|
"-----END ED25519 CERT-----\n"
|
|
"master-key-ed25519 %s\n",
|
|
ed_cert_base64, ed_fp_base64);
|
|
}
|
|
|
|
/* PEM-encode the onion key */
|
|
rsa_pubkey = router_get_rsa_onion_pkey(router->onion_pkey,
|
|
router->onion_pkey_len);
|
|
if (crypto_pk_write_public_key_to_string(rsa_pubkey,
|
|
&onion_pkey,&onion_pkeylen)<0) {
|
|
log_warn(LD_BUG,"write onion_pkey to string failed!");
|
|
goto err;
|
|
}
|
|
|
|
/* PEM-encode the identity key */
|
|
if (crypto_pk_write_public_key_to_string(router->identity_pkey,
|
|
&identity_pkey,&identity_pkeylen)<0) {
|
|
log_warn(LD_BUG,"write identity_pkey to string failed!");
|
|
goto err;
|
|
}
|
|
|
|
/* Cross-certify with RSA key */
|
|
if (tap_key && router->cache_info.signing_key_cert &&
|
|
router->cache_info.signing_key_cert->signing_key_included) {
|
|
char buf[256];
|
|
int tap_cc_len = 0;
|
|
uint8_t *tap_cc =
|
|
make_tap_onion_key_crosscert(tap_key,
|
|
&router->cache_info.signing_key_cert->signing_key,
|
|
router->identity_pkey,
|
|
&tap_cc_len);
|
|
if (!tap_cc) {
|
|
log_warn(LD_BUG,"make_tap_onion_key_crosscert failed!");
|
|
goto err;
|
|
}
|
|
|
|
if (base64_encode(buf, sizeof(buf), (const char*)tap_cc, tap_cc_len,
|
|
BASE64_ENCODE_MULTILINE) < 0) {
|
|
log_warn(LD_BUG,"base64_encode(rsa_crosscert) failed!");
|
|
tor_free(tap_cc);
|
|
goto err;
|
|
}
|
|
tor_free(tap_cc);
|
|
|
|
tor_asprintf(&rsa_tap_cc_line,
|
|
"onion-key-crosscert\n"
|
|
"-----BEGIN CROSSCERT-----\n"
|
|
"%s"
|
|
"-----END CROSSCERT-----\n", buf);
|
|
}
|
|
|
|
/* Cross-certify with onion keys */
|
|
if (ntor_keypair && router->cache_info.signing_key_cert &&
|
|
router->cache_info.signing_key_cert->signing_key_included) {
|
|
int sign = 0;
|
|
char buf[256];
|
|
/* XXXX Base the expiration date on the actual onion key expiration time?*/
|
|
tor_cert_t *cert =
|
|
make_ntor_onion_key_crosscert(ntor_keypair,
|
|
&router->cache_info.signing_key_cert->signing_key,
|
|
router->cache_info.published_on,
|
|
get_onion_key_lifetime(), &sign);
|
|
if (!cert) {
|
|
log_warn(LD_BUG,"make_ntor_onion_key_crosscert failed!");
|
|
goto err;
|
|
}
|
|
tor_assert(sign == 0 || sign == 1);
|
|
|
|
if (base64_encode(buf, sizeof(buf),
|
|
(const char*)cert->encoded, cert->encoded_len,
|
|
BASE64_ENCODE_MULTILINE)<0) {
|
|
log_warn(LD_BUG,"base64_encode(ntor_crosscert) failed!");
|
|
tor_cert_free(cert);
|
|
goto err;
|
|
}
|
|
tor_cert_free(cert);
|
|
|
|
tor_asprintf(&ntor_cc_line,
|
|
"ntor-onion-key-crosscert %d\n"
|
|
"-----BEGIN ED25519 CERT-----\n"
|
|
"%s"
|
|
"-----END ED25519 CERT-----\n", sign, buf);
|
|
}
|
|
|
|
/* Encode the publication time. */
|
|
format_iso_time(published, router->cache_info.published_on);
|
|
|
|
if (router->declared_family && smartlist_len(router->declared_family)) {
|
|
char *family = smartlist_join_strings(router->declared_family,
|
|
" ", 0, NULL);
|
|
tor_asprintf(&family_line, "family %s\n", family);
|
|
tor_free(family);
|
|
} else {
|
|
family_line = tor_strdup("");
|
|
}
|
|
|
|
if (!tor_digest_is_zero(router->cache_info.extra_info_digest)) {
|
|
char extra_info_digest[HEX_DIGEST_LEN+1];
|
|
base16_encode(extra_info_digest, sizeof(extra_info_digest),
|
|
router->cache_info.extra_info_digest, DIGEST_LEN);
|
|
if (!tor_digest256_is_zero(router->cache_info.extra_info_digest256)) {
|
|
char d256_64[BASE64_DIGEST256_LEN+1];
|
|
digest256_to_base64(d256_64, router->cache_info.extra_info_digest256);
|
|
tor_asprintf(&extra_info_line, "extra-info-digest %s %s\n",
|
|
extra_info_digest, d256_64);
|
|
} else {
|
|
tor_asprintf(&extra_info_line, "extra-info-digest %s\n",
|
|
extra_info_digest);
|
|
}
|
|
}
|
|
|
|
if (router->ipv6_orport &&
|
|
tor_addr_family(&router->ipv6_addr) == AF_INET6) {
|
|
char addr[TOR_ADDR_BUF_LEN];
|
|
const char *a;
|
|
a = tor_addr_to_str(addr, &router->ipv6_addr, sizeof(addr), 1);
|
|
if (a) {
|
|
tor_asprintf(&extra_or_address,
|
|
"or-address %s:%d\n", a, router->ipv6_orport);
|
|
log_debug(LD_OR, "My or-address line is <%s>", extra_or_address);
|
|
}
|
|
}
|
|
|
|
if (router->protocol_list) {
|
|
tor_asprintf(&proto_line, "proto %s\n", router->protocol_list);
|
|
} else {
|
|
proto_line = tor_strdup("");
|
|
}
|
|
|
|
address = tor_dup_ip(router->addr);
|
|
chunks = smartlist_new();
|
|
|
|
/* Generate the easy portion of the router descriptor. */
|
|
smartlist_add_asprintf(chunks,
|
|
"router %s %s %d 0 %d\n"
|
|
"%s"
|
|
"%s"
|
|
"platform %s\n"
|
|
"%s"
|
|
"published %s\n"
|
|
"fingerprint %s\n"
|
|
"uptime %ld\n"
|
|
"bandwidth %d %d %d\n"
|
|
"%s%s"
|
|
"onion-key\n%s"
|
|
"signing-key\n%s"
|
|
"%s%s"
|
|
"%s%s%s",
|
|
router->nickname,
|
|
address,
|
|
router->or_port,
|
|
router_should_advertise_dirport(options, router->dir_port),
|
|
ed_cert_line ? ed_cert_line : "",
|
|
extra_or_address ? extra_or_address : "",
|
|
router->platform,
|
|
proto_line,
|
|
published,
|
|
fingerprint,
|
|
get_uptime(),
|
|
(int) router->bandwidthrate,
|
|
(int) router->bandwidthburst,
|
|
(int) router->bandwidthcapacity,
|
|
extra_info_line ? extra_info_line : "",
|
|
(options->DownloadExtraInfo || options->V3AuthoritativeDir) ?
|
|
"caches-extra-info\n" : "",
|
|
onion_pkey, identity_pkey,
|
|
rsa_tap_cc_line ? rsa_tap_cc_line : "",
|
|
ntor_cc_line ? ntor_cc_line : "",
|
|
family_line,
|
|
we_are_hibernating() ? "hibernating 1\n" : "",
|
|
"hidden-service-dir\n");
|
|
|
|
if (options->ContactInfo && strlen(options->ContactInfo)) {
|
|
const char *ci = options->ContactInfo;
|
|
if (strchr(ci, '\n') || strchr(ci, '\r'))
|
|
ci = escaped(ci);
|
|
smartlist_add_asprintf(chunks, "contact %s\n", ci);
|
|
}
|
|
|
|
if (options->BridgeRelay) {
|
|
char *bd = NULL;
|
|
|
|
if (options->BridgeDistribution && strlen(options->BridgeDistribution)) {
|
|
bd = tor_strdup(options->BridgeDistribution);
|
|
} else {
|
|
bd = tor_strdup("any");
|
|
}
|
|
|
|
// Make sure our value is lowercased in the descriptor instead of just
|
|
// forwarding what the user wrote in their torrc directly.
|
|
tor_strlower(bd);
|
|
|
|
smartlist_add_asprintf(chunks, "bridge-distribution-request %s\n", bd);
|
|
tor_free(bd);
|
|
}
|
|
|
|
if (router->onion_curve25519_pkey) {
|
|
char kbuf[128];
|
|
base64_encode(kbuf, sizeof(kbuf),
|
|
(const char *)router->onion_curve25519_pkey->public_key,
|
|
CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE);
|
|
smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf);
|
|
} else {
|
|
/* Authorities will start rejecting relays without ntor keys in 0.2.9 */
|
|
log_err(LD_BUG, "A relay must have an ntor onion key");
|
|
goto err;
|
|
}
|
|
|
|
/* Write the exit policy to the end of 's'. */
|
|
if (!router->exit_policy || !smartlist_len(router->exit_policy)) {
|
|
smartlist_add_strdup(chunks, "reject *:*\n");
|
|
} else if (router->exit_policy) {
|
|
char *exit_policy = router_dump_exit_policy_to_string(router,1,0);
|
|
|
|
if (!exit_policy)
|
|
goto err;
|
|
|
|
smartlist_add_asprintf(chunks, "%s\n", exit_policy);
|
|
tor_free(exit_policy);
|
|
}
|
|
|
|
if (router->ipv6_exit_policy) {
|
|
char *p6 = write_short_policy(router->ipv6_exit_policy);
|
|
if (p6 && strcmp(p6, "reject 1-65535")) {
|
|
smartlist_add_asprintf(chunks,
|
|
"ipv6-policy %s\n", p6);
|
|
}
|
|
tor_free(p6);
|
|
}
|
|
|
|
if (router_should_advertise_begindir(options,
|
|
router->supports_tunnelled_dir_requests)) {
|
|
smartlist_add_strdup(chunks, "tunnelled-dir-server\n");
|
|
}
|
|
|
|
/* Sign the descriptor with Ed25519 */
|
|
if (emit_ed_sigs) {
|
|
smartlist_add_strdup(chunks, "router-sig-ed25519 ");
|
|
crypto_digest_smartlist_prefix(digest, DIGEST256_LEN,
|
|
ED_DESC_SIGNATURE_PREFIX,
|
|
chunks, "", DIGEST_SHA256);
|
|
ed25519_signature_t sig;
|
|
char buf[ED25519_SIG_BASE64_LEN+1];
|
|
if (ed25519_sign(&sig, (const uint8_t*)digest, DIGEST256_LEN,
|
|
signing_keypair) < 0)
|
|
goto err;
|
|
ed25519_signature_to_base64(buf, &sig);
|
|
|
|
smartlist_add_asprintf(chunks, "%s\n", buf);
|
|
}
|
|
|
|
/* Sign the descriptor with RSA */
|
|
smartlist_add_strdup(chunks, "router-signature\n");
|
|
|
|
crypto_digest_smartlist(digest, DIGEST_LEN, chunks, "", DIGEST_SHA1);
|
|
|
|
{
|
|
char *sig;
|
|
if (!(sig = router_get_dirobj_signature(digest, DIGEST_LEN, ident_key))) {
|
|
log_warn(LD_BUG, "Couldn't sign router descriptor");
|
|
goto err;
|
|
}
|
|
smartlist_add(chunks, sig);
|
|
}
|
|
|
|
/* include a last '\n' */
|
|
smartlist_add_strdup(chunks, "\n");
|
|
|
|
output = smartlist_join_strings(chunks, "", 0, NULL);
|
|
|
|
#ifdef DEBUG_ROUTER_DUMP_ROUTER_TO_STRING
|
|
{
|
|
char *s_dup;
|
|
const char *cp;
|
|
routerinfo_t *ri_tmp;
|
|
cp = s_dup = tor_strdup(output);
|
|
ri_tmp = router_parse_entry_from_string(cp, NULL, 1, 0, NULL, NULL);
|
|
if (!ri_tmp) {
|
|
log_err(LD_BUG,
|
|
"We just generated a router descriptor we can't parse.");
|
|
log_err(LD_BUG, "Descriptor was: <<%s>>", output);
|
|
goto err;
|
|
}
|
|
tor_free(s_dup);
|
|
routerinfo_free(ri_tmp);
|
|
}
|
|
#endif /* defined(DEBUG_ROUTER_DUMP_ROUTER_TO_STRING) */
|
|
|
|
goto done;
|
|
|
|
err:
|
|
tor_free(output); /* sets output to NULL */
|
|
done:
|
|
if (chunks) {
|
|
SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
|
|
smartlist_free(chunks);
|
|
}
|
|
crypto_pk_free(rsa_pubkey);
|
|
tor_free(address);
|
|
tor_free(family_line);
|
|
tor_free(onion_pkey);
|
|
tor_free(identity_pkey);
|
|
tor_free(extra_or_address);
|
|
tor_free(ed_cert_line);
|
|
tor_free(rsa_tap_cc_line);
|
|
tor_free(ntor_cc_line);
|
|
tor_free(extra_info_line);
|
|
tor_free(proto_line);
|
|
|
|
return output;
|
|
}
|
|
|
|
/**
|
|
* OR only: Given <b>router</b>, produce a string with its exit policy.
|
|
* If <b>include_ipv4</b> is true, include IPv4 entries.
|
|
* If <b>include_ipv6</b> is true, include IPv6 entries.
|
|
*/
|
|
char *
|
|
router_dump_exit_policy_to_string(const routerinfo_t *router,
|
|
int include_ipv4,
|
|
int include_ipv6)
|
|
{
|
|
if ((!router->exit_policy) || (router->policy_is_reject_star)) {
|
|
return tor_strdup("reject *:*");
|
|
}
|
|
|
|
return policy_dump_to_string(router->exit_policy,
|
|
include_ipv4,
|
|
include_ipv6);
|
|
}
|
|
|
|
/** Load the contents of <b>filename</b>, find the last line starting with
|
|
* <b>end_line</b>, ensure that its timestamp is not more than 25 hours in
|
|
* the past or more than 1 hour in the future with respect to <b>now</b>,
|
|
* and write the file contents starting with that line to *<b>out</b>.
|
|
* Return 1 for success, 0 if the file does not exist or is empty, or -1
|
|
* if the file does not contain a line matching these criteria or other
|
|
* failure. */
|
|
static int
|
|
load_stats_file(const char *filename, const char *end_line, time_t now,
|
|
char **out)
|
|
{
|
|
int r = -1;
|
|
char *fname = get_datadir_fname(filename);
|
|
char *contents, *start = NULL, *tmp, timestr[ISO_TIME_LEN+1];
|
|
time_t written;
|
|
switch (file_status(fname)) {
|
|
case FN_FILE:
|
|
/* X022 Find an alternative to reading the whole file to memory. */
|
|
if ((contents = read_file_to_str(fname, 0, NULL))) {
|
|
tmp = strstr(contents, end_line);
|
|
/* Find last block starting with end_line */
|
|
while (tmp) {
|
|
start = tmp;
|
|
tmp = strstr(tmp + 1, end_line);
|
|
}
|
|
if (!start)
|
|
goto notfound;
|
|
if (strlen(start) < strlen(end_line) + 1 + sizeof(timestr))
|
|
goto notfound;
|
|
strlcpy(timestr, start + 1 + strlen(end_line), sizeof(timestr));
|
|
if (parse_iso_time(timestr, &written) < 0)
|
|
goto notfound;
|
|
if (written < now - (25*60*60) || written > now + (1*60*60))
|
|
goto notfound;
|
|
*out = tor_strdup(start);
|
|
r = 1;
|
|
}
|
|
notfound:
|
|
tor_free(contents);
|
|
break;
|
|
/* treat empty stats files as if the file doesn't exist */
|
|
case FN_NOENT:
|
|
case FN_EMPTY:
|
|
r = 0;
|
|
break;
|
|
case FN_ERROR:
|
|
case FN_DIR:
|
|
default:
|
|
break;
|
|
}
|
|
tor_free(fname);
|
|
return r;
|
|
}
|
|
|
|
/** Add header strings to chunks, based on the extrainfo object extrainfo,
|
|
* and ed25519 keypair signing_keypair, if emit_ed_sigs is true.
|
|
* Helper for extrainfo_dump_to_string().
|
|
* Returns 0 on success, negative on failure. */
|
|
static int
|
|
extrainfo_dump_to_string_header_helper(
|
|
smartlist_t *chunks,
|
|
const extrainfo_t *extrainfo,
|
|
const ed25519_keypair_t *signing_keypair,
|
|
int emit_ed_sigs)
|
|
{
|
|
char identity[HEX_DIGEST_LEN+1];
|
|
char published[ISO_TIME_LEN+1];
|
|
char *ed_cert_line = NULL;
|
|
char *pre = NULL;
|
|
int rv = -1;
|
|
|
|
base16_encode(identity, sizeof(identity),
|
|
extrainfo->cache_info.identity_digest, DIGEST_LEN);
|
|
format_iso_time(published, extrainfo->cache_info.published_on);
|
|
if (emit_ed_sigs) {
|
|
if (!extrainfo->cache_info.signing_key_cert->signing_key_included ||
|
|
!ed25519_pubkey_eq(&extrainfo->cache_info.signing_key_cert->signed_key,
|
|
&signing_keypair->pubkey)) {
|
|
log_warn(LD_BUG, "Tried to sign a extrainfo descriptor with a "
|
|
"mismatched ed25519 key chain %d",
|
|
extrainfo->cache_info.signing_key_cert->signing_key_included);
|
|
goto err;
|
|
}
|
|
char ed_cert_base64[256];
|
|
if (base64_encode(ed_cert_base64, sizeof(ed_cert_base64),
|
|
(const char*)extrainfo->cache_info.signing_key_cert->encoded,
|
|
extrainfo->cache_info.signing_key_cert->encoded_len,
|
|
BASE64_ENCODE_MULTILINE) < 0) {
|
|
log_err(LD_BUG,"Couldn't base64-encode signing key certificate!");
|
|
goto err;
|
|
}
|
|
tor_asprintf(&ed_cert_line, "identity-ed25519\n"
|
|
"-----BEGIN ED25519 CERT-----\n"
|
|
"%s"
|
|
"-----END ED25519 CERT-----\n", ed_cert_base64);
|
|
} else {
|
|
ed_cert_line = tor_strdup("");
|
|
}
|
|
|
|
/* This is the first chunk in the file. If the file is too big, other chunks
|
|
* are removed. So we must only add one chunk here. */
|
|
tor_asprintf(&pre, "extra-info %s %s\n%spublished %s\n",
|
|
extrainfo->nickname, identity,
|
|
ed_cert_line,
|
|
published);
|
|
smartlist_add(chunks, pre);
|
|
|
|
rv = 0;
|
|
goto done;
|
|
|
|
err:
|
|
rv = -1;
|
|
|
|
done:
|
|
tor_free(ed_cert_line);
|
|
return rv;
|
|
}
|
|
|
|
/** Add pluggable transport and statistics strings to chunks, skipping
|
|
* statistics if write_stats_to_extrainfo is false.
|
|
* Helper for extrainfo_dump_to_string().
|
|
* Can not fail. */
|
|
static void
|
|
extrainfo_dump_to_string_stats_helper(smartlist_t *chunks,
|
|
int write_stats_to_extrainfo)
|
|
{
|
|
const or_options_t *options = get_options();
|
|
char *contents = NULL;
|
|
time_t now = time(NULL);
|
|
|
|
/* If the file is too big, these chunks are removed, starting with the last
|
|
* chunk. So each chunk must be a complete line, and the file must be valid
|
|
* after each chunk. */
|
|
|
|
/* Add information about the pluggable transports we support, even if we
|
|
* are not publishing statistics. This information is needed by BridgeDB
|
|
* to distribute bridges. */
|
|
if (options->ServerTransportPlugin) {
|
|
char *pluggable_transports = pt_get_extra_info_descriptor_string();
|
|
if (pluggable_transports)
|
|
smartlist_add(chunks, pluggable_transports);
|
|
}
|
|
|
|
if (options->ExtraInfoStatistics && write_stats_to_extrainfo) {
|
|
log_info(LD_GENERAL, "Adding stats to extra-info descriptor.");
|
|
/* Bandwidth usage stats don't have their own option */
|
|
{
|
|
contents = rep_hist_get_bandwidth_lines();
|
|
smartlist_add(chunks, contents);
|
|
}
|
|
/* geoip hashes aren't useful unless we are publishing other stats */
|
|
if (geoip_is_loaded(AF_INET))
|
|
smartlist_add_asprintf(chunks, "geoip-db-digest %s\n",
|
|
geoip_db_digest(AF_INET));
|
|
if (geoip_is_loaded(AF_INET6))
|
|
smartlist_add_asprintf(chunks, "geoip6-db-digest %s\n",
|
|
geoip_db_digest(AF_INET6));
|
|
if (options->DirReqStatistics &&
|
|
load_stats_file("stats"PATH_SEPARATOR"dirreq-stats",
|
|
"dirreq-stats-end", now, &contents) > 0) {
|
|
smartlist_add(chunks, contents);
|
|
}
|
|
if (options->HiddenServiceStatistics &&
|
|
load_stats_file("stats"PATH_SEPARATOR"hidserv-stats",
|
|
"hidserv-stats-end", now, &contents) > 0) {
|
|
smartlist_add(chunks, contents);
|
|
}
|
|
if (options->EntryStatistics &&
|
|
load_stats_file("stats"PATH_SEPARATOR"entry-stats",
|
|
"entry-stats-end", now, &contents) > 0) {
|
|
smartlist_add(chunks, contents);
|
|
}
|
|
if (options->CellStatistics &&
|
|
load_stats_file("stats"PATH_SEPARATOR"buffer-stats",
|
|
"cell-stats-end", now, &contents) > 0) {
|
|
smartlist_add(chunks, contents);
|
|
}
|
|
if (options->ExitPortStatistics &&
|
|
load_stats_file("stats"PATH_SEPARATOR"exit-stats",
|
|
"exit-stats-end", now, &contents) > 0) {
|
|
smartlist_add(chunks, contents);
|
|
}
|
|
if (options->ConnDirectionStatistics &&
|
|
load_stats_file("stats"PATH_SEPARATOR"conn-stats",
|
|
"conn-bi-direct", now, &contents) > 0) {
|
|
smartlist_add(chunks, contents);
|
|
}
|
|
if (options->PaddingStatistics) {
|
|
contents = rep_hist_get_padding_count_lines();
|
|
if (contents)
|
|
smartlist_add(chunks, contents);
|
|
}
|
|
/* bridge statistics */
|
|
if (should_record_bridge_info(options)) {
|
|
const char *bridge_stats = geoip_get_bridge_stats_extrainfo(now);
|
|
if (bridge_stats) {
|
|
smartlist_add_strdup(chunks, bridge_stats);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Add an ed25519 signature of chunks to chunks, using the ed25519 keypair
|
|
* signing_keypair.
|
|
* Helper for extrainfo_dump_to_string().
|
|
* Returns 0 on success, negative on failure. */
|
|
static int
|
|
extrainfo_dump_to_string_ed_sig_helper(
|
|
smartlist_t *chunks,
|
|
const ed25519_keypair_t *signing_keypair)
|
|
{
|
|
char sha256_digest[DIGEST256_LEN];
|
|
ed25519_signature_t ed_sig;
|
|
char buf[ED25519_SIG_BASE64_LEN+1];
|
|
int rv = -1;
|
|
|
|
/* These are two of the three final chunks in the file. If the file is too
|
|
* big, other chunks are removed. So we must only add two chunks here. */
|
|
smartlist_add_strdup(chunks, "router-sig-ed25519 ");
|
|
crypto_digest_smartlist_prefix(sha256_digest, DIGEST256_LEN,
|
|
ED_DESC_SIGNATURE_PREFIX,
|
|
chunks, "", DIGEST_SHA256);
|
|
if (ed25519_sign(&ed_sig, (const uint8_t*)sha256_digest, DIGEST256_LEN,
|
|
signing_keypair) < 0)
|
|
goto err;
|
|
ed25519_signature_to_base64(buf, &ed_sig);
|
|
|
|
smartlist_add_asprintf(chunks, "%s\n", buf);
|
|
|
|
rv = 0;
|
|
goto done;
|
|
|
|
err:
|
|
rv = -1;
|
|
|
|
done:
|
|
return rv;
|
|
}
|
|
|
|
/** Add an RSA signature of extrainfo_string to chunks, using the RSA key
|
|
* ident_key.
|
|
* Helper for extrainfo_dump_to_string().
|
|
* Returns 0 on success, negative on failure. */
|
|
static int
|
|
extrainfo_dump_to_string_rsa_sig_helper(smartlist_t *chunks,
|
|
crypto_pk_t *ident_key,
|
|
const char *extrainfo_string)
|
|
{
|
|
char sig[DIROBJ_MAX_SIG_LEN+1];
|
|
char digest[DIGEST_LEN];
|
|
int rv = -1;
|
|
|
|
memset(sig, 0, sizeof(sig));
|
|
if (router_get_extrainfo_hash(extrainfo_string, strlen(extrainfo_string),
|
|
digest) < 0 ||
|
|
router_append_dirobj_signature(sig, sizeof(sig), digest, DIGEST_LEN,
|
|
ident_key) < 0) {
|
|
log_warn(LD_BUG, "Could not append signature to extra-info "
|
|
"descriptor.");
|
|
goto err;
|
|
}
|
|
smartlist_add_strdup(chunks, sig);
|
|
|
|
rv = 0;
|
|
goto done;
|
|
|
|
err:
|
|
rv = -1;
|
|
|
|
done:
|
|
return rv;
|
|
}
|
|
|
|
/** Write the contents of <b>extrainfo</b>, to * *<b>s_out</b>, signing them
|
|
* with <b>ident_key</b>.
|
|
*
|
|
* If ExtraInfoStatistics is 1, also write aggregated statistics and related
|
|
* configuration data before signing. Most statistics also have an option that
|
|
* enables or disables that particular statistic.
|
|
*
|
|
* Always write pluggable transport lines.
|
|
*
|
|
* Return 0 on success, negative on failure. */
|
|
int
|
|
extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
|
|
crypto_pk_t *ident_key,
|
|
const ed25519_keypair_t *signing_keypair)
|
|
{
|
|
int result;
|
|
static int write_stats_to_extrainfo = 1;
|
|
char *s = NULL, *cp, *s_dup = NULL;
|
|
smartlist_t *chunks = smartlist_new();
|
|
extrainfo_t *ei_tmp = NULL;
|
|
const int emit_ed_sigs = signing_keypair &&
|
|
extrainfo->cache_info.signing_key_cert;
|
|
int rv = 0;
|
|
|
|
rv = extrainfo_dump_to_string_header_helper(chunks, extrainfo,
|
|
signing_keypair,
|
|
emit_ed_sigs);
|
|
if (rv < 0)
|
|
goto err;
|
|
|
|
extrainfo_dump_to_string_stats_helper(chunks, write_stats_to_extrainfo);
|
|
|
|
if (emit_ed_sigs) {
|
|
rv = extrainfo_dump_to_string_ed_sig_helper(chunks, signing_keypair);
|
|
if (rv < 0)
|
|
goto err;
|
|
}
|
|
|
|
/* This is one of the three final chunks in the file. If the file is too big,
|
|
* other chunks are removed. So we must only add one chunk here. */
|
|
smartlist_add_strdup(chunks, "router-signature\n");
|
|
s = smartlist_join_strings(chunks, "", 0, NULL);
|
|
|
|
while (strlen(s) > MAX_EXTRAINFO_UPLOAD_SIZE - DIROBJ_MAX_SIG_LEN) {
|
|
/* So long as there are at least two chunks (one for the initial
|
|
* extra-info line and one for the router-signature), we can keep removing
|
|
* things. If emit_ed_sigs is true, we also keep 2 additional chunks at the
|
|
* end for the ed25519 signature. */
|
|
const int required_chunks = emit_ed_sigs ? 4 : 2;
|
|
if (smartlist_len(chunks) > required_chunks) {
|
|
/* We remove the next-to-last or 4th-last element (remember, len-1 is the
|
|
* last element), since we need to keep the router-signature elements. */
|
|
int idx = smartlist_len(chunks) - required_chunks;
|
|
char *e = smartlist_get(chunks, idx);
|
|
smartlist_del_keeporder(chunks, idx);
|
|
log_warn(LD_GENERAL, "We just generated an extra-info descriptor "
|
|
"with statistics that exceeds the 50 KB "
|
|
"upload limit. Removing last added "
|
|
"statistics.");
|
|
tor_free(e);
|
|
tor_free(s);
|
|
s = smartlist_join_strings(chunks, "", 0, NULL);
|
|
} else {
|
|
log_warn(LD_BUG, "We just generated an extra-info descriptors that "
|
|
"exceeds the 50 KB upload limit.");
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
rv = extrainfo_dump_to_string_rsa_sig_helper(chunks, ident_key, s);
|
|
if (rv < 0)
|
|
goto err;
|
|
|
|
tor_free(s);
|
|
s = smartlist_join_strings(chunks, "", 0, NULL);
|
|
|
|
cp = s_dup = tor_strdup(s);
|
|
ei_tmp = extrainfo_parse_entry_from_string(cp, NULL, 1, NULL, NULL);
|
|
if (!ei_tmp) {
|
|
if (write_stats_to_extrainfo) {
|
|
log_warn(LD_GENERAL, "We just generated an extra-info descriptor "
|
|
"with statistics that we can't parse. Not "
|
|
"adding statistics to this or any future "
|
|
"extra-info descriptors.");
|
|
write_stats_to_extrainfo = 0;
|
|
result = extrainfo_dump_to_string(s_out, extrainfo, ident_key,
|
|
signing_keypair);
|
|
goto done;
|
|
} else {
|
|
log_warn(LD_BUG, "We just generated an extrainfo descriptor we "
|
|
"can't parse.");
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
*s_out = s;
|
|
s = NULL; /* prevent free */
|
|
result = 0;
|
|
goto done;
|
|
|
|
err:
|
|
result = -1;
|
|
|
|
done:
|
|
tor_free(s);
|
|
SMARTLIST_FOREACH(chunks, char *, chunk, tor_free(chunk));
|
|
smartlist_free(chunks);
|
|
tor_free(s_dup);
|
|
extrainfo_free(ei_tmp);
|
|
|
|
return result;
|
|
}
|
|
|
|
/** Forget that we have issued any router-related warnings, so that we'll
|
|
* warn again if we see the same errors. */
|
|
void
|
|
router_reset_warnings(void)
|
|
{
|
|
if (warned_family) {
|
|
SMARTLIST_FOREACH(warned_family, char *, cp, tor_free(cp));
|
|
smartlist_clear(warned_family);
|
|
}
|
|
}
|
|
|
|
/** Release all static resources held in router.c */
|
|
void
|
|
router_free_all(void)
|
|
{
|
|
crypto_pk_free(onionkey);
|
|
crypto_pk_free(lastonionkey);
|
|
crypto_pk_free(server_identitykey);
|
|
crypto_pk_free(client_identitykey);
|
|
|
|
/* Destroying a locked mutex is undefined behaviour. This mutex may be
|
|
* locked, because multiple threads can access it. But we need to destroy
|
|
* it, otherwise re-initialisation will trigger undefined behaviour.
|
|
* See #31735 for details. */
|
|
tor_mutex_free(key_lock);
|
|
routerinfo_free(desc_routerinfo);
|
|
extrainfo_free(desc_extrainfo);
|
|
crypto_pk_free(authority_signing_key);
|
|
authority_cert_free(authority_key_certificate);
|
|
crypto_pk_free(legacy_signing_key);
|
|
authority_cert_free(legacy_key_certificate);
|
|
|
|
memwipe(&curve25519_onion_key, 0, sizeof(curve25519_onion_key));
|
|
memwipe(&last_curve25519_onion_key, 0, sizeof(last_curve25519_onion_key));
|
|
|
|
if (warned_family) {
|
|
SMARTLIST_FOREACH(warned_family, char *, cp, tor_free(cp));
|
|
smartlist_free(warned_family);
|
|
}
|
|
}
|
|
|
|
/* From the given RSA key object, convert it to ASN-1 encoded format and set
|
|
* the newly allocated object in onion_pkey_out. The length of the key is set
|
|
* in onion_pkey_len_out. */
|
|
void
|
|
router_set_rsa_onion_pkey(const crypto_pk_t *pk, char **onion_pkey_out,
|
|
size_t *onion_pkey_len_out)
|
|
{
|
|
int len;
|
|
char buf[1024];
|
|
|
|
tor_assert(pk);
|
|
tor_assert(onion_pkey_out);
|
|
tor_assert(onion_pkey_len_out);
|
|
|
|
len = crypto_pk_asn1_encode(pk, buf, sizeof(buf));
|
|
if (BUG(len < 0)) {
|
|
goto done;
|
|
}
|
|
|
|
*onion_pkey_out = tor_memdup(buf, len);
|
|
*onion_pkey_len_out = len;
|
|
|
|
done:
|
|
return;
|
|
}
|
|
|
|
/* From an ASN-1 encoded onion pkey, return a newly allocated RSA key object.
|
|
* It is the caller responsability to free the returned object.
|
|
*
|
|
* Return NULL if the pkey is NULL, malformed or if the length is 0. */
|
|
crypto_pk_t *
|
|
router_get_rsa_onion_pkey(const char *pkey, size_t pkey_len)
|
|
{
|
|
if (!pkey || pkey_len == 0) {
|
|
return NULL;
|
|
}
|
|
return crypto_pk_asn1_decode(pkey, pkey_len);
|
|
}
|