Files
ocserv/src/worker-auth.c
Nikos Mavrogiannopoulos 6fe528ec4c post_auth_handler: added error checking to cstp_printf
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2020-11-26 14:22:25 +01:00

1745 lines
42 KiB
C

/*
* Copyright (C) 2013, 2014, 2015 Nikos Mavrogiannopoulos
* Copyright (C) 2015 Red Hat, Inc.
*
* 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 <errno.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <ipc.pb-c.h>
#include <base64-helper.h>
#include <vpn.h>
#include "html.h"
#include <worker.h>
#include <common.h>
#include <tlslib.h>
#include <http_parser.h>
#define VERSION_MSG "<version who=\"sg\">0.1(1)</version>\n"
static const char oc_success_msg_head[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<config-auth client=\"vpn\" type=\"complete\">\n"
VERSION_MSG
"<auth id=\"success\">\n"
"<title>SSL VPN Service</title>";
#define OC_SUCCESS_MSG_FOOT "</auth></config-auth>\n"
#define OC_SUCCESS_MSG_FOOT_PROFILE \
"</auth>\n" \
"<config client=\"vpn\" type=\"private\">" \
"<vpn-profile-manifest>" \
"<vpn rev=\"1.0\">" \
"<file type=\"profile\" service-type=\"user\">" \
"<uri>/profiles/%s</uri>" \
"<hash type=\"sha1\">%s</hash>" \
"</file>" \
"</vpn>" \
"</vpn-profile-manifest>\n" \
"</config>" \
"</config-auth>"
static const char ocv3_success_msg_head[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<auth id=\"success\">\n"
"<title>SSL VPN Service</title>";
static const char ocv3_success_msg_foot[] = "</auth>\n";
#define OC_LOGIN_START \
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \
"<config-auth client=\"vpn\" type=\"auth-request\">\n" \
VERSION_MSG \
"<auth id=\"main\">\n"
#define OC_LOGIN_FORM_START \
"<message>%s</message>\n" \
"<form method=\"post\" action=\"/auth\">\n"
#define OC_LOGIN_END \
"</form></auth>\n" "</config-auth>"
#define OC_LOGIN_FORM_INPUT_USER \
"<input type=\"text\" name=\"username\" label=\"Username:\" />\n"
#define DEFAULT_PASSWD_LABEL "Password:"
#define OC_LOGIN_FORM_INPUT_PASSWORD \
"<input type=\"password\" name=\"password\" label=\""DEFAULT_PASSWD_LABEL"\" />\n"
#define OC_LOGIN_FORM_INPUT_PASSWORD_CTR \
"<input type=\"password\" name=\"secondary_password\" label=\"Password%d:\" />\n"
#define _OCV3_LOGIN_MSG_START(x) \
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \
"<auth id=\""x"\">\n"
#define OCV3_LOGIN_START _OCV3_LOGIN_MSG_START("main")
#define OCV3_PASSWD_START _OCV3_LOGIN_MSG_START("passwd")
#define OCV3_LOGIN_END "</form></auth>\n"
#ifdef SUPPORT_OIDC_AUTH
#define HTTP_AUTH_OIDC_PREFIX "Bearer"
#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
static int oidc_auth_handler(worker_st * ws, unsigned http_ver);
#endif
int ws_switch_auth_to(struct worker_st *ws, unsigned auth)
{
unsigned i;
if (ws->selected_auth && ws->selected_auth->enabled != 0 &&
ws->selected_auth->type & auth)
return 1;
for (i=0;i<WSPCONFIG(ws)->auth_methods;i++) {
if (WSPCONFIG(ws)->auth[i].enabled && (WSPCONFIG(ws)->auth[i].type & auth) != 0) {
ws->selected_auth = &WSPCONFIG(ws)->auth[i];
return 1;
}
}
return 0;
}
/* disables the selected auth method and switches to the next
* available authentication method, and returns
* non-zero on success */
int ws_switch_auth_to_next(struct worker_st *ws)
{
unsigned i;
if (!ws->selected_auth) {
return 0;
}
ws->selected_auth->enabled = 0;
for (i=0;i<WSPCONFIG(ws)->auth_methods;i++) {
if (&WSPCONFIG(ws)->auth[i] != ws->selected_auth &&
WSPCONFIG(ws)->auth[i].enabled != 0) {
ws->selected_auth = &WSPCONFIG(ws)->auth[i];
return 1;
}
}
return 0;
}
static int append_group_idx(worker_st * ws, str_st *str, unsigned i)
{
char temp[128];
const char *name;
const char *value;
value = WSCONFIG(ws)->group_list[i];
if (WSCONFIG(ws)->friendly_group_list != NULL && WSCONFIG(ws)->friendly_group_list[i] != NULL)
name = WSCONFIG(ws)->friendly_group_list[i];
else
name = WSCONFIG(ws)->group_list[i];
snprintf(temp, sizeof(temp), "<option value=\"%s\">%s</option>\n", value, name);
if (str_append_str(str, temp) < 0)
return -1;
return 0;
}
static int append_group_str(worker_st * ws, str_st *str, const char *group)
{
char temp[256];
const char *name;
const char *value;
unsigned i;
value = name = group;
if (WSCONFIG(ws)->friendly_group_list) {
for (i=0;i<WSCONFIG(ws)->group_list_size;i++) {
if (strcmp(WSCONFIG(ws)->group_list[i], group) == 0) {
if (WSCONFIG(ws)->friendly_group_list[i] != NULL)
name = WSCONFIG(ws)->friendly_group_list[i];
break;
}
}
}
snprintf(temp, sizeof(temp), "<option value=\"%s\">%s</option>\n", value, name);
if (str_append_str(str, temp) < 0)
return -1;
return 0;
}
int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg, unsigned pcounter)
{
int ret;
char context[BASE64_ENCODE_RAW_LENGTH(SID_SIZE) + 1];
unsigned int i, j;
str_st str;
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_start = OCV3_PASSWD_START;
else
login_start = OCV3_LOGIN_START;
login_end = OCV3_LOGIN_END;
} else {
login_start = OC_LOGIN_START;
login_end = OC_LOGIN_END;
}
if ((ws->selected_auth->type & AUTH_TYPE_GSSAPI) && ws->auth_state < S_AUTH_COOKIE) {
if (ws->req.authorization == NULL || ws->req.authorization_size == 0)
return basic_auth_handler(ws, http_ver, NULL);
else
return post_auth_handler(ws, http_ver);
}
str_init(&str, ws);
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;
if (ws->sid_set != 0) {
char safe_id[SAFE_ID_SIZE];
oc_base64_encode((char *)ws->sid, sizeof(ws->sid), (char *)context,
sizeof(context));
ret =
cstp_printf(ws,
"Set-Cookie: webvpncontext=%s; Max-Age=%u; Secure\r\n",
context, (unsigned)WSCONFIG(ws)->cookie_timeout);
if (ret < 0)
return -1;
oclog(ws, LOG_SENSITIVE, "sent session id: %s", calc_safe_id(ws->sid, sizeof(ws->sid), safe_id, sizeof(safe_id)));
} else {
ret =
cstp_puts(ws,
"Set-Cookie: webvpncontext=; expires=Thu, 01 Jan 1970 22:00:00 GMT; path=/; Secure\r\n");
if (ret < 0)
return -1;
}
ret = cstp_puts(ws, "Content-Type: text/xml\r\n");
if (ret < 0) {
ret = -1;
goto cleanup;
}
if (ws->auth_state == S_AUTH_REQ) {
/* Password Form */
if (pmsg == NULL || strncasecmp(pmsg, DEFAULT_PASSWD_LABEL, sizeof(DEFAULT_PASSWD_LABEL)-1) == 0)
pmsg = "Please enter your password.";
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, OC_LOGIN_FORM_INPUT_PASSWORD_CTR, pcounter);
else
ret = str_append_str(&str, OC_LOGIN_FORM_INPUT_PASSWORD);
if (ret < 0) {
ret = -1;
goto cleanup;
}
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.";
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, "<banner>%s</banner>", 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, OC_LOGIN_FORM_INPUT_USER);
if (ret < 0) {
ret = -1;
goto cleanup;
}
}
if ((ws->selected_auth->type & AUTH_TYPE_CERTIFICATE) && ws->cert_auth_ok != 0) {
ret = get_cert_info(ws);
if (ret < 0) {
ret = -1;
oclog(ws, LOG_WARNING, "cannot obtain certificate information");
goto cleanup;
}
}
/* send groups */
if (WSCONFIG(ws)->group_list_size > 0 || ws->cert_groups_size > 0) {
ret = str_append_str(&str, "<select name=\"group_list\" label=\"Group:\">\n");
if (ret < 0) {
ret = -1;
goto cleanup;
}
/* Several anyconnect clients (and openconnect) submit the group name
* separately in that form. In that case they expect that we re-order
* the list and we place the group they selected first. WTF! No respect
* to server time.
*/
if (ws->groupname[0] != 0) {
ret = append_group_str(ws, &str, ws->groupname);
if (ret < 0) {
ret = -1;
goto cleanup;
}
}
if (WSCONFIG(ws)->default_select_group) {
ret = str_append_printf(&str, "<option>%s</option>\n", WSCONFIG(ws)->default_select_group);
if (ret < 0) {
ret = -1;
goto cleanup;
}
}
/* append any groups available in the certificate */
if ((ws->selected_auth->type & AUTH_TYPE_CERTIFICATE) && ws->cert_auth_ok != 0) {
unsigned dup;
for (i=0;i<ws->cert_groups_size;i++) {
dup = 0;
for (j=0;j<WSCONFIG(ws)->group_list_size;j++) {
if (strcmp(ws->cert_groups[i], WSCONFIG(ws)->group_list[j]) == 0) {
dup = 1;
break;
}
}
if (dup == 0 && ws->groupname[0] != 0 && strcmp(ws->groupname, ws->cert_groups[i]) == 0)
dup = 1;
if (dup != 0)
continue;
ret = str_append_printf(&str, "<option>%s</option>\n", ws->cert_groups[i]);
if (ret < 0) {
ret = -1;
goto cleanup;
}
}
}
for (i=0;i<WSCONFIG(ws)->group_list_size;i++) {
if (ws->groupname[0] != 0 && strcmp(ws->groupname, WSCONFIG(ws)->group_list[i]) == 0)
continue;
ret = append_group_idx(ws, &str, i);
if (ret < 0) {
ret = -1;
goto cleanup;
}
}
ret = str_append_str(&str, "</select>\n");
if (ret < 0) {
ret = -1;
goto cleanup;
}
}
ret = str_append_str(&str, login_end);
if (ret < 0) {
ret = -1;
goto cleanup;
}
}
ret =
cstp_printf(ws, "Content-Length: %u\r\n",
(unsigned int)str.length);
if (ret < 0) {
ret = -1;
goto cleanup;
}
ret = cstp_puts(ws, "X-Transcend-Version: 1\r\n");
if (ret < 0) {
ret = -1;
goto cleanup;
}
ret = cstp_puts(ws, "\r\n");
if (ret < 0) {
ret = -1;
goto cleanup;
}
ret = cstp_send(ws, str.data, str.length);
if (ret < 0) {
ret = -1;
goto cleanup;
}
ret = cstp_uncork(ws);
if (ret < 0) {
ret = -1;
goto cleanup;
}
ret = 0;
cleanup:
str_clear(&str);
return ret;
}
int get_auth_handler(worker_st * ws, unsigned http_ver)
{
return get_auth_handler2(ws, http_ver, NULL, 0);
}
int get_cert_names(worker_st * ws, const gnutls_datum_t * raw)
{
gnutls_x509_crt_t crt;
int ret;
unsigned i;
size_t size;
char cert_username[MAX_USERNAME_SIZE];
if (ws->cert_username[0] != 0 || ws->cert_groups_size > 0)
return 0; /* already read, nothing to do */
ret = gnutls_x509_crt_init(&crt);
if (ret < 0) {
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));
goto fail;
}
if (strcmp(WSCONFIG(ws)->cert_user_oid, "SAN(rfc822name)") == 0) { /* check for RFC822Name */
for (i = 0;; i++) {
size = sizeof(ws->cert_username);
ret =
gnutls_x509_crt_get_subject_alt_name(crt, i,
cert_username,
&size, NULL);
if (ret < 0) {
if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
ret = 1;
break;
}
if (ret == GNUTLS_SAN_RFC822NAME) {
strlcpy(ws->cert_username, cert_username, sizeof(ws->cert_username));
oclog(ws, LOG_INFO,
"RFC822NAME (%s) retrieved",
cert_username);
break;
}
}
} else if (WSCONFIG(ws)->cert_user_oid) { /* otherwise we check at the DN */
size = sizeof(ws->cert_username);
ret =
gnutls_x509_crt_get_dn_by_oid(crt,
WSCONFIG(ws)->cert_user_oid, 0,
0, cert_username, &size);
if (ret >= 0)
strlcpy(ws->cert_username, cert_username, sizeof(ws->cert_username));
} else {
size = sizeof(ws->cert_username);
ret = gnutls_x509_crt_get_dn(crt, cert_username, &size);
if (ret >= 0)
strlcpy(ws->cert_username, cert_username, sizeof(ws->cert_username));
}
if (ret < 0) {
if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER)
oclog(ws, LOG_ERR, "certificate's username exceed the maximum buffer size (%u)",
(unsigned)sizeof(ws->cert_username));
else if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
oclog(ws, LOG_ERR, "the certificate's DN does not contain OID %s; cannot determine username",
WSCONFIG(ws)->cert_user_oid);
} else {
oclog(ws, LOG_ERR, "cannot obtain user name from certificate DN(%s): %s",
WSCONFIG(ws)->cert_user_oid, gnutls_strerror(ret));
}
goto fail;
}
if (WSCONFIG(ws)->cert_group_oid) {
i = 0;
do {
ws->cert_groups = talloc_realloc(ws, ws->cert_groups, char*, i+1);
if (ws->cert_groups == NULL) {
oclog(ws, LOG_ERR, "cannot allocate memory for cert groups");
ret = -1;
goto fail;
}
size = 0;
ret =
gnutls_x509_crt_get_dn_by_oid(crt,
WSCONFIG(ws)->cert_group_oid, i,
0, NULL, &size);
if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
break;
if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) {
if (ret == 0)
ret = GNUTLS_E_INTERNAL_ERROR;
oclog(ws, LOG_ERR,
"cannot obtain group from certificate DN(%s): %s",
WSCONFIG(ws)->cert_group_oid, gnutls_strerror(ret));
goto fail;
}
ws->cert_groups[i] = talloc_size(ws->cert_groups, size);
if (ws->cert_groups[i] == NULL) {
oclog(ws, LOG_ERR, "cannot allocate memory for cert group");
ret = -1;
goto fail;
}
ret =
gnutls_x509_crt_get_dn_by_oid(crt,
WSCONFIG(ws)->cert_group_oid, i,
0, ws->cert_groups[i], &size);
if (ret < 0) {
oclog(ws, LOG_ERR,
"cannot obtain group from certificate DN: %s",
gnutls_strerror(ret));
goto fail;
}
i++;
} while (ret >= 0);
ws->cert_groups_size = i;
}
ret = 0;
fail:
gnutls_x509_crt_deinit(crt);
return ret;
}
static
unsigned check_if_default_route(char **routes, unsigned routes_size)
{
unsigned i;
for (i=0;i<routes_size;i++) {
if (strcmp(routes[i], "default") == 0 ||
strcmp(routes[i], "0.0.0.0/0") == 0)
return 1;
}
return 0;
}
/* auth reply from main process */
static int recv_cookie_auth_reply(worker_st * ws)
{
int ret;
int socketfd = -1;
AuthCookieReplyMsg *msg = NULL;
PROTOBUF_ALLOCATOR(pa, ws);
ret = recv_socket_msg(ws, ws->cmd_fd, AUTH_COOKIE_REP, &socketfd,
(void *)&msg,
(unpack_func) auth_cookie_reply_msg__unpack,
WSCONFIG(ws)->auth_timeout);
if (ret < 0) {
oclog(ws, LOG_ERR, "error receiving auth reply message");
return ret;
}
oclog(ws, LOG_DEBUG, "received auth reply message (value: %u)",
(unsigned)msg->reply);
switch (msg->reply) {
case AUTH__REP__OK:
if (socketfd != -1) {
ws->tun_fd = socketfd;
if (tun_claim(ws->tun_fd) != 0) {
ret = ERR_AUTH_FAIL;
goto cleanup;
}
if (msg->vname == NULL || msg->config == NULL || msg->user_name == NULL || msg->sid.len != sizeof(ws->sid)) {
ret = ERR_AUTH_FAIL;
goto cleanup;
}
/* update our sid */
memcpy(ws->sid, msg->sid.data, sizeof(ws->sid));
ws->sid_set = 1;
strlcpy(ws->vinfo.name, msg->vname, sizeof(ws->vinfo.name));
strlcpy(ws->username, msg->user_name, sizeof(ws->username));
if (msg->group_name != NULL) {
strlcpy(ws->groupname, msg->group_name, sizeof(ws->groupname));
} else {
ws->groupname[0] = 0;
}
memcpy(ws->session_id, msg->session_id.data,
msg->session_id.len);
ws->user_config = msg->config;
if (msg->ipv4 != NULL) {
talloc_free(ws->vinfo.ipv4);
if (strcmp(msg->ipv4, "0.0.0.0") == 0)
ws->vinfo.ipv4 = NULL;
else
ws->vinfo.ipv4 =
talloc_strdup(ws, msg->ipv4);
}
if (msg->ipv6 != NULL) {
talloc_free(ws->vinfo.ipv6);
if (strcmp(msg->ipv6, "::") == 0)
ws->vinfo.ipv6 = NULL;
else
ws->vinfo.ipv6 =
talloc_strdup(ws, msg->ipv6);
}
if (msg->ipv4_local != NULL) {
talloc_free(ws->vinfo.ipv4_local);
if (strcmp(msg->ipv4_local, "0.0.0.0") == 0)
ws->vinfo.ipv4_local = NULL;
else
ws->vinfo.ipv4_local =
talloc_strdup(ws, msg->ipv4_local);
}
if (msg->ipv6_local != NULL) {
talloc_free(ws->vinfo.ipv6_local);
if (strcmp(msg->ipv6_local, "::") == 0)
ws->vinfo.ipv6_local = NULL;
else
ws->vinfo.ipv6_local =
talloc_strdup(ws, msg->ipv6_local);
}
if (msg->config->no_udp != 0)
WSPCONFIG(ws)->udp_port = 0;
/* routes */
if (check_if_default_route(msg->config->routes, msg->config->n_routes))
ws->default_route = 1;
} else {
oclog(ws, LOG_ERR, "error in received message");
ret = ERR_AUTH_FAIL;
goto cleanup;
}
break;
case AUTH__REP__FAILED:
default:
if (msg->reply != AUTH__REP__FAILED)
oclog(ws, LOG_ERR, "unexpected auth reply %u",
(unsigned)msg->reply);
ret = ERR_AUTH_FAIL;
goto cleanup;
}
ret = 0;
cleanup:
if (ret < 0) {
/* we only release on error, as the user configuration
* remains. */
auth_cookie_reply_msg__free_unpacked(msg, &pa);
ws->user_config = NULL;
}
return ret;
}
/* returns the fd */
int connect_to_secmod(worker_st * ws)
{
int sd, ret, e;
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sd == -1) {
e = errno;
oclog(ws, LOG_ERR, "error opening unix socket (for sec-mod) %s",
strerror(e));
return -1;
}
ret =
connect(sd, (struct sockaddr *)&ws->secmod_addr,
ws->secmod_addr_len);
if (ret < 0) {
e = errno;
close(sd);
oclog(ws, LOG_ERR,
"error connecting to sec-mod socket '%s': %s",
ws->secmod_addr.sun_path, strerror(e));
return -1;
}
return sd;
}
static int recv_auth_reply(worker_st * ws, int sd, char **txt, unsigned *pcounter)
{
int ret;
SecAuthReplyMsg *msg = NULL;
PROTOBUF_ALLOCATOR(pa, ws);
/* We don't use the default socket timeout here, but rather the
* longer WSCONFIG(ws)->auth_timeout to allow for authentication
* methods which require the user input prior to returning a reply */
ret = recv_msg(ws, sd, CMD_SEC_AUTH_REPLY,
(void *)&msg, (unpack_func) sec_auth_reply_msg__unpack,
WSCONFIG(ws)->auth_timeout);
if (ret < 0) {
oclog(ws, LOG_ERR, "error receiving auth reply message");
return ret;
}
oclog(ws, LOG_DEBUG, "received auth reply message (value: %u)",
(unsigned)msg->reply);
if (txt) *txt = NULL;
switch (msg->reply) {
case AUTH__REP__MSG:
if (msg->msg)
*txt = talloc_strdup(ws, msg->msg);
else
*txt = NULL;
if (msg->has_passwd_counter)
*pcounter = msg->passwd_counter;
else
*pcounter = 0;
if (msg->has_sid && msg->sid.len == sizeof(ws->sid)) {
/* update our sid */
memcpy(ws->sid, msg->sid.data, sizeof(ws->sid));
ws->sid_set = 1;
}
ret = ERR_AUTH_CONTINUE;
goto cleanup;
case AUTH__REP__OK:
if (msg->user_name == NULL) {
ret = ERR_AUTH_FAIL;
goto cleanup;
}
strlcpy(ws->username, msg->user_name, sizeof(ws->username));
if (msg->has_sid && msg->sid.len == sizeof(ws->sid)) {
/* update our sid */
memcpy(ws->sid, msg->sid.data, sizeof(ws->sid));
ws->sid_set = 1;
}
if (msg->has_sid == 0 ||
msg->sid.len != sizeof(ws->cookie) ||
msg->dtls_session_id.len != sizeof(ws->session_id)) {
ret = ERR_AUTH_FAIL;
goto cleanup;
}
memcpy(ws->cookie, msg->sid.data, msg->sid.len);
ws->cookie_set = 1;
memcpy(ws->session_id, msg->dtls_session_id.data,
msg->dtls_session_id.len);
if (txt)
*txt = talloc_strdup(ws, msg->msg);
break;
case AUTH__REP__FAILED:
default:
if (msg->reply != AUTH__REP__FAILED)
oclog(ws, LOG_ERR, "unexpected auth reply %u",
(unsigned)msg->reply);
ret = ERR_AUTH_FAIL;
goto cleanup;
}
ret = 0;
cleanup:
sec_auth_reply_msg__free_unpacked(msg, &pa);
return ret;
}
/* grabs the username from the session certificate */
static
int get_cert_info(worker_st * ws)
{
const gnutls_datum_t *cert;
unsigned int ncerts;
int ret;
if (ws->session == NULL) {
/* if info has been passed using proxy protocol */
if (ws->cert_username[0] != 0)
return 0;
else
return -1;
}
/* this is superflous. Verification has already been performed
* during handshake. */
cert = gnutls_certificate_get_peers(ws->session, &ncerts);
if (cert == NULL) {
return -1;
}
ret = get_cert_names(ws, cert);
if (ret < 0) {
if (WSCONFIG(ws)->cert_user_oid == NULL) {
oclog(ws, LOG_ERR, "cannot read username from certificate; cert-user-oid is not set");
} else {
oclog(ws, LOG_ERR, "cannot read username from certificate");
}
return -1;
}
return 0;
}
/* This makes sure that the provided cookie is valid,
* and fills in the ws->user_config.
*/
void cookie_authenticate_or_exit(worker_st *ws)
{
int ret;
if (ws->auth_state == S_AUTH_COMPLETE)
return;
/* we must be in S_AUTH_COOKIE state */
if (ws->auth_state != S_AUTH_COOKIE || ws->cookie_set == 0) {
oclog(ws, LOG_WARNING, "no cookie found");
cstp_puts(ws,
"HTTP/1.1 503 Service Unavailable\r\n\r\n");
cstp_fatal_close(ws, GNUTLS_A_ACCESS_DENIED);
exit_worker(ws);
}
/* we have authenticated against sec-mod, we need to complete
* our authentication by forwarding our cookie to main. */
ret = auth_cookie(ws, ws->cookie, sizeof(ws->cookie));
if (ret < 0) {
oclog(ws, LOG_WARNING, "failed cookie authentication attempt");
if (ret == ERR_AUTH_FAIL) {
cstp_puts(ws,
"HTTP/1.1 401 Cookie is not acceptable\r\n\r\n");
} else {
cstp_puts(ws,
"HTTP/1.1 503 Service Unavailable\r\n\r\n");
}
cstp_fatal_close(ws, GNUTLS_A_ACCESS_DENIED);
exit_worker(ws);
}
ws->auth_state = S_AUTH_COMPLETE;
}
/* sends a cookie authentication request to main thread and waits for
* a reply.
* Returns 0 on success.
*/
int auth_cookie(worker_st * ws, void *cookie, size_t cookie_size)
{
int ret;
AuthCookieRequestMsg msg = AUTH_COOKIE_REQUEST_MSG__INIT;
if ((ws->selected_auth->type & AUTH_TYPE_CERTIFICATE)
&& WSCONFIG(ws)->cisco_client_compat == 0) {
if (ws->cert_auth_ok == 0) {
oclog(ws, LOG_INFO,
"no certificate provided for cookie authentication");
return -1;
} else {
ret = get_cert_info(ws);
if (ret < 0) {
oclog(ws, LOG_INFO, "cannot obtain certificate info");
return -1;
}
}
}
msg.cookie.data = cookie;
msg.cookie.len = cookie_size;
if (ws->req.hostname[0] != 0)
msg.hostname = ws->req.hostname;
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");
return ret;
}
ret = recv_cookie_auth_reply(ws);
if (ret < 0) {
oclog(ws, LOG_DEBUG,
"error receiving cookie authentication reply");
return ret;
}
return 0;
}
int post_common_handler(worker_st * ws, unsigned http_ver, const char *imsg)
{
int ret, size;
char str_cookie[BASE64_ENCODE_RAW_LENGTH(sizeof(ws->cookie))+1];
size_t str_cookie_size = sizeof(str_cookie);
char msg[MAX_BANNER_SIZE + 32];
const char *success_msg_head;
char *success_msg_foot;
unsigned success_msg_head_size;
unsigned success_msg_foot_size;
if (ws->req.user_agent_type == AGENT_OPENCONNECT_V3) {
success_msg_head = ocv3_success_msg_head;
success_msg_foot = talloc_strdup(ws, ocv3_success_msg_foot);
success_msg_head_size = sizeof(ocv3_success_msg_head)-1;
success_msg_foot_size = strlen(success_msg_foot);
} else {
success_msg_head = oc_success_msg_head;
success_msg_foot = NULL;
#ifdef ANYCONNECT_CLIENT_COMPAT
if (WSCONFIG(ws)->xml_config_file) {
success_msg_foot = talloc_asprintf(ws, OC_SUCCESS_MSG_FOOT_PROFILE,
WSCONFIG(ws)->xml_config_file, WSCONFIG(ws)->xml_config_hash);
}
#endif
if (success_msg_foot == NULL) {
success_msg_foot = talloc_strdup(ws, OC_SUCCESS_MSG_FOOT);
}
if (success_msg_foot == NULL)
return -1;
success_msg_head_size = sizeof(oc_success_msg_head)-1;
success_msg_foot_size = strlen(success_msg_foot);
}
oc_base64_encode((char *)ws->cookie, sizeof(ws->cookie),
(char *)str_cookie, str_cookie_size);
/* 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)
goto fail;
ret = cstp_puts(ws, "Connection: Keep-Alive\r\n");
if (ret < 0)
goto fail;
if ((ws->selected_auth->type & AUTH_TYPE_GSSAPI) && imsg != NULL && imsg[0] != 0) {
ret = cstp_printf(ws, "WWW-Authenticate: Negotiate %s\r\n", imsg);
if (ret < 0)
goto fail;
}
ret = cstp_puts(ws, "Content-Type: text/xml\r\n");
if (ret < 0)
goto fail;
if (WSCONFIG(ws)->banner) {
size =
snprintf(msg, sizeof(msg), "<banner>%s</banner>",
WSCONFIG(ws)->banner);
if (size <= 0)
goto fail;
/* snprintf() returns not a very useful value, so we need to recalculate */
size = strlen(msg);
} else {
msg[0] = 0;
size = 0;
}
size += success_msg_head_size + success_msg_foot_size;
ret = cstp_printf(ws, "Content-Length: %u\r\n", (unsigned)size);
if (ret < 0)
goto fail;
ret = cstp_puts(ws, "X-Transcend-Version: 1\r\n");
if (ret < 0)
goto fail;
if (ws->sid_set != 0) {
char context[BASE64_ENCODE_RAW_LENGTH(SID_SIZE) + 1];
char safe_id[SAFE_ID_SIZE];
oc_base64_encode((char *)ws->sid, sizeof(ws->sid), (char *)context,
sizeof(context));
ret =
cstp_printf(ws,
"Set-Cookie: webvpncontext=%s; Secure\r\n",
context);
if (ret < 0)
goto fail;
oclog(ws, LOG_SENSITIVE, "sent session id: %s", calc_safe_id(ws->sid, sizeof(ws->sid), safe_id, sizeof(safe_id)));
}
ret =
cstp_printf(ws,
"Set-Cookie: webvpn=%s; Secure\r\n",
str_cookie);
if (ret < 0)
goto fail;
ret =
cstp_puts(ws,
"Set-Cookie: webvpnc=; expires=Thu, 01 Jan 1970 22:00:00 GMT; path=/; Secure\r\n");
if (ret < 0)
goto fail;
#ifdef ANYCONNECT_CLIENT_COMPAT
if (WSCONFIG(ws)->xml_config_file) {
ret =
cstp_printf(ws,
"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",
WSPCONFIG(ws)->cert_hash,
WSCONFIG(ws)->xml_config_file,
WSCONFIG(ws)->xml_config_hash);
} else {
ret =
cstp_printf(ws,
"Set-Cookie: webvpnc=bu:/&p:t&iu:1/&sh:%s; path=/; Secure\r\n",
WSPCONFIG(ws)->cert_hash);
}
#endif
if (ret < 0)
goto fail;
ret =
cstp_printf(ws,
"\r\n%s%s%s", success_msg_head, msg, success_msg_foot);
if (ret < 0)
goto fail;
ret = cstp_uncork(ws);
if (ret < 0)
goto fail;
return 0;
fail:
talloc_free(success_msg_foot);
return -1;
}
/* Returns the contents of the password field in a newly allocated
* string, or a negative value on error.
*
* @body: is the string to search the xml field at, should be null-terminated.
* @value: the value that was found
*/
static
int match_password_in_reply(worker_st * ws, char *body, unsigned body_length,
char **value)
{
char *p;
unsigned len, xml = 0;
if (body == NULL || body_length == 0)
return -1;
if (memmem(body, body_length, "<?xml", 5) != 0) {
xml = 1;
/* body should contain <password?>test</password?> or <xxx_password>test</xxx_password> */
*value =
strcasestr(body, "<password");
if (*value == NULL)
*value =
strcasestr(body, "_password>");
if (*value == NULL) {
oclog(ws, LOG_HTTP_DEBUG,
"cannot find password in client XML message");
return -1;
}
/* find terminator */
p = strchr(*value, '>');
if (p == NULL) {
oclog(ws, LOG_HTTP_DEBUG,
"unterminated password in client XML message");
return -1;
}
p++;
*value = p;
len = 0;
while (*p != 0) {
if (*p == '<' && *(p+1) == '/') {
break;
}
p++;
len++;
}
} else { /* non-xml version */
/* body should be "username=test&password?=test" */
*value =
strcasestr(body, "password");
if (*value == NULL) {
oclog(ws, LOG_HTTP_DEBUG,
"cannot find password in client message");
return -1;
}
p = strchr(*value, '=');
if (p == NULL) {
oclog(ws, LOG_HTTP_DEBUG,
"unterminated password in client message");
return -1;
}
p++;
*value = p;
len = 0;
while (*p != 0) {
if (*p == '&') {
break;
}
p++;
len++;
}
}
if (len == 0) {
*value = talloc_strdup(ws->req.body, "");
if (*value != NULL)
return 0;
return -1;
}
if (xml)
*value = unescape_html(ws->req.body, *value, len, NULL);
else
*value = unescape_url(ws->req.body, *value, len, NULL);
if (*value == NULL) {
oclog(ws, LOG_ERR,
"password requested but no such field in client message");
return -1;
}
return 0;
}
/* Returns the contents of the provided fields in a newly allocated
* string, or a negative value on error.
*
* @body: is the string to search the xml field at, should be null-terminated.
* @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,
char **value)
{
char *p;
char temp1[64];
char temp2[64];
unsigned temp2_len, temp1_len;
unsigned len, xml = 0;
if (body == NULL || body_length == 0)
return -1;
if (memmem(body, body_length, "<?xml", 5) != 0) {
xml = 1;
if (xml_field) {
field = xml_field;
/*field_size = xml_field_size;*/
}
snprintf(temp1, sizeof(temp1), "<%s>", field);
snprintf(temp2, sizeof(temp2), "</%s>", field);
temp1_len = strlen(temp1);
temp2_len = strlen(temp2);
/* body should contain <field>test</field> */
*value =
strcasestr(body, temp1);
if (*value == NULL) {
oclog(ws, LOG_HTTP_DEBUG,
"cannot find '%s' in client XML message", field);
return -1;
}
*value += temp1_len;
p = *value;
len = 0;
while (*p != 0) {
if (*p == '<'
&& (strncasecmp(p, temp2, temp2_len) == 0)) {
break;
}
p++;
len++;
}
} else { /* non-xml version */
snprintf(temp1, sizeof(temp1), "%s=", field);
temp1_len = strlen(temp1);
/* body should be "username=test&password=test" */
*value =
strcasestr(body, temp1);
if (*value == NULL) {
oclog(ws, LOG_HTTP_DEBUG,
"cannot find '%s' in client message", field);
return -1;
}
*value += temp1_len;
p = *value;
len = 0;
while (*p != 0) {
if (*p == '&') {
break;
}
p++;
len++;
}
}
if (len == 0) {
*value = talloc_strdup(ws->req.body, "");
if (*value != NULL)
return 0;
return -1;
}
if (xml)
*value = unescape_html(ws->req.body, *value, len, NULL);
else
*value = unescape_url(ws->req.body, *value, len, NULL);
if (*value == NULL) {
oclog(ws, LOG_ERR,
"%s requested but no such field in client message", field);
return -1;
}
return 0;
}
#define SPNEGO_MSG "<html><body>Please authenticate using GSSAPI</body></html>"
static
int basic_auth_handler(worker_st * ws, unsigned http_ver, const char *msg)
{
int ret;
oclog(ws, LOG_HTTP_DEBUG, "HTTP sending: 401 Unauthorized");
cstp_cork(ws);
ret = cstp_printf(ws, "HTTP/1.%u 401 Unauthorized\r\n", http_ver);
if (ret < 0)
return -1;
if (WSPCONFIG(ws)->auth_methods > 1) {
ret = cstp_puts(ws, "X-HTTP-Auth-Support: fallback\r\n");
if (ret < 0)
return -1;
}
if (msg == NULL) {
oclog(ws, LOG_HTTP_DEBUG, "HTTP sending: WWW-Authenticate: Negotiate");
ret = cstp_puts(ws, "WWW-Authenticate: Negotiate\r\n");
} else {
oclog(ws, LOG_HTTP_DEBUG, "HTTP sending: WWW-Authenticate: Negotiate %s", msg);
ret = cstp_printf(ws, "WWW-Authenticate: Negotiate %s\r\n", msg);
}
if (ret < 0)
return -1;
ret = cstp_puts(ws, "Content-Length: 0\r\n");
if (ret < 0) {
ret = -1;
goto cleanup;
}
ret = cstp_puts(ws, "\r\n");
if (ret < 0) {
ret = -1;
goto cleanup;
}
ret = cstp_uncork(ws);
if (ret < 0) {
ret = -1;
goto cleanup;
}
ret = 0;
cleanup:
return ret;
}
#ifdef SUPPORT_OIDC_AUTH
static
int oidc_auth_handler(worker_st * ws, unsigned http_ver)
{
int ret;
oclog(ws, LOG_HTTP_DEBUG, "HTTP sending: 401 Unauthorized");
cstp_cork(ws);
ret = cstp_printf(ws, "HTTP/1.%u 401 Unauthorized\r\n", http_ver);
if (ret < 0)
return -1;
oclog(ws, LOG_HTTP_DEBUG, "HTTP sending: WWW-Authenticate: %s", HTTP_AUTH_OIDC_PREFIX);
ret = cstp_printf(ws, "WWW-Authenticate: %s\r\n", HTTP_AUTH_OIDC_PREFIX);
if (ret < 0)
return -1;
ret = cstp_puts(ws, "Content-Length: 0\r\n");
if (ret < 0) {
ret = -1;
goto cleanup;
}
ret = cstp_puts(ws, "\r\n");
if (ret < 0) {
ret = -1;
goto cleanup;
}
ret = cstp_uncork(ws);
if (ret < 0) {
ret = -1;
goto cleanup;
}
ret = 0;
cleanup:
return ret;
}
#endif
#define USERNAME_FIELD "username"
#define GROUPNAME_FIELD "group%5flist"
#define GROUPNAME_FIELD2 "group_list"
#define GROUPNAME_FIELD_XML "group-select"
#define MSG_INTERNAL_ERROR "Internal error"
#define MSG_CERT_READ_ERROR "Could not read certificate"
#define MSG_NO_CERT_ERROR "No certificate"
#define MSG_NO_PASSWORD_ERROR "No password"
int post_auth_handler(worker_st * ws, unsigned http_ver)
{
int ret = -1, sd = -1;
struct http_req_st *req = &ws->req;
const char *reason = "Authentication failed";
char *username = NULL;
char *password = NULL;
char *groupname = NULL;
char *msg = NULL;
unsigned def_group = 0;
unsigned pcounter = 0;
if (req->body_length > 0) {
oclog(ws, LOG_HTTP_DEBUG, "POST body: '%.*s'", (int)req->body_length,
req->body);
}
if (ws->sid_set && ws->auth_state == S_AUTH_INACTIVE)
ws->auth_state = S_AUTH_INIT;
if (ws->auth_state == S_AUTH_INACTIVE) {
SecAuthInitMsg ireq = SEC_AUTH_INIT_MSG__INIT;
ret = parse_reply(ws, req->body, req->body_length,
GROUPNAME_FIELD, sizeof(GROUPNAME_FIELD)-1,
GROUPNAME_FIELD_XML, sizeof(GROUPNAME_FIELD_XML)-1,
&groupname);
if (ret < 0) {
ret = parse_reply(ws, req->body, req->body_length,
GROUPNAME_FIELD2, sizeof(GROUPNAME_FIELD2)-1,
GROUPNAME_FIELD_XML, sizeof(GROUPNAME_FIELD_XML)-1,
&groupname);
}
if (ret < 0) {
oclog(ws, LOG_HTTP_DEBUG, "failed reading groupname");
} else {
if (WSCONFIG(ws)->default_select_group != NULL &&
strcmp(groupname, WSCONFIG(ws)->default_select_group) == 0) {
def_group = 1;
} else {
/* Some anyconnect clients send the group friendly name instead of
* the actual value; see #267 */
ws->groupname[0] = 0;
if (WSCONFIG(ws)->friendly_group_list != NULL) {
unsigned found = 0, i;
for (i=0;i<WSCONFIG(ws)->group_list_size;i++) {
if (strcmp(WSCONFIG(ws)->group_list[i], groupname) == 0) {
found = 1;
break;
}
}
if (!found)
for (i=0;i<WSCONFIG(ws)->group_list_size;i++) {
if (WSCONFIG(ws)->friendly_group_list[i] != NULL && strcmp(WSCONFIG(ws)->friendly_group_list[i], groupname) == 0) {
strlcpy(ws->groupname, WSCONFIG(ws)->group_list[i], sizeof(ws->groupname));
break;
}
}
}
if (ws->groupname[0] == 0)
strlcpy(ws->groupname, groupname, sizeof(ws->groupname));
ireq.group_name = ws->groupname;
}
}
talloc_free(groupname);
#ifdef SUPPORT_OIDC_AUTH
if (ws->selected_auth->type & AUTH_TYPE_OIDC) {
if (req->authorization == NULL || req->authorization_size == 0)
return oidc_auth_handler(ws, http_ver);
if ((req->authorization_size > (sizeof(HTTP_AUTH_OIDC_PREFIX) - 1)) && strncasecmp(req->authorization, HTTP_AUTH_OIDC_PREFIX, sizeof(HTTP_AUTH_OIDC_PREFIX) - 1) == 0) {
ireq.auth_type |= AUTH_TYPE_OIDC;
ireq.user_name = req->authorization + sizeof(HTTP_AUTH_OIDC_PREFIX);
} else {
oclog(ws, LOG_HTTP_DEBUG, "Invalid authorization data: %.*s", req->authorization_size, req->authorization);
goto auth_fail;
}
}
#endif
if (ws->selected_auth->type & AUTH_TYPE_GSSAPI) {
if (req->authorization == NULL || req->authorization_size == 0)
return basic_auth_handler(ws, http_ver, NULL);
if (req->authorization_size > 10) {
ireq.user_name = req->authorization + 10;
ireq.auth_type |= AUTH_TYPE_GSSAPI;
} else {
oclog(ws, LOG_HTTP_DEBUG, "Invalid authorization data: %.*s", req->authorization_size, req->authorization);
goto auth_fail;
}
}
if (ws->selected_auth->type & AUTH_TYPE_USERNAME_PASS) {
ret = parse_reply(ws, req->body, req->body_length,
USERNAME_FIELD, sizeof(USERNAME_FIELD)-1,
NULL, 0,
&username);
if (ret < 0) {
oclog(ws, LOG_HTTP_DEBUG, "failed reading username");
goto ask_auth;
}
strlcpy(ws->username, username, sizeof(ws->username));
talloc_free(username);
ireq.user_name = ws->username;
ireq.auth_type |= AUTH_TYPE_USERNAME_PASS;
}
if (ws->selected_auth->type & AUTH_TYPE_CERTIFICATE) {
if (ws->cert_auth_ok == 0) {
reason = MSG_NO_CERT_ERROR;
oclog(ws, LOG_INFO,
"no certificate provided for authentication");
if (ws_switch_auth_to_next(ws) == 0)
goto auth_fail;
ws->auth_state = S_AUTH_INACTIVE;
ws->sid_set = 0;
goto ask_auth;
} else {
ret = get_cert_info(ws);
if (ret < 0) {
reason = MSG_CERT_READ_ERROR;
oclog(ws, LOG_ERR,
"failed reading certificate info");
goto auth_fail;
}
}
if (def_group == 0 && ws->cert_groups_size > 0 && ws->groupname[0] == 0) {
oclog(ws, LOG_HTTP_DEBUG, "user has not selected a group");
return get_auth_handler2(ws, http_ver, "Please select your group.", 0);
}
ireq.tls_auth_ok = ws->cert_auth_ok;
ireq.cert_user_name = ws->cert_username;
ireq.cert_group_names = ws->cert_groups;
ireq.n_cert_group_names = ws->cert_groups_size;
ireq.auth_type |= AUTH_TYPE_CERTIFICATE;
}
ireq.vhost = ws->vhost->name;
ireq.ip = ws->remote_ip_str;
ireq.our_ip = ws->our_ip_str;
ireq.session_start_time = ws->session_start_time;
ireq.hmac.data = (uint8_t*)ws->sec_auth_init_hmac;
ireq.hmac.len = sizeof(ws->sec_auth_init_hmac);
if (req->user_agent[0] != 0)
ireq.user_agent = req->user_agent;
if (req->devtype[0] != 0)
ireq.device_type = req->devtype;
if (req->devplatform[0] != 0)
ireq.device_platform = req->devplatform;
sd = connect_to_secmod(ws);
if (sd == -1) {
reason = MSG_INTERNAL_ERROR;
oclog(ws, LOG_ERR, "failed connecting to sec mod");
goto auth_fail;
}
ret = send_msg_to_secmod(ws, sd, CMD_SEC_AUTH_INIT,
&ireq, (pack_size_func)
sec_auth_init_msg__get_packed_size,
(pack_func) sec_auth_init_msg__pack);
if (ret < 0) {
reason = MSG_INTERNAL_ERROR;
oclog(ws, LOG_ERR,
"failed sending auth init message to sec mod");
goto auth_fail;
}
ws->auth_state = S_AUTH_INIT;
} else if (ws->auth_state == S_AUTH_INIT
|| ws->auth_state == S_AUTH_REQ) {
SecAuthContMsg areq = SEC_AUTH_CONT_MSG__INIT;
areq.ip = ws->remote_ip_str;
if (ws->selected_auth->type & AUTH_TYPE_GSSAPI) {
if (req->authorization == NULL || req->authorization_size <= 10) {
if (req->authorization != NULL)
oclog(ws, LOG_HTTP_DEBUG, "Invalid authorization data: %.*s", req->authorization_size, req->authorization);
else
oclog(ws, LOG_HTTP_DEBUG, "No authorization data");
goto auth_fail;
}
areq.password = req->authorization + 10;
}
if (areq.password == NULL && (ws->selected_auth->type & AUTH_TYPE_USERNAME_PASS)) {
ret = match_password_in_reply(ws, req->body, req->body_length,
&password);
if (ret < 0) {
reason = MSG_NO_PASSWORD_ERROR;
oclog(ws, LOG_ERR, "failed reading password");
goto auth_fail;
}
areq.password = password;
}
if (areq.password != NULL) {
if (ws->sid_set != 0) {
areq.sid.data = ws->sid;
areq.sid.len = sizeof(ws->sid);
}
sd = connect_to_secmod(ws);
if (sd == -1) {
reason = MSG_INTERNAL_ERROR;
oclog(ws, LOG_ERR,
"failed connecting to sec mod");
goto auth_fail;
}
ret =
send_msg_to_secmod(ws, sd, CMD_SEC_AUTH_CONT, &areq,
(pack_size_func)
sec_auth_cont_msg__get_packed_size,
(pack_func)
sec_auth_cont_msg__pack);
talloc_free(password);
if (ret < 0) {
reason = MSG_INTERNAL_ERROR;
oclog(ws, LOG_ERR,
"failed sending auth req message to main");
goto auth_fail;
}
ws->auth_state = S_AUTH_REQ;
} else {
oclog(ws, LOG_ERR, "No password provided");
goto auth_fail;
}
} else {
oclog(ws, LOG_ERR, "unexpected POST request in auth state %u",
(unsigned)ws->auth_state);
goto auth_fail;
}
ret = recv_auth_reply(ws, sd, &msg, &pcounter);
if (sd != -1) {
close(sd);
sd = -1;
}
if (ret == ERR_AUTH_CONTINUE) {
oclog(ws, LOG_DEBUG, "continuing authentication for '%s'",
ws->username);
ws->auth_state = S_AUTH_REQ;
if (ws->selected_auth->type & AUTH_TYPE_GSSAPI) {
ret = basic_auth_handler(ws, http_ver, msg);
} else {
ret = get_auth_handler2(ws, http_ver, msg, pcounter);
}
goto cleanup;
} else if (ret < 0) {
if (ws->selected_auth->type & AUTH_TYPE_GSSAPI) {
/* Fallback from GSSAPI to USERNAME-PASSWORD */
oclog(ws, LOG_ERR, "failed gssapi authentication");
if (ws_switch_auth_to_next(ws) == 0)
goto auth_fail;
ws->auth_state = S_AUTH_INACTIVE;
ws->sid_set = 0;
goto ask_auth;
} else {
oclog(ws, LOG_ERR, "failed authentication for '%s'",
ws->username);
goto auth_fail;
}
}
oclog(ws, LOG_HTTP_DEBUG, "user '%s' obtained cookie", ws->username);
ws->auth_state = S_AUTH_COOKIE;
ret = post_common_handler(ws, http_ver, msg);
goto cleanup;
ask_auth:
return get_auth_handler(ws, http_ver);
auth_fail:
if (sd != -1)
close(sd);
oclog(ws, LOG_HTTP_DEBUG, "HTTP sending: 401 Unauthorized");
ret = cstp_printf(ws,
"HTTP/1.%d 401 %s\r\nContent-Length: 0\r\n\r\n",
http_ver, reason);
if (ret >= 0)
cstp_fatal_close(ws, GNUTLS_A_ACCESS_DENIED);
talloc_free(msg);
exit_worker(ws);
cleanup:
talloc_free(msg);
return ret;
}