mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-10-12 01:26:48 +08:00
Allow a client to reconnect and continue authentication.
That allows clients like CISCO anyconnect to resume authentication in a different session by keeping the username in a cookie. That works only when a single password is used.
This commit is contained in:
+28
@@ -23,6 +23,7 @@
|
||||
#include <unistd.h>
|
||||
#include <syslog.h>
|
||||
#include <c-strcase.h>
|
||||
#include <c-ctype.h>
|
||||
|
||||
#include "html.h"
|
||||
|
||||
@@ -106,3 +107,30 @@ char *unescape_url(const char *url, unsigned len, unsigned *out_len)
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
char *escape_url(const char *url, unsigned len, unsigned *out_len)
|
||||
{
|
||||
char *msg;
|
||||
int pos;
|
||||
unsigned i;
|
||||
|
||||
msg = malloc(3*len + 1);
|
||||
if (msg == NULL)
|
||||
return NULL;
|
||||
|
||||
for (i = pos = 0; i < len;) {
|
||||
if (c_isalpha(url[i])) {
|
||||
msg[pos++] = url[i++];
|
||||
} else {
|
||||
snprintf(&msg[pos], 3, "%%%02x", (unsigned)url[i++]);
|
||||
pos+=3;
|
||||
}
|
||||
}
|
||||
|
||||
msg[pos] = 0;
|
||||
if (out_len)
|
||||
*out_len = pos;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,5 +23,6 @@
|
||||
|
||||
char* unescape_html(const char *html, unsigned len, unsigned *out_len);
|
||||
char *unescape_url(const char *url, unsigned len, unsigned *out_len);
|
||||
char *escape_url(const char *url, unsigned len, unsigned *out_len);
|
||||
|
||||
#endif
|
||||
|
||||
+396
-274
@@ -51,25 +51,25 @@
|
||||
|
||||
#define CONFIG_MSG "<vpn-client-pkg-version><pkgversion>0,0,0000</pkgversion></vpn-client-pkg-version>\n"
|
||||
|
||||
static const char login_msg_user[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
"<auth id=\"main\">\n"
|
||||
"<message>Please enter your username</message>\n"
|
||||
"<form method=\"post\" action=\"/auth\">\n"
|
||||
"<input type=\"text\" name=\"username\" label=\"Username:\" />\n"
|
||||
"</form></auth>\n";
|
||||
static const char login_msg_user[] =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<auth id=\"main\">\n"
|
||||
"<message>Please enter your username</message>\n"
|
||||
"<form method=\"post\" action=\"/auth\">\n"
|
||||
"<input type=\"text\" name=\"username\" label=\"Username:\" />\n"
|
||||
"</form></auth>\n";
|
||||
|
||||
static const char login_msg_no_user[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
"<auth id=\"main\">\n"
|
||||
"<message>%s</message>\n"
|
||||
"<form method=\"post\" action=\"/auth\">\n"
|
||||
"<input type=\"password\" name=\"password\" label=\"Password:\" />\n"
|
||||
"</form></auth>\n";
|
||||
static const char login_msg_no_user[] =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<auth id=\"main\">\n"
|
||||
"<message>%s</message>\n" "<form method=\"post\" action=\"/auth\">\n"
|
||||
"<input type=\"password\" name=\"password\" label=\"Password:\" />\n"
|
||||
"</form></auth>\n";
|
||||
|
||||
int get_auth_handler2(worker_st *ws, unsigned http_ver, const char* pmsg)
|
||||
int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg)
|
||||
{
|
||||
int ret;
|
||||
char login_msg[MAX_MSG_SIZE+sizeof(login_msg_user)];
|
||||
unsigned int lsize;
|
||||
int ret;
|
||||
char login_msg[MAX_MSG_SIZE + sizeof(login_msg_user)];
|
||||
unsigned int lsize;
|
||||
char *u;
|
||||
|
||||
tls_cork(ws->session);
|
||||
ret = tls_printf(ws->session, "HTTP/1.%u 200 OK\r\n", http_ver);
|
||||
@@ -83,21 +83,40 @@ unsigned int lsize;
|
||||
ret = tls_puts(ws->session, "Content-Type: text/xml\r\n");
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
|
||||
if (ws->auth_state == S_AUTH_REQ) {
|
||||
/* only ask password */
|
||||
if (pmsg == NULL)
|
||||
pmsg = "Please enter password";
|
||||
lsize = snprintf(login_msg, sizeof(login_msg), login_msg_no_user, pmsg);
|
||||
if (pmsg == NULL)
|
||||
pmsg = "Please enter password";
|
||||
lsize =
|
||||
snprintf(login_msg, sizeof(login_msg), login_msg_no_user,
|
||||
pmsg);
|
||||
} else {
|
||||
/* ask for username only */
|
||||
lsize = snprintf(login_msg, sizeof(login_msg), login_msg_user);
|
||||
}
|
||||
|
||||
ret = tls_printf(ws->session, "Content-Length: %u\r\n", (unsigned int)lsize);
|
||||
ret =
|
||||
tls_printf(ws->session, "Content-Length: %u\r\n",
|
||||
(unsigned int)lsize);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
#ifdef ANYCONNECT_CLIENT_COMPAT
|
||||
if (ws->username[0] != 0) {
|
||||
/* This is to make sure that some cisco clients that
|
||||
* like to connect for each request, don't lose the
|
||||
* username */
|
||||
u = escape_url(ws->username, strlen(ws->username), NULL);
|
||||
ret = tls_printf(ws->session, "Set-Cookie: ocuser=%s\r\n", u);
|
||||
|
||||
free(u);
|
||||
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = tls_puts(ws->session, "X-Transcend-Version: 1\r\n");
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
@@ -113,175 +132,196 @@ unsigned int lsize;
|
||||
ret = tls_uncork(ws->session);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_auth_handler(worker_st *ws, unsigned http_ver)
|
||||
int get_auth_handler(worker_st * ws, unsigned http_ver)
|
||||
{
|
||||
return get_auth_handler2(ws, http_ver, NULL);
|
||||
}
|
||||
|
||||
static
|
||||
int get_cert_names(worker_st *ws, const gnutls_datum_t* raw,
|
||||
char* username, size_t username_size,
|
||||
char* groupname, size_t groupname_size)
|
||||
int get_cert_names(worker_st * ws, const gnutls_datum_t * raw,
|
||||
char *username, size_t username_size,
|
||||
char *groupname, size_t groupname_size)
|
||||
{
|
||||
gnutls_x509_crt_t crt;
|
||||
int ret;
|
||||
gnutls_x509_crt_t crt;
|
||||
int ret;
|
||||
|
||||
ret = gnutls_x509_crt_init(&crt);
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_ERR, "certificate init error: %s", gnutls_strerror(ret));
|
||||
oclog(ws, LOG_ERR, "certificate init error: %s",
|
||||
gnutls_strerror(ret));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
ret = gnutls_x509_crt_import(crt, raw, GNUTLS_X509_FMT_DER);
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_ERR, "certificate import error: %s", gnutls_strerror(ret));
|
||||
oclog(ws, LOG_ERR, "certificate import error: %s",
|
||||
gnutls_strerror(ret));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ws->config->cert_user_oid) { /* otherwise certificate username is ignored */
|
||||
ret = gnutls_x509_crt_get_dn_by_oid (crt, ws->config->cert_user_oid,
|
||||
0, 0, username, &username_size);
|
||||
|
||||
if (ws->config->cert_user_oid) { /* otherwise certificate username is ignored */
|
||||
ret =
|
||||
gnutls_x509_crt_get_dn_by_oid(crt,
|
||||
ws->config->cert_user_oid, 0,
|
||||
0, username, &username_size);
|
||||
} else {
|
||||
ret = gnutls_x509_crt_get_dn (crt, username, &username_size);
|
||||
ret = gnutls_x509_crt_get_dn(crt, username, &username_size);
|
||||
}
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_ERR, "cannot obtain user from certificate DN: %s", gnutls_strerror(ret));
|
||||
oclog(ws, LOG_ERR, "cannot obtain user from certificate DN: %s",
|
||||
gnutls_strerror(ret));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ws->config->cert_group_oid) {
|
||||
ret = gnutls_x509_crt_get_dn_by_oid (crt, ws->config->cert_group_oid,
|
||||
0, 0, groupname, &groupname_size);
|
||||
ret =
|
||||
gnutls_x509_crt_get_dn_by_oid(crt,
|
||||
ws->config->cert_group_oid, 0,
|
||||
0, groupname,
|
||||
&groupname_size);
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_ERR, "cannot obtain group from certificate DN: %s", gnutls_strerror(ret));
|
||||
oclog(ws, LOG_ERR,
|
||||
"cannot obtain group from certificate DN: %s",
|
||||
gnutls_strerror(ret));
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
groupname[0] = 0;
|
||||
}
|
||||
|
||||
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
|
||||
fail:
|
||||
gnutls_x509_crt_deinit(crt);
|
||||
return ret;
|
||||
|
||||
|
||||
}
|
||||
|
||||
static int recv_auth_reply(worker_st *ws, char* txt, size_t max_txt_size)
|
||||
static int recv_auth_reply(worker_st * ws, char *txt, size_t max_txt_size)
|
||||
{
|
||||
unsigned i;
|
||||
int ret;
|
||||
int socketfd = -1;
|
||||
AuthReplyMsg *msg = NULL;
|
||||
|
||||
|
||||
ret = recv_socket_msg(ws->cmd_fd, AUTH_REP, &socketfd,
|
||||
(void*)&msg,
|
||||
(unpack_func)auth_reply_msg__unpack);
|
||||
(void *)&msg,
|
||||
(unpack_func) auth_reply_msg__unpack);
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_ERR, "error receiving auth reply message");
|
||||
return ret;
|
||||
}
|
||||
|
||||
oclog(ws, LOG_DEBUG, "received auth reply message %u", (unsigned)msg->reply);
|
||||
oclog(ws, LOG_DEBUG, "received auth reply message %u",
|
||||
(unsigned)msg->reply);
|
||||
|
||||
switch(msg->reply) {
|
||||
case AUTH_REPLY_MSG__AUTH__REP__MSG:
|
||||
if (txt == NULL || msg->msg == NULL) {
|
||||
oclog(ws, LOG_ERR, "received unexpected msg");
|
||||
return ERR_AUTH_FAIL;
|
||||
}
|
||||
|
||||
snprintf(txt, max_txt_size, "%s", msg->msg);
|
||||
|
||||
ret = ERR_AUTH_CONTINUE;
|
||||
goto cleanup;
|
||||
case AUTH_REPLY_MSG__AUTH__REP__OK:
|
||||
if (socketfd != -1) {
|
||||
ws->tun_fd = socketfd;
|
||||
switch (msg->reply) {
|
||||
case AUTH_REPLY_MSG__AUTH__REP__MSG:
|
||||
if (txt == NULL || msg->msg == NULL) {
|
||||
oclog(ws, LOG_ERR, "received unexpected msg");
|
||||
return ERR_AUTH_FAIL;
|
||||
}
|
||||
|
||||
if (msg->vname == NULL || msg->user_name == NULL) {
|
||||
ret = ERR_AUTH_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
snprintf(txt, max_txt_size, "%s", msg->msg);
|
||||
|
||||
snprintf(ws->tun_name, sizeof(ws->tun_name), "%s", msg->vname);
|
||||
snprintf(ws->username, sizeof(ws->username), "%s", msg->user_name);
|
||||
ret = ERR_AUTH_CONTINUE;
|
||||
goto cleanup;
|
||||
case AUTH_REPLY_MSG__AUTH__REP__OK:
|
||||
if (socketfd != -1) {
|
||||
ws->tun_fd = socketfd;
|
||||
|
||||
if (msg->cookie.len != sizeof(ws->cookie) ||
|
||||
msg->session_id.len != sizeof(ws->session_id)) {
|
||||
ret = ERR_AUTH_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
memcpy(ws->cookie, msg->cookie.data, msg->cookie.len);
|
||||
memcpy(ws->session_id, msg->session_id.data, msg->session_id.len);
|
||||
|
||||
/* Read any additional data */
|
||||
if (msg->ipv4_dns != NULL) {
|
||||
free(ws->config->network.ipv4_dns);
|
||||
ws->config->network.ipv4_dns = strdup(msg->ipv4_dns);
|
||||
}
|
||||
|
||||
if (msg->ipv6_dns != NULL) {
|
||||
free(ws->config->network.ipv6_dns);
|
||||
ws->config->network.ipv4_dns = strdup(msg->ipv6_dns);
|
||||
}
|
||||
|
||||
if (msg->ipv4_nbns != NULL) {
|
||||
free(ws->config->network.ipv4_nbns);
|
||||
ws->config->network.ipv4_nbns = strdup(msg->ipv4_nbns);
|
||||
}
|
||||
|
||||
if (msg->ipv6_nbns != NULL) {
|
||||
free(ws->config->network.ipv6_nbns);
|
||||
ws->config->network.ipv4_nbns = strdup(msg->ipv6_nbns);
|
||||
}
|
||||
|
||||
if (msg->ipv4_netmask != NULL) {
|
||||
free(ws->config->network.ipv4_netmask);
|
||||
ws->config->network.ipv4_netmask = strdup(msg->ipv4_netmask);
|
||||
}
|
||||
|
||||
if (msg->ipv6_netmask != NULL) {
|
||||
free(ws->config->network.ipv6_netmask);
|
||||
ws->config->network.ipv4_netmask = strdup(msg->ipv6_netmask);
|
||||
}
|
||||
|
||||
if (msg->has_rx_per_sec)
|
||||
ws->config->rx_per_sec = msg->rx_per_sec;
|
||||
|
||||
if (msg->has_tx_per_sec)
|
||||
ws->config->tx_per_sec = msg->tx_per_sec;
|
||||
|
||||
if (msg->has_net_priority)
|
||||
ws->config->net_priority = msg->net_priority;
|
||||
|
||||
/* routes */
|
||||
ws->routes_size = msg->n_routes;
|
||||
|
||||
for (i=0;i<ws->routes_size;i++) {
|
||||
ws->routes[i] = strdup(msg->routes[i]);
|
||||
}
|
||||
} else {
|
||||
oclog(ws, LOG_ERR, "error in received message");
|
||||
if (msg->vname == NULL || msg->user_name == NULL) {
|
||||
ret = ERR_AUTH_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
break;
|
||||
case AUTH_REPLY_MSG__AUTH__REP__FAILED:
|
||||
default:
|
||||
if (msg->reply != AUTH_REPLY_MSG__AUTH__REP__FAILED)
|
||||
oclog(ws, LOG_ERR, "unexpected auth reply %u", (unsigned)msg->reply);
|
||||
|
||||
snprintf(ws->tun_name, sizeof(ws->tun_name), "%s",
|
||||
msg->vname);
|
||||
snprintf(ws->username, sizeof(ws->username), "%s",
|
||||
msg->user_name);
|
||||
|
||||
if (msg->cookie.len != sizeof(ws->cookie) ||
|
||||
msg->session_id.len != sizeof(ws->session_id)) {
|
||||
ret = ERR_AUTH_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
memcpy(ws->cookie, msg->cookie.data, msg->cookie.len);
|
||||
memcpy(ws->session_id, msg->session_id.data,
|
||||
msg->session_id.len);
|
||||
|
||||
/* Read any additional data */
|
||||
if (msg->ipv4_dns != NULL) {
|
||||
free(ws->config->network.ipv4_dns);
|
||||
ws->config->network.ipv4_dns =
|
||||
strdup(msg->ipv4_dns);
|
||||
}
|
||||
|
||||
if (msg->ipv6_dns != NULL) {
|
||||
free(ws->config->network.ipv6_dns);
|
||||
ws->config->network.ipv4_dns =
|
||||
strdup(msg->ipv6_dns);
|
||||
}
|
||||
|
||||
if (msg->ipv4_nbns != NULL) {
|
||||
free(ws->config->network.ipv4_nbns);
|
||||
ws->config->network.ipv4_nbns =
|
||||
strdup(msg->ipv4_nbns);
|
||||
}
|
||||
|
||||
if (msg->ipv6_nbns != NULL) {
|
||||
free(ws->config->network.ipv6_nbns);
|
||||
ws->config->network.ipv4_nbns =
|
||||
strdup(msg->ipv6_nbns);
|
||||
}
|
||||
|
||||
if (msg->ipv4_netmask != NULL) {
|
||||
free(ws->config->network.ipv4_netmask);
|
||||
ws->config->network.ipv4_netmask =
|
||||
strdup(msg->ipv4_netmask);
|
||||
}
|
||||
|
||||
if (msg->ipv6_netmask != NULL) {
|
||||
free(ws->config->network.ipv6_netmask);
|
||||
ws->config->network.ipv4_netmask =
|
||||
strdup(msg->ipv6_netmask);
|
||||
}
|
||||
|
||||
if (msg->has_rx_per_sec)
|
||||
ws->config->rx_per_sec = msg->rx_per_sec;
|
||||
|
||||
if (msg->has_tx_per_sec)
|
||||
ws->config->tx_per_sec = msg->tx_per_sec;
|
||||
|
||||
if (msg->has_net_priority)
|
||||
ws->config->net_priority = msg->net_priority;
|
||||
|
||||
/* routes */
|
||||
ws->routes_size = msg->n_routes;
|
||||
|
||||
for (i = 0; i < ws->routes_size; i++) {
|
||||
ws->routes[i] = strdup(msg->routes[i]);
|
||||
}
|
||||
} else {
|
||||
oclog(ws, LOG_ERR, "error in received message");
|
||||
ret = ERR_AUTH_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
break;
|
||||
case AUTH_REPLY_MSG__AUTH__REP__FAILED:
|
||||
default:
|
||||
if (msg->reply != AUTH_REPLY_MSG__AUTH__REP__FAILED)
|
||||
oclog(ws, LOG_ERR, "unexpected auth reply %u",
|
||||
(unsigned)msg->reply);
|
||||
ret = ERR_AUTH_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
cleanup:
|
||||
cleanup:
|
||||
if (msg != NULL)
|
||||
auth_reply_msg__free_unpacked(msg, NULL);
|
||||
return ret;
|
||||
@@ -289,24 +329,25 @@ cleanup:
|
||||
|
||||
/* grabs the username from the session certificate */
|
||||
static
|
||||
int get_cert_info(worker_st *ws, char* user, unsigned user_size,
|
||||
char* group, unsigned group_size)
|
||||
int get_cert_info(worker_st * ws, char *user, unsigned user_size,
|
||||
char *group, unsigned group_size)
|
||||
{
|
||||
const gnutls_datum_t * cert;
|
||||
unsigned int ncerts;
|
||||
int ret;
|
||||
const gnutls_datum_t *cert;
|
||||
unsigned int ncerts;
|
||||
int ret;
|
||||
|
||||
/* this is superflous. Verification has already been performed
|
||||
* during handshake. */
|
||||
cert = gnutls_certificate_get_peers (ws->session, &ncerts);
|
||||
cert = gnutls_certificate_get_peers(ws->session, &ncerts);
|
||||
|
||||
if (cert == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
ret = get_cert_names(ws, cert, user, user_size, group, group_size);
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_ERR, "cannot get username (%s) from certificate", ws->config->cert_user_oid);
|
||||
oclog(ws, LOG_ERR, "cannot get username (%s) from certificate",
|
||||
ws->config->cert_user_oid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -317,21 +358,23 @@ int ret;
|
||||
* a reply.
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int auth_cookie(worker_st *ws, void* cookie, size_t cookie_size)
|
||||
int auth_cookie(worker_st * ws, void *cookie, size_t cookie_size)
|
||||
{
|
||||
int ret;
|
||||
AuthCookieRequestMsg msg = AUTH_COOKIE_REQUEST_MSG__INIT;
|
||||
char tmp_user[MAX_USERNAME_SIZE];
|
||||
char tmp_group[MAX_USERNAME_SIZE];
|
||||
int ret;
|
||||
AuthCookieRequestMsg msg = AUTH_COOKIE_REQUEST_MSG__INIT;
|
||||
char tmp_user[MAX_USERNAME_SIZE];
|
||||
char tmp_group[MAX_USERNAME_SIZE];
|
||||
|
||||
if ((ws->config->auth_types & AUTH_TYPE_CERTIFICATE) && ws->config->force_cert_auth != 0) {
|
||||
if ((ws->config->auth_types & AUTH_TYPE_CERTIFICATE)
|
||||
&& ws->config->force_cert_auth != 0) {
|
||||
if (ws->cert_auth_ok == 0) {
|
||||
oclog(ws, LOG_INFO, "no certificate provided for cookie authentication");
|
||||
oclog(ws, LOG_INFO,
|
||||
"no certificate provided for cookie authentication");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = get_cert_info(ws, tmp_user, sizeof(tmp_user),
|
||||
tmp_group, sizeof(tmp_group));
|
||||
tmp_group, sizeof(tmp_group));
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_INFO, "cannot obtain certificate info");
|
||||
return -1;
|
||||
@@ -347,35 +390,38 @@ char tmp_group[MAX_USERNAME_SIZE];
|
||||
|
||||
oclog(ws, LOG_DEBUG, "sending cookie authentication request");
|
||||
|
||||
ret = send_msg_to_main(ws, AUTH_COOKIE_REQ, &msg,
|
||||
(pack_size_func)auth_cookie_request_msg__get_packed_size,
|
||||
(pack_func)auth_cookie_request_msg__pack);
|
||||
ret = send_msg_to_main(ws, AUTH_COOKIE_REQ, &msg,
|
||||
(pack_size_func)
|
||||
auth_cookie_request_msg__get_packed_size,
|
||||
(pack_func) auth_cookie_request_msg__pack);
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_INFO, "error sending cookie authentication request");
|
||||
oclog(ws, LOG_INFO,
|
||||
"error sending cookie authentication request");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = recv_auth_reply(ws, NULL, 0);
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_INFO, "error receiving cookie authentication reply");
|
||||
oclog(ws, LOG_INFO,
|
||||
"error receiving cookie authentication reply");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int post_common_handler(worker_st *ws, unsigned http_ver)
|
||||
int post_common_handler(worker_st * ws, unsigned http_ver)
|
||||
{
|
||||
int ret, size;
|
||||
char str_cookie[2*COOKIE_SIZE+1];
|
||||
char *p;
|
||||
unsigned i;
|
||||
char msg[MAX_BANNER_SIZE+32];
|
||||
int ret, size;
|
||||
char str_cookie[2 * COOKIE_SIZE + 1];
|
||||
char *p;
|
||||
unsigned i;
|
||||
char msg[MAX_BANNER_SIZE + 32];
|
||||
|
||||
p = str_cookie;
|
||||
for (i=0;i<sizeof(ws->cookie);i++) {
|
||||
for (i = 0; i < sizeof(ws->cookie); i++) {
|
||||
sprintf(p, "%.2x", (unsigned int)ws->cookie[i]);
|
||||
p+=2;
|
||||
p += 2;
|
||||
}
|
||||
|
||||
/* reply */
|
||||
@@ -388,13 +434,15 @@ char msg[MAX_BANNER_SIZE+32];
|
||||
ret = tls_puts(ws->session, "Connection: Keep-Alive\r\n");
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
|
||||
ret = tls_puts(ws->session, "Content-Type: text/xml\r\n");
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
if (ws->config->banner) {
|
||||
size = snprintf(msg, sizeof(msg), "<banner>%s</banner>", ws->config->banner);
|
||||
size =
|
||||
snprintf(msg, sizeof(msg), "<banner>%s</banner>",
|
||||
ws->config->banner);
|
||||
if (size <= 0)
|
||||
return -1;
|
||||
} else {
|
||||
@@ -402,9 +450,9 @@ char msg[MAX_BANNER_SIZE+32];
|
||||
size = 0;
|
||||
}
|
||||
|
||||
size += (sizeof(SUCCESS_MSG_HEAD)-1) + (sizeof(SUCCESS_MSG_FOOT)-1);
|
||||
size += (sizeof(SUCCESS_MSG_HEAD) - 1) + (sizeof(SUCCESS_MSG_FOOT) - 1);
|
||||
|
||||
ret = tls_printf(ws->session, "Content-Length: %u\r\n", (unsigned)size);
|
||||
ret = tls_printf(ws->session, "Content-Length: %u\r\n", (unsigned)size);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
@@ -412,35 +460,45 @@ char msg[MAX_BANNER_SIZE+32];
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
ret = tls_printf(ws->session, "Set-Cookie: webvpn=%s;Max-Age=%u\r\n", str_cookie, (unsigned)ws->config->cookie_validity);
|
||||
ret =
|
||||
tls_printf(ws->session, "Set-Cookie: webvpn=%s;Max-Age=%u\r\n",
|
||||
str_cookie, (unsigned)ws->config->cookie_validity);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
#ifdef ANYCONNECT_CLIENT_COMPAT
|
||||
ret = tls_puts(ws->session, "Set-Cookie: webvpnc=; expires=Thu, 01 Jan 1970 22:00:00 GMT; path=/; secure\r\n");
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
ret =
|
||||
tls_puts(ws->session,
|
||||
"Set-Cookie: webvpnc=; expires=Thu, 01 Jan 1970 22:00:00 GMT; path=/; secure\r\n");
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
if (ws->config->xml_config_file) {
|
||||
ret = tls_printf(ws->session, "Set-Cookie: webvpnc=bu:/&p:t&iu:1/&sh:%s&lu:/+CSCOT+/translation-table?textdomain%%3DAnyConnect%%26type%%3Dmanifest&fu:profiles%%2F%s&fh:%s; path=/; secure\r\n",
|
||||
ws->config->cert_hash,
|
||||
ws->config->xml_config_file,
|
||||
ws->config->xml_config_hash);
|
||||
ret =
|
||||
tls_printf(ws->session,
|
||||
"Set-Cookie: webvpnc=bu:/&p:t&iu:1/&sh:%s&lu:/+CSCOT+/translation-table?textdomain%%3DAnyConnect%%26type%%3Dmanifest&fu:profiles%%2F%s&fh:%s; path=/; secure\r\n",
|
||||
ws->config->cert_hash,
|
||||
ws->config->xml_config_file,
|
||||
ws->config->xml_config_hash);
|
||||
} else {
|
||||
ret = tls_printf(ws->session, "Set-Cookie: webvpnc=bu:/&p:t&iu:1/&sh:%s; path=/; secure\r\n",
|
||||
ws->config->cert_hash);
|
||||
ret =
|
||||
tls_printf(ws->session,
|
||||
"Set-Cookie: webvpnc=bu:/&p:t&iu:1/&sh:%s; path=/; secure\r\n",
|
||||
ws->config->cert_hash);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
ret = tls_printf(ws->session, "\r\n"SUCCESS_MSG_HEAD"%s"SUCCESS_MSG_FOOT, msg);
|
||||
ret =
|
||||
tls_printf(ws->session,
|
||||
"\r\n" SUCCESS_MSG_HEAD "%s" SUCCESS_MSG_FOOT, msg);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
#ifdef ANYCONNECT_CLIENT_COMPAT
|
||||
ret = tls_send(ws->session, CONFIG_MSG, sizeof(CONFIG_MSG)-1);
|
||||
ret = tls_send(ws->session, CONFIG_MSG, sizeof(CONFIG_MSG) - 1);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
#endif
|
||||
@@ -448,7 +506,7 @@ char msg[MAX_BANNER_SIZE+32];
|
||||
ret = tls_uncork(ws->session);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -461,137 +519,181 @@ char msg[MAX_BANNER_SIZE+32];
|
||||
* buffers.
|
||||
*/
|
||||
static
|
||||
int read_user_pass(worker_st *ws, char* body, unsigned body_length, char** username, char** password)
|
||||
int read_user_pass(worker_st * ws, char *body, unsigned body_length,
|
||||
char **username, char **password)
|
||||
{
|
||||
char *p;
|
||||
|
||||
|
||||
if (memmem(body, body_length, "<?xml", 5) != 0) {
|
||||
oclog(ws, LOG_HTTP_DEBUG, "POST body: '%.*s'", body_length, body);
|
||||
oclog(ws, LOG_HTTP_DEBUG, "POST body: '%.*s'", body_length,
|
||||
body);
|
||||
|
||||
if (username != NULL) {
|
||||
/* body should contain <username>test</username><password>test</password> */
|
||||
*username = memmem(body, body_length, XMLUSER, sizeof(XMLUSER)-1);
|
||||
*username =
|
||||
memmem(body, body_length, XMLUSER,
|
||||
sizeof(XMLUSER) - 1);
|
||||
if (*username == NULL) {
|
||||
oclog(ws, LOG_ERR, "cannot find username in client XML message");
|
||||
oclog(ws, LOG_ERR,
|
||||
"cannot find username in client XML message");
|
||||
return -1;
|
||||
}
|
||||
*username += sizeof(XMLUSER)-1;
|
||||
*username += sizeof(XMLUSER) - 1;
|
||||
}
|
||||
|
||||
if (password != NULL) {
|
||||
*password = memmem(body, body_length, XMLPASS, sizeof(XMLPASS)-1);
|
||||
if (*password == NULL) {
|
||||
oclog(ws, LOG_ERR, "cannot find password in client XML message");
|
||||
return -1;
|
||||
}
|
||||
*password += sizeof(XMLPASS)-1;
|
||||
}
|
||||
|
||||
*password =
|
||||
memmem(body, body_length, XMLPASS,
|
||||
sizeof(XMLPASS) - 1);
|
||||
if (*password == NULL) {
|
||||
oclog(ws, LOG_ERR,
|
||||
"cannot find password in client XML message");
|
||||
return -1;
|
||||
}
|
||||
*password += sizeof(XMLPASS) - 1;
|
||||
}
|
||||
|
||||
/* modify body */
|
||||
if (username != NULL) {
|
||||
p = *username;
|
||||
while(*p != 0) {
|
||||
if (*p == '<' && (strncmp(p, XMLUSER_END, sizeof(XMLUSER_END)-1) == 0)) {
|
||||
while (*p != 0) {
|
||||
if (*p == '<'
|
||||
&&
|
||||
(strncmp
|
||||
(p, XMLUSER_END,
|
||||
sizeof(XMLUSER_END) - 1) == 0)) {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
*username = unescape_html(*username, strlen(*username), NULL);
|
||||
*username =
|
||||
unescape_html(*username, strlen(*username), NULL);
|
||||
}
|
||||
|
||||
if (password != NULL) {
|
||||
p = *password;
|
||||
while(*p != 0) {
|
||||
if (*p == '<' && (strncmp(p, XMLPASS_END, sizeof(XMLPASS_END)-1) == 0)) {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
p = *password;
|
||||
while (*p != 0) {
|
||||
if (*p == '<'
|
||||
&&
|
||||
(strncmp
|
||||
(p, XMLPASS_END,
|
||||
sizeof(XMLPASS_END) - 1) == 0)) {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
*password = unescape_html(*password, strlen(*password), NULL);
|
||||
}
|
||||
*password =
|
||||
unescape_html(*password, strlen(*password), NULL);
|
||||
}
|
||||
|
||||
} else { /* non-xml version */
|
||||
} else { /* non-xml version */
|
||||
/* body should be "username=test&password=test" */
|
||||
if (username != NULL) {
|
||||
*username = memmem(body, body_length, "username=", sizeof("username=")-1);
|
||||
*username =
|
||||
memmem(body, body_length, "username=",
|
||||
sizeof("username=") - 1);
|
||||
if (*username == NULL) {
|
||||
oclog(ws, LOG_ERR, "cannot find username in client message");
|
||||
oclog(ws, LOG_ERR,
|
||||
"cannot find username in client message");
|
||||
return -1;
|
||||
}
|
||||
*username += sizeof("username=")-1;
|
||||
*username += sizeof("username=") - 1;
|
||||
}
|
||||
|
||||
if (password != NULL) {
|
||||
*password = memmem(body, body_length, "password=", sizeof("password=")-1);
|
||||
if (*password == NULL) {
|
||||
oclog(ws, LOG_ERR, "cannot find password in client message");
|
||||
return -1;
|
||||
}
|
||||
*password += sizeof("password=")-1;
|
||||
}
|
||||
|
||||
*password =
|
||||
memmem(body, body_length, "password=",
|
||||
sizeof("password=") - 1);
|
||||
if (*password == NULL) {
|
||||
oclog(ws, LOG_ERR,
|
||||
"cannot find password in client message");
|
||||
return -1;
|
||||
}
|
||||
*password += sizeof("password=") - 1;
|
||||
}
|
||||
|
||||
/* modify body */
|
||||
if (username != NULL) {
|
||||
p = *username;
|
||||
while(*p != 0) {
|
||||
while (*p != 0) {
|
||||
if (*p == '&') {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
*username = unescape_url(*username, strlen(*username), NULL);
|
||||
|
||||
*username =
|
||||
unescape_url(*username, strlen(*username), NULL);
|
||||
}
|
||||
|
||||
if (password != NULL) {
|
||||
p = *password;
|
||||
while(*p != 0) {
|
||||
if (*p == '&') {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
p = *password;
|
||||
while (*p != 0) {
|
||||
if (*p == '&') {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
*password = unescape_url(*password, strlen(*password), NULL);
|
||||
}
|
||||
*password =
|
||||
unescape_url(*password, strlen(*password), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (username != NULL && *username == NULL) {
|
||||
oclog(ws, LOG_ERR, "username requested but no username in client message");
|
||||
oclog(ws, LOG_ERR,
|
||||
"username requested but no username in client message");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (password != NULL && *password == NULL) {
|
||||
oclog(ws, LOG_ERR, "password requested but no password in client message");
|
||||
oclog(ws, LOG_ERR,
|
||||
"password requested but no password in client message");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int post_auth_handler(worker_st *ws, unsigned http_ver)
|
||||
int post_auth_handler(worker_st * ws, unsigned http_ver)
|
||||
{
|
||||
int ret;
|
||||
struct http_req_st *req = &ws->req;
|
||||
const char* reason = "Authentication failed";
|
||||
char * username = NULL;
|
||||
char * password = NULL;
|
||||
char tmp_user[MAX_USERNAME_SIZE];
|
||||
char tmp_group[MAX_USERNAME_SIZE];
|
||||
char msg[MAX_MSG_SIZE];
|
||||
int ret;
|
||||
struct http_req_st *req = &ws->req;
|
||||
const char *reason = "Authentication failed";
|
||||
char *username = NULL;
|
||||
char *password = NULL;
|
||||
char tmp_user[MAX_USERNAME_SIZE];
|
||||
char tmp_group[MAX_USERNAME_SIZE];
|
||||
char msg[MAX_MSG_SIZE];
|
||||
unsigned complete_auth = 0;
|
||||
|
||||
restart:
|
||||
|
||||
if (ws->auth_state == S_AUTH_INACTIVE) {
|
||||
AuthInitMsg msg = AUTH_INIT_MSG__INIT;
|
||||
|
||||
AuthInitMsg ireq = AUTH_INIT_MSG__INIT;
|
||||
|
||||
#ifdef ANYCONNECT_CLIENT_COMPAT
|
||||
if (req->ocuser_cookie_set != 0) {
|
||||
/* the client has provided the username in a different
|
||||
* session and reconnected here to provide the password.
|
||||
* So we read the username from the cookie, start auth
|
||||
* and continue reading the password.
|
||||
*/
|
||||
complete_auth = 1;
|
||||
ireq.user_name = ws->username;
|
||||
} else
|
||||
#endif
|
||||
if (ws->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
|
||||
ret = read_user_pass(ws, req->body, req->body_length, &username, NULL);
|
||||
ret =
|
||||
read_user_pass(ws, req->body, req->body_length,
|
||||
&username, NULL);
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_ERR, "failed reading username");
|
||||
goto ask_auth;
|
||||
@@ -599,44 +701,51 @@ char msg[MAX_MSG_SIZE];
|
||||
|
||||
snprintf(tmp_user, sizeof(tmp_user), "%s", username);
|
||||
free(username);
|
||||
msg.user_name = tmp_user;
|
||||
ireq.user_name = tmp_user;
|
||||
}
|
||||
|
||||
if (ws->config->auth_types & AUTH_TYPE_CERTIFICATE) {
|
||||
if (ws->cert_auth_ok == 0) {
|
||||
oclog(ws, LOG_INFO, "no certificate provided for authentication");
|
||||
oclog(ws, LOG_INFO,
|
||||
"no certificate provided for authentication");
|
||||
goto auth_fail;
|
||||
}
|
||||
|
||||
ret = get_cert_info(ws, tmp_user, sizeof(tmp_user),
|
||||
tmp_group, sizeof(tmp_group));
|
||||
tmp_group, sizeof(tmp_group));
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_ERR, "failed reading certificate info");
|
||||
oclog(ws, LOG_ERR,
|
||||
"failed reading certificate info");
|
||||
goto auth_fail;
|
||||
}
|
||||
|
||||
msg.tls_auth_ok = 1;
|
||||
msg.cert_user_name = tmp_user;
|
||||
msg.cert_group_name = tmp_group;
|
||||
ireq.tls_auth_ok = 1;
|
||||
ireq.cert_user_name = tmp_user;
|
||||
ireq.cert_group_name = tmp_group;
|
||||
}
|
||||
|
||||
msg.hostname = req->hostname;
|
||||
ireq.hostname = req->hostname;
|
||||
|
||||
ret = send_msg_to_main(ws, AUTH_INIT,
|
||||
&msg,
|
||||
(pack_size_func)auth_init_msg__get_packed_size,
|
||||
(pack_func)auth_init_msg__pack);
|
||||
ret = send_msg_to_main(ws, AUTH_INIT,
|
||||
&ireq,
|
||||
(pack_size_func)
|
||||
auth_init_msg__get_packed_size,
|
||||
(pack_func) auth_init_msg__pack);
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_ERR, "failed sending auth init message to main");
|
||||
oclog(ws, LOG_ERR,
|
||||
"failed sending auth init message to main");
|
||||
goto auth_fail;
|
||||
}
|
||||
|
||||
|
||||
ws->auth_state = S_AUTH_INIT;
|
||||
} else if (ws->auth_state == S_AUTH_INIT || ws->auth_state == S_AUTH_REQ) {
|
||||
} else if (ws->auth_state == S_AUTH_INIT
|
||||
|| ws->auth_state == S_AUTH_REQ) {
|
||||
AuthRequestMsg areq = AUTH_REQUEST_MSG__INIT;
|
||||
|
||||
if (ws->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
|
||||
ret = read_user_pass(ws, req->body, req->body_length, NULL, &password);
|
||||
ret =
|
||||
read_user_pass(ws, req->body, req->body_length,
|
||||
NULL, &password);
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_ERR, "failed reading password");
|
||||
goto auth_fail;
|
||||
@@ -645,31 +754,43 @@ char msg[MAX_MSG_SIZE];
|
||||
areq.password = password;
|
||||
|
||||
ret = send_msg_to_main(ws, AUTH_REQ, &areq,
|
||||
(pack_size_func)auth_request_msg__get_packed_size,
|
||||
(pack_func)auth_request_msg__pack);
|
||||
(pack_size_func)
|
||||
auth_request_msg__get_packed_size,
|
||||
(pack_func)
|
||||
auth_request_msg__pack);
|
||||
|
||||
free(password);
|
||||
|
||||
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_ERR, "failed sending auth req message to main");
|
||||
oclog(ws, LOG_ERR,
|
||||
"failed sending auth req message to main");
|
||||
goto auth_fail;
|
||||
}
|
||||
|
||||
|
||||
ws->auth_state = S_AUTH_REQ;
|
||||
} else
|
||||
goto auth_fail;
|
||||
} else {
|
||||
oclog(ws, LOG_ERR, "unexpected POST request in auth state %u", (unsigned)ws->auth_state);
|
||||
oclog(ws, LOG_ERR, "unexpected POST request in auth state %u",
|
||||
(unsigned)ws->auth_state);
|
||||
goto auth_fail;
|
||||
}
|
||||
|
||||
ret = recv_auth_reply(ws, msg, sizeof(msg));
|
||||
if (ret == ERR_AUTH_CONTINUE) {
|
||||
oclog(ws, LOG_DEBUG, "continuing authentication for '%s'", ws->username);
|
||||
oclog(ws, LOG_DEBUG, "continuing authentication for '%s'",
|
||||
ws->username);
|
||||
ws->auth_state = S_AUTH_REQ;
|
||||
|
||||
#ifdef ANYCONNECT_CLIENT_COMPAT
|
||||
if (complete_auth != 0) {
|
||||
goto restart;
|
||||
}
|
||||
#endif
|
||||
return get_auth_handler2(ws, http_ver, msg);
|
||||
} else if (ret < 0) {
|
||||
oclog(ws, LOG_ERR, "failed authentication for '%s'", ws->username);
|
||||
} else if (ret < 0) {
|
||||
oclog(ws, LOG_ERR, "failed authentication for '%s'",
|
||||
ws->username);
|
||||
goto auth_fail;
|
||||
}
|
||||
|
||||
@@ -678,12 +799,13 @@ char msg[MAX_MSG_SIZE];
|
||||
|
||||
return post_common_handler(ws, http_ver);
|
||||
|
||||
ask_auth:
|
||||
ask_auth:
|
||||
return get_auth_handler(ws, http_ver);
|
||||
|
||||
auth_fail:
|
||||
auth_fail:
|
||||
tls_printf(ws->session,
|
||||
"HTTP/1.1 503 Service Unavailable\r\nX-Reason: %s\r\n\r\n", reason);
|
||||
"HTTP/1.1 503 Service Unavailable\r\nX-Reason: %s\r\n\r\n",
|
||||
reason);
|
||||
tls_fatal_close(ws->session, GNUTLS_A_ACCESS_DENIED);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
+710
-509
File diff suppressed because it is too large
Load Diff
@@ -76,6 +76,7 @@ struct http_req_st {
|
||||
unsigned int next_header;
|
||||
unsigned char cookie[COOKIE_SIZE];
|
||||
unsigned int cookie_set;
|
||||
unsigned int ocuser_cookie_set;
|
||||
unsigned char master_secret[TLS_MASTER_SIZE];
|
||||
unsigned int master_secret_set;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user