Usage: --pull-filter accept|reject "option string" Permit a client to selectively accept or reject options pushed by the server. May be used multiple times. The filters are applied in the order specified to each pushed option received. The filtering stops as soon as a match is found. Full regex support is not available but partial matching is used to allow some flexibility. Thus
pull-filter accept "ifconfig 10.9.0." pull-filter reject "ifconfig " pull-filter accept "route 10." pull-filter reject "route " will reject assigned ip unless its in the "10.9.0.0/24" range and all pushed routes except those starting with "10.". Note the space at the end of "route " to not reject "route-gateway", for example. There is an implicit 'pull-filter accept ""' at the end so that all options not rejected by any filter are accepted. A maximum of 64 filters are supported. Acknowledges shameless imitation of --push-remove. Inspired by Trac #682. Signed-off-by: Selva Nair <[email protected]> --- Changes.rst | 4 ++ doc/openvpn.8 | 34 +++++++++++++++++ src/openvpn/options.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++ src/openvpn/options.h | 19 ++++++++++ 4 files changed, 155 insertions(+) diff --git a/Changes.rst b/Changes.rst index a6bb2a5..1ac3c2b 100644 --- a/Changes.rst +++ b/Changes.rst @@ -5,6 +5,10 @@ Version 2.4.0 New features ------------ +pull-filter + New option to explicitly allow or reject options pushed by the server. + May be used multiple times and is applied in the order specified. + push-remove new option to remove options on a per-client basis from the "push" list (more fine-grained than "push-reset") diff --git a/doc/openvpn.8 b/doc/openvpn.8 index e1dd7cd..aec37da 100644 --- a/doc/openvpn.8 +++ b/doc/openvpn.8 @@ -3830,6 +3830,40 @@ in situations where you don't trust the server to have control over the client's routing table. .\"********************************************************* .TP +.B \-\-pull\-filter accept|reject opt +Filter options matching +.B opt +pushed by the server: +.B accept +allows the option, +.B reject +removes it. May be specified multiple times, and each filter is +applied in the order it is specified. The filtering of each pushed +option stops as soon as a match (accept or reject) is found. + +Partial matching is used so that +.B \-\-pull\-filter reject route +would remove all pushed options starting with +.B route +which would include, for example, +.B route\-gateway. +Enclose the option string in quotes to embed spaces. + +.nf +.ft 3 +.in +4 +\-\-pull\-filter accept "route 192.168.1." +\-\-pull\-filter reject "route " +.in -4 +.ft +.fi + +would reject all pushed routes that do not start with +.B 192.168.1. + +A maximum of 64 filters are supported. May be used only on clients. +.\"********************************************************* +.TP .B \-\-auth\-user\-pass [up] Authenticate with server using username/password. .B up diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 55630c7..ad472ee 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -489,6 +489,10 @@ static const char usage_message[] = "--pull : Accept certain config file options from the peer as if they\n" " were part of the local config file. Must be specified\n" " when connecting to a '--mode server' remote host.\n" + "--pull-filter accept|reject opt : Selectively accept or reject an option pushed\n" + " by the server if it matches opt. Uses strncmp for comparison,\n" + " so partial matching is possible. May be specified multiple\n" + " times, and each filter is applied in the order of appearance.\n" "--auth-retry t : How to handle auth failures. Set t to\n" " none (default), interact, or nointeract.\n" "--static-challenge t e : Enable static challenge/response protocol using\n" @@ -1407,6 +1411,21 @@ show_connection_entries (const struct options *o) msg (D_SHOW_PARMS, "Connection profiles END"); } +static void +show_pull_filter_list (const struct pull_filter_list *l) +{ + int i; + if (!l) + return; + + msg (D_SHOW_PARMS, " Pull filters:"); + for (i = 0; i < l->len; i++) + { + msg (D_SHOW_PARMS, " %s \"%s\"", l->array[i]->type == PUF_TYPE_REJECT ? + "reject " : "accept ", l->array[i]->filter); + } +} + #endif void @@ -1537,6 +1556,8 @@ show_settings (const struct options *o) SHOW_BOOL (route_nopull); SHOW_BOOL (route_gateway_via_dhcp); SHOW_BOOL (allow_pull_fqdn); + show_pull_filter_list (o->pull_filter_list); + if (o->routes) print_route_options (o->routes, D_SHOW_PARMS); @@ -1797,6 +1818,31 @@ alloc_remote_entry (struct options *options, const int msglevel) return e; } +static struct pull_filter_list * +alloc_pull_filter_list (struct options *o) +{ + if (!o->pull_filter_list) + ALLOC_OBJ_CLEAR_GC (o->pull_filter_list, struct pull_filter_list, &o->gc); + return o->pull_filter_list; +} + +static struct pull_filter * +alloc_pull_filter (struct options *o, const int msglevel) +{ + struct pull_filter_list *l = alloc_pull_filter_list (o); + struct pull_filter *f; + + if (l->len >= PULL_FILTER_LIST_SIZE) + { + msg (msglevel, "Maximum number of 'pull-filter' options (%d) exceeded", + PULL_FILTER_LIST_SIZE); + return NULL; + } + ALLOC_OBJ_GC (f, struct pull_filter, &o->gc); + l->array[l->len++] = f; + return f; +} + void connection_entry_load_re (struct connection_entry *ce, const struct remote_entry *re) { @@ -2000,6 +2046,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne msg (M_USAGE, "--mode server only works with --dev tun or --dev tap"); if (options->pull) msg (M_USAGE, "--pull cannot be used with --mode server"); + if (options->pull_filter_list) + msg (M_USAGE, "--pull-filter cannot be used with --mode server"); if (!(proto_is_udp(ce->proto) || ce->proto == PROTO_TCP_SERVER)) msg (M_USAGE, "--mode server currently only supports " "--proto udp or --proto tcp-server or proto tcp6-server"); @@ -3964,6 +4012,32 @@ parse_argv (struct options *options, } } +static bool +apply_pull_filter (const struct options *o, const char *line) +{ + int i; + struct pull_filter_list *l = o->pull_filter_list; + + if (!l) return true; + + for (i = 0; i < l->len; i++) + { + struct pull_filter *f = l->array[i]; + if (f->type == PUF_TYPE_REJECT && strncmp (f->filter, line, f->size) == 0) + { + msg (D_PUSH, "Pushed option rejected by filter: '%s'", line); + return false; + } + else if (f->type == PUF_TYPE_ACCEPT && strncmp (f->filter, line, f->size) == 0) + { + msg (D_LOW, "Pushed option accepted by filter: '%s'", line); + return true; + } + } + /* accept by default */ + return true; +} + bool apply_push_options (struct options *options, struct buffer *buf, @@ -3981,6 +4055,10 @@ apply_push_options (struct options *options, char *p[MAX_PARMS]; CLEAR (p); ++line_num; + if (!apply_pull_filter(options, line)) + { + continue; + } if (parse_line (line, p, SIZE (p), file, line_num, msglevel, &options->gc)) { add_option (options, p, file, line_num, 0, msglevel, permission_mask, option_types_found, es); @@ -5373,6 +5451,26 @@ add_option (struct options *options, VERIFY_PERMISSION (OPT_P_GENERAL); options->route_nopull = true; } + else if (streq (p[0], "pull-filter") && p[1] && p[2] && !p[3]) + { + VERIFY_PERMISSION (OPT_P_GENERAL) + struct pull_filter *f = alloc_pull_filter (options, msglevel); + + /* f could be NULL if number of filters exceeded the limit */ + if (!f) + goto err; + if (strcmp ("accept", p[1]) == 0) + f->type = PUF_TYPE_ACCEPT; + else if (strcmp ("reject", p[1]) == 0) + f->type = PUF_TYPE_REJECT; + else + { + msg (msglevel, "Unknown --pull-filter type: %s", p[1]); + goto err; + } + f->filter = p[2]; + f->size = strlen(p[2]); + } else if (streq (p[0], "allow-pull-fqdn") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); diff --git a/src/openvpn/options.h b/src/openvpn/options.h index 2fa375f..b73bafc 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -163,6 +163,23 @@ struct remote_host_store char port[RH_PORT_LEN]; }; +#define PULL_FILTER_LIST_SIZE 64 +struct pull_filter +{ +# define PUF_TYPE_UNDEF 0 +# define PUF_TYPE_ACCEPT 1 +# define PUF_TYPE_REJECT 2 + int type; + int size; + char *filter; +}; + +struct pull_filter_list +{ + int len; + struct pull_filter *array[PULL_FILTER_LIST_SIZE]; +}; + /* Command line options */ struct options { @@ -597,6 +614,8 @@ struct options const char *keying_material_exporter_label; int keying_material_exporter_length; #endif + + struct pull_filter_list *pull_filter_list; }; #define streq(x, y) (!strcmp((x), (y))) -- 1.7.10.4
