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