📄 rendcommon.c
字号:
written = result;
/* Add introduction points. */
if (ipos_base64) {
result = tor_snprintf(desc_str + written, desc_len - written,
"introduction-points\n"
"-----BEGIN MESSAGE-----\n%s"
"-----END MESSAGE-----\n",
ipos_base64);
if (result < 0) {
log_warn(LD_BUG, "could not write introduction points.");
rend_encoded_v2_service_descriptor_free(enc);
goto err;
}
written += result;
}
/* Add signature. */
strlcpy(desc_str + written, "signature\n", desc_len - written);
written += strlen(desc_str + written);
if (crypto_digest(desc_digest, desc_str, written) < 0) {
log_warn(LD_BUG, "could not create digest.");
rend_encoded_v2_service_descriptor_free(enc);
goto err;
}
if (router_append_dirobj_signature(desc_str + written,
desc_len - written,
desc_digest, desc->pk) < 0) {
log_warn(LD_BUG, "Couldn't sign desc.");
rend_encoded_v2_service_descriptor_free(enc);
goto err;
}
written += strlen(desc_str+written);
if (written+2 > desc_len) {
log_warn(LD_BUG, "Could not finish desc.");
rend_encoded_v2_service_descriptor_free(enc);
goto err;
}
desc_str[written++] = '\n';
desc_str[written++] = 0;
/* Check if we can parse our own descriptor. */
if (!rend_desc_v2_is_parsable(enc)) {
log_warn(LD_BUG, "Could not parse my own descriptor: %s", desc_str);
rend_encoded_v2_service_descriptor_free(enc);
goto err;
}
smartlist_add(descs_out, enc);
}
log_info(LD_REND, "Successfully encoded a v2 descriptor and "
"confirmed that it is parsable.");
goto done;
err:
SMARTLIST_FOREACH(descs_out, rend_encoded_v2_service_descriptor_t *, d,
rend_encoded_v2_service_descriptor_free(d););
smartlist_clear(descs_out);
seconds_valid = -1;
done:
tor_free(ipos_base64);
return seconds_valid;
}
/** Encode a service descriptor for <b>desc</b>, and sign it with
* <b>key</b>. Store the descriptor in *<b>str_out</b>, and set
* *<b>len_out</b> to its length.
*/
int
rend_encode_service_descriptor(rend_service_descriptor_t *desc,
crypto_pk_env_t *key,
char **str_out, size_t *len_out)
{
char *cp;
char *end;
int i, r;
size_t asn1len;
size_t buflen =
PK_BYTES*2*(smartlist_len(desc->intro_nodes)+2);/*Too long, but ok*/
cp = *str_out = tor_malloc(buflen);
end = cp + PK_BYTES*2*(smartlist_len(desc->intro_nodes)+1);
r = crypto_pk_asn1_encode(desc->pk, cp+2, end-(cp+2));
if (r < 0) {
tor_free(*str_out);
return -1;
}
asn1len = r;
set_uint16(cp, htons((uint16_t)asn1len));
cp += 2+asn1len;
set_uint32(cp, htonl((uint32_t)desc->timestamp));
cp += 4;
set_uint16(cp, htons((uint16_t)smartlist_len(desc->intro_nodes)));
cp += 2;
for (i=0; i < smartlist_len(desc->intro_nodes); ++i) {
rend_intro_point_t *intro = smartlist_get(desc->intro_nodes, i);
char ipoint[HEX_DIGEST_LEN+2];
ipoint[0] = '$';
base16_encode(ipoint+1, HEX_DIGEST_LEN+1,
intro->extend_info->identity_digest,
DIGEST_LEN);
tor_assert(buflen + *str_out >= cp); /* XXX021 This assert is a kludge. */
strlcpy(cp, ipoint, buflen-(cp-*str_out));
cp += strlen(ipoint)+1;
}
note_crypto_pk_op(REND_SERVER);
r = crypto_pk_private_sign_digest(key, cp, *str_out, cp-*str_out);
if (r<0) {
tor_free(*str_out);
return -1;
}
cp += r;
*len_out = (size_t)(cp-*str_out);
return 0;
}
/** Parse a service descriptor at <b>str</b> (<b>len</b> bytes). On
* success, return a newly alloced service_descriptor_t. On failure,
* return NULL.
*/
rend_service_descriptor_t *
rend_parse_service_descriptor(const char *str, size_t len)
{
rend_service_descriptor_t *result = NULL;
int i, n_intro_points;
size_t keylen, asn1len;
const char *end, *cp, *eos;
rend_intro_point_t *intro;
result = tor_malloc_zero(sizeof(rend_service_descriptor_t));
cp = str;
end = str+len;
if (end-cp<2) goto truncated;
result->version = 0;
if (end-cp < 2) goto truncated;
asn1len = ntohs(get_uint16(cp));
cp += 2;
if ((size_t)(end-cp) < asn1len) goto truncated;
result->pk = crypto_pk_asn1_decode(cp, asn1len);
if (!result->pk) goto truncated;
cp += asn1len;
if (end-cp < 4) goto truncated;
result->timestamp = (time_t) ntohl(get_uint32(cp));
cp += 4;
result->protocols = 1<<2; /* always use intro format 2 */
if (end-cp < 2) goto truncated;
n_intro_points = ntohs(get_uint16(cp));
cp += 2;
result->intro_nodes = smartlist_create();
for (i=0;i<n_intro_points;++i) {
if (end-cp < 2) goto truncated;
eos = (const char *)memchr(cp,'\0',end-cp);
if (!eos) goto truncated;
/* Write nickname to extend info, but postpone the lookup whether
* we know that router. It's not part of the parsing process. */
intro = tor_malloc_zero(sizeof(rend_intro_point_t));
intro->extend_info = tor_malloc_zero(sizeof(extend_info_t));
strlcpy(intro->extend_info->nickname, cp,
sizeof(intro->extend_info->nickname));
smartlist_add(result->intro_nodes, intro);
cp = eos+1;
}
keylen = crypto_pk_keysize(result->pk);
tor_assert(end-cp >= 0);
if ((size_t)(end-cp) < keylen) goto truncated;
if ((size_t)(end-cp) > keylen) {
log_warn(LD_PROTOCOL,
"Signature is %d bytes too long on service descriptor.",
(int)((size_t)(end-cp) - keylen));
goto error;
}
note_crypto_pk_op(REND_CLIENT);
if (crypto_pk_public_checksig_digest(result->pk,
(char*)str,cp-str, /* data */
(char*)cp,end-cp /* signature*/
)<0) {
log_warn(LD_PROTOCOL, "Bad signature on service descriptor.");
goto error;
}
return result;
truncated:
log_warn(LD_PROTOCOL, "Truncated service descriptor.");
error:
rend_service_descriptor_free(result);
return NULL;
}
/** Sets <b>out</b> to the first 10 bytes of the digest of <b>pk</b>,
* base32 encoded. NUL-terminates out. (We use this string to
* identify services in directory requests and .onion URLs.)
*/
int
rend_get_service_id(crypto_pk_env_t *pk, char *out)
{
char buf[DIGEST_LEN];
tor_assert(pk);
if (crypto_pk_get_digest(pk, buf) < 0)
return -1;
base32_encode(out, REND_SERVICE_ID_LEN_BASE32+1, buf, REND_SERVICE_ID_LEN);
return 0;
}
/* ==== Rendezvous service descriptor cache. */
/** How old do we let hidden service descriptors get before discarding
* them as too old? */
#define REND_CACHE_MAX_AGE (2*24*60*60)
/** How wrong do we assume our clock may be when checking whether hidden
* services are too old or too new? */
#define REND_CACHE_MAX_SKEW (24*60*60)
/** Map from service id (as generated by rend_get_service_id) to
* rend_cache_entry_t. */
static strmap_t *rend_cache = NULL;
/** Map from descriptor id to rend_cache_entry_t; only for hidden service
* directories. */
static digestmap_t *rend_cache_v2_dir = NULL;
/** Initializes the service descriptor cache.
*/
void
rend_cache_init(void)
{
rend_cache = strmap_new();
rend_cache_v2_dir = digestmap_new();
}
/** Helper: free storage held by a single service descriptor cache entry. */
static void
_rend_cache_entry_free(void *p)
{
rend_cache_entry_t *e = p;
rend_service_descriptor_free(e->parsed);
tor_free(e->desc);
tor_free(e);
}
/** Free all storage held by the service descriptor cache. */
void
rend_cache_free_all(void)
{
strmap_free(rend_cache, _rend_cache_entry_free);
digestmap_free(rend_cache_v2_dir, _rend_cache_entry_free);
rend_cache = NULL;
rend_cache_v2_dir = NULL;
}
/** Removes all old entries from the service descriptor cache.
*/
void
rend_cache_clean(void)
{
strmap_iter_t *iter;
const char *key;
void *val;
rend_cache_entry_t *ent;
time_t cutoff;
cutoff = time(NULL) - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW;
for (iter = strmap_iter_init(rend_cache); !strmap_iter_done(iter); ) {
strmap_iter_get(iter, &key, &val);
ent = (rend_cache_entry_t*)val;
if (ent->parsed->timestamp < cutoff) {
iter = strmap_iter_next_rmv(rend_cache, iter);
_rend_cache_entry_free(ent);
} else {
iter = strmap_iter_next(rend_cache, iter);
}
}
}
/** Remove all old v2 descriptors and those for which this hidden service
* directory is not responsible for any more. */
void
rend_cache_clean_v2_descs_as_dir(void)
{
digestmap_iter_t *iter;
time_t cutoff = time(NULL) - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW;
for (iter = digestmap_iter_init(rend_cache_v2_dir);
!digestmap_iter_done(iter); ) {
const char *key;
void *val;
rend_cache_entry_t *ent;
digestmap_iter_get(iter, &key, &val);
ent = val;
if (ent->parsed->timestamp < cutoff ||
!hid_serv_responsible_for_desc_id(key)) {
char key_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
base32_encode(key_base32, sizeof(key_base32), key, DIGEST_LEN);
log_info(LD_REND, "Removing descriptor with ID '%s' from cache",
safe_str(key_base32));
iter = digestmap_iter_next_rmv(rend_cache_v2_dir, iter);
_rend_cache_entry_free(ent);
} else {
iter = digestmap_iter_next(rend_cache_v2_dir, iter);
}
}
}
/** Determines whether <b>a</b> is in the interval of <b>b</b> (excluded) and
* <b>c</b> (included) in a circular digest ring; returns 1 if this is the
* case, and 0 otherwise.
*/
int
rend_id_is_in_interval(const char *a, const char *b, const char *c)
{
int a_b, b_c, c_a;
tor_assert(a);
tor_assert(b);
tor_assert(c);
/* There are five cases in which a is outside the interval ]b,c]: */
a_b = memcmp(a,b,DIGEST_LEN);
if (a_b == 0)
return 0; /* 1. a == b (b is excluded) */
b_c = memcmp(b,c,DIGEST_LEN);
if (b_c == 0)
return 0; /* 2. b == c (interval is empty) */
else if (a_b <= 0 && b_c < 0)
return 0; /* 3. a b c */
c_a = memcmp(c,a,DIGEST_LEN);
if (c_a < 0 && a_b <= 0)
return 0; /* 4. c a b */
else if (b_c < 0 && c_a < 0)
return 0; /* 5. b c a */
/* In the other cases (a c b; b a c; c b a), a is inside the interval. */
return 1;
}
/** Return true iff <b>query</b> is a syntactically valid service ID (as
* generated by rend_get_service_id). */
int
rend_valid_service_id(const char *query)
{
if (strlen(query) != REND_SERVICE_ID_LEN_BASE32)
return 0;
if (strspn(query, BASE32_CHARS) != REND_SERVICE_ID_LEN_BASE32)
return 0;
return 1;
}
/** If we have a cached rend_cache_entry_t for the service ID <b>query</b>
* with <b>version</b>, set *<b>e</b> to that entry and return 1.
* Else return 0. If <b>version</b> is nonnegative, only return an entry
* in that descriptor format version. Otherwise (if <b>version</b> is
* negative), return the most recent format we have.
*/
int
rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e)
{
char key[REND_SERVICE_ID_LEN_BASE32+2]; /* <version><query>\0 */
tor_assert(rend_cache);
if (!rend_valid_service_id(query))
return -1;
*e = NULL;
if (version != 0) {
tor_snprintf(key, sizeof(key), "2%s", query);
*e = strmap_get_lc(rend_cache, key);
}
if (!*e && version != 2) {
tor_snprintf(key, sizeof(key), "0%s", query);
*e = strmap_get_lc(rend_cache, key);
}
if (!*e)
return 0;
return 1;
}
/** <b>query</b> is a base-32'ed service id. If it's malformed, return -1.
* Else look it up.
* - If it is found, point *desc to it, and write its length into
* *desc_len, and return 1.
* - If it is not found, return 0.
* Note: calls to rend_cache_clean or rend_cache_store may invalidate
* *desc.
*/
int
rend_cache_lookup_desc(const char *query, int version, const char **desc,
size_t *desc_len)
{
rend_cache_entry_t *e;
int r;
r = rend_cache_lookup_entry(query,version,&e);
if (r <= 0) return r;
*desc = e->desc;
*desc_len = e->len;
return 1;
}
/** Lookup the v2 service descriptor with base32-encoded <b>desc_id</b> and
* copy the pointer to it to *<b>desc</b>. Return 1 on success, 0 on
* well-formed-but-not-found, and -1 on failure.
*/
int
rend_cache_lookup_v2_desc_as_dir(const char *desc_id, const char **desc)
{
rend_cache_entry_t *e;
char desc_id_digest[DIGEST_LEN];
tor_assert(rend_cache_v2_dir);
if (base32_decode(desc_id_digest, DIGEST_LEN,
desc_id, REND_DESC_ID_V2_LEN_BASE32) < 0) {
log_warn(LD_REND, "Descriptor ID contains illegal characters: %s",
safe_str(desc_id));
return -1;
}
/* Determine if we are responsible. */
if (hid_serv_responsible_for_desc_id(desc_id_digest) < 0) {
log_info(LD_REND, "Could not answer fetch request for v2 descriptor; "
"either we are no hidden service directory, or we are "
"not responsible for the requested ID.");
return -1;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -