[tor-commits] [tor/master] Implement cert/auth cell reading

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


commit 6c7f28454e80da733e3bfb4f71101faf09b7ac24
Author: Nick Mathewson <nickm at torproject.org>
Date:   Tue Sep 13 16:24:49 2011 -0400

    Implement cert/auth cell reading
---
 src/or/command.c       |  278 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/or/connection_or.c |   24 ++---
 src/or/or.h            |   53 +++++++++
 3 files changed, 338 insertions(+), 17 deletions(-)

diff --git a/src/or/command.c b/src/or/command.c
index 72d8cd7..aad971f 100644
--- a/src/or/command.c
+++ b/src/or/command.c
@@ -660,3 +660,281 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn)
   assert_connection_ok(TO_CONN(conn),time(NULL));
 }
 
+/** Process a CERT cell from an OR connection.
+ *
+ * If the other side should not have sent us a CERT cell, or the cell is
+ * malformed, or it is supposed to authenticate the TLS key but it doesn't,
+ * then mark the connection.
+ *
+ * If the cell has a good cert chain and we're doing a v3 handshake, then
+ * store the certificates in or_handshake_state.  If this is the client side
+ * of the connection, we then authenticate the server or mark the connection.
+ * If it's the server side, wait for an AUTHENTICATE cell.
+ */
+static void
+command_process_cert_cell(var_cell_t *cell, or_connection_t *conn)
+{
+#define ERR(s)                                                  \
+  do {                                                          \
+    log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,                      \
+           "Received a bad CERT cell from %s:%d: %s",           \
+           conn->_base.address, conn->_base.port, (s));         \
+    connection_mark_for_close(TO_CONN(conn));                   \
+    goto err;                                                   \
+  } while (0)
+
+  tor_cert_t *link_cert = NULL;
+  tor_cert_t *id_cert = NULL;
+  tor_cert_t *auth_cert = NULL;
+
+  uint8_t *ptr;
+  int n_certs, i;
+
+  if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3)
+    ERR("We're not doing a v3 handshake!");
+  if (conn->handshake_state->received_cert_cell)
+    ERR("We already got one");
+  if (cell->payload_len < 1)
+    ERR("It had no body");
+  if (cell->circ_id)
+    ERR("It had a nonzero circuit ID");
+
+  n_certs = cell->payload[0];
+  ptr = cell->payload + 1;
+  for (i = 0; i < n_certs; ++i) {
+    uint8_t cert_type;
+    uint16_t cert_len;
+    if (ptr + 3 > cell->payload + cell->payload_len) {
+      goto truncated;
+    }
+    cert_type = *ptr;
+    cert_len = ntohs(get_uint16(ptr+1));
+    if (ptr + 3 + cert_len > cell->payload + cell->payload_len) {
+      goto truncated;
+    }
+    if (cert_type == OR_CERT_TYPE_TLS_LINK ||
+        cert_type == OR_CERT_TYPE_ID_1024 ||
+        cert_type == OR_CERT_TYPE_AUTH_1024) {
+      tor_cert_t *cert = tor_cert_decode(ptr + 3, cert_len);
+      if (!cert) {
+        log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+               "Received undecodable certificate in CERT cell from %s:%d",
+               conn->_base.address, conn->_base.port);
+      } else {
+        if (cert_type == OR_CERT_TYPE_TLS_LINK && !link_cert)
+          link_cert = cert;
+        else if (cert_type == OR_CERT_TYPE_ID_1024 && !id_cert)
+          id_cert = cert;
+        else if (cert_type == OR_CERT_TYPE_AUTH_1024 && !auth_cert)
+          auth_cert = cert;
+        else
+          tor_cert_free(cert);
+      }
+    }
+    ptr += 3 + cert_len;
+    continue;
+
+  truncated:
+    ERR("It ends in the middle of a certificate");
+  }
+
+  if (conn->handshake_state->started_here) {
+    if (! (id_cert && link_cert))
+      ERR("The certs we wanted were missing");
+    /* Okay. We should be able to check the certificates now. */
+    if (! tor_tls_cert_matches_key(conn->tls, link_cert)) {
+      ERR("The link certificate didn't match the TLS public key");
+    }
+    if (! tor_tls_cert_is_valid(link_cert, id_cert))
+      ERR("The link certificate was not valid");
+    if (! tor_tls_cert_is_valid(id_cert, id_cert))
+      ERR("The ID certificate was not valid");
+
+    /* XXXX  okay, we just got authentication.  Do something about that. */
+
+    conn->handshake_state->id_cert = id_cert;
+    id_cert = NULL;
+  } else {
+    if (! (id_cert && auth_cert))
+      ERR("The certs we wanted were missing");
+
+    /* Remember these certificates so we can check an AUTHENTICATE cell */
+    conn->handshake_state->id_cert = id_cert;
+    conn->handshake_state->auth_cert = auth_cert;
+    if (! tor_tls_cert_is_valid(auth_cert, id_cert))
+      ERR("The authentication certificate was not valid");
+    if (! tor_tls_cert_is_valid(id_cert, id_cert))
+      ERR("The ID certificate was not valid");
+
+    /* XXXX check more stuff? */
+
+    id_cert = auth_cert = NULL;
+  }
+
+  conn->handshake_state->received_cert_cell = 1;
+err:
+  tor_cert_free(id_cert);
+  tor_cert_free(link_cert);
+  tor_cert_free(auth_cert);
+#undef ERR
+}
+
+/** 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. */
+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", \
+           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->handshake_state->started_here)
+    ERR("We didn't originate this connection");
+  if (conn->handshake_state->received_auth_challenge)
+    ERR("We already received one");
+  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");
+
+  memcpy(conn->handshake_state->auth_challenge, cell->payload,
+         OR_AUTH_CHALLENGE_LEN);
+
+  /* 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;
+
+  /* Send back authentication if we want, and if use_type is set */
+#undef ERR
+}
+
+/** Process an AUTHENTICATE cell from an OR connection.
+ *
+ * If it's ill-formed or we weren't supposed to get one or we're not doing a
+ * v3 handshake, then mark the connection.  If it does not authenticate the
+ * other side of the connection successfully (because it isn't signed right,
+ * we didn't get a CERT cell, etc) mark the connection.  Otherwise, accept
+ * 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)
+{
+  uint8_t expected[V3_AUTH_FIXED_PART_LEN];
+  const uint8_t *auth;
+  int authlen;
+
+#define ERR(s)                                                  \
+  do {                                                          \
+    log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,                      \
+           "Received a bad AUTHETNICATE cell from %s:%d: %s",   \
+           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 doing a v3 handshake");
+  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->auth_cert == NULL)
+    ERR("We never got an authentication certificate");
+  if (cell->payload_len < 4)
+    ERR("Cell was way too short");
+
+  auth = cell->payload;
+  {
+    uint16_t type = ntohs(get_uint16(auth));
+    uint16_t len = ntohs(get_uint16(auth+2));
+    if (4 + len > cell->payload_len)
+      ERR("Authenticator was truncated");
+
+    if (type != AUTHTYPE_RSA_SHA256_TLSSECRET)
+      ERR("Authenticator type was not recognized");
+
+    auth += 4;
+    authlen = len;
+  }
+
+  if (authlen < V3_AUTH_BODY_LEN + 1)
+    ERR("Authenticator was too short");
+
+  if (connection_or_compute_authenticate_cell_body(
+                        conn, expected, sizeof(expected), NULL, 1) < 0)
+    ERR("Couldn't compute expected AUTHENTICATE cell body");
+
+  if (tor_memneq(expected, auth, sizeof(expected)))
+    ERR("Some field in the AUTHENTICATE cell body was not as expected");
+
+  {
+    crypto_pk_env_t *pk = tor_tls_cert_get_key(
+                                   conn->handshake_state->auth_cert);
+    char d[DIGEST256_LEN];
+    char *signed_data;
+    size_t keysize;
+    int signed_len;
+
+    crypto_digest256(d, (char*)auth, V3_AUTH_BODY_LEN, DIGEST_SHA256);
+
+    keysize = crypto_pk_keysize(pk);
+    signed_data = tor_malloc(keysize);
+    signed_len = crypto_pk_public_checksig(pk, signed_data, keysize,
+                                           (char*)auth + V3_AUTH_BODY_LEN,
+                                           authlen - V3_AUTH_BODY_LEN);
+    if (signed_len < 0) {
+      tor_free(signed_data);
+      ERR("Signature wasn't valid");
+    }
+    if (signed_len < DIGEST256_LEN) {
+      tor_free(signed_data);
+      ERR("Not enough data was signed");
+    }
+    if (tor_memneq(signed_data, d, DIGEST256_LEN)) {
+      tor_free(signed_data);
+      ERR("Signature did not match data to be signed.");
+    }
+    tor_free(signed_data);
+  }
+
+  /* XXXX we're authenticated.  Now remember the fact, and remember whom we're
+     authenticated to. */
+
+  conn->handshake_state->received_authenticate = 1;
+#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 c72d89d..93b0b3a 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -1761,10 +1761,6 @@ connection_or_send_netinfo(or_connection_t *conn)
   return 0;
 }
 
-/** DOCDOC */
-#define OR_CERT_TYPE_TLS_LINK 1
-#define OR_CERT_TYPE_ID_1024 2
-
 /** Send a CERT cell on the connection <b>conn</b>.  Return 0 on success, -1
  * on failure. */
 int
@@ -1846,23 +1842,17 @@ connection_or_send_auth_challenge_cell(or_connection_t *conn)
   return 0;
 }
 
-/** DOCDOC */
-#define V3_HS_AUTH_FIXED_PART_LEN (8+(32*6))
-#define V3_HS_AUTH_BODY_LEN (V3_HS_AUTH_FIXED_PART_LEN + 8 + 16)
-
-#define AUTHTYPE_RSA_SHA256_TLSSECRET 1
-
 /** Compute the main body of an AUTHENTICATE cell that a client can use
  * to authenticate itself on a v3 handshake for <b>conn</b>.  Write it to the
  * <b>outlen</b>-byte buffer at <b>out</b>.
  *
  * If <b>server</b> is true, only calculate the first
- * V3_HS_AUTH_FIXED_PART_LEN bytes -- the part of the authenticator that's
+ * V3_AUTH_FIXED_PART_LEN bytes -- the part of the authenticator that's
  * determined by the rest of the handshake, and which match the provided value
  * exactly.
  *
  * If <b>server</b> is false and <b>signing_key</b> is NULL, calculate the
- * first V3_HS_AUTH_BODY_LEN bytes of the authenticator (that is, everything
+ * first V3_AUTH_BODY_LEN bytes of the authenticator (that is, everything
  * that should be signed), but don't actually sign it.
  *
  * If <b>server</b> is false and <b>signing_key</b> is provided, calculate the
@@ -1878,8 +1868,8 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
 
   /* assert state is reasonable XXXX */
 
-  if (outlen < V3_HS_AUTH_FIXED_PART_LEN ||
-      (!server && outlen < V3_HS_AUTH_BODY_LEN))
+  if (outlen < V3_AUTH_FIXED_PART_LEN ||
+      (!server && outlen < V3_AUTH_BODY_LEN))
     return -1;
 
   ptr = out;
@@ -1950,7 +1940,7 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
   tor_tls_get_tlssecrets(conn->tls, ptr);
   ptr += 32;
 
-  tor_assert(ptr - out == V3_HS_AUTH_FIXED_PART_LEN);
+  tor_assert(ptr - out == V3_AUTH_FIXED_PART_LEN);
 
   if (server)
     return ptr-out;
@@ -1969,7 +1959,7 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
   crypto_rand((char*)ptr, 16);
   ptr += 16;
 
-  tor_assert(ptr - out == V3_HS_AUTH_BODY_LEN);
+  tor_assert(ptr - out == V3_AUTH_BODY_LEN);
 
   if (!signing_key)
     return ptr - out;
@@ -2004,7 +1994,7 @@ connection_or_send_authenticate_cell(or_connection_t *conn)
   if (!pk)
     return -1;/*XXXX log*/
   cell_maxlen = 4 + /* overhead */
-    V3_HS_AUTH_BODY_LEN + /* Authentication body */
+    V3_AUTH_BODY_LEN + /* Authentication body */
     crypto_pk_keysize(pk) + /* Max signature length */
     16 /* just in case XXXX */ ;
 
diff --git a/src/or/or.h b/src/or/or.h
index ebf9ab5..a40598f 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -1087,6 +1087,43 @@ typedef struct listener_connection_t {
 /** Minimum length of the random part of an AUTH_CHALLENGE cell. */
 #define OR_AUTH_CHALLENGE_LEN 32
 
+/**
+ * @name Certificate types for CERT cells.
+ *
+ * These values are defined by the protocol, and affect how an X509
+ * certificate in a CERT cell is interpreted and used.
+ *
+ * @{ */
+/** A certificate that authenticates a TLS link key.  The subject key
+ * must match the key used in the TLS handshake; it must be signed by
+ * the identity key. */
+#define OR_CERT_TYPE_TLS_LINK 1
+/** A self-signed identity certificate. The subject key must be a
+ * 1024-bit RSA key. */
+#define OR_CERT_TYPE_ID_1024 2
+/** A certificate that authenticates a key used in an AUTHENTICATE cell
+ * in the v3 handshake.  The subject key must be a 1024-bit RSA key; it
+ * must be signed by the identity key */
+#define OR_CERT_TYPE_AUTH_1024 3
+/**@}*/
+
+/** The one currently supported type of AUTHENTICATE cell.  It contains
+ * a bunch of structures signed with an RSA1024 key.  The signed
+ * structures include a HMAC using negotiated TLS secrets, and a digest
+ * of all cells sent or received before the AUTHENTICATE cell (including
+ * the random server-generated AUTH_CHALLENGE cell).
+ */
+#define AUTHTYPE_RSA_SHA256_TLSSECRET 1
+
+/** The length of the part of the AUTHENTICATE cell body that the client and
+ * server can generate independently (when using RSA_SHA256_TLSSECRET). It
+ * contains everything except the client's timestamp, the client's randomly
+ * generated nonce, and the signature. */
+#define V3_AUTH_FIXED_PART_LEN (8+(32*6))
+/** The length of the part of the AUTHENTICATE cell body that the client
+ * signs. */
+#define V3_AUTH_BODY_LEN (V3_AUTH_FIXED_PART_LEN + 8 + 16)
+
 /** Stores flags and information related to the portion of a v2/v3 Tor OR
  * connection handshake that happens after the TLS handshake is finished.
  */
@@ -1098,6 +1135,12 @@ typedef struct or_handshake_state_t {
   unsigned int started_here : 1;
   /** True iff we have received and processed a VERSIONS cell. */
   unsigned int received_versions : 1;
+  /** True iff we have received and processed an AUTH_CHALLENGE cell */
+  unsigned int received_auth_challenge : 1;
+  /** True iff we have received and processed a CERT cell. */
+  unsigned int received_cert_cell : 1;
+  /** True iff we have received and processed an AUTHENTICATE cell */
+  unsigned int received_authenticate : 1;
 
   /** Digests of the cells that we have sent or received as part of a V3
    * handshake.  Used for making and checking AUTHENTICATE cells.
@@ -1108,6 +1151,16 @@ typedef struct or_handshake_state_t {
   crypto_digest_env_t *digest_received;
   /** @} */
 
+  /** Certificates that a connection initiator sent us in a CERT cell; we're
+   * holding on to them until we get an AUTHENTICATE cell.
+   *
+   * @{
+   */
+  /** The cert for the key that's supposed to sign the AUTHENTICATE cell */
+  tor_cert_t *auth_cert;
+  /** A self-signed identity certificate */
+  tor_cert_t *id_cert;
+  /**@}*/
 } or_handshake_state_t;
 
 /** Subtype of connection_t for an "OR connection" -- that is, one that speaks





More information about the tor-commits mailing list