]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
On server, lock client-provided certs against mid-session TLS
authorjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>
Sun, 25 Oct 2009 15:51:04 +0000 (15:51 +0000)
committerjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>
Sun, 25 Oct 2009 15:51:04 +0000 (15:51 +0000)
renegotiations -- this is similer to how the common name is also
locked.

git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@5105 e7ae566f-a301-0410-adde-c780ea21d3b5

multi.c
ssl.c
ssl.h

diff --git a/multi.c b/multi.c
index 7f77cb841286b1f607098675cc96c9437c46a440..7ea64b43d5cd48bde50994d2912281e00f2e016b 100644 (file)
--- a/multi.c
+++ b/multi.c
@@ -1458,8 +1458,9 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
 
       ASSERT (mi->context.c1.tuntap);
 
-      /* lock down the common name so it can't change during future TLS renegotiations */
+      /* lock down the common name and cert hashes so they can't change during future TLS renegotiations */
       tls_lock_common_name (mi->context.c2.tls_multi);
+      tls_lock_cert_hash_set (mi->context.c2.tls_multi);
 
       /* generate a msg() prefix for this client instance */
       generate_prefix (mi);
diff --git a/ssl.c b/ssl.c
index 847c5ec7ebff42aae55b2387668a106707970bb5..d882c94f5ee4850b34bba5b04657a4c0ab2459c9 100644 (file)
--- a/ssl.c
+++ b/ssl.c
@@ -339,6 +339,104 @@ tmp_rsa_cb (SSL * s, int is_export, int keylength)
   return (rsa_tmp);
 }
 
+/*
+ * Cert hash functions
+ */
+static void
+cert_hash_remember (struct tls_session *session, const int error_depth, const unsigned char *sha1_hash)
+{
+  if (error_depth >= 0 && error_depth < MAX_CERT_DEPTH)
+    {
+      if (!session->cert_hash_set)
+       ALLOC_OBJ_CLEAR (session->cert_hash_set, struct cert_hash_set);
+      if (!session->cert_hash_set->ch[error_depth])
+       ALLOC_OBJ (session->cert_hash_set->ch[error_depth], struct cert_hash);
+      {
+       struct cert_hash *ch = session->cert_hash_set->ch[error_depth];
+       memcpy (ch->sha1_hash, sha1_hash, SHA_DIGEST_LENGTH);
+      }
+    }
+}
+
+#if 0
+static void
+cert_hash_print (const struct cert_hash_set *chs, int msglevel)
+{
+  struct gc_arena gc = gc_new ();
+  msg (msglevel, "CERT_HASH");
+  if (chs)
+    {
+      int i;
+      for (i = 0; i < MAX_CERT_DEPTH; ++i)
+       {
+         const struct cert_hash *ch = chs->ch[i];
+         if (ch)
+           msg (msglevel, "%d:%s", i, format_hex(ch->sha1_hash, SHA_DIGEST_LENGTH, 0, &gc));
+       }
+    }
+  gc_free (&gc);
+}
+#endif
+
+static void
+cert_hash_free (struct cert_hash_set *chs)
+{
+  if (chs)
+    {
+      int i;
+      for (i = 0; i < MAX_CERT_DEPTH; ++i)
+       free (chs->ch[i]);
+      free (chs);
+    }
+}
+
+static bool
+cert_hash_compare (const struct cert_hash_set *chs1, const struct cert_hash_set *chs2)
+{
+  if (chs1 && chs2)
+    {
+      int i;
+      for (i = 0; i < MAX_CERT_DEPTH; ++i)
+       {
+         const struct cert_hash *ch1 = chs1->ch[i];
+         const struct cert_hash *ch2 = chs2->ch[i];
+
+         if (!ch1 && !ch2)
+           continue;
+         else if (ch1 && ch2 && !memcmp (ch1->sha1_hash, ch2->sha1_hash, SHA_DIGEST_LENGTH))
+           continue;
+         else
+           return false;
+       }
+      return true;
+    }
+  else if (!chs1 && !chs2)
+    return true;
+  else
+    return false;
+}
+
+static struct cert_hash_set *
+cert_hash_copy (const struct cert_hash_set *chs)
+{
+  struct cert_hash_set *dest = NULL;
+  if (chs)
+    {
+      int i;
+      ALLOC_OBJ_CLEAR (dest, struct cert_hash_set);
+      for (i = 0; i < MAX_CERT_DEPTH; ++i)
+       {
+         const struct cert_hash *ch = chs->ch[i];
+         if (ch)
+           {
+             ALLOC_OBJ (dest->ch[i], struct cert_hash);
+             memcpy (dest->ch[i]->sha1_hash, ch->sha1_hash, SHA_DIGEST_LENGTH);
+           }
+       }
+    }
+  return dest;
+}
+
 /*
  * Extract a field from an X509 subject name.
  *
@@ -603,7 +701,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
   SSL *ssl;
   struct tls_session *session;
   const struct tls_options *opt;
-  const int max_depth = 8;
+  const int max_depth = MAX_CERT_DEPTH;
   struct argv argv = argv_new ();
 
   /* get the tls_session pointer */
@@ -645,9 +743,16 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
 
   string_mod_sslname (common_name, COMMON_NAME_CHAR_CLASS, opt->ssl_flags);
 
+  cert_hash_remember (session, ctx->error_depth, ctx->current_cert->sha1_hash);
+
 #if 0 /* print some debugging info */
-  msg (D_LOW, "LOCAL OPT: %s", opt->local_options);
-  msg (D_LOW, "X509: %s", subject);
+  {
+    struct gc_arena gc = gc_new ();
+    msg (M_INFO, "LOCAL OPT[%d]: %s", ctx->error_depth, opt->local_options);
+    msg (M_INFO, "X509[%d]: %s", ctx->error_depth, subject);
+    msg (M_INFO, "SHA1[%d]: %s", ctx->error_depth, format_hex(ctx->current_cert->sha1_hash, SHA_DIGEST_LENGTH, 0, &gc));
+    gc_free (&gc);
+  }
 #endif
 
   /* did peer present cert which was signed our root cert? */
@@ -898,6 +1003,14 @@ tls_lock_common_name (struct tls_multi *multi)
     multi->locked_cn = string_alloc (cn, NULL);
 }
 
+void
+tls_lock_cert_hash_set (struct tls_multi *multi)
+{
+  const struct cert_hash_set *chs = multi->session[TM_ACTIVE].cert_hash_set;
+  if (chs && !multi->locked_cert_hash_set)
+    multi->locked_cert_hash_set = cert_hash_copy (chs);
+}
+
 static bool
 tls_lock_username (struct tls_multi *multi, const char *username)
 {
@@ -2251,6 +2364,8 @@ tls_session_free (struct tls_session *session, bool clear)
   if (session->common_name)
     free (session->common_name);
 
+  cert_hash_free (session->cert_hash_set);
+
   if (clear)
     CLEAR (*session);
 }
@@ -2444,6 +2559,8 @@ tls_multi_free (struct tls_multi *multi, bool clear)
   if (multi->locked_username)
     free (multi->locked_username);
 
+  cert_hash_free (multi->locked_cert_hash_set);
+
   for (i = 0; i < TM_SIZE; ++i)
     tls_session_free (&multi->session[i], false);
 
@@ -3492,6 +3609,20 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
        }
     }
 
+  /* Don't allow the cert hashes to change once they have been locked */
+  if (ks->authenticated && multi->locked_cert_hash_set)
+    {
+      const struct cert_hash_set *chs = session->cert_hash_set;
+      if (chs && !cert_hash_compare (chs, multi->locked_cert_hash_set))
+       {
+         msg (D_TLS_ERRORS, "TLS Auth Error: TLS object CN=%s client-provided SSL certs unexpectedly changed during mid-session reauth",
+              session->common_name);
+
+         /* disable the tunnel */
+         tls_deauthenticate (multi);
+       }
+    }
+
   /* verify --client-config-dir based authentication */
   if (ks->authenticated && session->opt->client_config_dir_exclusive)
     {
diff --git a/ssl.h b/ssl.h
index 7e0bfb521a7fda0f0d697780faa3a67d056fb89a..3bb5fbe777ae574ce90598b26bb70dac6414c7e4 100644 (file)
--- a/ssl.h
+++ b/ssl.h
  */
 /* #define MEASURE_TLS_HANDSHAKE_STATS */
 
+/*
+ * Keep track of certificate hashes at various depths
+ */
+
+/* Maximum certificate depth we will allow */
+#define MAX_CERT_DEPTH 8
+
+struct cert_hash {
+  unsigned char sha1_hash[SHA_DIGEST_LENGTH];
+};
+
+struct cert_hash_set {
+  struct cert_hash *ch[MAX_CERT_DEPTH];
+};
+
 /*
  * Key material, used as source for PRF-based
  * key expansion.
@@ -518,6 +533,8 @@ struct tls_session
 
   char *common_name;
 
+  struct cert_hash_set *cert_hash_set;
+
 #ifdef ENABLE_PF
   uint32_t common_name_hashval;
 #endif
@@ -589,10 +606,11 @@ struct tls_multi
   int n_soft_errors;   /* errors due to unrecognized or failed-to-authenticate incoming packets */
 
   /*
-   * Our locked common name and username (cannot change during the life of this tls_multi object)
+   * Our locked common name, username, and cert hashes (cannot change during the life of this tls_multi object)
    */
   char *locked_cn;
   char *locked_username;
+  struct cert_hash_set *locked_cert_hash_set;
 
 #ifdef ENABLE_DEF_AUTH
   /*
@@ -692,6 +710,7 @@ bool tls_rec_payload (struct tls_multi *multi,
 const char *tls_common_name (const struct tls_multi* multi, const bool null);
 void tls_set_common_name (struct tls_multi *multi, const char *common_name);
 void tls_lock_common_name (struct tls_multi *multi);
+void tls_lock_cert_hash_set (struct tls_multi *multi);
 
 #define TLS_AUTHENTICATION_SUCCEEDED  0
 #define TLS_AUTHENTICATION_FAILED     1