diff --git a/configure.ac b/configure.ac
index babac5aa..f7c5f8fc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -391,6 +391,26 @@ AC_DEFINE([HAVE_LZ4], [], [LZ4 was found])
])
fi
+dnl GSSAPI
+AC_ARG_WITH(gssapi,
+ AS_HELP_STRING([--without-gssapi], [disable support for GSSAPI authentication]),
+ test_for_gssapi=$withval,
+ test_for_gssapi=yes)
+
+enable_gssapi=no
+if test "$test_for_gssapi" = yes;then
+PKG_CHECK_MODULES([LIBKRB5], [krb5-gssapi], [
+enable_gssapi=yes
+AC_DEFINE([HAVE_GSSAPI], [], [GSSAPI was found])
+],
+[
+ AC_MSG_WARN([[
+***
+*** gssapi not found. Will disable compression support.
+*** ]])
+])
+fi
+
dnl needed in the included PCL
AC_C_VOLATILE
AC_C_CONST
@@ -467,6 +487,7 @@ Summary of build options:
PAM auth backend: ${pam_enabled}
Radius auth backend: ${radius_enabled}
+ GSSAPI auth backend: ${enable_gssapi}
Anyconnect compat: ${anyconnect_enabled}
TCP wrappers: ${libwrap_enabled}
systemd: ${systemd_enabled}
diff --git a/src/Makefile.am b/src/Makefile.am
index 6c712f6d..cf55afdd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -5,7 +5,8 @@ AM_CPPFLAGS = -I$(srcdir)/../gl/ -I$(builddir)/../gl/ \
$(LIBGNUTLS_CFLAGS) \
$(LIBPROTOBUF_C_CFLAGS) $(LIBLZ4_CFLAGS) \
$(LIBNL3_CFLAGS) $(LIBREADLINE_CFLAGS) \
- $(LIBTALLOC_CFLAGS) $(LIBDBUS_CFLAGS)
+ $(LIBTALLOC_CFLAGS) $(LIBDBUS_CFLAGS) \
+ $(LIBKRB5_CFLAGS)
BUILT_SOURCES = ocpasswd-args.c ocpasswd-args.h \
ocserv-args.c ocserv-args.h ipc.pb-c.c ipc.pb-c.h \
@@ -63,7 +64,7 @@ COMMON_SOURCES=common.c common.h system.c system.h setproctitle.c setproctitle.h
# Authentication module sources
AUTH_SOURCES=auth/pam.c auth/pam.h auth/plain.c auth/plain.h auth/radius.c auth/radius.h \
- auth/common.c auth/common.h
+ auth/common.c auth/common.h auth/gssapi.h auth/gssapi.c
ocserv_SOURCES = main.c main-auth.c worker-vpn.c worker-auth.c tlslib.c \
cookies.c main-misc.c ip-lease.c ip-lease.h \
@@ -90,7 +91,7 @@ ocserv_LDADD = ../gl/libgnu.a $(NEEDED_LIBOPTS) libcmd-ocserv.a
ocserv_LDADD += $(LIBGNUTLS_LIBS) $(PAM_LIBS) $(LIBUTIL) \
$(LIBSECCOMP) $(LIBWRAP) $(LIBCRYPT) $(NEEDED_HTTP_PARSER_LIBS) \
$(LIBPROTOBUF_C_LIBS) $(LIBSYSTEMD) $(LIBTALLOC_LIBS) \
- $(FREERADIUS_CLIENT_LIBS) $(LIBLZ4_LIBS)
+ $(FREERADIUS_CLIENT_LIBS) $(LIBLZ4_LIBS) $(LIBKRB5_LIBS)
if PCL
diff --git a/src/auth/gssapi.c b/src/auth/gssapi.c
new file mode 100644
index 00000000..48d65bca
--- /dev/null
+++ b/src/auth/gssapi.c
@@ -0,0 +1,268 @@
+/*
+ * 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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "gssapi.h"
+#include "auth/common.h"
+#include
+#include
+#include
+#include
+
+#define MAX_MSG_SIZE 256
+
+#ifdef HAVE_GSSAPI
+
+static gss_cred_id_t glob_creds;
+gss_OID_set glob_oids;
+
+struct gssapi_ctx_st {
+ char username[MAX_USERNAME_SIZE];
+ gss_ctx_id_t gssctx;
+
+ gss_cred_id_t delegated_creds;
+ gss_buffer_desc msg;
+};
+
+static void gssapi_global_init(void *pool, void *additional)
+{
+ int ret;
+ OM_uint32 time, minor;
+ gss_name_t name = GSS_C_NO_NAME;
+
+ if (additional && strncmp(additional, "keytab:", 7) == 0) {
+ gss_key_value_element_desc element;
+ gss_key_value_set_desc cred_store;
+
+ element.key = "keytab";
+ element.value = additional+7;
+ cred_store.count = 1;
+ cred_store.elements = &element;
+
+ ret = gss_acquire_cred_from(&minor, name, 0, GSS_C_NO_OID_SET, 2,
+ &cred_store, &glob_creds, &glob_oids, &time);
+ } else {
+ ret = gss_acquire_cred(&minor, name, 0, GSS_C_NO_OID_SET, 2,
+ &glob_creds, &glob_oids, &time);
+ }
+
+ if (ret != GSS_S_COMPLETE) {
+ ret = -1;
+ syslog(LOG_ERR, "gssapi: error in gss_acquire_cred[%s]: %d", (name==GSS_C_NO_NAME)?"default":(char*)additional, ret);
+ exit(1);
+ }
+
+ if (name != GSS_C_NO_NAME)
+ gss_release_name(&minor, &name);
+
+ return;
+}
+
+static void gssapi_global_deinit()
+{
+ OM_uint32 minor;
+
+ if (glob_creds != NULL)
+ gss_release_cred(&minor, &glob_creds);
+}
+
+static void get_name(struct gssapi_ctx_st *pctx, gss_name_t client, gss_OID mech_type)
+{
+ int ret;
+ OM_uint32 minor;
+ gss_buffer_desc name;
+
+ pctx->username[0] = 0;
+
+ ret = gss_display_name(&minor, client, &name, NULL);
+ if (GSS_ERROR(ret)) {
+ syslog(LOG_ERR, "gssapi: error in gss_display_name: %d", ret);
+ return;
+ }
+
+ syslog(LOG_DEBUG, "gssapi: full username %.*s", (unsigned)name.length, (char*)name.value);
+ gss_release_buffer(&minor, &name);
+
+ ret = gss_localname(&minor, client, mech_type, &name);
+ if (GSS_ERROR(ret) || name.length >= MAX_USERNAME_SIZE) {
+ syslog(LOG_ERR, "gssapi: error in gss_display_name: %d", ret);
+ return;
+ }
+
+ syslog(LOG_DEBUG, "gssapi: username %.*s", (unsigned)name.length, (char*)name.value);
+
+ memcpy(pctx->username, name.value, name.length);
+ pctx->username[name.length] = 0;
+ gss_release_buffer(&minor, &name);
+
+ return;
+}
+
+static int gssapi_auth_init(void **ctx, void *pool, const char *spnego, const char *ip,
+ void *additional)
+{
+ struct gssapi_ctx_st *pctx;
+ OM_uint32 minor, flags, time;
+ gss_buffer_desc buf;
+ gss_name_t client = GSS_C_NO_NAME;
+ gss_OID mech_type = GSS_C_NO_OID;
+ int ret;
+ size_t raw_len;
+ char *raw;
+
+ if (spnego == NULL || spnego[0] == 0) {
+ syslog(LOG_ERR, "gssapi: error in spnego data %s", __func__);
+ return ERR_AUTH_FAIL;
+ }
+
+ pctx = talloc_zero(pool, struct gssapi_ctx_st);
+ if (pctx == NULL)
+ return ERR_AUTH_FAIL;
+
+ ret = base64_decode_alloc(spnego, strlen(spnego), &raw, &raw_len);
+ if (ret == 0) {
+ syslog(LOG_ERR, "gssapi: error in base64 decoding %s", __func__);
+ return ERR_AUTH_FAIL;
+ }
+
+ buf.value = raw;
+ buf.length = raw_len;
+ ret = gss_accept_sec_context(&minor, &pctx->gssctx, glob_creds, &buf,
+ GSS_C_NO_CHANNEL_BINDINGS, &client, &mech_type, &pctx->msg,
+ &flags, &time, &pctx->delegated_creds);
+ free(raw);
+
+ if (ret == GSS_S_CONTINUE_NEEDED) {
+ gss_release_name(&minor, &client);
+ ret = ERR_AUTH_CONTINUE;
+ } else if (ret == GSS_S_COMPLETE) {
+ get_name(pctx, client, mech_type);
+ gss_release_name(&minor, &client);
+ ret = 0;
+ } else {
+ syslog(LOG_ERR, "gssapi: error in gss_accept_sec_context: %d", ret);
+ return ERR_AUTH_FAIL;
+ }
+
+ *ctx = pctx;
+
+ return ret;
+}
+
+static int gssapi_auth_group(void *ctx, const char *suggested, char *groupname, int groupname_size)
+{
+ groupname[0] = 0;
+ return 0;
+}
+
+static int gssapi_auth_user(void *ctx, char *username, int username_size)
+{
+ struct gssapi_ctx_st *pctx = ctx;
+
+ strlcpy(username, pctx->username, username_size);
+ return -1;
+}
+
+/* Returns 0 if the user is successfully authenticated, and sets the appropriate group name.
+ */
+static int gssapi_auth_pass(void *ctx, const char *spnego, unsigned spnego_len)
+{
+ struct gssapi_ctx_st *pctx = ctx;
+ OM_uint32 minor, flags, time;
+ gss_buffer_desc buf;
+ gss_name_t client = GSS_C_NO_NAME;
+ gss_OID mech_type = GSS_C_NO_OID;
+ size_t raw_len;
+ char *raw;
+ int ret;
+
+ /* nothing to be done */
+ ret = base64_decode_alloc(spnego, spnego_len, &raw, &raw_len);
+ if (ret == 0) {
+ syslog(LOG_ERR, "gssapi: error in base64 decoding %s", __func__);
+ return ERR_AUTH_FAIL;
+ }
+
+ buf.value = raw;
+ buf.length = raw_len;
+ ret = gss_accept_sec_context(&minor, &pctx->gssctx, glob_creds, &buf,
+ GSS_C_NO_CHANNEL_BINDINGS, &client, &mech_type, &pctx->msg,
+ &flags, &time, &pctx->delegated_creds);
+ free(raw);
+
+ if (ret == GSS_S_CONTINUE_NEEDED) {
+ gss_release_name(&minor, &client);
+ return ERR_AUTH_CONTINUE;
+ } else if (ret == GSS_S_COMPLETE) {
+ get_name(pctx, client, mech_type);
+ gss_release_name(&minor, &client);
+ return 0;
+ } else {
+ syslog(LOG_ERR, "gssapi: error in gss_accept_sec_context: %d", ret);
+ return ERR_AUTH_FAIL;
+ }
+}
+
+static int gssapi_auth_msg(void *ctx, char *msg, size_t msg_size)
+{
+ struct gssapi_ctx_st *pctx = ctx;
+ OM_uint32 min;
+
+ /* our msg is our SPNEGO reply */
+ if (pctx->msg.value != NULL) {
+ base64_encode((char *)pctx->msg.value, pctx->msg.length, (char *)msg, msg_size);
+ gss_release_buffer(&min, &pctx->msg);
+ pctx->msg.value = NULL;
+ } else {
+ msg[0] = 0;
+ }
+ return 0;
+}
+
+static void gssapi_auth_deinit(void *ctx)
+{
+ struct gssapi_ctx_st *pctx = ctx;
+ OM_uint32 min;
+
+ gss_delete_sec_context(&min, &pctx->gssctx, GSS_C_NO_BUFFER);
+ gss_release_cred(&min, &pctx->delegated_creds);
+ gss_release_buffer(&min, &pctx->msg);
+ talloc_free(ctx);
+}
+
+const struct auth_mod_st gssapi_auth_funcs = {
+ .type = AUTH_TYPE_GSSAPI,
+ .auth_init = gssapi_auth_init,
+ .auth_deinit = gssapi_auth_deinit,
+ .auth_msg = gssapi_auth_msg,
+ .auth_pass = gssapi_auth_pass,
+ .auth_user = gssapi_auth_user,
+ .auth_group = gssapi_auth_group,
+ .global_init = gssapi_global_init,
+ .global_deinit = gssapi_global_deinit,
+};
+
+#endif
diff --git a/src/auth/gssapi.h b/src/auth/gssapi.h
new file mode 100644
index 00000000..9623258f
--- /dev/null
+++ b/src/auth/gssapi.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of ocserv.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see
+ */
+#ifndef GSSAPI_H
+#define GSSAPI_H
+
+#include
+
+extern const struct auth_mod_st gssapi_auth_funcs;
+
+#endif
diff --git a/src/auth/pam.c b/src/auth/pam.c
index 700b7f0f..c5aee89a 100644
--- a/src/auth/pam.c
+++ b/src/auth/pam.c
@@ -160,8 +160,11 @@ static int pam_auth_init(void** ctx, void *pool, const char* user, const char* i
int pret;
struct pam_ctx_st * pctx;
- if (user == NULL)
- return -1;
+ if (user == NULL || user[0] == 0) {
+ syslog(LOG_AUTH,
+ "pam-auth: no username present");
+ return ERR_AUTH_FAIL;
+ }
pctx = talloc_zero(pool, struct pam_ctx_st);
if (pctx == NULL)
@@ -188,7 +191,7 @@ struct pam_ctx_st * pctx;
*ctx = pctx;
- return 0;
+ return ERR_AUTH_CONTINUE;
fail2:
pam_end(pctx->ph, pret);
diff --git a/src/auth/plain.c b/src/auth/plain.c
index 2c0741e9..fbc5b491 100644
--- a/src/auth/plain.c
+++ b/src/auth/plain.c
@@ -167,6 +167,12 @@ static int plain_auth_init(void **ctx, void *pool, const char *username, const c
struct plain_ctx_st *pctx;
int ret;
+ if (username == NULL || username[0] == 0) {
+ syslog(LOG_AUTH,
+ "plain-auth: no username present");
+ return ERR_AUTH_FAIL;
+ }
+
pctx = talloc_zero(pool, struct plain_ctx_st);
if (pctx == NULL)
return ERR_AUTH_FAIL;
@@ -183,7 +189,7 @@ static int plain_auth_init(void **ctx, void *pool, const char *username, const c
*ctx = pctx;
- return 0;
+ return ERR_AUTH_CONTINUE;
}
static int plain_auth_group(void *ctx, const char *suggested, char *groupname, int groupname_size)
diff --git a/src/auth/radius.c b/src/auth/radius.c
index 85993538..230056e1 100644
--- a/src/auth/radius.c
+++ b/src/auth/radius.c
@@ -67,6 +67,12 @@ static int radius_auth_init(void **ctx, void *pool, const char *username, const
struct radius_ctx_st *pctx;
char *default_realm;
+ if (username == NULL || username[0] == 0) {
+ syslog(LOG_AUTH,
+ "radius-auth: no username present");
+ return ERR_AUTH_FAIL;
+ }
+
pctx = talloc_zero(pool, struct radius_ctx_st);
if (pctx == NULL)
return ERR_AUTH_FAIL;
@@ -88,7 +94,7 @@ static int radius_auth_init(void **ctx, void *pool, const char *username, const
*ctx = pctx;
- return 0;
+ return ERR_AUTH_CONTINUE;
}
static int radius_auth_group(void *ctx, const char *suggested, char *groupname, int groupname_size)
diff --git a/src/config.c b/src/config.c
index 86c88d9e..0db5dce6 100644
--- a/src/config.c
+++ b/src/config.c
@@ -34,6 +34,7 @@
#include
#include
#include
+#include
#include
#include
@@ -47,7 +48,6 @@
static char pid_file[_POSIX_PATH_MAX] = "";
static const char* cfg_file = DEFAULT_CFG_FILE;
-static const struct auth_mod_st *amod = NULL;
struct cfg_options {
const char* name;
@@ -58,6 +58,7 @@ struct cfg_options {
static struct cfg_options available_options[] = {
{ .name = "auth", .type = OPTION_MULTI_LINE, .mandatory = 1 },
+ { .name = "enable-auth", .type = OPTION_MULTI_LINE, .mandatory = 0 },
{ .name = "route", .type = OPTION_MULTI_LINE, .mandatory = 0 },
{ .name = "no-route", .type = OPTION_MULTI_LINE, .mandatory = 0 },
{ .name = "select-group", .type = OPTION_MULTI_LINE, .mandatory = 0 },
@@ -152,10 +153,10 @@ static struct cfg_options available_options[] = {
};
#define get_brackets_string get_brackets_string1
-static char *get_brackets_string1(void *pool, const char *str);
+static char *get_brackets_string1(struct cfg_st *config, const char *str);
#ifdef HAVE_RADIUS
-static char *get_brackets_string2(void *pool, const char *str)
+static char *get_brackets_string2(struct cfg_st *config, const char *str)
{
char *p, *p2;
unsigned len;
@@ -186,7 +187,28 @@ static char *get_brackets_string2(void *pool, const char *str)
len = p2 - p;
- return talloc_strndup(pool, p, len);
+ return talloc_strndup(config, p, len);
+}
+
+static char *radius_get_brackets_string(struct cfg_st *config, const char *str)
+{
+ char *ret, *p;
+
+ ret = get_brackets_string1(config, str+6);
+ if (ret == NULL) {
+ fprintf(stderr, "No radius configuration specified\n");
+ exit(1);
+ }
+
+ p = get_brackets_string2(config, str+6);
+ if (p != NULL) {
+ if (strcasecmp(p, "groupconfig") != 0) {
+ fprintf(stderr, "No known configuration option: %s\n", p);
+ exit(1);
+ }
+ config->sup_config_type = SUP_CONFIG_RADIUS;
+ }
+ return ret;
}
#endif
@@ -219,7 +241,7 @@ unsigned j;
do { \
if (val && !strcmp(val->pzName, name)==0) \
continue; \
- s_name[num] = talloc_strdup(config, val->v.strVal); \
+ s_name[num] = talloc_strdup(s_name, val->v.strVal); \
num++; \
if (num>=MAX_CONFIG_ENTRIES) \
break; \
@@ -345,7 +367,7 @@ unsigned j;
}
}
-static char *get_brackets_string1(void *pool, const char *str)
+static char *get_brackets_string1(struct cfg_st *config, const char *str)
{
char *p, *p2;
unsigned len;
@@ -369,10 +391,9 @@ static char *get_brackets_string1(void *pool, const char *str)
len = p2 - p;
- return talloc_strndup(pool, p, len);
+ return talloc_strndup(config, p, len);
}
-
/* Parses the string ::1/prefix, to return prefix
* and modify the string to contain the network only.
*/
@@ -395,9 +416,107 @@ unsigned extract_prefix(char *network)
return prefix;
}
-const struct auth_mod_st *get_auth_mod(void)
+typedef struct auth_types_st {
+ const char *name;
+ unsigned name_size;
+ const struct auth_mod_st *mod;
+ unsigned type;
+ char *(*get_brackets_string)(struct cfg_st *config, const char *);
+} auth_types_st;
+
+#define NAME(x) (x),(sizeof(x)-1)
+static auth_types_st avail_auth_types[] =
{
- return amod;
+#ifdef HAVE_PAM
+ {NAME("pam"), &pam_auth_funcs, AUTH_TYPE_PAM, get_brackets_string},
+#endif
+#ifdef HAVE_GSSAPI
+ {NAME("gssapi"), &gssapi_auth_funcs, AUTH_TYPE_GSSAPI, get_brackets_string},
+#endif
+#ifdef HAVE_RADIUS
+ {NAME("radius"), &radius_auth_funcs, AUTH_TYPE_RADIUS, radius_get_brackets_string},
+#endif
+ {NAME("plain"), &plain_auth_funcs, AUTH_TYPE_PLAIN, get_brackets_string},
+ {NAME("certificate[optional]"), NULL, AUTH_TYPE_CERTIFICATE_OPT, NULL},
+ {NAME("certificate"), NULL, AUTH_TYPE_CERTIFICATE, NULL},
+};
+
+static void figure_auth_funcs(struct cfg_st *config, char **auth, unsigned auth_size,
+ unsigned primary)
+{
+ unsigned j, i;
+ unsigned found;
+
+ if (primary != 0) {
+ /* Set the primary authentication methods */
+ for (j=0;jauth[0].additional = avail_auth_types[i].get_brackets_string(config, auth[j]+avail_auth_types[i].name_size);
+
+ if (config->auth[0].amod != NULL && avail_auth_types[i].mod != NULL) {
+ fprintf(stderr, "%s: You cannot mix multiple authentication methods of this type\n", auth[j]);
+ exit(1);
+ }
+
+ if (config->auth[0].amod == NULL)
+ config->auth[0].amod = avail_auth_types[i].mod;
+ config->auth[0].type |= avail_auth_types[i].type;
+ if (config->auth[0].name == NULL)
+ config->auth[0].name = avail_auth_types[i].name;
+ fprintf(stderr, "Setting '%s' as primary authentication method\n", avail_auth_types[i].name);
+ config->auth[0].enabled = 1;
+ config->auth_methods = 1;
+ found = 1;
+ break;
+ }
+ }
+
+ if (found == 0) {
+ fprintf(stderr, "Unknown or unsupported auth method: %s\n", auth[j]);
+ exit(1);
+ }
+ talloc_free(auth[j]);
+ }
+ } else {
+ unsigned x = config->auth_methods;
+ /* Append authentication methods (alternative options) */
+ for (j=0;jauth[x].additional = avail_auth_types[i].get_brackets_string(config, auth[j]+avail_auth_types[i].name_size);
+
+ if (config->auth[x].name == NULL)
+ config->auth[x].name = avail_auth_types[i].name;
+ fprintf(stderr, "Enabling '%s' as authentication method\n", avail_auth_types[i].name);
+
+ config->auth[x].amod = avail_auth_types[i].mod;
+ config->auth[x].type |= avail_auth_types[i].type;
+ config->auth[x].enabled = 1;
+ found = 1;
+ x++;
+ if (x >= MAX_AUTH_METHODS) {
+ fprintf(stderr, "You cannot enable more than %d authentication methods\n", x);
+ exit(1);
+ }
+ break;
+ }
+ }
+
+ if (found == 0) {
+ fprintf(stderr, "Unknown or unsupported auth method: %s\n", auth[j]);
+ exit(1);
+ }
+ talloc_free(auth[j]);
+ }
+ config->auth_methods = x;
+
+ }
+ talloc_free(auth);
}
static void parse_cfg_file(const char* file, struct cfg_st *config, unsigned reload)
@@ -439,81 +558,26 @@ unsigned force_cert_auth;
config->sup_config_type = SUP_CONFIG_FILE;
READ_MULTI_LINE("auth", auth, auth_size);
- for (j=0;jauth_additional = get_brackets_string(config, auth[j]+3);
- if ((config->auth_types & AUTH_TYPE_USERNAME_PASS) != 0) {
- fprintf(stderr, "You cannot mix multiple username/password authentication methods\n");
- exit(1);
- }
-#ifdef HAVE_PAM
- amod = &pam_auth_funcs;
- config->auth_types |= amod->type;
-#else
- fprintf(stderr, "PAM support is disabled\n");
- exit(1);
-#endif
- } else if (strncasecmp(auth[j], "plain", 5) == 0) {
- if ((config->auth_types & AUTH_TYPE_USERNAME_PASS) != 0) {
- fprintf(stderr, "You cannot mix multiple username/password authentication methods\n");
- exit(1);
- }
+ figure_auth_funcs(config, auth, auth_size, 1);
+ auth = NULL;
+ auth_size = 0;
- config->auth_additional = get_brackets_string(config, auth[j]+5);
- if (config->auth_additional == NULL) {
- fprintf(stderr, "Format error in %s\n", auth[j]);
- exit(1);
- }
- amod = &plain_auth_funcs;
- config->auth_types |= amod->type;
- } else if (strncasecmp(auth[j], "radius", 6) == 0) {
- if ((config->auth_types & AUTH_TYPE_USERNAME_PASS) != 0) {
- fprintf(stderr, "You cannot mix multiple username/password authentication methods\n");
- exit(1);
- } else {
+ READ_MULTI_LINE("enable-auth", auth, auth_size);
+ figure_auth_funcs(config, auth, auth_size, 0);
+ auth = NULL;
+ auth_size = 0;
-#ifdef HAVE_RADIUS
- const char *p;
-
- config->auth_additional = get_brackets_string1(config, auth[j]+6);
- if (config->auth_additional == NULL) {
- fprintf(stderr, "No configuration specified; error in %s\n", auth[j]);
- exit(1);
- }
-
- p = get_brackets_string2(config, auth[j]+6);
- if (p != NULL) {
- if (strcasecmp(p, "groupconfig") != 0) {
- fprintf(stderr, "No known configuration option: %s\n", p);
- exit(1);
- }
- config->sup_config_type = SUP_CONFIG_RADIUS;
- }
- amod = &radius_auth_funcs;
- config->auth_types |= amod->type;
-#else
- fprintf(stderr, "Radius support is disabled\n");
- exit(1);
-#endif
- }
- } else if (c_strcasecmp(auth[j], "certificate") == 0) {
- config->auth_types |= AUTH_TYPE_CERTIFICATE;
- } else if (c_strcasecmp(auth[j], "certificate[optional]") == 0) {
- config->auth_types |= AUTH_TYPE_CERTIFICATE_OPT;
- fprintf(stderr, "The authentication option certificate[optional] is experimental and may be removed in the future\n");
- } else {
- fprintf(stderr, "Unknown auth method: %s\n", auth[j]);
- exit(1);
- }
- talloc_free(auth[j]);
+ if (config->auth[0].enabled == 0) {
+ fprintf(stderr, "No authentication method was specified!\n");
+ exit(1);
}
- talloc_free(auth);
/* When adding allocated data, remember to modify
* reload_cfg_file();
*/
READ_STRING("listen-host", config->name);
READ_TF("listen-host-is-dyndns", config->is_dyndns, 0);
+ READ_STRING("listen-clear-file", config->unix_conn_file);
READ_NUMERIC("tcp-port", config->port);
READ_NUMERIC("udp-port", config->udp_port);
@@ -550,11 +614,6 @@ unsigned force_cert_auth;
if (reload == 0 && pid_file[0] == 0)
READ_STATIC_STRING("pid-file", pid_file);
- READ_STRING("listen-clear-file", config->unix_conn_file);
- if (config->unix_conn_file != NULL && (config->auth_types & AUTH_TYPE_CERTIFICATE)) {
- fprintf(stderr, "The option 'listen-clear-file' cannot be combined with 'auth=certificate'\n");
- exit(1);
- }
READ_STRING("socket-file", config->socket_file_prefix);
READ_STRING("occtl-socket-file", config->occtl_socket_file);
@@ -729,8 +788,9 @@ unsigned force_cert_auth;
READ_STRING("default-select-group", config->default_select_group);
READ_TF("auto-select-group", auto_select_group, 0);
- if (auto_select_group != 0 && amod != NULL && amod->group_list != NULL) {
- amod->group_list(config, config->auth_additional, &config->group_list, &config->group_list_size);
+
+ if (auto_select_group != 0 && config->auth[0].amod != NULL && config->auth[0].amod->group_list != NULL) {
+ config->auth[0].amod->group_list(config, config->auth[0].additional, &config->group_list, &config->group_list_size);
} else {
READ_MULTI_BRACKET_LINE("select-group",
config->group_list,
@@ -799,18 +859,29 @@ static void check_cfg(struct cfg_st *config)
exit(1);
}
- if (config->auth_types & AUTH_TYPE_CERTIFICATE) {
- if (config->cisco_client_compat == 0 && ((config->auth_types & AUTH_TYPE_CERTIFICATE_OPT) != AUTH_TYPE_CERTIFICATE_OPT))
+ if (config->auth[0].type & AUTH_TYPE_CERTIFICATE) {
+ if (config->cisco_client_compat == 0 && ((config->auth[0].type & AUTH_TYPE_CERTIFICATE_OPT) != AUTH_TYPE_CERTIFICATE_OPT))
config->cert_req = GNUTLS_CERT_REQUIRE;
else
config->cert_req = GNUTLS_CERT_REQUEST;
+ } else {
+ unsigned i;
+ for (i=1;iauth_methods;i++) {
+ if (config->auth[i].type & AUTH_TYPE_CERTIFICATE) {
+ config->cert_req = GNUTLS_CERT_REQUEST;
+ break;
+ }
+ }
}
- if (config->auth_additional != NULL && (config->auth_types & AUTH_TYPE_PLAIN) == AUTH_TYPE_PLAIN) {
- if (access(config->auth_additional, R_OK) != 0) {
- fprintf(stderr, "cannot access password file '%s'\n", config->auth_additional);
- exit(1);
- }
+ if (config->cert_req != 0 && config->cert_user_oid == NULL) {
+ fprintf(stderr, "A certificate is requested by the option 'cert-user-oid' is not set\n");
+ exit(1);
+ }
+
+ if (config->unix_conn_file != NULL && (config->cert_req != 0)) {
+ fprintf(stderr, "The option 'listen-clear-file' cannot be combined with 'auth=certificate'\n");
+ exit(1);
}
#ifdef ANYCONNECT_CLIENT_COMPAT
@@ -897,7 +968,7 @@ unsigned i;
DEL(config->per_group_dir);
DEL(config->socket_file_prefix);
DEL(config->default_domain);
- DEL(config->auth_additional);
+
DEL(config->ocsp_response);
DEL(config->banner);
DEL(config->dh_params_file);
@@ -969,6 +1040,9 @@ void print_version(tOptions *opts, tOptDesc *desc)
#ifdef HAVE_RADIUS
fprintf(stderr, "radius, ");
#endif
+#ifdef HAVE_GSSAPI
+ fprintf(stderr, "gssapi, ");
+#endif
#ifdef HAVE_PAM
fprintf(stderr, "PAM, ");
#endif
@@ -990,14 +1064,31 @@ void print_version(tOptions *opts, tOptDesc *desc)
exit(0);
}
+
void reload_cfg_file(void *pool, struct cfg_st* config)
{
+ auth_struct_st bak[MAX_AUTH_METHODS];
+ unsigned bak_size, i;
+
+ /* authentication methods can't change on reload */
+ memcpy(bak, config->auth, sizeof(config->auth));
+ memset(config->auth, 0, sizeof(config->auth));
+ bak_size = config->auth_methods;
+
clear_cfg_file(config);
memset(config, 0, sizeof(*config));
parse_cfg_file(cfg_file, config, 1);
+ /* reset the auth methods */
+ for (i=0;iauth_methods;i++) {
+ if (config->auth[i].enabled)
+ DEL(config->auth[i].additional);
+ }
+ memcpy(config->auth, bak, sizeof(config->auth));
+ config->auth_methods = bak_size;
+
check_cfg(config);
return;
diff --git a/src/http-heads.gperf b/src/http-heads.gperf
index 25264c44..c7d18c97 100644
--- a/src/http-heads.gperf
+++ b/src/http-heads.gperf
@@ -17,3 +17,5 @@ X-CSTP-Address-Type, HEADER_CSTP_ATYPE
X-CSTP-Hostname, HEADER_HOSTNAME
X-CSTP-Full-IPv6-Capability, HEADER_FULL_IPV6
X-AnyConnect-Identifier-DeviceType, HEADER_DEVICE_TYPE
+X-Need-SPNEGO, HEADER_NEED_SPNEGO
+Authorization, HEADER_AUTHORIZATION
diff --git a/src/ipc.proto b/src/ipc.proto
index 3c7e1288..bfd118f4 100644
--- a/src/ipc.proto
+++ b/src/ipc.proto
@@ -166,6 +166,7 @@ message sec_auth_init_msg
repeated string cert_group_names = 6;
optional string hostname = 7;
required string ip = 8;
+ required uint32 auth_type = 9 [default = 0];
}
/* SEC_AUTH_CONT */
diff --git a/src/main.h b/src/main.h
index 51b852fb..c82f25e6 100644
--- a/src/main.h
+++ b/src/main.h
@@ -293,5 +293,6 @@ void request_reload(int signo);
void request_stop(int signo);
const struct auth_mod_st *get_auth_mod(void);
+const struct auth_mod_st *get_backup_auth_mod(void);
#endif
diff --git a/src/ocserv-args.def b/src/ocserv-args.def
index a4808135..489a9641 100644
--- a/src/ocserv-args.def
+++ b/src/ocserv-args.def
@@ -105,6 +105,7 @@ An example configuration file follows.
#auth = "pam[gid-min=1000]"
#auth = "plain[/etc/ocserv/ocpasswd]"
#auth = "radius[/etc/radiusclient/radiusclient.conf,groupconfig]"
+#backup-auth = "gssapi[/etc/gssapi/config]"
# Whether to enable seccomp/Linux namespaces worker isolation. That restricts the number of
# system calls allowed to a worker process, in order to reduce damage from a
diff --git a/src/sec-mod-auth.c b/src/sec-mod-auth.c
index 566eec4d..948be679 100644
--- a/src/sec-mod-auth.c
+++ b/src/sec-mod-auth.c
@@ -51,24 +51,18 @@
#include
#include
-static const struct auth_mod_st *module = NULL;
+#ifdef HAVE_GSSAPI
+# include
+# include
+#endif
void sec_auth_init(sec_mod_st * sec, struct cfg_st *config)
{
- module = get_auth_mod();
+ unsigned i;
- if (module && module->global_init) {
- module->global_init(sec, config->auth_additional);
- }
-}
-
-void sec_auth_reinit(sec_mod_st * sec, struct cfg_st *config)
-{
- if (module) {
- if (module != get_auth_mod()) {
- seclog(sec, LOG_ERR, "Cannot change authentication method on reload");
- exit(1);
- }
+ for (i=0;iauth_methods;i++) {
+ if (config->auth[i].enabled && config->auth[i].amod && config->auth[i].amod->global_init)
+ config->auth[i].amod->global_init(sec, config->auth[i].additional);
}
}
@@ -165,18 +159,9 @@ static
int send_sec_auth_reply_msg(int cfd, sec_mod_st * sec, client_entry_st * e)
{
SecAuthReplyMsg msg = SEC_AUTH_REPLY_MSG__INIT;
- char tmp[MAX_MSG_SIZE] = "";
-
int ret;
- if (module == NULL || e->auth_ctx == NULL)
- return -1;
-
- ret = module->auth_msg(e->auth_ctx, tmp, sizeof(tmp));
- if (ret < 0)
- return ret;
-
- msg.msg = tmp;
+ msg.msg = e->msg_str;
msg.reply = AUTH__REP__MSG;
msg.has_sid = 1;
@@ -202,8 +187,8 @@ static int check_user_group_status(sec_mod_st * sec, client_entry_st * e,
unsigned need_cert = 1;
- if (sec->config->auth_types & AUTH_TYPE_CERTIFICATE) {
- if ((sec->config->auth_types & AUTH_TYPE_CERTIFICATE_OPT) == AUTH_TYPE_CERTIFICATE_OPT) {
+ if (e->auth_type & AUTH_TYPE_CERTIFICATE) {
+ if ((e->auth_type & AUTH_TYPE_CERTIFICATE_OPT) == AUTH_TYPE_CERTIFICATE_OPT) {
need_cert = 0;
}
@@ -307,7 +292,7 @@ int handle_sec_auth_res(int cfd, sec_mod_st * sec, client_entry_st * e, int resu
/* opens or closes a session.
*/
-int handle_sec_auth_session_cmd(int cfd, sec_mod_st * sec, const SecAuthSessionMsg * req,
+int handle_sec_auth_session_cmd(int cfd, sec_mod_st *sec, const SecAuthSessionMsg *req,
unsigned cmd)
{
client_entry_st *e;
@@ -340,8 +325,8 @@ int handle_sec_auth_session_cmd(int cfd, sec_mod_st * sec, const SecAuthSessionM
return -1;
}
- if (module != NULL && module->open_session != NULL) {
- ret = module->open_session(e->auth_ctx, req->sid.data, req->sid.len);
+ if (e->module != NULL && e->module->open_session != NULL) {
+ ret = e->module->open_session(e->auth_ctx, req->sid.data, req->sid.len);
if (ret < 0) {
e->status = PS_AUTH_FAILED;
seclog(sec, LOG_ERR, "could not open session.");
@@ -428,10 +413,10 @@ int handle_sec_auth_stats_cmd(sec_mod_st * sec, const CliStatsMsg * req)
if (req->uptime > e->stats.uptime)
e->stats.uptime = req->uptime;
- if (module == NULL || module->session_stats == NULL)
+ if (e->module == NULL || e->module->session_stats == NULL)
return 0;
- module->session_stats(e->auth_ctx, &e->stats);
+ e->module->session_stats(e->auth_ctx, &e->stats);
return 0;
}
@@ -473,8 +458,14 @@ int handle_sec_auth_cont(int cfd, sec_mod_st * sec, const SecAuthContMsg * req)
goto cleanup;
}
+ if (e->module == NULL) {
+ seclog(sec, LOG_ERR, "no module available!");
+ ret = -1;
+ goto cleanup;
+ }
+
ret =
- module->auth_pass(e->auth_ctx, req->password,
+ e->module->auth_pass(e->auth_ctx, req->password,
strlen(req->password));
if (ret < 0) {
seclog(sec, LOG_DEBUG,
@@ -486,10 +477,30 @@ int handle_sec_auth_cont(int cfd, sec_mod_st * sec, const SecAuthContMsg * req)
return handle_sec_auth_res(cfd, sec, e, ret);
}
+static
+int set_module(sec_mod_st * sec, client_entry_st *e, unsigned auth_type)
+{
+ unsigned i;
+
+ for (i=0;iconfig->auth_methods;i++) {
+ if (sec->config->auth[i].enabled && (sec->config->auth[i].type & auth_type) == auth_type) {
+ e->module = sec->config->auth[i].amod;
+ e->auth_type = sec->config->auth[i].type;
+ e->auth_additional = sec->config->auth[i].additional;
+
+ seclog(sec, LOG_INFO, "using '%s' authentication to authenticate user (%x)", sec->config->auth[i].name, auth_type);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
int handle_sec_auth_init(int cfd, sec_mod_st * sec, const SecAuthInitMsg * req)
{
int ret = -1;
client_entry_st *e;
+ unsigned need_continue = 0;
if (check_if_banned(sec, req->ip) != 0) {
seclog(sec, LOG_INFO,
@@ -497,34 +508,34 @@ int handle_sec_auth_init(int cfd, sec_mod_st * sec, const SecAuthInitMsg * req)
return -1;
}
- if ((req->user_name == NULL || req->user_name[0] == 0)
- && (sec->config->auth_types & AUTH_TYPE_USERNAME_PASS)) {
- seclog(sec, LOG_DEBUG,
- "auth init from '%s' with no username present", req->ip);
- return -1;
- }
-
e = new_client_entry(sec, req->ip);
if (e == NULL) {
seclog(sec, LOG_ERR, "cannot initialize memory");
return -1;
}
+ ret = set_module(sec, e, req->auth_type);
+ if (ret < 0) {
+ seclog(sec, LOG_ERR, "no module found for auth type %u", (unsigned)req->auth_type);
+ goto cleanup;
+ }
+
if (req->hostname != NULL) {
strlcpy(e->hostname, req->hostname, sizeof(e->hostname));
}
- if (sec->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
- /* req->username is non-null at this point */
+ if (e->module) {
ret =
- module->auth_init(&e->auth_ctx, e, req->user_name, req->ip,
- sec->config->auth_additional);
- if (ret < 0) {
+ e->module->auth_init(&e->auth_ctx, e, req->user_name, req->ip,
+ e->auth_additional);
+ if (ret == ERR_AUTH_CONTINUE) {
+ need_continue = 1;
+ } else if (ret < 0) {
goto cleanup;
}
ret =
- module->auth_group(e->auth_ctx, req->group_name, e->groupname,
+ e->module->auth_group(e->auth_ctx, req->group_name, e->groupname,
sizeof(e->groupname));
if (ret != 0) {
ret = -1;
@@ -534,14 +545,20 @@ int handle_sec_auth_init(int cfd, sec_mod_st * sec, const SecAuthInitMsg * req)
/* a module is allowed to change the name of the user */
ret =
- module->auth_user(e->auth_ctx, e->username,
+ e->module->auth_user(e->auth_ctx, e->username,
sizeof(e->username));
if (ret != 0 && req->user_name != NULL) {
strlcpy(e->username, req->user_name, sizeof(e->username));
}
+
+ ret = e->module->auth_msg(e->auth_ctx, e->msg_str, sizeof(e->msg_str));
+ if (ret < 0) {
+ ret = -1;
+ goto cleanup;
+ }
}
- if (sec->config->auth_types & AUTH_TYPE_CERTIFICATE) {
+ if (e->auth_type & AUTH_TYPE_CERTIFICATE) {
if (e->groupname[0] == 0 && req->group_name != NULL && sec->config->cert_group_oid != NULL) {
unsigned i, found = 0;
@@ -575,7 +592,7 @@ int handle_sec_auth_init(int cfd, sec_mod_st * sec, const SecAuthInitMsg * req)
req->tls_auth_ok?"(with cert) ":"",
e->username, e->groupname, req->ip);
- if (sec->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
+ if (need_continue != 0) {
ret = ERR_AUTH_CONTINUE;
goto cleanup;
}
@@ -587,15 +604,15 @@ int handle_sec_auth_init(int cfd, sec_mod_st * sec, const SecAuthInitMsg * req)
void sec_auth_user_deinit(sec_mod_st * sec, client_entry_st * e)
{
- if (module == NULL)
+ if (e->module == NULL)
return;
seclog(sec, LOG_DEBUG, "auth deinit for user '%s'", e->username);
if (e->auth_ctx != NULL) {
if (e->have_session) {
- module->close_session(e->auth_ctx, &e->stats);
+ e->module->close_session(e->auth_ctx, &e->stats);
}
- module->auth_deinit(e->auth_ctx);
+ e->module->auth_deinit(e->auth_ctx);
e->auth_ctx = NULL;
e->have_session = 0;
}
diff --git a/src/sec-mod.c b/src/sec-mod.c
index f9b29efd..95e3f273 100644
--- a/src/sec-mod.c
+++ b/src/sec-mod.c
@@ -358,7 +358,6 @@ static void check_other_work(sec_mod_st *sec)
if (need_reload) {
seclog(sec, LOG_DEBUG, "reloading configuration");
reload_cfg_file(sec, sec->config);
- sec_auth_reinit(sec, sec->config);
need_reload = 0;
}
diff --git a/src/sec-mod.h b/src/sec-mod.h
index f0e16b63..47ce986c 100644
--- a/src/sec-mod.h
+++ b/src/sec-mod.h
@@ -56,6 +56,8 @@ typedef struct client_entry_st {
unsigned in_use; /* counter of users of this structure */
unsigned tls_auth_ok;
+ char msg_str[MAX_MSG_SIZE];
+
stats_st stats;
unsigned status; /* PS_AUTH_ */
@@ -71,6 +73,12 @@ typedef struct client_entry_st {
/* The time this client entry was last modified (created or closed) */
time_t time;
+
+ /* the auth type associated with the user */
+ unsigned auth_type;
+ /* the module this entry is using */
+ const struct auth_mod_st *module;
+ void *auth_additional; /* input to auth_init */
} client_entry_st;
void *sec_mod_client_db_init(sec_mod_st *sec);
@@ -98,7 +106,6 @@ void seclog_hex(const struct sec_mod_st* sec, int priority,
const char *prefix, uint8_t* bin, unsigned bin_size, unsigned b64);
void sec_auth_init(sec_mod_st *sec, struct cfg_st *config);
-void sec_auth_reinit(sec_mod_st *sec, struct cfg_st *config);
int handle_sec_auth_init(int cfd, sec_mod_st *sec, const SecAuthInitMsg * req);
int handle_sec_auth_cont(int cfd, sec_mod_st *sec, const SecAuthContMsg * req);
diff --git a/src/tlslib.c b/src/tlslib.c
index db79f6c8..4e3c27dd 100644
--- a/src/tlslib.c
+++ b/src/tlslib.c
@@ -398,10 +398,10 @@ static int verify_certificate_cb(gnutls_session_t session)
fail:
/* In cisco client compatibility we don't hangup immediately, we
* simply use the flag (ws->cert_auth_ok). */
- if (ws->config->cisco_client_compat == 0)
- return GNUTLS_E_CERTIFICATE_ERROR;
- else
+ if (ws->config->cisco_client_compat != 0 || ws->config->cert_req != GNUTLS_CERT_REQUIRE)
return 0;
+ else
+ return GNUTLS_E_CERTIFICATE_ERROR;
}
diff --git a/src/vpn.h b/src/vpn.h
index 63eb41c8..2f7d58ee 100644
--- a/src/vpn.h
+++ b/src/vpn.h
@@ -86,6 +86,7 @@ extern int syslog_open;
#define AUTH_TYPE_CERTIFICATE (1<<3)
#define AUTH_TYPE_CERTIFICATE_OPT (1<<4|AUTH_TYPE_CERTIFICATE)
#define AUTH_TYPE_RADIUS (1<<5 | AUTH_TYPE_USERNAME_PASS)
+#define AUTH_TYPE_GSSAPI (1<<6)
#define ERR_SUCCESS 0
#define ERR_BAD_COMMAND -2
@@ -209,6 +210,16 @@ struct vpn_st {
unsigned int nbns_size;
};
+#define MAX_AUTH_METHODS 4
+
+typedef struct auth_struct_st {
+ const char *name;
+ char *additional;
+ unsigned type;
+ const struct auth_mod_st *amod;
+ bool enabled;
+} auth_struct_st;
+
struct cfg_st {
char *name; /* server name */
unsigned int port;
@@ -230,8 +241,10 @@ struct cfg_st {
char *dh_params_file;
char *cert_user_oid; /* The OID that will be used to extract the username */
char *cert_group_oid; /* The OID that will be used to extract the groupname */
- unsigned int auth_types; /* or'ed sequence of AUTH_TYPE */
- char *auth_additional; /* the additional string specified in the auth methode */
+
+ auth_struct_st auth[MAX_AUTH_METHODS];
+ unsigned auth_methods;
+
gnutls_certificate_request_t cert_req;
char *priorities;
unsigned enable_compression;
diff --git a/src/worker-auth.c b/src/worker-auth.c
index 1ebd4636..5c287e64 100644
--- a/src/worker-auth.c
+++ b/src/worker-auth.c
@@ -88,6 +88,22 @@ static const char ocv3_login_msg_end[] =
static int get_cert_info(worker_st * ws);
+int ws_switch_auth_to(struct worker_st *ws, unsigned auth)
+{
+ unsigned i;
+
+ if (ws->selected_auth && ws->selected_auth->type & auth)
+ return 1;
+
+ for (i=1;iconfig->auth_methods;i++) {
+ if ((ws->config->auth[i].type & auth) != 0) {
+ ws->selected_auth = &ws->config->auth[i];
+ return 1;
+ }
+ }
+ return 0;
+}
+
static int append_group_idx(worker_st * ws, str_st *str, unsigned i)
{
char temp[128];
@@ -215,7 +231,7 @@ int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg)
goto cleanup;
}
- if (ws->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
+ if (ws->selected_auth->type & AUTH_TYPE_USERNAME_PASS) {
ret = str_append_str(&str, login_msg_user);
if (ret < 0) {
ret = -1;
@@ -223,7 +239,7 @@ int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg)
}
}
- if (ws->config->auth_types & AUTH_TYPE_CERTIFICATE && ws->cert_auth_ok != 0) {
+ if (ws->selected_auth->type & AUTH_TYPE_CERTIFICATE && ws->cert_auth_ok != 0) {
ret = get_cert_info(ws);
if (ret < 0) {
ret = -1;
@@ -262,7 +278,7 @@ int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg)
}
/* append any groups available in the certificate */
- if (ws->config->auth_types & AUTH_TYPE_CERTIFICATE && ws->cert_auth_ok != 0) {
+ if (ws->selected_auth->type & AUTH_TYPE_CERTIFICATE && ws->cert_auth_ok != 0) {
unsigned dup;
for (i=0;icert_groups_size;i++) {
@@ -794,9 +810,9 @@ int auth_cookie(worker_st * ws, void *cookie, size_t cookie_size)
int ret;
AuthCookieRequestMsg msg = AUTH_COOKIE_REQUEST_MSG__INIT;
- if ((ws->config->auth_types & AUTH_TYPE_CERTIFICATE)
+ if ((ws->selected_auth->type & AUTH_TYPE_CERTIFICATE)
&& ws->config->cisco_client_compat == 0) {
- if (((ws->config->auth_types & AUTH_TYPE_CERTIFICATE_OPT) != AUTH_TYPE_CERTIFICATE_OPT && ws->cert_auth_ok == 0)) {
+ if (((ws->selected_auth->type & AUTH_TYPE_CERTIFICATE_OPT) != AUTH_TYPE_CERTIFICATE_OPT && ws->cert_auth_ok == 0)) {
oclog(ws, LOG_INFO,
"no certificate provided for cookie authentication");
return -1;
@@ -833,7 +849,7 @@ int auth_cookie(worker_st * ws, void *cookie, size_t cookie_size)
return 0;
}
-int post_common_handler(worker_st * ws, unsigned http_ver)
+int post_common_handler(worker_st * ws, unsigned http_ver, const char *imsg)
{
int ret, size;
char str_cookie[BASE64_LENGTH(ws->cookie_size)+1];
@@ -870,6 +886,12 @@ int post_common_handler(worker_st * ws, unsigned http_ver)
if (ret < 0)
return -1;
+ if (ws->selected_auth->type & AUTH_TYPE_GSSAPI && imsg != NULL) {
+ ret = cstp_printf(ws, "WWW-Authenticate: Negotiate %s\r\n", imsg);
+ if (ret < 0)
+ return -1;
+ }
+
ret = cstp_puts(ws, "Content-Type: text/xml\r\n");
if (ret < 0)
return -1;
@@ -966,6 +988,9 @@ int parse_reply(worker_st * ws, char *body, unsigned body_length,
unsigned temp2_len, temp1_len;
unsigned len, xml = 0;
+ if (body == NULL || body_length == 0)
+ return -1;
+
if (memmem(body, body_length, "config->auth_types & AUTH_TYPE_USERNAME_PASS) {
+ 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,
@@ -1115,10 +1189,11 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
strlcpy(ws->username, username, sizeof(ws->username));
talloc_free(username);
ireq.user_name = ws->username;
+ ireq.auth_type |= AUTH_TYPE_USERNAME_PASS;
}
- if (ws->config->auth_types & AUTH_TYPE_CERTIFICATE) {
- if ((ws->config->auth_types & AUTH_TYPE_CERTIFICATE_OPT) != AUTH_TYPE_CERTIFICATE_OPT && ws->cert_auth_ok == 0) {
+ if (ws->selected_auth->type & AUTH_TYPE_CERTIFICATE) {
+ if ((ws->selected_auth->type & AUTH_TYPE_CERTIFICATE_OPT) != AUTH_TYPE_CERTIFICATE_OPT && ws->cert_auth_ok == 0) {
reason = MSG_NO_CERT_ERROR;
oclog(ws, LOG_INFO,
"no certificate provided for authentication");
@@ -1144,6 +1219,7 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
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.hostname = req->hostname;
@@ -1174,7 +1250,16 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
|| ws->auth_state == S_AUTH_REQ) {
SecAuthContMsg areq = SEC_AUTH_CONT_MSG__INIT;
- if (ws->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
+ 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);
+ goto auth_fail;
+ }
+ areq.password = req->authorization + 10;
+ }
+
+ if (areq.password == NULL && ws->selected_auth->type & AUTH_TYPE_USERNAME_PASS) {
ret = parse_reply(ws, req->body, req->body_length,
PASSWORD_FIELD, sizeof(PASSWORD_FIELD)-1,
NULL, 0,
@@ -1186,6 +1271,9 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
}
areq.password = password;
+ }
+
+ if (areq.password != NULL) {
if (ws->sid_set != 0) {
areq.sid.data = ws->sid;
areq.sid.len = sizeof(ws->sid);
@@ -1234,7 +1322,10 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
ws->username);
ws->auth_state = S_AUTH_REQ;
- return get_auth_handler2(ws, http_ver, msg);
+ if (ws->selected_auth->type & AUTH_TYPE_GSSAPI)
+ return basic_auth_handler(ws, http_ver, msg);
+ else
+ return get_auth_handler2(ws, http_ver, msg);
} else if (ret < 0) {
oclog(ws, LOG_ERR, "failed authentication for '%s'",
ws->username);
@@ -1244,7 +1335,7 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
oclog(ws, LOG_HTTP_DEBUG, "user '%s' obtained cookie", ws->username);
ws->auth_state = S_AUTH_COOKIE;
- return post_common_handler(ws, http_ver);
+ return post_common_handler(ws, http_ver, msg);
ask_auth:
diff --git a/src/worker-http.c b/src/worker-http.c
index 826bbae0..bf501475 100644
--- a/src/worker-http.c
+++ b/src/worker-http.c
@@ -204,6 +204,16 @@ void header_value_check(struct worker_st *ws, struct http_req_st *req)
case HEADER_DEVICE_TYPE:
req->is_mobile = 1;
break;
+ case HEADER_NEED_SPNEGO:
+ ws_switch_auth_to(ws, AUTH_TYPE_GSSAPI);
+ break;
+ case HEADER_AUTHORIZATION:
+ if (req->authorization != NULL)
+ talloc_free(req->authorization);
+ req->authorization = value;
+ req->authorization_size = value_length;
+ value = NULL;
+ break;
case HEADER_USER_AGENT:
if (value_length + 1 > MAX_AGENT_NAME) {
memcpy(req->user_agent, value, MAX_AGENT_NAME-1);
diff --git a/src/worker-vpn.c b/src/worker-vpn.c
index 2e0c9cfa..d487a8b1 100644
--- a/src/worker-vpn.c
+++ b/src/worker-vpn.c
@@ -355,6 +355,10 @@ void vpn_server(struct worker_st *ws)
memset(&settings, 0, sizeof(settings));
+ ws->selected_auth = &ws->config->auth[0];
+ if (ws->cert_auth_ok)
+ ws_switch_auth_to(ws, AUTH_TYPE_CERTIFICATE);
+
settings.on_url = http_url_cb;
settings.on_header_field = http_header_field_cb;
settings.on_header_value = http_header_value_cb;
diff --git a/src/worker.h b/src/worker.h
index 158df6c6..df8a657c 100644
--- a/src/worker.h
+++ b/src/worker.h
@@ -58,7 +58,9 @@ enum {
HEADER_FULL_IPV6,
HEADER_USER_AGENT,
HEADER_CSTP_ENCODING,
- HEADER_DTLS_ENCODING
+ HEADER_DTLS_ENCODING,
+ HEADER_NEED_SPNEGO,
+ HEADER_AUTHORIZATION
};
enum {
@@ -130,6 +132,9 @@ struct http_req_st {
unsigned no_ipv4;
unsigned no_ipv6;
+
+ char *authorization;
+ unsigned authorization_size;
};
typedef struct dtls_transport_ptr {
@@ -143,6 +148,7 @@ typedef struct worker_st {
gnutls_session_t session;
gnutls_session_t dtls_session;
+ auth_struct_st *selected_auth;
const compression_method_st *dtls_selected_comp;
const compression_method_st *cstp_selected_comp;
@@ -309,6 +315,7 @@ int send_tun_mtu(worker_st *ws, unsigned int mtu);
int handle_worker_commands(struct worker_st *ws);
int disable_system_calls(struct worker_st *ws);
void ocsigaltstack(struct worker_st *ws);
+int ws_switch_auth_to(struct worker_st *ws, unsigned auth);
int connect_to_secmod(worker_st * ws);
inline static