Add support for Cisco IP-Phone Enterprise firmware VPN client.

The VPN client that comes with the Cisco IP-Phone Enterprise
firmware is based on AnyConnect but was unable to authenticate
with ocserv.

The phone makes an initial GET request and looks for a cookie
named 'webvpn' that has an expiry attribute and a cookie named
'webvpnlogin' containing a non-empty value.

When username+password mode is configured, the phone will then
send a POST request containing those credentials. When using
certificate authentication an empty POST request is sent.

A handler that implements this new behaviour has been added
under the '/svc' path.

To use DTLS 'dtls-legacy' must be enabled and 'udp-port' must
be 443, a new 'cisco-svc-client-compat' option automatically
checks those settings.

New test cases test-pass-svc and test-cert-svc check the above
behaviour.

Older versions of the phone's firmware will fail to create the
DTLS tunnel if the cipher negotiated for HTTPS does not match
that selected for DTLS.

To work-around this either disable DTLS or only allow the
RSA-AES-256-CBC/SHA1 or RSA-AES-128-CBC/SHA1 cipher to be used.

doc/README-cisco-svc.md includes additional information.

Note: 'Enterprise' here is used to differentiate between that
firmware and the MPP (Multi-Platform) firmware which uses the
same hardware.

Signed-off-by: Gareth Palmer <gareth.palmer3@gmail.com>
This commit is contained in:
Gareth Palmer
2023-07-11 22:48:22 +12:00
parent 2eaf754c51
commit 996d021e1b
14 changed files with 574 additions and 10 deletions

3
NEWS
View File

@@ -8,6 +8,9 @@
- Avoid login failure when the end point of server URI
contains a query string.
- Eliminated the need for using the gnulib portability library.
- Add support for Cisco Enterprise phones to authenticate via
the /svc endpoint and the 'cisco-svc-client-compat' config
option.
* Version 1.1.7 (released 2023-05-07)

55
doc/README-cisco-svc.md Normal file
View File

@@ -0,0 +1,55 @@
Using Cisco IP-Phones with ocserv
=================================
The 'Enterprise' firmware on the 7800, 8800, 8900 and 9900 series
Cisco IP-Phones can use ocserv as a VPN gateway.
An 'Enterprise' specific URL path of `/svc` is required when
configuring the phone. In the phone's `SEPMAC.cnf.xml` that looks
like:
```
<vpnGroup>
...
<addresses>
<url>https://host.name.for.ocserv/svc</url>
</addresses>
</vpnGroup>
```
Both username+password and certificate-based authentication
are supported.
Ocserv configuration
====================
In addition to requiring an 'Enterprise' specific path, ocserv must
also be configured to work-around limitations in the behavior of the
phone's VPN client.
To enable that mode use:
```
cisco-svc-client-compat = true
```
**Note:** If the ciphers do not match the phone will log the error
`old session cipher not returned` in the console log. Older phone
VPN clients may negotiate an unsupported TLS+DTLS cipher so you will
need to force either AES256-CBC or AES128-CBC, eg:
```
tls-priorities = "NONE:%SERVER_PRECEDENCE:%COMPAT:+VERS-TLS-ALL:+SIGN-ALL:+COMP-ALL:+RSA:+SHA1:+AES-256-CBC"
```
**Note:** While you may specify any port to use for HTTPS, the phone
will only use port `443` for DTLS.
Additional information
======================
Refer to the following documentation on [usecallmanager.nz](https://usecallmanager.nz)
for additional information about how to configure the phone's VPN.
* [SEPMAC.cnf.xml](https://usecallmanager.nz/sepmac-cnf-xml.html):
The main configuration file for the phone.
* [VPN Group](https://usecallmanager.nz/vpn-group.html): VPN
specific configuration.

View File

@@ -698,6 +698,11 @@ cisco-client-compat = true
# by the dtls-psk protocol supported by openconnect 7.08+.
dtls-legacy = true
# This option will enable the settings needed for Cisco SVC IPPhone clients
# to connect. It implies dtls-legacy = false and tls-priorities is changed to
# only the ciphers the device supports.
cisco-svc-client-compat = false
# This option will enable the X-CSTP-Client-Bypass-Protocol (disabled by default).
# If the server has not configured an IPv6 or IPv4 address pool, enabling this option
# will instruct the client to bypass the server for that IP protocol. The option is

View File

@@ -75,7 +75,7 @@ ocserv_worker_SOURCES = $(CORE_SOURCES) \
html.c html.h http-heads.h worker.c worker.h worker-auth.c \
worker-bandwidth.c worker-bandwidth.h worker-http.c worker-http-handlers.c \
worker-kkdcp.c worker-misc.c worker-privs.c worker-proxyproto.c \
worker-resume.c worker-vpn.c
worker-resume.c worker-vpn.c worker-svc.c
ocserv_worker_LDADD = $(CORE_LDADD)

View File

@@ -921,6 +921,8 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co
fprintf(stderr, NOTESTR"'always-require-cert' was replaced by 'cisco-client-compat'\n");
config->cisco_client_compat = 1;
}
} else if (strcmp(name, "cisco-svc-client-compat") == 0) {
READ_TF(config->cisco_svc_client_compat);
} else if (strcmp(name, "dtls-psk") == 0) {
if (!WARN_ON_VHOST(vhost->name, "dtls-psk", dtls_psk))
READ_TF(config->dtls_psk);
@@ -1461,6 +1463,19 @@ static void check_cfg(vhost_cfg_st *vhost, vhost_cfg_st *defvhost, unsigned sile
}
#endif
if (config->cisco_svc_client_compat) {
if (!config->dtls_legacy && !silent) {
fprintf(stderr, NOTESTR"%sthe cisco-svc-client-compat option implies dtls-legacy = true; enabling\n", PREFIX_VHOST(vhost));
}
config->dtls_legacy = 1;
/* The client will only connect to port 443 */
if (vhost->perm_config.udp_port != 0 && vhost->perm_config.udp_port != 443) {
fprintf(stderr, ERRSTR"%s cisco-svc-client-compat option requires udp-port = 443\n", PREFIX_VHOST(vhost));
exit(EXIT_FAILURE);
}
}
if (config->priorities == NULL) {
char *tmp = "";
/* on vhosts assign the main host priorities. We furthermore disable TLS1.3 on Cisco clients

View File

@@ -603,10 +603,13 @@ int handle_disconnect_id_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_s
static const char *fix_ciphersuite(char *txt)
{
if (txt != NULL && txt[0] != 0) {
if (strlen(txt) > 16 && strncmp(txt, "(DTLS", 5) == 0 &&
(strncmp(&txt[8], ")-(RSA)-", 8) == 0 || strncmp(&txt[8], ")-(PSK)-", 8) == 0)) {
if (txt != NULL && txt[0] != 0 && strlen(txt) > 16 && strncmp(txt, "(DTLS", 5) == 0) {
if (strncmp(&txt[8], ")-(RSA)-", 8) == 0 || strncmp(&txt[8], ")-(PSK)-", 8) == 0) {
return txt + 16;
} else if (strncmp(&txt[8], ")-(ECDHE-RSA)-", 14) == 0) {
return txt + 22;
} else {
return txt + 10;
}
}

View File

@@ -305,6 +305,7 @@ struct cfg_st {
unsigned cisco_client_compat; /* do not require client certificate,
* and allow auth to complete in different
* TCP sessions. */
unsigned cisco_svc_client_compat; /* force allowed ciphers and disable dtls-legacy */
unsigned rate_limit_ms; /* if non zero force a connection every rate_limit milliseconds if ocserv-sm is heavily loaded */
unsigned ping_leases; /* non zero if we need to ping prior to leasing */
unsigned server_drain_ms; /* how long to wait after we stop accepting new connections before closing old connections */

View File

@@ -109,7 +109,6 @@ static const char ocv3_success_msg_foot[] = "</auth>\n";
#endif
static int get_cert_info(worker_st * ws);
static int basic_auth_handler(worker_st * ws, unsigned http_ver, const char *msg);
#ifdef SUPPORT_OIDC_AUTH
@@ -770,7 +769,7 @@ int connect_to_secmod(worker_st * ws)
return sd;
}
static int recv_auth_reply(worker_st * ws, int sd, char **txt, unsigned *pcounter)
int recv_auth_reply(worker_st * ws, int sd, char **txt, unsigned *pcounter)
{
int ret;
SecAuthReplyMsg *msg = NULL;
@@ -859,7 +858,6 @@ static int recv_auth_reply(worker_st * ws, int sd, char **txt, unsigned *pcounte
}
/* grabs the username from the session certificate */
static
int get_cert_info(worker_st * ws)
{
const gnutls_datum_t *cert;
@@ -1248,7 +1246,6 @@ int match_password_in_reply(worker_st * ws, char *body, unsigned body_length,
* @xml_field: the XML field to check for (e.g., MYFIELD)
* @value: the value that was found
*/
static
int parse_reply(worker_st * ws, char *body, unsigned body_length,
const char *field, unsigned field_size,
const char *xml_field, unsigned xml_field_size,

View File

@@ -74,6 +74,7 @@ static const struct known_urls_st known_urls[] = {
LL("/+CSCOT+/", get_string_handler, NULL),
LL("/logout", get_empty_handler, NULL),
#endif
LL("/svc", get_svc_handler, post_svc_handler),
{NULL, 0, 0, NULL, NULL}
};
@@ -169,6 +170,29 @@ static const dtls_ciphersuite_st ciphersuites12[] = {
.gnutls_cipher = GNUTLS_CIPHER_AES_256_GCM,
.dtls12_mode = 1,
.server_prio = 90
},
/* these next two are currently only used by cisco-svc-client-compat devices */
{
.oc_name = "ECDHE-RSA-AES128-GCM-SHA256",
.gnutls_name =
"NONE:+VERS-DTLS1.2:+COMP-NULL:+AES-128-GCM:+AEAD:+SHA256:+ECDHE-RSA:+SIGN-ALL:"WORKAROUND_STR,
.gnutls_version = GNUTLS_DTLS1_2,
.gnutls_mac = GNUTLS_MAC_AEAD,
.gnutls_kx = GNUTLS_KX_ECDHE_RSA,
.gnutls_cipher = GNUTLS_CIPHER_AES_128_GCM,
.dtls12_mode = 1,
.server_prio = 70,
},
{
.oc_name = "ECDHE-RSA-AES256-GCM-SHA384",
.gnutls_name =
"NONE:+VERS-DTLS1.2:+COMP-NULL:+AES-256-GCM:+AEAD:+SHA384:+ECDHE-RSA:+SIGN-ALL:"WORKAROUND_STR,
.gnutls_version = GNUTLS_DTLS1_2,
.gnutls_mac = GNUTLS_MAC_AEAD,
.gnutls_kx = GNUTLS_KX_ECDHE_RSA,
.gnutls_cipher = GNUTLS_CIPHER_AES_256_GCM,
.dtls12_mode = 1,
.server_prio = 80,
}
};
@@ -423,6 +447,9 @@ void header_value_check(struct worker_st *ws, struct http_req_st *req)
} else if (strncasecmp(req->user_agent, "AnyLink Secure Client", 21) == 0) {
oclog(ws, LOG_DEBUG, "Detected AnyLink");
req->user_agent_type = AGENT_ANYLINK;
} else if (strncasecmp(req->user_agent, "Cisco SVC IPPhone Client", 24) == 0) {
oclog(ws, LOG_DEBUG, "Detected Cisco SVC IPPhone Client");
req->user_agent_type = AGENT_SVC_IPPHONE;
} else {
oclog(ws, LOG_DEBUG, "Unknown client (%s)", req->user_agent);
}

286
src/worker-svc.c Normal file
View File

@@ -0,0 +1,286 @@
/*
* Copyright (C) 2023 Gareth Palmer
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#include <gnutls/x509.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <base64-helper.h>
#include <vpn.h>
#include "html.h"
#include <worker.h>
#include <tlslib.h>
int get_svc_handler(worker_st *ws, unsigned http_ver)
{
int ret;
if (!WSCONFIG(ws)->cisco_svc_client_compat)
oclog(ws, LOG_WARNING, "request to /svc but cisco-svc-client-compat = false");
if (ws->req.user_agent_type != AGENT_SVC_IPPHONE)
oclog(ws, LOG_WARNING, "unexpected /svc user-agent of '%s'", ws->req.user_agent);
oclog(ws, LOG_HTTP_DEBUG, "HTTP sending: 200 OK");
cstp_cork(ws);
ret = cstp_printf(ws, "HTTP/1.%u 200 OK\r\n", http_ver);
if (ret < 0)
return -1;
ret = cstp_puts(ws, "Content-Length: 0\r\n");
if (ret < 0)
goto fail;
ret = cstp_puts(ws, "Set-Cookie: webvpn=; expires=Thu, 01 Jan 1970 22:00:00 GMT; path=/; secure\r\n");
if (ret < 0)
goto fail;
/* this tells the client to send a post request with auth credentials */
ret = cstp_puts(ws, "Set-Cookie: webvpnlogin=1; secure\r\n");
if (ret < 0)
goto fail;
ret = cstp_puts(ws, "X-Transcend-Version: 1\r\n");
if (ret < 0)
goto fail;
/* end of headers */
ret = cstp_puts(ws, "\r\n");
if (ret < 0)
goto fail;
ret = cstp_uncork(ws);
if (ret < 0)
goto fail;
ret = 0;
fail:
if (ret < 0)
ret = -1;
return ret;
}
static int client_auth(worker_st *ws, char *password)
{
int ret = -1, sd = -1;
char *msg;
unsigned pcounter = 0;
SecAuthInitMsg init = SEC_AUTH_INIT_MSG__INIT;
SecAuthContMsg cont = SEC_AUTH_CONT_MSG__INIT;
if (ws->auth_state != S_AUTH_INACTIVE) {
oclog(ws, LOG_ERR, "not in auth inactive state");
return -1;
}
ws->auth_state = S_AUTH_INIT;
if (ws->selected_auth->type & AUTH_TYPE_USERNAME_PASS) {
ws->groupname[0] = '\0';
init.user_name = ws->username;
init.auth_type |= AUTH_TYPE_USERNAME_PASS;
} else if (ws->selected_auth->type & AUTH_TYPE_CERTIFICATE) {
init.tls_auth_ok = ws->cert_auth_ok;
init.cert_user_name = ws->cert_username;
init.cert_group_names = ws->cert_groups;
init.n_cert_group_names = ws->cert_groups_size;
init.auth_type |= AUTH_TYPE_CERTIFICATE;
}
init.vhost = ws->vhost->name;
init.ip = ws->remote_ip_str;
init.our_ip = ws->our_ip_str;
init.session_start_time = ws->session_start_time;
init.hmac.data = (uint8_t*)ws->sec_auth_init_hmac;
init.hmac.len = sizeof(ws->sec_auth_init_hmac);
if (ws->req.user_agent[0] != 0)
init.user_agent = ws->req.user_agent;
if (ws->req.devtype[0] != 0)
init.device_type = ws->req.devtype;
if (ws->req.devplatform[0] != 0)
init.device_platform = ws->req.devplatform;
sd = connect_to_secmod(ws);
if (sd == -1) {
oclog(ws, LOG_ERR, "failed connecting to sec mod");
goto fail;
}
ret = send_msg_to_secmod(ws, sd, CMD_SEC_AUTH_INIT, &init,
(pack_size_func)sec_auth_init_msg__get_packed_size,
(pack_func)sec_auth_init_msg__pack);
if (ret < 0) {
oclog(ws, LOG_ERR, "failed sending auth init message to sec mod");
goto fail;
}
ret = recv_auth_reply(ws, sd, &msg, &pcounter);
if (ws->selected_auth->type & AUTH_TYPE_USERNAME_PASS) {
if (ret != ERR_AUTH_CONTINUE) {
oclog(ws, LOG_ERR, "not in auth continue state");
goto fail;
}
ws->auth_state = S_AUTH_INIT;
cont.ip = ws->remote_ip_str;
cont.password = password;
if (ws->sid_set != 0) {
cont.sid.data = ws->sid;
cont.sid.len = sizeof(ws->sid);
}
close(sd);
sd = connect_to_secmod(ws);
if (sd == -1) {
oclog(ws, LOG_ERR, "failed connecting to sec mod");
goto fail;
}
ret = send_msg_to_secmod(ws, sd, CMD_SEC_AUTH_CONT, &cont,
(pack_size_func)sec_auth_cont_msg__get_packed_size,
(pack_func)sec_auth_cont_msg__pack);
if (ret < 0) {
oclog(ws, LOG_ERR, "failed sending auth cont message to sec mod");
goto fail;
}
ret = recv_auth_reply(ws, sd, &msg, &pcounter);
}
if (ret != ERR_SUCCESS) {
oclog(ws, LOG_ERR, "failed authentication for '%s'",
ws->username);
goto fail;
}
ws->auth_state = S_AUTH_REQ;
fail:
if (sd != -1)
close(sd);
return ret;
}
int post_svc_handler(worker_st *ws, unsigned http_ver)
{
char *username = NULL;
char *password = NULL;
int ret = -1;
char cookie[BASE64_ENCODE_RAW_LENGTH(sizeof(ws->cookie)) + 1];
if (!WSCONFIG(ws)->cisco_svc_client_compat)
oclog(ws, LOG_WARNING, "request to /svc but cisco-svc-client-compat = false");
if (ws->req.user_agent_type != AGENT_SVC_IPPHONE)
oclog(ws, LOG_WARNING, "unexpected /svc user-agent of '%s'", ws->req.user_agent);
if (ws->selected_auth->type & AUTH_TYPE_USERNAME_PASS) {
/* fail if username or password is missing */
ret = parse_reply(ws, ws->req.body, ws->req.body_length,
"username", 8, NULL, 0, &username);
if (ret < 0) {
oclog(ws, LOG_ERR, "no username field in body");
return get_svc_handler(ws, http_ver);
}
strlcpy(ws->username, username, sizeof(ws->username));
talloc_free(username);
ret = parse_reply(ws, ws->req.body, ws->req.body_length,
"password", 8, NULL, 0, &password);
if (ret < 0) {
oclog(ws, LOG_ERR, "no password field in body");
return get_svc_handler(ws, http_ver);
}
} else if (ws->selected_auth->type & AUTH_TYPE_CERTIFICATE) {
ret = get_cert_info(ws);
}
if (ret >= 0)
ret = client_auth(ws, password);
talloc_free(password);
if (ret < 0) {
oclog(ws, LOG_HTTP_DEBUG, "HTTP sending: 401 Unauthorized");
cstp_printf(ws, "HTTP/1.%d 401 Authentication failed\r\n"
"Content-Length: 0\r\n"
"\r\n", http_ver);
cstp_fatal_close(ws, GNUTLS_A_ACCESS_DENIED);
exit_worker(ws);
return -1;
}
oclog(ws, LOG_HTTP_DEBUG, "user '%s' obtained cookie", ws->username);
ws->auth_state = S_AUTH_COOKIE;
oc_base64_encode((char *)ws->cookie, sizeof(ws->cookie),
cookie, sizeof(cookie));
/* reply */
oclog(ws, LOG_HTTP_DEBUG, "HTTP sending: 200 OK");
cstp_cork(ws);
ret = cstp_printf(ws, "HTTP/1.%u 200 OK\r\n", http_ver);
if (ret < 0)
return -1;
ret = cstp_puts(ws, "Connection: Keep-Alive\r\n");
if (ret < 0)
return -1;
ret = cstp_puts(ws, "Content-Length: 0\r\n");
if (ret < 0)
return -1;
ret = cstp_printf(ws, "Set-Cookie: webvpn=%s; secure\r\n", cookie);
if (ret < 0)
return -1;
ret = cstp_puts(ws, "X-Transcend-Version: 1\r\n");
if (ret < 0)
return -1;
/* end of headers */
ret = cstp_puts(ws, "\r\n");
if (ret < 0)
return -1;
ret = cstp_uncork(ws);
if (ret < 0)
return -1;
return 0;
}

View File

@@ -93,7 +93,8 @@ enum {
AGENT_OPENCONNECT,
AGENT_ANYCONNECT,
AGENT_OPENCONNECT_CLAVISTER,
AGENT_ANYLINK
AGENT_ANYLINK,
AGENT_SVC_IPPHONE
};
typedef int (*decompress_fn)(void* dst, int maxDstSize, const void* src, int src_size);
@@ -340,6 +341,9 @@ int get_cert_handler(worker_st * ws, unsigned http_ver);
int get_cert_der_handler(worker_st * ws, unsigned http_ver);
int get_ca_handler(worker_st * ws, unsigned http_ver);
int get_ca_der_handler(worker_st * ws, unsigned http_ver);
int get_svc_handler(worker_st *ws, unsigned http_ver);
int post_svc_handler(worker_st *ws, unsigned http_ver);
int response_404(worker_st *ws, unsigned http_ver);
int response_401(worker_st *ws, unsigned http_ver, char* realm);
@@ -414,6 +418,13 @@ int send_msg_to_secmod(worker_st * ws, int sd, uint8_t cmd,
return send_msg(ws, sd, cmd, msg, get_size, pack);
}
int recv_auth_reply(worker_st * ws, int sd, char **txt, unsigned *pcounter);
int get_cert_info(worker_st * ws);
int parse_reply(worker_st * ws, char *body, unsigned body_length,
const char *field, unsigned field_size,
const char *xml_field, unsigned xml_field_size,
char **value);
inline static
int send_msg_to_main(worker_st *ws, uint8_t cmd,
const void* msg, pack_size_func get_size, pack_func pack)

View File

@@ -99,7 +99,7 @@ dist_check_SCRIPTS += test-pass test-pass-cert test-cert test-group-pass \
test-gssapi-opt-cert haproxy-auth test-maintenance resumption \
test-group-name flowcontrol banner invalid-configs haproxy-proxyproto \
haproxy-proxyproto-v1 drain-server drain-server-fail test-ignore-querystring-of-post \
test-group-cert test-fork
test-group-cert test-fork test-pass-svc test-cert-svc
if HAVE_CWRAP_PAM
dist_check_SCRIPTS += test-pam test-pam-noauth

83
tests/test-cert-svc Executable file
View File

@@ -0,0 +1,83 @@
#!/bin/sh
#
# Copyright (C) 2023 Gareth Palmer
#
# 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.
SERV="${SERV:-../src/ocserv}"
srcdir=${srcdir:-.}
NO_NEED_ROOT=1
TMPFILE=test-cert-svc.$$.tmp
CRLNAME=crl.pem.$$.tmp
CRLTMPLNAME=crl.tmpl.$$.tmp
. `dirname $0`/common.sh
eval "${GETPORT}"
echo "Testing ocserv with certificates... "
rm -f "${CRLNAME}" "${CRLTMPLNAME}"
echo crl_next_update = 999 >"${CRLTMPLNAME}"
echo crl_number = 1 >>"${CRLTMPLNAME}"
certtool --generate-crl --load-ca-privkey "${srcdir}/certs/ca-key.pem" --load-ca-certificate "${srcdir}/certs/ca.pem" \
--outfile "${CRLNAME}" --template "${CRLTMPLNAME}" >/dev/null 2>&1
if test $? != 0;then
kill $PID
exit 77
fi
update_config test3.config
echo "udp-port = 0" >>${CONFIG}
echo "cisco-svc-client-compat = true" >>${CONFIG}
launch_simple_sr_server -d 1 -f -c ${CONFIG}
PID=$!
wait_server $PID
echo -n "Connecting to obtain cookie (without certificate)... "
( LD_PRELOAD=libsocket_wrapper.so curl --user-agent "Cisco SVC IPPhone Client v1.0" --silent --insecure https://$ADDRESS:$PORT/svc --include --request POST --output $TMPFILE ) ||
fail $PID "POST request failed"
grep -q "Set-Cookie: webvpn=[^;]\+" $TMPFILE && fail $PID "Could connect without certificate"
echo "ok (failed as expected)"
echo -n "Connecting to obtain cookie (with invalid certificate)... "
( LD_PRELOAD=libsocket_wrapper.so curl --user-agent "Cisco SVC IPPhone Client v1.0" --silent --insecure --key "${srcdir}/certs/user-key.pem" --cert "${srcdir}/certs/user-cert-invalid.pem" https://$ADDRESS:$PORT/svc --include --request POST --output $TMPFILE ) ||
echo "" >$TMPFILE
grep -q "Set-Cookie: webvpn=[^;]\+" $TMPFILE && fail $PID "Could connect with invalid certificate"
echo "ok (failed as expected)"
echo -n "Connecting to obtain cookie (with certificate)... "
( LD_PRELOAD=libsocket_wrapper.so curl --user-agent "Cisco SVC IPPhone Client v1.0" --silent --insecure --key "${srcdir}/certs/user-key.pem" --cert "${srcdir}/certs/user-cert.pem" https://$ADDRESS:$PORT/svc --include --request POST --output $TMPFILE ) ||
fail $PID "POST request failed"
grep -q "Set-Cookie: webvpn=[^;]\+" $TMPFILE || fail $PID "Could not connect with certificate"
echo ok
rm -f "${CRLNAME}" "${CRLTMPLNAME}" "${TMPFILE}"
cleanup
if test $? != 0;then
exit 1
fi
exit 0

78
tests/test-pass-svc Executable file
View File

@@ -0,0 +1,78 @@
#!/bin/sh
#
# Copyright (C) 2023 Gareth Palmer
#
# 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.
SERV="${SERV:-../src/ocserv}"
srcdir=${srcdir:-.}
NO_NEED_ROOT=1
PIDFILE=ocserv-pid.$$.tmp
TMPFILE=test-pass-svc.$$.tmp
. `dirname $0`/common.sh
eval "${GETPORT}"
echo "Testing local backend with username-password... "
update_config test1.config
echo "udp-port = 0" >>${CONFIG}
echo "cisco-svc-client-compat = true" >>${CONFIG}
launch_sr_server -d 1 -p ${PIDFILE} -f -c ${CONFIG} & PID=$!
wait_server $PID
echo "Connecting to obtain non-auth cookies... "
( LD_PRELOAD=libsocket_wrapper.so curl --user-agent "Cisco SVC IPPhone Client v1.0" --silent --insecure https://$ADDRESS:$PORT/svc --include --request GET --output $TMPFILE ) ||
fail $PID "GET request failed"
grep -q "^Set-Cookie: webvpn=; expires=" $TMPFILE || fail $PID "Did not receive cookie"
grep -q "^Set-Cookie: webvpnlogin=1" $TMPFILE || fail $PID "Did not receive non-auth cookie"
echo "Connecting to obtain cookie... "
( LD_PRELOAD=libsocket_wrapper.so curl --user-agent "Cisco SVC IPPhone Client v1.0" --silent --insecure https://$ADDRESS:$PORT/svc --include --request POST --data "username=test&password=test" --output $TMPFILE ) ||
fail $PID "POST request failed"
grep -q "^Set-Cookie: webvpn=[^;]\+" $TMPFILE || fail $PID "Did not receive cookie"
echo "Connecting to obtain cookie with wrong password... "
( LD_PRELOAD=libsocket_wrapper.so curl --user-agent "Cisco SVC IPPhone Client v1.0" --silent --insecure https://$ADDRESS:$PORT/svc --include --request POST --data "username=test&password=tost" --output $TMPFILE ) ||
fail $PID "POST request failed"
grep -q "Set-Cookie: webvpn=[^;]\+" $TMPFILE && fail $PID "Received cookie when we shouldn't"
echo "Connecting to obtain cookie with empty password... "
( LD_PRELOAD=libsocket_wrapper.so curl --user-agent "Cisco SVC IPPhone Client v1.0" --silent --insecure https://$ADDRESS:$PORT/svc --include --request POST --data "username=test&password=" --output $TMPFILE ) ||
fail $PID "POST request failed"
grep -q "Set-Cookie: webvpn=[^;]\+" $TMPFILE && fail $PID "Received cookie when we shouldn't"
echo "Connecting to obtain cookie with wrong username... "
( LD_PRELOAD=libsocket_wrapper.so curl --user-agent "Cisco SVC IPPhone Client v1.0" --silent --insecure https://$ADDRESS:$PORT/svc --include --request POST --data "username=tost&password=test" --output $TMPFILE ) ||
fail $PID "POST request failed"
grep -q "Set-Cookie: webvpn=[^;]\+" $TMPFILE && fail $PID "Received cookie when we shouldn't"
if ! test -f ${PIDFILE};then
fail $PID "Could not find pid file ${PIDFILE}"
fi
cleanup
rm -f "$TMPFILE"
exit 0