/** 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; } }
/** 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; } } } }
/** 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 }