Browse code

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 <steffan@karger.me>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <CAA1Abx+gSgFH3=+xO6QN4NDAYwf8jctYhe8VyRxD8e1L=D6LWg@mail.gmail.com>
URL: http://article.gmane.org/gmane.network.openvpn.devel/11170
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Steffan Karger authored on 2016/02/16 05:07:11
Showing 2 changed files
... ...
@@ -40,6 +40,30 @@
40 40
 
41 41
 #if P2MP
42 42
 
43
+/**
44
+ * Add an option to the push list by providing a format string.
45
+ *
46
+ * The string added to the push options is allocated in o->gc, so the caller
47
+ * does not have to preserve anything.
48
+ *
49
+ * @param o		The current connection's options
50
+ * @param msglevel	The message level to use when printing errors
51
+ * @param fmt		Format string for the option
52
+ * @param ...		Format string arguments
53
+ *
54
+ * @return true on success, false on failure.
55
+ */
56
+static bool push_option_fmt(struct options *o, int msglevel,
57
+    const char *fmt, ...)
58
+#ifdef __GNUC__
59
+#if __USE_MINGW_ANSI_STDIO
60
+    __attribute__ ((format (gnu_printf, 3, 4)))
61
+#else
62
+    __attribute__ ((format (__printf__, 3, 4)))
63
+#endif
64
+#endif
65
+    ;
66
+
43 67
 /*
44 68
  * Auth username/password
45 69
  *
... ...
@@ -239,7 +263,47 @@ send_push_request (struct context *c)
239 239
 
240 240
 #if P2MP_SERVER
241 241
 
242
-bool
242
+/**
243
+ * Prepare push options, based on local options and available peer info.
244
+ *
245
+ * @param options	Connection options
246
+ * @param tls_multi	TLS state structure for the current tunnel
247
+ *
248
+ * @return true on success, false on failure.
249
+ */
250
+static bool
251
+prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
252
+{
253
+  const char *optstr = NULL;
254
+  const char * const peer_info = tls_multi->peer_info;
255
+
256
+  /* Send peer-id if client supports it */
257
+  optstr = peer_info ? strstr(peer_info, "IV_PROTO=") : NULL;
258
+  if (optstr)
259
+    {
260
+      int proto = 0;
261
+      int r = sscanf(optstr, "IV_PROTO=%d", &proto);
262
+      if ((r == 1) && (proto >= 2))
263
+	{
264
+	  push_option_fmt(o, M_USAGE, "peer-id %d", tls_multi->peer_id);
265
+	}
266
+    }
267
+
268
+  /* Push cipher if client supports Negotiable Crypto Parameters */
269
+  optstr = peer_info ? strstr(peer_info, "IV_NCP=") : NULL;
270
+  if (optstr)
271
+    {
272
+      int ncp = 0;
273
+      int r = sscanf(optstr, "IV_NCP=%d", &ncp);
274
+      if ((r == 1) && (ncp == 2))
275
+	{
276
+	  push_option_fmt(o, M_USAGE, "cipher %s", o->ciphername);
277
+	}
278
+    }
279
+  return true;
280
+}
281
+
282
+static bool
243 283
 send_push_reply (struct context *c)
244 284
 {
245 285
   struct gc_arena gc = gc_new ();
... ...
@@ -309,19 +373,6 @@ send_push_reply (struct context *c)
309 309
   if (multi_push)
310 310
     buf_printf (&buf, ",push-continuation 1");
311 311
 
312
-  /* Send peer-id if client supports it */
313
-  if (c->c2.tls_multi->peer_info)
314
-    {
315
-      const char* proto_str = strstr(c->c2.tls_multi->peer_info, "IV_PROTO=");
316
-      if (proto_str)
317
-	{
318
-	  int proto = 0;
319
-	  int r = sscanf(proto_str, "IV_PROTO=%d", &proto);
320
-	  if ((r == 1) && (proto >= 2))
321
-	    buf_printf(&buf, ",peer-id %d", c->c2.tls_multi->peer_id);
322
-	}
323
-  }
324
-
325 312
   if (BLEN (&buf) > sizeof(cmd)-1)
326 313
     {
327 314
       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)
409 409
   push_option (o, opt, msglevel);
410 410
 }
411 411
 
412
+static bool push_option_fmt(struct options *o, int msglevel,
413
+    const char *format, ...)
414
+{
415
+  va_list arglist;
416
+  char tmp[256] = {0};
417
+  int len = -1;
418
+  va_start (arglist, format);
419
+  len = vsnprintf (tmp, sizeof(tmp), format, arglist);
420
+  va_end (arglist);
421
+  if (len > sizeof(tmp)-1)
422
+    return false;
423
+  push_option (o, string_alloc (tmp, &o->gc), msglevel);
424
+  return true;
425
+}
426
+
412 427
 void
413 428
 push_reset (struct options *o)
414 429
 {
... ...
@@ -442,7 +508,8 @@ process_incoming_push_request (struct context *c)
442 442
 	}
443 443
       else
444 444
 	{
445
-	  if (send_push_reply (c))
445
+	  if (prepare_push_reply(&c->options, c->c2.tls_multi) &&
446
+	      send_push_reply (c))
446 447
 	    {
447 448
 	      ret = PUSH_MSG_REQUEST;
448 449
 	      c->c2.sent_push_reply_expiry = now + 30;
... ...
@@ -62,8 +62,6 @@ void push_options (struct options *o, char **p, int msglevel, struct gc_arena *g
62 62
 
63 63
 void push_reset (struct options *o);
64 64
 
65
-bool send_push_reply (struct context *c);
66
-
67 65
 void remove_iroutes_from_push_route_list (struct options *o);
68 66
 
69 67
 void send_auth_failed (struct context *c, const char *client_reason);