1 /* Copyright 2004 Roger Dingledine, Nick Mathewson. */
2 /* See LICENSE for licensing information */
4 const char rendclient_c_id
[] = "$Id$";
8 * \brief Client code to access location-hidden services.
13 /** Called when we've established a circuit to an introduction point:
14 * send the introduction request. */
16 rend_client_introcirc_has_opened(circuit_t
*circ
)
18 tor_assert(circ
->purpose
== CIRCUIT_PURPOSE_C_INTRODUCING
);
19 tor_assert(CIRCUIT_IS_ORIGIN(circ
));
20 tor_assert(circ
->cpath
);
22 log_fn(LOG_INFO
,"introcirc is open");
23 connection_ap_attach_pending();
26 /** Send the establish-rendezvous cell along a rendezvous circuit. if
27 * it fails, mark the circ for close and return -1. else return 0.
30 rend_client_send_establish_rendezvous(circuit_t
*circ
)
32 tor_assert(circ
->purpose
== CIRCUIT_PURPOSE_C_ESTABLISH_REND
);
33 log_fn(LOG_INFO
, "Sending an ESTABLISH_RENDEZVOUS cell");
35 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
);
40 if (connection_edge_send_command(NULL
,circ
,
41 RELAY_COMMAND_ESTABLISH_RENDEZVOUS
,
42 circ
->rend_cookie
, REND_COOKIE_LEN
,
43 circ
->cpath
->prev
)<0) {
44 /* circ is already marked for close */
45 log_fn(LOG_WARN
, "Couldn't send ESTABLISH_RENDEZVOUS cell");
52 /** Called when we're trying to connect an ap conn; sends an INTRODUCE1 cell
53 * down introcirc if possible.
56 rend_client_send_introduction(circuit_t
*introcirc
, circuit_t
*rendcirc
) {
59 char payload
[RELAY_PAYLOAD_SIZE
];
60 char tmp
[1+(MAX_HEX_NICKNAME_LEN
+1)+REND_COOKIE_LEN
+DH_KEY_LEN
];
61 rend_cache_entry_t
*entry
;
64 tor_assert(introcirc
->purpose
== CIRCUIT_PURPOSE_C_INTRODUCING
);
65 tor_assert(rendcirc
->purpose
== CIRCUIT_PURPOSE_C_REND_READY
);
66 tor_assert(!rend_cmp_service_ids(introcirc
->rend_query
, 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 introcirc
->rend_query
);
74 /* first 20 bytes of payload are the hash of bob's pk */
75 if (crypto_pk_get_digest(entry
->parsed
->pk
, payload
)<0) {
76 log_fn(LOG_WARN
, "Couldn't hash public key.");
80 /* Initialize the pending_final_cpath and start the DH handshake. */
81 cpath
= rendcirc
->build_state
->pending_final_cpath
;
83 cpath
= rendcirc
->build_state
->pending_final_cpath
=
84 tor_malloc_zero(sizeof(crypt_path_t
));
85 if (!(cpath
->handshake_state
= crypto_dh_new())) {
86 log_fn(LOG_WARN
, "Couldn't allocate DH");
89 if (crypto_dh_generate_public(cpath
->handshake_state
)<0) {
90 log_fn(LOG_WARN
, "Couldn't generate g^x");
95 /* write the remaining items into tmp */
97 tmp
[0] = 1; /* version 1 of the cell format */
99 strncpy(tmp
+1, rendcirc
->build_state
->chosen_exit_name
, (MAX_HEX_NICKNAME_LEN
+1));
100 memcpy(tmp
+1+MAX_HEX_NICKNAME_LEN
+1, rendcirc
->rend_cookie
, REND_COOKIE_LEN
);
102 strncpy(tmp
, rendcirc
->build_state
->chosen_exit_name
, (MAX_NICKNAME_LEN
+1)); /* nul pads */
103 memcpy(tmp
+MAX_NICKNAME_LEN
+1, rendcirc
->rend_cookie
, REND_COOKIE_LEN
);
105 if (crypto_dh_get_public(cpath
->handshake_state
,
107 tmp
+1+MAX_HEX_NICKNAME_LEN
+1+REND_COOKIE_LEN
,
109 tmp
+MAX_NICKNAME_LEN
+1+REND_COOKIE_LEN
,
113 log_fn(LOG_WARN
, "Couldn't extract g^x");
117 /*XXX maybe give crypto_pk_public_hybrid_encrypt a max_len arg,
118 * to avoid buffer overflows? */
119 r
= crypto_pk_public_hybrid_encrypt(entry
->parsed
->pk
, payload
+DIGEST_LEN
, tmp
,
121 1+MAX_HEX_NICKNAME_LEN
+1+REND_COOKIE_LEN
+DH_KEY_LEN
,
123 MAX_NICKNAME_LEN
+1+REND_COOKIE_LEN
+DH_KEY_LEN
,
125 PK_PKCS1_OAEP_PADDING
, 0);
127 log_fn(LOG_WARN
,"hybrid pk encrypt failed.");
131 tor_assert(DIGEST_LEN
+ r
<= RELAY_PAYLOAD_SIZE
); /* we overran something */
132 payload_len
= DIGEST_LEN
+ r
;
134 if (connection_edge_send_command(NULL
, introcirc
,
135 RELAY_COMMAND_INTRODUCE1
,
136 payload
, payload_len
,
137 introcirc
->cpath
->prev
)<0) {
138 /* introcirc is already marked for close. leave rendcirc alone. */
139 log_fn(LOG_WARN
, "Couldn't send INTRODUCE1 cell");
143 /* Now, we wait for an ACK or NAK on this circuit. */
144 introcirc
->purpose
= CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT
;
148 circuit_mark_for_close(introcirc
);
149 circuit_mark_for_close(rendcirc
);
153 /** Called when a rendezvous circuit is open; sends a establish
154 * rendezvous circuit as appropriate. */
156 rend_client_rendcirc_has_opened(circuit_t
*circ
)
158 tor_assert(circ
->purpose
== CIRCUIT_PURPOSE_C_ESTABLISH_REND
);
159 tor_assert(CIRCUIT_IS_ORIGIN(circ
));
161 log_fn(LOG_INFO
,"rendcirc is open");
163 /* generate a rendezvous cookie, store it in circ */
164 if (rend_client_send_establish_rendezvous(circ
) < 0) {
169 /** Called when get an ACK or a NAK for a REND_INTRODUCE1 cell.
172 rend_client_introduction_acked(circuit_t
*circ
,
173 const char *request
, size_t request_len
)
178 if (circ
->purpose
!= CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT
) {
179 log_fn(LOG_WARN
, "Received REND_INTRODUCE_ACK on unexpected circuit %d",
181 circuit_mark_for_close(circ
);
185 tor_assert(circ
->build_state
->chosen_exit_name
);
187 if (request_len
== 0) {
188 /* It's an ACK; the introduction point relayed our introduction request. */
189 /* Locate the rend circ which is waiting to hear about this ack,
192 log_fn(LOG_INFO
,"Received ack. Telling rend circ...");
193 rendcirc
= circuit_get_by_rend_query_and_purpose(
194 circ
->rend_query
, CIRCUIT_PURPOSE_C_REND_READY
);
195 if (rendcirc
) { /* remember the ack */
196 rendcirc
->purpose
= CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED
;
198 log_fn(LOG_INFO
,"...Found no rend circ. Dropping on the floor.");
200 /* close the circuit: we won't need it anymore. */
201 circ
->purpose
= CIRCUIT_PURPOSE_C_INTRODUCE_ACKED
;
202 circuit_mark_for_close(circ
);
204 /* It's a NAK; the introduction point didn't relay our request. */
205 circ
->purpose
= CIRCUIT_PURPOSE_C_INTRODUCING
;
206 /* Remove this intro point from the set of viable introduction
207 * points. If any remain, extend to a new one and try again.
208 * If none remain, refetch the service descriptor.
210 if (rend_client_remove_intro_point(circ
->build_state
->chosen_exit_name
,
211 circ
->rend_query
) > 0) {
212 /* There are introduction points left. re-extend the circuit to
213 * another intro point and try again. */
215 nickname
= rend_client_get_random_intro(circ
->rend_query
);
216 tor_assert(nickname
);
217 log_fn(LOG_INFO
,"Got nack for %s from %s, extending to %s.",
218 circ
->rend_query
, circ
->build_state
->chosen_exit_name
, nickname
);
219 if (!(r
= router_get_by_nickname(nickname
))) {
220 log_fn(LOG_WARN
, "Advertised intro point '%s' for %s is not known. Closing.",
221 nickname
, circ
->rend_query
);
223 circuit_mark_for_close(circ
);
226 log_fn(LOG_INFO
, "Chose new intro point %s for %s (circ %d)",
227 nickname
, circ
->rend_query
, circ
->n_circ_id
);
228 if (circuit_append_new_hop(circ
, nickname
, r
->identity_digest
) < 0)
235 /** If we are not currently fetching a rendezvous service descriptor
236 * for the service ID <b>query</b>, start a directory connection to fetch a
240 rend_client_refetch_renddesc(const char *query
)
242 if (connection_get_by_type_state_rendquery(CONN_TYPE_DIR
, 0, query
)) {
243 log_fn(LOG_INFO
,"Would fetch a new renddesc here (for %s), but one is already in progress.", query
);
245 /* not one already; initiate a dir rend desc lookup */
246 directory_get_from_dirserver(DIR_PURPOSE_FETCH_RENDDESC
, query
, 1);
250 /** remove failed_intro from ent. if ent now has no intro points, or
251 * service is unrecognized, then launch a new renddesc fetch.
253 * Return -1 if error, 0 if no intro points remain or service
254 * unrecognized, 1 if recognized and some intro points remain.
257 rend_client_remove_intro_point(char *failed_intro
, const char *query
)
260 rend_cache_entry_t
*ent
;
263 r
= rend_cache_lookup_entry(query
, &ent
);
265 log_fn(LOG_WARN
, "Malformed service ID '%s'", query
);
269 log_fn(LOG_INFO
, "Unknown service %s. Re-fetching descriptor.", query
);
270 rend_client_refetch_renddesc(query
);
274 for (i
=0; i
< ent
->parsed
->n_intro_points
; ++i
) {
275 if (!strcasecmp(ent
->parsed
->intro_points
[i
], failed_intro
)) {
276 tor_free(ent
->parsed
->intro_points
[i
]);
277 ent
->parsed
->intro_points
[i
] =
278 ent
->parsed
->intro_points
[--ent
->parsed
->n_intro_points
];
283 if (!ent
->parsed
->n_intro_points
) {
284 log_fn(LOG_INFO
,"No more intro points remain for %s. Re-fetching descriptor.", query
);
285 rend_client_refetch_renddesc(query
);
287 /* move all pending streams back to renddesc_wait */
288 while ((conn
= connection_get_by_type_state_rendquery(CONN_TYPE_AP
,
289 AP_CONN_STATE_CIRCUIT_WAIT
, query
))) {
290 conn
->state
= AP_CONN_STATE_RENDDESC_WAIT
;
295 log_fn(LOG_INFO
,"%d options left for %s.", ent
->parsed
->n_intro_points
, query
);
299 /** Called when we receive a RENDEZVOUS_ESTABLISHED cell; changes the state of
300 * the circuit to C_REND_READY.
303 rend_client_rendezvous_acked(circuit_t
*circ
, const char *request
, size_t request_len
)
305 /* we just got an ack for our establish-rendezvous. switch purposes. */
306 if (circ
->purpose
!= CIRCUIT_PURPOSE_C_ESTABLISH_REND
) {
307 log_fn(LOG_WARN
,"Got a rendezvous ack when we weren't expecting one. Closing circ.");
308 circuit_mark_for_close(circ
);
311 log_fn(LOG_INFO
,"Got rendezvous ack. This circuit is now ready for rendezvous.");
312 circ
->purpose
= CIRCUIT_PURPOSE_C_REND_READY
;
316 /** Bob sent us a rendezvous cell; join the circuits. */
318 rend_client_receive_rendezvous(circuit_t
*circ
, const char *request
, size_t request_len
)
321 char keys
[DIGEST_LEN
+CPATH_KEY_MATERIAL_LEN
];
323 if ((circ
->purpose
!= CIRCUIT_PURPOSE_C_REND_READY
&&
324 circ
->purpose
!= CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED
)
325 || !circ
->build_state
->pending_final_cpath
) {
326 log_fn(LOG_WARN
,"Got rendezvous2 cell from Bob, but not expecting it. Closing.");
327 circuit_mark_for_close(circ
);
331 if (request_len
!= DH_KEY_LEN
+DIGEST_LEN
) {
332 log_fn(LOG_WARN
,"Incorrect length (%d) on RENDEZVOUS2 cell.",(int)request_len
);
336 /* first DH_KEY_LEN bytes are g^y from bob. Finish the dh handshake...*/
337 tor_assert(circ
->build_state
);
338 tor_assert(circ
->build_state
->pending_final_cpath
);
339 hop
= circ
->build_state
->pending_final_cpath
;
340 tor_assert(hop
->handshake_state
);
341 if (crypto_dh_compute_secret(hop
->handshake_state
, request
, DH_KEY_LEN
,
342 keys
, DIGEST_LEN
+CPATH_KEY_MATERIAL_LEN
)<0) {
343 log_fn(LOG_WARN
, "Couldn't complete DH handshake");
346 /* ... and set up cpath. */
347 if (circuit_init_cpath_crypto(hop
, keys
+DIGEST_LEN
, 0)<0)
350 /* Check whether the digest is right... */
351 if (memcmp(keys
, request
+DH_KEY_LEN
, DIGEST_LEN
)) {
352 log_fn(LOG_WARN
, "Incorrect digest of key material");
356 crypto_dh_free(hop
->handshake_state
);
357 hop
->handshake_state
= NULL
;
359 /* All is well. Extend the circuit. */
360 circ
->purpose
= CIRCUIT_PURPOSE_C_REND_JOINED
;
361 hop
->state
= CPATH_STATE_OPEN
;
362 /* set the windows to default. these are the windows
363 * that alice thinks bob has.
365 hop
->package_window
= CIRCWINDOW_START
;
366 hop
->deliver_window
= CIRCWINDOW_START
;
368 onion_append_to_cpath(&circ
->cpath
, hop
);
369 circ
->build_state
->pending_final_cpath
= NULL
; /* prevent double-free */
372 circuit_mark_for_close(circ
);
376 /** Find all the apconns in state AP_CONN_STATE_RENDDESC_WAIT that
377 * are waiting on query. If there's a working cache entry here
378 * with at least one intro point, move them to the next state;
381 void rend_client_desc_here(char *query
) {
383 rend_cache_entry_t
*entry
;
385 while ((conn
= connection_get_by_type_state_rendquery(CONN_TYPE_AP
,
386 AP_CONN_STATE_RENDDESC_WAIT
, query
))) {
387 if (rend_cache_lookup_entry(conn
->rend_query
, &entry
) == 1 &&
388 entry
->parsed
->n_intro_points
> 0) {
389 /* either this fetch worked, or it failed but there was a
390 * valid entry from before which we should reuse */
391 log_fn(LOG_INFO
,"Rend desc is usable. Launching circuits.");
392 conn
->state
= AP_CONN_STATE_CIRCUIT_WAIT
;
393 if (connection_ap_handshake_attach_circuit(conn
) < 0) {
394 /* it will never work */
395 log_fn(LOG_WARN
,"attaching to a rend circ failed. Closing conn.");
396 conn
->has_sent_end
= 1;
397 connection_mark_for_close(conn
);
399 tor_assert(conn
->state
!= AP_CONN_STATE_RENDDESC_WAIT
); /* avoid loop */
400 } else { /* 404, or fetch didn't get that far */
401 log_fn(LOG_NOTICE
,"Closing stream for '%s.onion': hidden service is unavailable (try again later).", query
);
402 conn
->has_sent_end
= 1;
403 connection_mark_for_close(conn
);
408 /** strdup a nickname for a random introduction
409 * point of query. return NULL if error.
411 char *rend_client_get_random_intro(char *query
) {
416 rend_cache_entry_t
*entry
;
418 if (rend_cache_lookup_entry(query
, &entry
) < 1) {
419 log_fn(LOG_WARN
,"query '%s' didn't have valid rend desc in cache. Failing.", query
);
423 sl
= smartlist_create();
425 /* add the intro point nicknames */
426 for (i
=0;i
<entry
->parsed
->n_intro_points
;i
++)
427 smartlist_add(sl
,entry
->parsed
->intro_points
[i
]);
429 choice
= smartlist_choose(sl
);
434 nickname
= tor_strdup(choice
);