mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-10 21:23:58 +01:00
Apply proposal 121 patch 3, with minor tweaks and a few comments.
svn:r16598
This commit is contained in:
parent
0711408c22
commit
24f1d29be1
31
src/or/or.h
31
src/or/or.h
@ -651,6 +651,23 @@ typedef enum {
|
||||
* exchanging client authorization between hidden service and client. */
|
||||
#define REND_DESC_COOKIE_LEN_BASE64 22
|
||||
|
||||
/** Length of client identifier in encrypted introduction points for hidden
|
||||
* service authorization type 'basic'. */
|
||||
#define REND_BASIC_AUTH_CLIENT_ID_LEN 4
|
||||
|
||||
/** Multiple of the number of clients to which the real number of clients
|
||||
* is padded with fake clients for hidden service authorization type
|
||||
* 'basic'. */
|
||||
#define REND_BASIC_AUTH_CLIENT_MULTIPLE 16
|
||||
|
||||
/** Length of client entry consisting of client identifier and encrypted
|
||||
* session key for hidden service authorization type 'basic'. */
|
||||
#define REND_BASIC_AUTH_CLIENT_ENTRY_LEN (REND_BASIC_AUTH_CLIENT_ID_LEN \
|
||||
+ CIPHER_KEY_LEN)
|
||||
|
||||
/** Maximum size of v2 hidden service descriptors. */
|
||||
#define REND_DESC_MAX_SIZE (20 * 1024)
|
||||
|
||||
/** Legal characters for use in authorized client names for a hidden
|
||||
* service. */
|
||||
#define REND_LEGAL_CLIENTNAME_CHARACTERS \
|
||||
@ -3926,7 +3943,9 @@ int rend_cache_store_v2_desc_as_dir(const char *desc);
|
||||
int rend_cache_size(void);
|
||||
int rend_encode_v2_descriptors(smartlist_t *descs_out,
|
||||
rend_service_descriptor_t *desc, time_t now,
|
||||
const char *descriptor_cookie, uint8_t period);
|
||||
uint8_t period, rend_auth_type_t auth_type,
|
||||
crypto_pk_env_t *client_key,
|
||||
smartlist_t *client_cookies);
|
||||
int rend_compute_v2_desc_id(char *desc_id_out, const char *service_id,
|
||||
const char *descriptor_cookie,
|
||||
time_t now, uint8_t replica);
|
||||
@ -4315,10 +4334,14 @@ int rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
|
||||
size_t *intro_points_encrypted_size_out,
|
||||
size_t *encoded_size_out,
|
||||
const char **next_out, const char *desc);
|
||||
int rend_decrypt_introduction_points(rend_service_descriptor_t *parsed,
|
||||
int rend_decrypt_introduction_points(char **ipos_decrypted,
|
||||
size_t *ipos_decrypted_size,
|
||||
const char *descriptor_cookie,
|
||||
const char *intro_content,
|
||||
size_t intro_size);
|
||||
const char *ipos_encrypted,
|
||||
size_t ipos_encrypted_size);
|
||||
int rend_parse_introduction_points(rend_service_descriptor_t *parsed,
|
||||
const char *intro_points_encoded,
|
||||
size_t intro_points_encoded_size);
|
||||
int rend_parse_client_keys(strmap_t *parsed_clients, const char *str);
|
||||
|
||||
#endif
|
||||
|
@ -150,15 +150,11 @@ rend_compute_v2_desc_id(char *desc_id_out, const char *service_id,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Encode the introduction points in <b>desc</b>, optionally encrypt them with
|
||||
* an optional <b>descriptor_cookie</b> of length REND_DESC_COOKIE_LEN,
|
||||
* encode it in base64, and write it to a newly allocated string, and write a
|
||||
* pointer to it to *<b>ipos_base64</b>. Return 0 for success, -1
|
||||
* otherwise. */
|
||||
/** Encode the introduction points in <b>desc</b> and write the result to a
|
||||
* newly allocated string pointed to by <b>encoded</b>. Return 0 for
|
||||
* success, -1 otherwise. */
|
||||
static int
|
||||
rend_encode_v2_intro_points(char **ipos_base64,
|
||||
rend_service_descriptor_t *desc,
|
||||
const char *descriptor_cookie)
|
||||
rend_encode_v2_intro_points(char **encoded, rend_service_descriptor_t *desc)
|
||||
{
|
||||
size_t unenc_len;
|
||||
char *unenc = NULL;
|
||||
@ -166,7 +162,6 @@ rend_encode_v2_intro_points(char **ipos_base64,
|
||||
int i;
|
||||
int r = -1;
|
||||
/* Assemble unencrypted list of introduction points. */
|
||||
*ipos_base64 = NULL;
|
||||
unenc_len = smartlist_len(desc->intro_nodes) * 1000; /* too long, but ok. */
|
||||
unenc = tor_malloc_zero(unenc_len);
|
||||
for (i = 0; i < smartlist_len(desc->intro_nodes); i++) {
|
||||
@ -231,37 +226,163 @@ rend_encode_v2_intro_points(char **ipos_base64,
|
||||
}
|
||||
unenc[unenc_written++] = '\n';
|
||||
unenc[unenc_written++] = 0;
|
||||
/* If a descriptor cookie is passed, encrypt introduction points. */
|
||||
if (descriptor_cookie) {
|
||||
char *enc = tor_malloc_zero(unenc_written + CIPHER_IV_LEN);
|
||||
crypto_cipher_env_t *cipher =
|
||||
crypto_create_init_cipher(descriptor_cookie, 1);
|
||||
int enclen = crypto_cipher_encrypt_with_iv(cipher, enc,
|
||||
unenc_written + CIPHER_IV_LEN,
|
||||
unenc, unenc_written);
|
||||
crypto_free_cipher_env(cipher);
|
||||
if (enclen < 0) {
|
||||
log_warn(LD_REND, "Could not encrypt introduction point string.");
|
||||
tor_free(enc);
|
||||
goto done;
|
||||
}
|
||||
/* Replace original string with the encrypted one. */
|
||||
tor_free(unenc);
|
||||
unenc = enc;
|
||||
unenc_written = enclen;
|
||||
}
|
||||
/* Base64-encode introduction points. */
|
||||
*ipos_base64 = tor_malloc_zero(unenc_written * 2);
|
||||
if (base64_encode(*ipos_base64, unenc_written * 2, unenc, unenc_written)<0) {
|
||||
log_warn(LD_REND, "Could not encode introduction point string to "
|
||||
"base64.");
|
||||
goto done;
|
||||
}
|
||||
*encoded = unenc;
|
||||
r = 0;
|
||||
done:
|
||||
if (r<0)
|
||||
tor_free(*ipos_base64);
|
||||
tor_free(unenc);
|
||||
tor_free(unenc);
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Encrypt the encoded introduction points in <b>encoded</b> using
|
||||
* authorization type 'basic' with <b>client_cookies</b> and write the
|
||||
* result to a newly allocated string pointed to by <b>encrypted_out</b> of
|
||||
* length <b>encrypted_len</b>. Return 0 for success, -1 otherwise. */
|
||||
static int
|
||||
rend_encrypt_v2_intro_points_basic(char **encrypted_out,
|
||||
size_t *encrypted_len_out,
|
||||
const char *encoded,
|
||||
smartlist_t *client_cookies)
|
||||
{
|
||||
int r = -1, i, pos, enclen, client_blocks;
|
||||
size_t len, client_entries_len;
|
||||
char *enc = NULL, iv[CIPHER_IV_LEN], *client_part = NULL,
|
||||
session_key[CIPHER_KEY_LEN];
|
||||
smartlist_t *encrypted_session_keys = NULL;
|
||||
crypto_digest_env_t *digest;
|
||||
crypto_cipher_env_t *cipher;
|
||||
tor_assert(encoded);
|
||||
tor_assert(client_cookies && smartlist_len(client_cookies) > 0);
|
||||
|
||||
/* Generate session key. */
|
||||
if (crypto_rand(session_key, CIPHER_KEY_LEN) < 0) {
|
||||
log_warn(LD_REND, "Unable to generate random session key to encrypt "
|
||||
"introduction point string.");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Determine length of encrypted introduction points including session
|
||||
* keys. */
|
||||
client_blocks = 1 + ((smartlist_len(client_cookies) - 1) /
|
||||
REND_BASIC_AUTH_CLIENT_MULTIPLE);
|
||||
client_entries_len = client_blocks * REND_BASIC_AUTH_CLIENT_MULTIPLE *
|
||||
REND_BASIC_AUTH_CLIENT_ENTRY_LEN;
|
||||
len = 2 + client_entries_len + CIPHER_IV_LEN + strlen(encoded);
|
||||
if (client_blocks >= 256) {
|
||||
log_warn(LD_REND, "Too many clients in introduction point string.");
|
||||
goto done;
|
||||
}
|
||||
enc = tor_malloc_zero(len);
|
||||
enc[0] = 0x01; /* type of authorization. */
|
||||
enc[1] = (uint8_t)client_blocks;
|
||||
|
||||
/* Encrypt with random session key. */
|
||||
cipher = crypto_create_init_cipher(session_key, 1);
|
||||
enclen = crypto_cipher_encrypt_with_iv(cipher,
|
||||
enc + 2 + client_entries_len,
|
||||
CIPHER_IV_LEN + strlen(encoded), encoded, strlen(encoded));
|
||||
crypto_free_cipher_env(cipher);
|
||||
if (enclen < 0) {
|
||||
log_warn(LD_REND, "Could not encrypt introduction point string.");
|
||||
goto done;
|
||||
}
|
||||
memcpy(iv, enc + 2 + client_entries_len, CIPHER_IV_LEN);
|
||||
|
||||
/* Encrypt session key for cookies, determine client IDs, and put both
|
||||
* in a smartlist. */
|
||||
encrypted_session_keys = smartlist_create();
|
||||
SMARTLIST_FOREACH_BEGIN(client_cookies, const char *, cookie) {
|
||||
client_part = tor_malloc_zero(REND_BASIC_AUTH_CLIENT_ENTRY_LEN);
|
||||
/* Encrypt session key. */
|
||||
cipher = crypto_create_init_cipher(cookie, 1);
|
||||
if (crypto_cipher_encrypt(cipher, client_part +
|
||||
REND_BASIC_AUTH_CLIENT_ID_LEN,
|
||||
session_key, CIPHER_KEY_LEN) < 0) {
|
||||
log_warn(LD_REND, "Could not encrypt session key for client.");
|
||||
crypto_free_cipher_env(cipher);
|
||||
tor_free(client_part);
|
||||
goto done;
|
||||
}
|
||||
crypto_free_cipher_env(cipher);
|
||||
|
||||
/* Determine client ID. */
|
||||
digest = crypto_new_digest_env();
|
||||
crypto_digest_add_bytes(digest, cookie, REND_DESC_COOKIE_LEN);
|
||||
crypto_digest_add_bytes(digest, iv, CIPHER_IV_LEN);
|
||||
crypto_digest_get_digest(digest, client_part,
|
||||
REND_BASIC_AUTH_CLIENT_ID_LEN);
|
||||
crypto_free_digest_env(digest);
|
||||
|
||||
/* Put both together. */
|
||||
smartlist_add(encrypted_session_keys, client_part);
|
||||
} SMARTLIST_FOREACH_END(cookie);
|
||||
|
||||
/* Add some fake client IDs and encrypted session keys. */
|
||||
for (i = (smartlist_len(client_cookies) - 1) %
|
||||
REND_BASIC_AUTH_CLIENT_MULTIPLE;
|
||||
i < REND_BASIC_AUTH_CLIENT_MULTIPLE - 1; i++) {
|
||||
client_part = tor_malloc_zero(REND_BASIC_AUTH_CLIENT_ENTRY_LEN);
|
||||
if (crypto_rand(client_part, REND_BASIC_AUTH_CLIENT_ENTRY_LEN) < 0) {
|
||||
log_warn(LD_REND, "Unable to generate fake client entry.");
|
||||
tor_free(client_part);
|
||||
goto done;
|
||||
}
|
||||
smartlist_add(encrypted_session_keys, client_part);
|
||||
}
|
||||
/* Sort smartlist and put elements in result in order. */
|
||||
smartlist_sort_digests(encrypted_session_keys);
|
||||
pos = 2;
|
||||
SMARTLIST_FOREACH(encrypted_session_keys, const char *, entry, {
|
||||
memcpy(enc + pos, entry, REND_BASIC_AUTH_CLIENT_ENTRY_LEN);
|
||||
pos += REND_BASIC_AUTH_CLIENT_ENTRY_LEN;
|
||||
});
|
||||
*encrypted_out = enc;
|
||||
*encrypted_len_out = len;
|
||||
enc = NULL; /* prevent free. */
|
||||
r = 0;
|
||||
done:
|
||||
tor_free(enc);
|
||||
if (encrypted_session_keys) {
|
||||
SMARTLIST_FOREACH(encrypted_session_keys, char *, d, tor_free(d););
|
||||
smartlist_free(encrypted_session_keys);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Encrypt the encoded introduction points in <b>encoded</b> using
|
||||
* authorization type 'stealth' with <b>descriptor_cookie</b> of length
|
||||
* REND_DESC_COOKIE_LEN and write the result to a newly allocated string
|
||||
* pointed to by <b>encrypted</b> of length <b>encrypted_len</b>. Return 0
|
||||
* for success, -1 otherwise. */
|
||||
static int
|
||||
rend_encrypt_v2_intro_points_stealth(char **encrypted_out,
|
||||
size_t *encrypted_len_out,
|
||||
const char *encoded,
|
||||
const char *descriptor_cookie)
|
||||
{
|
||||
int r = -1, enclen;
|
||||
crypto_cipher_env_t *cipher;
|
||||
char *enc;
|
||||
tor_assert(encoded);
|
||||
tor_assert(descriptor_cookie);
|
||||
|
||||
enc = tor_malloc_zero(1 + CIPHER_IV_LEN + strlen(encoded));
|
||||
enc[0] = 0x02; /* Auth type */
|
||||
cipher = crypto_create_init_cipher(descriptor_cookie, 1);
|
||||
enclen = crypto_cipher_encrypt_with_iv(cipher, enc + 1,
|
||||
CIPHER_IV_LEN+strlen(encoded),
|
||||
encoded, strlen(encoded));
|
||||
crypto_free_cipher_env(cipher);
|
||||
if (enclen < 0) {
|
||||
log_warn(LD_REND, "Could not encrypt introduction point string.");
|
||||
goto done;
|
||||
}
|
||||
*encrypted_out = enc;
|
||||
*encrypted_len_out = enclen;
|
||||
enc = NULL; /* prevent free */
|
||||
r = 0;
|
||||
done:
|
||||
tor_free(enc);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -308,38 +429,97 @@ rend_intro_point_free(rend_intro_point_t *intro)
|
||||
}
|
||||
|
||||
/** Encode a set of rend_encoded_v2_service_descriptor_t's for <b>desc</b>
|
||||
* at time <b>now</b> using <b>descriptor_cookie</b> (may be <b>NULL</b> if
|
||||
* introduction points shall not be encrypted) and <b>period</b> (e.g. 0
|
||||
* for the current period, 1 for the next period, etc.) and add them to
|
||||
* the existing list <b>descs_out</b>; return the number of seconds that
|
||||
* the descriptors will be found by clients, or -1 if the encoding was not
|
||||
* successful. */
|
||||
* at time <b>now</b> using <b>service_key</b>, depending on
|
||||
* <b>auth_type</b> a <b>descriptor_cookie</b> and a list of
|
||||
* <b>client_cookies</b> (which are both <b>NULL</b> if no client
|
||||
* authorization is performed), and <b>period</b> (e.g. 0 for the current
|
||||
* period, 1 for the next period, etc.) and add them to the existing list
|
||||
* <b>descs_out</b>; return the number of seconds that the descriptors will
|
||||
* be found by clients, or -1 if the encoding was not successful. */
|
||||
int
|
||||
rend_encode_v2_descriptors(smartlist_t *descs_out,
|
||||
rend_service_descriptor_t *desc, time_t now,
|
||||
const char *descriptor_cookie, uint8_t period)
|
||||
uint8_t period, rend_auth_type_t auth_type,
|
||||
crypto_pk_env_t *client_key,
|
||||
smartlist_t *client_cookies)
|
||||
{
|
||||
char service_id[DIGEST_LEN];
|
||||
uint32_t time_period;
|
||||
char *ipos_base64 = NULL;
|
||||
char *ipos_base64 = NULL, *ipos = NULL, *ipos_encrypted = NULL,
|
||||
*descriptor_cookie = NULL;
|
||||
size_t ipos_len = 0, ipos_encrypted_len = 0;
|
||||
int k;
|
||||
uint32_t seconds_valid;
|
||||
crypto_pk_env_t *service_key = auth_type == REND_STEALTH_AUTH ?
|
||||
client_key : desc->pk;
|
||||
tor_assert(service_key);
|
||||
if (auth_type == REND_STEALTH_AUTH) {
|
||||
descriptor_cookie = smartlist_get(client_cookies, 0);
|
||||
tor_assert(descriptor_cookie);
|
||||
}
|
||||
if (!desc) {
|
||||
log_warn(LD_REND, "Could not encode v2 descriptor: No desc given.");
|
||||
return -1;
|
||||
}
|
||||
/* Obtain service_id from public key. */
|
||||
crypto_pk_get_digest(desc->pk, service_id);
|
||||
crypto_pk_get_digest(service_key, service_id);
|
||||
/* Calculate current time-period. */
|
||||
time_period = get_time_period(now, period, service_id);
|
||||
/* Determine how many seconds the descriptor will be valid. */
|
||||
seconds_valid = period * REND_TIME_PERIOD_V2_DESC_VALIDITY +
|
||||
get_seconds_valid(now, service_id);
|
||||
/* Assemble, possibly encrypt, and encode introduction points. */
|
||||
if (smartlist_len(desc->intro_nodes) > 0 &&
|
||||
rend_encode_v2_intro_points(&ipos_base64, desc, descriptor_cookie) < 0) {
|
||||
log_warn(LD_REND, "Encoding of introduction points did not succeed.");
|
||||
return -1;
|
||||
if (smartlist_len(desc->intro_nodes) > 0) {
|
||||
if (rend_encode_v2_intro_points(&ipos, desc) < 0) {
|
||||
log_warn(LD_REND, "Encoding of introduction points did not succeed.");
|
||||
return -1;
|
||||
}
|
||||
switch (auth_type) {
|
||||
case REND_NO_AUTH:
|
||||
ipos_len = strlen(ipos);
|
||||
break;
|
||||
case REND_BASIC_AUTH:
|
||||
if (rend_encrypt_v2_intro_points_basic(&ipos_encrypted,
|
||||
&ipos_encrypted_len, ipos,
|
||||
client_cookies) < 0) {
|
||||
log_warn(LD_REND, "Encrypting of introduction points did not "
|
||||
"succeed.");
|
||||
tor_free(ipos);
|
||||
return -1;
|
||||
}
|
||||
tor_free(ipos);
|
||||
ipos = ipos_encrypted;
|
||||
ipos_len = ipos_encrypted_len;
|
||||
break;
|
||||
case REND_STEALTH_AUTH:
|
||||
if (rend_encrypt_v2_intro_points_stealth(&ipos_encrypted,
|
||||
&ipos_encrypted_len, ipos,
|
||||
descriptor_cookie) < 0) {
|
||||
log_warn(LD_REND, "Encrypting of introduction points did not "
|
||||
"succeed.");
|
||||
tor_free(ipos);
|
||||
return -1;
|
||||
}
|
||||
tor_free(ipos);
|
||||
ipos = ipos_encrypted;
|
||||
ipos_len = ipos_encrypted_len;
|
||||
break;
|
||||
default:
|
||||
log_warn(LD_REND|LD_BUG, "Unrecognized authorization type %d",
|
||||
(int)auth_type);
|
||||
tor_free(ipos);
|
||||
return -1;
|
||||
}
|
||||
/* Base64-encode introduction points. */
|
||||
ipos_base64 = tor_malloc_zero(ipos_len * 2);
|
||||
if (base64_encode(ipos_base64, ipos_len * 2, ipos, ipos_len)<0) {
|
||||
log_warn(LD_REND, "Could not encode introduction point string to "
|
||||
"base64. length=%d", ipos_len);
|
||||
tor_free(ipos_base64);
|
||||
tor_free(ipos);
|
||||
return -1;
|
||||
}
|
||||
tor_free(ipos);
|
||||
}
|
||||
/* Encode REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS descriptors. */
|
||||
for (k = 0; k < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; k++) {
|
||||
@ -369,7 +549,7 @@ rend_encode_v2_descriptors(smartlist_t *descs_out,
|
||||
base32_encode(desc_id_base32, sizeof(desc_id_base32),
|
||||
enc->desc_id, DIGEST_LEN);
|
||||
/* PEM-encode the public key */
|
||||
if (crypto_pk_write_public_key_to_string(desc->pk, &permanent_key,
|
||||
if (crypto_pk_write_public_key_to_string(service_key, &permanent_key,
|
||||
&permanent_key_len) < 0) {
|
||||
log_warn(LD_BUG, "Could not write public key to string.");
|
||||
rend_encoded_v2_service_descriptor_free(enc);
|
||||
@ -437,7 +617,7 @@ rend_encode_v2_descriptors(smartlist_t *descs_out,
|
||||
}
|
||||
if (router_append_dirobj_signature(desc_str + written,
|
||||
desc_len - written,
|
||||
desc_digest, desc->pk) < 0) {
|
||||
desc_digest, service_key) < 0) {
|
||||
log_warn(LD_BUG, "Couldn't sign desc.");
|
||||
rend_encoded_v2_service_descriptor_free(enc);
|
||||
goto err;
|
||||
@ -1085,6 +1265,7 @@ rend_cache_store_v2_desc_as_client(const char *desc,
|
||||
rend_cache_entry_t *e;
|
||||
tor_assert(rend_cache);
|
||||
tor_assert(desc);
|
||||
(void) descriptor_cookie; /* We don't use it, yet. */
|
||||
/* Parse the descriptor. */
|
||||
if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
|
||||
&intro_size, &encoded_size,
|
||||
@ -1103,8 +1284,8 @@ rend_cache_store_v2_desc_as_client(const char *desc,
|
||||
}
|
||||
/* Decode/decrypt introduction points. */
|
||||
if (intro_content) {
|
||||
if (rend_decrypt_introduction_points(parsed, descriptor_cookie,
|
||||
intro_content, intro_size) < 0) {
|
||||
if (rend_parse_introduction_points(parsed, intro_content,
|
||||
intro_size) < 0) {
|
||||
log_warn(LD_PROTOCOL,"Couldn't decode/decrypt introduction points.");
|
||||
rend_service_descriptor_free(parsed);
|
||||
tor_free(intro_content);
|
||||
|
@ -487,9 +487,8 @@ rend_service_update_descriptor(rend_service_t *service)
|
||||
d->timestamp = time(NULL);
|
||||
d->version = service->descriptor_version;
|
||||
d->intro_nodes = smartlist_create();
|
||||
/* Whoever understands descriptor version 2 also understands intro
|
||||
* protocol 2. So we only support 2. */
|
||||
d->protocols = 1 << 2;
|
||||
/* Support intro protocols 2 and 3. */
|
||||
d->protocols = (1 << 2) + (1 << 3);
|
||||
|
||||
for (i = 0; i < smartlist_len(service->intro_nodes); ++i) {
|
||||
rend_intro_point_t *intro_svc = smartlist_get(service->intro_nodes, i);
|
||||
@ -1446,52 +1445,86 @@ upload_service_descriptor(rend_service_t *service)
|
||||
get_options()->PublishHidServDescriptors) {
|
||||
networkstatus_t *c = networkstatus_get_latest_consensus();
|
||||
if (c && smartlist_len(c->routerstatus_list) > 0) {
|
||||
int seconds_valid;
|
||||
int seconds_valid, i, j, num_descs;
|
||||
smartlist_t *descs = smartlist_create();
|
||||
int i;
|
||||
/* Encode the current descriptor. */
|
||||
seconds_valid = rend_encode_v2_descriptors(descs, service->desc, now,
|
||||
NULL, 0);
|
||||
if (seconds_valid < 0) {
|
||||
log_warn(LD_BUG, "Internal error: couldn't encode service descriptor; "
|
||||
"not uploading.");
|
||||
smartlist_free(descs);
|
||||
return;
|
||||
}
|
||||
/* Post the current descriptors to the hidden service directories. */
|
||||
rend_get_service_id(service->desc->pk, serviceid);
|
||||
log_info(LD_REND, "Sending publish request for hidden service %s",
|
||||
serviceid);
|
||||
directory_post_to_hs_dir(descs, serviceid, seconds_valid);
|
||||
/* Free memory for descriptors. */
|
||||
for (i = 0; i < smartlist_len(descs); i++)
|
||||
rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
|
||||
smartlist_clear(descs);
|
||||
/* Update next upload time. */
|
||||
if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS
|
||||
> rendpostperiod)
|
||||
service->next_upload_time = now + rendpostperiod;
|
||||
else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS)
|
||||
service->next_upload_time = now + seconds_valid + 1;
|
||||
else
|
||||
service->next_upload_time = now + seconds_valid -
|
||||
REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1;
|
||||
/* Post also the next descriptors, if necessary. */
|
||||
if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) {
|
||||
smartlist_t *client_cookies = smartlist_create();
|
||||
/* Either upload a single descriptor (including replicas) or one
|
||||
* descriptor for each authorized client in case of authorization
|
||||
* type 'stealth'. */
|
||||
num_descs = service->auth_type == REND_STEALTH_AUTH ?
|
||||
smartlist_len(service->clients) : 1;
|
||||
for (j = 0; j < num_descs; j++) {
|
||||
crypto_pk_env_t *client_key = NULL;
|
||||
rend_authorized_client_t *client = NULL;
|
||||
smartlist_clear(client_cookies);
|
||||
switch (service->auth_type) {
|
||||
case REND_NO_AUTH:
|
||||
/* Do nothing here. */
|
||||
break;
|
||||
case REND_BASIC_AUTH:
|
||||
SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *,
|
||||
cl, smartlist_add(client_cookies, cl->descriptor_cookie));
|
||||
break;
|
||||
case REND_STEALTH_AUTH:
|
||||
client = smartlist_get(service->clients, j);
|
||||
client_key = client->client_key;
|
||||
smartlist_add(client_cookies, client->descriptor_cookie);
|
||||
break;
|
||||
}
|
||||
/* Encode the current descriptor. */
|
||||
seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
|
||||
now, NULL, 1);
|
||||
now, 0,
|
||||
service->auth_type,
|
||||
client_key,
|
||||
client_cookies);
|
||||
if (seconds_valid < 0) {
|
||||
log_warn(LD_BUG, "Internal error: couldn't encode service "
|
||||
"descriptor; not uploading.");
|
||||
smartlist_free(descs);
|
||||
smartlist_free(client_cookies);
|
||||
return;
|
||||
}
|
||||
/* Post the current descriptors to the hidden service directories. */
|
||||
rend_get_service_id(service->desc->pk, serviceid);
|
||||
log_info(LD_REND, "Sending publish request for hidden service %s",
|
||||
serviceid);
|
||||
directory_post_to_hs_dir(descs, serviceid, seconds_valid);
|
||||
/* Free memory for descriptors. */
|
||||
for (i = 0; i < smartlist_len(descs); i++)
|
||||
rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
|
||||
smartlist_clear(descs);
|
||||
/* Update next upload time. */
|
||||
if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS
|
||||
> rendpostperiod)
|
||||
service->next_upload_time = now + rendpostperiod;
|
||||
else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS)
|
||||
service->next_upload_time = now + seconds_valid + 1;
|
||||
else
|
||||
service->next_upload_time = now + seconds_valid -
|
||||
REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1;
|
||||
/* Post also the next descriptors, if necessary. */
|
||||
if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) {
|
||||
seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
|
||||
now, 1,
|
||||
service->auth_type,
|
||||
client_key,
|
||||
client_cookies);
|
||||
if (seconds_valid < 0) {
|
||||
log_warn(LD_BUG, "Internal error: couldn't encode service "
|
||||
"descriptor; not uploading.");
|
||||
smartlist_free(descs);
|
||||
smartlist_free(client_cookies);
|
||||
return;
|
||||
}
|
||||
directory_post_to_hs_dir(descs, serviceid, seconds_valid);
|
||||
/* Free memory for descriptors. */
|
||||
for (i = 0; i < smartlist_len(descs); i++)
|
||||
rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
|
||||
smartlist_clear(descs);
|
||||
}
|
||||
}
|
||||
smartlist_free(descs);
|
||||
smartlist_free(client_cookies);
|
||||
uploaded = 1;
|
||||
log_info(LD_REND, "Successfully uploaded v2 rend descriptors!");
|
||||
}
|
||||
|
@ -3460,6 +3460,13 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
|
||||
eos = desc + strlen(desc);
|
||||
else
|
||||
eos = eos + 1;
|
||||
/* Check length. */
|
||||
if (strlen(desc) > REND_DESC_MAX_SIZE) {
|
||||
log_warn(LD_REND, "Descriptor length is %i which exceeds "
|
||||
"maximum rendezvous descriptor size of %i kilobytes.",
|
||||
strlen(desc), REND_DESC_MAX_SIZE);
|
||||
goto err;
|
||||
}
|
||||
/* Tokenize descriptor. */
|
||||
area = memarea_new(4096);
|
||||
if (tokenize_string(area, desc, eos, tokens, desc_token_table, 0)) {
|
||||
@ -3599,21 +3606,123 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Decrypt and decode the introduction points in
|
||||
* <b>intro_points_encrypted</b> of length
|
||||
* <b>intro_points_encrypted_size</b> using <b>descriptor_cookie</b>
|
||||
* (which may also be <b>NULL</b> if no decryption, but only parsing is
|
||||
* required), parse the introduction points, and write the result to
|
||||
* <b>parsed</b>; return the number of successfully parsed introduction
|
||||
* points or -1 in case of a failure.
|
||||
*/
|
||||
/** Decrypt the encrypted introduction points in <b>ipos_encrypted</b> of
|
||||
* length <b>ipos_encrypted_size</b> using <b>descriptor_cookie</b> and
|
||||
* write the result to a newly allocated string that is pointed to by
|
||||
* <b>ipos_decrypted</b> and its length to <b>ipos_decrypted_size</b>.
|
||||
* Return 0 if decryption was successful and -1 otherwise. */
|
||||
int
|
||||
rend_decrypt_introduction_points(rend_service_descriptor_t *parsed,
|
||||
rend_decrypt_introduction_points(char **ipos_decrypted,
|
||||
size_t *ipos_decrypted_size,
|
||||
const char *descriptor_cookie,
|
||||
const char *intro_points_encrypted,
|
||||
size_t intro_points_encrypted_size)
|
||||
const char *ipos_encrypted,
|
||||
size_t ipos_encrypted_size)
|
||||
{
|
||||
tor_assert(ipos_encrypted);
|
||||
tor_assert(descriptor_cookie);
|
||||
if (ipos_encrypted_size < 2) {
|
||||
log_warn(LD_REND, "Size of encrypted introduction points is too "
|
||||
"small.");
|
||||
return -1;
|
||||
}
|
||||
if (ipos_encrypted[0] == (int)REND_BASIC_AUTH) {
|
||||
char iv[CIPHER_IV_LEN], client_id[REND_BASIC_AUTH_CLIENT_ID_LEN],
|
||||
session_key[CIPHER_KEY_LEN], *dec;
|
||||
int declen, client_blocks;
|
||||
size_t pos = 0, len, client_entries_len;
|
||||
crypto_digest_env_t *digest;
|
||||
crypto_cipher_env_t *cipher;
|
||||
client_blocks = (int) ipos_encrypted[1];
|
||||
client_entries_len = client_blocks * REND_BASIC_AUTH_CLIENT_MULTIPLE *
|
||||
REND_BASIC_AUTH_CLIENT_ENTRY_LEN;
|
||||
if (ipos_encrypted_size < 2 + client_entries_len + CIPHER_IV_LEN + 1) {
|
||||
log_warn(LD_REND, "Size of encrypted introduction points is too "
|
||||
"small.");
|
||||
return -1;
|
||||
}
|
||||
memcpy(iv, ipos_encrypted + 2 + client_entries_len, CIPHER_IV_LEN);
|
||||
digest = crypto_new_digest_env();
|
||||
crypto_digest_add_bytes(digest, descriptor_cookie, REND_DESC_COOKIE_LEN);
|
||||
crypto_digest_add_bytes(digest, iv, CIPHER_IV_LEN);
|
||||
crypto_digest_get_digest(digest, client_id,
|
||||
REND_BASIC_AUTH_CLIENT_ID_LEN);
|
||||
crypto_free_digest_env(digest);
|
||||
for (pos = 2; pos < 2 + client_entries_len;
|
||||
pos += REND_BASIC_AUTH_CLIENT_ENTRY_LEN) {
|
||||
if (!memcmp(ipos_encrypted + pos, client_id,
|
||||
REND_BASIC_AUTH_CLIENT_ID_LEN)) {
|
||||
/* Attempt to decrypt introduction points. */
|
||||
cipher = crypto_create_init_cipher(descriptor_cookie, 0);
|
||||
if (crypto_cipher_decrypt(cipher, session_key, ipos_encrypted
|
||||
+ pos + REND_BASIC_AUTH_CLIENT_ID_LEN,
|
||||
CIPHER_KEY_LEN) < 0) {
|
||||
log_warn(LD_REND, "Could not decrypt session key for client.");
|
||||
crypto_free_cipher_env(cipher);
|
||||
return -1;
|
||||
}
|
||||
crypto_free_cipher_env(cipher);
|
||||
cipher = crypto_create_init_cipher(session_key, 0);
|
||||
len = ipos_encrypted_size - 2 - client_entries_len - CIPHER_IV_LEN;
|
||||
dec = tor_malloc_zero(len);
|
||||
declen = crypto_cipher_decrypt_with_iv(cipher, dec, len,
|
||||
ipos_encrypted + 2 + client_entries_len,
|
||||
ipos_encrypted_size - 2 - client_entries_len);
|
||||
crypto_free_cipher_env(cipher);
|
||||
if (declen < 0) {
|
||||
log_warn(LD_REND, "Could not decrypt introduction point string.");
|
||||
tor_free(dec);
|
||||
return -1;
|
||||
}
|
||||
if (strcmpstart(dec, "introduction-point ")) {
|
||||
log_warn(LD_REND, "Decrypted introduction points don't "
|
||||
"look like we could parse them.");
|
||||
tor_free(dec);
|
||||
continue;
|
||||
}
|
||||
*ipos_decrypted = dec;
|
||||
*ipos_decrypted_size = declen;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
log_warn(LD_REND, "Could not decrypt introduction points. Please "
|
||||
"check your authorization for this service!");
|
||||
return -1;
|
||||
} else if (ipos_encrypted[0] == (int)REND_STEALTH_AUTH) {
|
||||
crypto_cipher_env_t *cipher;
|
||||
char *dec;
|
||||
int declen;
|
||||
dec = tor_malloc_zero(ipos_encrypted_size - CIPHER_IV_LEN - 1);
|
||||
cipher = crypto_create_init_cipher(descriptor_cookie, 0);
|
||||
declen = crypto_cipher_decrypt_with_iv(cipher, dec,
|
||||
ipos_encrypted_size -
|
||||
CIPHER_IV_LEN - 1,
|
||||
ipos_encrypted + 1,
|
||||
ipos_encrypted_size - 1);
|
||||
crypto_free_cipher_env(cipher);
|
||||
if (declen < 0) {
|
||||
log_warn(LD_REND, "Decrypting introduction points failed!");
|
||||
tor_free(dec);
|
||||
return -1;
|
||||
}
|
||||
*ipos_decrypted = dec;
|
||||
*ipos_decrypted_size = declen;
|
||||
return 0;
|
||||
} else {
|
||||
log_warn(LD_REND, "Unknown authorization type number: %d",
|
||||
ipos_encrypted[0]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/** Parse the encoded introduction points in <b>intro_points_encoded</b> of
|
||||
* length <b>intro_points_encoded_size</b> and write the result to the
|
||||
* descriptor in <b>parsed</b>; return the number of successfully parsed
|
||||
* introduction points or -1 in case of a failure. */
|
||||
int
|
||||
rend_parse_introduction_points(rend_service_descriptor_t *parsed,
|
||||
const char *intro_points_encoded,
|
||||
size_t intro_points_encoded_size)
|
||||
{
|
||||
char *ipos_decrypted = NULL;
|
||||
const char **current_ipo;
|
||||
smartlist_t *tokens;
|
||||
directory_token_t *tok;
|
||||
@ -3624,28 +3733,10 @@ rend_decrypt_introduction_points(rend_service_descriptor_t *parsed,
|
||||
tor_assert(parsed);
|
||||
/** Function may only be invoked once. */
|
||||
tor_assert(!parsed->intro_nodes);
|
||||
tor_assert(intro_points_encrypted);
|
||||
tor_assert(intro_points_encrypted_size > 0);
|
||||
/* Decrypt introduction points, if required. */
|
||||
if (descriptor_cookie) {
|
||||
crypto_cipher_env_t *cipher;
|
||||
int unenclen;
|
||||
ipos_decrypted = tor_malloc_zero(intro_points_encrypted_size - 16);
|
||||
cipher = crypto_create_init_cipher(descriptor_cookie, 0);
|
||||
unenclen = crypto_cipher_decrypt_with_iv(cipher, ipos_decrypted,
|
||||
intro_points_encrypted_size - 16,
|
||||
intro_points_encrypted,
|
||||
intro_points_encrypted_size);
|
||||
crypto_free_cipher_env(cipher);
|
||||
if (unenclen < 0) {
|
||||
tor_free(ipos_decrypted);
|
||||
return -1;
|
||||
}
|
||||
intro_points_encrypted = ipos_decrypted;
|
||||
intro_points_encrypted_size = unenclen;
|
||||
}
|
||||
tor_assert(intro_points_encoded);
|
||||
tor_assert(intro_points_encoded_size > 0);
|
||||
/* Consider one intro point after the other. */
|
||||
current_ipo = &intro_points_encrypted;
|
||||
current_ipo = &intro_points_encoded;
|
||||
tokens = smartlist_create();
|
||||
parsed->intro_nodes = smartlist_create();
|
||||
area = memarea_new(4096);
|
||||
|
@ -4049,8 +4049,8 @@ test_rend_fns_v2(void)
|
||||
intro->intro_key = crypto_pk_dup_key(pk2);
|
||||
smartlist_add(generated->intro_nodes, intro);
|
||||
}
|
||||
test_assert(rend_encode_v2_descriptors(descs, generated, now,
|
||||
NULL, 0) > 0);
|
||||
test_assert(rend_encode_v2_descriptors(descs, generated, now, 0,
|
||||
REND_NO_AUTH, NULL, NULL) > 0);
|
||||
test_assert(rend_compute_v2_desc_id(computed_desc_id, service_id_base32,
|
||||
NULL, now, 0) == 0);
|
||||
test_memeq(((rend_encoded_v2_service_descriptor_t *)
|
||||
@ -4065,9 +4065,8 @@ test_rend_fns_v2(void)
|
||||
test_assert(parsed);
|
||||
test_memeq(((rend_encoded_v2_service_descriptor_t *)
|
||||
smartlist_get(descs, 0))->desc_id, parsed_desc_id, DIGEST_LEN);
|
||||
test_assert(rend_decrypt_introduction_points(parsed, NULL,
|
||||
intro_points_encrypted,
|
||||
intro_points_size) == 3);
|
||||
test_assert(rend_parse_introduction_points(parsed, intro_points_encrypted,
|
||||
intro_points_size) == 3);
|
||||
test_assert(!crypto_pk_cmp_keys(generated->pk, parsed->pk));
|
||||
test_eq(parsed->timestamp, now);
|
||||
test_eq(parsed->version, 2);
|
||||
|
Loading…
Reference in New Issue
Block a user