Hi,

Thanks for the review.  Attached a v2 of the patch, and responses inline below.

On Mon, Feb 15, 2016 at 8:58 PM, Gert Doering <g...@greenie.muc.de> wrote:
> On Sun, Feb 07, 2016 at 08:47:18PM +0100, Steffan Karger wrote:
>> This patch:
>>  * Makes the server advertise "IV_NCP=2", if --push-peer-info 2 is enabled.
>
> I'd rather not do that - it won't do harm, but technically, it's not
> correct (we don't support "negotiable" ciphers in that direction either) -
> and then, nothing at the client ever *looks* at the peer-info variables,
> so it's somewhat moot.  And that makes it "unneccessary extra code"...

Ok, removed from the patch.

> As a side note: I was wondering if o->gc is save to use here (when is it
> cleaned up?  is it ever cleaned up?) and under the assumption that the
> existing code in push_option_ex() is correct, the new code is correct
> as well - the existing code uses o->gc too.

Yes, o->gc is the right thing here.  It is freshly initialized by
init_instance() when a new 'child' (connection/session) instance is
created, and cleaned up by multi_close_instance() when the child is
instance is destroyed.

> A minor comment comment:
>
>>  #if P2MP
>>
>> +/**
>> + * Add an option to the push list by providing a format string.
>> + *
>> + * The string added to the push options is allocated in o->gc, so fmt may be
>> + * free'd by the caller before the option is sent to the peer.
>
> Since nobody ever free()s *fmt*, maybe word that slightly differently,
> like:
>
>  * The resulting string added to the push options is allocated in o->gc,
>  * so the caller does not have to preserve anything
>
> or so

Yes, much better.  Updated.

-Steffan
From b0a9c3f22b39c7b658aa2f59786cad217256c9d6 Mon Sep 17 00:00:00 2001
From: Steffan Karger <stef...@karger.me>
Date: Mon, 15 Feb 2016 21:07:11 +0100
Subject: [PATCH 10/10 v2] Add preliminary server-side support for negotiable
 crypto parameters

Add preliminary support for Negotiable Crypto Parameters 'level 2'
(IV_NCP=2), as proposed by James Yonan on the openvpn-devel mailinglist:
http://comments.gmane.org/gmane.network.openvpn.devel/9385

This patch makes a server push a 'cipher XXX' directive to the client,
if the client advertises "IV_NCP=2", where XXX is the cipher set in the
server config file.

This enables clients that have support for IV_NCP to connect to a
server, even when the client does not have the correct cipher specified
in it's config file.

Since pushing the cipher directive is quite similar to pushing peer-id,
I moved peer-id pushing to the same prepare_push_reply() function I
created for pushing cipher.  Adding these directives as regular push
options allows us to use the existing 'push-continuation'
infrastructure.  Note that we should not reduce safe_cap in
send_push_reply, because it was never increased to account for peer-id.

This is a preliminary patch, which will be followed by more patches to
add client support, and configurability.

v2:
 * Reword doxygen of push_options_fmt()
 * No longer push IV_NCP as a server

Signed-off-by: Steffan Karger <stef...@karger.me>
---
 src/openvpn/push.c | 97 +++++++++++++++++++++++++++++++++++++++++++++---------
 src/openvpn/push.h |  2 --
 2 files changed, 82 insertions(+), 17 deletions(-)

diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index d4f3cb6..c29093b 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -40,6 +40,30 @@
 
 #if P2MP
 
+/**
+ * Add an option to the push list by providing a format string.
+ *
+ * The string added to the push options is allocated in o->gc, so the caller
+ * does not have to preserve anything.
+ *
+ * @param o		The current connection's options
+ * @param msglevel	The message level to use when printing errors
+ * @param fmt		Format string for the option
+ * @param ...		Format string arguments
+ *
+ * @return true on success, false on failure.
+ */
+static bool push_option_fmt(struct options *o, int msglevel,
+    const char *fmt, ...)
+#ifdef __GNUC__
+#if __USE_MINGW_ANSI_STDIO
+    __attribute__ ((format (gnu_printf, 3, 4)))
+#else
+    __attribute__ ((format (__printf__, 3, 4)))
+#endif
+#endif
+    ;
+
 /*
  * Auth username/password
  *
@@ -239,7 +263,47 @@ send_push_request (struct context *c)
 
 #if P2MP_SERVER
 
-bool
+/**
+ * Prepare push options, based on local options and available peer info.
+ *
+ * @param options	Connection options
+ * @param tls_multi	TLS state structure for the current tunnel
+ *
+ * @return true on success, false on failure.
+ */
+static bool
+prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
+{
+  const char *optstr = NULL;
+  const char * const peer_info = tls_multi->peer_info;
+
+  /* Send peer-id if client supports it */
+  optstr = peer_info ? strstr(peer_info, "IV_PROTO=") : NULL;
+  if (optstr)
+    {
+      int proto = 0;
+      int r = sscanf(optstr, "IV_PROTO=%d", &proto);
+      if ((r == 1) && (proto >= 2))
+	{
+	  push_option_fmt(o, M_USAGE, "peer-id %d", tls_multi->peer_id);
+	}
+    }
+
+  /* Push cipher if client supports Negotiable Crypto Parameters */
+  optstr = peer_info ? strstr(peer_info, "IV_NCP=") : NULL;
+  if (optstr)
+    {
+      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);
+	}
+    }
+  return true;
+}
+
+static bool
 send_push_reply (struct context *c)
 {
   struct gc_arena gc = gc_new ();
@@ -309,19 +373,6 @@ send_push_reply (struct context *c)
   if (multi_push)
     buf_printf (&buf, ",push-continuation 1");
 
-  /* Send peer-id if client supports it */
-  if (c->c2.tls_multi->peer_info)
-    {
-      const char* proto_str = strstr(c->c2.tls_multi->peer_info, "IV_PROTO=");
-      if (proto_str)
-	{
-	  int proto = 0;
-	  int r = sscanf(proto_str, "IV_PROTO=%d", &proto);
-	  if ((r == 1) && (proto >= 2))
-	    buf_printf(&buf, ",peer-id %d", c->c2.tls_multi->peer_id);
-	}
-  }
-
   if (BLEN (&buf) > sizeof(cmd)-1)
     {
       const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
@@ -409,6 +460,21 @@ push_options (struct options *o, char **p, int msglevel, struct gc_arena *gc)
   push_option (o, opt, msglevel);
 }
 
+static bool push_option_fmt(struct options *o, int msglevel,
+    const char *format, ...)
+{
+  va_list arglist;
+  char tmp[256] = {0};
+  int len = -1;
+  va_start (arglist, format);
+  len = vsnprintf (tmp, sizeof(tmp), format, arglist);
+  va_end (arglist);
+  if (len > sizeof(tmp)-1)
+    return false;
+  push_option (o, string_alloc (tmp, &o->gc), msglevel);
+  return true;
+}
+
 void
 push_reset (struct options *o)
 {
@@ -442,7 +508,8 @@ process_incoming_push_request (struct context *c)
 	}
       else
 	{
-	  if (send_push_reply (c))
+	  if (prepare_push_reply(&c->options, c->c2.tls_multi) &&
+	      send_push_reply (c))
 	    {
 	      ret = PUSH_MSG_REQUEST;
 	      c->c2.sent_push_reply_expiry = now + 30;
diff --git a/src/openvpn/push.h b/src/openvpn/push.h
index fa06e08..19bbf5e 100644
--- a/src/openvpn/push.h
+++ b/src/openvpn/push.h
@@ -62,8 +62,6 @@ void push_options (struct options *o, char **p, int msglevel, struct gc_arena *g
 
 void push_reset (struct options *o);
 
-bool send_push_reply (struct context *c);
-
 void remove_iroutes_from_push_route_list (struct options *o);
 
 void send_auth_failed (struct context *c, const char *client_reason);
-- 
2.5.0

Reply via email to