Hook up all of the prop176 code; allow v3 negotiations to actually work

This commit is contained in:
Nick Mathewson 2011-09-27 13:15:36 -04:00
parent 445f947890
commit 83bb9742b5
4 changed files with 435 additions and 102 deletions

View File

@ -46,6 +46,15 @@ uint64_t stats_n_versions_cells_processed = 0;
/** How many CELL_NETINFO cells have we received, ever? */
uint64_t stats_n_netinfo_cells_processed = 0;
/** How many CELL_VPADDING cells have we received, ever? */
uint64_t stats_n_vpadding_cells_processed = 0;
/** How many CELL_CERTS cells have we received, ever? */
uint64_t stats_n_cert_cells_processed = 0;
/** How many CELL_AUTH_CHALLENGE cells have we received, ever? */
uint64_t stats_n_auth_challenge_cells_processed = 0;
/** How many CELL_AUTHENTICATE cells have we received, ever? */
uint64_t stats_n_authenticate_cells_processed = 0;
/* These are the main functions for processing cells */
static void command_process_create_cell(cell_t *cell, or_connection_t *conn);
static void command_process_created_cell(cell_t *cell, or_connection_t *conn);
@ -54,6 +63,12 @@ static void command_process_destroy_cell(cell_t *cell, or_connection_t *conn);
static void command_process_versions_cell(var_cell_t *cell,
or_connection_t *conn);
static void command_process_netinfo_cell(cell_t *cell, or_connection_t *conn);
static void command_process_cert_cell(var_cell_t *cell,
or_connection_t *conn);
static void command_process_auth_challenge_cell(var_cell_t *cell,
or_connection_t *conn);
static void command_process_authenticate_cell(var_cell_t *cell,
or_connection_t *conn);
#ifdef KEEP_TIMING_STATS
/** This is a wrapper function around the actual function that processes the
@ -93,7 +108,7 @@ command_time_process_cell(cell_t *cell, or_connection_t *conn, int *time,
void
command_process_cell(cell_t *cell, or_connection_t *conn)
{
int handshaking = (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V2);
int handshaking = (conn->_base.state != OR_CONN_STATE_OPEN);
#ifdef KEEP_TIMING_STATS
/* how many of each cell have we seen so far this second? needs better
* name. */
@ -137,6 +152,10 @@ command_process_cell(cell_t *cell, or_connection_t *conn)
if (handshaking && cell->command != CELL_VERSIONS &&
cell->command != CELL_NETINFO)
return;
/* XXXX VERSIONS should be impossible; it's variable-length. */
if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3)
or_handshake_state_record_cell(conn->handshake_state, cell, 1);
switch (cell->command) {
case CELL_PADDING:
@ -206,15 +225,55 @@ command_process_var_cell(var_cell_t *cell, or_connection_t *conn)
}
#endif
/* reject all when not handshaking. */
if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V2)
switch (conn->_base.state)
{
case OR_CONN_STATE_OR_HANDSHAKING_V2:
if (cell->command != CELL_VERSIONS)
return;
break;
case OR_CONN_STATE_TLS_HANDSHAKING:
/* If we're using bufferevents, it's entirely possible for us to
* notice "hey, data arrived!" before we notice "hey, the handshake
* finished!" And we need to be accepting both at once to handle both
* the v2 and v3 handshakes. */
/* fall through */
case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING:
if (cell->command != CELL_VERSIONS)
return; /*XXXX023 log*/
break;
case OR_CONN_STATE_OR_HANDSHAKING_V3:
or_handshake_state_record_var_cell(conn->handshake_state, cell, 1);
break; /* Everything is allowed */
case OR_CONN_STATE_OPEN:
if (conn->link_proto < 3)
return;
default:
/*XXXX023 log */
return;
}
switch (cell->command) {
case CELL_VERSIONS:
++stats_n_versions_cells_processed;
PROCESS_CELL(versions, cell, conn);
break;
case CELL_VPADDING:
++stats_n_vpadding_cells_processed;
PROCESS_CELL(versions, cell, conn);
break;
case CELL_CERT:
++stats_n_cert_cells_processed;
PROCESS_CELL(cert, cell, conn);
break;
case CELL_AUTH_CHALLENGE:
++stats_n_auth_challenge_cells_processed;
PROCESS_CELL(auth_challenge, cell, conn);
break;
case CELL_AUTHENTICATE:
++stats_n_authenticate_cells_processed;
PROCESS_CELL(authenticate, cell, conn);
break;
default:
log_fn(LOG_INFO, LD_PROTOCOL,
"Variable-length cell of unknown type (%d) received.",
@ -504,14 +563,40 @@ 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->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V2 ||
(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:
break;
case OR_CONN_STATE_TLS_HANDSHAKING:
case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING:
if (started_here) {
log_fn(LOG_PROTOCOL_WARN, LD_OR,
"Received a versions 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;
}
or_handshake_state_record_var_cell(conn->handshake_state, cell, 1);
break;
case OR_CONN_STATE_OR_HANDSHAKING_V3:
break;
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) {
@ -533,20 +618,81 @@ command_process_versions_cell(var_cell_t *cell, or_connection_t *conn)
"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);
tor_assert(conn->link_proto >= 2);
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_versions ? " AUTH_CHALLENGE" : "",
send_netinfo ? " NETINFO" : "");
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_cert_cell(conn) < 0) {
log_warn(LD_OR, "Couldn't send cert 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;
}
}
}
}
/** Process a 'netinfo' cell: read and act on its contents, and set the
@ -571,13 +717,26 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn)
conn->link_proto == 0 ? "non-versioned" : "a v1");
return;
}
if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V2) {
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));
}
}
}
/* Decode the cell. */
timestamp = ntohl(get_uint32(cell->payload));
if (labs(now - conn->handshake_state->sent_versions_at) < 180) {
@ -692,8 +851,14 @@ command_process_cert_cell(var_cell_t *cell, or_connection_t *conn)
if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3)
ERR("We're not doing a v3 handshake!");
if (conn->link_proto < 3)
ERR("We're not using link protocol >= 3");
if (conn->handshake_state->received_cert_cell)
ERR("We already got one");
if (conn->handshake_state->authenticated) {
/* Should be unreachable, but let's make sure. */
ERR("We're already authenticated!");
}
if (cell->payload_len < 1)
ERR("It had no body");
if (cell->circ_id)
@ -750,7 +915,20 @@ command_process_cert_cell(var_cell_t *cell, or_connection_t *conn)
if (! tor_tls_cert_is_valid(id_cert, id_cert, 1))
ERR("The ID certificate was not valid");
/* XXXX okay, we just got authentication. Do something about that. */
conn->handshake_state->authenticated = 1;
{
crypto_pk_env_t *identity_rcvd = tor_tls_cert_get_key(id_cert);
const digests_t *id_digests = tor_cert_get_id_digests(id_cert);
memcpy(conn->handshake_state->authenticated_peer_id,
id_digests->d[DIGEST_SHA1], DIGEST_LEN);
connection_or_set_circid_type(conn, identity_rcvd);
crypto_free_pk_env(identity_rcvd);
}
if (connection_or_client_learned_peer_id(conn,
conn->handshake_state->authenticated_peer_id) < 0)
ERR("Problem setting or checking peer id");
conn->handshake_state->id_cert = id_cert;
id_cert = NULL;
@ -803,10 +981,14 @@ command_process_auth_challenge_cell(var_cell_t *cell, or_connection_t *conn)
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_cert_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)
@ -829,7 +1011,19 @@ command_process_auth_challenge_cell(var_cell_t *cell, or_connection_t *conn)
conn->handshake_state->received_auth_challenge = 1;
/* Send back authentication if we want, and if use_type is set */
if (use_type && public_server_mode(get_options())) {
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;
}
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
}
@ -842,7 +1036,7 @@ command_process_auth_challenge_cell(var_cell_t *cell, or_connection_t *conn)
* the identity of the router on the other side of the connection.
*/
static void
command_process_authenticate_cell(or_connection_t *conn, var_cell_t *cell)
command_process_authenticate_cell(var_cell_t *cell, or_connection_t *conn)
{
uint8_t expected[V3_AUTH_FIXED_PART_LEN];
const uint8_t *auth;
@ -859,12 +1053,22 @@ command_process_authenticate_cell(or_connection_t *conn, var_cell_t *cell)
if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3)
ERR("We're not 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 originated this connection");
if (conn->handshake_state->received_authenticate)
ERR("We already got one!");
if (conn->handshake_state->authenticated) {
/* Should be impossible given other checks */
ERR("The peer is already authenticated");
}
if (! conn->handshake_state->received_cert_cell)
ERR("We never got a cert cell");
if (conn->handshake_state->auth_cert == NULL)
ERR("We never got an authentication certificate");
if (conn->handshake_state->id_cert == NULL)
ERR("We never got an identity certificate");
if (cell->payload_len < 4)
ERR("Cell was way too short");
@ -915,6 +1119,8 @@ command_process_authenticate_cell(or_connection_t *conn, var_cell_t *cell)
tor_free(signed_data);
ERR("Not enough data was signed");
}
/* Note that we deliberately allow *more* than DIGEST256_LEN bytes here,
* in case they're later used to hold a SHA3 digest or something. */
if (tor_memneq(signed_data, d, DIGEST256_LEN)) {
tor_free(signed_data);
ERR("Signature did not match data to be signed.");
@ -922,19 +1128,28 @@ command_process_authenticate_cell(or_connection_t *conn, var_cell_t *cell)
tor_free(signed_data);
}
/* XXXX we're authenticated. Now remember the fact, and remember whom we're
authenticated to. */
/* Okay, we are authenticated. */
conn->handshake_state->received_authenticate = 1;
conn->handshake_state->authenticated = 1;
{
crypto_pk_env_t *identity_rcvd =
tor_tls_cert_get_key(conn->handshake_state->id_cert);
const digests_t *id_digests =
tor_cert_get_id_digests(conn->handshake_state->id_cert);
memcpy(conn->handshake_state->authenticated_peer_id,
id_digests->d[DIGEST_SHA1], DIGEST_LEN);
connection_or_set_circid_type(conn, identity_rcvd);
crypto_free_pk_env(identity_rcvd);
connection_or_init_conn_from_address(conn,
&conn->_base.addr,
conn->_base.port,
(const char*)conn->handshake_state->authenticated_peer_id,
0);
}
#undef ERR
}
void dummy_function(void);
void dummy_function(void)
{
/* this is only here to avoid 'static function isn't used' warnings */
command_process_auth_challenge_cell(NULL, NULL);
command_process_cert_cell(NULL, NULL);
command_process_authenticate_cell(NULL, NULL);
}

View File

@ -35,10 +35,8 @@
#endif
static int connection_tls_finish_handshake(or_connection_t *conn);
static int connection_or_launch_v3_or_handshake(or_connection_t *conn);
static int connection_or_process_cells_from_inbuf(or_connection_t *conn);
static int connection_or_send_versions(or_connection_t *conn);
static int connection_init_or_handshake_state(or_connection_t *conn,
int started_here);
static int connection_or_check_valid_tls_handshake(or_connection_t *conn,
int started_here,
char *digest_rcvd_out);
@ -388,6 +386,7 @@ connection_or_process_inbuf(or_connection_t *conn)
#endif
case OR_CONN_STATE_OPEN:
case OR_CONN_STATE_OR_HANDSHAKING_V2:
case OR_CONN_STATE_OR_HANDSHAKING_V3:
return connection_or_process_cells_from_inbuf(conn);
default:
return 0; /* don't do anything */
@ -627,7 +626,7 @@ connection_or_update_token_buckets(smartlist_t *conns,
/** If we don't necessarily know the router we're connecting to, but we
* have an addr/port/id_digest, then fill in as much as we can. Start
* by checking to see if this describes a router we know. */
static void
void
connection_or_init_conn_from_address(or_connection_t *conn,
const tor_addr_t *addr, uint16_t port,
const char *id_digest,
@ -1180,16 +1179,22 @@ connection_tls_continue_handshake(or_connection_t *conn)
if (! tor_tls_used_v1_handshake(conn->tls)) {
if (!tor_tls_is_server(conn->tls)) {
if (conn->_base.state == OR_CONN_STATE_TLS_HANDSHAKING) {
if (tor_tls_received_v3_certificate(conn->tls)) {
log_notice(LD_OR, "Client got a v3 cert! Moving on to v3 "
"handshake.");
return connection_or_launch_v3_or_handshake(conn);
} else {
log_debug(LD_OR, "Done with initial SSL handshake (client-side). "
"Requesting renegotiation.");
conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING;
goto again;
}
}
// log_notice(LD_OR,"Done. state was %d.", conn->_base.state);
} else {
/* improved handshake, but not a client. */
/* v2/v3 handshake, but not a client. */
log_debug(LD_OR, "Done with initial SSL handshake (server-side). "
"Expecting renegotiation.");
"Expecting renegotiation or VERSIONS cell");
tor_tls_set_renegotiate_callback(conn->tls,
connection_or_tls_renegotiated_cb,
conn);
@ -1234,6 +1239,12 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event,
if (! tor_tls_used_v1_handshake(conn->tls)) {
if (!tor_tls_is_server(conn->tls)) {
if (conn->_base.state == OR_CONN_STATE_TLS_HANDSHAKING) {
if (tor_tls_received_v3_certificate(conn->tls)) {
log_notice(LD_OR, "Client got a v3 cert!");
if (connection_or_launch_v3_or_handshake(conn) < 0)
connection_mark_for_close(TO_CONN(conn));
return;
} else {
conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING;
tor_tls_unblock_renegotiation(conn->tls);
if (bufferevent_ssl_renegotiate(conn->_base.bufev)<0) {
@ -1243,8 +1254,9 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event,
tor_tls_unblock_renegotiation(conn->tls);
return; /* ???? */
}
}
} else if (tor_tls_get_num_server_handshakes(conn->tls) == 1) {
/* improved handshake, as a server. Only got one handshake, so
/* v2 or v3 handshake, as a server. Only got one handshake, so
* wait for the next one. */
tor_tls_set_renegotiate_callback(conn->tls,
connection_or_tls_renegotiated_cb,
@ -1256,7 +1268,7 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event,
const int handshakes = tor_tls_get_num_server_handshakes(conn->tls);
tor_assert(handshakes >= 2);
if (handshakes == 2) {
/* improved handshake, as a server. Two handshakes happened already,
/* v2 handshake, as a server. Two handshakes happened already,
* so we treat renegotiation as done.
*/
connection_or_tls_renegotiated_cb(conn->tls, conn);
@ -1300,6 +1312,29 @@ connection_or_nonopen_was_started_here(or_connection_t *conn)
return !tor_tls_is_server(conn->tls);
}
/** Set the circid_type field of <b>conn</b> (which determines which part of
* the circuit ID space we're willing to use) based on comparing our ID to
* <b>identity_rcvd</b> */
void
connection_or_set_circid_type(or_connection_t *conn,
crypto_pk_env_t *identity_rcvd)
{
const int started_here = connection_or_nonopen_was_started_here(conn);
crypto_pk_env_t *our_identity =
started_here ? get_tlsclient_identity_key() :
get_server_identity_key();
if (identity_rcvd) {
if (crypto_pk_cmp_keys(our_identity, identity_rcvd)<0) {
conn->circ_id_type = CIRC_ID_TYPE_LOWER;
} else {
conn->circ_id_type = CIRC_ID_TYPE_HIGHER;
}
} else {
conn->circ_id_type = CIRC_ID_TYPE_NEITHER;
}
}
/** <b>Conn</b> just completed its handshake. Return 0 if all is well, and
* return -1 if he is lying, broken, or otherwise something is wrong.
*
@ -1337,10 +1372,7 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn,
started_here ? conn->_base.address :
safe_str_client(conn->_base.address);
const char *conn_type = started_here ? "outgoing" : "incoming";
crypto_pk_env_t *our_identity =
started_here ? get_tlsclient_identity_key() :
get_server_identity_key();
int has_cert = 0, has_identity=0;
int has_cert = 0;
check_no_tls_errors();
has_cert = tor_tls_peer_has_cert(conn->tls);
@ -1375,21 +1407,46 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn,
}
if (identity_rcvd) {
has_identity = 1;
crypto_pk_get_digest(identity_rcvd, digest_rcvd_out);
if (crypto_pk_cmp_keys(our_identity, identity_rcvd)<0) {
conn->circ_id_type = CIRC_ID_TYPE_LOWER;
} else {
conn->circ_id_type = CIRC_ID_TYPE_HIGHER;
}
crypto_free_pk_env(identity_rcvd);
} else {
memset(digest_rcvd_out, 0, DIGEST_LEN);
conn->circ_id_type = CIRC_ID_TYPE_NEITHER;
}
if (started_here && tor_digest_is_zero(conn->identity_digest)) {
connection_or_set_identity_digest(conn, digest_rcvd_out);
connection_or_set_circid_type(conn, identity_rcvd);
crypto_free_pk_env(identity_rcvd);
if (started_here)
return connection_or_client_learned_peer_id(conn,
(const uint8_t*)digest_rcvd_out);
return 0;
}
/** Called when we (as a connection initiator) have definitively,
* authenticatedly, learned that ID of the Tor instance on the other
* side of <b>conn</b> is <b>peer_id</b>. For v1 and v2 handshakes,
* this is right after we get a certificate chain in a TLS handshake
* or renegotiation. For v3 handshakes, this is right after we get a
* certificate chain in a CERT cell.
*
* If we want any particular ID before, record the one we got.
*
* If we wanted an ID, but we didn't get it, log a warning and return -1.
*
* If we're testing reachability, remember what we learned.
*
* Return 0 on success, -1 on failure.
*/
int
connection_or_client_learned_peer_id(or_connection_t *conn,
const uint8_t *peer_id)
{
int as_expected = 1;
const or_options_t *options = get_options();
int severity = server_mode(options) ? LOG_PROTOCOL_WARN : LOG_WARN;
if (tor_digest_is_zero(conn->identity_digest)) {
connection_or_set_identity_digest(conn, (const char*)peer_id);
tor_free(conn->nickname);
conn->nickname = tor_malloc(HEX_DIGEST_LEN+2);
conn->nickname[0] = '$';
@ -1401,18 +1458,14 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn,
/* if it's a bridge and we didn't know its identity fingerprint, now
* we do -- remember it for future attempts. */
learned_router_identity(&conn->_base.addr, conn->_base.port,
digest_rcvd_out);
(const char*)peer_id);
}
if (started_here) {
int as_advertised = 1;
tor_assert(has_cert);
tor_assert(has_identity);
if (tor_memneq(digest_rcvd_out, conn->identity_digest, DIGEST_LEN)) {
if (tor_memneq(peer_id, conn->identity_digest, DIGEST_LEN)) {
/* I was aiming for a particular digest. I didn't get it! */
char seen[HEX_DIGEST_LEN+1];
char expected[HEX_DIGEST_LEN+1];
base16_encode(seen, sizeof(seen), digest_rcvd_out, DIGEST_LEN);
base16_encode(seen, sizeof(seen), (const char*)peer_id, DIGEST_LEN);
base16_encode(expected, sizeof(expected), conn->identity_digest,
DIGEST_LEN);
log_fn(severity, LD_HANDSHAKE,
@ -1425,19 +1478,19 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn,
END_OR_CONN_REASON_OR_IDENTITY);
if (!authdir_mode_tests_reachability(options))
control_event_bootstrap_problem("foo", END_OR_CONN_REASON_OR_IDENTITY);
as_advertised = 0;
as_expected = 0;
}
if (authdir_mode_tests_reachability(options)) {
dirserv_orconn_tls_done(conn->_base.address, conn->_base.port,
digest_rcvd_out, as_advertised);
(const char*)peer_id, as_expected);
}
if (!as_advertised)
if (!as_expected)
return -1;
}
return 0;
}
/** The tls handshake is finished.
/** The v1/v2 TLS handshake is finished.
*
* Make sure we are happy with the person we just handshaked with.
*
@ -1447,6 +1500,8 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn,
* If all is successful, call circuit_n_conn_done() to handle events
* that have been pending on the <tls handshake completion. Also set the
* directory to be dirty (only matters if I'm an authdirserver).
*
* If this is a v2 TLS handshake, send a versions cell.
*/
static int
connection_tls_finish_handshake(or_connection_t *conn)
@ -1483,13 +1538,35 @@ connection_tls_finish_handshake(or_connection_t *conn)
connection_or_init_conn_from_address(conn, &conn->_base.addr,
conn->_base.port, digest_rcvd, 0);
}
return connection_or_send_versions(conn);
return connection_or_send_versions(conn, 0);
}
}
/**
* Called as client when initial TLS handshake is done, and we notice
* that we got a v3-handshake signalling certificate from the server.
* Set up structures, do bookkeeping, and send the versions cell.
* Return 0 on success and -1 on failure.
*/
static int
connection_or_launch_v3_or_handshake(or_connection_t *conn)
{
tor_assert(connection_or_nonopen_was_started_here(conn));
tor_assert(tor_tls_received_v3_certificate(conn->tls));
circuit_build_times_network_is_live(&circ_times);
conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
if (connection_init_or_handshake_state(conn, 1) < 0)
return -1;
return connection_or_send_versions(conn, 1);
}
/** Allocate a new connection handshake state for the connection
* <b>conn</b>. Return 0 on success, -1 on failure. */
static int
int
connection_init_or_handshake_state(or_connection_t *conn, int started_here)
{
or_handshake_state_t *s;
@ -1639,6 +1716,9 @@ connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn)
connection_write_to_buf(networkcell.body, CELL_NETWORK_SIZE, TO_CONN(conn));
if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3)
or_handshake_state_record_cell(conn->handshake_state, cell, 0);
if (cell->command != CELL_PADDING)
conn->timestamp_last_added_nonpadding = approx_time();
}
@ -1658,6 +1738,8 @@ connection_or_write_var_cell_to_buf(const var_cell_t *cell,
connection_write_to_buf(hdr, sizeof(hdr), TO_CONN(conn));
connection_write_to_buf((char*)cell->payload,
cell->payload_len, TO_CONN(conn));
if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3)
or_handshake_state_record_var_cell(conn->handshake_state, cell, 0);
if (cell->command != CELL_PADDING)
conn->timestamp_last_added_nonpadding = approx_time();
}
@ -1742,7 +1824,7 @@ connection_or_send_destroy(circid_t circ_id, or_connection_t *conn, int reason)
}
/** Array of recognized link protocol versions. */
static const uint16_t or_protocol_versions[] = { 1, 2 };
static const uint16_t or_protocol_versions[] = { 1, 2, 3 };
/** Number of versions in <b>or_protocol_versions</b>. */
static const int n_or_protocol_versions =
(int)( sizeof(or_protocol_versions)/sizeof(uint16_t) );
@ -1761,20 +1843,33 @@ is_or_protocol_version_known(uint16_t v)
}
/** Send a VERSIONS cell on <b>conn</b>, telling the other host about the
* link protocol versions that this Tor can support. */
static int
connection_or_send_versions(or_connection_t *conn)
* link protocol versions that this Tor can support.
*
* If <b>v3_plus</b>, this is part of a V3 protocol handshake, so only
* allow protocol version v3 or later. If not <b>v3_plus</b>, this is
* not part of a v3 protocol handshake, so don't allow protocol v3 or
* later.
**/
int
connection_or_send_versions(or_connection_t *conn, int v3_plus)
{
var_cell_t *cell;
int i;
int n_versions = 0;
const int min_version = v3_plus ? 3 : 0;
const int max_version = v3_plus ? UINT16_MAX : 2;
tor_assert(conn->handshake_state &&
!conn->handshake_state->sent_versions_at);
cell = var_cell_new(n_or_protocol_versions * 2);
cell->command = CELL_VERSIONS;
for (i = 0; i < n_or_protocol_versions; ++i) {
uint16_t v = or_protocol_versions[i];
set_uint16(cell->payload+(2*i), htons(v));
if (v < min_version || v > max_version)
continue;
set_uint16(cell->payload+(2*n_versions), htons(v));
++n_versions;
}
cell->payload_len = n_versions * 2;
connection_or_write_var_cell_to_buf(cell, conn);
conn->handshake_state->sent_versions_at = time(NULL);
@ -2047,7 +2142,7 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
/** Send an AUTHENTICATE cell on the connection <b>conn</b>. Return 0 on
* success, -1 on failure */
int
connection_or_send_authenticate_cell(or_connection_t *conn)
connection_or_send_authenticate_cell(or_connection_t *conn, int authtype)
{
var_cell_t *cell;
crypto_pk_env_t *pk = tor_tls_get_my_client_auth_key();
@ -2057,6 +2152,9 @@ connection_or_send_authenticate_cell(or_connection_t *conn)
if (!pk)
return -1;/*XXXX log*/
if (authtype != AUTHTYPE_RSA_SHA256_TLSSECRET)
return -1;/*XXXX log*/
cell_maxlen = 4 + /* overhead */
V3_AUTH_BODY_LEN + /* Authentication body */
crypto_pk_keysize(pk) + /* Max signature length */

View File

@ -41,6 +41,15 @@ void connection_or_report_broken_states(int severity, int domain);
int connection_tls_start_handshake(or_connection_t *conn, int receiving);
int connection_tls_continue_handshake(or_connection_t *conn);
int connection_init_or_handshake_state(or_connection_t *conn, int started_here);
void connection_or_init_conn_from_address(or_connection_t *conn,
const tor_addr_t *addr, uint16_t port,
const char *id_digest,
int started_here);
int connection_or_client_learned_peer_id(or_connection_t *conn,
const uint8_t *peer_id);
void connection_or_set_circid_type(or_connection_t *conn,
crypto_pk_env_t *identity_rcvd);
void or_handshake_state_free(or_handshake_state_t *state);
void or_handshake_state_record_cell(or_handshake_state_t *state,
const cell_t *cell,
@ -56,6 +65,7 @@ void connection_or_write_var_cell_to_buf(const var_cell_t *cell,
or_connection_t *conn);
int connection_or_send_destroy(circid_t circ_id, or_connection_t *conn,
int reason);
int connection_or_send_versions(or_connection_t *conn, int v3_plus);
int connection_or_send_netinfo(or_connection_t *conn);
int connection_or_send_cert_cell(or_connection_t *conn);
int connection_or_send_auth_challenge_cell(or_connection_t *conn);
@ -63,7 +73,7 @@ int connection_or_compute_authenticate_cell_body(or_connection_t *conn,
uint8_t *out, size_t outlen,
crypto_pk_env_t *signing_key,
int server);
int connection_or_send_authenticate_cell(or_connection_t *conn);
int connection_or_send_authenticate_cell(or_connection_t *conn, int type);
int is_or_protocol_version_known(uint16_t version);

View File

@ -1142,6 +1142,16 @@ typedef struct or_handshake_state_t {
/** True iff we have received and processed an AUTHENTICATE cell */
unsigned int received_authenticate : 1;
/* True iff we've received valid authentication to some identity. */
unsigned int authenticated : 1;
/** Identity digest that we have received and authenticated for our peer
* on this connection. */
uint8_t authenticated_peer_id[DIGEST_LEN];
/** DOCDOC */
uint8_t auth_challenge[OR_AUTH_CHALLENGE_LEN];
/** Digests of the cells that we have sent or received as part of a V3
* handshake. Used for making and checking AUTHENTICATE cells.
*