[tor-commits] [tor/master] Hook up all of the prop176 code; allow v3 negotiations to actually work

nickm at torproject.org nickm at torproject.org
Tue Oct 11 03:22:16 UTC 2011


commit 83bb9742b541ff53e72c407f45093c137e13a073
Author: Nick Mathewson <nickm at torproject.org>
Date:   Tue Sep 27 13:15:36 2011 -0400

    Hook up all of the prop176 code; allow v3 negotiations to actually work
---
 src/or/command.c       |  273 ++++++++++++++++++++++++++++++++++++++++++-----
 src/or/connection_or.c |  242 ++++++++++++++++++++++++++++++-------------
 src/or/connection_or.h |   12 ++-
 src/or/or.h            |   10 ++
 4 files changed, 435 insertions(+), 102 deletions(-)

diff --git a/src/or/command.c b/src/or/command.c
index a32671f..a8e68e1 100644
--- a/src/or/command.c
+++ b/src/or/command.c
@@ -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)
-    return;
+  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,19 +618,80 @@ 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;
 
-  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 (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;
+    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;
+      }
+    }
   }
 }
 
@@ -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);
-}
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index 4caa3d3..a391ca7 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -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) {
-            log_debug(LD_OR, "Done with initial SSL handshake (client-side). "
-                             "Requesting renegotiation.");
-            conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING;
-            goto again;
+            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,17 +1239,24 @@ 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) {
-          conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING;
-          tor_tls_unblock_renegotiation(conn->tls);
-          if (bufferevent_ssl_renegotiate(conn->_base.bufev)<0) {
-            log_warn(LD_OR, "Start_renegotiating went badly.");
-            connection_mark_for_close(TO_CONN(conn));
+          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) {
+              log_warn(LD_OR, "Start_renegotiating went badly.");
+              connection_mark_for_close(TO_CONN(conn));
+            }
+            tor_tls_unblock_renegotiation(conn->tls);
+            return; /* ???? */
           }
-          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,43 +1458,39 @@ 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)) {
-      /* 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(expected, sizeof(expected), conn->identity_digest,
-                    DIGEST_LEN);
-      log_fn(severity, LD_HANDSHAKE,
-             "Tried connecting to router at %s:%d, but identity key was not "
-             "as expected: wanted %s but got %s.",
-             conn->_base.address, conn->_base.port, expected, seen);
-      entry_guard_register_connect_status(conn->identity_digest, 0, 1,
-                                          time(NULL));
-      control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED,
-              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;
-    }
-    if (authdir_mode_tests_reachability(options)) {
-      dirserv_orconn_tls_done(conn->_base.address, conn->_base.port,
-                              digest_rcvd_out, as_advertised);
-    }
-    if (!as_advertised)
-      return -1;
+  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), (const char*)peer_id, DIGEST_LEN);
+    base16_encode(expected, sizeof(expected), conn->identity_digest,
+                  DIGEST_LEN);
+    log_fn(severity, LD_HANDSHAKE,
+           "Tried connecting to router at %s:%d, but identity key was not "
+           "as expected: wanted %s but got %s.",
+           conn->_base.address, conn->_base.port, expected, seen);
+    entry_guard_register_connect_status(conn->identity_digest, 0, 1,
+                                        time(NULL));
+    control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED,
+                                 END_OR_CONN_REASON_OR_IDENTITY);
+    if (!authdir_mode_tests_reachability(options))
+      control_event_bootstrap_problem("foo", END_OR_CONN_REASON_OR_IDENTITY);
+    as_expected = 0;
+  }
+  if (authdir_mode_tests_reachability(options)) {
+    dirserv_orconn_tls_done(conn->_base.address, conn->_base.port,
+                            (const char*)peer_id, as_expected);
   }
+  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 */
diff --git a/src/or/connection_or.h b/src/or/connection_or.h
index a4d3be0..de3a989 100644
--- a/src/or/connection_or.h
+++ b/src/or/connection_or.h
@@ -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);
 
diff --git a/src/or/or.h b/src/or/or.h
index a40598f..887dcb6 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -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.
    *





More information about the tor-commits mailing list