From 14d19b3e9a3e8bb4da5e2d8097ccf3c0693f8108 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Mon, 7 Dec 2015 11:15:54 +0100 Subject: [PATCH] Enhanced configuration option 'restrict-user-to-ports' This enhancement allows to negate the rules and allow the user connecting to all ports except the specified. --- doc/sample.config | 3 +++ src/config-ports.c | 27 +++++++++++++++++++++++--- src/ipc.proto | 2 ++ src/main-user.c | 20 ++++++++++++++++---- src/ocserv-args.def | 3 +++ src/ocserv-fw | 45 ++++++++++++++++++++++++++++++++++++++++---- tests/port-parsing.c | 15 +++++++++++++++ 7 files changed, 104 insertions(+), 11 deletions(-) diff --git a/doc/sample.config b/doc/sample.config index 63c9096a..14f069ac 100644 --- a/doc/sample.config +++ b/doc/sample.config @@ -480,6 +480,9 @@ no-route = 192.168.5.0/255.255.255.0 # or in the per-user configuration. #restrict-user-to-ports = "tcp(443), tcp(80), udp(443), sctp(99), tcp(583), icmp(), icmpv6()" +# You could also use negation, i.e., block the user from accessing these ports only. +#restrict-user-to-ports = "!(tcp(443), tcp(80))" + # When set to true, all client's iroutes are made visible to all # connecting clients except for the ones offering them. This option # only makes sense if config-per-user is set. diff --git a/src/config-ports.c b/src/config-ports.c index 71236558..b3470576 100644 --- a/src/config-ports.c +++ b/src/config-ports.c @@ -27,7 +27,7 @@ #include -static int append_port(void *pool, FwPortSt ***fw_ports, size_t *n_fw_ports, int port, fw_proto_t proto) +static int append_port(void *pool, FwPortSt ***fw_ports, size_t *n_fw_ports, int port, fw_proto_t proto, unsigned negate) { FwPortSt *current; @@ -45,6 +45,7 @@ static int append_port(void *pool, FwPortSt ***fw_ports, size_t *n_fw_ports, int current->port = port; current->proto = proto; + current->negate = negate; (*fw_ports)[*n_fw_ports] = current; (*n_fw_ports)++; @@ -61,13 +62,33 @@ int cfg_parse_ports(void *pool, FwPortSt ***fw_ports, size_t *n_fw_ports, const unsigned finish = 0; int port, ret; fw_proto_t proto; + int negate = 0, bracket_start = 0; if (str == NULL) return 0; p = str; + while (c_isspace(*p)) + p++; + + if (*p == '!') { + negate = 1; + p++; + while (c_isspace(*p) || (*p == '(')) { + if (*p == '(') + bracket_start = 1; + p++; + } + + if (bracket_start == 0) { + syslog(LOG_ERR, "no bracket following negation at %d '%s'", (int)(ptrdiff_t)(p-str), str); + return -1; + } + } + do { + while (c_isspace(*p)) p++; @@ -105,7 +126,7 @@ int cfg_parse_ports(void *pool, FwPortSt ***fw_ports, size_t *n_fw_ports, const p++; port = atoi(p); - ret = append_port(pool, fw_ports, n_fw_ports, port, proto); + ret = append_port(pool, fw_ports, n_fw_ports, port, proto, negate); if (ret < 0) { syslog(LOG_ERR, "memory error"); return -1; @@ -121,7 +142,7 @@ int cfg_parse_ports(void *pool, FwPortSt ***fw_ports, size_t *n_fw_ports, const while (c_isspace(*p2)) p2++; - if (*p2 == 0) { + if (*p2 == 0 || (negate != 0 && *p2 == ')')) { finish = 1; } else if (*p2 != ',') { syslog(LOG_ERR, "expected comma or end of line on restrict-user-to-ports at %d '%s'", (int)(ptrdiff_t)(p2-str), str); diff --git a/src/ipc.proto b/src/ipc.proto index 85afa54d..cce3fe9c 100644 --- a/src/ipc.proto +++ b/src/ipc.proto @@ -18,6 +18,8 @@ message fw_port_st required uint32 port = 1; /* fw_proto_t */ required uint32 proto = 2; + /* negative rule, i.e., if non zero reject this port */ + required uint32 negate = 3; } /* This is a structure for per-user/group supplemental configuration. diff --git a/src/main-user.c b/src/main-user.c index af54f22b..0b054c8d 100644 --- a/src/main-user.c +++ b/src/main-user.c @@ -62,7 +62,7 @@ static void export_fw_info(main_server_st *s, struct proc_st* proc) str_st str4; str_st str6; str_st str_common; - unsigned i; + unsigned i, negate = 0; int ret; str_init(&str4, proc); @@ -188,6 +188,9 @@ static void export_fw_info(main_server_st *s, struct proc_st* proc) if (proc->config->n_fw_ports > 0) { for (i=0;iconfig->n_fw_ports;i++) { + if (proc->config->fw_ports[i]->negate) + negate = 1; + switch(proc->config->fw_ports[i]->proto) { case PROTO_UDP: ret = str_append_printf(&str_common, "udp %u ", proc->config->fw_ports[i]->port); @@ -216,9 +219,18 @@ static void export_fw_info(main_server_st *s, struct proc_st* proc) } } - if (str_common.length > 0 && setenv("OCSERV_PORTS", (char*)str_common.data, 1) == -1) { - mslog(s, proc, LOG_ERR, "could not export PORTS\n"); - exit(1); + if (str_common.length > 0) { + if (negate) { + if (setenv("OCSERV_DENY_PORTS", (char*)str_common.data, 1) == -1) { + mslog(s, proc, LOG_ERR, "could not export DENY_PORTS\n"); + exit(1); + } + } else { + if (setenv("OCSERV_ALLOW_PORTS", (char*)str_common.data, 1) == -1) { + mslog(s, proc, LOG_ERR, "could not export ALLOW_PORTS\n"); + exit(1); + } + } } str_clear(&str_common); diff --git a/src/ocserv-args.def b/src/ocserv-args.def index c84899d2..c5d543ee 100644 --- a/src/ocserv-args.def +++ b/src/ocserv-args.def @@ -568,6 +568,9 @@ no-route = 192.168.5.0/255.255.255.0 # or in the per-user configuration. #restrict-user-to-ports = "tcp(443), tcp(80), udp(443), sctp(99), tcp(583), icmp(), icmpv6()" +# You could also use negation, i.e., block the user from accessing these ports only. +#restrict-user-to-ports = "!(tcp(443), tcp(80))" + # When set to true, all client's iroutes are made visible to all # connecting clients except for the ones offering them. This option # only makes sense if config-per-user is set. diff --git a/src/ocserv-fw b/src/ocserv-fw index d99ef348..892c1d33 100755 --- a/src/ocserv-fw +++ b/src/ocserv-fw @@ -124,6 +124,24 @@ allow_port() { esac } +deny_port() { + proto=$1 + port=$2 + + case "$proto" in + icmp) + iptables -A INPUT -i ${DEVICE} -p $proto -j REJECT --match comment --comment "${COMMENT}" + ;; + icmpv6) + ip6tables -A INPUT -i ${DEVICE} -p $proto -j REJECT --match comment --comment "${COMMENT}" + ;; + *) + iptables -A INPUT -i ${DEVICE} -p $proto --dport $port -j REJECT --match comment --comment "${COMMENT}" + ip6tables -A INPUT -i ${DEVICE} -p $proto --dport $port -j REJECT --match comment --comment "${COMMENT}" + ;; + esac +} + disallow_all_ports() { iptables -A INPUT -i ${DEVICE} -j REJECT --match comment --comment "${COMMENT}" ip6tables -A INPUT -i ${DEVICE} -j REJECT --match comment --comment "${COMMENT}" @@ -140,24 +158,43 @@ for i in $OCSERV_DNS6;do done # block ports - if needed -if test -n "${OCSERV_PORTS}";then +if test -n "${OCSERV_DENY_PORTS}";then INPUT_CHAIN="${SEC_INPUT_CHAIN}" iptables -N "${INPUT_CHAIN}" ip6tables -N "${INPUT_CHAIN}" - set ${OCSERV_PORTS} + set ${OCSERV_DENY_PORTS} while test $# -gt 1; do proto=$1 port=$2 - allow_port $proto $port + deny_port $proto $port if test $# -gt 1;then shift 2 else break fi done - disallow_all_ports +else + if test -n "${OCSERV_ALLOW_PORTS}";then + INPUT_CHAIN="${SEC_INPUT_CHAIN}" + iptables -N "${INPUT_CHAIN}" + ip6tables -N "${INPUT_CHAIN}" + + set ${OCSERV_ALLOW_PORTS} + while test $# -gt 1; do + proto=$1 + port=$2 + + allow_port $proto $port + if test $# -gt 1;then + shift 2 + else + break + fi + done + disallow_all_ports + fi fi if test "${OCSERV_RESTRICT_TO_ROUTES}" = "1";then diff --git a/tests/port-parsing.c b/tests/port-parsing.c index f3343855..4eef1bbe 100644 --- a/tests/port-parsing.c +++ b/tests/port-parsing.c @@ -108,5 +108,20 @@ int main() exit(1); } + reset(fw_ports, n_fw_ports); + strcpy(p, "!(icmp(), tcp(88), udp(90), sctp(70), tcp(443), udp(80), icmpv6())"); + + ret = cfg_parse_ports(NULL, &fw_ports, &n_fw_ports, p); + if (ret < 0) { + fprintf(stderr, "error in %d\n", __LINE__); + exit(1); + } + + check_vals(fw_ports, n_fw_ports); + if (fw_ports[0]->negate == 0) { + fprintf(stderr, "error in %d\n", __LINE__); + exit(1); + } + return 0; }