/** Send the establish-rendezvous cell along a rendezvous circuit. if * it fails, mark the circ for close and return -1. else return 0. */ static int rend_client_send_establish_rendezvous(origin_circuit_t *circ) { tor_assert(circ->base_.purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND); tor_assert(circ->rend_data); log_info(LD_REND, "Sending an ESTABLISH_RENDEZVOUS cell"); crypto_rand(circ->rend_data->rend_cookie, REND_COOKIE_LEN); /* Set timestamp_dirty, because circuit_expire_building expects it, * and the rend cookie also means we've used the circ. */ circ->base_.timestamp_dirty = time(NULL); /* We've attempted to use this circuit. Probe it if we fail */ pathbias_count_use_attempt(circ); if (relay_send_command_from_edge(0, TO_CIRCUIT(circ), RELAY_COMMAND_ESTABLISH_RENDEZVOUS, circ->rend_data->rend_cookie, REND_COOKIE_LEN, circ->cpath->prev)<0) { /* circ is already marked for close */ log_warn(LD_GENERAL, "Couldn't send ESTABLISH_RENDEZVOUS cell"); return -1; } return 0; }
/** Send the establish-rendezvous cell along a rendezvous circuit. if * it fails, mark the circ for close and return -1. else return 0. */ static int rend_client_send_establish_rendezvous(origin_circuit_t *circ) { tor_assert(circ->base_.purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND); tor_assert(circ->rend_data); log_info(LD_REND, "Sending an ESTABLISH_RENDEZVOUS cell"); if (crypto_rand(circ->rend_data->rend_cookie, REND_COOKIE_LEN) < 0) { log_warn(LD_BUG, "Internal error: Couldn't produce random cookie."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); return -1; } /* Set timestamp_dirty, because circuit_expire_building expects it, * and the rend cookie also means we've used the circ. */ circ->base_.timestamp_dirty = time(NULL); if (relay_send_command_from_edge(0, TO_CIRCUIT(circ), RELAY_COMMAND_ESTABLISH_RENDEZVOUS, circ->rend_data->rend_cookie, REND_COOKIE_LEN, circ->cpath->prev)<0) { /* circ is already marked for close */ log_warn(LD_GENERAL, "Couldn't send ESTABLISH_RENDEZVOUS cell"); return -1; } return 0; }
/** Process a 'destroy' <b>cell</b> that just arrived from * <b>conn</b>. Find the circ that it refers to (if any). * * If the circ is in state * onionskin_pending, then call onion_pending_remove() to remove it * from the pending onion list (note that if it's already being * processed by the cpuworker, it won't be in the list anymore; but * when the cpuworker returns it, the circuit will be gone, and the * cpuworker response will be dropped). * * Then mark the circuit for close (which marks all edges for close, * and passes the destroy cell onward if necessary). */ static void command_process_destroy_cell(cell_t *cell, or_connection_t *conn) { circuit_t *circ; int reason; circ = circuit_get_by_circid_orconn(cell->circ_id, conn); reason = (uint8_t)cell->payload[0]; if (!circ) { log_info(LD_OR,"unknown circuit %d on connection from %s:%d. Dropping.", cell->circ_id, conn->_base.address, conn->_base.port); return; } log_debug(LD_OR,"Received for circID %d.",cell->circ_id); if (!CIRCUIT_IS_ORIGIN(circ) && cell->circ_id == TO_OR_CIRCUIT(circ)->p_circ_id) { /* the destroy came from behind */ circuit_set_p_circid_orconn(TO_OR_CIRCUIT(circ), 0, NULL); circuit_mark_for_close(circ, reason|END_CIRC_REASON_FLAG_REMOTE); } else { /* the destroy came from ahead */ circuit_set_n_circid_orconn(circ, 0, NULL); if (CIRCUIT_IS_ORIGIN(circ)) { circuit_mark_for_close(circ, reason|END_CIRC_REASON_FLAG_REMOTE); } else { char payload[1]; log_debug(LD_OR, "Delivering 'truncated' back."); payload[0] = (char)reason; relay_send_command_from_edge(0, circ, RELAY_COMMAND_TRUNCATED, payload, sizeof(payload), NULL); } } }
/** Process an ESTABLISH_RENDEZVOUS cell by setting the circuit's purpose and * rendezvous cookie. */ int rend_mid_establish_rendezvous(or_circuit_t *circ, const uint8_t *request, size_t request_len) { char hexid[9]; int reason = END_CIRC_REASON_TORPROTOCOL; log_info(LD_REND, "Received an ESTABLISH_RENDEZVOUS request on circuit %u", (unsigned)circ->p_circ_id); if (circ->base_.purpose != CIRCUIT_PURPOSE_OR) { log_warn(LD_PROTOCOL, "Tried to establish rendezvous on non-OR circuit with purpose %s", circuit_purpose_to_string(circ->base_.purpose)); goto err; } if (circ->base_.n_chan) { log_warn(LD_PROTOCOL, "Tried to establish rendezvous on non-edge circuit"); goto err; } if (request_len != REND_COOKIE_LEN) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Invalid length on ESTABLISH_RENDEZVOUS."); goto err; } if (circuit_get_rendezvous(request)) { log_warn(LD_PROTOCOL, "Duplicate rendezvous cookie in ESTABLISH_RENDEZVOUS."); goto err; } /* Acknowledge the request. */ if (relay_send_command_from_edge(0,TO_CIRCUIT(circ), RELAY_COMMAND_RENDEZVOUS_ESTABLISHED, "", 0, NULL)<0) { log_warn(LD_PROTOCOL, "Couldn't send RENDEZVOUS_ESTABLISHED cell."); reason = END_CIRC_REASON_INTERNAL; goto err; } circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_REND_POINT_WAITING); circuit_set_rendezvous_cookie(circ, request); base16_encode(hexid,9,(char*)request,4); log_info(LD_REND, "Established rendezvous point on circuit %u for cookie %s", (unsigned)circ->p_circ_id, hexid); return 0; err: circuit_mark_for_close(TO_CIRCUIT(circ), reason); return -1; }
/** Process an ESTABLISH_RENDEZVOUS cell by setting the circuit's purpose and * rendezvous cookie. */ int rend_mid_establish_rendezvous(or_circuit_t *circ, const char *request, size_t request_len) { char hexid[9]; int reason = END_CIRC_REASON_TORPROTOCOL; log_info(LD_REND, "Received an ESTABLISH_RENDEZVOUS request on circuit %d", circ->p_circ_id); if (circ->_base.purpose != CIRCUIT_PURPOSE_OR || circ->_base.n_conn) { log_warn(LD_PROTOCOL, "Tried to establish rendezvous on non-OR or non-edge circuit."); goto err; } if (request_len != REND_COOKIE_LEN) { log_warn(LD_PROTOCOL, "Invalid length on ESTABLISH_RENDEZVOUS."); goto err; } if (circuit_get_rendezvous(request)) { log_warn(LD_PROTOCOL, "Duplicate rendezvous cookie in ESTABLISH_RENDEZVOUS."); goto err; } /* Acknowledge the request. */ if (relay_send_command_from_edge(0,TO_CIRCUIT(circ), RELAY_COMMAND_RENDEZVOUS_ESTABLISHED, "", 0, NULL)<0) { log_warn(LD_PROTOCOL, "Couldn't send RENDEZVOUS_ESTABLISHED cell."); reason = END_CIRC_REASON_INTERNAL; goto err; } circ->_base.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; memcpy(circ->rend_token, request, REND_COOKIE_LEN); base16_encode(hexid,9,request,4); log_info(LD_REND, "Established rendezvous point on circuit %d for cookie %s", circ->p_circ_id, hexid); return 0; err: circuit_mark_for_close(TO_CIRCUIT(circ), reason); return -1; }
/* Send an ESTABLISH_RENDEZVOUS cell along the rendezvous circuit circ. On * success, 0 is returned else -1 and the circuit is marked for close. */ int hs_circ_send_establish_rendezvous(origin_circuit_t *circ) { ssize_t cell_len = 0; uint8_t cell[RELAY_PAYLOAD_SIZE] = {0}; tor_assert(circ); tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND); log_info(LD_REND, "Send an ESTABLISH_RENDEZVOUS cell on circuit %u", TO_CIRCUIT(circ)->n_circ_id); /* Set timestamp_dirty, because circuit_expire_building expects it, * and the rend cookie also means we've used the circ. */ TO_CIRCUIT(circ)->timestamp_dirty = time(NULL); /* We've attempted to use this circuit. Probe it if we fail */ pathbias_count_use_attempt(circ); /* Generate the RENDEZVOUS_COOKIE and place it in the identifier so we can * complete the handshake when receiving the acknowledgement. */ crypto_rand((char *) circ->hs_ident->rendezvous_cookie, HS_REND_COOKIE_LEN); /* Generate the client keypair. No need to be extra strong, not long term */ curve25519_keypair_generate(&circ->hs_ident->rendezvous_client_kp, 0); cell_len = hs_cell_build_establish_rendezvous(circ->hs_ident->rendezvous_cookie, cell); if (BUG(cell_len < 0)) { goto err; } if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(circ), RELAY_COMMAND_ESTABLISH_RENDEZVOUS, (const char *) cell, cell_len, circ->cpath->prev) < 0) { /* Circuit has been marked for close */ log_warn(LD_REND, "Unable to send ESTABLISH_RENDEZVOUS cell on " "circuit %u", TO_CIRCUIT(circ)->n_circ_id); memwipe(cell, 0, cell_len); goto err; } memwipe(cell, 0, cell_len); return 0; err: return -1; }
/** Process a 'created' <b>cell</b> that just arrived from <b>conn</b>. * Find the circuit * that it's intended for. If we're not the origin of the circuit, package * the 'created' cell in an 'extended' relay cell and pass it back. If we * are the origin of the circuit, send it to circuit_finish_handshake() to * finish processing keys, and then call circuit_send_next_onion_skin() to * extend to the next hop in the circuit if necessary. */ static void command_process_created_cell(cell_t *cell, or_connection_t *conn) { circuit_t *circ; circ = circuit_get_by_circid_orconn(cell->circ_id, conn); if (!circ) { log_info(LD_OR, "(circID %d) unknown circ (probably got a destroy earlier). " "Dropping.", cell->circ_id); return; } if (circ->n_circ_id != cell->circ_id) { log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL, "got created cell from Tor client? Closing."); circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); return; } if (CIRCUIT_IS_ORIGIN(circ)) { /* we're the OP. Handshake this. */ origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ); int err_reason = 0; log_debug(LD_OR,"at OP. Finishing handshake."); if ((err_reason = circuit_finish_handshake(origin_circ, cell->command, cell->payload)) < 0) { log_warn(LD_OR,"circuit_finish_handshake failed."); circuit_mark_for_close(circ, -err_reason); return; } log_debug(LD_OR,"Moving to next skin."); if ((err_reason = circuit_send_next_onion_skin(origin_circ)) < 0) { log_info(LD_OR,"circuit_send_next_onion_skin failed."); /* XXX push this circuit_close lower */ circuit_mark_for_close(circ, -err_reason); return; } } else { /* pack it into an extended relay cell, and send it. */ log_debug(LD_OR, "Converting created cell to extended relay cell, sending."); relay_send_command_from_edge(0, circ, RELAY_COMMAND_EXTENDED, (char*)cell->payload, ONIONSKIN_REPLY_LEN, NULL); } }
/* For a given introduction point and an introduction circuit, send the * ESTABLISH_INTRO cell. The service object is used for logging. This can fail * and if so, the circuit is closed and the intro point object is flagged * that the circuit is not established anymore which is important for the * retry mechanism. */ static void send_establish_intro(const hs_service_t *service, hs_service_intro_point_t *ip, origin_circuit_t *circ) { ssize_t cell_len; uint8_t payload[RELAY_PAYLOAD_SIZE]; tor_assert(service); tor_assert(ip); tor_assert(circ); /* Encode establish intro cell. */ cell_len = hs_cell_build_establish_intro(circ->cpath->prev->rend_circ_nonce, ip, payload); if (cell_len < 0) { log_warn(LD_REND, "Unable to encode ESTABLISH_INTRO cell for service %s " "on circuit %u. Closing circuit.", safe_str_client(service->onion_address), TO_CIRCUIT(circ)->n_circ_id); goto err; } /* Send the cell on the circuit. */ if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(circ), RELAY_COMMAND_ESTABLISH_INTRO, (char *) payload, cell_len, circ->cpath->prev) < 0) { log_info(LD_REND, "Unable to send ESTABLISH_INTRO cell for service %s " "on circuit %u.", safe_str_client(service->onion_address), TO_CIRCUIT(circ)->n_circ_id); /* On error, the circuit has been closed. */ goto done; } /* Record the attempt to use this circuit. */ pathbias_count_use_attempt(circ); goto done; err: circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); done: memwipe(payload, 0, sizeof(payload)); }
/* Send a circuit-level SENDME on the given circuit using the layer_hint if * not NULL. The digest is only used for version 1. * * Return 0 on success else a negative value and the circuit will be closed * because we failed to send the cell on it. */ static int send_circuit_level_sendme(circuit_t *circ, crypt_path_t *layer_hint, const uint8_t *cell_digest) { uint8_t emit_version; uint8_t payload[RELAY_PAYLOAD_SIZE]; ssize_t payload_len; tor_assert(circ); tor_assert(cell_digest); emit_version = get_emit_min_version(); switch (emit_version) { case 0x01: payload_len = build_cell_payload_v1(cell_digest, payload); if (BUG(payload_len < 0)) { /* Unable to encode the cell, abort. We can recover from this by closing * the circuit but in theory it should never happen. */ return -1; } log_debug(LD_PROTOCOL, "Emitting SENDME version 1 cell."); break; case 0x00: /* Fallthrough because default is to use v0. */ default: /* Unknown version, fallback to version 0 meaning no payload. */ payload_len = 0; break; } if (relay_send_command_from_edge(0, circ, RELAY_COMMAND_SENDME, (char *) payload, payload_len, layer_hint) < 0) { log_warn(LD_CIRC, "SENDME relay_send_command_from_edge failed. Circuit's closed."); return -1; /* the circuit's closed, don't continue */ } return 0; }
/** Send the establish-rendezvous cell along a rendezvous circuit. if * it fails, mark the circ for close and return -1. else return 0. */ static int rend_client_send_establish_rendezvous(origin_circuit_t *circ) { tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND); tor_assert(circ->rend_data); log_info(LD_REND, "Sending an ESTABLISH_RENDEZVOUS cell"); if (crypto_rand(circ->rend_data->rend_cookie, REND_COOKIE_LEN) < 0) { log_warn(LD_BUG, "Internal error: Couldn't produce random cookie."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); return -1; } if (relay_send_command_from_edge(0, TO_CIRCUIT(circ), RELAY_COMMAND_ESTABLISH_RENDEZVOUS, circ->rend_data->rend_cookie, REND_COOKIE_LEN, circ->cpath->prev)<0) { /* circ is already marked for close */ log_warn(LD_GENERAL, "Couldn't send ESTABLISH_RENDEZVOUS cell"); return -1; } return 0; }
/** Process a 'created' <b>cell</b> that just arrived from <b>chan</b>. * Find the circuit * that it's intended for. If we're not the origin of the circuit, package * the 'created' cell in an 'extended' relay cell and pass it back. If we * are the origin of the circuit, send it to circuit_finish_handshake() to * finish processing keys, and then call circuit_send_next_onion_skin() to * extend to the next hop in the circuit if necessary. */ static void command_process_created_cell(cell_t *cell, channel_t *chan) { circuit_t *circ; extended_cell_t extended_cell; circ = circuit_get_by_circid_channel(cell->circ_id, chan); if (!circ) { log_info(LD_OR, "(circID %u) unknown circ (probably got a destroy earlier). " "Dropping.", (unsigned)cell->circ_id); return; } if (circ->n_circ_id != cell->circ_id) { log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL, "got created cell from Tor client? Closing."); circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); return; } if (created_cell_parse(&extended_cell.created_cell, cell) < 0) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Unparseable created cell."); circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); return; } if (CIRCUIT_IS_ORIGIN(circ)) { /* we're the OP. Handshake this. */ origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ); int err_reason = 0; log_debug(LD_OR,"at OP. Finishing handshake."); if ((err_reason = circuit_finish_handshake(origin_circ, &extended_cell.created_cell)) < 0) { log_warn(LD_OR,"circuit_finish_handshake failed."); circuit_mark_for_close(circ, -err_reason); return; } log_debug(LD_OR,"Moving to next skin."); if ((err_reason = circuit_send_next_onion_skin(origin_circ)) < 0) { log_info(LD_OR,"circuit_send_next_onion_skin failed."); /* XXX push this circuit_close lower */ circuit_mark_for_close(circ, -err_reason); return; } } else { /* pack it into an extended relay cell, and send it. */ uint8_t command=0; uint16_t len=0; uint8_t payload[RELAY_PAYLOAD_SIZE]; log_debug(LD_OR, "Converting created cell to extended relay cell, sending."); memset(payload, 0, sizeof(payload)); if (extended_cell.created_cell.cell_type == CELL_CREATED2) extended_cell.cell_type = RELAY_COMMAND_EXTENDED2; else extended_cell.cell_type = RELAY_COMMAND_EXTENDED; if (extended_cell_format(&command, &len, payload, &extended_cell) < 0) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Can't format extended cell."); circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); return; } relay_send_command_from_edge(0, circ, command, (const char*)payload, len, NULL); } }
/** Process a RENDEZVOUS1 cell by looking up the correct rendezvous * circuit by its relaying the cell's body in a RENDEZVOUS2 cell, and * connecting the two circuits. */ int rend_mid_rendezvous(or_circuit_t *circ, const uint8_t *request, size_t request_len) { or_circuit_t *rend_circ; char hexid[9]; int reason = END_CIRC_REASON_INTERNAL; if (circ->base_.purpose != CIRCUIT_PURPOSE_OR || circ->base_.n_chan) { log_info(LD_REND, "Tried to complete rendezvous on non-OR or non-edge circuit %u.", (unsigned)circ->p_circ_id); reason = END_CIRC_REASON_TORPROTOCOL; goto err; } if (request_len != REND_COOKIE_LEN+DH_KEY_LEN+DIGEST_LEN) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Rejecting RENDEZVOUS1 cell with bad length (%d) on circuit %u.", (int)request_len, (unsigned)circ->p_circ_id); reason = END_CIRC_REASON_TORPROTOCOL; goto err; } base16_encode(hexid, sizeof(hexid), (const char*)request, 4); log_info(LD_REND, "Got request for rendezvous from circuit %u to cookie %s.", (unsigned)circ->p_circ_id, hexid); rend_circ = circuit_get_rendezvous(request); if (!rend_circ) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Rejecting RENDEZVOUS1 cell with unrecognized rendezvous cookie %s.", hexid); reason = END_CIRC_REASON_TORPROTOCOL; goto err; } /* Send the RENDEZVOUS2 cell to Alice. */ if (relay_send_command_from_edge(0, TO_CIRCUIT(rend_circ), RELAY_COMMAND_RENDEZVOUS2, (char*)(request+REND_COOKIE_LEN), request_len-REND_COOKIE_LEN, NULL)) { log_warn(LD_GENERAL, "Unable to send RENDEZVOUS2 cell to client on circuit %u.", (unsigned)rend_circ->p_circ_id); goto err; } /* Join the circuits. */ log_info(LD_REND, "Completing rendezvous: circuit %u joins circuit %u (cookie %s)", (unsigned)circ->p_circ_id, (unsigned)rend_circ->p_circ_id, hexid); circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_REND_ESTABLISHED); circuit_change_purpose(TO_CIRCUIT(rend_circ), CIRCUIT_PURPOSE_REND_ESTABLISHED); circuit_set_rendezvous_cookie(circ, NULL); rend_circ->rend_splice = circ; circ->rend_splice = rend_circ; return 0; err: circuit_mark_for_close(TO_CIRCUIT(circ), reason); return -1; }
/** Respond to an ESTABLISH_INTRO cell by checking the signed data and * setting the circuit's purpose and service pk digest. */ int rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request, size_t request_len) { crypto_pk_t *pk = NULL; char buf[DIGEST_LEN+9]; char expected_digest[DIGEST_LEN]; char pk_digest[DIGEST_LEN]; size_t asn1len; or_circuit_t *c; char serviceid[REND_SERVICE_ID_LEN_BASE32+1]; int reason = END_CIRC_REASON_INTERNAL; log_info(LD_REND, "Received an ESTABLISH_INTRO request on circuit %u", (unsigned) circ->p_circ_id); if (circ->base_.purpose != CIRCUIT_PURPOSE_OR || circ->base_.n_chan) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Rejecting ESTABLISH_INTRO on non-OR or non-edge circuit."); reason = END_CIRC_REASON_TORPROTOCOL; goto err; } if (request_len < 2+DIGEST_LEN) goto truncated; /* First 2 bytes: length of asn1-encoded key. */ asn1len = ntohs(get_uint16(request)); /* Next asn1len bytes: asn1-encoded key. */ if (request_len < 2+DIGEST_LEN+asn1len) goto truncated; pk = crypto_pk_asn1_decode((char*)(request+2), asn1len); if (!pk) { reason = END_CIRC_REASON_TORPROTOCOL; log_warn(LD_PROTOCOL, "Couldn't decode public key."); goto err; } /* Next 20 bytes: Hash of rend_circ_nonce | "INTRODUCE" */ memcpy(buf, circ->rend_circ_nonce, DIGEST_LEN); memcpy(buf+DIGEST_LEN, "INTRODUCE", 9); if (crypto_digest(expected_digest, buf, DIGEST_LEN+9) < 0) { log_warn(LD_BUG, "Internal error computing digest."); goto err; } if (tor_memneq(expected_digest, request+2+asn1len, DIGEST_LEN)) { log_warn(LD_PROTOCOL, "Hash of session info was not as expected."); reason = END_CIRC_REASON_TORPROTOCOL; goto err; } /* Rest of body: signature of previous data */ note_crypto_pk_op(REND_MID); if (crypto_pk_public_checksig_digest(pk, (char*)request, 2+asn1len+DIGEST_LEN, (char*)(request+2+DIGEST_LEN+asn1len), request_len-(2+DIGEST_LEN+asn1len))<0) { log_warn(LD_PROTOCOL, "Incorrect signature on ESTABLISH_INTRO cell; rejecting."); reason = END_CIRC_REASON_TORPROTOCOL; goto err; } /* The request is valid. First, compute the hash of Bob's PK.*/ if (crypto_pk_get_digest(pk, pk_digest)<0) { log_warn(LD_BUG, "Internal error: couldn't hash public key."); goto err; } crypto_pk_free(pk); /* don't need it anymore */ pk = NULL; /* so we don't free it again if err */ base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1, pk_digest, REND_SERVICE_ID_LEN); /* Close any other intro circuits with the same pk. */ c = NULL; while ((c = circuit_get_intro_point((const uint8_t *)pk_digest))) { log_info(LD_REND, "Replacing old circuit for service %s", safe_str(serviceid)); circuit_mark_for_close(TO_CIRCUIT(c), END_CIRC_REASON_FINISHED); /* Now it's marked, and it won't be returned next time. */ } /* Acknowledge the request. */ if (relay_send_command_from_edge(0, TO_CIRCUIT(circ), RELAY_COMMAND_INTRO_ESTABLISHED, "", 0, NULL)<0) { log_info(LD_GENERAL, "Couldn't send INTRO_ESTABLISHED cell."); goto err; } /* Now, set up this circuit. */ circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT); circuit_set_intro_point_digest(circ, (uint8_t *)pk_digest); log_info(LD_REND, "Established introduction point on circuit %u for service %s", (unsigned) circ->p_circ_id, safe_str(serviceid)); return 0; truncated: log_warn(LD_PROTOCOL, "Rejecting truncated ESTABLISH_INTRO cell."); reason = END_CIRC_REASON_TORPROTOCOL; err: if (pk) crypto_pk_free(pk); circuit_mark_for_close(TO_CIRCUIT(circ), reason); return -1; }
/** Process an INTRODUCE1 cell by finding the corresponding introduction * circuit, and relaying the body of the INTRODUCE1 cell inside an * INTRODUCE2 cell. */ int rend_mid_introduce(or_circuit_t *circ, const uint8_t *request, size_t request_len) { or_circuit_t *intro_circ; char serviceid[REND_SERVICE_ID_LEN_BASE32+1]; char nak_body[1]; log_info(LD_REND, "Received an INTRODUCE1 request on circuit %u", (unsigned)circ->p_circ_id); if (circ->base_.purpose != CIRCUIT_PURPOSE_OR || circ->base_.n_chan) { log_warn(LD_PROTOCOL, "Rejecting INTRODUCE1 on non-OR or non-edge circuit %u.", (unsigned)circ->p_circ_id); goto err; } /* We could change this to MAX_HEX_NICKNAME_LEN now that 0.0.9.x is * obsolete; however, there isn't much reason to do so, and we're going * to revise this protocol anyway. */ if (request_len < (DIGEST_LEN+(MAX_NICKNAME_LEN+1)+REND_COOKIE_LEN+ DH_KEY_LEN+CIPHER_KEY_LEN+PKCS1_OAEP_PADDING_OVERHEAD)) { log_warn(LD_PROTOCOL, "Impossibly short INTRODUCE1 cell on circuit %u; " "responding with nack.", (unsigned)circ->p_circ_id); goto err; } base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1, (char*)request, REND_SERVICE_ID_LEN); /* The first 20 bytes are all we look at: they have a hash of Bob's PK. */ intro_circ = circuit_get_intro_point((const uint8_t*)request); if (!intro_circ) { log_info(LD_REND, "No intro circ found for INTRODUCE1 cell (%s) from circuit %u; " "responding with nack.", safe_str(serviceid), (unsigned)circ->p_circ_id); goto err; } log_info(LD_REND, "Sending introduction request for service %s " "from circ %u to circ %u", safe_str(serviceid), (unsigned)circ->p_circ_id, (unsigned)intro_circ->p_circ_id); /* Great. Now we just relay the cell down the circuit. */ if (relay_send_command_from_edge(0, TO_CIRCUIT(intro_circ), RELAY_COMMAND_INTRODUCE2, (char*)request, request_len, NULL)) { log_warn(LD_GENERAL, "Unable to send INTRODUCE2 cell to Tor client."); goto err; } /* And sent an ack down Alice's circuit. Empty body means succeeded. */ if (relay_send_command_from_edge(0,TO_CIRCUIT(circ), RELAY_COMMAND_INTRODUCE_ACK, NULL,0,NULL)) { log_warn(LD_GENERAL, "Unable to send INTRODUCE_ACK cell to Tor client."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); return -1; } return 0; err: /* Send the client an NACK */ nak_body[0] = 1; if (relay_send_command_from_edge(0,TO_CIRCUIT(circ), RELAY_COMMAND_INTRODUCE_ACK, nak_body, 1, NULL)) { log_warn(LD_GENERAL, "Unable to send NAK to Tor client."); /* Is this right? */ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); } return -1; }
/* Build the cell payload. */ cell = trn_cell_intro_established_new(); ext = trn_cell_extension_new(); trn_cell_extension_set_num(ext, 0); trn_cell_intro_established_set_extensions(cell, ext); /* Encode the cell to binary format. */ encoded_len = trn_cell_intro_established_encoded_len(cell); tor_assert(encoded_len > 0); encoded_cell = tor_malloc_zero(encoded_len); result_len = trn_cell_intro_established_encode(encoded_cell, encoded_len, cell); tor_assert(encoded_len == result_len); ret = relay_send_command_from_edge(0, TO_CIRCUIT(circ), RELAY_COMMAND_INTRO_ESTABLISHED, (char *) encoded_cell, encoded_len, NULL); /* On failure, the above function will close the circuit. */ trn_cell_intro_established_free(cell); tor_free(encoded_cell); return ret; } /** We received an ESTABLISH_INTRO <b>parsed_cell</b> on <b>circ</b>. It's * well-formed and passed our verifications. Perform appropriate actions to * establish an intro point. */ static int handle_verified_establish_intro_cell(or_circuit_t *circ, const trn_cell_establish_intro_t *parsed_cell) { /* Get the auth key of this intro point */
/* Called when a service rendezvous point circuit is done building. Given the * service and the circuit, this function will send a RENDEZVOUS1 cell on the * circuit using the information in the circuit identifier. If the cell can't * be sent, the circuit is closed. */ void hs_circ_service_rp_has_opened(const hs_service_t *service, origin_circuit_t *circ) { size_t payload_len; uint8_t payload[RELAY_PAYLOAD_SIZE] = {0}; tor_assert(service); tor_assert(circ); tor_assert(circ->hs_ident); /* Some useful logging. */ log_info(LD_REND, "Rendezvous circuit %u has opened with cookie %s " "for service %s", TO_CIRCUIT(circ)->n_circ_id, hex_str((const char *) circ->hs_ident->rendezvous_cookie, REND_COOKIE_LEN), safe_str_client(service->onion_address)); circuit_log_path(LOG_INFO, LD_REND, circ); /* This can't fail. */ payload_len = hs_cell_build_rendezvous1( circ->hs_ident->rendezvous_cookie, sizeof(circ->hs_ident->rendezvous_cookie), circ->hs_ident->rendezvous_handshake_info, sizeof(circ->hs_ident->rendezvous_handshake_info), payload); /* Pad the payload with random bytes so it matches the size of a legacy cell * which is normally always bigger. Also, the size of a legacy cell is * always smaller than the RELAY_PAYLOAD_SIZE so this is safe. */ if (payload_len < HS_LEGACY_RENDEZVOUS_CELL_SIZE) { crypto_rand((char *) payload + payload_len, HS_LEGACY_RENDEZVOUS_CELL_SIZE - payload_len); payload_len = HS_LEGACY_RENDEZVOUS_CELL_SIZE; } if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(circ), RELAY_COMMAND_RENDEZVOUS1, (const char *) payload, payload_len, circ->cpath->prev) < 0) { /* On error, circuit is closed. */ log_warn(LD_REND, "Unable to send RENDEZVOUS1 cell on circuit %u " "for service %s", TO_CIRCUIT(circ)->n_circ_id, safe_str_client(service->onion_address)); goto done; } /* Setup end-to-end rendezvous circuit between the client and us. */ if (hs_circuit_setup_e2e_rend_circ(circ, circ->hs_ident->rendezvous_ntor_key_seed, sizeof(circ->hs_ident->rendezvous_ntor_key_seed), 1) < 0) { log_warn(LD_GENERAL, "Failed to setup circ"); goto done; } done: memwipe(payload, 0, sizeof(payload)); }
/* Given the introduction circuit intro_circ, the rendezvous circuit * rend_circ, a descriptor intro point object ip and the service's * subcredential, send an INTRODUCE1 cell on intro_circ. * * This will also setup the circuit identifier on rend_circ containing the key * material for the handshake and e2e encryption. Return 0 on success else * negative value. Because relay_send_command_from_edge() closes the circuit * on error, it is possible that intro_circ is closed on error. */ int hs_circ_send_introduce1(origin_circuit_t *intro_circ, origin_circuit_t *rend_circ, const hs_desc_intro_point_t *ip, const uint8_t *subcredential) { int ret = -1; ssize_t payload_len; uint8_t payload[RELAY_PAYLOAD_SIZE] = {0}; hs_cell_introduce1_data_t intro1_data; tor_assert(intro_circ); tor_assert(rend_circ); tor_assert(ip); tor_assert(subcredential); /* It is undefined behavior in hs_cell_introduce1_data_clear() if intro1_data * has been declared on the stack but not initialized. Here, we set it to 0. */ memset(&intro1_data, 0, sizeof(hs_cell_introduce1_data_t)); /* This takes various objects in order to populate the introduce1 data * object which is used to build the content of the cell. */ const node_t *exit_node = build_state_get_exit_node(rend_circ->build_state); if (exit_node == NULL) { log_info(LD_REND, "Unable to get rendezvous point for circuit %u. " "Failing.", TO_CIRCUIT(intro_circ)->n_circ_id); goto done; } setup_introduce1_data(ip, exit_node, subcredential, &intro1_data); /* If we didn't get any link specifiers, it's because our node was * bad. */ if (BUG(!intro1_data.link_specifiers) || !smartlist_len(intro1_data.link_specifiers)) { log_warn(LD_REND, "Unable to get link specifiers for INTRODUCE1 cell on " "circuit %u.", TO_CIRCUIT(intro_circ)->n_circ_id); goto done; } /* Final step before we encode a cell, we setup the circuit identifier which * will generate both the rendezvous cookie and client keypair for this * connection. Those are put in the ident. */ intro1_data.rendezvous_cookie = rend_circ->hs_ident->rendezvous_cookie; intro1_data.client_kp = &rend_circ->hs_ident->rendezvous_client_kp; memcpy(intro_circ->hs_ident->rendezvous_cookie, rend_circ->hs_ident->rendezvous_cookie, sizeof(intro_circ->hs_ident->rendezvous_cookie)); /* From the introduce1 data object, this will encode the INTRODUCE1 cell * into payload which is then ready to be sent as is. */ payload_len = hs_cell_build_introduce1(&intro1_data, payload); if (BUG(payload_len < 0)) { goto done; } if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(intro_circ), RELAY_COMMAND_INTRODUCE1, (const char *) payload, payload_len, intro_circ->cpath->prev) < 0) { /* On error, circuit is closed. */ log_warn(LD_REND, "Unable to send INTRODUCE1 cell on circuit %u.", TO_CIRCUIT(intro_circ)->n_circ_id); goto done; } /* Success. */ ret = 0; goto done; done: hs_cell_introduce1_data_clear(&intro1_data); memwipe(payload, 0, sizeof(payload)); return ret; }