/** 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 have already done an introduction on this circuit but we just received a request for another one. We block it since this might be an attempt to DoS a hidden service (#15515). */ if (circ->already_received_introduce1) { log_fn(LOG_PROTOCOL_WARN, LD_REND, "Blocking multiple introductions on the same circuit. " "Someone might be trying to attack a hidden service through " "this relay."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL); return -1; } circ->already_received_introduce1 = 1; /* 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 send 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 a 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; }
/** 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 char *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 %d", circ->p_circ_id); if (circ->_base.purpose != CIRCUIT_PURPOSE_OR || circ->_base.n_conn) { log_warn(LD_PROTOCOL, "Rejecting INTRODUCE1 on non-OR or non-edge circuit %d.", 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 %d; " "responding with nack.", circ->p_circ_id); goto err; } base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1, 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(request); if (!intro_circ) { log_info(LD_REND, "No intro circ found for INTRODUCE1 cell (%s) from circuit %d; " "responding with nack.", safe_str(serviceid), circ->p_circ_id); goto err; } log_info(LD_REND, "Sending introduction request for service %s " "from circ %d to circ %d", safe_str(serviceid), circ->p_circ_id, 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, 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; }
/** 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 char *request, size_t request_len) { or_circuit_t *rend_circ; char hexid[9]; int reason = END_CIRC_REASON_INTERNAL; base16_encode(hexid,9,request,request_len<4?request_len:4); if (request_len>=4) { log_info(LD_REND, "Got request for rendezvous from circuit %d to cookie %s.", circ->p_circ_id, hexid); } if (circ->_base.purpose != CIRCUIT_PURPOSE_OR || circ->_base.n_conn) { log_info(LD_REND, "Tried to complete rendezvous on non-OR or non-edge circuit %d.", 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 %d.", (int)request_len, circ->p_circ_id); reason = END_CIRC_REASON_TORPROTOCOL; goto err; } 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, request+REND_COOKIE_LEN, request_len-REND_COOKIE_LEN, NULL)) { log_warn(LD_GENERAL, "Unable to send RENDEZVOUS2 cell to client on circuit %d.", rend_circ->p_circ_id); goto err; } /* Join the circuits. */ log_info(LD_REND, "Completing rendezvous: circuit %d joins circuit %d (cookie %s)", circ->p_circ_id, rend_circ->p_circ_id, hexid); circ->_base.purpose = CIRCUIT_PURPOSE_REND_ESTABLISHED; rend_circ->_base.purpose = CIRCUIT_PURPOSE_REND_ESTABLISHED; memset(circ->rend_token, 0, REND_COOKIE_LEN); rend_circ->rend_splice = circ; circ->rend_splice = rend_circ; return 0; err: circuit_mark_for_close(TO_CIRCUIT(circ), reason); return -1; }
/** Add <b>circ</b> to the end of ol_list and return 0, except * if ol_list is too long, in which case do nothing and return -1. */ int onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin) { onion_queue_t *tmp; time_t now = time(NULL); if (onionskin->handshake_type > MAX_ONION_HANDSHAKE_TYPE) { /* LCOV_EXCL_START * We should have rejected this far before this point */ log_warn(LD_BUG, "Handshake %d out of range! Dropping.", onionskin->handshake_type); return -1; /* LCOV_EXCL_STOP */ } tmp = tor_malloc_zero(sizeof(onion_queue_t)); tmp->circ = circ; tmp->handshake_type = onionskin->handshake_type; tmp->onionskin = onionskin; tmp->when_added = now; if (!have_room_for_onionskin(onionskin->handshake_type)) { #define WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL (60) static ratelim_t last_warned = RATELIM_INIT(WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL); char *m; if (onionskin->handshake_type == ONION_HANDSHAKE_TYPE_NTOR && (m = rate_limit_log(&last_warned, approx_time()))) { log_warn(LD_GENERAL, "Your computer is too slow to handle this many circuit " "creation requests! Please consider using the " "MaxAdvertisedBandwidth config option or choosing a more " "restricted exit policy.%s",m); tor_free(m); } tor_free(tmp); return -1; } ++ol_entries[onionskin->handshake_type]; log_info(LD_OR, "New create (%s). Queues now ntor=%d and tap=%d.", onionskin->handshake_type == ONION_HANDSHAKE_TYPE_NTOR ? "ntor" : "tap", ol_entries[ONION_HANDSHAKE_TYPE_NTOR], ol_entries[ONION_HANDSHAKE_TYPE_TAP]); circ->onionqueue_entry = tmp; TOR_TAILQ_INSERT_TAIL(&ol_list[onionskin->handshake_type], tmp, next); /* cull elderly requests. */ while (1) { onion_queue_t *head = TOR_TAILQ_FIRST(&ol_list[onionskin->handshake_type]); if (now - head->when_added < (time_t)ONIONQUEUE_WAIT_CUTOFF) break; circ = head->circ; circ->onionqueue_entry = NULL; onion_queue_entry_remove(head); log_info(LD_CIRC, "Circuit create request is too old; canceling due to overload."); if (! TO_CIRCUIT(circ)->marked_for_close) { circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT); } } return 0; }
/** Mark <b>circ</b> to be closed next time we call * circuit_close_all_marked(). Do any cleanup needed: * - If state is onionskin_pending, remove circ from the onion_pending * list. * - If circ isn't open yet: call circuit_build_failed() if we're * the origin, and in either case call circuit_rep_hist_note_result() * to note stats. * - If purpose is C_INTRODUCE_ACK_WAIT, report the intro point * failure we just had to the hidden service client module. * - If purpose is C_INTRODUCING and <b>reason</b> isn't TIMEOUT, * report to the hidden service client module that the intro point * we just tried may be unreachable. * - Send appropriate destroys and edge_destroys for conns and * streams attached to circ. * - If circ->rend_splice is set (we are the midpoint of a joined * rendezvous stream), then mark the other circuit to close as well. */ void _circuit_mark_for_close(circuit_t *circ, int reason, int line, const char *file) { int orig_reason = reason; /* Passed to the controller */ assert_circuit_ok(circ); tor_assert(line); tor_assert(file); if (circ->marked_for_close) { log(LOG_WARN,LD_BUG, "Duplicate call to circuit_mark_for_close at %s:%d" " (first at %s:%d)", file, line, circ->marked_for_close_file, circ->marked_for_close); return; } if (reason == END_CIRC_AT_ORIGIN) { if (!CIRCUIT_IS_ORIGIN(circ)) { log_warn(LD_BUG, "Specified 'at-origin' non-reason for ending circuit, " "but circuit was not at origin. (called %s:%d, purpose=%d)", file, line, circ->purpose); } reason = END_CIRC_REASON_NONE; } if (CIRCUIT_IS_ORIGIN(circ)) { /* We don't send reasons when closing circuits at the origin. */ reason = END_CIRC_REASON_NONE; } if (reason & END_CIRC_REASON_FLAG_REMOTE) reason &= ~END_CIRC_REASON_FLAG_REMOTE; if (reason < _END_CIRC_REASON_MIN || reason > _END_CIRC_REASON_MAX) { if (!(orig_reason & END_CIRC_REASON_FLAG_REMOTE)) log_warn(LD_BUG, "Reason %d out of range at %s:%d", reason, file, line); reason = END_CIRC_REASON_NONE; } if (circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) { onion_pending_remove(TO_OR_CIRCUIT(circ)); } /* If the circuit ever became OPEN, we sent it to the reputation history * module then. If it isn't OPEN, we send it there now to remember which * links worked and which didn't. */ if (circ->state != CIRCUIT_STATE_OPEN) { if (CIRCUIT_IS_ORIGIN(circ)) { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); circuit_build_failed(ocirc); /* take actions if necessary */ circuit_rep_hist_note_result(ocirc); } } if (circ->state == CIRCUIT_STATE_OR_WAIT) { if (circuits_pending_or_conns) smartlist_remove(circuits_pending_or_conns, circ); } if (CIRCUIT_IS_ORIGIN(circ)) { control_event_circuit_status(TO_ORIGIN_CIRCUIT(circ), (circ->state == CIRCUIT_STATE_OPEN)?CIRC_EVENT_CLOSED:CIRC_EVENT_FAILED, orig_reason); } if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); int timed_out = (reason == END_CIRC_REASON_TIMEOUT); tor_assert(circ->state == CIRCUIT_STATE_OPEN); tor_assert(ocirc->build_state->chosen_exit); tor_assert(ocirc->rend_data); /* treat this like getting a nack from it */ log_info(LD_REND, "Failed intro circ %s to %s (awaiting ack). %s", safe_str_client(ocirc->rend_data->onion_address), safe_str_client(build_state_get_exit_nickname(ocirc->build_state)), timed_out ? "Recording timeout." : "Removing from descriptor."); rend_client_report_intro_point_failure(ocirc->build_state->chosen_exit, ocirc->rend_data, timed_out ? INTRO_POINT_FAILURE_TIMEOUT : INTRO_POINT_FAILURE_GENERIC); } else if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCING && reason != END_CIRC_REASON_TIMEOUT) { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); if (ocirc->build_state->chosen_exit && ocirc->rend_data) { log_info(LD_REND, "Failed intro circ %s to %s " "(building circuit to intro point). " "Marking intro point as possibly unreachable.", safe_str_client(ocirc->rend_data->onion_address), safe_str_client(build_state_get_exit_nickname(ocirc->build_state))); rend_client_report_intro_point_failure(ocirc->build_state->chosen_exit, ocirc->rend_data, INTRO_POINT_FAILURE_UNREACHABLE); } } if (circ->n_conn) { circuit_clear_cell_queue(circ, circ->n_conn); connection_or_send_destroy(circ->n_circ_id, circ->n_conn, reason); } if (! CIRCUIT_IS_ORIGIN(circ)) { or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); edge_connection_t *conn; for (conn=or_circ->n_streams; conn; conn=conn->next_stream) connection_edge_destroy(or_circ->p_circ_id, conn); or_circ->n_streams = NULL; while (or_circ->resolving_streams) { conn = or_circ->resolving_streams; or_circ->resolving_streams = conn->next_stream; if (!conn->_base.marked_for_close) { /* The client will see a DESTROY, and infer that the connections * are closing because the circuit is getting torn down. No need * to send an end cell. */ conn->edge_has_sent_end = 1; conn->end_reason = END_STREAM_REASON_DESTROY; conn->end_reason |= END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED; connection_mark_for_close(TO_CONN(conn)); } conn->on_circuit = NULL; } if (or_circ->p_conn) { circuit_clear_cell_queue(circ, or_circ->p_conn); connection_or_send_destroy(or_circ->p_circ_id, or_circ->p_conn, reason); } } else { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); edge_connection_t *conn; for (conn=ocirc->p_streams; conn; conn=conn->next_stream) connection_edge_destroy(circ->n_circ_id, conn); ocirc->p_streams = NULL; } circ->marked_for_close = line; circ->marked_for_close_file = file; if (!CIRCUIT_IS_ORIGIN(circ)) { or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); if (or_circ->rend_splice) { if (!or_circ->rend_splice->_base.marked_for_close) { /* do this after marking this circuit, to avoid infinite recursion. */ circuit_mark_for_close(TO_CIRCUIT(or_circ->rend_splice), reason); } or_circ->rend_splice = NULL; } } }
static void test_rend_token_maps(void *arg) { or_circuit_t *c1, *c2, *c3, *c4; const uint8_t tok1[REND_TOKEN_LEN] = "The cat can't tell y"; const uint8_t tok2[REND_TOKEN_LEN] = "ou its name, and it "; const uint8_t tok3[REND_TOKEN_LEN] = "doesn't really care."; /* -- Adapted from a quote by Fredrik Lundh. */ (void)arg; (void)tok1; //xxxx c1 = or_circuit_new(0, NULL); c2 = or_circuit_new(0, NULL); c3 = or_circuit_new(0, NULL); c4 = or_circuit_new(0, NULL); /* Make sure we really filled up the tok* variables */ tt_int_op(tok1[REND_TOKEN_LEN-1], OP_EQ, 'y'); tt_int_op(tok2[REND_TOKEN_LEN-1], OP_EQ, ' '); tt_int_op(tok3[REND_TOKEN_LEN-1], OP_EQ, '.'); /* No maps; nothing there. */ tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok1)); tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok1)); circuit_set_rendezvous_cookie(c1, tok1); circuit_set_intro_point_digest(c2, tok2); tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok3)); tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok3)); tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok2)); tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok1)); /* Without purpose set, we don't get the circuits */ tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok1)); tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok2)); c1->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; c2->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; /* Okay, make sure they show up now. */ tt_ptr_op(c1, OP_EQ, circuit_get_rendezvous(tok1)); tt_ptr_op(c2, OP_EQ, circuit_get_intro_point(tok2)); /* Two items at the same place with the same token. */ c3->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; circuit_set_rendezvous_cookie(c3, tok2); tt_ptr_op(c2, OP_EQ, circuit_get_intro_point(tok2)); tt_ptr_op(c3, OP_EQ, circuit_get_rendezvous(tok2)); /* Marking a circuit makes it not get returned any more */ circuit_mark_for_close(TO_CIRCUIT(c1), END_CIRC_REASON_FINISHED); tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok1)); circuit_free(TO_CIRCUIT(c1)); c1 = NULL; /* Freeing a circuit makes it not get returned any more. */ circuit_free(TO_CIRCUIT(c2)); c2 = NULL; tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok2)); /* c3 -- are you still there? */ tt_ptr_op(c3, OP_EQ, circuit_get_rendezvous(tok2)); /* Change its cookie. This never happens in Tor per se, but hey. */ c3->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; circuit_set_intro_point_digest(c3, tok3); tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok2)); tt_ptr_op(c3, OP_EQ, circuit_get_intro_point(tok3)); /* Now replace c3 with c4. */ c4->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; circuit_set_intro_point_digest(c4, tok3); tt_ptr_op(c4, OP_EQ, circuit_get_intro_point(tok3)); tt_ptr_op(c3->rendinfo, OP_EQ, NULL); tt_ptr_op(c4->rendinfo, OP_NE, NULL); tt_mem_op(c4->rendinfo, OP_EQ, tok3, REND_TOKEN_LEN); /* Now clear c4's cookie. */ circuit_set_intro_point_digest(c4, NULL); tt_ptr_op(c4->rendinfo, OP_EQ, NULL); tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok3)); done: if (c1) circuit_free(TO_CIRCUIT(c1)); if (c2) circuit_free(TO_CIRCUIT(c2)); if (c3) circuit_free(TO_CIRCUIT(c3)); if (c4) circuit_free(TO_CIRCUIT(c4)); }
static void test_clist_maps(void *arg) { channel_t *ch1 = new_fake_channel(); channel_t *ch2 = new_fake_channel(); channel_t *ch3 = new_fake_channel(); or_circuit_t *or_c1=NULL, *or_c2=NULL; (void) arg; MOCK(circuitmux_attach_circuit, circuitmux_attach_mock); MOCK(circuitmux_detach_circuit, circuitmux_detach_mock); memset(&cam, 0, sizeof(cam)); memset(&cdm, 0, sizeof(cdm)); tt_assert(ch1); tt_assert(ch2); tt_assert(ch3); ch1->cmux = tor_malloc(1); ch2->cmux = tor_malloc(1); ch3->cmux = tor_malloc(1); or_c1 = or_circuit_new(100, ch2); tt_assert(or_c1); GOT_CMUX_ATTACH(ch2->cmux, or_c1, CELL_DIRECTION_IN); tt_int_op(or_c1->p_circ_id, OP_EQ, 100); tt_ptr_op(or_c1->p_chan, OP_EQ, ch2); or_c2 = or_circuit_new(100, ch1); tt_assert(or_c2); GOT_CMUX_ATTACH(ch1->cmux, or_c2, CELL_DIRECTION_IN); tt_int_op(or_c2->p_circ_id, OP_EQ, 100); tt_ptr_op(or_c2->p_chan, OP_EQ, ch1); circuit_set_n_circid_chan(TO_CIRCUIT(or_c1), 200, ch1); GOT_CMUX_ATTACH(ch1->cmux, or_c1, CELL_DIRECTION_OUT); circuit_set_n_circid_chan(TO_CIRCUIT(or_c2), 200, ch2); GOT_CMUX_ATTACH(ch2->cmux, or_c2, CELL_DIRECTION_OUT); tt_ptr_op(circuit_get_by_circid_channel(200, ch1), OP_EQ, TO_CIRCUIT(or_c1)); tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, TO_CIRCUIT(or_c2)); tt_ptr_op(circuit_get_by_circid_channel(100, ch2), OP_EQ, TO_CIRCUIT(or_c1)); /* Try the same thing again, to test the "fast" path. */ tt_ptr_op(circuit_get_by_circid_channel(100, ch2), OP_EQ, TO_CIRCUIT(or_c1)); tt_assert(circuit_id_in_use_on_channel(100, ch2)); tt_assert(! circuit_id_in_use_on_channel(101, ch2)); /* Try changing the circuitid and channel of that circuit. */ circuit_set_p_circid_chan(or_c1, 500, ch3); GOT_CMUX_DETACH(ch2->cmux, TO_CIRCUIT(or_c1)); GOT_CMUX_ATTACH(ch3->cmux, TO_CIRCUIT(or_c1), CELL_DIRECTION_IN); tt_ptr_op(circuit_get_by_circid_channel(100, ch2), OP_EQ, NULL); tt_assert(! circuit_id_in_use_on_channel(100, ch2)); tt_ptr_op(circuit_get_by_circid_channel(500, ch3), OP_EQ, TO_CIRCUIT(or_c1)); /* Now let's see about destroy handling. */ tt_assert(! circuit_id_in_use_on_channel(205, ch2)); tt_assert(circuit_id_in_use_on_channel(200, ch2)); channel_note_destroy_pending(ch2, 200); channel_note_destroy_pending(ch2, 205); channel_note_destroy_pending(ch1, 100); tt_assert(circuit_id_in_use_on_channel(205, ch2)) tt_assert(circuit_id_in_use_on_channel(200, ch2)); tt_assert(circuit_id_in_use_on_channel(100, ch1)); tt_assert(TO_CIRCUIT(or_c2)->n_delete_pending != 0); tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, TO_CIRCUIT(or_c2)); tt_ptr_op(circuit_get_by_circid_channel(100, ch1), OP_EQ, TO_CIRCUIT(or_c2)); /* Okay, now free ch2 and make sure that the circuit ID is STILL not * usable, because we haven't declared the destroy to be nonpending */ tt_int_op(cdm.ncalls, OP_EQ, 0); circuit_free(TO_CIRCUIT(or_c2)); or_c2 = NULL; /* prevent free */ tt_int_op(cdm.ncalls, OP_EQ, 2); memset(&cdm, 0, sizeof(cdm)); tt_assert(circuit_id_in_use_on_channel(200, ch2)); tt_assert(circuit_id_in_use_on_channel(100, ch1)); tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, NULL); tt_ptr_op(circuit_get_by_circid_channel(100, ch1), OP_EQ, NULL); /* Now say that the destroy is nonpending */ channel_note_destroy_not_pending(ch2, 200); tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, NULL); channel_note_destroy_not_pending(ch1, 100); tt_ptr_op(circuit_get_by_circid_channel(100, ch1), OP_EQ, NULL); tt_assert(! circuit_id_in_use_on_channel(200, ch2)); tt_assert(! circuit_id_in_use_on_channel(100, ch1)); done: if (or_c1) circuit_free(TO_CIRCUIT(or_c1)); if (or_c2) circuit_free(TO_CIRCUIT(or_c2)); if (ch1) tor_free(ch1->cmux); if (ch2) tor_free(ch2->cmux); if (ch3) tor_free(ch3->cmux); tor_free(ch1); tor_free(ch2); tor_free(ch3); UNMOCK(circuitmux_attach_circuit); UNMOCK(circuitmux_detach_circuit); }
static void test_received_introduce1_handling(void *arg) { int ret; uint8_t *request = NULL, buf[128]; trn_cell_introduce1_t *cell = NULL; or_circuit_t *circ = NULL; (void) arg; MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge); hs_circuitmap_init(); /* Too small request length. An INTRODUCE1 expect at the very least a * DIGEST_LEN size. */ { memset(buf, 0, sizeof(buf)); circ = helper_create_intro_circuit(); ret = hs_intro_received_introduce1(circ, buf, DIGEST_LEN - 1); tt_int_op(ret, OP_EQ, -1); circuit_free_(TO_CIRCUIT(circ)); } /* We have a unit test only for the suitability of a circuit to receive an * INTRODUCE1 cell so from now on we'll only test the handling of a cell. */ /* Bad request. */ { circ = helper_create_intro_circuit(); uint8_t test[2]; /* Too small request. */ memset(test, 0, sizeof(test)); ret = handle_introduce1(circ, test, sizeof(test)); tor_free(circ->p_chan); circuit_free_(TO_CIRCUIT(circ)); tt_int_op(ret, OP_EQ, -1); } /* Valid case. */ { cell = helper_create_introduce1_cell(); ssize_t request_len = trn_cell_introduce1_encoded_len(cell); tt_int_op((int)request_len, OP_GT, 0); request = tor_malloc_zero(request_len); ssize_t encoded_len = trn_cell_introduce1_encode(request, request_len, cell); tt_int_op((int)encoded_len, OP_GT, 0); circ = helper_create_intro_circuit(); or_circuit_t *service_circ = helper_create_intro_circuit(); circuit_change_purpose(TO_CIRCUIT(service_circ), CIRCUIT_PURPOSE_INTRO_POINT); /* Register the circuit in the map for the auth key of the cell. */ ed25519_public_key_t auth_key; const uint8_t *cell_auth_key = trn_cell_introduce1_getconstarray_auth_key(cell); memcpy(auth_key.pubkey, cell_auth_key, ED25519_PUBKEY_LEN); hs_circuitmap_register_intro_circ_v3_relay_side(service_circ, &auth_key); ret = hs_intro_received_introduce1(circ, request, request_len); circuit_free_(TO_CIRCUIT(circ)); circuit_free_(TO_CIRCUIT(service_circ)); tt_int_op(ret, OP_EQ, 0); } /* Valid legacy cell. */ { tor_free(request); trn_cell_introduce1_free(cell); cell = helper_create_introduce1_cell(); uint8_t *legacy_key_id = trn_cell_introduce1_getarray_legacy_key_id(cell); memset(legacy_key_id, 'a', DIGEST_LEN); /* Add an arbitrary amount of data for the payload of a v2 cell. */ size_t request_len = trn_cell_introduce1_encoded_len(cell) + 256; tt_size_op(request_len, OP_GT, 0); request = tor_malloc_zero(request_len + 256); ssize_t encoded_len = trn_cell_introduce1_encode(request, request_len, cell); tt_int_op((int)encoded_len, OP_GT, 0); circ = helper_create_intro_circuit(); or_circuit_t *service_circ = helper_create_intro_circuit(); circuit_change_purpose(TO_CIRCUIT(service_circ), CIRCUIT_PURPOSE_INTRO_POINT); /* Register the circuit in the map for the auth key of the cell. */ uint8_t token[REND_TOKEN_LEN]; memcpy(token, legacy_key_id, sizeof(token)); hs_circuitmap_register_intro_circ_v2_relay_side(service_circ, token); ret = hs_intro_received_introduce1(circ, request, request_len); circuit_free_(TO_CIRCUIT(circ)); circuit_free_(TO_CIRCUIT(service_circ)); tt_int_op(ret, OP_EQ, 0); } done: trn_cell_introduce1_free(cell); tor_free(request); hs_circuitmap_free_all(); UNMOCK(relay_send_command_from_edge_); }
/** Successfully register a v2 intro point and a v3 intro point. Ensure that HS * circuitmap is maintained properly. */ static void test_intro_point_registration(void *arg) { int retval; hs_circuitmap_ht *the_hs_circuitmap = NULL; or_circuit_t *intro_circ = NULL; trn_cell_establish_intro_t *establish_intro_cell = NULL; ed25519_public_key_t auth_key; crypto_pk_t *legacy_auth_key = NULL; or_circuit_t *legacy_intro_circ = NULL; or_circuit_t *returned_intro_circ = NULL; (void) arg; MOCK(hs_intro_send_intro_established_cell, mock_send_intro_established_cell); hs_circuitmap_init(); /* Check that the circuitmap is currently empty */ { the_hs_circuitmap = get_hs_circuitmap(); tt_assert(the_hs_circuitmap); tt_int_op(0, OP_EQ, HT_SIZE(the_hs_circuitmap)); /* Do a circuitmap query in any case */ returned_intro_circ =hs_circuitmap_get_intro_circ_v3_relay_side(&auth_key); tt_ptr_op(returned_intro_circ, OP_EQ, NULL); } /* Create a v3 intro point */ { intro_circ = or_circuit_new(0, NULL); tt_assert(intro_circ); establish_intro_cell = helper_establish_intro_v3(intro_circ); /* Check that the intro point was registered on the HS circuitmap */ the_hs_circuitmap = get_hs_circuitmap(); tt_assert(the_hs_circuitmap); tt_int_op(1, OP_EQ, HT_SIZE(the_hs_circuitmap)); get_auth_key_from_cell(&auth_key, RELAY_COMMAND_ESTABLISH_INTRO, establish_intro_cell); returned_intro_circ = hs_circuitmap_get_intro_circ_v3_relay_side(&auth_key); tt_ptr_op(intro_circ, OP_EQ, returned_intro_circ); } /* Create a v2 intro point */ { char key_digest[DIGEST_LEN]; legacy_intro_circ = or_circuit_new(1, NULL); tt_assert(legacy_intro_circ); legacy_auth_key = helper_establish_intro_v2(legacy_intro_circ); tt_assert(legacy_auth_key); /* Check that the circuitmap now has two elements */ the_hs_circuitmap = get_hs_circuitmap(); tt_assert(the_hs_circuitmap); tt_int_op(2, OP_EQ, HT_SIZE(the_hs_circuitmap)); /* Check that the new element is our legacy intro circuit. */ retval = crypto_pk_get_digest(legacy_auth_key, key_digest); tt_int_op(retval, OP_EQ, 0); returned_intro_circ = hs_circuitmap_get_intro_circ_v2_relay_side((uint8_t*)key_digest); tt_ptr_op(legacy_intro_circ, OP_EQ, returned_intro_circ); } /* XXX Continue test and try to register a second v3 intro point with the * same auth key. Make sure that old intro circuit gets closed. */ done: crypto_pk_free(legacy_auth_key); circuit_free_(TO_CIRCUIT(intro_circ)); circuit_free_(TO_CIRCUIT(legacy_intro_circ)); trn_cell_establish_intro_free(establish_intro_cell); test_circuitmap_free_all(); UNMOCK(hs_intro_send_intro_established_cell); }
/* Send a legit ESTABLISH_INTRO cell but with a wrong MAC. Should fail. */ static void test_establish_intro_wrong_mac(void *arg) { int retval; char circ_nonce[DIGEST_LEN] = {0}; ssize_t cell_len = 0; uint8_t cell_body[RELAY_PAYLOAD_SIZE]; trn_cell_establish_intro_t *cell = NULL; or_circuit_t *intro_circ = or_circuit_new(0,NULL); (void) arg; /* Get the auth key of the intro point */ crypto_rand(circ_nonce, sizeof(circ_nonce)); helper_prepare_circ_for_intro(intro_circ, circ_nonce); /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we * attempt to parse it. */ cell_len = new_establish_intro_cell(circ_nonce, &cell); tt_i64_op(cell_len, OP_GT, 0); tt_assert(cell); /* Mangle one byte of the MAC. */ uint8_t *handshake_ptr = trn_cell_establish_intro_getarray_handshake_mac(cell); handshake_ptr[TRUNNEL_SHA3_256_LEN - 1]++; /* We need to resign the payload with that change. */ { ed25519_signature_t sig; ed25519_keypair_t key_struct; /* New keypair for the signature since we don't have access to the private * key material generated earlier when creating the cell. */ retval = ed25519_keypair_generate(&key_struct, 0); tt_int_op(retval, OP_EQ, 0); uint8_t *auth_key_ptr = trn_cell_establish_intro_getarray_auth_key(cell); memcpy(auth_key_ptr, key_struct.pubkey.pubkey, ED25519_PUBKEY_LEN); /* Encode payload so we can sign it. */ cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body), cell); tt_i64_op(cell_len, OP_GT, 0); retval = ed25519_sign_prefixed(&sig, cell_body, cell_len - (ED25519_SIG_LEN + sizeof(cell->sig_len)), ESTABLISH_INTRO_SIG_PREFIX, &key_struct); tt_int_op(retval, OP_EQ, 0); /* And write the signature to the cell */ uint8_t *sig_ptr = trn_cell_establish_intro_getarray_sig(cell); memcpy(sig_ptr, sig.sig, cell->sig_len); /* Re-encode with the new signature. */ cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body), cell); tt_i64_op(cell_len, OP_GT, 0); } /* Receive the cell. Should fail because our MAC is wrong. */ setup_full_capture_of_logs(LOG_INFO); retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); expect_log_msg_containing("ESTABLISH_INTRO handshake_auth not as expected"); teardown_capture_of_logs(); tt_int_op(retval, OP_EQ, -1); done: trn_cell_establish_intro_free(cell); circuit_free_(TO_CIRCUIT(intro_circ)); }
static void test_cntev_format_cell_stats(void *arg) { char *event_string = NULL; origin_circuit_t *ocirc = NULL; or_circuit_t *or_circ = NULL; cell_stats_t *cell_stats = NULL; channel_tls_t *n_chan=NULL, *p_chan=NULL; (void)arg; n_chan = tor_malloc_zero(sizeof(channel_tls_t)); n_chan->base_.global_identifier = 1; ocirc = tor_malloc_zero(sizeof(origin_circuit_t)); ocirc->base_.magic = ORIGIN_CIRCUIT_MAGIC; ocirc->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL; ocirc->global_identifier = 2; ocirc->base_.n_circ_id = 3; ocirc->base_.n_chan = &(n_chan->base_); /* Origin circuit was completely idle. */ cell_stats = tor_malloc_zero(sizeof(cell_stats_t)); format_cell_stats(&event_string, TO_CIRCUIT(ocirc), cell_stats); tt_str_op("ID=2 OutboundQueue=3 OutboundConn=1", OP_EQ, event_string); tor_free(event_string); /* Origin circuit had 4 RELAY cells added to its exitward queue. */ cell_stats->added_cells_exitward[CELL_RELAY] = 4; format_cell_stats(&event_string, TO_CIRCUIT(ocirc), cell_stats); tt_str_op("ID=2 OutboundQueue=3 OutboundConn=1 OutboundAdded=relay:4", OP_EQ, event_string); tor_free(event_string); /* Origin circuit also had 5 CREATE2 cells added to its exitward * queue. */ cell_stats->added_cells_exitward[CELL_CREATE2] = 5; format_cell_stats(&event_string, TO_CIRCUIT(ocirc), cell_stats); tt_str_op("ID=2 OutboundQueue=3 OutboundConn=1 OutboundAdded=relay:4," "create2:5", OP_EQ, event_string); tor_free(event_string); /* Origin circuit also had 7 RELAY cells removed from its exitward queue * which together spent 6 msec in the queue. */ cell_stats->total_time_exitward[CELL_RELAY] = 6; cell_stats->removed_cells_exitward[CELL_RELAY] = 7; format_cell_stats(&event_string, TO_CIRCUIT(ocirc), cell_stats); tt_str_op("ID=2 OutboundQueue=3 OutboundConn=1 OutboundAdded=relay:4," "create2:5 OutboundRemoved=relay:7 OutboundTime=relay:6", OP_EQ, event_string); tor_free(event_string); p_chan = tor_malloc_zero(sizeof(channel_tls_t)); p_chan->base_.global_identifier = 2; or_circ = tor_malloc_zero(sizeof(or_circuit_t)); or_circ->base_.magic = OR_CIRCUIT_MAGIC; or_circ->base_.purpose = CIRCUIT_PURPOSE_OR; or_circ->p_circ_id = 8; or_circ->p_chan = &(p_chan->base_); or_circ->base_.n_circ_id = 9; or_circ->base_.n_chan = &(n_chan->base_); tor_free(cell_stats); /* OR circuit was idle. */ cell_stats = tor_malloc_zero(sizeof(cell_stats_t)); format_cell_stats(&event_string, TO_CIRCUIT(or_circ), cell_stats); tt_str_op("InboundQueue=8 InboundConn=2 OutboundQueue=9 OutboundConn=1", OP_EQ, event_string); tor_free(event_string); /* OR circuit had 3 RELAY cells added to its appward queue. */ cell_stats->added_cells_appward[CELL_RELAY] = 3; format_cell_stats(&event_string, TO_CIRCUIT(or_circ), cell_stats); tt_str_op("InboundQueue=8 InboundConn=2 InboundAdded=relay:3 " "OutboundQueue=9 OutboundConn=1", OP_EQ, event_string); tor_free(event_string); /* OR circuit had 7 RELAY cells removed from its appward queue which * together spent 6 msec in the queue. */ cell_stats->total_time_appward[CELL_RELAY] = 6; cell_stats->removed_cells_appward[CELL_RELAY] = 7; format_cell_stats(&event_string, TO_CIRCUIT(or_circ), cell_stats); tt_str_op("InboundQueue=8 InboundConn=2 InboundAdded=relay:3 " "InboundRemoved=relay:7 InboundTime=relay:6 " "OutboundQueue=9 OutboundConn=1", OP_EQ, event_string); done: tor_free(cell_stats); tor_free(event_string); tor_free(or_circ); tor_free(ocirc); tor_free(p_chan); tor_free(n_chan); }
/** Process a 'create' <b>cell</b> that just arrived from <b>chan</b>. Make a * new circuit with the p_circ_id specified in cell. Put the circuit in state * onionskin_pending, and pass the onionskin to the cpuworker. Circ will get * picked up again when the cpuworker finishes decrypting it. */ static void command_process_create_cell(cell_t *cell, channel_t *chan) { or_circuit_t *circ; const or_options_t *options = get_options(); int id_is_high; create_cell_t *create_cell; tor_assert(cell); tor_assert(chan); log_debug(LD_OR, "Got a CREATE cell for circ_id %u on channel " U64_FORMAT " (%p)", (unsigned)cell->circ_id, U64_PRINTF_ARG(chan->global_identifier), chan); if (we_are_hibernating()) { log_info(LD_OR, "Received create cell but we're shutting down. Sending back " "destroy."); channel_send_destroy(cell->circ_id, chan, END_CIRC_REASON_HIBERNATING); return; } if (!server_mode(options) || (!public_server_mode(options) && channel_is_outgoing(chan))) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received create cell (type %d) from %s, but we're connected " "to it as a client. " "Sending back a destroy.", (int)cell->command, channel_get_canonical_remote_descr(chan)); channel_send_destroy(cell->circ_id, chan, END_CIRC_REASON_TORPROTOCOL); return; } if (cell->circ_id == 0) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received a create cell (type %d) from %s with zero circID; " " ignoring.", (int)cell->command, channel_get_actual_remote_descr(chan)); return; } /* If the high bit of the circuit ID is not as expected, close the * circ. */ if (chan->wide_circ_ids) id_is_high = cell->circ_id & (1u<<31); else id_is_high = cell->circ_id & (1u<<15); if ((id_is_high && chan->circ_id_type == CIRC_ID_TYPE_HIGHER) || (!id_is_high && chan->circ_id_type == CIRC_ID_TYPE_LOWER)) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received create cell with unexpected circ_id %u. Closing.", (unsigned)cell->circ_id); channel_send_destroy(cell->circ_id, chan, END_CIRC_REASON_TORPROTOCOL); return; } if (circuit_id_in_use_on_channel(cell->circ_id, chan)) { const node_t *node = node_get_by_id(chan->identity_digest); log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received CREATE cell (circID %u) for known circ. " "Dropping (age %d).", (unsigned)cell->circ_id, (int)(time(NULL) - channel_when_created(chan))); if (node) { char *p = esc_for_log(node_get_platform(node)); log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Details: router %s, platform %s.", node_describe(node), p); tor_free(p); } return; } circ = or_circuit_new(cell->circ_id, chan); circ->base_.purpose = CIRCUIT_PURPOSE_OR; circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_ONIONSKIN_PENDING); create_cell = tor_malloc_zero(sizeof(create_cell_t)); if (create_cell_parse(create_cell, cell) < 0) { tor_free(create_cell); log_fn(LOG_PROTOCOL_WARN, LD_OR, "Bogus/unrecognized create cell; closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL); return; } if (create_cell->handshake_type != ONION_HANDSHAKE_TYPE_FAST) { /* hand it off to the cpuworkers, and then return. */ if (connection_or_digest_is_known_relay(chan->identity_digest)) rep_hist_note_circuit_handshake_requested(create_cell->handshake_type); if (assign_onionskin_to_cpuworker(NULL, circ, create_cell) < 0) { log_debug(LD_GENERAL,"Failed to hand off onionskin. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT); return; } log_debug(LD_OR,"success: handed off onionskin."); } else { /* This is a CREATE_FAST cell; we can handle it immediately without using * a CPU worker. */ uint8_t keys[CPATH_KEY_MATERIAL_LEN]; uint8_t rend_circ_nonce[DIGEST_LEN]; int len; created_cell_t created_cell; /* Make sure we never try to use the OR connection on which we * received this cell to satisfy an EXTEND request, */ channel_mark_client(chan); memset(&created_cell, 0, sizeof(created_cell)); len = onion_skin_server_handshake(ONION_HANDSHAKE_TYPE_FAST, create_cell->onionskin, create_cell->handshake_len, NULL, created_cell.reply, keys, CPATH_KEY_MATERIAL_LEN, rend_circ_nonce); tor_free(create_cell); if (len < 0) { log_warn(LD_OR,"Failed to generate key material. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); tor_free(create_cell); return; } created_cell.cell_type = CELL_CREATED_FAST; created_cell.handshake_len = len; if (onionskin_answer(circ, &created_cell, (const char *)keys, rend_circ_nonce)<0) { log_warn(LD_OR,"Failed to reply to CREATE_FAST cell. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); return; } memwipe(keys, 0, sizeof(keys)); } }