Esempio n. 1
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;
  }
}
Esempio n. 2
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 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;
      }
    }
  }
}