Updated patch: 

- setenv_str() will set only valid tls_ekm attribute

diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index c2907cd..7870d10 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2307,6 +2307,13 @@ do_init_crypto_tls (struct context *c, const unsigned 
int flags)
      length prefix */
   socket_adjust_frame_parameters (&to.frame, options->ce.proto);

+  /* Checking for required parameters for Channel Bindings */
+  to.ekm_size = options->keying_material_exporter_length;
+
+  to.ekm_label = (uint8_t*)options->keying_material_exporter_label;
+  to.ekm_label_size = to.ekm_label ? strlen(to.ekm_label) : 0;
+  to.ekm_used = (to.ekm_label_size && to.ekm_size >= 20) ? true : false;
+
   /*
    * Initialize OpenVPN's master TLS-mode object.
    */
@@ -2315,6 +2322,7 @@ do_init_crypto_tls (struct context *c, const unsigned int 
flags)

   if (flags & CF_INIT_TLS_AUTH_STANDALONE)
     c->c2.tls_auth_standalone = tls_auth_standalone_init (&to, &c->c2.gc);
+
 }

 static void
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 18cb354..5323cd7 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -6966,6 +6966,24 @@ add_option (struct options *options,
       options->persist_mode = 1;
     }
 #endif
+  else if (streq (p[0], "keying-material-exporter-label") && p[1])
+    {
+      if (strncmp(p[1], "EXPORTER", 8))
+        {
+          msg (msglevel, "keying material exporter labels SHOULD begin with 
\"EXPORTER\"");
+          goto err;
+        }
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+
+      options->keying_material_exporter_label = p[1];
+    }
+  else if (streq (p[0], "keying-material-exporter-length"))
+    {
+      int len = positive_atoi (p[1]);
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+
+      options->keying_material_exporter_length = len;
+    }
   else
     {
       int i;
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index ec1d091..8c8aeb3 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -600,6 +600,10 @@ struct options
   bool show_net_up;
   int route_method;
 #endif
+
+  /* Keying Material Exporters [RFC 5705] */
+  const char *keying_material_exporter_label;
+  int keying_material_exporter_length;
 };

 #define streq(x, y) (!strcmp((x), (y)))
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index d4acc0f..b757bfe 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -1,13 +1,14 @@
 /*
  *  OpenVPN -- An application to securely tunnel IP networks
  *             over a single TCP/UDP port, with support for SSL/TLS-based
- *             session authentication and key exchange,
+ *             session authentication, key exchange and channel bindings,
  *             packet encryption, packet authentication, and
  *             packet compression.
  *
  *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sa...@openvpn.net>
  *  Copyright (C) 2010 Fox Crypto B.V. <open...@fox-it.com>
  *  Copyright (C) 2008-2013 David Sommerseth <d...@users.sourceforge.net>
+ *  Copyright (C) 2014 Daniel Kubec <n...@rtfm.cz>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2
@@ -905,6 +906,14 @@ tls_session_init (struct tls_multi *multi, struct 
tls_session *session)

   key_state_init (session, &session->key[KS_PRIMARY]);

+  /* allocate buffers for vpn_binding_key and tls_ekm [RFC 5705] */
+  session->ekm = malloc (session->opt->ekm_size);
+  session->ekm_exported = false;
+  session->binding_key = malloc (session->opt->ekm_size);
+
+  memset(session->ekm, 0, session->opt->ekm_size);
+  memset(session->binding_key, 0, session->opt->ekm_size);
+
   dmsg (D_TLS_DEBUG, "TLS: tls_session_init: new session object, sid=%s",
        session_id_print (&session->session_id, &gc));

@@ -937,6 +946,12 @@ tls_session_free (struct tls_session *session, bool clear)
   if (session->common_name)
     free (session->common_name);

+  if (session->ekm)
+    free (session->ekm);
+
+  if (session->binding_key)
+    free (session->binding_key);
+
   cert_hash_free (session->cert_hash_set);

   if (clear)
@@ -1454,6 +1469,62 @@ tls1_PRF(uint8_t *label,
   gc_free (&gc);
 }

+/*                                                                             
 
+ * Use the tls1_keying_material_exporter for generating VPN Binding Key
+ *
+ * Labels here have the same definition as in TLS, i.e., an ASCII string
+ * with no terminating NULL.  
+ * 
+ * Note that exporter labels have the potential to collide with existing 
+ * tls1_PRF labels.  In order to prevent this, labels SHOULD begin with 
+ * "EXPORTER".  
+ *
+ * The output is a pseudorandom bit string of length bytes generated
+ * from the master_secret.  
+ *
+ * If no context is provided, it then computes:
+ * 
+ *     tls1_PRF(SecurityParameters.master_secret, label,
+ *              SecurityParameters.client_random +
+ *              SecurityParameters.server_random)[length]
+ *
+ * If context is provided, it computes:
+ *
+ *     tls1_PRF(SecurityParameters.master_secret, label,
+ *              SecurityParameters.client_random +
+ *              SecurityParameters.server_random +
+ *              context_value_length + context_value)[length] 
+ */
+
+static void                                                                    
 
+tls1_keying_material_exporter(uint8_t *master, int master_len,
+                              uint8_t *label, int label_len,
+                              const uint8_t *client_random, int 
client_random_len,
+                              const uint8_t *server_random, int 
server_random_len,
+                              const uint8_t *context_value, int 
context_value_len,
+                              uint8_t *out, int out_len)
+{
+  struct buffer seed = alloc_buf (master_len
+                                + label_len
+                                + client_random_len
+                                + server_random_len);
+
+  ASSERT (buf_write (&seed, label, label_len));
+  ASSERT (buf_write (&seed, client_random, client_random_len));
+  ASSERT (buf_write (&seed, server_random, server_random_len));
+
+  if (context_value)
+    ASSERT (buf_write (&seed, context_value, context_value_len));
+
+  /* compute PRF */                                                            
 
+  tls1_PRF (BPTR(&seed), BLEN(&seed), master, master_len, out, out_len);       
 
+
+  buf_clear (&seed);
+  free_buf (&seed);
+
+  VALGRIND_MAKE_READABLE ((void *)out, out_len);
+}
+
 static void
 openvpn_PRF (const uint8_t *secret,
             int secret_len,
@@ -1492,45 +1563,87 @@ openvpn_PRF (const uint8_t *secret,
   VALGRIND_MAKE_READABLE ((void *)output, output_len);
 }

+/*
+ * Generated Binding Key based on Keying Material Exporters [RFC 5705]
+ */
+static bool
+generate_binding_key(uint8_t *master,
+                     int master_len,
+                     struct key_ctx_bi *key,
+                     const struct key_type *key_type,
+                     const struct key_source2 *key_src,
+                     uint8_t *label, int label_len,
+                     uint8_t *binding_key, int binding_key_len)
+{
+  const char *context_value = KEY_EXPANSION_ID " binding key";
+  struct gc_arena gc = gc_new ();
+
+  tls1_keying_material_exporter(master, master_len,
+                                label, label_len,
+                                key_src->client.random2,
+                                sizeof(key_src->client.random2),
+                                key_src->server.random2,
+                                sizeof(key_src->server.random2),
+                                context_value,
+                                strlen(context_value),
+                                binding_key, binding_key_len);
+
+  dmsg (D_SHOW_KEY_SOURCE, "vpn_binding_key: %s",
+        format_hex (binding_key, binding_key_len, 0, &gc));
+  gc_free (&gc);
+  return true;
+}
+
+static void
+generate_master_secret(struct key_ctx_bi *key,
+                       const struct key_type *key_type,
+                       const struct key_source2 *key_src,
+                       uint8_t *master,
+                       unsigned int master_size)
+{
+  /* compute master secret */
+  openvpn_PRF (key_src->client.pre_master,
+               sizeof(key_src->client.pre_master),
+               KEY_EXPANSION_ID " master secret",
+               key_src->client.random1,
+               sizeof(key_src->client.random1),
+               key_src->server.random1,
+               sizeof(key_src->server.random1),
+               NULL,
+               NULL,
+               master,
+               master_size);
+
+  key_source2_print (key_src);
+
+}
+
 /* 
  * Using source entropy from local and remote hosts, mix into
  * master key.
  */
 static bool
-generate_key_expansion (struct key_ctx_bi *key,
+generate_key_expansion (uint8_t *master,
+                       int master_len,
+                       struct key_ctx_bi *key,
                        const struct key_type *key_type,
                        const struct key_source2 *key_src,
                        const struct session_id *client_sid,
                        const struct session_id *server_sid,
                        bool server)
 {
-  uint8_t master[48];
   struct key2 key2;
   bool ret = false;
   int i;

-  CLEAR (master);
   CLEAR (key2);

   /* debugging print of source key material */
   key_source2_print (key_src);

-  /* compute master secret */
-  openvpn_PRF (key_src->client.pre_master,
-              sizeof(key_src->client.pre_master),
-              KEY_EXPANSION_ID " master secret",
-              key_src->client.random1,
-              sizeof(key_src->client.random1),
-              key_src->server.random1,
-              sizeof(key_src->server.random1),
-              NULL,
-              NULL,
-              master,
-              sizeof(master));
-  
   /* compute key expansion */
   openvpn_PRF (master,
-              sizeof(master),
+              master_len,
               KEY_EXPANSION_ID " key expansion",
               key_src->client.random2,
               sizeof(key_src->client.random2),
@@ -1869,6 +1982,10 @@ static bool
 key_method_2_write (struct buffer *buf, struct tls_session *session)
 {
   struct key_state *ks = &session->key[KS_PRIMARY];       /* primary key */
+  struct gc_arena gc = gc_new ();
+  uint8_t master_secret[48];                                            
+
+  CLEAR(master_secret);

   ASSERT (session->opt->key_method == 2);
   ASSERT (buf_init (buf, 0));
@@ -1919,30 +2036,80 @@ key_method_2_write (struct buffer *buf, struct 
tls_session *session)
   /*
    * generate tunnel keys if server
    */
-  if (session->opt->server)
+  
+  if (!session->opt->server)
+    goto done;
+
+  session->negotiations++;
+
+  if (ks->authenticated)
     {
-      if (ks->authenticated)
-       {
-         if (!generate_key_expansion (&ks->key,
-                                      &session->opt->key_type,
-                                      ks->key_src,
-                                      &ks->session_id_remote,
-                                      &session->session_id,
-                                      true))
-           {
-             msg (D_TLS_ERRORS, "TLS Error: server generate_key_expansion 
failed");
-             goto error;
-           }
-       }
-                     
-      CLEAR (*ks->key_src);
+       generate_master_secret(&ks->key,
+                              &session->opt->key_type,
+                              ks->key_src,
+                              master_secret,
+                              sizeof(master_secret));
+
+       /*
+        * Generate VPN Binding Key
+        */
+       if (session->opt->ekm_used && session->negotiations == 1)
+           generate_binding_key(master_secret,
+                               sizeof(master_secret),
+                               &ks->key,
+                               &session->opt->key_type,
+                               ks->key_src,
+                               session->opt->ekm_label,                        
 
+                               session->opt->ekm_label_size, 
+                               session->binding_key,
+                               session->opt->ekm_size);
+
     }

+  /*
+   * Call OPENVPN_PLUGIN_TLS_FINAL plugin if defined, for final
+   * veto opportunity over authentication decision.
+   */
+  if (ks->authenticated && plugin_defined (session->opt->plugins, 
OPENVPN_PLUGIN_TLS_FINAL))
+    {
+      if (session->opt->ekm_used)
+        {
+          /* VPN binding key identifies TLS Session across (re)negotiations */
+          setenv_str (session->opt->es, "vpn_binding_key", format_hex 
(session->binding_key, session->opt->ekm_size, 0, &gc));
+
+          /* Exported Keying Material [RFC 5705] */
+          if (session->ekm_exported == true)
+            setenv_str (session->opt->es, "tls_ekm", 
+                        format_hex (session->ekm, session->opt->ekm_size, 0, 
&gc));
+        }
+
+    if (plugin_call (session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL, NULL, 
NULL, session->opt->es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+       ks->authenticated = false;
+  }
+
+  if (!generate_key_expansion (master_secret,
+                               sizeof(master_secret),
+                               &ks->key,
+                               &session->opt->key_type,
+                               ks->key_src,
+                               &ks->session_id_remote,
+                               &session->session_id,
+                               true))
+  {
+    msg (D_TLS_ERRORS, "TLS Error: server generate_key_expansion failed");
+    goto error;
+  }
+
+  CLEAR (*ks->key_src);
+
+done:
+  gc_free (&gc);
   return true;

  error:
   msg (D_TLS_ERRORS, "TLS Error: Key Method #2 write failed");
   CLEAR (*ks->key_src);
+  gc_free (&gc);
   return false;
 }

@@ -2017,6 +2184,9 @@ key_method_2_read (struct buffer *buf, struct tls_multi 
*multi, struct tls_sessi
   struct gc_arena gc = gc_new ();
   char *options;
   struct user_pass *up;
+  uint8_t master_secret[48];   /* master secret */
+
+  CLEAR(master_secret);

   /* allocate temporary objects */
   ALLOC_ARRAY_CLEAR_GC (options, char, TLS_OPTIONS_LEN, &gc);
@@ -2120,39 +2290,81 @@ key_method_2_read (struct buffer *buf, struct tls_multi 
*multi, struct tls_sessi

   buf_clear (buf);

+  /*                                                                           
 
+   * Generate keys if client because server_random is not ready here for the
+   * server end-point                                           
+   */
+  if (session->opt->server)
+    goto done;
+
+  session->negotiations++;
+
+  /* 
+   * Generate master secret here because it is used in
+   * generate_binding_key(), generate_channel_id() and generate_key_expansion()
+   */
+  generate_master_secret(&ks->key,
+                         &session->opt->key_type,
+                         ks->key_src,
+                         master_secret,
+                         sizeof(master_secret));
+  /*
+   * Generate VPN binding 
+   */
+  if (session->opt->ekm_used && session->negotiations == 1)
+    generate_binding_key(master_secret,
+                        sizeof(master_secret),
+                        &ks->key,
+                        &session->opt->key_type,
+                        ks->key_src,
+                        session->opt->ekm_label,
+                        session->opt->ekm_label_size, 
+                        session->binding_key,
+                        session->opt->ekm_size);
   /*
    * Call OPENVPN_PLUGIN_TLS_FINAL plugin if defined, for final
    * veto opportunity over authentication decision.
    */
   if (ks->authenticated && plugin_defined (session->opt->plugins, 
OPENVPN_PLUGIN_TLS_FINAL))
     {
+      if (session->opt->ekm_used)
+        {
+          /* VPN binding key identifies TLS Session across (re)negotiations */
+          setenv_str (session->opt->es, "vpn_binding_key", format_hex 
(session->binding_key, session->opt->ekm_size, 0, &gc));
+
+          /* Exported Keying Material [RFC 5705] */
+          if (session->ekm_exported == true)
+            setenv_str (session->opt->es, "tls_ekm", 
+                        format_hex (session->ekm, session->opt->ekm_size, 0, 
&gc));
+        }
+
       if (plugin_call (session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL, NULL, 
NULL, session->opt->es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
        ks->authenticated = false;
     }

   /*
-   * Generate tunnel keys if client
+   * Generate tunnel keys 
    */
-  if (!session->opt->server)
-    {
-      if (!generate_key_expansion (&ks->key,
-                                  &session->opt->key_type,
-                                  ks->key_src,
-                                  &session->session_id,
-                                  &ks->session_id_remote,
-                                  false))
-       {
-         msg (D_TLS_ERRORS, "TLS Error: client generate_key_expansion failed");
-         goto error;
-       }
-                     
-      CLEAR (*ks->key_src);
+  if (!generate_key_expansion (master_secret,
+                               sizeof(master_secret),
+                               &ks->key,
+                               &session->opt->key_type,
+                               ks->key_src,
+                               &session->session_id,
+                               &ks->session_id_remote,
+                               false))
+    {
+      msg (D_TLS_ERRORS, "TLS Error: client generate_key_expansion failed");
+      goto error;
     }
+                     
+  CLEAR (*ks->key_src);

+done:
   gc_free (&gc);
   return true;

- error:
+error:
   CLEAR (*ks->key_src);
   buf_clear (buf);
   gc_free (&gc);
diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 04ba789..40cbdbe 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -314,6 +314,12 @@ struct tls_options

   /* --gremlin bits */
   int gremlin;
+
+  /* Keying Material Exporter [RFC 5705] parameters */
+  uint8_t *ekm_label;
+  int ekm_label_size;
+  bool ekm_used; /* true when Keying Material should be exported */
+  int ekm_size;
 };

 /** @addtogroup control_processor
@@ -359,6 +365,16 @@ struct tls_session
   struct crypto_options tls_auth;
   struct packet_id tls_auth_pid;

+  /* number of negotiations for this session */
+  unsigned int negotiations;
+
+  /* Exported Keying Material [RFC 5705] */
+  uint8_t *ekm;                 /* buffer size: session->opt->ekm_size */
+  bool ekm_exported;            /* true when ekm contains updated material */
+
+  /* VPN's binding key derived by tls1_keying_material_exporter */
+  uint8_t *binding_key;         /* buffer size: session->opt->ekm_size */
+
   int initial_opcode;          /* our initial P_ opcode */
   struct session_id session_id;        /* our random session ID */
   int key_id;                  /* increments with each soft reset (for key 
renegotiation) */
diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c
index 0b63e26..873b163 100644
--- a/src/openvpn/ssl_openssl.c
+++ b/src/openvpn/ssl_openssl.c
@@ -154,6 +154,28 @@ info_callback (INFO_CALLBACK_SSL_CONST SSL * s, int where, 
int ret)
           SSL_alert_type_string_long (ret),
           SSL_alert_desc_string_long (ret));
     }
+  else if (where & SSL_CB_HANDSHAKE_DONE)
+    {
+      struct tls_session *tls;
+      tls = (struct tls_session *)SSL_get_ex_data (s, mydata_index);
+
+      tls->ekm_exported = false;
+      if (tls->opt->ekm_used == false)
+        goto done;
+
+      memset(tls->ekm, 0, tls->opt->ekm_size);
+
+#if (OPENSSL_VERSION_NUMBER > 0x10002000)
+      tls->ekm_exported = (bool)SSL_export_keying_material((SSL *)s, 
+                                                           tls->ekm, 
+                                                           tls->opt->ekm_size, 
+                                                           tls->opt->ekm_label,
+                                                           
tls->opt->ekm_label_size, 
+                                                           NULL, 0, 0);
+#endif
+done:
+      msg (D_HANDSHAKE_VERBOSE, "SSL Handshake done.");
+    }
 }

 /*

Reply via email to