Commit 9ca1af9a authored by Nick Mathewson's avatar Nick Mathewson
Browse files

Merge remote-tracking branch 'dgoulet/ticket20700_035_03'

parents 13d0855a 3695ef63
......@@ -459,6 +459,7 @@ static config_var_t option_vars_[] = {
VAR("HiddenServiceNumIntroductionPoints", LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceStatistics", BOOL, HiddenServiceStatistics_option, "1"),
V(HidServAuth, LINELIST, NULL),
V(ClientOnionAuthDir, FILENAME, NULL),
OBSOLETE("CloseHSClientCircuitsImmediatelyOnTimeout"),
OBSOLETE("CloseHSServiceRendCircuitsImmediatelyOnTimeout"),
V(HiddenServiceSingleHopMode, BOOL, "0"),
......@@ -1927,7 +1928,7 @@ options_act(const or_options_t *old_options)
// LCOV_EXCL_STOP
}
if (running_tor && rend_parse_service_authorization(options, 0) < 0) {
if (running_tor && hs_config_client_auth_all(options, 0) < 0) {
// LCOV_EXCL_START
log_warn(LD_BUG, "Previously validated client authorization for "
"hidden services could not be added!");
......@@ -3193,6 +3194,8 @@ warn_about_relative_paths(or_options_t *options)
n += warn_if_option_path_is_relative("AccelDir",options->AccelDir);
n += warn_if_option_path_is_relative("DataDirectory",options->DataDirectory);
n += warn_if_option_path_is_relative("PidFile",options->PidFile);
n += warn_if_option_path_is_relative("ClientOnionAuthDir",
options->ClientOnionAuthDir);
for (config_line_t *hs_line = options->RendConfigLines; hs_line;
hs_line = hs_line->next) {
......@@ -4344,7 +4347,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("Failed to configure rendezvous options. See logs for details.");
/* Parse client-side authorization for hidden services. */
if (rend_parse_service_authorization(options, 1) < 0)
if (hs_config_client_auth_all(options, 1) < 0)
REJECT("Failed to configure client authorization for hidden services. "
"See logs for details.");
......
......@@ -380,6 +380,8 @@ struct or_options_t {
struct config_line_t *HidServAuth; /**< List of configuration lines for
* client-side authorizations for hidden
* services */
char *ClientOnionAuthDir; /**< Directory to keep client
* onion service authorization secret keys */
char *ContactInfo; /**< Contact info to be published in the directory. */
int HeartbeatPeriod; /**< Log heartbeat messages after this many seconds
......
......@@ -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)
......@@ -1177,6 +1181,19 @@ can_client_refetch_desc(const ed25519_public_key_t *identity_pk,
return 0;
}
/* Return the client auth in the map using the service identity public key.
* Return NULL if it does not exist in the map. */
static hs_client_service_authorization_t *
find_client_auth(const ed25519_public_key_t *service_identity_pk)
{
/* If the map is not allocated, we can assume that we do not have any client
* auth information. */
if (!client_auths) {
return NULL;
}
return digest256map_get(client_auths, service_identity_pk->pubkey);
}
/* ========== */
/* Public API */
/* ========== */
......@@ -1215,11 +1232,19 @@ hs_client_decode_descriptor(const char *desc_str,
int ret;
uint8_t subcredential[DIGEST256_LEN];
ed25519_public_key_t blinded_pubkey;
hs_client_service_authorization_t *client_auth = NULL;
curve25519_secret_key_t *client_auht_sk = NULL;
tor_assert(desc_str);
tor_assert(service_identity_pk);
tor_assert(desc);
/* Check if we have a client authorization for this service in the map. */
client_auth = find_client_auth(service_identity_pk);
if (client_auth) {
client_auht_sk = &client_auth->enc_seckey;
}
/* Create subcredential for this HS so that we can decrypt */
{
uint64_t current_time_period = hs_get_time_period_num(0);
......@@ -1229,7 +1254,8 @@ hs_client_decode_descriptor(const char *desc_str,
}
/* Parse descriptor */
ret = hs_desc_decode_descriptor(desc_str, subcredential, desc);
ret = hs_desc_decode_descriptor(desc_str, subcredential,
client_auht_sk, desc);
memwipe(subcredential, 0, sizeof(subcredential));
if (ret < 0) {
log_warn(LD_GENERAL, "Could not parse received descriptor as client.");
......@@ -1393,6 +1419,233 @@ 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;
}
if (strlen(seckey_b32) != BASE32_NOPAD_LEN(CURVE25519_PUBKEY_LEN)) {
log_warn(LD_REND, "Client authorization encoded base32 private key "
"length is invalid: %s", seckey_b32);
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;
log_info(LD_REND, "Loading a client authorization key file %s...",
filename);
if (!auth_key_filename_is_valid(filename)) {
log_notice(LD_REND, "Client authorization unrecognized filename %s. "
"File must end in .auth_private. Ignoring.",
filename);
continue;
}
/* 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) {
log_warn(LD_REND, "The file %s cannot be read.", filename);
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);
log_warn(LD_REND, "The onion address \"%s\" is invalid in "
"file %s", filename, auth->onion_address);
continue;
}
if (digest256map_get(auths, identity_pk.pubkey)) {
client_service_authorization_free(auth);
log_warn(LD_REND, "Duplicate authorization for the same hidden "
"service address %s.",
safe_str_client(auth->onion_address));
goto end;
}
digest256map_set(auths, identity_pk.pubkey, auth);
log_info(LD_REND, "Loaded a client authorization key file %s.",
filename);
}
} 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 +1842,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
......@@ -1621,3 +1875,13 @@ hs_client_dir_info_changed(void)
* AP_CONN_STATE_RENDDESC_WAIT state in order to fetch the descriptor. */
retry_all_socks_conn_waiting_for_desc();
}
#ifdef TOR_UNIT_TESTS
STATIC digest256map_t *
get_hs_client_auths_map(void)
{
return client_auths;
}
#endif /* defined(TOR_UNIT_TESTS) */
......@@ -31,6 +31,16 @@ typedef enum {
HS_CLIENT_FETCH_PENDING = 5,
} hs_client_fetch_status_t;
/** Client-side configuration of authorization for a service. */
typedef struct hs_client_service_authorization_t {
/* An curve25519 secret key used to compute decryption keys that
* allow the client to decrypt the hidden service descriptor. */
curve25519_secret_key_t enc_seckey;
/* An onion address that is used to connect to the onion service. */
char onion_address[HS_SERVICE_ADDR_LEN_BASE32+1];
} hs_client_service_authorization_t;
void hs_client_note_connection_attempt_succeeded(
const edge_connection_t *conn);
......@@ -63,6 +73,9 @@ void hs_client_desc_has_arrived(const hs_ident_dir_conn_t *ident);
extend_info_t *hs_client_get_random_intro_from_edge(
const edge_connection_t *edge_conn);
int hs_config_client_authorization(const or_options_t *options,
int validate_only);
int hs_client_reextend_intro_circuit(origin_circuit_t *circ);
void hs_client_purge_state(void);
......@@ -71,6 +84,11 @@ void hs_client_free_all(void);
#ifdef HS_CLIENT_PRIVATE
STATIC int auth_key_filename_is_valid(const char *filename);
STATIC hs_client_service_authorization_t *
parse_auth_file_content(const char *client_key_str);
STATIC routerstatus_t *
pick_hsdir_v3(const ed25519_public_key_t *onion_identity_pk);
......@@ -86,6 +104,12 @@ STATIC int handle_rendezvous2(origin_circuit_t *circ, const uint8_t *payload,
MOCK_DECL(STATIC hs_client_fetch_status_t,
fetch_v3_desc, (const ed25519_public_key_t *onion_identity_pk));
#ifdef TOR_UNIT_TESTS
STATIC digest256map_t *get_hs_client_auths_map(void);
#endif /* defined(TOR_UNIT_TESTS) */
#endif /* defined(HS_CLIENT_PRIVATE) */
#endif /* !defined(TOR_HS_CLIENT_H) */
......
......@@ -27,7 +27,9 @@
#include "feature/hs/hs_common.h"
#include "feature/hs/hs_config.h"
#include "feature/hs/hs_client.h"
#include "feature/hs/hs_service.h"
#include "feature/rend/rendclient.h"
#include "feature/rend/rendservice.h"
#include "lib/encoding/confline.h"
#include "app/config/or_options_st.h"
......@@ -613,3 +615,28 @@ hs_config_service_all(const or_options_t *options, int validate_only)
/* Tor main should call the free all function on error. */
return ret;
}
/* From a set of <b>options</b>, setup every client authorization 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
* configured state. */
int
hs_config_client_auth_all(const or_options_t *options, int validate_only)
{
int ret = -1;
/* Configure v2 authorization. */
if (rend_parse_service_authorization(options, validate_only) < 0) {
goto done;
}
/* Configure v3 authorization. */
if (hs_config_client_authorization(options, validate_only) < 0) {
goto done;
}
/* Success. */
ret = 0;
done:
return ret;
}
......@@ -19,6 +19,7 @@
/* API */
int hs_config_service_all(const or_options_t *options, int validate_only);
int hs_config_client_auth_all(const or_options_t *options, int validate_only);
#endif /* !defined(TOR_HS_CONFIG_H) */
This diff is collapsed.
......@@ -37,12 +37,6 @@ struct link_specifier_t;
#define HS_DESC_CERT_LIFETIME (54 * 60 * 60)
/* Length of the salt needed for the encrypted section of a descriptor. */
#define HS_DESC_ENCRYPTED_SALT_LEN 16
/* Length of the secret input needed for the KDF construction which derives
* the encryption key for the encrypted data section of the descriptor. This
* adds up to 68 bytes being the blinded key, hashed subcredential and
* revision counter. */
#define HS_DESC_ENCRYPTED_SECRET_INPUT_LEN \
ED25519_PUBKEY_LEN + DIGEST256_LEN + sizeof(uint64_t)
/* Length of the KDF output value which is the length of the secret key,
* the secret IV and MAC key length which is the length of H() output. */
#define HS_DESC_ENCRYPTED_KDF_OUTPUT_LEN \
......@@ -59,6 +53,17 @@ struct link_specifier_t;
#define HS_DESC_ENCRYPTED_KEY_LEN CIPHER256_KEY_LEN
#define HS_DESC_ENCRYPTED_BIT_SIZE (HS_DESC_ENCRYPTED_KEY_LEN * 8)
/* Length of each components in the auth client section in the descriptor. */
#define HS_DESC_CLIENT_ID_LEN 8
#define HS_DESC_DESCRIPTOR_COOKIE_LEN 16
#define HS_DESC_COOKIE_KEY_LEN 32
#define HS_DESC_COOKIE_KEY_BIT_SIZE (HS_DESC_COOKIE_KEY_LEN * 8)
#define HS_DESC_ENCRYPED_COOKIE_LEN HS_DESC_DESCRIPTOR_COOKIE_LEN
/* The number of auth client entries in the descriptor must be the multiple
* of this constant. */
#define HS_DESC_AUTH_CLIENT_MULTIPLE 16
/* Type of authentication in the descriptor. */
typedef enum {
HS_DESC_AUTH_ED25519 = 1
......@@ -126,6 +131,20 @@ typedef struct hs_desc_intro_point_t {
unsigned int cross_certified : 1;
} hs_desc_intro_point_t;
/* Authorized client information located in a descriptor. */
typedef struct hs_desc_authorized_client_t {
/* An identifier that the client will use to identify which auth client
* entry it needs to use. */
uint8_t client_id[HS_DESC_CLIENT_ID_LEN];
/* An IV that is used to decrypt the encrypted descriptor cookie. */
uint8_t iv[CIPHER_IV_LEN];
/* An encrypted descriptor cookie that the client needs to decrypt to use
* it to decrypt the descriptor. */
uint8_t encrypted_cookie[HS_DESC_ENCRYPED_COOKIE_LEN];
} hs_desc_authorized_client_t;
/* The encrypted data section of a descriptor. Obviously the data in this is
* in plaintext but encrypted once encoded. */
typedef struct hs_desc_encrypted_data_t {
......@@ -144,6 +163,24 @@ typedef struct hs_desc_encrypted_data_t {
smartlist_t *intro_points;
} hs_desc_encrypted_data_t;
/* The superencrypted data section of a descriptor. Obviously the data in
* this is in plaintext but encrypted once encoded. */
typedef struct hs_desc_superencrypted_data_t {
/* This field contains ephemeral x25519 public key which is used by
* the encryption scheme in the client authorization. */
curve25519_public_key_t auth_ephemeral_pubkey;
/* A list of authorized clients. Contains hs_desc_authorized_client_t
* objects. */
smartlist_t *clients;
/* Decoding only: The b64-decoded encrypted blob from the descriptor */
uint8_t *encrypted_blob;
/* Decoding only: Size of the encrypted_blob */
size_t encrypted_blob_size;
} hs_desc_superencrypted_data_t;
/* Plaintext data that is unencrypted information of the descriptor. */
typedef struct hs_desc_plaintext_data_t {
/* Version of the descriptor format. Spec specifies this field as a
......@@ -182,6 +219,11 @@ typedef struct hs_descriptor_t {
/* Contains the plaintext part of the descriptor. */
hs_desc_plaintext_data_t plaintext_data;
/* The following contains what's in the superencrypted part of the
* descriptor. It's only encrypted in the encoded version of the descriptor
* thus the data contained in that object is in plaintext. */
hs_desc_superencrypted_data_t superencrypted_data;
/* The following contains what's in the encrypted part of the descriptor.
* It's only encrypted in the encoded version of the descriptor thus the
* data contained in that object is in plaintext. */
......@@ -211,6 +253,10 @@ void hs_descriptor_free_(hs_descriptor_t *desc);
void hs_desc_plaintext_data_free_(hs_desc_plaintext_data_t *desc);
#define hs_desc_plaintext_data_free(desc) \
FREE_AND_NULL(hs_desc_plaintext_data_t, hs_desc_plaintext_data_free_, (desc))
void hs_desc_superencrypted_data_free_(hs_desc_superencrypted_data_t *desc);
#define hs_desc_superencrypted_data_free(desc) \
FREE_AND_NULL(hs_desc_superencrypted_data_t, \
hs_desc_superencrypted_data_free_, (desc))
void hs_desc_encrypted_data_free_(hs_desc_encrypted_data_t *desc);
#define hs_desc_encrypted_data_free(desc) \
FREE_AND_NULL(hs_desc_encrypted_data_t, hs_desc_encrypted_data_free_, (desc))
......@@ -226,14 +272,19 @@ void hs_descriptor_clear_intro_points(hs_descriptor_t *desc);
MOCK_DECL(int,
hs_desc_encode_descriptor,(const hs_descriptor_t *desc,
const ed25519_keypair_t *signing_kp,
const uint8_t *descriptor_cookie,
char **encoded_out));
int hs_desc_decode_descriptor(const char *encoded,
const uint8_t *subcredential,
const curve25519_secret_key_t *client_auth_sk,
hs_descriptor_t **desc_out);
int hs_desc_decode_plaintext(const char *encoded,
hs_desc_plaintext_data_t *plaintext);
int hs_desc_decode_superencrypted(const hs_descriptor_t *desc,
hs_desc_superencrypted_data_t *desc_out);
int hs_desc_decode_encrypted(const hs_descriptor_t *desc,
const curve25519_secret_key_t *client_auth_sk,
hs_desc_encrypted_data_t *desc_out);
size_t hs_desc_obj_size(const hs_descriptor_t *data);
......@@ -243,10 +294,27 @@ hs_desc_intro_point_t *hs_desc_intro_point_new(void);
void hs_desc_intro_point_free_(hs_desc_intro_point_t *ip);
#define hs_desc_intro_point_free(ip) \
FREE_AND_NULL(hs_desc_intro_point_t, hs_desc_intro_point_free_, (ip))
void hs_desc_authorized_client_free_(hs_desc_authorized_client_t *client);
#define hs_desc_authorized_client_free(client) \
FREE_AND_NULL(hs_desc_authorized_client_t, \
hs_desc_authorized_client_free_, (client))
link_specifier_t *hs_desc_lspec_to_trunnel(
const hs_desc_link_specifier_t *spec);
hs_desc_authorized_client_t *hs_desc_build_fake_authorized_client(void);
void hs_desc_build_authorized_client(const uint8_t *subcredential,
const curve25519_public_key_t *
client_auth_pk,
const curve25519_secret_key_t *
auth_ephemeral_sk,
const uint8_t *descriptor_cookie,
hs_desc_authorized_client_t *client_out);
void hs_desc_plaintext_data_free_contents(hs_desc_plaintext_data_t *desc);
void hs_desc_superencrypted_data_free_contents(
hs_desc_superencrypted_data_t *desc);
void hs_desc_encrypted_data_free_contents(hs_desc_encrypted_data_t *desc);
#ifdef HS_DESCRIPTOR_PRIVATE
/* Encoding. */
......@@ -265,13 +333,11 @@ STATIC int cert_is_valid(tor_cert_t *cert, uint8_t type,
STATIC int desc_sig_is_valid(const char *b64_sig,
const ed25519_public_key_t *signing_pubkey,
const char *encoded_desc, size_t encoded_len);
STATIC size_t decode_superencrypted(const char *message, size_t message_len,
uint8_t **encrypted_out);
STATIC void desc_plaintext_data_free_contents(hs_desc_plaintext_data_t *desc);
MOCK_DECL(STATIC size_t, decrypt_desc_layer,(const hs_descriptor_t *desc,
const uint8_t *encrypted_blob,
size_t encrypted_blob_size,
const uint8_t *descriptor_cookie,
int is_superencrypted_layer,
char **decrypted_out));
......
This diff is collapsed.
......@@ -105,6 +105,13 @@ typedef struct hs_service_descriptor_t {
* publishes the descriptor. */