From 9460367822810d43599071f1f57826518ca0d799 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Mon, 27 Jul 2020 09:55:36 +0200 Subject: [PATCH] Added the config option of a pre-login banner Resolves: #313 Signed-off-by: Nikos Mavrogiannopoulos --- NEWS | 1 + doc/sample.config | 5 ++- src/config.c | 2 + src/vpn.h | 1 + src/worker-auth.c | 82 +++++++++++++++++++++++++---------------- tests/Makefile.am | 2 +- tests/banner | 72 ++++++++++++++++++++++++++++++++++++ tests/data/test1.config | 14 +------ 8 files changed, 134 insertions(+), 45 deletions(-) create mode 100755 tests/banner diff --git a/NEWS b/NEWS index da02da76..07c968dd 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,7 @@ 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) +- Added the config option of a pre-login-banner (#313) * Version 1.1.0 (released 2020-06-16) diff --git a/doc/sample.config b/doc/sample.config index 0bcf1ae0..a291b8d4 100644 --- a/doc/sample.config +++ b/doc/sample.config @@ -190,9 +190,12 @@ ca-cert = ../tests/certs/ca.pem # information at: https://gitlab.com/ocserv/ocserv/issues isolate-workers = true -# A banner to be displayed on clients +# A banner to be displayed on clients after connection #banner = "Welcome" +# A banner to be displayed on clients before connection +#pre-login-banner = "Welcome" + # Limit the number of clients. Unset or set to zero for unlimited. #max-clients = 1024 max-clients = 16 diff --git a/src/config.c b/src/config.c index 1f1ab123..0278db0c 100644 --- a/src/config.c +++ b/src/config.c @@ -874,6 +874,8 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co fprintf(stderr, WARNSTR"the option 'session-control' is deprecated\n"); } else if (strcmp(name, "banner") == 0) { READ_STRING(config->banner); + } else if (strcmp(name, "pre-login-banner") == 0) { + READ_STRING(config->pre_login_banner); } else if (strcmp(name, "dtls-legacy") == 0) { READ_TF(config->dtls_legacy); } else if (strcmp(name, "cisco-client-compat") == 0) { diff --git a/src/vpn.h b/src/vpn.h index 5876bb21..77933dcc 100644 --- a/src/vpn.h +++ b/src/vpn.h @@ -242,6 +242,7 @@ struct cfg_st { unsigned no_compress_limit; /* under this size (in bytes) of data there will be no compression */ #endif char *banner; + char *pre_login_banner; char *ocsp_response; /* file with the OCSP response */ char *default_domain; /* domain to be advertised */ diff --git a/src/worker-auth.c b/src/worker-auth.c index 40ea3a7e..b708164f 100644 --- a/src/worker-auth.c +++ b/src/worker-auth.c @@ -73,41 +73,41 @@ static const char ocv3_success_msg_head[] = "\n"; -#define OC_LOGIN_MSG_START \ +#define OC_LOGIN_START \ "\n" \ "\n" \ VERSION_MSG \ - "\n" \ + "\n" + +#define OC_LOGIN_FORM_START \ "%s\n" \ "
\n" -static const char oc_login_msg_end[] = - "
\n" "
"; +#define OC_LOGIN_END \ + "\n" "" -static const char login_msg_user[] = - "\n"; +#define OC_LOGIN_FORM_INPUT_USER \ + "\n" #define DEFAULT_PASSWD_LABEL "Password:" -#define LOGIN_MSG_PASSWORD \ +#define OC_LOGIN_FORM_INPUT_PASSWORD \ "\n" -#define LOGIN_MSG_PASSWORD_CTR \ +#define OC_LOGIN_FORM_INPUT_PASSWORD_CTR \ "\n" #define _OCV3_LOGIN_MSG_START(x) \ "\n" \ - "\n" \ - "%s\n" \ - "
\n" + "\n" -#define OCV3_LOGIN_MSG_START _OCV3_LOGIN_MSG_START("main") -#define OCV3_PASSWD_MSG_START _OCV3_LOGIN_MSG_START("passwd") +#define OCV3_LOGIN_START _OCV3_LOGIN_MSG_START("main") +#define OCV3_PASSWD_START _OCV3_LOGIN_MSG_START("passwd") + +#define OCV3_LOGIN_END "
\n" #ifdef SUPPORT_OIDC_AUTH #define HTTP_AUTH_OIDC_PREFIX "Bearer" #endif -static const char ocv3_login_msg_end[] = - "
\n"; static int get_cert_info(worker_st * ws); static int basic_auth_handler(worker_st * ws, unsigned http_ver, const char *msg); @@ -208,20 +208,20 @@ int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg, unsig char context[BASE64_ENCODE_RAW_LENGTH(SID_SIZE) + 1]; unsigned int i, j; str_st str; - const char *login_msg_start; - const char *login_msg_end; + const char *login_start; + const char *login_end; if (ws->req.user_agent_type == AGENT_OPENCONNECT_V3) { /* certain v2.x modified clients require a different auth_id * when password is being requested, rather than username */ if (ws->auth_state == S_AUTH_REQ) - login_msg_start = OCV3_PASSWD_MSG_START; + login_start = OCV3_PASSWD_START; else - login_msg_start = OCV3_LOGIN_MSG_START; - login_msg_end = ocv3_login_msg_end; + login_start = OCV3_LOGIN_START; + login_end = OCV3_LOGIN_END; } else { - login_msg_start = OC_LOGIN_MSG_START; - login_msg_end = oc_login_msg_end; + login_start = OC_LOGIN_START; + login_end = OC_LOGIN_END; } if ((ws->selected_auth->type & AUTH_TYPE_GSSAPI) && ws->auth_state < S_AUTH_COOKIE) { @@ -269,44 +269,64 @@ int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg, unsig } if (ws->auth_state == S_AUTH_REQ) { - /* only ask password */ + /* Password Form */ if (pmsg == NULL || strncasecmp(pmsg, DEFAULT_PASSWD_LABEL, sizeof(DEFAULT_PASSWD_LABEL)-1) == 0) pmsg = "Please enter your password."; - ret = str_append_printf(&str, login_msg_start, pmsg); + ret = str_append_str(&str, login_start); + if (ret < 0) { + ret = -1; + goto cleanup; + } + + ret = str_append_printf(&str, OC_LOGIN_FORM_START, pmsg); if (ret < 0) { ret = -1; goto cleanup; } if (pcounter > 0) - ret = str_append_printf(&str, LOGIN_MSG_PASSWORD_CTR, pcounter); + ret = str_append_printf(&str, OC_LOGIN_FORM_INPUT_PASSWORD_CTR, pcounter); else - ret = str_append_str(&str, LOGIN_MSG_PASSWORD); + ret = str_append_str(&str, OC_LOGIN_FORM_INPUT_PASSWORD); if (ret < 0) { ret = -1; goto cleanup; } - ret = str_append_str(&str, login_msg_end); + ret = str_append_str(&str, login_end); if (ret < 0) { ret = -1; goto cleanup; } } else { + /* Username / Groups Form */ if (pmsg == NULL) pmsg = "Please enter your username."; - /* ask for username and groups */ - ret = str_append_printf(&str, login_msg_start, pmsg); + ret = str_append_str(&str, login_start); + if (ret < 0) { + ret = -1; + goto cleanup; + } + + if (WSCONFIG(ws)->pre_login_banner) { + ret = str_append_printf(&str, "%s", WSCONFIG(ws)->pre_login_banner); + if (ret < 0) { + ret = -1; + goto cleanup; + } + } + + ret = str_append_printf(&str, OC_LOGIN_FORM_START, pmsg); if (ret < 0) { ret = -1; goto cleanup; } if (ws->selected_auth->type & AUTH_TYPE_USERNAME_PASS) { - ret = str_append_str(&str, login_msg_user); + ret = str_append_str(&str, OC_LOGIN_FORM_INPUT_USER); if (ret < 0) { ret = -1; goto cleanup; @@ -396,7 +416,7 @@ int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg, unsig } } - ret = str_append_str(&str, login_msg_end); + ret = str_append_str(&str, login_end); if (ret < 0) { ret = -1; goto cleanup; diff --git a/tests/Makefile.am b/tests/Makefile.am index a99746cc..7e14e037 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -96,7 +96,7 @@ dist_check_SCRIPTS += test-pass test-pass-cert test-cert test-group-pass \ test-enc-key test-sighup-key-change test-get-cert test-san-cert \ test-gssapi test-pass-opt-cert test-cert-opt-pass test-gssapi-opt-pass \ test-gssapi-opt-cert haproxy-auth test-maintenance \ - test-group-name flowcontrol + test-group-name flowcontrol banner if HAVE_CWRAP_PAM dist_check_SCRIPTS += test-pam test-pam-noauth diff --git a/tests/banner b/tests/banner new file mode 100755 index 00000000..e43fc7a8 --- /dev/null +++ b/tests/banner @@ -0,0 +1,72 @@ +#!/bin/bash +# +# Copyright (C) 2013 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 GnuTLS; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# tests whether the pre-login-banner is printed + +SERV="${SERV:-../src/ocserv}" +srcdir=${srcdir:-.} +NO_NEED_ROOT=1 +PORT=4173 +TMPFILE=ocserv-plbanner.$$.tmp +PIDFILE=ocserv-pid.$$.tmp + +. `dirname $0`/common.sh + +function finish { + set +e + echo " * Cleaning up..." + test -n "${PIDFILE}" && test -f "${PIDFILE}" && kill $(cat ${PIDFILE}) >/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 "${TMPFILE}" && rm -f ${TMPFILE} >/dev/null 2>&1 +} +trap finish EXIT + +echo "Testing banner printing... " + +update_config test1.config +launch_sr_server -d 1 -p ${PIDFILE} -f -c ${CONFIG} & PID=$! +wait_server $PID + +sleep 3 + +echo "Connecting to obtain cookie... " +( echo "test" | LD_PRELOAD=libsocket_wrapper.so $OPENCONNECT $ADDRESS:$PORT -u test --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3 --cookieonly >${TMPFILE} 2>&1 ) || + fail $PID "Could not receive cookie from server" + +grep "AN UNPREDICTABLE BANNER" ${TMPFILE} >/dev/null +if test $? != 0;then + echo "The banner was not printed" + cat $TMPFILE + exit 1 +fi + +echo "Connecting to obtain cookie with wrong password... " +( echo "tost" | LD_PRELOAD=libsocket_wrapper.so $OPENCONNECT $ADDRESS:$PORT -u test --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3 --cookieonly >${TMPFILE} 2>&1 ) && + fail $PID "Received cookie when we shouldn't" + +grep "AN UNPREDICTABLE BANNER" ${TMPFILE} >/dev/null +if test $? != 0;then + echo "The banner was not printed" + cat $TMPFILE + exit 1 +fi + +exit 0 diff --git a/tests/data/test1.config b/tests/data/test1.config index 251a822b..cce0a376 100644 --- a/tests/data/test1.config +++ b/tests/data/test1.config @@ -12,6 +12,8 @@ max-ban-score = 0 # A banner to be displayed on clients #banner = "Welcome" +pre-login-banner = "AN UNPREDICTABLE BANNER" + # Use listen-host to limit to specific IPs or to the IPs of a provided hostname. #listen-host = [IP|HOSTNAME] @@ -107,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), @@ -122,12 +118,6 @@ cookie-validity = 172800 #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