]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Add server-side support for cipher negotiation
authorSteffan Karger <steffan@karger.me>
Tue, 28 Jun 2016 21:36:11 +0000 (23:36 +0200)
committerGert Doering <gert@greenie.muc.de>
Mon, 25 Jul 2016 15:34:29 +0000 (17:34 +0200)
Pushes AES-256-GCM when a connection client advertises IV_NCP=2, and
supports serving connections to clients with different data channel
cipher configuration simultaneously.

v2:
 * Update manpage
 * Add Changes.rst entry

v3:
 * Do not regenerate keys if the client sends a second pull request
 * Don't postpone key generation if client has no IV_NCP support

v4:
 * rebase on client-side NCP v4

Signed-off-by: Steffan Karger <steffan@karger.me>
Acked-by: Arne Schwabe <arne@rfc2549.org>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <1467149771-10374-1-git-send-email-steffan@karger.me>
URL: http://article.gmane.org/gmane.network.openvpn.devel/12009
Signed-off-by: Gert Doering <gert@greenie.muc.de>
Changes.rst
doc/openvpn.8
src/openvpn/init.c
src/openvpn/push.c
src/openvpn/ssl.c
src/openvpn/ssl.h
src/openvpn/ssl_common.h

index 55fca958ed11c8c19c309974e863d436bf22630a..9fcba75cf7e1e222af7482d3c8a796291ecebe8a 100644 (file)
@@ -55,6 +55,13 @@ Http proxy password inside config file
        Http proxy passwords can be specified with the inline file option
     http-proxy-user-pass
 
+Cipher negotiation
+    Data channel ciphers are now by default negotiated.  If a client advertises
+    support for Negotiable Crypto Parameters (NCP), the server will choose a
+    cipher (by default AES-256-GCM) for the data channel, and tell the client
+    to use that cipher.  Data channel cipher negotiation can be controlled
+    using --ncp-ciphers and --ncp-disable.
+
 
 User-visible Changes
 --------------------
@@ -124,6 +131,11 @@ User-visible Changes
   time in seconds to wait between reconnection attempts when an exponential
   backoff is triggered due to repeated retries. Default = 300 seconds.
 
+- Data channel cipher negotiation (see New features section) can override
+  ciphers configured in the config file.  Use --ncp-disable if you don't want
+  that.
+
+
 Maintainer-visible changes
 --------------------------
 - OpenVPN no longer supports building with crypto support, but without TLS
index 4f7353bc7c75a46dbf3b9fbedbb9fddcca4725c0..2f4263608d5ab06bdc13e88e65ee632b3194b009 100644 (file)
@@ -4137,6 +4137,10 @@ Restrict the allowed ciphers to be negotiated to the ciphers in
 .B cipher_list
 is a colon-separated list of ciphers, and defaults to
 "AES-256-GCM:AES-128-GCM".
+
+For servers, the first cipher from
+.B cipher_list
+will be pushed to clients that support cipher negotiation.
 .\"*********************************************************
 .TP
 .B \-\-ncp\-disable
index b96f22eab935bce73dd3cf578fb885efed1adf1e..5a47b923e13025a4224bb31067bdf36723196e4e 100644 (file)
@@ -2330,8 +2330,8 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
   /* In short form, unique datagram identifier is 32 bits, in long form 64 bits */
   packet_id_long_form = cipher_kt_mode_ofb_cfb (c->c1.ks.key_type.cipher);
 
-  /* Compute MTU parameters (postpone if we pull options) */
-  if (c->options.pull)
+  /* Compute MTU parameters (postpone if we push/pull options) */
+  if (c->options.pull || c->options.mode == MODE_SERVER)
     {
       /* Account for worst-case crypto overhead before allocating buffers */
       frame_add_to_extra_frame (&c->c2.frame, crypto_max_overhead());
@@ -2375,6 +2375,7 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
   to.renegotiate_packets = options->renegotiate_packets;
   to.renegotiate_seconds = options->renegotiate_seconds;
   to.single_session = options->single_session;
+  to.mode = options->mode;
   to.pull = options->pull;
 #ifdef ENABLE_PUSH_PEER_INFO
   if (options->push_peer_info)         /* all there is */
index 4239e3ef92baa988aaa683ddc76d381c65033531..000c82f93f3025b63259945c50181f3387272424 100644 (file)
@@ -245,13 +245,30 @@ incoming_push_message (struct context *c, const struct buffer *buffer)
          if (!do_up (c, true, c->options.push_option_types_found))
            {
              msg (D_PUSH_ERRORS, "Failed to open tun/tap interface");
-             register_signal (c, SIGUSR1, "do_up-failed");
-             goto cleanup;
+             goto error;
            }
        }
       event_timeout_clear (&c->c2.push_request_interval);
     }
+  else if (status == PUSH_MSG_REQUEST)
+    {
+      if (c->options.mode == MODE_SERVER)
+       {
+         struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
+         /* Do not regenerate keys if client send a second push request */
+         if (!session->key[KS_PRIMARY].crypto_options.key_ctx_bi.initialized &&
+             !tls_session_update_crypto_params (session, &c->options,
+                 &c->c2.frame))
+           {
+             msg (D_TLS_ERRORS, "TLS Error: server generate_key_expansion failed");
+             goto error;
+           }
+       }
+    }
 
+  goto cleanup;
+error:
+  register_signal (c, SIGUSR1, "process-push-msg-failed");
 cleanup:
   gc_free (&gc);
 }
@@ -302,15 +319,13 @@ prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
     }
 
   /* Push cipher if client supports Negotiable Crypto Parameters */
-  optstr = peer_info ? strstr(peer_info, "IV_NCP=") : NULL;
-  if (optstr)
+  if (tls_peer_info_ncp_ver (peer_info) >= 2 && o->ncp_enabled)
     {
-      int ncp = 0;
-      int r = sscanf(optstr, "IV_NCP=%d", &ncp);
-      if ((r == 1) && (ncp == 2))
-       {
-         push_option_fmt(o, M_USAGE, "cipher %s", o->ciphername);
-       }
+      /* Push the first cipher from --ncp-ciphers to the client.
+       * TODO: actual negotiation, instead of server dictatorship. */
+      char *push_cipher = string_alloc(o->ncp_ciphers, &o->gc);
+      o->ciphername = strtok (push_cipher, ":");
+      push_option_fmt(o, M_USAGE, "cipher %s", o->ciphername);
     }
   return true;
 }
index 8717324ab9da3dbaec9487857b4efe377e525b14..72001a3c2ce8679f9e56c42c65b026fc2d66d7cf 100644 (file)
@@ -1665,10 +1665,10 @@ tls_session_update_crypto_params(struct tls_session *session,
   bool ret = false;
   struct key_state *ks = &session->key[KS_PRIMARY];    /* primary key */
 
-  ASSERT (!session->opt->server);
   ASSERT (ks->authenticated);
 
-  if (0 != strcmp(options->ciphername, session->opt->config_ciphername) &&
+  if (!session->opt->server &&
+      0 != strcmp(options->ciphername, session->opt->config_ciphername) &&
       !item_in_list(options->ciphername, options->ncp_ciphers))
     {
       msg (D_TLS_ERRORS, "Error: pushed cipher not allowed - %s not in %s or %s",
@@ -1695,12 +1695,13 @@ tls_session_update_crypto_params(struct tls_session *session,
       options->ce.tun_mtu_defined, options->ce.tun_mtu);
   frame_print (frame, D_MTU_INFO, "Data Channel MTU parms");
 
+  const struct session_id *client_sid = session->opt->server ?
+      &ks->session_id_remote : &session->session_id;
+  const struct session_id *server_sid = !session->opt->server ?
+      &ks->session_id_remote : &session->session_id;
   if (!generate_key_expansion (&ks->crypto_options.key_ctx_bi,
-                              &session->opt->key_type,
-                              ks->key_src,
-                              &session->session_id,
-                              &ks->session_id_remote,
-                              false))
+      &session->opt->key_type, ks->key_src, client_sid, server_sid,
+      session->opt->server))
     {
       msg (D_TLS_ERRORS, "TLS Error: server generate_key_expansion failed");
       goto cleanup;
@@ -1958,8 +1959,11 @@ push_peer_info(struct buffer *buf, struct tls_session *session)
       buf_printf(&out, "IV_PROTO=2\n");
 
       /* support for Negotiable Crypto Paramters */
-      if (session->opt->ncp_enabled && session->opt->pull)
-       buf_printf(&out, "IV_NCP=2\n");
+      if (session->opt->ncp_enabled &&
+         (session->opt->mode == MODE_SERVER || session->opt->pull))
+       {
+         buf_printf(&out, "IV_NCP=2\n");
+       }
 
       /* push compression status */
 #ifdef USE_COMP
@@ -2063,10 +2067,13 @@ key_method_2_write (struct buffer *buf, struct tls_session *session)
   if (!push_peer_info (buf, session))
     goto error;
 
-  /*
-   * generate tunnel keys if server
+  /* Generate tunnel keys if we're a TLS server.
+   * If we're a p2mp server and IV_NCP >= 2 is negotiated, the first key
+   * generation is postponed until after the pull/push, so we can process pushed
+   * cipher directives.
    */
-  if (session->opt->server)
+  if (session->opt->server && !(session->opt->ncp_enabled &&
+       session->opt->mode == MODE_SERVER && ks->key_id <= 0))
     {
       if (ks->authenticated)
        {
@@ -2218,6 +2225,12 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
   multi->peer_info = read_string_alloc (buf);
   if ( multi->peer_info )
       output_peer_info_env (session->opt->es, multi->peer_info);
+
+  if (tls_peer_info_ncp_ver (multi->peer_info) < 2)
+    {
+      /* Peer does not support NCP */
+      session->opt->ncp_enabled = false;
+    }
 #endif
 
   if (tls_session_user_pass_enabled(session))
@@ -3689,6 +3702,20 @@ tls_update_remote_addr (struct tls_multi *multi, const struct link_socket_actual
   gc_free (&gc);
 }
 
+int
+tls_peer_info_ncp_ver(const char *peer_info)
+{
+  const char *ncpstr = peer_info ? strstr (peer_info, "IV_NCP=") : NULL;
+  if (ncpstr)
+    {
+      int ncp = 0;
+      int r = sscanf(ncpstr, "IV_NCP=%d", &ncp);
+      if (r == 1)
+       return ncp;
+    }
+  return 0;
+}
+
 /*
  * Dump a human-readable rendition of an openvpn packet
  * into a garbage collectable string which is returned.
index 416f42636876344401bd49f117fc816007052371..de68b69e93f7c9a64291e89dde1dcf80cce5cae4 100644 (file)
@@ -497,6 +497,12 @@ tls_get_peer_info(const struct tls_multi *multi)
 }
 #endif
 
+/**
+ * Return the Negotiable Crypto Parameters version advertised in the peer info
+ * string, or 0 if none specified.
+ */
+int tls_peer_info_ncp_ver(const char *peer_info);
+
 /*
  * inline functions
  */
index 6bfde6baeee5f5c49d71a0edeceb2bdff51517e2..eb2ad6f956defd52677304a0c9532ab38d4e8a9d 100644 (file)
@@ -236,6 +236,7 @@ struct tls_options
 #ifdef ENABLE_OCC
   bool disable_occ;
 #endif
+  int mode;
   bool pull;
 #ifdef ENABLE_PUSH_PEER_INFO
   int push_peer_info_detail;