Browse code

Implement push-remove option to selectively remove pushed options.

With this option, the server can remove individual options from the
set pushed to a client (call from --client-config-dir file, or from
--client-connect script or plugin). Options are removed at parse
time, so it is possible to do stuff like:

push-remove route-ipv6
push "route-ipv6 fd00::/8"

to first remove all IPv6 route options set so far, then add something
specific (what "push-reset" does to all the options).

Arguments to push-remove are strncmp()'ed to option string, so partial
matches like

push-remove "route-ipv6 2001:"

are possible ("remove all IPv6 routes starting with 2001:").

Implementation of remove_iroutes_from_push_route_list() had to be changed
slightly to stop it from re-enabling all disabled options again.

v2: documentation (Changes.rst, doc/openvpn.8)
remove surplus gc_arena
implement filtering of "ifconfig-ipv6"

v3: correct quoting in commit message
only handle a single argument per push-remove statement - if multiple
options are to be removed, just use multiple push-remove statements

Trac #29, #614

Signed-off-by: Gert Doering <gert@greenie.muc.de>
Acked-by: Arne Schwabe <arne@rfc2549.org>
Message-Id: <1463393584-8318-1-git-send-email-gert@greenie.muc.de>
URL: http://article.gmane.org/gmane.network.openvpn.devel/11665
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Gert Doering authored on 2016/05/16 19:13:04
Showing 6 changed files
... ...
@@ -5,6 +5,10 @@ Version 2.4.0
5 5
 New features
6 6
 ------------
7 7
 
8
+push-remove
9
+    new option to remove options on a per-client basis from the "push" list
10
+    (more fine-grained than "push-reset")
11
+
8 12
 keying-material-exporter
9 13
     Keying Material Exporter [RFC-5705] allow additional keying material to be
10 14
     derived from existing TLS channel.
... ...
@@ -2965,6 +2965,39 @@ as with a
2965 2965
 configuration file.  This option will ignore
2966 2966
 .B \-\-push
2967 2967
 options at the global config file level.
2968
+.\"*********************************************************
2969
+.TP
2970
+.B \-\-push\-remove opt
2971
+selectively remove all
2972
+.B \-\-push
2973
+options matching "opt" from the option list for a client.  "opt" is matched
2974
+as a substring against the whole option string to-be-pushed to the client, so
2975
+.B \-\-push\-remove route
2976
+would remove all
2977
+.B \-\-push route ...
2978
+and
2979
+.B \-\-push route-ipv6 ...
2980
+statements, while
2981
+.B \-\-push\-remove 'route-ipv6 2001:'
2982
+would only remove IPv6 routes for 2001:... networks.
2983
+
2984
+.B \-\-push\-remove
2985
+can only be used in a client-specific context, like in a
2986
+.B \-\-client\-config\-dir
2987
+file, or
2988
+.B \-\-client\-connect
2989
+script or plugin -- similar to
2990
+.B \-\-push\-reset,
2991
+just more selective.
2992
+
2993
+NOTE: to
2994
+.I change
2995
+an option,
2996
+.B \-\-push\-remove
2997
+can be used to first remove the old value, and then add a new
2998
+.B \-\-push
2999
+option with the new value.
3000
+.\"*********************************************************
2968 3001
 .TP
2969 3002
 .B \-\-push\-peer\-info
2970 3003
 Push additional information about the client to server.  The additional information
... ...
@@ -3289,7 +3322,7 @@ without needing to restart the server.
3289 3289
 
3290 3290
 The following
3291 3291
 options are legal in a client-specific context:
3292
-.B \-\-push, \-\-push\-reset, \-\-iroute, \-\-ifconfig\-push,
3292
+.B \-\-push, \-\-push\-reset, \-\-push\-remove, \-\-iroute, \-\-ifconfig\-push,
3293 3293
 and
3294 3294
 .B \-\-config.
3295 3295
 .\"*********************************************************
... ...
@@ -5582,6 +5582,11 @@ add_option (struct options *options,
5582 5582
       VERIFY_PERMISSION (OPT_P_INSTANCE);
5583 5583
       push_reset (options);
5584 5584
     }
5585
+  else if (streq (p[0], "push-remove") && p[1] && !p[2])
5586
+    {
5587
+      VERIFY_PERMISSION (OPT_P_INSTANCE);
5588
+      push_remove_option (options,p[1]);
5589
+    }
5585 5590
   else if (streq (p[0], "ifconfig-pool") && p[1] && p[2] && !p[4])
5586 5591
     {
5587 5592
       const int lev = M_WARN;
... ...
@@ -5930,6 +5935,7 @@ add_option (struct options *options,
5930 5930
       options->push_ifconfig_ipv6_local = local;
5931 5931
       options->push_ifconfig_ipv6_netbits = netbits;
5932 5932
       options->push_ifconfig_ipv6_remote = remote;
5933
+      options->push_ifconfig_ipv6_blocked = false;
5933 5934
     }
5934 5935
   else if (streq (p[0], "disable") && !p[1])
5935 5936
     {
... ...
@@ -432,6 +432,7 @@ struct options
432 432
   struct in6_addr push_ifconfig_ipv6_local;		/* IPv6 */
433 433
   int 		  push_ifconfig_ipv6_netbits;		/* IPv6 */
434 434
   struct in6_addr push_ifconfig_ipv6_remote;		/* IPv6 */
435
+  bool            push_ifconfig_ipv6_blocked;		/* IPv6 */
435 436
   bool enable_c2c;
436 437
   bool duplicate_cn;
437 438
   int cf_max;
... ...
@@ -322,7 +322,8 @@ send_push_reply (struct context *c)
322 322
 
323 323
   buf_printf (&buf, "%s", cmd);
324 324
 
325
-  if ( c->c2.push_ifconfig_ipv6_defined )
325
+  if ( c->c2.push_ifconfig_ipv6_defined &&
326
+          !c->options.push_ifconfig_ipv6_blocked )
326 327
     {
327 328
       /* IPv6 is put into buffer first, could be lengthy */
328 329
       buf_printf( &buf, ",ifconfig-ipv6 %s/%d %s",
... ...
@@ -483,6 +484,37 @@ push_reset (struct options *o)
483 483
 {
484 484
   CLEAR (o->push_list);
485 485
 }
486
+
487
+void
488
+push_remove_option (struct options *o, const char *p)
489
+{
490
+  msg( D_PUSH, "PUSH_REMOVE '%s'", p );
491
+
492
+  /* ifconfig-ipv6 is special, as not part of the push list */
493
+  if ( streq( p, "ifconfig-ipv6" ))
494
+    {
495
+      o->push_ifconfig_ipv6_blocked = true;
496
+      return;
497
+    }
498
+
499
+  if (o && o->push_list.head )
500
+    {
501
+      struct push_entry *e = o->push_list.head;
502
+
503
+      /* cycle through the push list */
504
+      while (e)
505
+	{
506
+	  if ( e->enable &&
507
+               strncmp( e->option, p, strlen(p) ) == 0 )
508
+	    {
509
+	      msg (D_PUSH, "PUSH_REMOVE removing: '%s'", e->option);
510
+	      e->enable = false;
511
+	    }
512
+
513
+	  e = e->next;
514
+	}
515
+    }
516
+}
486 517
 #endif
487 518
 
488 519
 #if P2MP_SERVER
... ...
@@ -613,7 +645,8 @@ remove_iroutes_from_push_route_list (struct options *o)
613 613
 
614 614
 	  /* parse the push item */
615 615
 	  CLEAR (p);
616
-	  if (parse_line (e->option, p, SIZE (p), "[PUSH_ROUTE_REMOVE]", 1, D_ROUTE_DEBUG, &gc))
616
+	  if ( e->enable &&
617
+               parse_line (e->option, p, SIZE (p), "[PUSH_ROUTE_REMOVE]", 1, D_ROUTE_DEBUG, &gc))
617 618
 	    {
618 619
 	      /* is the push item a route directive? */
619 620
 	      if (p[0] && !strcmp (p[0], "route") && !p[3])
... ...
@@ -639,12 +672,12 @@ remove_iroutes_from_push_route_list (struct options *o)
639 639
 			}
640 640
 		    }
641 641
 		}
642
-	    }
643 642
 
644
-	  /* should we copy the push item? */
645
-	  e->enable = enable;
646
-	  if (!enable)
647
-	    msg (D_PUSH, "REMOVE PUSH ROUTE: '%s'", e->option);
643
+	      /* should we copy the push item? */
644
+	      e->enable = enable;
645
+	      if (!enable)
646
+		msg (D_PUSH, "REMOVE PUSH ROUTE: '%s'", e->option);
647
+	    }
648 648
 
649 649
 	  e = e->next;
650 650
 	}
... ...
@@ -61,6 +61,7 @@ void push_option (struct options *o, const char *opt, int msglevel);
61 61
 void push_options (struct options *o, char **p, int msglevel, struct gc_arena *gc);
62 62
 
63 63
 void push_reset (struct options *o);
64
+void push_remove_option (struct options *o, const char *p);
64 65
 
65 66
 void remove_iroutes_from_push_route_list (struct options *o);
66 67