mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-02-10 16:57:00 +08:00
Added GSSAPI as an additional password auth mechanism
That also adds the ability to support an OR composition of multiple authentication methods. That is using the 'enable-auth' config option.
This commit is contained in:
21
configure.ac
21
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}
|
||||
|
||||
@@ -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
|
||||
|
||||
268
src/auth/gssapi.c
Normal file
268
src/auth/gssapi.c
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <vpn.h>
|
||||
#include <c-ctype.h>
|
||||
#include "gssapi.h"
|
||||
#include "auth/common.h"
|
||||
#include <gssapi/gssapi.h>
|
||||
#include <gssapi/gssapi_ext.h>
|
||||
#include <gssapi/gssapi_krb5.h>
|
||||
#include <gl/base64.h>
|
||||
|
||||
#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
|
||||
28
src/auth/gssapi.h
Normal file
28
src/auth/gssapi.h
Normal file
@@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
#ifndef GSSAPI_H
|
||||
#define GSSAPI_H
|
||||
|
||||
#include <sec-mod-auth.h>
|
||||
|
||||
extern const struct auth_mod_st gssapi_auth_funcs;
|
||||
|
||||
#endif
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
273
src/config.c
273
src/config.c
@@ -34,6 +34,7 @@
|
||||
#include <auth/pam.h>
|
||||
#include <auth/radius.h>
|
||||
#include <auth/plain.h>
|
||||
#include <auth/gssapi.h>
|
||||
#include <sec-mod-sup-config.h>
|
||||
|
||||
#include <vpn.h>
|
||||
@@ -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;j<auth_size;j++) {
|
||||
found = 0;
|
||||
for (i=0;i<sizeof(avail_auth_types)/sizeof(avail_auth_types[0]);i++) {
|
||||
if (c_strncasecmp(auth[j], avail_auth_types[i].name, avail_auth_types[i].name_size) == 0) {
|
||||
if (avail_auth_types[i].get_brackets_string)
|
||||
config->auth[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;j<auth_size;j++) {
|
||||
found = 0;
|
||||
for (i=0;i<sizeof(avail_auth_types)/sizeof(avail_auth_types[0]);i++) {
|
||||
if (c_strncasecmp(auth[j], avail_auth_types[i].name, avail_auth_types[i].name_size) == 0) {
|
||||
if (avail_auth_types[i].get_brackets_string)
|
||||
config->auth[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;j<auth_size;j++) {
|
||||
if (c_strncasecmp(auth[j], "pam", 3) == 0) {
|
||||
config->auth_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;i<config->auth_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;i<config->auth_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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -51,24 +51,18 @@
|
||||
#include <vpn.h>
|
||||
#include <sec-mod-sup-config.h>
|
||||
|
||||
static const struct auth_mod_st *module = NULL;
|
||||
#ifdef HAVE_GSSAPI
|
||||
# include <gssapi/gssapi.h>
|
||||
# include <gssapi/gssapi_ext.h>
|
||||
#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;i<config->auth_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;i<sec->config->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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
17
src/vpn.h
17
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;
|
||||
|
||||
@@ -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;i<ws->config->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;i<ws->cert_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, "<?xml", 5) != 0) {
|
||||
xml = 1;
|
||||
if (xml_field) {
|
||||
@@ -1045,6 +1070,42 @@ int parse_reply(worker_st * ws, char *body, unsigned body_length,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int basic_auth_handler(worker_st * ws, unsigned http_ver, const char *msg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
cstp_cork(ws);
|
||||
ret = cstp_printf(ws, "HTTP/1.%u 401 Unauthorized\r\n", http_ver);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
if (msg == NULL) {
|
||||
ret = cstp_puts(ws, "WWW-Authenticate: Negotiate\r\n");
|
||||
} else {
|
||||
ret = cstp_printf(ws, "WWW-Authenticate: Negotiate %s\r\n", msg);
|
||||
}
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#define USERNAME_FIELD "username"
|
||||
#define PASSWORD_FIELD "password"
|
||||
#define GROUPNAME_FIELD "group%5flist"
|
||||
@@ -1101,7 +1162,20 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
|
||||
}
|
||||
talloc_free(groupname);
|
||||
|
||||
if (ws->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:
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user