Merge branch 'issue360' into 'master'

Don't apply BanIP checks to clients on the same subnet.

Closes #360

See merge request openconnect/ocserv!222
This commit is contained in:
Alan Jowett
2020-10-01 21:22:45 +00:00
6 changed files with 238 additions and 2 deletions

View File

@@ -43,6 +43,10 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <ccan/hash/hash.h> #include <ccan/hash/hash.h>
#include <ccan/htable/htable.h> #include <ccan/htable/htable.h>
#include <ifaddrs.h>
#include <sys/socket.h>
static bool if_address_test_local(main_server_st * s, struct sockaddr_storage *addr);
static size_t rehash(const void *_e, void *unused) static size_t rehash(const void *_e, void *unused)
{ {
@@ -270,6 +274,11 @@ unsigned check_if_banned(main_server_st *s, struct sockaddr_storage *addr, sockl
(void)(txt); (void)(txt);
if (if_address_test_local(s, addr)) {
mslog(s, NULL, LOG_DEBUG, "Not applying ban to local IP: %s", human_addr2((struct sockaddr*)addr, addr_size, txt, sizeof(txt), 0));
return 0;
}
in_size = SA_IN_SIZE(addr_size); in_size = SA_IN_SIZE(addr_size);
if (in_size != 4 && in_size != 16) { if (in_size != 4 && in_size != 16) {
mslog(s, NULL, LOG_ERR, "unknown address type for %s", human_addr2((struct sockaddr*)addr, addr_size, txt, sizeof(txt), 0)); mslog(s, NULL, LOG_ERR, "unknown address type for %s", human_addr2((struct sockaddr*)addr, addr_size, txt, sizeof(txt), 0));
@@ -320,3 +329,118 @@ void cleanup_banned_entries(main_server_st *s)
} }
} }
int if_address_init(main_server_st *s)
{
struct ifaddrs * ifaddr = NULL, *ifa;
if_address_st * local_if_addresses = NULL;
int retval = 0;
unsigned count = 0;
s->if_addresses_count = 0;
s->if_addresses = NULL;
if (getifaddrs(&ifaddr) < 0) {
int err = errno;
fprintf(stderr, "Failed to read local if address list: %s", strerror(err));
goto cleanup;
}
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == NULL) {
continue;
}
count ++;
}
local_if_addresses = talloc_array(s, if_address_st, count);
if (local_if_addresses == NULL) {
fprintf(stderr, "Failed to allocate");
goto cleanup;
}
count = 0;
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
sa_family_t family;
if (ifa->ifa_addr == NULL) {
continue;
}
family = ifa->ifa_addr->sa_family;
if (family == AF_INET || family == AF_INET6) {
memcpy(&local_if_addresses[count].if_addr, ifa->ifa_addr, sizeof(struct sockaddr));
memcpy(&local_if_addresses[count].if_netmask, ifa->ifa_netmask, sizeof(struct sockaddr));
count++;
}
}
s->if_addresses = local_if_addresses;
s->if_addresses_count = count;
local_if_addresses = NULL;
retval = 1;
cleanup:
if (ifaddr != NULL)
freeifaddrs(ifaddr);
if (local_if_addresses != NULL)
talloc_free(local_if_addresses);
return retval;
}
static bool test_local_ipv4(struct sockaddr_in * remote, struct sockaddr_in * local, struct sockaddr_in * network)
{
uint32_t l = local->sin_addr.s_addr & network->sin_addr.s_addr;
uint32_t r = remote->sin_addr.s_addr & network->sin_addr.s_addr;
if (l != r)
return false;
else
return true;
}
static bool test_local_ipv6(struct sockaddr_in6 * remote, struct sockaddr_in6 * local, struct sockaddr_in6 * network)
{
unsigned index = 0;
for (index = 0; index < 4; index ++) {
uint32_t l = local->sin6_addr.s6_addr32[index] & network->sin6_addr.s6_addr32[index];
uint32_t r = remote->sin6_addr.s6_addr32[index] & network->sin6_addr.s6_addr32[index];
if (l != r)
return false;
}
return true;
}
static bool if_address_test_local(main_server_st * s, struct sockaddr_storage *addr)
{
unsigned index;
for (index = 0; index < s->if_addresses_count; index ++)
{
if_address_st * ifa = &s->if_addresses[index];
if (ifa->if_addr.sa_family != addr->ss_family)
continue;
switch (addr->ss_family) {
case AF_INET:
if (test_local_ipv4((struct sockaddr_in *)addr, (struct sockaddr_in *)&ifa->if_addr, (struct sockaddr_in *)&ifa->if_netmask))
return true;
break;
case AF_INET6:
if (test_local_ipv6((struct sockaddr_in6 *)addr, (struct sockaddr_in6 *)&ifa->if_addr, (struct sockaddr_in6 *)&ifa->if_netmask))
return true;
break;
default:
break;
}
}
return false;
}
void if_address_cleanup(main_server_st * s)
{
if (s->if_addresses)
talloc_free(s->if_addresses);
s->if_addresses = NULL;
s->if_addresses_count = 0;
}

View File

@@ -44,4 +44,7 @@ unsigned main_ban_db_elems(main_server_st *s);
void main_ban_db_deinit(main_server_st *s); void main_ban_db_deinit(main_server_st *s);
void *main_ban_db_init(main_server_st *s); void *main_ban_db_init(main_server_st *s);
int if_address_init(main_server_st *s);
void if_address_cleanup(main_server_st * s);
#endif #endif

View File

@@ -542,6 +542,7 @@ void clear_lists(main_server_st *s)
proc_table_deinit(s); proc_table_deinit(s);
ctl_handler_deinit(s); ctl_handler_deinit(s);
main_ban_db_deinit(s); main_ban_db_deinit(s);
if_address_cleanup(s);
/* clear libev state */ /* clear libev state */
if (loop) { if (loop) {
@@ -1441,6 +1442,11 @@ int main(int argc, char** argv)
ip_lease_init(&s->ip_leases); ip_lease_init(&s->ip_leases);
proc_table_init(s); proc_table_init(s);
main_ban_db_init(s); main_ban_db_init(s);
if (if_address_init(s) == 0)
{
fprintf(stderr, "failed to initialize local addresses\n");
exit(1);
}
sigemptyset(&sig_default_set); sigemptyset(&sig_default_set);

View File

@@ -24,6 +24,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/resource.h>
#include <unistd.h> #include <unistd.h>
#include <net/if.h> #include <net/if.h>
#include <vpn.h> #include <vpn.h>
@@ -261,6 +262,11 @@ typedef struct sec_mod_instance_st {
} sec_mod_instance_st; } sec_mod_instance_st;
typedef struct if_address_st {
struct sockaddr if_addr;
struct sockaddr if_netmask;
} if_address_st;
typedef struct main_server_st { typedef struct main_server_st {
/* virtual hosts are only being added to that list, never removed */ /* virtual hosts are only being added to that list, never removed */
struct list_head *vconfig; struct list_head *vconfig;
@@ -301,6 +307,9 @@ typedef struct main_server_st {
#ifdef RLIMIT_NOFILE #ifdef RLIMIT_NOFILE
struct rlimit fd_limits_default_set; struct rlimit fd_limits_default_set;
#endif #endif
struct if_address_st * if_addresses;
unsigned int if_addresses_count;
} main_server_st; } main_server_st;
void clear_lists(main_server_st *s); void clear_lists(main_server_st *s);

View File

@@ -57,10 +57,10 @@ if ENABLE_ROOT_TESTS
#other root requiring tests #other root requiring tests
dist_check_SCRIPTS += haproxy-connect test-iroute test-multi-cookie test-pass-script \ dist_check_SCRIPTS += haproxy-connect test-iroute test-multi-cookie test-pass-script \
test-cookie-timeout test-cookie-timeout-2 test-explicit-ip \ test-cookie-timeout test-cookie-timeout-2 test-explicit-ip \
test-cookie-invalidation test-user-config test-append-routes test-ban \ test-cookie-invalidation test-user-config test-append-routes test-ban \
multiple-routes json test-udp-listen-host test-max-same-1 test-script-multi-user \ multiple-routes json test-udp-listen-host test-max-same-1 test-script-multi-user \
apple-ios ipv6-iface test-namespace-listen disconnect-user disconnect-user2 \ apple-ios ipv6-iface test-namespace-listen disconnect-user disconnect-user2 \
ping-leases ping-leases test-ban-local
if RADIUS_ENABLED if RADIUS_ENABLED
dist_check_SCRIPTS += radius-group radius-otp dist_check_SCRIPTS += radius-group radius-otp

94
tests/test-ban-local Executable file
View File

@@ -0,0 +1,94 @@
#!/bin/bash
#
# Copyright (C) 2015 Red Hat
#
# This file is part of ocserv.
#
# ocserv is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at
# your option) any later version.
#
# ocserv is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GnuTLS; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
OCCTL="${OCCTL:-../src/occtl/occtl}"
SERV="${SERV:-../src/ocserv}"
srcdir=${srcdir:-.}
OCCTL_SOCKET=./occtl-ban-$$.socket
PIDFILE=ocserv-pid.$$.tmp
OUTFILE=ban.$$.tmp
ADDRESS=127.0.0.1
VPNNET=172.17.215.0/24
VPNADDR=172.17.215.1
VPNNET6=fc39:d561:62c6:861b:9f38:9734:9fa1:0/112
VPNADDR6=fc39:d561:62c6:861b:9f38:9734:9fa1:0
. `dirname $0`/common.sh
eval "${GETPORT}"
update_config test-ban.config
if test "$VERBOSE" = 1;then
DEBUG="-d 3"
fi
function finish {
set +e
echo " * Cleaning up..."
test -n "${PID}" && kill ${PID} >/dev/null 2>&1
test -n "${PIDFILE}" && rm -f ${PIDFILE} >/dev/null 2>&1
test -n "${CONFIG}" && rm -f ${CONFIG} >/dev/null 2>&1
test -n "${OUTFILE}" && rm -f ${OUTFILE} >/dev/null 2>&1
}
trap finish EXIT
echo "Testing whether ban operates as expected on a local subnet ... "
echo $PORT
${SERV} -p ${PIDFILE} -f -c ${CONFIG} ${DEBUG} & PID=$!
sleep 4
echo "Connecting with wrong password 5 times... "
echo "notest" | ${OPENCONNECT} --passwd-on-stdin -q ${ADDRESS}:${PORT} -u test --authenticate --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3
echo "notest" | ${OPENCONNECT} --passwd-on-stdin -q ${ADDRESS}:${PORT} -u test --authenticate --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3
echo "notest" | ${OPENCONNECT} --passwd-on-stdin -q ${ADDRESS}:${PORT} -u test --authenticate --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3
echo "notest" | ${OPENCONNECT} --passwd-on-stdin -q ${ADDRESS}:${PORT} -u test --authenticate --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3
echo "notest" | ${OPENCONNECT} --passwd-on-stdin -q ${ADDRESS}:${PORT} -u test --authenticate --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3
echo ""
echo "Connecting with correct password... "
eval `echo "test" | ${OPENCONNECT} --passwd-on-stdin -q ${ADDRESS}:${PORT} -u test --authenticate --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3`
if [ -z "$COOKIE" ];then
fail $PID "Could not obtain cookie even though client should be exempt"
fi
${OCCTL} -s ${OCCTL_SOCKET} show status >${OUTFILE}
if test $? != 0;then
echo "occtl couldn't run!"
exit 1
fi
grep "IPs in ban list: 1$" ${OUTFILE}
if test $? != 0;then
echo "The banned list didn't contain the expected (1) entries!"
cat ${OUTFILE}
exit 1
fi
kill $PID
wait
exit 0