V(MetricsPortPolicy, LINELIST, NULL),
V(TestingMinTimeToReportBandwidth, INTERVAL, "1 day"),
VAR("MyFamily", LINELIST, MyFamily_lines, NULL),
+ V(UseFamilyKeys, BOOL, "0"),
V(NewCircuitPeriod, INTERVAL, "30 seconds"),
OBSOLETE("NamingAuthoritativeDirectory"),
OBSOLETE("NATDListenAddress"),
struct config_line_t *MyFamily_lines; /**< Declared family for this OR. */
struct config_line_t *MyFamily; /**< Declared family for this OR,
normalized */
+ int UseFamilyKeys; /**< If set, we use one or more family keys
+ * to certify this OR's membership. */
struct config_line_t *NodeFamilies; /**< List of config lines for
* node families */
/** List of parsed NodeFamilies values. */
generate_ed_link_cert(options, now, new_signing_key > 0)) {
log_warn(LD_OR, "Problem reloading Ed25519 keys; still using old keys.");
}
+ const networkstatus_t *ns = networkstatus_get_latest_consensus();
+ if (load_family_id_keys(options, ns)) {
+ log_warn(LD_OR, "Problem reloading family ID keys; "
+ "still using old keys.");
+ }
/* Update cpuworker and dnsworker processes, so they get up-to-date
* configuration options. */
YES_IF_CHANGED_STRING(ContactInfo);
YES_IF_CHANGED_STRING(BridgeDistribution);
YES_IF_CHANGED_LINELIST(MyFamily);
+ YES_IF_CHANGED_BOOL(UseFamilyKeys);
YES_IF_CHANGED_STRING(AccountingStart);
YES_IF_CHANGED_INT(AccountingMax);
YES_IF_CHANGED_INT(AccountingRule);
if (new_signing_key < 0)
return -1;
+ if (options->command == CMD_RUN_TOR) {
+ if (load_family_id_keys(options, networkstatus_get_latest_consensus()) < 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);
publish_even_when_ipv4_orport_unreachable = ar;
publish_even_when_ipv6_orport_unreachable = ar || ar6;
+
+ warn_about_family_id_config(get_options(), ns);
+}
+
+/**
+ * Return true if the parameters in `ns` say that we should publish
+ * a legacy family list.
+ *
+ * Use the latest networkstatus (or returns the default) if `ns` is NULL.
+ */
+bool
+should_publish_family_list(const networkstatus_t *ns)
+{
+ return networkstatus_get_param(ns, "publish-family-list",
+ 1, 0, 1); // default, min, max
}
/** Mark our descriptor out of data iff the IPv6 omit status flag is flipped
int should_refuse_unknown_exits(const or_options_t *options);
void router_new_consensus_params(const networkstatus_t *);
+bool should_publish_family_list(const networkstatus_t *ns);
+
void router_upload_dir_desc_to_dirservers(int force);
void mark_my_descriptor_dirty_if_too_old(time_t now);
void mark_my_descriptor_dirty(const char *reason);
* (TODO: The keys in router.c should go here too.)
*/
+#define ROUTERKEYS_PRIVATE
+
#include "core/or/or.h"
#include "app/config/config.h"
#include "feature/relay/router.h"
#include "feature/relay/routermode.h"
#include "feature/keymgt/loadkey.h"
#include "feature/nodelist/torcert.h"
+#include "feature/nodelist/networkstatus_st.h"
+#include "feature/dirauth/dirvote.h"
#include "lib/crypt_ops/crypto_util.h"
#include "lib/tls/tortls.h"
return auth_key_cert;
}
+/**
+ * Prefix for the filename in which we expect to find a family ID key.
+ */
+#define FAMILY_KEY_FNAME "secret_family_key"
+
+/**
+ * Return true if `fname` is a possible filename of a family ID key.
+ *
+ * Family ID key filenames are FAMILY_KEY_FNAME, followed optionally
+ * by "." and a positive integer.
+ */
+STATIC bool
+is_family_key_fname(const char *fname)
+{
+ if (0 == strcmp(fname, FAMILY_KEY_FNAME))
+ return true;
+
+ unsigned num;
+ char ch;
+ if (tor_sscanf(fname, FAMILY_KEY_FNAME".%u%c", &num, &ch) == 1)
+ return true;
+
+ return false;
+}
+
+/**
+ * Tag to use on family key files.
+ */
+#define FAMILY_KEY_FILE_TAG "fmly-id"
+
+/**
+ * Look for all the family keys in `keydir`, load them into
+ * family_id_keys.
+ */
+STATIC int
+load_family_id_keys_impl(const char *keydir)
+{
+ if (BUG(!keydir))
+ return -1;
+
+ smartlist_t *files = tor_listdir(keydir);
+ smartlist_t *new_keys = NULL;
+ ed25519_keypair_t *kp_tmp = NULL;
+ char *fn_tmp = NULL;
+ char *tag_tmp = NULL;
+ int r = -1;
+
+ if (files == NULL) {
+ log_warn(LD_OR, "Unable to list contents of directory %s", keydir);
+ goto end;
+ }
+
+ new_keys = smartlist_new();
+ SMARTLIST_FOREACH_BEGIN(files, const char *, fn) {
+ if (!is_family_key_fname(fn))
+ continue;
+
+ tor_asprintf(&fn_tmp, "%s%s%s", keydir, PATH_SEPARATOR, fn);
+
+ kp_tmp = tor_malloc_zero(sizeof(*kp_tmp));
+ // TODO: If we ever allow cert provisioning here,
+ // use ed_key_init_from_file() instead.
+ if (ed25519_seckey_read_from_file(&kp_tmp->seckey, &tag_tmp, fn_tmp) < 0) {
+ log_warn(LD_OR, "%s was not an ed25519 secret key.", fn_tmp);
+ goto end;
+ }
+ if (0 != strcmp(tag_tmp, FAMILY_KEY_FILE_TAG)) {
+ log_warn(LD_OR, "%s was not a family ID key.", fn_tmp);
+ goto end;
+ }
+ if (ed25519_public_key_generate(&kp_tmp->pubkey, &kp_tmp->seckey) < 0) {
+ log_warn(LD_OR, "Unable to generate public key for %s", fn_tmp);
+ goto end;
+ }
+
+ smartlist_add(new_keys, kp_tmp);
+ kp_tmp = NULL; // prevent double-free
+
+ tor_free(fn_tmp);
+ tor_free(tag_tmp);
+ } SMARTLIST_FOREACH_END(fn);
+
+ set_family_id_keys(new_keys);
+ new_keys = NULL; // prevent double-free
+ r = 0;
+ end:
+ if (files) {
+ SMARTLIST_FOREACH(files, char *, cp, tor_free(cp));
+ smartlist_free(files);
+ }
+ if (new_keys) {
+ SMARTLIST_FOREACH(new_keys, ed25519_keypair_t *, kp,
+ ed25519_keypair_free(kp));
+ smartlist_free(new_keys);
+ }
+ tor_free(fn_tmp);
+ tor_free(tag_tmp);
+ ed25519_keypair_free(kp_tmp);
+ return r;
+}
+
+/**
+ * Create a new family ID key, and store it in `fname`.
+ **/
+int
+create_family_id_key(const char *fname)
+{
+ int r = -1;
+ ed25519_keypair_t *kp = tor_malloc_zero(sizeof(ed25519_keypair_t));
+ if (ed25519_keypair_generate(kp, 1) < 0) {
+ log_warn(LD_BUG, "Can't generate ed25519 key!");
+ goto done;
+ }
+
+ if (ed25519_seckey_write_to_file(&kp->seckey,
+ fname, FAMILY_KEY_FILE_TAG)<0) {
+ log_warn(LD_BUG, "Can't write key to file.");
+ goto done;
+ }
+
+ r = 0;
+
+ done:
+ ed25519_keypair_free(kp);
+ return r;
+}
+
+/**
+ * If configured to do so, load our family keys from the key directory.
+ * Otherwise, clear the family keys.
+ *
+ * Additionally, warn about inconsistencies between family options.
+ * If `ns` is provided, provide additional warnings.
+ *
+ * `options` is required; `ns` may be NULL.
+ */
+int
+load_family_id_keys(const or_options_t *options,
+ const networkstatus_t *ns)
+{
+ if (options->UseFamilyKeys) {
+ if (load_family_id_keys_impl(options->KeyDirectory) < 0)
+ return -1;
+
+ // This warning is _here_ because we want to give it (or not)
+ // every time keys are reloaded.
+ if (!smartlist_len(get_current_family_id_keys())) {
+ log_warn(LD_OR,
+ "UseFamilyKeys was configured, but no family keys were found. "
+ "Family keys need to be in %s, with names like "
+ FAMILY_KEY_FNAME", "FAMILY_KEY_FNAME".1, "
+ FAMILY_KEY_FNAME".2, etc. "
+ "See (XXXX INSERT URL HERE) for instructions.",
+ options->KeyDirectory);
+ } else {
+ log_info(LD_OR, "Found %d family ID keys",
+ smartlist_len(get_current_family_id_keys()));
+ }
+ } else {
+ set_family_id_keys(NULL);
+ }
+ warn_about_family_id_config(options, ns);
+ return 0;
+}
+
+/** Generate warnings as appropriate about our family ID configuration.
+ *
+ * `options` is required; `ns` may be NULL.
+ */
+void
+warn_about_family_id_config(const or_options_t *options,
+ const networkstatus_t *ns)
+{
+ static int have_warned_absent_myfamily = 0;
+ static int have_warned_absent_familykeys = 0;
+
+ if (options->UseFamilyKeys) {
+ if (!have_warned_absent_myfamily &&
+ !options->MyFamily && ns && should_publish_family_list(ns)) {
+ log_warn(LD_OR,
+ "UseFamilyKeys was configured, but MyFamily was not. "
+ "UseFamilyKeys is good, but the Tor network still requires "
+ "MyFamily while clients are migrating to use family "
+ "keys instead.");
+ have_warned_absent_myfamily = 1;
+ }
+ } else {
+ if (!have_warned_absent_familykeys &&
+ options->MyFamily &&
+ ns && ns->consensus_method >= MIN_METHOD_FOR_FAMILY_IDS) {
+ log_notice(LD_OR,
+ "MyFamily was configured, but UseFamilyKeys was not. "
+ "It's a good time to start migrating your relays "
+ "to use family keys. "
+ "See (XXXX INSERT URL HERE) for instructions.");
+ have_warned_absent_familykeys = 1;
+ }
+ }
+}
+
/**
* Return a list of our current family id keypairs,
* as a list of `ed25519_keypair_t`.
return family_id_keys;
}
-#ifdef TOR_UNIT_TESTS
/**
- * Testing only: Replace our list of family ID keys with `family_id_keys`,
+ * Replace our list of family ID keys with `family_id_keys`,
* which must be a list of `ed25519_keypair_t`.
*
* Takes ownership of its input.
*/
-void
-set_mock_family_id_keys(smartlist_t *keys)
+STATIC void
+set_family_id_keys(smartlist_t *keys)
{
if (family_id_keys) {
SMARTLIST_FOREACH(family_id_keys, ed25519_keypair_t *, kp,
}
family_id_keys = keys;
}
-#endif
void
get_master_rsa_crosscert(const uint8_t **cert_out,
ed25519_keypair_free(master_identity_key);
ed25519_keypair_free(master_signing_key);
ed25519_keypair_free(current_auth_key);
- if (family_id_keys) {
- SMARTLIST_FOREACH(family_id_keys, ed25519_keypair_t *, kp,
- ed25519_keypair_free(kp));
- smartlist_free(family_id_keys);
- }
+ set_family_id_keys(NULL);
tor_cert_free(signing_key_cert);
tor_cert_free(link_cert_cert);
int log_cert_expiration(void);
int load_ed_keys(const or_options_t *options, time_t now);
+int load_family_id_keys(const or_options_t *options,
+ const networkstatus_t *ns);
+int create_family_id_key(const char *fname);
+void warn_about_family_id_config(const or_options_t *options,
+ const networkstatus_t *ns);
int should_make_new_ed_keys(const or_options_t *options, const time_t now);
int generate_ed_link_cert(const or_options_t *options, time_t now, int force);
* CMD_KEYGEN. */
#define load_ed_keys(x,y) \
(puts("Not available: Tor has been compiled without relay support"), 0)
+#define load_family_id_keys(x,y) \
+ (puts("Not available: Tor has been compiled without relay support"), 0)
#endif /* defined(HAVE_MODULE_RELAY) */
#ifdef TOR_UNIT_TESTS
const ed25519_keypair_t *get_master_identity_keypair(void);
void init_mock_ed_keys(const crypto_pk_t *rsa_identity_key);
-void set_mock_family_id_keys(smartlist_t *keys);
+#endif
+
+#ifdef ROUTERKEYS_PRIVATE
+STATIC void set_family_id_keys(smartlist_t *keys);
+STATIC bool is_family_key_fname(const char *fname);
+STATIC int load_family_id_keys_impl(const char *keydir);
#endif
#endif /* !defined(TOR_ROUTERKEYS_H) */
#define RELAY_PRIVATE
#define ROUTERLIST_PRIVATE
#define ROUTER_PRIVATE
+#define ROUTERKEYS_PRIVATE
#define ROUTERPARSE_PRIVATE
#define UNPARSEABLE_PRIVATE
#define VOTEFLAGS_PRIVATE
smartlist_t *family_keys = smartlist_new();
smartlist_add(family_keys, tor_memdup(&family_1, sizeof(family_1)));
smartlist_add(family_keys, tor_memdup(&family_2, sizeof(family_2)));
- set_mock_family_id_keys(family_keys); // takes ownership.
+ set_family_id_keys(family_keys); // takes ownership.
}
buf = router_dump_router_to_string(r2, r2->identity_pkey,
#include "orconfig.h"
#define ROUTER_PRIVATE
+#define ROUTERKEYS_PRIVATE
#include "core/or/or.h"
#include "app/config/config.h"
#include "feature/relay/router.h"
tor_free(cc);
}
+static void
+test_routerkeys_family_key_fname(void *arg)
+{
+ (void)arg;
+
+ tt_assert(is_family_key_fname("secret_family_key"));
+ tt_assert(is_family_key_fname("secret_family_key.1"));
+ tt_assert(is_family_key_fname("secret_family_key.413"));
+ tt_assert(! is_family_key_fname("secret_family_key.413x"));
+ tt_assert(! is_family_key_fname("secret_family_key1"));
+ tt_assert(! is_family_key_fname("secret_family_key.hello"));
+ tt_assert(! is_family_key_fname("secret_family_key.-1"));
+ tt_assert(! is_family_key_fname("fun_with_filenames.1"));
+ tt_assert(! is_family_key_fname("fun_with_filenames"));
+
+ done:
+ ;
+}
+
+static void
+test_routerkeys_load_family_keys(void *arg)
+{
+ (void) arg;
+ char *dname = tor_strdup(get_fname_rnd("fkeys"));
+ char *fname = NULL;
+
+#ifdef _WIN32
+ tt_assert(0==mkdir(dname));
+#else
+ tt_assert(0==mkdir(dname,0700));
+#endif
+
+ // Not a family key, will be ignored
+ tor_asprintf(&fname, "%s"PATH_SEPARATOR"junk.1", dname);
+ write_str_to_file(fname, "hello world", 0);
+ tor_free(fname);
+
+ tt_int_op(0, OP_EQ, load_family_id_keys_impl(dname));
+ tt_int_op(0, OP_EQ, smartlist_len(get_current_family_id_keys()));
+
+ // Create a family key; make sure we can load it.
+ tor_asprintf(&fname, "%s"PATH_SEPARATOR"secret_family_key.12", dname);
+ tt_int_op(0, OP_EQ, create_family_id_key(fname));
+ tor_free(fname);
+
+ tt_int_op(0, OP_EQ, load_family_id_keys_impl(dname));
+ tt_int_op(1, OP_EQ, smartlist_len(get_current_family_id_keys()));
+
+ //Try a second key.
+ tor_asprintf(&fname, "%s"PATH_SEPARATOR"secret_family_key.413", dname);
+ tt_int_op(0, OP_EQ, create_family_id_key(fname));
+ tor_free(fname);
+
+ tt_int_op(0, OP_EQ, load_family_id_keys_impl(dname));
+ tt_int_op(2, OP_EQ, smartlist_len(get_current_family_id_keys()));
+
+ // Make a junk key, make sure it causes an error.
+ tor_asprintf(&fname, "%s"PATH_SEPARATOR"secret_family_key.6", dname);
+ write_str_to_file(fname, "hello world", 0);
+ tor_free(fname);
+
+ tt_int_op(-1, OP_EQ, load_family_id_keys_impl(dname));
+ // keys unchanged
+ tt_int_op(2, OP_EQ, smartlist_len(get_current_family_id_keys()));
+
+ done:
+ tor_free(dname);
+ tor_free(fname);
+}
+
#define TEST(name, flags) \
{ #name , test_routerkeys_ ## name, (flags), NULL, NULL }
TEST(cross_certify_ntor, 0),
TEST(cross_certify_tap, 0),
TEST(rsa_ed_crosscert, 0),
+ TEST(family_key_fname, 0),
+ TEST(load_family_keys, TT_FORK),
END_OF_TESTCASES
};