[or-cvs] r12396: Parse CERT cells and act correctly when we get them. (in tor/trunk: . doc src/common src/or)

nickm at seul.org nickm at seul.org
Tue Nov 6 18:00:07 UTC 2007


Author: nickm
Date: 2007-11-06 13:00:07 -0500 (Tue, 06 Nov 2007)
New Revision: 12396

Modified:
   tor/trunk/
   tor/trunk/doc/TODO
   tor/trunk/src/common/crypto.c
   tor/trunk/src/common/crypto.h
   tor/trunk/src/common/tortls.c
   tor/trunk/src/common/tortls.h
   tor/trunk/src/or/command.c
   tor/trunk/src/or/connection_or.c
Log:
 r16455 at catbus:  nickm | 2007-11-06 12:48:00 -0500
 Parse CERT cells and act correctly when we get them.



Property changes on: tor/trunk
___________________________________________________________________
 svk:merge ticket from /tor/trunk [r16455] on 8246c3cf-6607-4228-993b-4d95d33730f1

Modified: tor/trunk/doc/TODO
===================================================================
--- tor/trunk/doc/TODO	2007-11-06 14:21:08 UTC (rev 12395)
+++ tor/trunk/doc/TODO	2007-11-06 18:00:07 UTC (rev 12396)
@@ -34,16 +34,16 @@
         o Add parse logic
         o Make CERT variable.
         o Make VERSIONS variable.
-      - CERT cells
-        - functions to parse x509 certs
-        - functions to validate a single x509 cert against a TLS connection
-        - functions to validate a chain of x509 certs, and extract a PK.
+      o CERT cells
+        o functions to parse x509 certs
+        o functions to validate a single x509 cert against a TLS connection
+        o functions to validate a chain of x509 certs, and extract a PK.
         o function to encode x509 certs
         o Parse CERT cells
         o Generate CERT cells
         o Keep copies of X509 certs around, not necessarily associated with
           connection.
-      - LINK_AUTH cells
+      . LINK_AUTH cells
         o Code to generate
           o Remember certificate digests from TLS
         o Code to parse and check

Modified: tor/trunk/src/common/crypto.c
===================================================================
--- tor/trunk/src/common/crypto.c	2007-11-06 14:21:08 UTC (rev 12395)
+++ tor/trunk/src/common/crypto.c	2007-11-06 18:00:07 UTC (rev 12396)
@@ -246,6 +246,17 @@
   return env;
 }
 
+/** used by tortls.c: wrap the RSA from an evp_pkey in a crypto_pk_env_t.
+ * returns NULL if this isn't an RSA key. */
+crypto_pk_env_t *
+_crypto_new_pk_env_evp_pkey(EVP_PKEY *pkey)
+{
+  RSA *rsa;
+  if (!(rsa = EVP_PKEY_get1_RSA(pkey)))
+    return NULL;
+  return _crypto_new_pk_env_rsa(rsa);
+}
+
 /** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_env_t.  Iff
  * private is set, include the private-key portion of the key. */
 EVP_PKEY *

Modified: tor/trunk/src/common/crypto.h
===================================================================
--- tor/trunk/src/common/crypto.h	2007-11-06 14:21:08 UTC (rev 12395)
+++ tor/trunk/src/common/crypto.h	2007-11-06 18:00:07 UTC (rev 12396)
@@ -195,6 +195,7 @@
 struct evp_pkey_st;
 struct dh_st;
 crypto_pk_env_t *_crypto_new_pk_env_rsa(struct rsa_st *rsa);
+crypto_pk_env_t *_crypto_new_pk_env_evp_pkey(struct evp_pkey_st *pkey);
 struct evp_pkey_st *_crypto_pk_env_get_evp_pkey(crypto_pk_env_t *env,
                                                 int private);
 struct dh_st *_crypto_dh_env_get_dh(crypto_dh_env_t *dh);

Modified: tor/trunk/src/common/tortls.c
===================================================================
--- tor/trunk/src/common/tortls.c	2007-11-06 14:21:08 UTC (rev 12395)
+++ tor/trunk/src/common/tortls.c	2007-11-06 18:00:07 UTC (rev 12396)
@@ -815,27 +815,22 @@
     tor_free(s2);
 }
 
-/** If the provided tls connection is authenticated and has a
- * certificate that is currently valid and signed, then set
- * *<b>identity_key</b> to the identity certificate's key and return
- * 0.  Else, return -1 and log complaints with log-level <b>severity</b>.
- */
-int
-tor_tls_verify_v1(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key)
+/** DOCDOC helper.
+ * cert_out needs to be freed. id_cert_out doesn't. */
+static void
+try_to_extract_certs_from_tls(int severity, tor_tls_t *tls,
+                              X509 **cert_out, X509 **id_cert_out)
 {
   X509 *cert = NULL, *id_cert = NULL;
   STACK_OF(X509) *chain = NULL;
-  EVP_PKEY *id_pkey = NULL;
-  RSA *rsa;
-  int num_in_chain;
-  int r = -1, i;
+  int num_in_chain, i;
+  *cert_out = *id_cert_out = NULL;
 
-  *identity_key = NULL;
-
   if (!(cert = SSL_get_peer_certificate(tls->ssl)))
-    goto done;
+    return;
+  *cert_out = cert;
   if (!(chain = SSL_get_peer_cert_chain(tls->ssl)))
-    goto done;
+    return;
   num_in_chain = sk_X509_num(chain);
   /* 1 means we're receiving (server-side), and it's just the id_cert.
    * 2 means we're connecting (client-side), and it's both the link
@@ -845,18 +840,38 @@
     log_fn(severity,LD_PROTOCOL,
            "Unexpected number of certificates in chain (%d)",
            num_in_chain);
-    goto done;
+    return;
   }
   for (i=0; i<num_in_chain; ++i) {
     id_cert = sk_X509_value(chain, i);
     if (X509_cmp(id_cert, cert) != 0)
       break;
   }
+  *id_cert_out = id_cert;
+}
+
+/** If the provided tls connection is authenticated and has a
+ * certificate that is currently valid and signed, then set
+ * *<b>identity_key</b> to the identity certificate's key and return
+ * 0.  Else, return -1 and log complaints with log-level <b>severity</b>.
+ */
+int
+tor_tls_verify_v1(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key)
+{
+  X509 *cert = NULL, *id_cert = NULL;
+  EVP_PKEY *id_pkey = NULL;
+  RSA *rsa;
+  int r = -1;
+
+  *identity_key = NULL;
+
+  try_to_extract_certs_from_tls(severity, tls, &cert, &id_cert);
+  if (!cert)
+    goto done;
   if (!id_cert) {
     log_fn(severity,LD_PROTOCOL,"No distinct identity certificate found");
     goto done;
   }
-
   if (!(id_pkey = X509_get_pubkey(id_cert)) ||
       X509_verify(cert, id_pkey) <= 0) {
     log_fn(severity,LD_PROTOCOL,"X509_verify on cert and pkey returned <= 0");
@@ -884,6 +899,111 @@
   return r;
 }
 
+/** DOCDOC
+ *
+ * Returns 1 on "verification is done", 0 on "still need LINK_AUTH."
+ */
+int
+tor_tls_verify_certs_v2(int severity, tor_tls_t *tls,
+                        const char *cert_str, size_t cert_len,
+                        const char *id_cert_str, size_t id_cert_len,
+                        crypto_pk_env_t **cert_key_out,
+                        char *conn_cert_digest_out,
+                        char *id_digest_out)
+{
+  X509 *cert = NULL, *id_cert = NULL;
+  EVP_PKEY *id_pkey = NULL, *cert_pkey = NULL;
+  int free_id_cert = 0, peer_used_tls_cert = 0;
+  int r = -1;
+
+  tor_assert(cert_key_out);
+  tor_assert(conn_cert_digest_out);
+  tor_assert(id_digest_out);
+
+  *cert_key_out = NULL;
+
+  if (cert_str && cert_len) {
+    /*XXXX020 warn on error. */
+    const unsigned char *cp = (const unsigned char*) cert_str;
+    cert = d2i_X509(NULL, &cp, cert_len);
+  }
+  if (id_cert_str && id_cert_len) {
+    /*XXXX020 warn on error. */
+    const unsigned char *cp = (const unsigned char*) id_cert_str;
+    id_cert = d2i_X509(NULL, &cp, id_cert_len);
+    if (id_cert)
+      free_id_cert = 1;
+  }
+
+  if (cert) {
+    int cmp = 0;
+    X509 *cert_tmp = SSL_get_peer_certificate(tls->ssl);
+    if (cert_tmp) {
+      peer_used_tls_cert = 1;
+      cmp = X509_cmp(cert, cert_tmp);
+      X509_free(cert_tmp);
+    }
+    if (cmp != 0) {
+      log_fn(severity, LD_PROTOCOL,
+             "Certificate in CERT cell didn't match TLS cert.");
+      goto done;
+    }
+  }
+
+  if (!cert || !id_cert) {
+    X509 *c=NULL, *id=NULL;
+    try_to_extract_certs_from_tls(severity, tls, &c, &id);
+    if (c) {
+      if (!cert)
+        cert = c;
+      else
+        X509_free(c);
+    }
+    if (id && !id_cert)
+      id_cert = id;
+  }
+  if (!id_cert || !cert)
+    goto done;
+
+  if (!(id_pkey = X509_get_pubkey(id_cert)) ||
+      X509_verify(cert, id_pkey) <= 0) {
+    log_fn(severity,LD_PROTOCOL,"X509_verify on cert and pkey returned <= 0");
+    tls_log_errors(severity,"verifying certificate");
+    goto done;
+  }
+
+  {
+    crypto_pk_env_t *i = _crypto_new_pk_env_evp_pkey(id_pkey);
+    if (!i)
+      goto done;
+    crypto_pk_get_digest(i, id_digest_out);
+    crypto_free_pk_env(i);
+  }
+  if (!(cert_pkey = X509_get_pubkey(cert)))
+    goto done;
+  if (!(*cert_key_out = _crypto_new_pk_env_evp_pkey(cert_pkey)))
+    goto done;
+
+  {
+    unsigned int len = 0;
+    X509_digest(cert, EVP_sha1(), (unsigned char*)conn_cert_digest_out, &len);
+    tor_assert(len == DIGEST_LEN);
+  }
+
+  r = peer_used_tls_cert ? 1 : 0;
+ done:
+  if (cert)
+    X509_free(cert);
+  if (id_cert && free_id_cert)
+    X509_free(id_cert);
+  if (id_pkey)
+    EVP_PKEY_free(id_pkey);
+  if (cert_pkey)
+    EVP_PKEY_free(cert_pkey);
+
+  return r;
+}
+
 /** Check whether the certificate set on the connection <b>tls</b> is
  * expired or not-yet-valid, give or take <b>tolerance</b>
  * seconds. Return 0 for valid, -1 for failure.

Modified: tor/trunk/src/common/tortls.h
===================================================================
--- tor/trunk/src/common/tortls.h	2007-11-06 14:21:08 UTC (rev 12395)
+++ tor/trunk/src/common/tortls.h	2007-11-06 18:00:07 UTC (rev 12396)
@@ -60,6 +60,12 @@
 crypto_pk_env_t *tor_tls_dup_private_key(tor_tls_t *tls);
 int tor_tls_verify_v1(int severity, tor_tls_t *tls,
                       crypto_pk_env_t **identity);
+int tor_tls_verify_certs_v2(int severity, tor_tls_t *tls,
+                            const char *cert_str, size_t cert_len,
+                            const char *id_cert_str, size_t id_cert_len,
+                            crypto_pk_env_t **cert_key_out,
+                            char *conn_cert_digest_out,
+                            char *id_digest_out);
 int tor_tls_check_lifetime(tor_tls_t *tls, int tolerance);
 int tor_tls_read(tor_tls_t *tls, char *cp, size_t len);
 int tor_tls_write(tor_tls_t *tls, const char *cp, size_t n);

Modified: tor/trunk/src/or/command.c
===================================================================
--- tor/trunk/src/or/command.c	2007-11-06 14:21:08 UTC (rev 12395)
+++ tor/trunk/src/or/command.c	2007-11-06 18:00:07 UTC (rev 12396)
@@ -608,9 +608,10 @@
 command_process_cert_cell(var_cell_t *cell, or_connection_t *conn)
 {
   int n_certs = 0;
-  uint16_t conn_cert_len, id_cert_len;
+  uint16_t conn_cert_len = 0, id_cert_len = 0;
   const char *conn_cert = NULL, *id_cert = NULL;
   const char *cp, *end;
+  int authenticated = 0;
 
   /*XXXX020 log messages*/
   if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING)
@@ -633,9 +634,11 @@
     if (end-cp < len)
       goto err;
     if (n_certs == 0) {
-      conn_cert = cp;
-      conn_cert_len = len;
+      id_cert = cp;
+      id_cert_len = len;
     } else if (n_certs == 1) {
+      conn_cert = id_cert;
+      conn_cert_len = id_cert_len;
       id_cert = cp;
       id_cert_len = len;
     } else {
@@ -646,20 +649,34 @@
   }
 
   /* Now we have 0, 1, or 2 certs. */
+  if (n_certs == 0) {
+    /* The other side is unauthenticated. */
+  } else {
+    int r;
+    r = tor_tls_verify_certs_v2(LOG_PROTOCOL_WARN, conn->tls,
+                                conn_cert, conn_cert_len,
+                                id_cert, id_cert_len,
+                                &conn->handshake_state->signing_key,
+                                (conn->handshake_state->started_here ?
+                                 conn->handshake_state->server_cert_digest :
+                                 conn->handshake_state->client_cert_digest),
+                                conn->handshake_state->cert_id_digest);
+    if (r < 0)
+      goto err;
+    if (r == 1)
+      authenticated = 1;
+  }
 
+  conn->handshake_state->received_certs = 1;
+  if (authenticated) {
+    /* XXXX020 make the connection open. */
+  }
+  if (! conn->handshake_state->signing_key)
+    goto err;
 
-  /* Verify that identity cert has signed peer cert in SSL, or
-   * peer cert in the cell. */
-  /* Verify that identity cert is self-signed. */
-  /* Learn ID digest. */
-  /* Learn cert digests. */
-  /* Remember peer cert public key. */
-  /* set received_certs. */
-
-  conn->handshake_state->received_certs = 1;
   return;
  err:
-  ;
+  /*XXXX020 close the connection */;
 }
 
 #define LINK_AUTH_STRING "Tor initiator certificate verification"
@@ -732,7 +749,7 @@
   /* Okay, we're authenticated. */
   s->authenticated = 1;
 
-  /* XXXX020 act on being authenticated: */
+  /* XXXX020 act on being authenticated: Open the connection. */
 
   return;
  err:

Modified: tor/trunk/src/or/connection_or.c
===================================================================
--- tor/trunk/src/or/connection_or.c	2007-11-06 14:21:08 UTC (rev 12395)
+++ tor/trunk/src/or/connection_or.c	2007-11-06 18:00:07 UTC (rev 12396)
@@ -633,8 +633,9 @@
  *    this guy; and note that this guy is reachable.
  */
 static int
-connection_or_check_valid_handshake(or_connection_t *conn, int started_here,
-                                    char *digest_rcvd)
+connection_or_check_valid_tls_handshake(or_connection_t *conn,
+                                        int started_here,
+                                        char *digest_rcvd_out)
 {
   crypto_pk_env_t *identity_rcvd=NULL;
   or_options_t *options = get_options();
@@ -677,7 +678,7 @@
 
   if (identity_rcvd) {
     has_identity=1;
-    crypto_pk_get_digest(identity_rcvd, digest_rcvd);
+    crypto_pk_get_digest(identity_rcvd, digest_rcvd_out);
 
     if (crypto_pk_cmp_keys(get_identity_key(), identity_rcvd)<0) {
       conn->circ_id_type = CIRC_ID_TYPE_LOWER;
@@ -686,12 +687,12 @@
     }
     crypto_free_pk_env(identity_rcvd);
   } else {
-    memset(digest_rcvd, 0, DIGEST_LEN);
+    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)) {
-    memcpy(conn->identity_digest, digest_rcvd, DIGEST_LEN);
+    memcpy(conn->identity_digest, digest_rcvd_out, DIGEST_LEN);
     tor_free(conn->nickname);
     conn->nickname = tor_malloc(HEX_DIGEST_LEN+2);
     conn->nickname[0] = '$';
@@ -706,11 +707,11 @@
     int as_advertised = 1;
     tor_assert(has_cert);
     tor_assert(has_identity);
-    if (memcmp(digest_rcvd, conn->identity_digest, DIGEST_LEN)) {
+    if (memcmp(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, DIGEST_LEN);
+      base16_encode(seen, sizeof(seen), digest_rcvd_out, DIGEST_LEN);
       base16_encode(expected, sizeof(expected), conn->identity_digest,
                     DIGEST_LEN);
       log_fn(severity, LD_OR,
@@ -728,7 +729,7 @@
        * with the same address:port and a different key.
        */
       dirserv_orconn_tls_done(conn->_base.address, conn->_base.port,
-                              digest_rcvd, as_advertised);
+                              digest_rcvd_out, as_advertised);
     }
     if (!as_advertised)
       return -1;
@@ -755,7 +756,8 @@
 
   log_debug(LD_OR,"tls handshake done. verifying.");
   /* V1 only XXXX020 */
-  if (connection_or_check_valid_handshake(conn, started_here, digest_rcvd) < 0)
+  if (connection_or_check_valid_tls_handshake(conn, started_here,
+                                              digest_rcvd) < 0)
     return -1;
 
   if (!started_here) { /* V1 only XXXX020 */



More information about the tor-commits mailing list