Skip to content
Snippets Groups Projects
router.c 121 KiB
Newer Older
  • Learn to ignore specific revisions
  • Roger Dingledine's avatar
    Roger Dingledine committed
    /* Copyright (c) 2001 Matej Pfajfar.
     * Copyright (c) 2001-2004, Roger Dingledine.
    
     * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
    
     * Copyright (c) 2007-2021, The Tor Project, Inc. */
    
    /* See LICENSE for licensing information */
    
    
    #include "core/or/or.h"
    #include "app/config/config.h"
    
    #include "app/config/resolve_addr.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/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/relay_find_addr.h"
    
    #include "feature/relay/relay_periodic.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/bwhist.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"
    
    Roger Dingledine's avatar
    Roger Dingledine committed
    /**
     * \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.)
    
    Roger Dingledine's avatar
    Roger Dingledine committed
     **/
    
    Nick Mathewson's avatar
    Nick Mathewson committed
    
    
    /************************************************************/
    
    
    Nick Mathewson's avatar
    Nick Mathewson committed
    /*****
     * Key management: ORs only.
     *****/
    
    
    Roger Dingledine's avatar
    Roger Dingledine committed
    /** Private keys for this OR.  There is also an SSL key managed by tortls.c.
    
    Nick Mathewson's avatar
    Nick Mathewson committed
     */
    
    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;
    
    Nick Mathewson's avatar
    Nick Mathewson committed
    /** Previous private onionskin decryption key: used to decode CREATE cells
    
    Roger Dingledine's avatar
    Roger Dingledine committed
     * 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. */
    
    
    /** Indicate if the IPv6 address should be omitted from the descriptor when
     * publishing it. This can happen if the IPv4 is reachable but the
     * auto-discovered IPv6 is not. We still publish the descriptor.
     *
     * Only relays should look at this and only for their descriptor.
     *
     * XXX: The real harder fix is to never put in the routerinfo_t a non
     * reachable address and instead use the last resolved address cache to do
     * reachability test or anything that has to do with what address tor thinks
     * it has. */
    static bool omit_ipv6_on_publish = false;
    
    
    /** 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";
    
    rl1987's avatar
    rl1987 committed
        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";
    
    rl1987's avatar
    rl1987 committed
      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://bugs.torproject.org/tpo/core/tor/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().
    
    Nick Mathewson's avatar
    Nick Mathewson committed
     */
    
    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);
    
      tor_mutex_acquire(key_lock);
    
      crypto_pk_free(onionkey);
    
      tor_mutex_release(key_lock);
    
      mark_my_descriptor_dirty("set onion key");
    
    Roger Dingledine's avatar
    Roger Dingledine committed
    /** Return the current onion key.  Requires that the onion key has been
    
    Nick Mathewson's avatar
    Nick Mathewson committed
     * loaded or generated. */
    
    MOCK_IMPL(crypto_pk_t *,
    get_onion_key,(void))
    
    Roger Dingledine's avatar
    Roger Dingledine committed
      tor_assert(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.
    
    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
    
        *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 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. */
    
    ntor_key_map_free_(di_digest256_map_t *map)
    
      dimap_free(map, ntor_key_map_free_helper);
    }
    
    
    Roger Dingledine's avatar
    Roger Dingledine committed
    /** Return the time when the onion key was last set.  This is either the time
    
    Nick Mathewson's avatar
    Nick Mathewson committed
     * when the process launched, or the time of the most recent key rotation since
     * the process launched.
     */
    
      return onionkey_set_at;
    
    /** Set the current server identity key to <b>k</b>.
    
    Nick Mathewson's avatar
    Nick Mathewson committed
     */
    
    set_server_identity_key(crypto_pk_t *k)
    
      crypto_pk_free(server_identitykey);
    
      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);
    }
    
    Nick Mathewson's avatar
    Nick Mathewson committed
    #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)
    {
    
      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));
    
    /** Returns the current server identity key; requires that the key has
    
     * been set, and that we are running as a Tor server.
    
    Nick Mathewson's avatar
    Nick Mathewson committed
     */
    
    MOCK_IMPL(crypto_pk_t *,
    get_server_identity_key,(void))
    
      tor_assert(server_identitykey);
    
    Neel Chauhan's avatar
    Neel Chauhan committed
      tor_assert(server_mode(get_options()) ||
                 get_options()->command == CMD_KEY_EXPIRATION);
    
      assert_identity_keys_ok();
    
    Nick Mathewson's avatar
    Nick Mathewson committed
    #endif /* defined(HAVE_MODULE_RELAY) */
    
    Nick Mathewson's avatar
    Nick Mathewson committed
    /** Return true iff we are a server and the server identity key
     * has been set. */
    
    server_identity_key_is_set(void)
    
    Neel Chauhan's avatar
    Neel Chauhan committed
      return (server_mode(get_options()) ||
              get_options()->command == CMD_KEY_EXPIRATION) &&
             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);
    
    /** 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)
    
      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;
    }
    
    
    Roger Dingledine's avatar
    Roger Dingledine committed
    /** 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:
    
    Roger Dingledine's avatar
    Roger Dingledine committed
     *   - 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.
    
      char *fname, *fname_prev;
    
      or_state_t *state = get_or_state();
    
      curve25519_keypair_t new_curve25519_keypair;
    
      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;
    
      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);
    
      log_warn(LD_GENERAL, "Couldn't rotate onion key.");
    
        crypto_pk_free(prkey);
    
      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/lifecycle-of-a"
             "-new-relay");
    
    /** 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);
    
        /* 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_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
    
    Nick Mathewson's avatar
    Nick Mathewson committed
     * *<b>cert_out</b> respectively, and return 0.  On failure, return -1. */
    
    load_authority_keyset(int legacy, crypto_pk_t **key_out,
    
                          authority_cert_t **cert_out)
    
      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;
      }
    
      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);
    
      *key_out = signing_key;
      *cert_out = parsed;
      r = 0;
    
    
     done:
      tor_free(fname);
      tor_free(cert);
    
      crypto_pk_free(signing_key);
    
    /** 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;
    }
    
    
    Roger Dingledine's avatar
    Roger Dingledine committed
    /** 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));
    
        tor_log(badness, LD_DIR, "Your v3 authority certificate expires in %d "
                "days; Generate a new one soon.", time_left/(24*60*60));
    
    /** 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;
    }
    
    
    Nick Mathewson's avatar
    Nick Mathewson committed
    /** 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. */
    
                                  get_tlsclient_identity_key(),
    
                                  server_mode(options) ?
    
                                  get_server_identity_key() : NULL,
    
                                  (unsigned int)lifetime);
    
    /** Announce URL to bridge status page. */
    STATIC void
    router_announce_bridge_status_page(void)
    {
      char fingerprint[FINGERPRINT_LEN + 1];
    
      if (crypto_pk_get_hashed_fingerprint(get_server_identity_key(),
                                           fingerprint) < 0) {
        // LCOV_EXCL_START
        log_err(LD_GENERAL, "Unable to compute bridge fingerprint");
        return;
        // LCOV_EXCL_STOP
      }
    
      log_notice(LD_GENERAL, "You can check the status of your bridge relay at "
                             "https://bridges.torproject.org/status?id=%s",
                             fingerprint);
    }
    
    
    /** 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,
     */
    
    router_write_fingerprint(int hashed, int ed25519_identity)
    
      char *keydir = NULL;
    
      const char *fname = hashed ? "hashed-fingerprint" :
    
                          (ed25519_identity ? "fingerprint-ed25519" :
                                              "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 %s%s to \"%s\"...", hashed ? "hashed " : "",
               ed25519_identity ? "ed25519 identity" : "fingerprint", keydir);
    
      if (ed25519_identity) { /* ed25519 identity */
        digest256_to_base64(fingerprint, (const char *)
                                         get_master_identity_key()->pubkey);
      } else { /* RSA identity */
        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. */
    
      if (write_str_to_file_if_not_equal(keydir, fingerprint_line)) {
    
        log_err(LD_FS, "Error writing %s%s line to file",
                hashed ? "hashed " : "",
                ed25519_identity ? "ed25519 identity" : "fingerprint");
        goto done;
    
      log_notice(LD_GENERAL, "Your Tor %s identity key %sfingerprint is '%s %s'",
    
                 hashed ? "bridge's hashed" : "server's",
    
                 ed25519_identity ? "ed25519 " : "",
    
                 options->Nickname, fingerprint);
    
    
      result = 0;
     done:
      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;
    }
    
    
    Roger Dingledine's avatar
    Roger Dingledine committed
    /** 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.
    
    Nick Mathewson's avatar
    Nick Mathewson committed
     */
    
      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();
    
      time_t now = time(NULL);
    
      int v3_digest_set = 0;
    
      /* OP's don't need persistent keys; just make up an identity and
    
    Neel Chauhan's avatar
    Neel Chauhan committed
      if (!server_mode(options) && !(options->command == CMD_KEY_EXPIRATION)) {
    
      if (init_keys_common() < 0)
        return -1;
    
    
      if (create_keys_directory(options) < 0)
    
      /* 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 "