static void NS(test_main)(void *arg) { int retval; int made_pending; edge_connection_t *exitconn = create_valid_exitconn(); or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t)); (void)arg; NS_MOCK(router_my_exit_policy_is_reject_star); TO_CONN(exitconn)->address = tor_strdup("invalid#@!.org"); retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending, NULL); tt_int_op(retval,OP_EQ,-1); done: NS_UNMOCK(router_my_exit_policy_is_reject_star); tor_free(TO_CONN(exitconn)->address); tor_free(exitconn); tor_free(on_circ); return; }
static void NS(test_main)(void *arg) { int retval; int made_pending; const tor_addr_t *resolved_addr; tor_addr_t addr_to_compare; (void)arg; tor_addr_parse(&addr_to_compare, "8.8.8.8"); or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t)); edge_connection_t *exitconn = create_valid_exitconn(); TO_CONN(exitconn)->address = tor_strdup("8.8.8.8"); retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending, NULL); resolved_addr = &(exitconn->base_.addr); tt_int_op(retval,OP_EQ,1); tt_assert(tor_addr_eq(resolved_addr, (const tor_addr_t *)&addr_to_compare)); tt_int_op(exitconn->address_ttl,OP_EQ,DEFAULT_DNS_TTL); done: tor_free(on_circ); tor_free(TO_CONN(exitconn)->address); tor_free(exitconn); return; }
/* Helper: Do a successful Extended ORPort authentication handshake. */ static void do_ext_or_handshake(or_connection_t *conn) { char b[256]; tt_int_op(0, OP_EQ, connection_ext_or_start_auth(conn)); CONTAINS("\x01\x00", 2); WRITE("\x01", 1); WRITE("But when I look ahead up the whi", 32); MOCK(crypto_rand, crypto_rand_return_tse_str); tt_int_op(0, OP_EQ, connection_ext_or_process_inbuf(conn)); UNMOCK(crypto_rand); tt_int_op(TO_CONN(conn)->state, OP_EQ, EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH); CONTAINS("\xec\x80\xed\x6e\x54\x6d\x3b\x36\xfd\xfc\x22\xfe\x13\x15\x41\x6b" "\x02\x9f\x1a\xde\x76\x10\xd9\x10\x87\x8b\x62\xee\xb7\x40\x38\x21" "te road There is always another ", 64); /* Send the right response this time. */ WRITE("\xab\x39\x17\x32\xdd\x2e\xd9\x68\xcd\x40\xc0\x87\xd1\xb1\xf2\x5b" "\x33\xb3\xcd\x77\xff\x79\xbd\x80\xc2\x07\x4b\xbf\x43\x81\x19\xa2", 32); tt_int_op(0, OP_EQ, connection_ext_or_process_inbuf(conn)); CONTAINS("\x01", 1); tt_assert(! TO_CONN(conn)->marked_for_close); tt_int_op(TO_CONN(conn)->state, OP_EQ, EXT_OR_CONN_STATE_OPEN); done: ; }
static void test_conn_get_rend(void *arg) { dir_connection_t *conn = DOWNCAST(dir_connection_t, arg); tt_assert(conn); assert_connection_ok(&conn->base_, time(NULL)); tt_assert(connection_get_by_type_state_rendquery( conn->base_.type, conn->base_.state, conn->rend_data->onion_address) == TO_CONN(conn)); tt_assert(connection_get_by_type_state_rendquery( TEST_CONN_TYPE, TEST_CONN_STATE, TEST_CONN_REND_ADDR) == TO_CONN(conn)); tt_assert(connection_get_by_type_state_rendquery(TEST_CONN_REND_TYPE_2, !conn->base_.state, "") == NULL); tt_assert(connection_get_by_type_state_rendquery(TEST_CONN_REND_TYPE_2, !TEST_CONN_STATE, TEST_CONN_REND_ADDR_2) == NULL); done: ; }
static void NS(test_main)(void *arg) { int retval; int made_pending = 0; pending_connection_t *pending_conn = NULL; edge_connection_t *exitconn = create_valid_exitconn(); or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t)); cached_resolve_t *cache_entry = NULL; cached_resolve_t query; (void)arg; TO_CONN(exitconn)->address = tor_strdup("torproject.org"); strlcpy(query.address, TO_CONN(exitconn)->address, sizeof(query.address)); NS_MOCK(router_my_exit_policy_is_reject_star); NS_MOCK(launch_resolve); dns_init(); retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending, NULL); tt_int_op(retval,OP_EQ,0); tt_int_op(made_pending,OP_EQ,1); cache_entry = dns_get_cache_entry(&query); tt_assert(cache_entry); pending_conn = cache_entry->pending_connections; tt_assert(pending_conn != NULL); tt_assert(pending_conn->conn == exitconn); tt_assert(last_launched_resolve == cache_entry); tt_str_op(cache_entry->address,OP_EQ,TO_CONN(exitconn)->address); done: NS_UNMOCK(router_my_exit_policy_is_reject_star); NS_UNMOCK(launch_resolve); tor_free(on_circ); tor_free(TO_CONN(exitconn)->address); if (cache_entry) tor_free(cache_entry->pending_connections); tor_free(cache_entry); tor_free(exitconn); return; }
/** Log, at severity <b>severity</b>, information about each circuit * that is connected to <b>conn</b>. */ void circuit_dump_by_conn(connection_t *conn, int severity) { circuit_t *circ; edge_connection_t *tmpconn; for (circ=global_circuitlist;circ;circ = circ->next) { circid_t n_circ_id = circ->n_circ_id, p_circ_id = 0; if (circ->marked_for_close) continue; if (! CIRCUIT_IS_ORIGIN(circ)) p_circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; if (! CIRCUIT_IS_ORIGIN(circ) && TO_OR_CIRCUIT(circ)->p_conn && TO_CONN(TO_OR_CIRCUIT(circ)->p_conn) == conn) circuit_dump_details(severity, circ, conn->conn_array_index, "App-ward", p_circ_id, n_circ_id); if (CIRCUIT_IS_ORIGIN(circ)) { for (tmpconn=TO_ORIGIN_CIRCUIT(circ)->p_streams; tmpconn; tmpconn=tmpconn->next_stream) { if (TO_CONN(tmpconn) == conn) { circuit_dump_details(severity, circ, conn->conn_array_index, "App-ward", p_circ_id, n_circ_id); } } } if (circ->n_conn && TO_CONN(circ->n_conn) == conn) circuit_dump_details(severity, circ, conn->conn_array_index, "Exit-ward", n_circ_id, p_circ_id); if (! CIRCUIT_IS_ORIGIN(circ)) { for (tmpconn=TO_OR_CIRCUIT(circ)->n_streams; tmpconn; tmpconn=tmpconn->next_stream) { if (TO_CONN(tmpconn) == conn) { circuit_dump_details(severity, circ, conn->conn_array_index, "Exit-ward", n_circ_id, p_circ_id); } } } if (!circ->n_conn && circ->n_hop && tor_addr_eq(&circ->n_hop->addr, &conn->addr) && circ->n_hop->port == conn->port && conn->type == CONN_TYPE_OR && !memcmp(TO_OR_CONN(conn)->identity_digest, circ->n_hop->identity_digest, DIGEST_LEN)) { circuit_dump_details(severity, circ, conn->conn_array_index, (circ->state == CIRCUIT_STATE_OPEN && !CIRCUIT_IS_ORIGIN(circ)) ? "Endpoint" : "Pending", n_circ_id, p_circ_id); } } }
static void test_oos_kill_conn_list(void *arg) { connection_t *c1, *c2; or_connection_t *or_c1 = NULL; dir_connection_t *dir_c2 = NULL; smartlist_t *l = NULL; (void)arg; /* Set up mocks */ mark_calls = 0; MOCK(connection_mark_for_close_internal_, mark_for_close_oos_mock); cfe_calls = 0; MOCK(connection_or_close_for_error, close_for_error_mock); /* Make fake conns */ or_c1 = tor_malloc_zero(sizeof(*or_c1)); or_c1->base_.magic = OR_CONNECTION_MAGIC; or_c1->base_.type = CONN_TYPE_OR; c1 = TO_CONN(or_c1); dir_c2 = tor_malloc_zero(sizeof(*dir_c2)); dir_c2->base_.magic = DIR_CONNECTION_MAGIC; dir_c2->base_.type = CONN_TYPE_DIR; dir_c2->base_.state = DIR_CONN_STATE_MIN_; dir_c2->base_.purpose = DIR_PURPOSE_MIN_; c2 = TO_CONN(dir_c2); tt_assert(c1 != NULL); tt_assert(c2 != NULL); /* Make list */ l = smartlist_new(); smartlist_add(l, c1); smartlist_add(l, c2); /* Run kill_conn_list_for_oos() */ kill_conn_list_for_oos(l); /* Check call counters */ tt_int_op(mark_calls, OP_EQ, 1); tt_int_op(cfe_calls, OP_EQ, 1); done: UNMOCK(connection_or_close_for_error); UNMOCK(connection_mark_for_close_internal_); if (l) smartlist_free(l); tor_free(or_c1); tor_free(dir_c2); return; }
/** Create an <b>edge_connection_t</b> instance that is considered a * valid exit connection by asserts in dns_resolve_impl. */ static edge_connection_t * create_valid_exitconn(void) { edge_connection_t *exitconn = tor_malloc_zero(sizeof(edge_connection_t)); TO_CONN(exitconn)->type = CONN_TYPE_EXIT; TO_CONN(exitconn)->magic = EDGE_CONNECTION_MAGIC; TO_CONN(exitconn)->purpose = EXIT_PURPOSE_RESOLVE; TO_CONN(exitconn)->state = EXIT_CONN_STATE_RESOLVING; exitconn->base_.s = TOR_INVALID_SOCKET; return exitconn; }
/* Test connection_or_remove_from_ext_or_id_map and * connection_or_set_ext_or_identifier */ static void test_ext_or_id_map(void *arg) { or_connection_t *c1 = NULL, *c2 = NULL, *c3 = NULL; char *idp = NULL, *idp2 = NULL; (void)arg; /* pre-initialization */ tt_ptr_op(NULL, OP_EQ, connection_or_get_by_ext_or_id("xxxxxxxxxxxxxxxxxxxx")); c1 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET); c2 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET); c3 = or_connection_new(CONN_TYPE_OR, AF_INET); tt_ptr_op(c1->ext_or_conn_id, OP_NE, NULL); tt_ptr_op(c2->ext_or_conn_id, OP_NE, NULL); tt_ptr_op(c3->ext_or_conn_id, OP_EQ, NULL); tt_ptr_op(c1, OP_EQ, connection_or_get_by_ext_or_id(c1->ext_or_conn_id)); tt_ptr_op(c2, OP_EQ, connection_or_get_by_ext_or_id(c2->ext_or_conn_id)); tt_ptr_op(NULL, OP_EQ, connection_or_get_by_ext_or_id("xxxxxxxxxxxxxxxxxxxx")); idp = tor_memdup(c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN); /* Give c2 a new ID. */ connection_or_set_ext_or_identifier(c2); tt_mem_op(idp, OP_NE, c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN); idp2 = tor_memdup(c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN); tt_assert(!tor_digest_is_zero(idp2)); tt_ptr_op(NULL, OP_EQ, connection_or_get_by_ext_or_id(idp)); tt_ptr_op(c2, OP_EQ, connection_or_get_by_ext_or_id(idp2)); /* Now remove it. */ connection_or_remove_from_ext_or_id_map(c2); tt_ptr_op(NULL, OP_EQ, connection_or_get_by_ext_or_id(idp)); tt_ptr_op(NULL, OP_EQ, connection_or_get_by_ext_or_id(idp2)); done: if (c1) connection_free_(TO_CONN(c1)); if (c2) connection_free_(TO_CONN(c2)); if (c3) connection_free_(TO_CONN(c3)); tor_free(idp); tor_free(idp2); connection_or_clear_ext_or_id_map(); }
static void NS(test_main)(void *arg) { int retval; int made_pending = 0; edge_connection_t *exitconn = create_valid_exitconn(); or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t)); cached_resolve_t *resolve_out = NULL; cached_resolve_t *cache_entry = tor_malloc_zero(sizeof(cached_resolve_t)); cache_entry->magic = CACHED_RESOLVE_MAGIC; cache_entry->state = CACHE_STATE_CACHED; cache_entry->minheap_idx = -1; cache_entry->expire = time(NULL) + 60 * 60; (void)arg; TO_CONN(exitconn)->address = tor_strdup("torproject.org"); strlcpy(cache_entry->address, TO_CONN(exitconn)->address, sizeof(cache_entry->address)); NS_MOCK(router_my_exit_policy_is_reject_star); NS_MOCK(set_exitconn_info_from_resolve); dns_init(); dns_insert_cache_entry(cache_entry); retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending, &resolve_out); tt_int_op(retval,OP_EQ,0); tt_int_op(made_pending,OP_EQ,0); tt_assert(resolve_out == cache_entry); tt_assert(last_exitconn == exitconn); tt_assert(last_resolve == cache_entry); done: NS_UNMOCK(router_my_exit_policy_is_reject_star); NS_UNMOCK(set_exitconn_info_from_resolve); tor_free(on_circ); tor_free(TO_CONN(exitconn)->address); tor_free(cache_entry->pending_connections); tor_free(cache_entry); return; }
/** Helper function: called whenever the client sends a resolve request to our * controller. We need to eventually answer the request <b>req</b>. * Returns 0 if the controller will be getting (or has gotten) an event in * response; -1 if we couldn't launch the request. */ int dnsserv_launch_request(const char *name, int reverse) { entry_connection_t *entry_conn; edge_connection_t *conn; char *q_name; /* Make a new dummy AP connection, and attach the request to it. */ entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET); conn = ENTRY_TO_EDGE_CONN(entry_conn); conn->base_.state = AP_CONN_STATE_RESOLVE_WAIT; if (reverse) entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR; else entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE; conn->is_dns_request = 1; strlcpy(entry_conn->socks_request->address, name, sizeof(entry_conn->socks_request->address)); entry_conn->socks_request->listener_type = CONN_TYPE_CONTROL_LISTENER; entry_conn->original_dest_address = tor_strdup(name); entry_conn->session_group = SESSION_GROUP_CONTROL_RESOLVE; entry_conn->nym_epoch = get_signewnym_epoch(); entry_conn->isolation_flags = ISO_DEFAULT; if (connection_add(TO_CONN(conn))<0) { log_warn(LD_APP, "Couldn't register dummy connection for RESOLVE request"); connection_free(TO_CONN(conn)); return -1; } /* Now, unless a controller asked us to leave streams unattached, * throw the connection over to get rewritten (which will * answer it immediately if it's in the cache, or completely bogus, or * automapped), and then attached to a circuit. */ log_info(LD_APP, "Passing request for %s to rewrite_and_attach.", escaped_safe_str_client(name)); q_name = tor_strdup(name); /* q could be freed in rewrite_and_attach */ connection_ap_rewrite_and_attach_if_allowed(entry_conn, NULL, NULL); /* Now, the connection is marked if it was bad. */ log_info(LD_APP, "Passed request for %s to rewrite_and_attach_if_allowed.", escaped_safe_str_client(q_name)); tor_free(q_name); return 0; }
/** Process a 'versions' cell. The current link protocol version must be 0 * to indicate that no version has yet been negotiated. We compare the * versions in the cell to the list of versions we support, pick the * highest version we have in common, and continue the negotiation from * there. */ static void command_process_versions_cell(var_cell_t *cell, or_connection_t *conn) { int highest_supported_version = 0; const char *cp, *end; if (conn->link_proto != 0 || conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING || (conn->handshake_state && conn->handshake_state->received_versions)) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Received a VERSIONS cell on a connection with its version " "already set to %d; dropping", (int) conn->link_proto); return; } tor_assert(conn->handshake_state); end = cell->payload + cell->payload_len; for (cp = cell->payload; cp+1 < end; ++cp) { uint16_t v = ntohs(get_uint16(cp)); if (is_or_protocol_version_known(v) && v > highest_supported_version) highest_supported_version = v; } if (!highest_supported_version) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Couldn't find a version in common between my version list and the " "list in the VERSIONS cell; closing connection."); connection_mark_for_close(TO_CONN(conn)); return; } else if (highest_supported_version == 1) { /* Negotiating version 1 makes no sense, since version 1 has no VERSIONS * cells. */ log_fn(LOG_PROTOCOL_WARN, LD_OR, "Used version negotiation protocol to negotiate a v1 connection. " "That's crazily non-compliant. Closing connection."); connection_mark_for_close(TO_CONN(conn)); return; } conn->link_proto = highest_supported_version; conn->handshake_state->received_versions = 1; log_info(LD_OR, "Negotiated version %d with %s:%d; sending NETINFO.", highest_supported_version, safe_str(conn->_base.address), conn->_base.port); tor_assert(conn->link_proto >= 2); if (connection_or_send_netinfo(conn) < 0) { connection_mark_for_close(TO_CONN(conn)); return; } }
static void test_ext_or_write_command(void *arg) { or_connection_t *c1; char *cp = NULL; char *buf = NULL; size_t sz; (void) arg; MOCK(connection_write_to_buf_impl_, connection_write_to_buf_impl_replacement); c1 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET); tt_assert(c1); /* Length too long */ tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 100, "X", 100000), OP_LT, 0); /* Empty command */ tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 0x99, NULL, 0), OP_EQ, 0); cp = buf_get_contents(TO_CONN(c1)->outbuf, &sz); tt_int_op(sz, OP_EQ, 4); tt_mem_op(cp, OP_EQ, "\x00\x99\x00\x00", 4); tor_free(cp); /* Medium command. */ tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 0x99, "Wai\0Hello", 9), OP_EQ, 0); cp = buf_get_contents(TO_CONN(c1)->outbuf, &sz); tt_int_op(sz, OP_EQ, 13); tt_mem_op(cp, OP_EQ, "\x00\x99\x00\x09Wai\x00Hello", 13); tor_free(cp); /* Long command */ buf = tor_malloc(65535); memset(buf, 'x', 65535); tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 0xf00d, buf, 65535), OP_EQ, 0); cp = buf_get_contents(TO_CONN(c1)->outbuf, &sz); tt_int_op(sz, OP_EQ, 65539); tt_mem_op(cp, OP_EQ, "\xf0\x0d\xff\xff", 4); tt_mem_op(cp+4, OP_EQ, buf, 65535); tor_free(cp); done: if (c1) connection_free_(TO_CONN(c1)); tor_free(cp); tor_free(buf); UNMOCK(connection_write_to_buf_impl_); }
static edge_connection_t * dummy_edge_conn_new(circuit_t *circ, int type, size_t in_bytes, size_t out_bytes) { edge_connection_t *conn; generic_buffer_t *inbuf, *outbuf; if (type == CONN_TYPE_EXIT) conn = edge_connection_new(type, AF_INET); else conn = ENTRY_TO_EDGE_CONN(entry_connection_new(type, AF_INET)); #ifdef USE_BUFFEREVENTS inbuf = bufferevent_get_input(TO_CONN(conn)->bufev); outbuf = bufferevent_get_output(TO_CONN(conn)->bufev); #else inbuf = TO_CONN(conn)->inbuf; outbuf = TO_CONN(conn)->outbuf; #endif /* We add these bytes directly to the buffers, to avoid all the * edge connection read/write machinery. */ add_bytes_to_buf(inbuf, in_bytes); add_bytes_to_buf(outbuf, out_bytes); conn->on_circuit = circ; if (type == CONN_TYPE_EXIT) { or_circuit_t *oc = TO_OR_CIRCUIT(circ); conn->next_stream = oc->n_streams; oc->n_streams = conn; } else { origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ); conn->next_stream = oc->p_streams; oc->p_streams = conn; } return conn; }
static int get_num_circuits_mock(or_connection_t *conn) { int circs = 0; tt_assert(conn != NULL); if (conns_with_circs && smartlist_contains(conns_with_circs, TO_CONN(conn))) { circs = 1; } done: return circs; }
/** Called when we've just received a relay data cell, when we've just * finished flushing all bytes to stream <b>conn</b>, or when we've flushed * *some* bytes to the stream <b>conn</b>. * * If conn->outbuf is not too full, and our deliver window is low, send back a * suitable number of stream-level sendme cells. */ void sendme_connection_edge_consider_sending(edge_connection_t *conn) { tor_assert(conn); int log_domain = TO_CONN(conn)->type == CONN_TYPE_AP ? LD_APP : LD_EXIT; /* Don't send it if we still have data to deliver. */ if (connection_outbuf_too_full(TO_CONN(conn))) { goto end; } if (circuit_get_by_edge_conn(conn) == NULL) { /* This can legitimately happen if the destroy has already arrived and * torn down the circuit. */ log_info(log_domain, "No circuit associated with edge connection. " "Skipping sending SENDME."); goto end; } while (conn->deliver_window <= (STREAMWINDOW_START - STREAMWINDOW_INCREMENT)) { log_debug(log_domain, "Outbuf %" TOR_PRIuSZ ", queuing stream SENDME.", TO_CONN(conn)->outbuf_flushlen); conn->deliver_window += STREAMWINDOW_INCREMENT; if (connection_edge_send_command(conn, RELAY_COMMAND_SENDME, NULL, 0) < 0) { log_warn(LD_BUG, "connection_edge_send_command failed while sending " "a SENDME. Circuit probably closed, skipping."); goto end; /* The circuit's closed, don't continue */ } } end: return; }
/** Called when we as a server receive an appropriate cell while waiting * either for a cell or a TLS handshake. Set the connection's state to * "handshaking_v3', initializes the or_handshake_state field as needed, * and add the cell to the hash of incoming cells.) * * Return 0 on success; return -1 and mark the connection on failure. */ static int enter_v3_handshake_with_cell(var_cell_t *cell, or_connection_t *conn) { const int started_here = connection_or_nonopen_was_started_here(conn); tor_assert(conn->_base.state == OR_CONN_STATE_TLS_HANDSHAKING || conn->_base.state == OR_CONN_STATE_TLS_SERVER_RENEGOTIATING); if (started_here) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Received a cell while TLS-handshaking, not in " "OR_HANDSHAKING_V3, on a connection we originated."); } conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING_V3; if (connection_init_or_handshake_state(conn, started_here) < 0) { connection_mark_for_close(TO_CONN(conn)); return -1; } or_handshake_state_record_var_cell(conn->handshake_state, cell, 1); return 0; }
static int recv_certs_cleanup(const struct testcase_t *test, void *obj) { (void)test; certs_data_t *d = obj; UNMOCK(tor_tls_cert_matches_key); UNMOCK(connection_or_send_netinfo); UNMOCK(connection_or_close_for_error); if (d) { tor_free(d->cell); certs_cell_free(d->ccell); connection_free_(TO_CONN(d->c)); circuitmux_free(d->chan->base_.cmux); tor_free(d->chan); crypto_pk_free(d->key1); crypto_pk_free(d->key2); tor_free(d); } return 1; }
/** Release all storage held by circuits. */ void circuit_free_all(void) { circuit_t *next; while (global_circuitlist) { next = global_circuitlist->next; if (! CIRCUIT_IS_ORIGIN(global_circuitlist)) { or_circuit_t *or_circ = TO_OR_CIRCUIT(global_circuitlist); while (or_circ->resolving_streams) { edge_connection_t *next_conn; next_conn = or_circ->resolving_streams->next_stream; connection_free(TO_CONN(or_circ->resolving_streams)); or_circ->resolving_streams = next_conn; } } circuit_free(global_circuitlist); global_circuitlist = next; } if (circuits_pending_or_conns) { smartlist_free(circuits_pending_or_conns); circuits_pending_or_conns = NULL; } HT_CLEAR(orconn_circid_map, &orconn_circid_circuit_map); }
/** Process an AUTH_CHALLENGE cell from an OR connection. * * If we weren't supposed to get one (for example, because we're not the * originator of the connection), or it's ill-formed, or we aren't doing a v3 * handshake, mark the connection. If the cell is well-formed but we don't * want to authenticate, just drop it. If the cell is well-formed *and* we * want to authenticate, send an AUTHENTICATE cell and then a NETINFO cell. */ static void command_process_auth_challenge_cell(var_cell_t *cell, or_connection_t *conn) { int n_types, i, use_type = -1; uint8_t *cp; #define ERR(s) \ do { \ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ "Received a bad AUTH_CHALLENGE cell from %s:%d: %s", \ safe_str(conn->_base.address), conn->_base.port, (s)); \ connection_mark_for_close(TO_CONN(conn)); \ return; \ } while (0) if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) ERR("We're not currently doing a v3 handshake"); if (conn->link_proto < 3) ERR("We're not using link protocol >= 3"); if (! conn->handshake_state->started_here) ERR("We didn't originate this connection"); if (conn->handshake_state->received_auth_challenge) ERR("We already received one"); if (! conn->handshake_state->received_certs_cell) ERR("We haven't gotten a CERTS cell yet"); if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2) ERR("It was too short"); if (cell->circ_id) ERR("It had a nonzero circuit ID"); n_types = ntohs(get_uint16(cell->payload + OR_AUTH_CHALLENGE_LEN)); if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2 + 2*n_types) ERR("It looks truncated"); /* Now see if there is an authentication type we can use */ cp=cell->payload+OR_AUTH_CHALLENGE_LEN+2; for (i=0; i < n_types; ++i, cp += 2) { uint16_t authtype = ntohs(get_uint16(cp)); if (authtype == AUTHTYPE_RSA_SHA256_TLSSECRET) use_type = authtype; } conn->handshake_state->received_auth_challenge = 1; if (! public_server_mode(get_options())) { /* If we're not a public server then we don't want to authenticate on a connection we originated, and we already sent a NETINFO cell when we got the CERTS cell. We have nothing more to do. */ return; } if (use_type >= 0) { log_info(LD_OR, "Got an AUTH_CHALLENGE cell from %s:%d: Sending " "authentication", safe_str(conn->_base.address), conn->_base.port); if (connection_or_send_authenticate_cell(conn, use_type) < 0) { log_warn(LD_OR, "Couldn't send authenticate cell"); connection_mark_for_close(TO_CONN(conn)); return; } } else { log_info(LD_OR, "Got an AUTH_CHALLENGE cell from %s:%d, but we don't " "know any of its authentication types. Not authenticating.", safe_str(conn->_base.address), conn->_base.port); } if (connection_or_send_netinfo(conn) < 0) { log_warn(LD_OR, "Couldn't send netinfo cell"); connection_mark_for_close(TO_CONN(conn)); return; } #undef ERR }
/** Called when we get an AUTHENTICATE message. Check whether the * authentication is valid, and if so, update the connection's state to * OPEN. Reply with DONE or ERROR. */ int handle_control_authenticate(control_connection_t *conn, uint32_t len, const char *body) { int used_quoted_string = 0; const or_options_t *options = get_options(); const char *errstr = "Unknown error"; char *password; size_t password_len; const char *cp; int i; int bad_cookie=0, bad_password=0; smartlist_t *sl = NULL; if (!len) { password = tor_strdup(""); password_len = 0; } else if (TOR_ISXDIGIT(body[0])) { cp = body; while (TOR_ISXDIGIT(*cp)) ++cp; i = (int)(cp - body); tor_assert(i>0); password_len = i/2; password = tor_malloc(password_len + 1); if (base16_decode(password, password_len+1, body, i) != (int) password_len) { connection_write_str_to_buf( "551 Invalid hexadecimal encoding. Maybe you tried a plain text " "password? If so, the standard requires that you put it in " "double quotes.\r\n", conn); connection_mark_for_close(TO_CONN(conn)); tor_free(password); return 0; } } else { if (!decode_escaped_string(body, len, &password, &password_len)) { connection_write_str_to_buf("551 Invalid quoted string. You need " "to put the password in double quotes.\r\n", conn); connection_mark_for_close(TO_CONN(conn)); return 0; } used_quoted_string = 1; } if (conn->safecookie_client_hash != NULL) { /* The controller has chosen safe cookie authentication; the only * acceptable authentication value is the controller-to-server * response. */ tor_assert(authentication_cookie_is_set); if (password_len != DIGEST256_LEN) { log_warn(LD_CONTROL, "Got safe cookie authentication response with wrong length " "(%d)", (int)password_len); errstr = "Wrong length for safe cookie response."; goto err; } if (tor_memneq(conn->safecookie_client_hash, password, DIGEST256_LEN)) { log_warn(LD_CONTROL, "Got incorrect safe cookie authentication response"); errstr = "Safe cookie response did not match expected value."; goto err; } tor_free(conn->safecookie_client_hash); goto ok; } if (!options->CookieAuthentication && !options->HashedControlPassword && !options->HashedControlSessionPassword) { /* if Tor doesn't demand any stronger authentication, then * the controller can get in with anything. */ goto ok; } if (options->CookieAuthentication) { int also_password = options->HashedControlPassword != NULL || options->HashedControlSessionPassword != NULL; if (password_len != AUTHENTICATION_COOKIE_LEN) { if (!also_password) { log_warn(LD_CONTROL, "Got authentication cookie with wrong length " "(%d)", (int)password_len); errstr = "Wrong length on authentication cookie."; goto err; } bad_cookie = 1; } else if (tor_memneq(authentication_cookie, password, password_len)) { if (!also_password) { log_warn(LD_CONTROL, "Got mismatched authentication cookie"); errstr = "Authentication cookie did not match expected value."; goto err; } bad_cookie = 1; } else { goto ok; } } if (options->HashedControlPassword || options->HashedControlSessionPassword) { int bad = 0; smartlist_t *sl_tmp; char received[DIGEST_LEN]; int also_cookie = options->CookieAuthentication; sl = smartlist_new(); if (options->HashedControlPassword) { sl_tmp = decode_hashed_passwords(options->HashedControlPassword); if (!sl_tmp) bad = 1; else { smartlist_add_all(sl, sl_tmp); smartlist_free(sl_tmp); } } if (options->HashedControlSessionPassword) { sl_tmp = decode_hashed_passwords(options->HashedControlSessionPassword); if (!sl_tmp) bad = 1; else { smartlist_add_all(sl, sl_tmp); smartlist_free(sl_tmp); } } if (bad) { if (!also_cookie) { log_warn(LD_BUG, "Couldn't decode HashedControlPassword: invalid base16"); errstr="Couldn't decode HashedControlPassword value in configuration."; goto err; } bad_password = 1; SMARTLIST_FOREACH(sl, char *, str, tor_free(str)); smartlist_free(sl); sl = NULL; } else { SMARTLIST_FOREACH(sl, char *, expected, { secret_to_key_rfc2440(received,DIGEST_LEN, password,password_len,expected); if (tor_memeq(expected + S2K_RFC2440_SPECIFIER_LEN, received, DIGEST_LEN)) goto ok; });
/** Called when we get an AUTHCHALLENGE command. */ int handle_control_authchallenge(control_connection_t *conn, uint32_t len, const char *body) { const char *cp = body; char *client_nonce; size_t client_nonce_len; char server_hash[DIGEST256_LEN]; char server_hash_encoded[HEX_DIGEST256_LEN+1]; char server_nonce[SAFECOOKIE_SERVER_NONCE_LEN]; char server_nonce_encoded[(2*SAFECOOKIE_SERVER_NONCE_LEN) + 1]; cp += strspn(cp, " \t\n\r"); if (!strcasecmpstart(cp, "SAFECOOKIE")) { cp += strlen("SAFECOOKIE"); } else { connection_write_str_to_buf("513 AUTHCHALLENGE only supports SAFECOOKIE " "authentication\r\n", conn); connection_mark_for_close(TO_CONN(conn)); return -1; } if (!authentication_cookie_is_set) { connection_write_str_to_buf("515 Cookie authentication is disabled\r\n", conn); connection_mark_for_close(TO_CONN(conn)); return -1; } cp += strspn(cp, " \t\n\r"); if (*cp == '"') { const char *newcp = decode_escaped_string(cp, len - (cp - body), &client_nonce, &client_nonce_len); if (newcp == NULL) { connection_write_str_to_buf("513 Invalid quoted client nonce\r\n", conn); connection_mark_for_close(TO_CONN(conn)); return -1; } cp = newcp; } else { size_t client_nonce_encoded_len = strspn(cp, "0123456789ABCDEFabcdef"); client_nonce_len = client_nonce_encoded_len / 2; client_nonce = tor_malloc_zero(client_nonce_len); if (base16_decode(client_nonce, client_nonce_len, cp, client_nonce_encoded_len) != (int) client_nonce_len) { connection_write_str_to_buf("513 Invalid base16 client nonce\r\n", conn); connection_mark_for_close(TO_CONN(conn)); tor_free(client_nonce); return -1; } cp += client_nonce_encoded_len; } cp += strspn(cp, " \t\n\r"); if (*cp != '\0' || cp != body + len) { connection_write_str_to_buf("513 Junk at end of AUTHCHALLENGE command\r\n", conn); connection_mark_for_close(TO_CONN(conn)); tor_free(client_nonce); return -1; } crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN); /* Now compute and send the server-to-controller response, and the * server's nonce. */ tor_assert(authentication_cookie != NULL); { size_t tmp_len = (AUTHENTICATION_COOKIE_LEN + client_nonce_len + SAFECOOKIE_SERVER_NONCE_LEN); char *tmp = tor_malloc_zero(tmp_len); char *client_hash = tor_malloc_zero(DIGEST256_LEN); memcpy(tmp, authentication_cookie, AUTHENTICATION_COOKIE_LEN); memcpy(tmp + AUTHENTICATION_COOKIE_LEN, client_nonce, client_nonce_len); memcpy(tmp + AUTHENTICATION_COOKIE_LEN + client_nonce_len, server_nonce, SAFECOOKIE_SERVER_NONCE_LEN); crypto_hmac_sha256(server_hash, SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT, strlen(SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT), tmp, tmp_len); crypto_hmac_sha256(client_hash, SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT, strlen(SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT), tmp, tmp_len); conn->safecookie_client_hash = client_hash; tor_free(tmp); } base16_encode(server_hash_encoded, sizeof(server_hash_encoded), server_hash, sizeof(server_hash)); base16_encode(server_nonce_encoded, sizeof(server_nonce_encoded), server_nonce, sizeof(server_nonce)); connection_printf_to_buf(conn, "250 AUTHCHALLENGE SERVERHASH=%s " "SERVERNONCE=%s\r\n", server_hash_encoded, server_nonce_encoded); tor_free(client_nonce); return 0; }
/** Helper function: called by evdns whenever the client sends a request to our * DNSPort. We need to eventually answer the request <b>req</b>. */ static void evdns_server_callback(struct evdns_server_request *req, void *data_) { const listener_connection_t *listener = data_; entry_connection_t *entry_conn; edge_connection_t *conn; int i = 0; struct evdns_server_question *q = NULL, *supported_q = NULL; struct sockaddr_storage addr; struct sockaddr *sa; int addrlen; tor_addr_t tor_addr; uint16_t port; int err = DNS_ERR_NONE; char *q_name; tor_assert(req); log_info(LD_APP, "Got a new DNS request!"); req->flags |= 0x80; /* set RA */ /* First, check whether the requesting address matches our SOCKSPolicy. */ if ((addrlen = evdns_server_request_get_requesting_addr(req, (struct sockaddr*)&addr, (socklen_t)sizeof(addr))) < 0) { log_warn(LD_APP, "Couldn't get requesting address."); evdns_server_request_respond(req, DNS_ERR_SERVERFAILED); return; } (void) addrlen; sa = (struct sockaddr*) &addr; if (tor_addr_from_sockaddr(&tor_addr, sa, &port)<0) { log_warn(LD_APP, "Requesting address wasn't recognized."); evdns_server_request_respond(req, DNS_ERR_SERVERFAILED); return; } if (!socks_policy_permits_address(&tor_addr)) { log_warn(LD_APP, "Rejecting DNS request from disallowed IP."); evdns_server_request_respond(req, DNS_ERR_REFUSED); return; } /* Now, let's find the first actual question of a type we can answer in this * DNS request. It makes us a little noncompliant to act like this; we * should fix that eventually if it turns out to make a difference for * anybody. */ if (req->nquestions == 0) { log_info(LD_APP, "No questions in DNS request; sending back nil reply."); evdns_server_request_respond(req, 0); return; } if (req->nquestions > 1) { log_info(LD_APP, "Got a DNS request with more than one question; I only " "handle one question at a time for now. Skipping the extras."); } for (i = 0; i < req->nquestions; ++i) { if (req->questions[i]->dns_question_class != EVDNS_CLASS_INET) continue; switch (req->questions[i]->type) { case EVDNS_TYPE_A: case EVDNS_TYPE_AAAA: case EVDNS_TYPE_PTR: /* We always pick the first one of these questions, if there is one. */ if (! supported_q) supported_q = req->questions[i]; break; default: break; } } if (supported_q) q = supported_q; if (!q) { log_info(LD_APP, "None of the questions we got were ones we're willing " "to support. Sending NOTIMPL."); evdns_server_request_respond(req, DNS_ERR_NOTIMPL); return; } /* Make sure the name isn't too long: This should be impossible, I think. */ if (err == DNS_ERR_NONE && strlen(q->name) > MAX_SOCKS_ADDR_LEN-1) err = DNS_ERR_FORMAT; if (err != DNS_ERR_NONE || !supported_q) { /* We got an error? There's no question we're willing to answer? Then * send back an answer immediately; we're done. */ evdns_server_request_respond(req, err); return; } /* Make a new dummy AP connection, and attach the request to it. */ entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET); conn = ENTRY_TO_EDGE_CONN(entry_conn); CONNECTION_AP_EXPECT_NONPENDING(entry_conn); TO_CONN(conn)->state = AP_CONN_STATE_RESOLVE_WAIT; conn->is_dns_request = 1; tor_addr_copy(&TO_CONN(conn)->addr, &tor_addr); TO_CONN(conn)->port = port; TO_CONN(conn)->address = tor_addr_to_str_dup(&tor_addr); if (q->type == EVDNS_TYPE_A || q->type == EVDNS_TYPE_AAAA || q->type == EVDNS_QTYPE_ALL) { entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE; } else { tor_assert(q->type == EVDNS_TYPE_PTR); entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR; } /* This serves our DNS port so enable DNS request by default. */ entry_conn->entry_cfg.dns_request = 1; if (q->type == EVDNS_TYPE_A || q->type == EVDNS_QTYPE_ALL) { entry_conn->entry_cfg.ipv4_traffic = 1; entry_conn->entry_cfg.ipv6_traffic = 0; entry_conn->entry_cfg.prefer_ipv6 = 0; } else if (q->type == EVDNS_TYPE_AAAA) { entry_conn->entry_cfg.ipv4_traffic = 0; entry_conn->entry_cfg.ipv6_traffic = 1; entry_conn->entry_cfg.prefer_ipv6 = 1; } strlcpy(entry_conn->socks_request->address, q->name, sizeof(entry_conn->socks_request->address)); entry_conn->socks_request->listener_type = listener->base_.type; entry_conn->dns_server_request = req; entry_conn->entry_cfg.isolation_flags = listener->entry_cfg.isolation_flags; entry_conn->entry_cfg.session_group = listener->entry_cfg.session_group; entry_conn->nym_epoch = get_signewnym_epoch(); if (connection_add(ENTRY_TO_CONN(entry_conn)) < 0) { log_warn(LD_APP, "Couldn't register dummy connection for DNS request"); evdns_server_request_respond(req, DNS_ERR_SERVERFAILED); connection_free(ENTRY_TO_CONN(entry_conn)); return; } control_event_stream_status(entry_conn, STREAM_EVENT_NEW_RESOLVE, 0); /* Now, unless a controller asked us to leave streams unattached, * throw the connection over to get rewritten (which will * answer it immediately if it's in the cache, or completely bogus, or * automapped), and then attached to a circuit. */ log_info(LD_APP, "Passing request for %s to rewrite_and_attach.", escaped_safe_str_client(q->name)); q_name = tor_strdup(q->name); /* q could be freed in rewrite_and_attach */ connection_ap_rewrite_and_attach_if_allowed(entry_conn, NULL, NULL); /* Now, the connection is marked if it was bad. */ log_info(LD_APP, "Passed request for %s to rewrite_and_attach_if_allowed.", escaped_safe_str_client(q_name)); tor_free(q_name); }
/** Helper function: called whenever the client sends a resolve request to our * controller. We need to eventually answer the request <b>req</b>. * Returns 0 if the controller will be getting (or has gotten) an event in * response; -1 if we couldn't launch the request. */ int dnsserv_launch_request(const char *name, int reverse, control_connection_t *control_conn) { entry_connection_t *entry_conn; edge_connection_t *conn; char *q_name; /* Make a new dummy AP connection, and attach the request to it. */ entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET); conn = ENTRY_TO_EDGE_CONN(entry_conn); CONNECTION_AP_EXPECT_NONPENDING(entry_conn); conn->base_.state = AP_CONN_STATE_RESOLVE_WAIT; tor_addr_copy(&TO_CONN(conn)->addr, &control_conn->base_.addr); #ifdef AF_UNIX /* * The control connection can be AF_UNIX and if so tor_addr_to_str_dup will * unhelpfully say "<unknown address type>"; say "(Tor_internal)" * instead. */ if (control_conn->base_.socket_family == AF_UNIX) { TO_CONN(conn)->port = 0; TO_CONN(conn)->address = tor_strdup("(Tor_internal)"); } else { TO_CONN(conn)->port = control_conn->base_.port; TO_CONN(conn)->address = tor_addr_to_str_dup(&control_conn->base_.addr); } #else TO_CONN(conn)->port = control_conn->base_.port; TO_CONN(conn)->address = tor_addr_to_str_dup(&control_conn->base_.addr); #endif if (reverse) entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR; else entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE; conn->is_dns_request = 1; strlcpy(entry_conn->socks_request->address, name, sizeof(entry_conn->socks_request->address)); entry_conn->socks_request->listener_type = CONN_TYPE_CONTROL_LISTENER; entry_conn->original_dest_address = tor_strdup(name); entry_conn->entry_cfg.session_group = SESSION_GROUP_CONTROL_RESOLVE; entry_conn->nym_epoch = get_signewnym_epoch(); entry_conn->entry_cfg.isolation_flags = ISO_DEFAULT; if (connection_add(TO_CONN(conn))<0) { log_warn(LD_APP, "Couldn't register dummy connection for RESOLVE request"); connection_free(TO_CONN(conn)); return -1; } control_event_stream_status(entry_conn, STREAM_EVENT_NEW_RESOLVE, 0); /* Now, unless a controller asked us to leave streams unattached, * throw the connection over to get rewritten (which will * answer it immediately if it's in the cache, or completely bogus, or * automapped), and then attached to a circuit. */ log_info(LD_APP, "Passing request for %s to rewrite_and_attach.", escaped_safe_str_client(name)); q_name = tor_strdup(name); /* q could be freed in rewrite_and_attach */ connection_ap_rewrite_and_attach_if_allowed(entry_conn, NULL, NULL); /* Now, the connection is marked if it was bad. */ log_info(LD_APP, "Passed request for %s to rewrite_and_attach_if_allowed.", escaped_safe_str_client(q_name)); tor_free(q_name); return 0; }
static void test_oos_pick_oos_victims(void *arg) { (void)arg; or_connection_t *ortmp; dir_connection_t *dirtmp; smartlist_t *picked; /* Set up mocks */ conns_for_mock = smartlist_new(); MOCK(get_connection_array, get_conns_mock); conns_with_circs = smartlist_new(); MOCK(connection_or_get_num_circuits, get_num_circuits_mock); /* Make some fake connections */ ortmp = tor_malloc_zero(sizeof(*ortmp)); ortmp->base_.magic = OR_CONNECTION_MAGIC; ortmp->base_.type = CONN_TYPE_OR; smartlist_add(conns_for_mock, TO_CONN(ortmp)); /* We'll pretend this one has a circuit too */ smartlist_add(conns_with_circs, TO_CONN(ortmp)); /* Next one */ ortmp = tor_malloc_zero(sizeof(*ortmp)); ortmp->base_.magic = OR_CONNECTION_MAGIC; ortmp->base_.type = CONN_TYPE_OR; smartlist_add(conns_for_mock, TO_CONN(ortmp)); /* Next one is moribund */ ortmp = tor_malloc_zero(sizeof(*ortmp)); ortmp->base_.magic = OR_CONNECTION_MAGIC; ortmp->base_.type = CONN_TYPE_OR; ortmp->base_.marked_for_close = 1; smartlist_add(conns_for_mock, TO_CONN(ortmp)); /* Last one isn't an orconn */ dirtmp = tor_malloc_zero(sizeof(*dirtmp)); dirtmp->base_.magic = DIR_CONNECTION_MAGIC; dirtmp->base_.type = CONN_TYPE_DIR; smartlist_add(conns_for_mock, TO_CONN(dirtmp)); /* Try picking one */ picked = pick_oos_victims(1); /* It should be the one with circuits */ tt_assert(picked != NULL); tt_int_op(smartlist_len(picked), OP_EQ, 1); tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 0))); smartlist_free(picked); /* Try picking none */ picked = pick_oos_victims(0); /* We should get an empty list */ tt_assert(picked != NULL); tt_int_op(smartlist_len(picked), OP_EQ, 0); smartlist_free(picked); /* Try picking two */ picked = pick_oos_victims(2); /* We should get both active orconns */ tt_assert(picked != NULL); tt_int_op(smartlist_len(picked), OP_EQ, 2); tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 0))); tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 1))); smartlist_free(picked); /* Try picking three - only two are eligible */ picked = pick_oos_victims(3); tt_int_op(smartlist_len(picked), OP_EQ, 2); tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 0))); tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 1))); smartlist_free(picked); done: /* Free leftover stuff */ if (conns_with_circs) { smartlist_free(conns_with_circs); conns_with_circs = NULL; } UNMOCK(connection_or_get_num_circuits); if (conns_for_mock) { SMARTLIST_FOREACH(conns_for_mock, connection_t *, c, tor_free(c)); smartlist_free(conns_for_mock); conns_for_mock = NULL; } UNMOCK(get_connection_array); return; }
/** Test that we will use our directory guards to fetch mds even if we don't * have any dirinfo (tests bug #23862). */ static void test_directory_guard_fetch_with_no_dirinfo(void *arg) { int retval; char *consensus_text_md = NULL; or_options_t *options = get_options_mutable(); time_t now = time(NULL); (void) arg; hibernate_set_state_for_testing_(HIBERNATE_STATE_LIVE); /* Initialize the SRV subsystem */ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m); mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, strlen(AUTHORITY_CERT_1), NULL); sr_init(0); UNMOCK(get_my_v3_authority_cert); /* Initialize the entry node configuration from the ticket */ options->UseEntryGuards = 1; options->StrictNodes = 1; get_options_mutable()->EntryNodes = routerset_new(); routerset_parse(get_options_mutable()->EntryNodes, "2121212121212121212121212121212121212121", "foo"); /* Mock some functions */ dummy_state = tor_malloc_zero(sizeof(or_state_t)); MOCK(get_or_state, get_or_state_replacement); MOCK(directory_initiate_request, mock_directory_initiate_request); /* we need to mock this one to avoid memleaks */ MOCK(circuit_guard_state_new, mock_circuit_guard_state_new); /* Call guards_update_all() to simulate loading our state file (see * entry_guards_load_guards_from_state() and ticket #23989). */ guards_update_all(); /* Test logic: Simulate the arrival of a new consensus when we have no * dirinfo at all. Tor will need to fetch the mds from the consensus. Make * sure that Tor will use the specified entry guard instead of relying on the * fallback directories. */ /* Fixup the dirconn that will deliver the consensus */ dir_connection_t *conn = dir_connection_new(AF_INET); tor_addr_from_ipv4h(&conn->base_.addr, 0x7f000001); conn->base_.port = 8800; TO_CONN(conn)->address = tor_strdup("127.0.0.1"); conn->base_.purpose = DIR_PURPOSE_FETCH_CONSENSUS; conn->requested_resource = tor_strdup("ns"); /* Construct a consensus */ construct_consensus(&consensus_text_md, now); tt_assert(consensus_text_md); /* Place the consensus in the dirconn */ response_handler_args_t args; memset(&args, 0, sizeof(response_handler_args_t)); args.status_code = 200; args.body = consensus_text_md; args.body_len = strlen(consensus_text_md); /* Update approx time so that the consensus is considered live */ update_approx_time(now+1010); setup_capture_of_logs(LOG_DEBUG); /* Now handle the consensus */ retval = handle_response_fetch_consensus(conn, &args); tt_int_op(retval, OP_EQ, 0); /* Make sure that our primary guard was chosen */ expect_log_msg_containing("Selected primary guard router3"); done: tor_free(consensus_text_md); tor_free(dummy_state); connection_free_minimal(TO_CONN(conn)); entry_guards_free_all(); teardown_capture_of_logs(); }
/** Process a 'versions' cell. The current link protocol version must be 0 * to indicate that no version has yet been negotiated. We compare the * versions in the cell to the list of versions we support, pick the * highest version we have in common, and continue the negotiation from * there. */ static void command_process_versions_cell(var_cell_t *cell, or_connection_t *conn) { int highest_supported_version = 0; const uint8_t *cp, *end; const int started_here = connection_or_nonopen_was_started_here(conn); if (conn->link_proto != 0 || (conn->handshake_state && conn->handshake_state->received_versions)) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Received a VERSIONS cell on a connection with its version " "already set to %d; dropping", (int) conn->link_proto); return; } switch (conn->_base.state) { case OR_CONN_STATE_OR_HANDSHAKING_V2: case OR_CONN_STATE_OR_HANDSHAKING_V3: break; case OR_CONN_STATE_TLS_HANDSHAKING: case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING: default: log_fn(LOG_PROTOCOL_WARN, LD_OR, "VERSIONS cell while in unexpected state"); return; } tor_assert(conn->handshake_state); end = cell->payload + cell->payload_len; for (cp = cell->payload; cp+1 < end; ++cp) { uint16_t v = ntohs(get_uint16(cp)); if (is_or_protocol_version_known(v) && v > highest_supported_version) highest_supported_version = v; } if (!highest_supported_version) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Couldn't find a version in common between my version list and the " "list in the VERSIONS cell; closing connection."); connection_mark_for_close(TO_CONN(conn)); return; } else if (highest_supported_version == 1) { /* Negotiating version 1 makes no sense, since version 1 has no VERSIONS * cells. */ log_fn(LOG_PROTOCOL_WARN, LD_OR, "Used version negotiation protocol to negotiate a v1 connection. " "That's crazily non-compliant. Closing connection."); connection_mark_for_close(TO_CONN(conn)); return; } else if (highest_supported_version < 3 && conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Negotiated link protocol 2 or lower after doing a v3 TLS " "handshake. Closing connection."); connection_mark_for_close(TO_CONN(conn)); return; } conn->link_proto = highest_supported_version; conn->handshake_state->received_versions = 1; if (conn->link_proto == 2) { log_info(LD_OR, "Negotiated version %d with %s:%d; sending NETINFO.", highest_supported_version, safe_str_client(conn->_base.address), conn->_base.port); if (connection_or_send_netinfo(conn) < 0) { connection_mark_for_close(TO_CONN(conn)); return; } } else { const int send_versions = !started_here; /* If we want to authenticate, send a CERTS cell */ const int send_certs = !started_here || public_server_mode(get_options()); /* If we're a relay that got a connection, ask for authentication. */ const int send_chall = !started_here && public_server_mode(get_options()); /* If our certs cell will authenticate us, or if we have no intention of * authenticating, send a netinfo cell right now. */ const int send_netinfo = !(started_here && public_server_mode(get_options())); const int send_any = send_versions || send_certs || send_chall || send_netinfo; tor_assert(conn->link_proto >= 3); log_info(LD_OR, "Negotiated version %d with %s:%d; %s%s%s%s%s", highest_supported_version, safe_str_client(conn->_base.address), conn->_base.port, send_any ? "Sending cells:" : "Waiting for CERTS cell", send_versions ? " VERSIONS" : "", send_certs ? " CERTS" : "", send_chall ? " AUTH_CHALLENGE" : "", send_netinfo ? " NETINFO" : ""); #ifdef DISABLE_V3_LINKPROTO_SERVERSIDE if (1) { connection_mark_for_close(TO_CONN(conn)); return; } #endif if (send_versions) { if (connection_or_send_versions(conn, 1) < 0) { log_warn(LD_OR, "Couldn't send versions cell"); connection_mark_for_close(TO_CONN(conn)); return; } } if (send_certs) { if (connection_or_send_certs_cell(conn) < 0) { log_warn(LD_OR, "Couldn't send certs cell"); connection_mark_for_close(TO_CONN(conn)); return; } } if (send_chall) { if (connection_or_send_auth_challenge_cell(conn) < 0) { log_warn(LD_OR, "Couldn't send auth_challenge cell"); connection_mark_for_close(TO_CONN(conn)); return; } } if (send_netinfo) { if (connection_or_send_netinfo(conn) < 0) { log_warn(LD_OR, "Couldn't send netinfo cell"); connection_mark_for_close(TO_CONN(conn)); return; } } } }
/* Test good certs cells */ static void test_link_handshake_certs_ok(void *arg) { (void) arg; or_connection_t *c1 = or_connection_new(CONN_TYPE_OR, AF_INET); or_connection_t *c2 = or_connection_new(CONN_TYPE_OR, AF_INET); var_cell_t *cell1 = NULL, *cell2 = NULL; certs_cell_t *cc1 = NULL, *cc2 = NULL; channel_tls_t *chan1 = NULL, *chan2 = NULL; crypto_pk_t *key1 = NULL, *key2 = NULL; scheduler_init(); MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key); MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell); MOCK(connection_or_send_netinfo, mock_send_netinfo); key1 = pk_generate(2); key2 = pk_generate(3); /* We need to make sure that our TLS certificates are set up before we can * actually generate a CERTS cell. */ tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, key1, key2, 86400), ==, 0); c1->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; c1->link_proto = 3; tt_int_op(connection_init_or_handshake_state(c1, 1), ==, 0); c2->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; c2->link_proto = 3; tt_int_op(connection_init_or_handshake_state(c2, 0), ==, 0); tt_int_op(0, ==, connection_or_send_certs_cell(c1)); tt_assert(mock_got_var_cell); cell1 = mock_got_var_cell; tt_int_op(0, ==, connection_or_send_certs_cell(c2)); tt_assert(mock_got_var_cell); cell2 = mock_got_var_cell; tt_int_op(cell1->command, ==, CELL_CERTS); tt_int_op(cell1->payload_len, >, 1); tt_int_op(cell2->command, ==, CELL_CERTS); tt_int_op(cell2->payload_len, >, 1); tt_int_op(cell1->payload_len, ==, certs_cell_parse(&cc1, cell1->payload, cell1->payload_len)); tt_int_op(cell2->payload_len, ==, certs_cell_parse(&cc2, cell2->payload, cell2->payload_len)); tt_int_op(2, ==, cc1->n_certs); tt_int_op(2, ==, cc2->n_certs); tt_int_op(certs_cell_get_certs(cc1, 0)->cert_type, ==, CERTTYPE_RSA1024_ID_AUTH); tt_int_op(certs_cell_get_certs(cc1, 1)->cert_type, ==, CERTTYPE_RSA1024_ID_ID); tt_int_op(certs_cell_get_certs(cc2, 0)->cert_type, ==, CERTTYPE_RSA1024_ID_LINK); tt_int_op(certs_cell_get_certs(cc2, 1)->cert_type, ==, CERTTYPE_RSA1024_ID_ID); chan1 = tor_malloc_zero(sizeof(*chan1)); channel_tls_common_init(chan1); c1->chan = chan1; chan1->conn = c1; c1->base_.address = tor_strdup("C1"); c1->tls = tor_tls_new(-1, 0); c1->link_proto = 4; c1->base_.conn_array_index = -1; crypto_pk_get_digest(key2, c1->identity_digest); channel_tls_process_certs_cell(cell2, chan1); tt_assert(c1->handshake_state->received_certs_cell); tt_assert(c1->handshake_state->auth_cert == NULL); tt_assert(c1->handshake_state->id_cert); tt_assert(! tor_mem_is_zero( (char*)c1->handshake_state->authenticated_peer_id, 20)); chan2 = tor_malloc_zero(sizeof(*chan2)); channel_tls_common_init(chan2); c2->chan = chan2; chan2->conn = c2; c2->base_.address = tor_strdup("C2"); c2->tls = tor_tls_new(-1, 1); c2->link_proto = 4; c2->base_.conn_array_index = -1; crypto_pk_get_digest(key1, c2->identity_digest); channel_tls_process_certs_cell(cell1, chan2); tt_assert(c2->handshake_state->received_certs_cell); tt_assert(c2->handshake_state->auth_cert); tt_assert(c2->handshake_state->id_cert); tt_assert(tor_mem_is_zero( (char*)c2->handshake_state->authenticated_peer_id, 20)); done: UNMOCK(tor_tls_cert_matches_key); UNMOCK(connection_or_write_var_cell_to_buf); UNMOCK(connection_or_send_netinfo); connection_free_(TO_CONN(c1)); connection_free_(TO_CONN(c2)); tor_free(cell1); tor_free(cell2); certs_cell_free(cc1); certs_cell_free(cc2); if (chan1) circuitmux_free(chan1->base_.cmux); tor_free(chan1); if (chan2) circuitmux_free(chan2->base_.cmux); tor_free(chan2); crypto_pk_free(key1); crypto_pk_free(key2); }
/** Process a 'netinfo' cell: read and act on its contents, and set the * connection state to "open". */ static void command_process_netinfo_cell(cell_t *cell, or_connection_t *conn) { time_t timestamp; uint8_t my_addr_type; uint8_t my_addr_len; const uint8_t *my_addr_ptr; const uint8_t *cp, *end; uint8_t n_other_addrs; time_t now = time(NULL); long apparent_skew = 0; uint32_t my_apparent_addr = 0; if (conn->link_proto < 2) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Received a NETINFO cell on %s connection; dropping.", conn->link_proto == 0 ? "non-versioned" : "a v1"); return; } if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V2 && conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Received a NETINFO cell on non-handshaking connection; dropping."); return; } tor_assert(conn->handshake_state && conn->handshake_state->received_versions); if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) { tor_assert(conn->link_proto >= 3); if (conn->handshake_state->started_here) { if (!conn->handshake_state->authenticated) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Got a NETINFO cell from server, " "but no authentication. Closing the connection."); connection_mark_for_close(TO_CONN(conn)); return; } } else { /* we're the server. If the client never authenticated, we have some housekeeping to do.*/ if (!conn->handshake_state->authenticated) { tor_assert(tor_digest_is_zero( (const char*)conn->handshake_state->authenticated_peer_id)); connection_or_set_circid_type(conn, NULL); connection_or_init_conn_from_address(conn, &conn->_base.addr, conn->_base.port, (const char*)conn->handshake_state->authenticated_peer_id, 0); } } } /* Decode the cell. */ timestamp = ntohl(get_uint32(cell->payload)); if (labs(now - conn->handshake_state->sent_versions_at) < 180) { apparent_skew = now - timestamp; } my_addr_type = (uint8_t) cell->payload[4]; my_addr_len = (uint8_t) cell->payload[5]; my_addr_ptr = (uint8_t*) cell->payload + 6; end = cell->payload + CELL_PAYLOAD_SIZE; cp = cell->payload + 6 + my_addr_len; if (cp >= end) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Addresses too long in netinfo cell; closing connection."); connection_mark_for_close(TO_CONN(conn)); return; } else if (my_addr_type == RESOLVED_TYPE_IPV4 && my_addr_len == 4) { my_apparent_addr = ntohl(get_uint32(my_addr_ptr)); } n_other_addrs = (uint8_t) *cp++; while (n_other_addrs && cp < end-2) { /* Consider all the other addresses; if any matches, this connection is * "canonical." */ tor_addr_t addr; const uint8_t *next = decode_address_from_payload(&addr, cp, (int)(end-cp)); if (next == NULL) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Bad address in netinfo cell; closing connection."); connection_mark_for_close(TO_CONN(conn)); return; } if (tor_addr_eq(&addr, &conn->real_addr)) { conn->is_canonical = 1; break; } cp = next; --n_other_addrs; } /* Act on apparent skew. */ /** Warn when we get a netinfo skew with at least this value. */ #define NETINFO_NOTICE_SKEW 3600 if (labs(apparent_skew) > NETINFO_NOTICE_SKEW && router_get_by_id_digest(conn->identity_digest)) { char dbuf[64]; int severity; /*XXXX be smarter about when everybody says we are skewed. */ if (router_digest_is_trusted_dir(conn->identity_digest)) severity = LOG_WARN; else severity = LOG_INFO; format_time_interval(dbuf, sizeof(dbuf), apparent_skew); log_fn(severity, LD_GENERAL, "Received NETINFO cell with skewed time from " "server at %s:%d. It seems that our clock is %s by %s, or " "that theirs is %s. Tor requires an accurate clock to work: " "please check your time and date settings.", conn->_base.address, (int)conn->_base.port, apparent_skew>0 ? "ahead" : "behind", dbuf, apparent_skew>0 ? "behind" : "ahead"); if (severity == LOG_WARN) /* only tell the controller if an authority */ control_event_general_status(LOG_WARN, "CLOCK_SKEW SKEW=%ld SOURCE=OR:%s:%d", apparent_skew, conn->_base.address, conn->_base.port); } /* XXX maybe act on my_apparent_addr, if the source is sufficiently * trustworthy. */ (void)my_apparent_addr; if (connection_or_set_state_open(conn)<0) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Got good NETINFO cell from %s:%d; but " "was unable to make the OR connection become open.", safe_str_client(conn->_base.address), conn->_base.port); connection_mark_for_close(TO_CONN(conn)); } else { log_info(LD_OR, "Got good NETINFO cell from %s:%d; OR connection is now " "open, using protocol version %d. Its ID digest is %s", safe_str_client(conn->_base.address), conn->_base.port, (int)conn->link_proto, hex_str(conn->identity_digest, DIGEST_LEN)); } assert_connection_ok(TO_CONN(conn),time(NULL)); }
/** 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, remove the intro point we * just tried from our list of intro points for that service * descriptor. * - 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); 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). " "Removing from descriptor.", safe_str(ocirc->rend_data->onion_address), safe_str(build_state_get_exit_nickname(ocirc->build_state))); rend_client_remove_intro_point(ocirc->build_state->chosen_exit, ocirc->rend_data); } if (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); 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) 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); } 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; } } }