1
/* Copyright 2004-2005 Roger Dingledine, Nick Mathewson. */
1
/* Copyright 2004-2006 Roger Dingledine, Nick Mathewson. */
2
2
/* See LICENSE for licensing information */
3
/* $Id: rendclient.c,v 1.86 2005/05/03 10:04:08 arma Exp $ */
4
const char rendclient_c_id[] = "$Id: rendclient.c,v 1.86 2005/05/03 10:04:08 arma Exp $";
3
/* $Id: rendclient.c,v 1.107 2006/03/06 00:25:39 nickm Exp $ */
4
const char rendclient_c_id[] =
5
"$Id: rendclient.c,v 1.107 2006/03/06 00:25:39 nickm Exp $";
30
31
rend_client_send_establish_rendezvous(circuit_t *circ)
32
33
tor_assert(circ->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND);
33
log_fn(LOG_INFO, "Sending an ESTABLISH_RENDEZVOUS cell");
34
log_info(LD_REND, "Sending an ESTABLISH_RENDEZVOUS cell");
35
36
if (crypto_rand(circ->rend_cookie, REND_COOKIE_LEN) < 0) {
36
log_fn(LOG_WARN, "Couldn't get random cookie");
37
circuit_mark_for_close(circ);
37
log_warn(LD_BUG, "Internal error: Couldn't produce random cookie.");
38
circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
40
41
if (connection_edge_send_command(NULL,circ,
53
54
* down introcirc if possible.
56
rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc) {
57
rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc)
57
59
size_t payload_len;
59
61
char payload[RELAY_PAYLOAD_SIZE];
60
char tmp[1+(MAX_HEX_NICKNAME_LEN+1)+REND_COOKIE_LEN+DH_KEY_LEN];
62
char tmp[RELAY_PAYLOAD_SIZE];
61
63
rend_cache_entry_t *entry;
62
64
crypt_path_t *cpath;
64
67
tor_assert(introcirc->purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
65
68
tor_assert(rendcirc->purpose == CIRCUIT_PURPOSE_C_REND_READY);
66
tor_assert(!rend_cmp_service_ids(introcirc->rend_query, rendcirc->rend_query));
69
tor_assert(!rend_cmp_service_ids(introcirc->rend_query,
70
rendcirc->rend_query));
68
if (rend_cache_lookup_entry(introcirc->rend_query, &entry) < 1) {
69
log_fn(LOG_WARN,"query '%s' didn't have valid rend desc in cache. Failing.",
70
safe_str(introcirc->rend_query));
72
if (rend_cache_lookup_entry(introcirc->rend_query, -1, &entry) < 1) {
74
"query %s didn't have valid rend desc in cache. Failing.",
75
escaped_safe_str(introcirc->rend_query));
74
79
/* first 20 bytes of payload are the hash of bob's pk */
75
80
if (crypto_pk_get_digest(entry->parsed->pk, payload)<0) {
76
log_fn(LOG_WARN, "Couldn't hash public key.");
81
log_warn(LD_BUG, "Internal error: couldn't hash public key.");
84
89
tor_malloc_zero(sizeof(crypt_path_t));
85
90
cpath->magic = CRYPT_PATH_MAGIC;
86
91
if (!(cpath->dh_handshake_state = crypto_dh_new())) {
87
log_fn(LOG_WARN, "Couldn't allocate DH");
92
log_warn(LD_BUG, "Internal error: couldn't allocate DH.");
90
95
if (crypto_dh_generate_public(cpath->dh_handshake_state)<0) {
91
log_fn(LOG_WARN, "Couldn't generate g^x");
96
log_warn(LD_BUG, "Internal error: couldn't generate g^x.");
96
101
/* write the remaining items into tmp */
98
tmp[0] = 1; /* version 1 of the cell format */
100
strncpy(tmp+1, rendcirc->build_state->chosen_exit_name, (MAX_HEX_NICKNAME_LEN+1));
101
memcpy(tmp+1+MAX_HEX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN);
103
strncpy(tmp, rendcirc->build_state->chosen_exit_name, (MAX_NICKNAME_LEN+1)); /* nul pads */
104
memcpy(tmp+MAX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN);
106
if (crypto_dh_get_public(cpath->dh_handshake_state,
108
tmp+1+MAX_HEX_NICKNAME_LEN+1+REND_COOKIE_LEN,
110
tmp+MAX_NICKNAME_LEN+1+REND_COOKIE_LEN,
102
if (entry->parsed->protocols & (1<<2)) {
103
/* version 2 format */
104
extend_info_t *extend_info = rendcirc->build_state->chosen_exit;
106
tmp[0] = 2; /* version 2 of the cell format */
108
set_uint32(tmp+1, htonl(extend_info->addr));
109
set_uint16(tmp+5, htons(extend_info->port));
110
memcpy(tmp+7, extend_info->identity_digest, DIGEST_LEN);
111
klen = crypto_pk_asn1_encode(extend_info->onion_key, tmp+7+DIGEST_LEN+2,
112
sizeof(tmp)-(7+DIGEST_LEN+2));
113
set_uint16(tmp+7+DIGEST_LEN, htons(klen));
114
memcpy(tmp+7+DIGEST_LEN+2+klen, rendcirc->rend_cookie, REND_COOKIE_LEN);
115
dh_offset = 7+DIGEST_LEN+2+klen+REND_COOKIE_LEN;
118
strncpy(tmp, rendcirc->build_state->chosen_exit->nickname,
119
(MAX_NICKNAME_LEN+1)); /* nul pads */
120
memcpy(tmp+MAX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN);
121
dh_offset = MAX_NICKNAME_LEN+1+REND_COOKIE_LEN;
124
if (crypto_dh_get_public(cpath->dh_handshake_state, tmp+dh_offset,
114
log_fn(LOG_WARN, "Couldn't extract g^x");
126
log_warn(LD_BUG, "Internal error: couldn't extract g^x.");
118
130
/*XXX maybe give crypto_pk_public_hybrid_encrypt a max_len arg,
119
131
* to avoid buffer overflows? */
120
r = crypto_pk_public_hybrid_encrypt(entry->parsed->pk, payload+DIGEST_LEN, tmp,
122
1+MAX_HEX_NICKNAME_LEN+1+REND_COOKIE_LEN+DH_KEY_LEN,
124
MAX_NICKNAME_LEN+1+REND_COOKIE_LEN+DH_KEY_LEN,
132
r = crypto_pk_public_hybrid_encrypt(entry->parsed->pk, payload+DIGEST_LEN,
134
dh_offset+DH_KEY_LEN,
126
135
PK_PKCS1_OAEP_PADDING, 0);
128
log_fn(LOG_WARN,"hybrid pk encrypt failed.");
137
log_warn(LD_BUG,"Internal error: hybrid pk encrypt failed.");
173
182
rend_client_introduction_acked(circuit_t *circ,
174
183
const char *request, size_t request_len)
177
185
circuit_t *rendcirc;
179
187
if (circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
180
log_fn(LOG_WARN, "Received REND_INTRODUCE_ACK on unexpected circuit %d",
182
circuit_mark_for_close(circ);
188
log_warn(LD_PROTOCOL,
189
"Received REND_INTRODUCE_ACK on unexpected circuit %d.",
191
circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
186
tor_assert(circ->build_state->chosen_exit_name);
195
tor_assert(circ->build_state->chosen_exit);
196
tor_assert(circ->build_state->chosen_exit->nickname);
188
198
if (request_len == 0) {
189
199
/* It's an ACK; the introduction point relayed our introduction request. */
190
200
/* Locate the rend circ which is waiting to hear about this ack,
193
log_fn(LOG_INFO,"Received ack. Telling rend circ...");
203
log_info(LD_REND,"Received ack. Telling rend circ...");
194
204
rendcirc = circuit_get_by_rend_query_and_purpose(
195
205
circ->rend_query, CIRCUIT_PURPOSE_C_REND_READY);
196
206
if (rendcirc) { /* remember the ack */
197
207
rendcirc->purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED;
199
log_fn(LOG_INFO,"...Found no rend circ. Dropping on the floor.");
209
log_info(LD_REND,"...Found no rend circ. Dropping on the floor.");
201
211
/* close the circuit: we won't need it anymore. */
202
212
circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACKED;
203
circuit_mark_for_close(circ);
213
circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
205
215
/* It's a NAK; the introduction point didn't relay our request. */
206
216
circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING;
208
218
* points. If any remain, extend to a new one and try again.
209
219
* If none remain, refetch the service descriptor.
211
if (rend_client_remove_intro_point(circ->build_state->chosen_exit_name,
221
if (rend_client_remove_intro_point(circ->build_state->chosen_exit,
212
222
circ->rend_query) > 0) {
213
/* There are introduction points left. re-extend the circuit to
223
/* There are introduction points left. Re-extend the circuit to
214
224
* another intro point and try again. */
216
nickname = rend_client_get_random_intro(circ->rend_query);
217
tor_assert(nickname);
218
log_fn(LOG_INFO,"Got nack for %s from %s, extending to %s.",
219
safe_str(circ->rend_query),
220
circ->build_state->chosen_exit_name, nickname);
221
if (!(r = router_get_by_nickname(nickname))) {
222
log_fn(LOG_WARN, "Advertised intro point '%s' for %s is not known. Closing.",
223
nickname, safe_str(circ->rend_query));
225
circuit_mark_for_close(circ);
225
extend_info_t *extend_info;
227
extend_info = rend_client_get_random_intro(circ->rend_query);
229
log_warn(LD_REND, "No introduction points left for %s. Closing.",
230
escaped_safe_str(circ->rend_query));
231
circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
228
log_fn(LOG_INFO, "Chose new intro point %s for %s (circ %d)",
229
nickname, safe_str(circ->rend_query), circ->n_circ_id);
231
return circuit_extend_to_new_exit(circ, r);
235
"Got nack for %s from %s. Re-extending circ %d, "
237
escaped_safe_str(circ->rend_query),
238
circ->build_state->chosen_exit->nickname, circ->n_circ_id,
239
extend_info->nickname);
240
result = circuit_extend_to_new_exit(circ, extend_info);
241
extend_info_free(extend_info);
242
253
rend_client_refetch_renddesc(const char *query)
255
if (!get_options()->FetchHidServDescriptors)
244
257
if (connection_get_by_type_state_rendquery(CONN_TYPE_DIR, 0, query)) {
245
log_fn(LOG_INFO,"Would fetch a new renddesc here (for %s), but one is already in progress.", safe_str(query));
258
log_info(LD_REND,"Would fetch a new renddesc here (for %s), but one is "
259
"already in progress.", escaped_safe_str(query));
247
261
/* not one already; initiate a dir rend desc lookup */
248
262
directory_get_from_dirserver(DIR_PURPOSE_FETCH_RENDDESC, query, 1);
252
/** remove failed_intro from ent. if ent now has no intro points, or
266
/** Remove failed_intro from ent. If ent now has no intro points, or
253
267
* service is unrecognized, then launch a new renddesc fetch.
255
269
* Return -1 if error, 0 if no intro points remain or service
256
270
* unrecognized, 1 if recognized and some intro points remain.
259
rend_client_remove_intro_point(char *failed_intro, const char *query)
273
rend_client_remove_intro_point(extend_info_t *failed_intro, const char *query)
262
276
rend_cache_entry_t *ent;
263
277
connection_t *conn;
265
r = rend_cache_lookup_entry(query, &ent);
279
r = rend_cache_lookup_entry(query, -1, &ent);
267
log_fn(LOG_WARN, "Malformed service ID '%s'", safe_str(query));
281
log_warn(LD_BUG, "Bug: malformed service ID %s.", escaped_safe_str(query));
271
log_fn(LOG_INFO, "Unknown service %s. Re-fetching descriptor.",
285
log_info(LD_REND, "Unknown service %s. Re-fetching descriptor.",
286
escaped_safe_str(query));
273
287
rend_client_refetch_renddesc(query);
277
for (i=0; i < ent->parsed->n_intro_points; ++i) {
278
if (!strcasecmp(ent->parsed->intro_points[i], failed_intro)) {
279
tor_free(ent->parsed->intro_points[i]);
280
ent->parsed->intro_points[i] =
281
ent->parsed->intro_points[--ent->parsed->n_intro_points];
291
if (ent->parsed->intro_point_extend_info) {
292
for (i=0; i < ent->parsed->n_intro_points; ++i) {
293
if (!memcmp(failed_intro->identity_digest,
294
ent->parsed->intro_point_extend_info[i]->identity_digest,
296
tor_assert(!strcmp(ent->parsed->intro_points[i],
297
ent->parsed->intro_point_extend_info[i]->nickname));
298
tor_free(ent->parsed->intro_points[i]);
299
extend_info_free(ent->parsed->intro_point_extend_info[i]);
300
--ent->parsed->n_intro_points;
301
ent->parsed->intro_points[i] =
302
ent->parsed->intro_points[ent->parsed->n_intro_points];
303
ent->parsed->intro_point_extend_info[i] =
304
ent->parsed->intro_point_extend_info[ent->parsed->n_intro_points];
309
for (i=0; i < ent->parsed->n_intro_points; ++i) {
310
if (!strcasecmp(ent->parsed->intro_points[i], failed_intro->nickname)) {
311
tor_free(ent->parsed->intro_points[i]);
312
ent->parsed->intro_points[i] =
313
ent->parsed->intro_points[--ent->parsed->n_intro_points];
286
319
if (!ent->parsed->n_intro_points) {
287
log_fn(LOG_INFO,"No more intro points remain for %s. Re-fetching descriptor.",
321
"No more intro points remain for %s. Re-fetching descriptor.",
322
escaped_safe_str(query));
289
323
rend_client_refetch_renddesc(query);
291
325
/* move all pending streams back to renddesc_wait */
305
339
* the circuit to C_REND_READY.
308
rend_client_rendezvous_acked(circuit_t *circ, const char *request, size_t request_len)
342
rend_client_rendezvous_acked(circuit_t *circ, const char *request,
310
345
/* we just got an ack for our establish-rendezvous. switch purposes. */
311
346
if (circ->purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND) {
312
log_fn(LOG_WARN,"Got a rendezvous ack when we weren't expecting one. Closing circ.");
313
circuit_mark_for_close(circ);
347
log_warn(LD_PROTOCOL,"Got a rendezvous ack when we weren't expecting one. "
349
circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
316
log_fn(LOG_INFO,"Got rendezvous ack. This circuit is now ready for rendezvous.");
352
log_info(LD_REND,"Got rendezvous ack. This circuit is now ready for "
317
354
circ->purpose = CIRCUIT_PURPOSE_C_REND_READY;
321
358
/** Bob sent us a rendezvous cell; join the circuits. */
323
rend_client_receive_rendezvous(circuit_t *circ, const char *request, size_t request_len)
360
rend_client_receive_rendezvous(circuit_t *circ, const char *request,
325
363
crypt_path_t *hop;
326
364
char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN];
328
366
if ((circ->purpose != CIRCUIT_PURPOSE_C_REND_READY &&
329
367
circ->purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED)
330
368
|| !circ->build_state->pending_final_cpath) {
331
log_fn(LOG_WARN,"Got rendezvous2 cell from Bob, but not expecting it. Closing.");
332
circuit_mark_for_close(circ);
369
log_warn(LD_PROTOCOL,"Got rendezvous2 cell from hidden service, but not "
370
"expecting it. Closing.");
371
circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
336
375
if (request_len != DH_KEY_LEN+DIGEST_LEN) {
337
log_fn(LOG_WARN,"Incorrect length (%d) on RENDEZVOUS2 cell.",(int)request_len);
376
log_warn(LD_PROTOCOL,"Incorrect length (%d) on RENDEZVOUS2 cell.",
383
423
* with at least one intro point, move them to the next state;
384
424
* else fail them.
386
void rend_client_desc_here(char *query) {
427
rend_client_desc_here(const char *query)
387
429
connection_t *conn;
388
430
rend_cache_entry_t *entry;
389
431
time_t now = time(NULL);
391
while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP,
392
AP_CONN_STATE_RENDDESC_WAIT, query))) {
393
if (rend_cache_lookup_entry(conn->rend_query, &entry) == 1 &&
433
connection_t **carray;
435
get_connection_array(&carray, &n_conns);
437
for (i = 0; i < n_conns; ++i) {
439
if (conn->type != CONN_TYPE_AP ||
440
conn->state != AP_CONN_STATE_RENDDESC_WAIT ||
441
conn->marked_for_close ||
442
rend_cmp_service_ids(query, conn->rend_query))
444
assert_connection_ok(conn, now);
445
if (rend_cache_lookup_entry(conn->rend_query, -1, &entry) == 1 &&
394
446
entry->parsed->n_intro_points > 0) {
395
447
/* either this fetch worked, or it failed but there was a
396
448
* valid entry from before which we should reuse */
397
log_fn(LOG_INFO,"Rend desc is usable. Launching circuits.");
449
log_info(LD_REND,"Rend desc is usable. Launching circuits.");
398
450
conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
400
452
/* restart their timeout values, so they get a fair shake at
406
458
if (connection_ap_handshake_attach_circuit(conn) < 0) {
407
459
/* it will never work */
408
log_fn(LOG_WARN,"attaching to a rend circ failed. Closing conn.");
460
log_warn(LD_REND,"Rendezvous attempt failed. Closing.");
409
461
connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
411
tor_assert(conn->state != AP_CONN_STATE_RENDDESC_WAIT); /* avoid loop */
412
463
} else { /* 404, or fetch didn't get that far */
413
log_fn(LOG_NOTICE,"Closing stream for '%s.onion': hidden service is unavailable (try again later).", safe_str(query));
464
log_notice(LD_REND,"Closing stream for '%s.onion': hidden service is "
465
"unavailable (try again later).", safe_str(query));
414
466
connection_mark_unattached_ap(conn, END_STREAM_REASON_TIMEOUT);
419
/** strdup a nickname for a random introduction
420
* point of query. return NULL if error.
471
/** Return a newly allocated extend_info_t* for a randomly chosen introduction
472
* point for the named hidden service. Return NULL if all introduction points
473
* have been tried and failed.
422
char *rend_client_get_random_intro(char *query) {
476
rend_client_get_random_intro(const char *query)
427
479
rend_cache_entry_t *entry;
429
if (rend_cache_lookup_entry(query, &entry) < 1) {
430
log_fn(LOG_WARN,"query '%s' didn't have valid rend desc in cache. Failing.",
435
sl = smartlist_create();
437
/* add the intro point nicknames */
438
for (i=0;i<entry->parsed->n_intro_points;i++)
439
smartlist_add(sl,entry->parsed->intro_points[i]);
441
choice = smartlist_choose(sl);
446
nickname = tor_strdup(choice);
481
if (rend_cache_lookup_entry(query, -1, &entry) < 1) {
483
"Query '%s' didn't have valid rend desc in cache. Failing.",
489
if (!entry->parsed->n_intro_points)
492
i = crypto_rand_int(entry->parsed->n_intro_points);
494
if (entry->parsed->intro_point_extend_info) {
495
return extend_info_dup(entry->parsed->intro_point_extend_info[i]);
497
/* add the intro point nicknames */
498
char *choice = entry->parsed->intro_points[i];
499
routerinfo_t *router = router_get_by_nickname(choice, 0);
501
log_info(LD_REND, "Unknown router with nickname '%s'; trying another.",
504
entry->parsed->intro_points[i] =
505
entry->parsed->intro_points[--entry->parsed->n_intro_points];
508
return extend_info_from_router(router);