From 8aa39b01069dbddebc841a607325f1b2d2cce023 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Mon, 20 Jul 2020 22:27:18 +0200 Subject: [PATCH] Improved user disconnection to avoid race conditions Previously when we were disconnecting a user there were few seconds after which the cookie was still valid, so a reconnect would succeed by the same user. This change ensures that a disconnected (via occtl) user cannot re-use the same cookie to connect. That enables a safe user removal from the authentication database, and from run-time. Resolves: #59 Signed-off-by: Nikos Mavrogiannopoulos --- NEWS | 1 + src/config.c | 2 + src/ipc.proto | 1 + src/main-auth.c | 24 ++-- src/main-ctl-unix.c | 5 +- src/main-sec-mod-cmd.c | 5 +- src/main.h | 18 ++- src/sec-mod-auth.c | 7 +- src/vpn.h | 4 +- src/worker-vpn.c | 11 +- tests/Makefile.am | 5 +- tests/data/disconnect-user.config | 188 +++++++++++++++++++++++++++++ tests/data/disconnect-user2.config | 186 ++++++++++++++++++++++++++++ tests/data/test-traffic.config | 6 - tests/disconnect-user | 121 +++++++++++++++++++ tests/disconnect-user2 | 119 ++++++++++++++++++ 16 files changed, 674 insertions(+), 29 deletions(-) create mode 100644 tests/data/disconnect-user.config create mode 100644 tests/data/disconnect-user2.config create mode 100755 tests/disconnect-user create mode 100755 tests/disconnect-user2 diff --git a/NEWS b/NEWS index 14acb460..da02da76 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ - Disable TLS1.3 when cisco client compatibility is enabled. New anyconnect clients seem to supporting TLS1.3 but unable to handle a client with an RSA key (#318) +- Enable a race free user disconnection via occtl (#59) * Version 1.1.0 (released 2020-06-16) diff --git a/src/config.c b/src/config.c index abe6e879..1f1ab123 100644 --- a/src/config.c +++ b/src/config.c @@ -773,6 +773,8 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co READ_MULTI_LINE(vhost->perm_config.cert, vhost->perm_config.cert_size); } else if (strcmp(name, "server-key") == 0) { READ_MULTI_LINE(vhost->perm_config.key, vhost->perm_config.key_size); + } else if (strcmp(name, "debug-no-secmod-stats") == 0) { + READ_TF(vhost->perm_config.debug_no_secmod_stats); } else if (strcmp(name, "dh-params") == 0) { READ_STRING(vhost->perm_config.dh_params_file); } else if (strcmp(name, "pin-file") == 0) { diff --git a/src/ipc.proto b/src/ipc.proto index 613edd79..dc8739d7 100644 --- a/src/ipc.proto +++ b/src/ipc.proto @@ -322,6 +322,7 @@ message secm_session_close_msg optional uint64 bytes_out = 5; optional string ipv4 = 6; optional string ipv6 = 7; + required bool server_disconnected = 8 [default = false]; } /* SECM_STATS */ diff --git a/src/main-auth.c b/src/main-auth.c index 3a86ee3f..9021d224 100644 --- a/src/main-auth.c +++ b/src/main-auth.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2014, 2015 Nikos Mavrogiannopoulos + * Copyright (C) 2013-2020 Nikos Mavrogiannopoulos * Copyright (C) 2014, 2015 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify @@ -166,8 +166,8 @@ int send_cookie_auth_reply(main_server_st* s, struct proc_st* proc, int handle_auth_cookie_req(main_server_st* s, struct proc_st* proc, const AuthCookieRequestMsg * req) { -int ret; -struct proc_st *old_proc; + int ret; + struct proc_st *old_proc; if (req->cookie.data == NULL || req->cookie.len != sizeof(proc->sid)) return -1; @@ -179,6 +179,15 @@ struct proc_st *old_proc; return -1; proc->dtls_session_id_size = sizeof(proc->dtls_session_id); + /* check for a user with the same sid as in the cookie */ + old_proc = proc_search_sid(s, req->cookie.data); + if (old_proc != NULL) { + if (old_proc->invalidated != 0) { + mslog(s, proc, LOG_ERR, "the reused session has been invalidated"); + return -1; + } + } + /* loads sup config and basic proc info (e.g., username) */ ret = session_open(s, proc, req->cookie.data, req->cookie.len); if (ret < 0) { @@ -191,17 +200,16 @@ struct proc_st *old_proc; put_into_cgroup(s, proc->config->cgroup, proc->pid); } - /* check for a user with the same sid as in the cookie */ - old_proc = proc_search_sid(s, req->cookie.data); + /* disconnect and re-use previous session's IPs*/ if (old_proc != NULL) { - mslog(s, old_proc, LOG_INFO, "disconnecting previous user session due to session re-use"); - if (strcmp(proc->username, old_proc->username) != 0) { mslog(s, old_proc, LOG_ERR, "the user of the new session doesn't match the old (new: %s)", - proc->username); + proc->username); return -1; } + mslog(s, old_proc, LOG_INFO, "disconnecting previous user session due to session re-use"); + /* steal its leases */ steal_ip_leases(old_proc, proc); diff --git a/src/main-ctl-unix.c b/src/main-ctl-unix.c index b1980e21..4cbbd7f1 100644 --- a/src/main-ctl-unix.c +++ b/src/main-ctl-unix.c @@ -718,7 +718,7 @@ static void method_disconnect_user_name(method_ctx *ctx, /* got the name. Try to disconnect */ list_for_each_safe(&ctx->s->proc_list.head, ctmp, cpos, list) { if (strcmp(ctmp->username, req->username) == 0) { - terminate_proc(ctx->s, ctmp); + disconnect_proc(ctx->s, ctmp); rep.status = 1; } } @@ -755,8 +755,9 @@ static void method_disconnect_user_id(method_ctx *ctx, int cfd, /* got the ID. Try to disconnect */ list_for_each_safe(&ctx->s->proc_list.head, ctmp, cpos, list) { if (ctmp->pid == req->id) { - terminate_proc(ctx->s, ctmp); + disconnect_proc(ctx->s, ctmp); rep.status = 1; + if (req->id != -1) break; } diff --git a/src/main-sec-mod-cmd.c b/src/main-sec-mod-cmd.c index 59be5fc3..0447cce1 100644 --- a/src/main-sec-mod-cmd.c +++ b/src/main-sec-mod-cmd.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2015-2017 Red Hat, Inc. - * Copyright (C) 2015-2017 Nikos Mavrogiannopoulos + * Copyright (C) 2015-2020 Nikos Mavrogiannopoulos * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -658,6 +658,9 @@ int session_close(main_server_st * s, struct proc_st *proc) ireq.sid.data = proc->sid; ireq.sid.len = sizeof(proc->sid); + if (proc->invalidated) + ireq.server_disconnected = 1; + mslog(s, proc, LOG_DEBUG, "sending msg %s to sec-mod", cmd_request_to_str(CMD_SECM_SESSION_CLOSE)); ret = send_msg(proc, s->sec_mod_fd_sync, CMD_SECM_SESSION_CLOSE, diff --git a/src/main.h b/src/main.h index bae91949..0e535781 100644 --- a/src/main.h +++ b/src/main.h @@ -121,6 +121,10 @@ typedef struct proc_st { uint8_t sid[SID_SIZE]; unsigned active_sid; + /* non zero if the sid has been invalidated and must not be allowed + * to reconnect. */ + unsigned invalidated; + /* whether the host-update script has already been called */ unsigned host_updated; @@ -353,14 +357,18 @@ struct proc_st *new_proc(main_server_st * s, pid_t pid, int cmd_fd, void remove_proc(main_server_st* s, struct proc_st *proc, unsigned flags); void proc_to_zombie(main_server_st* s, struct proc_st *proc); -inline static void terminate_proc(main_server_st *s, proc_st *proc) +inline static void disconnect_proc(main_server_st *s, proc_st *proc) { - /* if it has an IP, send a signal so that we cleanup - * and get stats properly */ - if (proc->pid != -1 && proc->pid != 0) + /* make sure that the SID cannot be reused */ + proc->invalidated = 1; + + /* if it has a PID, send a signal so that we cleanup + * and sec-mod gets stats orderly */ + if (proc->pid != -1 && proc->pid != 0) { kill(proc->pid, SIGTERM); - else + } else { remove_proc(s, proc, RPROC_KILL); + } } void put_into_cgroup(main_server_st * s, const char* cgroup, pid_t pid); diff --git a/src/sec-mod-auth.c b/src/sec-mod-auth.c index 10b643fe..9ee87996 100644 --- a/src/sec-mod-auth.c +++ b/src/sec-mod-auth.c @@ -562,7 +562,7 @@ int handle_secm_session_close_cmd(sec_mod_st *sec, int fd, const SecmSessionClos e = find_client_entry(sec, req->sid.data); if (e == NULL) { seclog(sec, LOG_INFO, "session close but with non-existing SID"); - return send_msg(e, fd, CMD_SECM_CLI_STATS, &rep, + return send_msg(sec, fd, CMD_SECM_CLI_STATS, &rep, (pack_size_func) cli_stats_msg__get_packed_size, (pack_func) cli_stats_msg__pack); } @@ -585,6 +585,10 @@ int handle_secm_session_close_cmd(sec_mod_st *sec, int fd, const SecmSessionClos e->stats.bytes_out = req->bytes_out; } + if (req->server_disconnected) { + e->discon_reason = REASON_SERVER_DISCONNECT; + } + /* send reply */ rep.bytes_in = e->stats.bytes_in; rep.bytes_out = e->stats.bytes_out; @@ -607,7 +611,6 @@ int handle_secm_session_close_cmd(sec_mod_st *sec, int fd, const SecmSessionClos return 0; } - void handle_sec_auth_ban_ip_reply(sec_mod_st *sec, const BanIpReplyMsg *msg) { client_entry_st *e; diff --git a/src/vpn.h b/src/vpn.h index 2415b88b..5876bb21 100644 --- a/src/vpn.h +++ b/src/vpn.h @@ -381,7 +381,6 @@ struct perm_cfg_st { #ifdef ANYCONNECT_CLIENT_COMPAT char *cert_hash; #endif - unsigned int stats_reset_time; unsigned foreground; unsigned no_chdir; @@ -397,6 +396,9 @@ struct perm_cfg_st { unsigned int port; unsigned int udp_port; + /* for testing ocserv only */ + unsigned debug_no_secmod_stats; + /* attic, where old config allocated values are stored */ struct list_head attic; }; diff --git a/src/worker-vpn.c b/src/worker-vpn.c index bcd81fc5..55f4411a 100644 --- a/src/worker-vpn.c +++ b/src/worker-vpn.c @@ -106,7 +106,7 @@ static void link_mtu_set(worker_st * ws, unsigned mtu); static void handle_alarm(int signo) { if (global_ws) - exit_worker(global_ws); + exit_worker_reason(global_ws, terminate_reason); _exit(1); } @@ -471,6 +471,10 @@ void send_stats_to_secmod(worker_st * ws, time_t now, unsigned discon_reason) CliStatsMsg msg = CLI_STATS_MSG__INIT; int sd, ret, e; + /* this is only used by certain tests */ + if (WSPCONFIG(ws)->debug_no_secmod_stats != 0) + return; + ws->last_stats_msg = now; sd = connect_to_secmod(ws); @@ -745,8 +749,10 @@ void vpn_server(struct worker_st *ws) ocsignal(SIGALRM, handle_alarm); global_ws = ws; - if (GETCONFIG(ws)->auth_timeout) + if (GETCONFIG(ws)->auth_timeout) { + terminate_reason = REASON_SERVER_DISCONNECT; alarm(GETCONFIG(ws)->auth_timeout); + } /* do not allow this process to be traced. That * prevents worker processes tracing each other. */ @@ -1213,6 +1219,7 @@ int periodic_check(worker_st * ws, struct timespec *tnow, unsigned dpd) * freezes in the worker due to an unexpected block (due to worker * bug or kernel bug). In that case the worker will be killed due * the the alarm instead of hanging. */ + terminate_reason = REASON_SERVER_DISCONNECT; alarm(1800); if (WSCONFIG(ws)->idle_timeout > 0) { diff --git a/tests/Makefile.am b/tests/Makefile.am index 48c0858d..a99746cc 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -40,7 +40,8 @@ EXTRA_DIST = certs/ca-key.pem certs/ca.pem ns.sh common.sh certs/server-cert.pem connect-ios-script data/apple-ios.config certs/kerberos-cert.pem \ data/kdc.conf data/krb5.conf data/k5.KERBEROS.TEST data/kadm5.acl \ data/ipv6-iface.config data/no-route-default.config data/no-route-group.config \ - data/group-config/group1 data/test-namespace-listen.config + data/group-config/group1 data/test-namespace-listen.config data/disconnect-user.config \ + data/disconnect-user2.config SUBDIRS = docker-ocserv @@ -65,7 +66,7 @@ dist_check_SCRIPTS += haproxy-connect test-iroute test-multi-cookie test-pass-sc test-cookie-timeout test-cookie-timeout-2 test-explicit-ip \ 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 \ - apple-ios ipv6-iface test-namespace-listen + apple-ios ipv6-iface test-namespace-listen disconnect-user disconnect-user2 if RADIUS_ENABLED dist_check_SCRIPTS += radius-group radius-otp diff --git a/tests/data/disconnect-user.config b/tests/data/disconnect-user.config new file mode 100644 index 00000000..4b0e7b4e --- /dev/null +++ b/tests/data/disconnect-user.config @@ -0,0 +1,188 @@ +# User authentication method. Could be set multiple times and in that case +# all should succeed. +# Options: certificate, pam. +#auth = "certificate" +auth = "plain[@SRCDIR@/data/test1.passwd]" +#auth = "pam" + +isolate-workers = @ISOLATE_WORKERS@ + +max-ban-score = 0 + +# A banner to be displayed on clients +#banner = "Welcome" + +# Use listen-host to limit to specific IPs or to the IPs of a provided hostname. +#listen-host = @ADDRESS@ + +use-dbus = no + +# Limit the number of clients. Unset or set to zero for unlimited. +#max-clients = 1024 +max-clients = 16 + +listen-proxy-proto = false + +# Limit the number of client connections to one every X milliseconds +# (X is the provided value). Set to zero for no limit. +#rate-limit-ms = 100 + +# Limit the number of identical clients (i.e., users connecting multiple times) +# Unset or set to zero for unlimited. +max-same-clients = 2 + +# TCP and UDP port number +tcp-port = @PORT@ +udp-port = @PORT@ + +# Keepalive in seconds +keepalive = 32400 + +# Dead peer detection in seconds +dpd = 440 + +# MTU discovery (DPD must be enabled) +try-mtu-discovery = false + +# The key and the certificates of the server +# The key may be a file, or any URL supported by GnuTLS (e.g., +# tpmkey:uuid=xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx;storage=user +# or pkcs11:object=my-vpn-key;object-type=private) +# +# There may be multiple certificate and key pairs and each key +# should correspond to the preceding certificate. +server-cert = @SRCDIR@/certs/server-cert.pem +server-key = @SRCDIR@/certs/server-key.pem + +# Diffie-Hellman parameters. Only needed if you require support +# for the DHE ciphersuites (by default this server supports ECDHE). +# Can be generated using: +# certtool --generate-dh-params --outfile /path/to/dh.pem +#dh-params = /path/to/dh.pem + +# If you have a certificate from a CA that provides an OCSP +# service you may provide a fresh OCSP status response within +# the TLS handshake. That will prevent the client from connecting +# independently on the OCSP server. +# You can update this response periodically using: +# ocsptool --ask --load-cert=your_cert --load-issuer=your_ca --outfile response +# Make sure that you replace the following file in an atomic way. +#ocsp-response = /path/to/ocsp.der + +# In case PKCS #11 or TPM keys are used the PINs should be available +# in files. The srk-pin-file is applicable to TPM keys only (It's the storage +# root key). +#pin-file = /path/to/pin.txt +#srk-pin-file = /path/to/srkpin.txt + +# The Certificate Authority that will be used +# to verify clients if certificate authentication +# is set. +#ca-cert = /path/to/ca.pem + +# The object identifier that will be used to read the user ID in the client certificate. +# The object identifier should be part of the certificate's DN +# Useful OIDs are: +# CN = 2.5.4.3, UID = 0.9.2342.19200300.100.1.1 +#cert-user-oid = 0.9.2342.19200300.100.1.1 + +# The object identifier that will be used to read the user group in the client +# certificate. The object identifier should be part of the certificate's DN +# Useful OIDs are: +# OU (organizational unit) = 2.5.4.11 +#cert-group-oid = 2.5.4.11 + +# A revocation list of ca-cert is set +#crl = /path/to/crl.pem + +# GnuTLS priority string +tls-priorities = "PERFORMANCE:%SERVER_PRECEDENCE:%COMPAT" + +# To enforce perfect forward secrecy (PFS) on the main channel. +#tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-RSA" + +# The time (in seconds) that a client is allowed to stay connected prior +# to authentication +auth-timeout = 40 + +# The time (in seconds) that a client is not allowed to reconnect after +# a failed authentication attempt. +#min-reauth-time = 2 + +# Script to call when a client connects and obtains an IP +# Parameters are passed on the environment. +# REASON, USERNAME, GROUPNAME, HOSTNAME (the hostname selected by client), +# DEVICE, IP_REAL (the real IP of the client), IP_LOCAL (the local IP +# in the P-t-P connection), IP_REMOTE (the VPN IP of the client). REASON +# may be "connect" or "disconnect". +#connect-script = /usr/bin/myscript +#disconnect-script = /usr/bin/myscript + +# UTMP +#use-utmp = true + +# PID file +#pid-file = ./ocserv.pid + +# The default server directory. Does not require any devices present. +#chroot-dir = /path/to/chroot + +# socket file used for IPC, will be appended with .PID +# It must be accessible within the chroot environment (if any) +socket-file = ./ocserv-socket + +occtl-socket-file = @OCCTL_SOCKET@ +use-occtl = true + +# The user the worker processes will be run as. It should be +# unique (no other services run as this user). +run-as-user = @USERNAME@ +run-as-group = @GROUP@ + +# Network settings + +device = vpns + +# The default domain to be advertised +default-domain = example.com + +ipv4-network = @VPNNET@ +# Use the keywork local to advertize the local P-t-P address as DNS server +ipv4-dns = 192.168.1.1 + +# The NBNS server (if any) +#ipv4-nbns = 192.168.2.3 + +ipv6-network = @VPNNET6@ +#address = +#ipv6-mask = +#ipv6-dns = + +# Prior to leasing any IP from the pool ping it to verify that +# it is not in use by another (unrelated to this server) host. +ping-leases = false + +# Leave empty to assign the default MTU of the device +# mtu = + +#route = 192.168.1.0/255.255.255.0 +#route = 192.168.5.0/255.255.255.0 + +# +# The following options are for (experimental) AnyConnect client +# compatibility. They are only available if the server is built +# with --enable-anyconnect +# + +# Client profile xml. A sample file exists in doc/profile.xml. +# This file must be accessible from inside the worker's chroot. +# The profile is ignored by the openconnect client. +#user-profile = profile.xml + +# Unless set to false it is required for clients to present their +# certificate even if they are authenticating via a previously granted +# cookie. Legacy CISCO clients do not do that, and thus this option +# should be set for them. +#always-require-cert = false + +debug-no-secmod-stats = true diff --git a/tests/data/disconnect-user2.config b/tests/data/disconnect-user2.config new file mode 100644 index 00000000..02fde916 --- /dev/null +++ b/tests/data/disconnect-user2.config @@ -0,0 +1,186 @@ +# User authentication method. Could be set multiple times and in that case +# all should succeed. +# Options: certificate, pam. +#auth = "certificate" +auth = "plain[@SRCDIR@/data/test1.passwd]" +#auth = "pam" + +isolate-workers = @ISOLATE_WORKERS@ + +max-ban-score = 0 + +# A banner to be displayed on clients +#banner = "Welcome" + +# Use listen-host to limit to specific IPs or to the IPs of a provided hostname. +#listen-host = @ADDRESS@ + +use-dbus = no + +# Limit the number of clients. Unset or set to zero for unlimited. +#max-clients = 1024 +max-clients = 16 + +listen-proxy-proto = false + +# Limit the number of client connections to one every X milliseconds +# (X is the provided value). Set to zero for no limit. +#rate-limit-ms = 100 + +# Limit the number of identical clients (i.e., users connecting multiple times) +# Unset or set to zero for unlimited. +max-same-clients = 2 + +# TCP and UDP port number +tcp-port = @PORT@ +udp-port = @PORT@ + +# Keepalive in seconds +keepalive = 32400 + +# Dead peer detection in seconds +dpd = 440 + +# MTU discovery (DPD must be enabled) +try-mtu-discovery = false + +# The key and the certificates of the server +# The key may be a file, or any URL supported by GnuTLS (e.g., +# tpmkey:uuid=xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx;storage=user +# or pkcs11:object=my-vpn-key;object-type=private) +# +# There may be multiple certificate and key pairs and each key +# should correspond to the preceding certificate. +server-cert = @SRCDIR@/certs/server-cert.pem +server-key = @SRCDIR@/certs/server-key.pem + +# Diffie-Hellman parameters. Only needed if you require support +# for the DHE ciphersuites (by default this server supports ECDHE). +# Can be generated using: +# certtool --generate-dh-params --outfile /path/to/dh.pem +#dh-params = /path/to/dh.pem + +# If you have a certificate from a CA that provides an OCSP +# service you may provide a fresh OCSP status response within +# the TLS handshake. That will prevent the client from connecting +# independently on the OCSP server. +# You can update this response periodically using: +# ocsptool --ask --load-cert=your_cert --load-issuer=your_ca --outfile response +# Make sure that you replace the following file in an atomic way. +#ocsp-response = /path/to/ocsp.der + +# In case PKCS #11 or TPM keys are used the PINs should be available +# in files. The srk-pin-file is applicable to TPM keys only (It's the storage +# root key). +#pin-file = /path/to/pin.txt +#srk-pin-file = /path/to/srkpin.txt + +# The Certificate Authority that will be used +# to verify clients if certificate authentication +# is set. +#ca-cert = /path/to/ca.pem + +# The object identifier that will be used to read the user ID in the client certificate. +# The object identifier should be part of the certificate's DN +# Useful OIDs are: +# CN = 2.5.4.3, UID = 0.9.2342.19200300.100.1.1 +#cert-user-oid = 0.9.2342.19200300.100.1.1 + +# The object identifier that will be used to read the user group in the client +# certificate. The object identifier should be part of the certificate's DN +# Useful OIDs are: +# OU (organizational unit) = 2.5.4.11 +#cert-group-oid = 2.5.4.11 + +# A revocation list of ca-cert is set +#crl = /path/to/crl.pem + +# GnuTLS priority string +tls-priorities = "PERFORMANCE:%SERVER_PRECEDENCE:%COMPAT" + +# To enforce perfect forward secrecy (PFS) on the main channel. +#tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-RSA" + +# The time (in seconds) that a client is allowed to stay connected prior +# to authentication +auth-timeout = 40 + +# The time (in seconds) that a client is not allowed to reconnect after +# a failed authentication attempt. +#min-reauth-time = 2 + +# Script to call when a client connects and obtains an IP +# Parameters are passed on the environment. +# REASON, USERNAME, GROUPNAME, HOSTNAME (the hostname selected by client), +# DEVICE, IP_REAL (the real IP of the client), IP_LOCAL (the local IP +# in the P-t-P connection), IP_REMOTE (the VPN IP of the client). REASON +# may be "connect" or "disconnect". +#connect-script = /usr/bin/myscript +#disconnect-script = /usr/bin/myscript + +# UTMP +#use-utmp = true + +# PID file +#pid-file = ./ocserv.pid + +# The default server directory. Does not require any devices present. +#chroot-dir = /path/to/chroot + +# socket file used for IPC, will be appended with .PID +# It must be accessible within the chroot environment (if any) +socket-file = ./ocserv-socket + +occtl-socket-file = @OCCTL_SOCKET@ +use-occtl = true + +# The user the worker processes will be run as. It should be +# unique (no other services run as this user). +run-as-user = @USERNAME@ +run-as-group = @GROUP@ + +# Network settings + +device = vpns + +# The default domain to be advertised +default-domain = example.com + +ipv4-network = @VPNNET@ +# Use the keywork local to advertize the local P-t-P address as DNS server +ipv4-dns = 192.168.1.1 + +# The NBNS server (if any) +#ipv4-nbns = 192.168.2.3 + +ipv6-network = @VPNNET6@ +#address = +#ipv6-mask = +#ipv6-dns = + +# Prior to leasing any IP from the pool ping it to verify that +# it is not in use by another (unrelated to this server) host. +ping-leases = false + +# Leave empty to assign the default MTU of the device +# mtu = + +#route = 192.168.1.0/255.255.255.0 +#route = 192.168.5.0/255.255.255.0 + +# +# The following options are for (experimental) AnyConnect client +# compatibility. They are only available if the server is built +# with --enable-anyconnect +# + +# Client profile xml. A sample file exists in doc/profile.xml. +# This file must be accessible from inside the worker's chroot. +# The profile is ignored by the openconnect client. +#user-profile = profile.xml + +# Unless set to false it is required for clients to present their +# certificate even if they are authenticating via a previously granted +# cookie. Legacy CISCO clients do not do that, and thus this option +# should be set for them. +#always-require-cert = false diff --git a/tests/data/test-traffic.config b/tests/data/test-traffic.config index 787cdc02..70bc5995 100644 --- a/tests/data/test-traffic.config +++ b/tests/data/test-traffic.config @@ -109,12 +109,6 @@ auth-timeout = 40 # a failed authentication attempt. #min-reauth-time = 2 -# Cookie validity time (in seconds) -# Once a client is authenticated he's provided a cookie with -# which he can reconnect. This option sets the maximum lifetime -# of that cookie. -cookie-validity = 172800 - # Script to call when a client connects and obtains an IP # Parameters are passed on the environment. # REASON, USERNAME, GROUPNAME, HOSTNAME (the hostname selected by client), diff --git a/tests/disconnect-user b/tests/disconnect-user new file mode 100755 index 00000000..f0113d89 --- /dev/null +++ b/tests/disconnect-user @@ -0,0 +1,121 @@ +#!/bin/bash +# +# Copyright (C) 2020 Nikos Mavrogiannopoulos +# +# 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 this program. If not, see . +# + + +# This test checks whether an explicitly disconnected user via occtl +# can connect again using its cookie. It uses debug-no-secmod-stats = true +# to ensure that it is testing the worst case scenario where the worker +# process has died without notifying sec-mod. + +OCCTL="${OCCTL:-../src/occtl/occtl}" +SERV="${SERV:-../src/ocserv}" +srcdir=${srcdir:-.} +PORT=4477 +TMPFILE=ocfile.$$.tmp +PIDFILE=ocserv-pid.$$.tmp +CLIPID=oc-pid.$$.tmp +OCCTL_SOCKET=./occtl-$$.socket + +. `dirname $0`/common.sh + +if test "$(id -u)" != "0";then + echo "This test must be run as root" + exit 77 +fi + +echo "Testing ocserv disconnection via occtl... " + +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 "${CLIPID}" && test -f "${CLIPID}" && kill $(cat ${CLIPID}) >/dev/null 2>&1 + test -n "${CLIPID}" && rm -f ${CLIPID} >/dev/null 2>&1 + test -n "${CONFIG}" && rm -f ${CONFIG} >/dev/null 2>&1 + test -n "${TMPFILE}" && rm -f ${TMPFILE} >/dev/null 2>&1 +} +trap finish EXIT + +# server address +ADDRESS=10.236.2.1 +CLI_ADDRESS=10.236.1.1 +VPNNET=192.168.1.0/24 +VPNADDR=192.168.1.1 +VPNNET6=fcbe:735c:407a:3b62:164d:fa21:1495:0/112 +VPNADDR6=fcbe:735c:407a:3b62:164d:fa21:1495:1 + +. `dirname $0`/ns.sh + +# Run servers +update_config disconnect-user.config +if test "$VERBOSE" = 1;then +DEBUG="-d 3" +fi + +${CMDNS2} ${SERV} -p ${PIDFILE} -f -c ${CONFIG} ${DEBUG} & PID=$! + +sleep 3 + +# Run clients +echo " * Getting cookie from ${ADDRESS}:${PORT}..." +( echo "test" | ${CMDNS1} ${OPENCONNECT} ${ADDRESS}:${PORT} -u test --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3 --authenticate >${TMPFILE} ) +if test $? != 0;then + echo "Could not get cookie from server" + exit 1 +fi + +eval $(cat ${TMPFILE}) +echo " * Connecting to ${ADDRESS}:${PORT}..." +( ${CMDNS1} ${OPENCONNECT} -q ${ADDRESS}:${PORT} -u test --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3 -s ${srcdir}/scripts/vpnc-script -C "${COOKIE}" --pid-file=${CLIPID} -b ) +if test $? != 0;then + echo "Could not connect to server" + exit 1 +fi + +set -e +echo " * ping remote address" + +${CMDNS1} ping -c 3 ${VPNADDR} + +set +e + +${OCCTL} -s ${OCCTL_SOCKET} disconnect user test +if test $? != 0;then + echo "occtl didn't find connected user!" + exit 1 +fi + +echo " * Re-connecting to obtain cookie after disconnect... " +( ${CMDNS1} ${OPENCONNECT} -q ${ADDRESS}:${PORT} -u test --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3 -s ${srcdir}/scripts/vpnc-script -C "${COOKIE}" --pid-file=${CLIPID} -b ) +if test $? = 0;then + echo "Succeeded using the cookie to connect" + exit 1 +fi + +sleep 2 + +${OCCTL} -s ${OCCTL_SOCKET} show user test +if test $? = 0;then + echo "occtl found disconnected user!" + exit 1 +fi + +exit 0 diff --git a/tests/disconnect-user2 b/tests/disconnect-user2 new file mode 100755 index 00000000..cded8a95 --- /dev/null +++ b/tests/disconnect-user2 @@ -0,0 +1,119 @@ +#!/bin/bash +# +# Copyright (C) 2020 Nikos Mavrogiannopoulos +# +# 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 this program. If not, see . +# + + +# This test checks whether an explicitly disconnected user via occtl +# can connect again using its cookie. + +OCCTL="${OCCTL:-../src/occtl/occtl}" +SERV="${SERV:-../src/ocserv}" +srcdir=${srcdir:-.} +PORT=4459 +TMPFILE=ocfile.$$.tmp +PIDFILE=ocserv-pid.$$.tmp +CLIPID=oc-pid.$$.tmp +OCCTL_SOCKET=./occtl-$$.socket + +. `dirname $0`/common.sh + +if test "$(id -u)" != "0";then + echo "This test must be run as root" + exit 77 +fi + +echo "Testing ocserv disconnection via occtl... " + +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 "${CLIPID}" && test -f ${CLIPID} && kill $(cat ${CLIPID}) >/dev/null 2>&1 + test -n "${CLIPID}" && rm -f ${CLIPID} >/dev/null 2>&1 + test -n "${CONFIG}" && rm -f ${CONFIG} >/dev/null 2>&1 + test -n "${TMPFILE}" && rm -f ${TMPFILE} >/dev/null 2>&1 +} +trap finish EXIT + +# server address +ADDRESS=10.32.81.0 +CLI_ADDRESS=10.32.80.1 +VPNNET=192.168.1.0/24 +VPNADDR=192.168.1.1 +VPNNET6=fcbe:735c:407a:3fc2:164d:fa21:1495:0/112 +VPNADDR6=fcbe:735c:407a:3fc2:164d:fa21:1495:1 + +. `dirname $0`/ns.sh + +# Run servers +update_config disconnect-user2.config +if test "$VERBOSE" = 1;then +DEBUG="-d 3" +fi + +${CMDNS2} ${SERV} -p ${PIDFILE} -f -c ${CONFIG} ${DEBUG} & PID=$! + +sleep 3 + +# Run clients +echo " * Getting cookie from ${ADDRESS}:${PORT}..." +( echo "test" | ${CMDNS1} ${OPENCONNECT} ${ADDRESS}:${PORT} -u test --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3 --authenticate >${TMPFILE} ) +if test $? != 0;then + echo "Could not get cookie from server" + exit 1 +fi + +eval $(cat ${TMPFILE}) +echo " * Connecting to ${ADDRESS}:${PORT}..." +( ${CMDNS1} ${OPENCONNECT} -q ${ADDRESS}:${PORT} -u test --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3 -s ${srcdir}/scripts/vpnc-script -C "${COOKIE}" --pid-file=${CLIPID} -b ) +if test $? != 0;then + echo "Could not connect to server" + exit 1 +fi + +set -e +echo " * ping remote address" + +${CMDNS1} ping -c 3 ${VPNADDR} + +set +e + +${OCCTL} -s ${OCCTL_SOCKET} disconnect user test +if test $? != 0;then + echo "occtl didn't find connected user!" + exit 1 +fi + +echo " * Re-connecting to obtain cookie after disconnect... " +( ${CMDNS1} ${OPENCONNECT} -q ${ADDRESS}:${PORT} -u test --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3 -s ${srcdir}/scripts/vpnc-script -C "${COOKIE}" --pid-file=${CLIPID} -b ) +if test $? = 0;then + echo "Succeeded using the cookie to connect" + exit 1 +fi + +sleep 2 + +${OCCTL} -s ${OCCTL_SOCKET} show user test +if test $? = 0;then + echo "occtl found disconnected user!" + exit 1 +fi + +exit 0