From 61ad81c36e46d7b05b66601520c7ff42469f24d7 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Wed, 22 Aug 2018 11:38:28 -0400 Subject: [PATCH] hs: Learn service version by trying to load the keys In order to switch the default HS version from 2 to 3, we need tor to be smart and be able to decide on the version by trying to load the service keys during configuration validation. Part of #27215 Signed-off-by: David Goulet --- src/feature/hs/hs_config.c | 24 +++++++++++++++ src/feature/hs/hs_service.c | 53 ++++++++++++++++++++++++++++++++++ src/feature/hs/hs_service.h | 1 + src/feature/rend/rendservice.c | 23 +++++++++++++++ src/feature/rend/rendservice.h | 1 + 5 files changed, 102 insertions(+) diff --git a/src/feature/hs/hs_config.c b/src/feature/hs/hs_config.c index 2c25a168a2..e972576482 100644 --- a/src/feature/hs/hs_config.c +++ b/src/feature/hs/hs_config.c @@ -143,6 +143,24 @@ helper_parse_uint64(const char *opt, const char *value, uint64_t min, return ret; } +/* Return the service version by trying to learn it from the key on disk if + * any. If nothing is found, the current service configured version is + * returned. */ +static int +config_learn_service_version(hs_service_t *service) +{ + int version; + + tor_assert(service); + + version = hs_service_get_version_from_key(service); + if (version < 0) { + version = service->config.version; + } + + return version; +} + /* Return true iff the given options starting at line_ for a hidden service * contains at least one invalid option. Each hidden service option don't * apply to all versions so this function can find out. The line_ MUST start @@ -490,6 +508,12 @@ config_service(const config_line_t *line, const or_options_t *options, 0) < 0) { goto err; } + /* We'll try to learn the service version here by loading the key(s) if + * present. Depending on the key format, we can figure out the service + * version. If we can't find a key, the configuration version will be used + * which has been set previously. */ + service->config.version = config_learn_service_version(service); + /* Different functions are in charge of specific options for a version. We * start just after the service directory line so once we hit another * directory line, the function knows that it has to stop parsing. */ diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c index a9364a7514..fd981f8e17 100644 --- a/src/feature/hs/hs_service.c +++ b/src/feature/hs/hs_service.c @@ -2884,6 +2884,29 @@ service_add_fnames_to_list(const hs_service_t *service, smartlist_t *list) smartlist_add(list, hs_path_from_filename(s_dir, fname)); } +/* Return true iff the given service identity key is present on disk. */ +static int +service_key_on_disk(const char *directory_path) +{ + int ret = 0; + char *fname; + ed25519_keypair_t *kp = NULL; + + tor_assert(directory_path); + + /* Build the v3 key path name and then try to load it. */ + fname = hs_path_from_filename(directory_path, fname_keyfile_prefix); + kp = ed_key_init_from_file(fname, INIT_ED_KEY_SPLIT, + LOG_DEBUG, NULL, 0, 0, 0, NULL, NULL); + if (kp) { + ret = 1; + } + + ed25519_keypair_free(kp); + tor_free(fname); + return ret; +} + /* ========== */ /* Public API */ /* ========== */ @@ -3375,6 +3398,36 @@ hs_service_circuit_has_opened(origin_circuit_t *circ) } } +/* Return the service version by looking at the key in the service directory. + * If the key is not found or unrecognized, -1 is returned. Else, the service + * version is returned. */ +int +hs_service_get_version_from_key(const hs_service_t *service) +{ + int version = -1; /* Unknown version. */ + const char *directory_path; + + tor_assert(service); + + /* We'll try to load the key for version 3. If not found, we'll try version + * 2 and if not found, we'll send back an unknown version (0). */ + directory_path = service->config.directory_path; + + /* Version 3 check. */ + if (service_key_on_disk(directory_path)) { + version = HS_VERSION_THREE; + goto end; + } + /* Version 2 check. */ + if (rend_service_key_on_disk(directory_path)) { + version = HS_VERSION_TWO; + goto end; + } + + end: + return version; +} + /* Load and/or generate keys for all onion services including the client * authorization if any. Return 0 on success, -1 on failure. */ int diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h index 5c5443a35f..6ccd935512 100644 --- a/src/feature/hs/hs_service.h +++ b/src/feature/hs/hs_service.h @@ -262,6 +262,7 @@ void hs_service_free_(hs_service_t *service); unsigned int hs_service_get_num_services(void); void hs_service_stage_services(const smartlist_t *service_list); int hs_service_load_all_keys(void); +int hs_service_get_version_from_key(const hs_service_t *service); void hs_service_lists_fnames_for_sandbox(smartlist_t *file_list, smartlist_t *dir_list); int hs_service_set_conn_addr_port(const origin_circuit_t *circ, diff --git a/src/feature/rend/rendservice.c b/src/feature/rend/rendservice.c index 1a99bd56ed..1af9117aaf 100644 --- a/src/feature/rend/rendservice.c +++ b/src/feature/rend/rendservice.c @@ -1341,6 +1341,29 @@ rend_service_poison_new_single_onion_dir(const rend_service_t *s, return 0; } +/* Return true iff the given service identity key is present on disk. This is + * used to try to learn the service version during configuration time. */ +int +rend_service_key_on_disk(const char *directory_path) +{ + int ret = 0; + char *fname; + crypto_pk_t *pk = NULL; + + tor_assert(directory_path); + + /* Load key */ + fname = hs_path_from_filename(directory_path, private_key_fname); + pk = init_key_from_file(fname, 0, LOG_DEBUG, 0); + if (pk) { + ret = 1; + } + + crypto_pk_free(pk); + tor_free(fname); + return ret; +} + /** Load and/or generate private keys for all hidden services, possibly * including keys for client authorization. * If a service_list is provided, treat it as the list of hidden diff --git a/src/feature/rend/rendservice.h b/src/feature/rend/rendservice.h index 7096789629..7186289fc7 100644 --- a/src/feature/rend/rendservice.h +++ b/src/feature/rend/rendservice.h @@ -145,6 +145,7 @@ int rend_config_service(const struct config_line_t *line_, void rend_service_prune_list(void); void rend_service_free_staging_list(void); int rend_service_load_all_keys(const smartlist_t *service_list); +int rend_service_key_on_disk(const char *directory_path); void rend_services_add_filenames_to_lists(smartlist_t *open_lst, smartlist_t *stat_lst); void rend_consider_services_intro_points(time_t now);