summaryrefslogtreecommitdiff
path: root/src/feature/hs/hs_client.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/feature/hs/hs_client.c')
-rw-r--r--src/feature/hs/hs_client.c215
1 files changed, 215 insertions, 0 deletions
diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c
index 1f9218e15a..7c545c35d5 100644
--- a/src/feature/hs/hs_client.c
+++ b/src/feature/hs/hs_client.c
@@ -42,6 +42,10 @@
#include "core/or/extend_info_st.h"
#include "core/or/origin_circuit_st.h"
+/* Client-side authorizations for hidden services; map of service identity
+ * public key to hs_client_service_authorization_t *. */
+static digest256map_t *client_auths = NULL;
+
/* Return a human-readable string for the client fetch status code. */
static const char *
fetch_status_to_string(hs_client_fetch_status_t status)
@@ -1393,6 +1397,216 @@ hs_client_receive_rendezvous_acked(origin_circuit_t *circ,
return -1;
}
+#define client_service_authorization_free(auth) \
+ FREE_AND_NULL(hs_client_service_authorization_t, \
+ client_service_authorization_free_, (auth))
+
+static void
+client_service_authorization_free_(hs_client_service_authorization_t *auth)
+{
+ if (auth) {
+ memwipe(auth, 0, sizeof(*auth));
+ }
+ tor_free(auth);
+}
+
+/** Helper for digest256map_free. */
+static void
+client_service_authorization_free_void(void *auth)
+{
+ client_service_authorization_free_(auth);
+}
+
+static void
+client_service_authorization_free_all(void)
+{
+ if (!client_auths) {
+ return;
+ }
+ digest256map_free(client_auths, client_service_authorization_free_void);
+}
+
+/* Check if the auth key file name is valid or not. Return 1 if valid,
+ * otherwise return 0. */
+static int
+auth_key_filename_is_valid(const char *filename)
+{
+ int ret = 1;
+ const char *valid_extension = ".auth_private";
+
+ tor_assert(filename);
+
+ /* The length of the filename must be greater than the length of the
+ * extension and the valid extension must be at the end of filename. */
+ if (!strcmpend(filename, valid_extension) &&
+ strlen(filename) != strlen(valid_extension)) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static hs_client_service_authorization_t *
+parse_auth_file_content(const char *client_key_str)
+{
+ char *onion_address = NULL;
+ char *auth_type = NULL;
+ char *key_type = NULL;
+ char *seckey_b32 = NULL;
+ hs_client_service_authorization_t *auth = NULL;
+ smartlist_t *fields = smartlist_new();
+
+ tor_assert(client_key_str);
+
+ smartlist_split_string(fields, client_key_str, ":",
+ SPLIT_SKIP_SPACE, 0);
+ /* Wrong number of fields. */
+ if (smartlist_len(fields) != 4) {
+ goto err;
+ }
+
+ onion_address = smartlist_get(fields, 0);
+ auth_type = smartlist_get(fields, 1);
+ key_type = smartlist_get(fields, 2);
+ seckey_b32 = smartlist_get(fields, 3);
+
+ /* Currently, the only supported auth type is "descriptor" and the only
+ * supported key type is "x25519". */
+ if (strcmp(auth_type, "descriptor") || strcmp(key_type, "x25519")) {
+ goto err;
+ }
+
+ auth = tor_malloc_zero(sizeof(hs_client_service_authorization_t));
+ if (base32_decode((char *) auth->enc_seckey.secret_key,
+ sizeof(auth->enc_seckey.secret_key),
+ seckey_b32, strlen(seckey_b32)) < 0) {
+ goto err;
+ }
+ strncpy(auth->onion_address, onion_address, HS_SERVICE_ADDR_LEN_BASE32);
+
+ /* Success. */
+ goto done;
+
+ err:
+ client_service_authorization_free(auth);
+ done:
+ /* It is also a good idea to wipe the private key. */
+ if (seckey_b32) {
+ memwipe(seckey_b32, 0, strlen(seckey_b32));
+ }
+ if (fields) {
+ SMARTLIST_FOREACH(fields, char *, s, tor_free(s));
+ smartlist_free(fields);
+ }
+ return auth;
+}
+
+/* From a set of <b>options</b>, setup every client authorization detail
+ * found. Return 0 on success or -1 on failure. If <b>validate_only</b>
+ * is set, parse, warn and return as normal, but don't actually change
+ * the configuration. */
+int
+hs_config_client_authorization(const or_options_t *options,
+ int validate_only)
+{
+ int ret = -1;
+ digest256map_t *auths = digest256map_new();
+ char *key_dir = NULL;
+ smartlist_t *file_list = NULL;
+ char *client_key_str = NULL;
+ char *client_key_file_path = NULL;
+
+ tor_assert(options);
+
+ /* There is no client auth configured. We can just silently ignore this
+ * function. */
+ if (!options->ClientOnionAuthDir) {
+ ret = 0;
+ goto end;
+ }
+
+ key_dir = tor_strdup(options->ClientOnionAuthDir);
+
+ /* Make sure the directory exists and is private enough. */
+ if (check_private_dir(key_dir, 0, options->User) < 0) {
+ goto end;
+ }
+
+ file_list = tor_listdir(key_dir);
+ if (file_list == NULL) {
+ log_warn(LD_REND, "Client authorization key directory %s can't be listed.",
+ key_dir);
+ goto end;
+ }
+
+ SMARTLIST_FOREACH_BEGIN(file_list, char *, filename) {
+
+ hs_client_service_authorization_t *auth = NULL;
+ ed25519_public_key_t identity_pk;
+
+ if (auth_key_filename_is_valid(filename)) {
+ /* Create a full path for a file. */
+ client_key_file_path = hs_path_from_filename(key_dir, filename);
+ client_key_str = read_file_to_str(client_key_file_path, 0, NULL);
+ /* Free the file path immediately after using it. */
+ tor_free(client_key_file_path);
+
+ /* If we cannot read the file, continue with the next file. */
+ if (!client_key_str) {
+ continue;
+ }
+
+ auth = parse_auth_file_content(client_key_str);
+ /* Free immediately after using it. */
+ tor_free(client_key_str);
+
+ if (auth) {
+ /* Parse the onion address to get an identity public key and use it
+ * as a key of global map in the future. */
+ if (hs_parse_address(auth->onion_address, &identity_pk,
+ NULL, NULL) < 0) {
+ client_service_authorization_free(auth);
+ continue;
+ }
+
+ if (digest256map_get(auths, identity_pk.pubkey)) {
+ client_service_authorization_free(auth);
+
+ log_warn(LD_REND, "Duplicate authorization for the same hidden "
+ "service.");
+ goto end;
+ }
+
+ digest256map_set(auths, identity_pk.pubkey, auth);
+ }
+ }
+
+ } SMARTLIST_FOREACH_END(filename);
+
+ /* Success. */
+ ret = 0;
+
+ end:
+ tor_free(key_dir);
+ tor_free(client_key_str);
+ tor_free(client_key_file_path);
+ if (file_list) {
+ SMARTLIST_FOREACH(file_list, char *, s, tor_free(s));
+ smartlist_free(file_list);
+ }
+
+ if (!validate_only && ret == 0) {
+ client_service_authorization_free_all();
+ client_auths = auths;
+ } else {
+ digest256map_free(auths, client_service_authorization_free_void);
+ }
+
+ return ret;
+}
+
/* This is called when a descriptor has arrived following a fetch request and
* has been stored in the client cache. Every entry connection that matches
* the service identity key in the ident will get attached to the hidden
@@ -1589,6 +1803,7 @@ hs_client_free_all(void)
{
/* Purge the hidden service request cache. */
hs_purge_last_hid_serv_requests();
+ client_service_authorization_free_all();
}
/* Purge all potentially remotely-detectable state held in the hidden