📄 rendservice.c
字号:
service->desc = NULL;
}
d = service->desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
d->pk = crypto_pk_dup_key(service->private_key);
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;
for (i = 0; i < smartlist_len(service->intro_nodes); ++i) {
rend_intro_point_t *intro_svc = smartlist_get(service->intro_nodes, i);
rend_intro_point_t *intro_desc;
circ = find_intro_circuit(intro_svc, service->pk_digest, d->version);
if (!circ || circ->_base.purpose != CIRCUIT_PURPOSE_S_INTRO)
continue;
/* We have an entirely established intro circuit. */
intro_desc = tor_malloc_zero(sizeof(rend_intro_point_t));
intro_desc->extend_info = extend_info_dup(intro_svc->extend_info);
if (intro_svc->intro_key)
intro_desc->intro_key = crypto_pk_dup_key(intro_svc->intro_key);
smartlist_add(d->intro_nodes, intro_desc);
}
}
/** Load and/or generate private keys for all hidden services. Return 0 on
* success, -1 on failure.
*/
int
rend_service_load_keys(void)
{
int i;
rend_service_t *s;
char fname[512];
char buf[128];
for (i=0; i < smartlist_len(rend_service_list); ++i) {
s = smartlist_get(rend_service_list,i);
if (s->private_key)
continue;
log_info(LD_REND, "Loading hidden-service keys from \"%s\"",
s->directory);
/* Check/create directory */
if (check_private_dir(s->directory, CPD_CREATE) < 0)
return -1;
/* Load key */
if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
strlcat(fname,PATH_SEPARATOR"private_key",sizeof(fname))
>= sizeof(fname)) {
log_warn(LD_CONFIG, "Directory name too long to store key file: \"%s\".",
s->directory);
return -1;
}
s->private_key = init_key_from_file(fname, 1, LOG_ERR);
if (!s->private_key)
return -1;
/* Create service file */
if (rend_get_service_id(s->private_key, s->service_id)<0) {
log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
return -1;
}
if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) {
log_warn(LD_BUG, "Couldn't compute hash of public key.");
return -1;
}
if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
strlcat(fname,PATH_SEPARATOR"hostname",sizeof(fname))
>= sizeof(fname)) {
log_warn(LD_CONFIG, "Directory name too long to store hostname file:"
" \"%s\".", s->directory);
return -1;
}
tor_snprintf(buf, sizeof(buf),"%s.onion\n", s->service_id);
if (write_str_to_file(fname,buf,0)<0)
return -1;
}
return 0;
}
/** Return the service whose public key has a digest of <b>digest</b> and
* which publishes the given descriptor <b>version</b>. Return NULL if no
* such service exists.
*/
static rend_service_t *
rend_service_get_by_pk_digest_and_version(const char* digest,
uint8_t version)
{
SMARTLIST_FOREACH(rend_service_list, rend_service_t*, s,
if (!memcmp(s->pk_digest,digest,DIGEST_LEN) &&
s->descriptor_version == version) return s);
return NULL;
}
/** Return 1 if any virtual port in <b>service</b> wants a circuit
* to have good uptime. Else return 0.
*/
static int
rend_service_requires_uptime(rend_service_t *service)
{
int i;
rend_service_port_config_t *p;
for (i=0; i < smartlist_len(service->ports); ++i) {
p = smartlist_get(service->ports, i);
if (smartlist_string_num_isin(get_options()->LongLivedPorts,
p->virtual_port))
return 1;
}
return 0;
}
/******
* Handle cells
******/
/** Respond to an INTRODUCE2 cell by launching a circuit to the chosen
* rendezvous point.
*/
int
rend_service_introduce(origin_circuit_t *circuit, const char *request,
size_t request_len)
{
char *ptr, *r_cookie;
extend_info_t *extend_info = NULL;
char buf[RELAY_PAYLOAD_SIZE];
char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */
rend_service_t *service;
int r, i;
size_t len, keylen;
crypto_dh_env_t *dh = NULL;
origin_circuit_t *launched = NULL;
crypt_path_t *cpath = NULL;
char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
char hexcookie[9];
int circ_needs_uptime;
int reason = END_CIRC_REASON_TORPROTOCOL;
crypto_pk_env_t *intro_key;
char intro_key_digest[DIGEST_LEN];
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
circuit->rend_pk_digest, REND_SERVICE_ID_LEN);
log_info(LD_REND, "Received INTRODUCE2 cell for service %s on circ %d.",
escaped(serviceid), circuit->_base.n_circ_id);
if (circuit->_base.purpose != CIRCUIT_PURPOSE_S_INTRO) {
log_warn(LD_PROTOCOL,
"Got an INTRODUCE2 over a non-introduction circuit %d.",
circuit->_base.n_circ_id);
return -1;
}
/* min key length plus digest length plus nickname length */
if (request_len < DIGEST_LEN+REND_COOKIE_LEN+(MAX_NICKNAME_LEN+1)+
DH_KEY_LEN+42) {
log_warn(LD_PROTOCOL, "Got a truncated INTRODUCE2 cell on circ %d.",
circuit->_base.n_circ_id);
return -1;
}
/* look up service depending on circuit. */
service = rend_service_get_by_pk_digest_and_version(
circuit->rend_pk_digest, circuit->rend_desc_version);
if (!service) {
log_warn(LD_REND, "Got an INTRODUCE2 cell for an unrecognized service %s.",
escaped(serviceid));
return -1;
}
/* if descriptor version is 2, use intro key instead of service key. */
if (circuit->rend_desc_version == 0) {
intro_key = service->private_key;
} else {
intro_key = circuit->intro_key;
}
/* first DIGEST_LEN bytes of request is intro or service pk digest */
crypto_pk_get_digest(intro_key, intro_key_digest);
if (memcmp(intro_key_digest, request, DIGEST_LEN)) {
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
request, REND_SERVICE_ID_LEN);
log_warn(LD_REND, "Got an INTRODUCE2 cell for the wrong service (%s).",
escaped(serviceid));
return -1;
}
keylen = crypto_pk_keysize(intro_key);
if (request_len < keylen+DIGEST_LEN) {
log_warn(LD_PROTOCOL,
"PK-encrypted portion of INTRODUCE2 cell was truncated.");
return -1;
}
/* Next N bytes is encrypted with service key */
note_crypto_pk_op(REND_SERVER);
r = crypto_pk_private_hybrid_decrypt(
intro_key,buf,request+DIGEST_LEN,request_len-DIGEST_LEN,
PK_PKCS1_OAEP_PADDING,1);
if (r<0) {
log_warn(LD_PROTOCOL, "Couldn't decrypt INTRODUCE2 cell.");
return -1;
}
len = r;
if (*buf == 2) {
/* Version 2 INTRODUCE2 cell. */
int klen;
extend_info = tor_malloc_zero(sizeof(extend_info_t));
extend_info->addr = ntohl(get_uint32(buf+1));
extend_info->port = ntohs(get_uint16(buf+5));
memcpy(extend_info->identity_digest, buf+7, DIGEST_LEN);
extend_info->nickname[0] = '$';
base16_encode(extend_info->nickname+1, sizeof(extend_info->nickname)-1,
extend_info->identity_digest, DIGEST_LEN);
klen = ntohs(get_uint16(buf+7+DIGEST_LEN));
if ((int)len != 7+DIGEST_LEN+2+klen+20+128) {
log_warn(LD_PROTOCOL, "Bad length %u for version 2 INTRODUCE2 cell.",
(int)len);
reason = END_CIRC_REASON_TORPROTOCOL;
goto err;
}
extend_info->onion_key = crypto_pk_asn1_decode(buf+7+DIGEST_LEN+2, klen);
if (!extend_info->onion_key) {
log_warn(LD_PROTOCOL,
"Error decoding onion key in version 2 INTRODUCE2 cell.");
reason = END_CIRC_REASON_TORPROTOCOL;
goto err;
}
ptr = buf+7+DIGEST_LEN+2+klen;
len -= 7+DIGEST_LEN+2+klen;
} else {
char *rp_nickname;
size_t nickname_field_len;
routerinfo_t *router;
int version;
if (*buf == 1) {
rp_nickname = buf+1;
nickname_field_len = MAX_HEX_NICKNAME_LEN+1;
version = 1;
} else {
nickname_field_len = MAX_NICKNAME_LEN+1;
rp_nickname = buf;
version = 0;
}
ptr=memchr(rp_nickname,0,nickname_field_len);
if (!ptr || ptr == rp_nickname) {
log_warn(LD_PROTOCOL,
"Couldn't find a nul-padded nickname in INTRODUCE2 cell.");
return -1;
}
if ((version == 0 && !is_legal_nickname(rp_nickname)) ||
(version == 1 && !is_legal_nickname_or_hexdigest(rp_nickname))) {
log_warn(LD_PROTOCOL, "Bad nickname in INTRODUCE2 cell.");
return -1;
}
/* Okay, now we know that a nickname is at the start of the buffer. */
ptr = rp_nickname+nickname_field_len;
len -= nickname_field_len;
len -= rp_nickname - buf; /* also remove header space used by version, if
* any */
router = router_get_by_nickname(rp_nickname, 0);
if (!router) {
log_info(LD_REND, "Couldn't find router %s named in introduce2 cell.",
escaped_safe_str(rp_nickname));
/* XXXX Add a no-such-router reason? */
reason = END_CIRC_REASON_TORPROTOCOL;
goto err;
}
extend_info = extend_info_from_router(router);
}
if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
log_warn(LD_PROTOCOL, "Bad length %u for INTRODUCE2 cell.", (int)len);
reason = END_CIRC_REASON_TORPROTOCOL;
goto err;
}
r_cookie = ptr;
base16_encode(hexcookie,9,r_cookie,4);
/* Try DH handshake... */
dh = crypto_dh_new();
if (!dh || crypto_dh_generate_public(dh)<0) {
log_warn(LD_BUG,"Internal error: couldn't build DH state "
"or generate public key.");
reason = END_CIRC_REASON_INTERNAL;
goto err;
}
if (crypto_dh_compute_secret(dh, ptr+REND_COOKIE_LEN, DH_KEY_LEN, keys,
DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) {
log_warn(LD_BUG, "Internal error: couldn't complete DH handshake");
reason = END_CIRC_REASON_INTERNAL;
goto err;
}
circ_needs_uptime = rend_service_requires_uptime(service);
/* help predict this next time */
rep_hist_note_used_internal(time(NULL), circ_needs_uptime, 1);
/* Launch a circuit to alice's chosen rendezvous point.
*/
for (i=0;i<MAX_REND_FAILURES;i++) {
int flags = CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_IS_INTERNAL;
if (circ_needs_uptime) flags |= CIRCLAUNCH_NEED_UPTIME;
launched = circuit_launch_by_extend_info(
CIRCUIT_PURPOSE_S_CONNECT_REND, extend_info, flags);
if (launched)
break;
}
if (!launched) { /* give up */
log_warn(LD_REND, "Giving up launching first hop of circuit to rendezvous "
"point %s for service %s.",
escaped_safe_str(extend_info->nickname), serviceid);
reason = END_CIRC_REASON_CONNECTFAILED;
goto err;
}
log_info(LD_REND,
"Accepted intro; launching circuit to %s "
"(cookie %s) for service %s.",
escaped_safe_str(extend_info->nickname), hexcookie, serviceid);
tor_assert(launched->build_state);
/* Fill in the circuit's state. */
memcpy(launched->rend_pk_digest, circuit->rend_pk_digest,
DIGEST_LEN);
memcpy(launched->rend_cookie, r_cookie, REND_COOKIE_LEN);
strlcpy(launched->rend_query, service->service_id,
sizeof(launched->rend_query));
launched->rend_desc_version = service->descriptor_version;
launched->build_state->pending_final_cpath = cpath =
tor_malloc_zero(sizeof(crypt_path_t));
cpath->magic = CRYPT_PATH_MAGIC;
launched->build_state->expiry_time = time(NULL) + MAX_REND_TIMEOUT;
cpath->dh_handshake_state = dh;
dh = NULL;
if (circuit_init_cpath_crypto(cpath,keys+DIGEST_LEN,1)<0)
goto err;
memcpy(cpath->handshake_digest, keys, DIGEST_LEN);
if (extend_info) extend_info_free(extend_info);
return 0;
err:
if (dh) crypto_dh_free(dh);
if (launched)
circuit_mark_for_close(TO_CIRCUIT(launched), reason);
if (extend_info) extend_info_free(extend_info);
return -1;
}
/** Called when we fail building a rendezvous circuit at some point other
* than the last hop: launches a new circuit to the same rendezvous point.
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -