kdc: support for GSS-API pre-authentication
Add support for GSS-API pre-authentication to the KDC, using a simplified variation of draft-perez-krb-wg-gss-preauth-02 that encodes GSS-API context tokens directly in PADATA, and uses FX-COOKIE for state management. More information on the protocol and implementation may be found in lib/gssapi/preauth/README.md.
This commit is contained in:
@@ -117,6 +117,7 @@ libkdc_la_SOURCES = \
|
||||
csr_authorizer.c \
|
||||
process.c \
|
||||
windc.c \
|
||||
gss_preauth.c \
|
||||
rx.h
|
||||
|
||||
KDC_PROTOS = $(srcdir)/kdc-protos.h $(srcdir)/kdc-private.h
|
||||
@@ -185,6 +186,7 @@ libkdc_la_LIBADD = \
|
||||
$(LIB_pkinit) \
|
||||
$(top_builddir)/lib/hdb/libhdb.la \
|
||||
$(top_builddir)/lib/krb5/libkrb5.la \
|
||||
$(top_builddir)/lib/gssapi/libgssapi.la \
|
||||
$(LIB_kdb) \
|
||||
$(top_builddir)/lib/ntlm/libheimntlm.la \
|
||||
$(LIB_hcrypto) \
|
||||
|
||||
@@ -107,7 +107,8 @@ LIBKDC_OBJS=\
|
||||
$(OBJ)\token_validator.obj \
|
||||
$(OBJ)\csr_authorizer.obj \
|
||||
$(OBJ)\process.obj \
|
||||
$(OBJ)\windc.obj
|
||||
$(OBJ)\windc.obj \
|
||||
$(OBJ)\gss_preauth.obj
|
||||
|
||||
LIBKDC_LIBS=\
|
||||
$(LIBHDB) \
|
||||
@@ -146,6 +147,7 @@ libkdc_la_SOURCES = \
|
||||
csr_authorizer.c \
|
||||
process.c \
|
||||
windc.c \
|
||||
gss_preauth.c \
|
||||
rx.h
|
||||
|
||||
$(OBJ)\kdc-protos.h: $(libkdc_la_SOURCES)
|
||||
|
||||
@@ -74,6 +74,7 @@ krb5_kdc_get_config(krb5_context context, krb5_kdc_configuration **config)
|
||||
{
|
||||
static heim_base_once_t load_kdc_plugins = HEIM_BASE_ONCE_INIT;
|
||||
krb5_kdc_configuration *c;
|
||||
krb5_error_code ret;
|
||||
|
||||
heim_base_once_f(&load_kdc_plugins, context, load_kdc_plugins_once);
|
||||
|
||||
@@ -329,6 +330,36 @@ krb5_kdc_get_config(krb5_context context, krb5_kdc_configuration **config)
|
||||
"synthetic_clients_max_renew",
|
||||
NULL);
|
||||
|
||||
c->enable_gss_preauth =
|
||||
krb5_config_get_bool_default(context, NULL,
|
||||
c->enable_gss_preauth,
|
||||
"kdc",
|
||||
"enable_gss_preauth", NULL);
|
||||
|
||||
c->enable_gss_auth_data =
|
||||
krb5_config_get_bool_default(context, NULL,
|
||||
c->enable_gss_auth_data,
|
||||
"kdc",
|
||||
"enable_gss_auth_data", NULL);
|
||||
|
||||
ret = _kdc_gss_get_mechanism_config(context, "kdc",
|
||||
"gss_mechanisms_allowed",
|
||||
&c->gss_mechanisms_allowed);
|
||||
if (ret) {
|
||||
free(c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = _kdc_gss_get_mechanism_config(context, "kdc",
|
||||
"gss_cross_realm_mechanisms_allowed",
|
||||
&c->gss_cross_realm_mechanisms_allowed);
|
||||
if (ret) {
|
||||
OM_uint32 minor;
|
||||
gss_release_oid_set(&minor, &c->gss_mechanisms_allowed);
|
||||
free(c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*config = c;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include <getarg.h>
|
||||
|
||||
typedef struct pk_client_params pk_client_params;
|
||||
typedef struct gss_client_params gss_client_params;
|
||||
struct DigestREQ;
|
||||
struct Kx509Request;
|
||||
|
||||
|
||||
65
kdc/fast.c
65
kdc/fast.c
@@ -257,6 +257,20 @@ _kdc_fast_mk_error(astgs_request_t r,
|
||||
|
||||
krb5_data_zero(&e_data);
|
||||
|
||||
if (armor_crypto || (r && r->fast.fast_state.len)) {
|
||||
if (r)
|
||||
ret = fast_add_cookie(r, error_method);
|
||||
else
|
||||
ret = krb5_padata_add(context, error_method,
|
||||
KRB5_PADATA_FX_COOKIE,
|
||||
NULL, 0);
|
||||
if (ret) {
|
||||
kdc_log(r->context, r->config, 1, "failed to add fast cookie with: %d", ret);
|
||||
free_METHOD_DATA(error_method);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (armor_crypto) {
|
||||
PA_FX_FAST_REPLY fxfastrep;
|
||||
KrbFastResponse fastrep;
|
||||
@@ -292,18 +306,6 @@ _kdc_fast_mk_error(astgs_request_t r,
|
||||
error_server = NULL;
|
||||
e_text = NULL;
|
||||
|
||||
if (r)
|
||||
ret = fast_add_cookie(r, error_method);
|
||||
else
|
||||
ret = krb5_padata_add(context, error_method,
|
||||
KRB5_PADATA_FX_COOKIE,
|
||||
NULL, 0);
|
||||
if (ret) {
|
||||
kdc_log(r->context, r->config, 1, "failed to add fast cookie with: %d", ret);
|
||||
free_METHOD_DATA(error_method);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = _kdc_fast_mk_response(context, armor_crypto,
|
||||
error_method, NULL, NULL,
|
||||
req_body->nonce, &e_data);
|
||||
@@ -342,8 +344,8 @@ _kdc_fast_mk_error(astgs_request_t r,
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_kdc_fast_unwrap_request(astgs_request_t r)
|
||||
static krb5_error_code
|
||||
fast_unwrap_request(astgs_request_t r)
|
||||
{
|
||||
krb5_principal armor_server = NULL;
|
||||
hdb_entry_ex *armor_user = NULL;
|
||||
@@ -362,17 +364,6 @@ _kdc_fast_unwrap_request(astgs_request_t r)
|
||||
const PA_DATA *pa;
|
||||
int i = 0;
|
||||
|
||||
/*
|
||||
* First look for FX_COOKIE and and process it
|
||||
*/
|
||||
pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_COOKIE);
|
||||
if (pa) {
|
||||
ret = fast_parse_cookie(r, pa);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST);
|
||||
if (pa == NULL)
|
||||
return 0;
|
||||
@@ -562,6 +553,30 @@ _kdc_fast_unwrap_request(astgs_request_t r)
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_kdc_fast_unwrap_request(astgs_request_t r)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
const PA_DATA *pa;
|
||||
int i = 0;
|
||||
|
||||
ret = fast_unwrap_request(r);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Non-FAST mechanisms may use FX-COOKIE to manage state.
|
||||
*/
|
||||
pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_COOKIE);
|
||||
if (pa) {
|
||||
ret = fast_parse_cookie(r, pa);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
_kdc_free_fast_state(KDCFastState *state)
|
||||
{
|
||||
|
||||
804
kdc/gss_preauth.c
Normal file
804
kdc/gss_preauth.c
Normal file
@@ -0,0 +1,804 @@
|
||||
/*
|
||||
* Copyright (c) 2021, PADL Software Pty Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2019 Kungliga Tekniska Högskolan
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of PADL Software nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "kdc_locl.h"
|
||||
|
||||
#include <gssapi/gssapi.h>
|
||||
#include <gssapi_mech.h>
|
||||
#include "../lib/gssapi/preauth/pa-private.h"
|
||||
|
||||
#include "gss_preauth_authorizer_plugin.h"
|
||||
|
||||
struct gss_client_params {
|
||||
gss_ctx_id_t context_handle;
|
||||
gss_name_t initiator_name;
|
||||
gss_OID mech_type;
|
||||
gss_buffer_desc output_token;
|
||||
OM_uint32 flags;
|
||||
OM_uint32 lifetime;
|
||||
};
|
||||
|
||||
static void
|
||||
pa_gss_display_status(astgs_request_t r,
|
||||
OM_uint32 major,
|
||||
OM_uint32 minor,
|
||||
gss_client_params *gcp,
|
||||
const char *msg);
|
||||
|
||||
static void
|
||||
pa_gss_display_name(gss_name_t name,
|
||||
gss_buffer_t namebuf,
|
||||
gss_const_buffer_t *namebuf_p);
|
||||
|
||||
/*
|
||||
* Deserialize a GSS-API security context from the FAST cookie.
|
||||
*/
|
||||
static krb5_error_code
|
||||
pa_gss_get_context_state(astgs_request_t r,
|
||||
gss_client_params *gcp)
|
||||
{
|
||||
int idx = 0;
|
||||
PA_DATA *fast_pa;
|
||||
|
||||
fast_pa = krb5_find_padata(r->fast.fast_state.val,
|
||||
r->fast.fast_state.len,
|
||||
KRB5_PADATA_GSS, &idx);
|
||||
if (fast_pa) {
|
||||
gss_buffer_desc sec_context_token;
|
||||
OM_uint32 major, minor;
|
||||
|
||||
_krb5_gss_data_to_buffer(&fast_pa->padata_value, &sec_context_token);
|
||||
major = gss_import_sec_context(&minor, &sec_context_token,
|
||||
&gcp->context_handle);
|
||||
if (GSS_ERROR(major))
|
||||
pa_gss_display_status(r, major, minor, gcp,
|
||||
"Failed to import GSS pre-authentication context");
|
||||
|
||||
return _krb5_gss_map_error(major, minor);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Serialize a GSS-API security context into a FAST cookie.
|
||||
*/
|
||||
static krb5_error_code
|
||||
pa_gss_set_context_state(astgs_request_t r,
|
||||
gss_client_params *gcp)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
PA_DATA *fast_pa;
|
||||
int idx = 0;
|
||||
|
||||
OM_uint32 major, minor;
|
||||
gss_buffer_desc sec_context_token = GSS_C_EMPTY_BUFFER;
|
||||
|
||||
major = gss_export_sec_context(&minor, &gcp->context_handle,
|
||||
&sec_context_token);
|
||||
if (GSS_ERROR(major)) {
|
||||
pa_gss_display_status(r, major, minor, gcp,
|
||||
"Failed to export GSS pre-authentication context");
|
||||
return _krb5_gss_map_error(major, minor);
|
||||
}
|
||||
|
||||
fast_pa = krb5_find_padata(r->fast.fast_state.val,
|
||||
r->fast.fast_state.len,
|
||||
KRB5_PADATA_GSS, &idx);
|
||||
if (fast_pa) {
|
||||
krb5_data_free(&fast_pa->padata_value);
|
||||
_krb5_gss_buffer_to_data(&sec_context_token, &fast_pa->padata_value);
|
||||
} else {
|
||||
ret = krb5_padata_add(r->context,
|
||||
&r->fast.fast_state,
|
||||
KRB5_PADATA_GSS,
|
||||
sec_context_token.value,
|
||||
sec_context_token.length);
|
||||
if (ret) {
|
||||
gss_release_buffer(&minor, &sec_context_token);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
pa_gss_acquire_acceptor_cred(astgs_request_t r,
|
||||
gss_client_params *gcp,
|
||||
gss_cred_id_t *cred)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
|
||||
OM_uint32 major, minor;
|
||||
gss_name_t target_name = GSS_C_NO_NAME;
|
||||
gss_buffer_desc display_name = GSS_C_EMPTY_BUFFER;
|
||||
gss_const_buffer_t display_name_p;
|
||||
|
||||
*cred = GSS_C_NO_CREDENTIAL;
|
||||
|
||||
ret = _krb5_gss_pa_unparse_name(r->context, r->server_princ, &target_name);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pa_gss_display_name(target_name, &display_name, &display_name_p);
|
||||
|
||||
kdc_log(r->context, r->config, 4,
|
||||
"Acquiring GSS acceptor credential for %.*s",
|
||||
(int)display_name_p->length, (char *)display_name_p->value);
|
||||
|
||||
major = gss_acquire_cred(&minor, target_name, GSS_C_INDEFINITE,
|
||||
r->config->gss_mechanisms_allowed,
|
||||
GSS_C_ACCEPT, cred, NULL, NULL);
|
||||
ret = _krb5_gss_map_error(major, minor);
|
||||
|
||||
if (ret)
|
||||
pa_gss_display_status(r, major, minor, gcp,
|
||||
"Failed to acquire GSS acceptor credential");
|
||||
|
||||
gss_release_buffer(&minor, &display_name);
|
||||
gss_release_name(&minor, &target_name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_kdc_gss_rd_padata(astgs_request_t r,
|
||||
const PA_DATA *pa,
|
||||
gss_client_params **pgcp,
|
||||
int *open)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
size_t size;
|
||||
|
||||
OM_uint32 major, minor;
|
||||
gss_client_params *gcp = NULL;
|
||||
gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
|
||||
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
|
||||
struct gss_channel_bindings_struct cb;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
|
||||
*pgcp = NULL;
|
||||
|
||||
if (!r->config->enable_gss_preauth) {
|
||||
ret = KRB5KDC_ERR_POLICY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (pa->padata_value.length == 0) {
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
gcp = calloc(1, sizeof(*gcp));
|
||||
if (gcp == NULL) {
|
||||
ret = krb5_enomem(r->context);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = pa_gss_get_context_state(r, gcp);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = pa_gss_acquire_acceptor_cred(r, gcp, &cred);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
_krb5_gss_data_to_buffer(&pa->padata_value, &input_token);
|
||||
|
||||
ASN1_MALLOC_ENCODE(KDC_REQ_BODY, cb.application_data.value,
|
||||
cb.application_data.length, &r->req.req_body,
|
||||
&size, ret);
|
||||
heim_assert(ret || size == cb.application_data.length,
|
||||
"internal asn1 encoder error");
|
||||
|
||||
major = gss_accept_sec_context(&minor,
|
||||
&gcp->context_handle,
|
||||
cred,
|
||||
&input_token,
|
||||
&cb,
|
||||
&gcp->initiator_name,
|
||||
&gcp->mech_type,
|
||||
&gcp->output_token,
|
||||
&gcp->flags,
|
||||
&gcp->lifetime,
|
||||
NULL); /* delegated_cred_handle */
|
||||
if (GSS_ERROR(major)) {
|
||||
pa_gss_display_status(r, major, minor, gcp,
|
||||
"Failed to accept GSS security context");
|
||||
ret = _krb5_gss_map_error(major, minor);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((gcp->flags & GSS_C_ANON_FLAG) && !_kdc_is_anon_request(&r->req)) {
|
||||
kdc_log(r->context, r->config, 2,
|
||||
"Anonymous GSS pre-authentication request w/o anonymous flag");
|
||||
ret = KRB5KDC_ERR_BADOPTION;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*open = (major == GSS_S_COMPLETE);
|
||||
|
||||
out:
|
||||
gss_release_cred(&minor, &cred);
|
||||
gss_release_buffer(&minor, &cb.application_data);
|
||||
|
||||
if (ret == 0)
|
||||
*pgcp = gcp;
|
||||
else
|
||||
_kdc_gss_free_client_param(r, gcp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_timestamp
|
||||
_kdc_gss_endtime(astgs_request_t r,
|
||||
gss_client_params *gcp)
|
||||
{
|
||||
krb5_timestamp endtime;
|
||||
|
||||
if (gcp->lifetime == GSS_C_INDEFINITE)
|
||||
endtime = 0;
|
||||
else
|
||||
endtime = kdc_time + gcp->lifetime;
|
||||
|
||||
kdc_log(r->context, r->config, 10,
|
||||
"GSS pre-authentication endtime is %ld", endtime);
|
||||
|
||||
return endtime;
|
||||
}
|
||||
|
||||
struct pa_gss_plugin_ctx {
|
||||
astgs_request_t r;
|
||||
struct gss_client_params *gcp;
|
||||
krb5_boolean authorized;
|
||||
krb5_principal initiator_princ;
|
||||
};
|
||||
|
||||
static krb5_error_code
|
||||
pa_gss_authorize_cb(krb5_context context,
|
||||
const void *plug,
|
||||
void *plugctx,
|
||||
void *userctx)
|
||||
{
|
||||
const krb5plugin_gss_preauth_authorizer_ftable *authorizer = plug;
|
||||
struct pa_gss_plugin_ctx *pa_gss_plugin_ctx = userctx;
|
||||
|
||||
return authorizer->authorize(plugctx, context,
|
||||
&pa_gss_plugin_ctx->r->req,
|
||||
pa_gss_plugin_ctx->r->client_princ,
|
||||
pa_gss_plugin_ctx->r->client,
|
||||
pa_gss_plugin_ctx->gcp->initiator_name,
|
||||
pa_gss_plugin_ctx->gcp->mech_type,
|
||||
pa_gss_plugin_ctx->gcp->flags,
|
||||
&pa_gss_plugin_ctx->authorized,
|
||||
&pa_gss_plugin_ctx->initiator_princ);
|
||||
}
|
||||
|
||||
static const char *plugin_deps[] = {
|
||||
"kdc",
|
||||
"hdb",
|
||||
"gssapi",
|
||||
"krb5",
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct heim_plugin_data
|
||||
gss_preauth_authorizer_data = {
|
||||
"kdc",
|
||||
KDC_GSS_PREAUTH_AUTHORIZER,
|
||||
KDC_GSS_PREAUTH_AUTHORIZER_VERSION_0,
|
||||
plugin_deps,
|
||||
kdc_get_instance
|
||||
};
|
||||
|
||||
static krb5_error_code
|
||||
pa_gss_authorize_plugin(astgs_request_t r,
|
||||
struct gss_client_params *gcp,
|
||||
gss_const_buffer_t display_name,
|
||||
krb5_boolean *authorized,
|
||||
krb5_principal *initiator_princ)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
struct pa_gss_plugin_ctx ctx;
|
||||
|
||||
ctx.r = r;
|
||||
ctx.gcp = gcp;
|
||||
ctx.authorized = 0;
|
||||
ctx.initiator_princ = NULL;
|
||||
|
||||
krb5_clear_error_message(r->context);
|
||||
ret = _krb5_plugin_run_f(r->context, &gss_preauth_authorizer_data,
|
||||
0, &ctx, pa_gss_authorize_cb);
|
||||
|
||||
if (ret != KRB5_PLUGIN_NO_HANDLE) {
|
||||
const char *msg = krb5_get_error_message(r->context, ret);
|
||||
|
||||
kdc_log(r->context, r->config, 7,
|
||||
"GSS authz plugin %sauthorize%s %s initiator %.*s: %s",
|
||||
ctx.authorized ? "" : "did not " ,
|
||||
ctx.authorized ? "d" : "",
|
||||
gss_oid_to_name(gcp->mech_type),
|
||||
(int)display_name->length, (char *)display_name->value,
|
||||
msg);
|
||||
krb5_free_error_message(r->context, msg);
|
||||
}
|
||||
|
||||
*authorized = ctx.authorized;
|
||||
*initiator_princ = ctx.initiator_princ;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
pa_gss_authorize_default(astgs_request_t r,
|
||||
struct gss_client_params *gcp,
|
||||
gss_const_buffer_t display_name,
|
||||
krb5_boolean *authorized,
|
||||
krb5_principal *initiator_princ)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_principal principal;
|
||||
krb5_const_realm realm = r->server->entry.principal->realm;
|
||||
int flags = 0, cross_realm_allowed = 0, unauth_anon;
|
||||
|
||||
/*
|
||||
* gss_cross_realm_mechanisms_allowed is a list of GSS-API mechanisms
|
||||
* that are allowed to map directly to Kerberos principals in any
|
||||
* realm. If the authenticating mechanism is not on the list, then
|
||||
* the initiator will be mapped to an enterprise principal in the
|
||||
* service realm. This is useful to stop synthetic principals in
|
||||
* foreign realms being conflated with true cross-realm principals.
|
||||
*/
|
||||
if (r->config->gss_cross_realm_mechanisms_allowed) {
|
||||
OM_uint32 minor;
|
||||
|
||||
gss_test_oid_set_member(&minor, gcp->mech_type,
|
||||
r->config->gss_cross_realm_mechanisms_allowed,
|
||||
&cross_realm_allowed);
|
||||
}
|
||||
|
||||
kdc_log(r->context, r->config, 10,
|
||||
"Initiator %.*s will be mapped to %s",
|
||||
(int)display_name->length, (char *)display_name->value,
|
||||
cross_realm_allowed ? "nt-principal" : "nt-enterprise-principal");
|
||||
|
||||
if (!cross_realm_allowed)
|
||||
flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE | KRB5_PRINCIPAL_PARSE_NO_REALM;
|
||||
|
||||
ret = _krb5_gss_pa_parse_name(r->context, gcp->initiator_name,
|
||||
flags, &principal);
|
||||
if (ret) {
|
||||
const char *msg = krb5_get_error_message(r->context, ret);
|
||||
|
||||
kdc_log(r->context, r->config, 2,
|
||||
"Failed to parse %s initiator name %.*s: %s",
|
||||
gss_oid_to_name(gcp->mech_type),
|
||||
(int)display_name->length, (char *)display_name->value, msg);
|
||||
krb5_free_error_message(r->context, msg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* GSS_C_ANON_FLAG indicates the client requested anonymous authentication
|
||||
* (it is validated against the request-anonymous flag).
|
||||
*
|
||||
* _kdc_is_anonymous_pkinit() returns TRUE if the principal contains both
|
||||
* the well known anonymous name and realm.
|
||||
*/
|
||||
unauth_anon = (gcp->flags & GSS_C_ANON_FLAG) &&
|
||||
_kdc_is_anonymous_pkinit(r->context, principal);
|
||||
|
||||
/*
|
||||
* Always use the anonymous entry created in our HDB, i.e. with the local
|
||||
* realm, for authorizing anonymous requests. This matches PKINIT behavior
|
||||
* as anonymous PKINIT requests include the KDC realm in the request.
|
||||
*/
|
||||
if (unauth_anon || (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE)) {
|
||||
ret = krb5_principal_set_realm(r->context, principal, realm);
|
||||
if (ret) {
|
||||
krb5_free_principal(r->context, principal);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (unauth_anon) {
|
||||
/*
|
||||
* Special case to avoid changing _kdc_as_rep(). If the initiator is
|
||||
* the unauthenticated anonymous principal, r->client_princ also needs
|
||||
* to be set in order to force the AS-REP realm to be set to the well-
|
||||
* known anonymous identity. This is because (unlike anonymous PKINIT)
|
||||
* we only require the anonymous flag, not the anonymous name, in the
|
||||
* client AS-REQ.
|
||||
*/
|
||||
krb5_principal anon_princ;
|
||||
|
||||
ret = krb5_copy_principal(r->context, principal, &anon_princ);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
krb5_free_principal(r->context, r->client_princ);
|
||||
r->client_princ = anon_princ;
|
||||
}
|
||||
|
||||
*authorized = TRUE;
|
||||
*initiator_princ = principal;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_kdc_gss_check_client(astgs_request_t r,
|
||||
gss_client_params *gcp,
|
||||
char **client_name)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_principal initiator_princ = NULL;
|
||||
hdb_entry_ex *initiator = NULL;
|
||||
krb5_boolean authorized = FALSE;
|
||||
|
||||
OM_uint32 minor;
|
||||
gss_buffer_desc display_name = GSS_C_EMPTY_BUFFER;
|
||||
gss_const_buffer_t display_name_p;
|
||||
|
||||
*client_name = NULL;
|
||||
|
||||
pa_gss_display_name(gcp->initiator_name, &display_name, &display_name_p);
|
||||
|
||||
/*
|
||||
* If no plugins handled the authorization request, then all clients
|
||||
* are authorized as the directly corresponding Kerberos principal.
|
||||
*/
|
||||
ret = pa_gss_authorize_plugin(r, gcp, display_name_p,
|
||||
&authorized, &initiator_princ);
|
||||
if (ret == KRB5_PLUGIN_NO_HANDLE)
|
||||
ret = pa_gss_authorize_default(r, gcp, display_name_p,
|
||||
&authorized, &initiator_princ);
|
||||
if (ret == 0 && !authorized)
|
||||
ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_unparse_name(r->context, initiator_princ, client_name);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
kdc_log(r->context, r->config, 4,
|
||||
"Mapped GSS %s initiator %.*s to principal %s",
|
||||
gss_oid_to_name(gcp->mech_type),
|
||||
(int)display_name_p->length, (char *)display_name_p->value,
|
||||
*client_name);
|
||||
|
||||
ret = _kdc_db_fetch(r->context,
|
||||
r->config,
|
||||
initiator_princ,
|
||||
HDB_F_FOR_AS_REQ | HDB_F_GET_CLIENT |
|
||||
HDB_F_CANON | HDB_F_SYNTHETIC_OK,
|
||||
NULL,
|
||||
&r->clientdb,
|
||||
&initiator);
|
||||
if (ret) {
|
||||
const char *msg = krb5_get_error_message(r->context, ret);
|
||||
|
||||
kdc_log(r->context, r->config, 4, "UNKNOWN -- %s: %s",
|
||||
*client_name, msg);
|
||||
krb5_free_error_message(r->context, msg);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the AS-REQ client name was the well-known federated name, then
|
||||
* replace the client name with the initiator name. Otherwise, the
|
||||
* two principals must match, noting that GSS pre-authentication is
|
||||
* for authentication, not general purpose impersonation.
|
||||
*/
|
||||
if (krb5_principal_is_federated(r->context, r->client->entry.principal)) {
|
||||
initiator->entry.flags.force_canonicalize = 1;
|
||||
|
||||
_kdc_free_ent(r->context, r->client);
|
||||
r->client = initiator;
|
||||
initiator = NULL;
|
||||
} else if (!krb5_principal_compare(r->context,
|
||||
r->client->entry.principal,
|
||||
initiator->entry.principal)) {
|
||||
kdc_log(r->context, r->config, 2,
|
||||
"GSS %s initiator %.*s does not match principal %s",
|
||||
gss_oid_to_name(gcp->mech_type),
|
||||
(int)display_name_p->length, (char *)display_name_p->value,
|
||||
r->cname);
|
||||
ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
krb5_free_principal(r->context, initiator_princ);
|
||||
if (initiator)
|
||||
_kdc_free_ent(r->context, initiator);
|
||||
gss_release_buffer(&minor, &display_name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_kdc_gss_mk_pa_reply(astgs_request_t r,
|
||||
gss_client_params *gcp)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
const KDC_REQ *req = &r->req;
|
||||
|
||||
OM_uint32 major, minor;
|
||||
int open;
|
||||
|
||||
major = gss_inquire_context(&minor,
|
||||
gcp->context_handle,
|
||||
NULL, /* initiator_name */
|
||||
NULL, /* target_name */
|
||||
NULL, /* lifetime_req */
|
||||
NULL, /* mech_type */
|
||||
NULL, /* ctx_flags */
|
||||
NULL, /* locally_initiated */
|
||||
&open);
|
||||
if (GSS_ERROR(major)) {
|
||||
pa_gss_display_status(r, major, minor, gcp,
|
||||
"Failed to inquire GSS context");
|
||||
ret = _krb5_gss_map_error(major, minor);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (open) {
|
||||
krb5_enctype enctype;
|
||||
uint32_t kfe = 0;
|
||||
krb5_keyblock *reply_key = NULL;
|
||||
|
||||
if (krb5_principal_is_krbtgt(r->context, r->server_princ))
|
||||
kfe |= KFE_IS_TGS;
|
||||
|
||||
ret = _kdc_find_etype(r, kfe, req->req_body.etype.val,
|
||||
req->req_body.etype.len, &enctype, NULL, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = _krb5_gss_pa_derive_key(r->context, gcp->context_handle,
|
||||
req->req_body.nonce,
|
||||
enctype, &reply_key);
|
||||
if (ret) {
|
||||
kdc_log(r->context, r->config, 10,
|
||||
"Failed to derive GSS reply key: %d", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
krb5_free_keyblock_contents(r->context, &r->reply_key);
|
||||
r->reply_key = *reply_key;
|
||||
free(reply_key);
|
||||
} else {
|
||||
ret = pa_gss_set_context_state(r, gcp);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = krb5_padata_add(r->context, &r->outpadata, KRB5_PADATA_GSS,
|
||||
gcp->output_token.value, gcp->output_token.length);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* token is now owned by outpadata */
|
||||
gcp->output_token.length = 0;
|
||||
gcp->output_token.value = NULL;
|
||||
|
||||
if (!open)
|
||||
ret = KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_kdc_gss_mk_composite_name_ad(astgs_request_t r,
|
||||
gss_client_params *gcp)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_data data;
|
||||
|
||||
OM_uint32 major, minor;
|
||||
gss_buffer_desc namebuf = GSS_C_EMPTY_BUFFER;
|
||||
|
||||
if (!r->config->enable_gss_auth_data || (gcp->flags & GSS_C_ANON_FLAG))
|
||||
return 0;
|
||||
|
||||
major = gss_export_name_composite(&minor, gcp->initiator_name, &namebuf);
|
||||
if (major == GSS_S_COMPLETE) {
|
||||
_krb5_gss_buffer_to_data(&namebuf, &data);
|
||||
|
||||
ret = _kdc_tkt_add_if_relevant_ad(r->context, &r->et,
|
||||
KRB5_AUTHDATA_GSS_COMPOSITE_NAME,
|
||||
&data);
|
||||
} else if (major != GSS_S_UNAVAILABLE)
|
||||
ret = _krb5_gss_map_error(major, minor);
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
gss_release_buffer(&minor, &namebuf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
_kdc_gss_free_client_param(astgs_request_t r,
|
||||
gss_client_params *gcp)
|
||||
{
|
||||
OM_uint32 minor;
|
||||
|
||||
if (gcp == NULL)
|
||||
return;
|
||||
|
||||
gss_delete_sec_context(&minor, &gcp->context_handle, GSS_C_NO_BUFFER);
|
||||
gss_release_name(&minor, &gcp->initiator_name);
|
||||
gss_release_buffer(&minor, &gcp->output_token);
|
||||
memset(gcp, 0, sizeof(*gcp));
|
||||
free(gcp);
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_kdc_gss_get_mechanism_config(krb5_context context,
|
||||
const char *section,
|
||||
const char *key,
|
||||
gss_OID_set *oidsp)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char **mechs, **mechp;
|
||||
|
||||
gss_OID_set oids = GSS_C_NO_OID_SET;
|
||||
OM_uint32 major, minor;
|
||||
|
||||
mechs = krb5_config_get_strings(context, NULL, section, key, NULL);
|
||||
if (mechs == NULL)
|
||||
return 0;
|
||||
|
||||
major = gss_create_empty_oid_set(&minor, &oids);
|
||||
if (GSS_ERROR(major)) {
|
||||
krb5_config_free_strings(mechs);
|
||||
return _krb5_gss_map_error(major, minor);
|
||||
}
|
||||
|
||||
for (mechp = mechs; *mechp; mechp++) {
|
||||
gss_OID oid = gss_name_to_oid(*mechp);
|
||||
if (oid == GSS_C_NO_OID)
|
||||
continue;
|
||||
|
||||
major = gss_add_oid_set_member(&minor, oid, &oids);
|
||||
if (GSS_ERROR(major))
|
||||
break;
|
||||
}
|
||||
|
||||
ret = _krb5_gss_map_error(major, minor);
|
||||
if (ret == 0)
|
||||
*oidsp = oids;
|
||||
else
|
||||
gss_release_oid_set(&minor, &oids);
|
||||
|
||||
krb5_config_free_strings(mechs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
pa_gss_display_status(astgs_request_t r,
|
||||
OM_uint32 major,
|
||||
OM_uint32 minor,
|
||||
gss_client_params *gcp,
|
||||
const char *msg)
|
||||
{
|
||||
krb5_error_code ret = _krb5_gss_map_error(major, minor);
|
||||
gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
|
||||
OM_uint32 dmaj, dmin;
|
||||
OM_uint32 more = 0;
|
||||
char *gmmsg = NULL;
|
||||
char *gmsg = NULL;
|
||||
char *s = NULL;
|
||||
|
||||
do {
|
||||
gss_release_buffer(&dmin, &buf);
|
||||
dmaj = gss_display_status(&dmin, major, GSS_C_GSS_CODE, GSS_C_NO_OID,
|
||||
&more, &buf);
|
||||
if (GSS_ERROR(dmaj) ||
|
||||
buf.length >= INT_MAX ||
|
||||
asprintf(&s, "%s%s%.*s", gmsg ? gmsg : "", gmsg ? ": " : "",
|
||||
(int)buf.length, (char *)buf.value) == -1 ||
|
||||
s == NULL) {
|
||||
free(gmsg);
|
||||
gmsg = NULL;
|
||||
break;
|
||||
}
|
||||
gmsg = s;
|
||||
s = NULL;
|
||||
} while (!GSS_ERROR(dmaj) && more);
|
||||
|
||||
if (gcp->mech_type != GSS_C_NO_OID) {
|
||||
do {
|
||||
gss_release_buffer(&dmin, &buf);
|
||||
dmaj = gss_display_status(&dmin, major, GSS_C_MECH_CODE,
|
||||
gcp->mech_type, &more, &buf);
|
||||
if (GSS_ERROR(dmaj) ||
|
||||
asprintf(&s, "%s%s%.*s", gmmsg ? gmmsg : "", gmmsg ? ": " : "",
|
||||
(int)buf.length, (char *)buf.value) == -1 ||
|
||||
s == NULL) {
|
||||
free(gmmsg);
|
||||
gmmsg = NULL;
|
||||
break;
|
||||
}
|
||||
gmmsg = s;
|
||||
s = NULL;
|
||||
} while (!GSS_ERROR(dmaj) && more);
|
||||
}
|
||||
|
||||
if (gmsg == NULL)
|
||||
krb5_set_error_message(r->context, ENOMEM,
|
||||
"Error displaying GSS-API status");
|
||||
else
|
||||
krb5_set_error_message(r->context, ret, "%s%s%s%s", gmsg,
|
||||
gmmsg ? " (" : "", gmmsg ? gmmsg : "",
|
||||
gmmsg ? ")" : "");
|
||||
krb5_prepend_error_message(r->context, ret, "%s", msg);
|
||||
|
||||
kdc_log(r->context, r->config, 1,
|
||||
"%s: %s%s%s%s",
|
||||
msg, gmsg, gmmsg ? " (" : "", gmmsg ? gmmsg : "",
|
||||
gmmsg ? ")" : "");
|
||||
|
||||
free(gmmsg);
|
||||
free(gmsg);
|
||||
}
|
||||
|
||||
static const gss_buffer_desc
|
||||
gss_pa_unknown_display_name = {
|
||||
sizeof("<unknown name>") - 1,
|
||||
"<unknown name>"
|
||||
};
|
||||
|
||||
static void
|
||||
pa_gss_display_name(gss_name_t name,
|
||||
gss_buffer_t namebuf,
|
||||
gss_const_buffer_t *namebuf_p)
|
||||
{
|
||||
OM_uint32 major, minor;
|
||||
|
||||
major = gss_display_name(&minor, name, namebuf, NULL);
|
||||
if (GSS_ERROR(major))
|
||||
*namebuf_p = &gss_pa_unknown_display_name;
|
||||
else
|
||||
*namebuf_p = namebuf;
|
||||
}
|
||||
78
kdc/gss_preauth_authorizer_plugin.h
Normal file
78
kdc/gss_preauth_authorizer_plugin.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef HEIMDAL_KDC_GSS_PREAUTH_AUTHORIZER_PLUGIN_H
|
||||
#define HEIMDAL_KDC_GSS_PREAUTH_AUTHORIZER_PLUGIN_H 1
|
||||
|
||||
#define KDC_GSS_PREAUTH_AUTHORIZER "kdc_gss_preauth_authorizer"
|
||||
#define KDC_GSS_PREAUTH_AUTHORIZER_VERSION_0 0
|
||||
|
||||
#include <krb5.h>
|
||||
#include <gssapi/gssapi.h>
|
||||
|
||||
/*
|
||||
* @param init Plugin initialization function (see krb5-plugin(7))
|
||||
* @param minor_version The plugin minor version number (0)
|
||||
* @param fini Plugin finalization function
|
||||
* @param authorize Plugin name authorization function
|
||||
*
|
||||
* -# plug_ctx, the context value output by the plugin's init function
|
||||
* -# context, a krb5_context
|
||||
* -# req, the AS-REQ request
|
||||
* -# client_name, the requested client principal name
|
||||
* -# client, the requested client HDB entry
|
||||
* -# initiator_name, the authenticated GSS initiator name
|
||||
* -# ret_flags, the flags returned by GSS_Init_sec_context()
|
||||
* -# authorized, indicate whether the initiator was authorized
|
||||
* -# mapped_name, the mapped principal name
|
||||
*
|
||||
* @ingroup krb5_support
|
||||
*/
|
||||
|
||||
typedef struct krb5plugin_gss_preauth_authorizer_ftable_desc {
|
||||
int minor_version;
|
||||
krb5_error_code (KRB5_LIB_CALL *init)(krb5_context, void **);
|
||||
void (KRB5_LIB_CALL *fini)(void *);
|
||||
krb5_error_code (KRB5_LIB_CALL *authorize)(void *, /*plug_ctx*/
|
||||
krb5_context, /*context*/
|
||||
KDC_REQ *, /*req*/
|
||||
krb5_const_principal,/*client_name*/
|
||||
hdb_entry_ex *, /*client*/
|
||||
gss_const_name_t, /*initiator_name*/
|
||||
gss_const_OID, /*mech_type*/
|
||||
OM_uint32, /*ret_flags*/
|
||||
krb5_boolean *, /*authorized*/
|
||||
krb5_principal *); /*mapped_name*/
|
||||
} krb5plugin_gss_preauth_authorizer_ftable;
|
||||
|
||||
#endif /* HEIMDAL_KDC_GSS_PREAUTH_AUTHORIZER_PLUGIN_H */
|
||||
@@ -98,6 +98,7 @@
|
||||
#include <hdb.h>
|
||||
#include <hdb_err.h>
|
||||
#include <der.h>
|
||||
#include <gssapi/gssapi.h>
|
||||
|
||||
#ifndef NO_NTLM
|
||||
#include <heimntlm.h>
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include <hdb.h>
|
||||
#include <krb5.h>
|
||||
#include <kx509_asn1.h>
|
||||
#include <gssapi/gssapi.h>
|
||||
|
||||
enum krb5_kdc_trpolicy {
|
||||
TRPOLICY_ALWAYS_CHECK,
|
||||
@@ -107,6 +108,11 @@ typedef struct krb5_kdc_configuration {
|
||||
int enable_digest;
|
||||
int digests_allowed;
|
||||
|
||||
int enable_gss_preauth;
|
||||
int enable_gss_auth_data;
|
||||
gss_OID_set gss_mechanisms_allowed;
|
||||
gss_OID_set gss_cross_realm_mechanisms_allowed;
|
||||
|
||||
size_t max_datagram_reply_length;
|
||||
|
||||
int enable_kx509;
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "headers.h"
|
||||
|
||||
typedef struct pk_client_params pk_client_params;
|
||||
typedef struct gss_client_params gss_client_params;
|
||||
|
||||
#include <kdc-private.h>
|
||||
|
||||
|
||||
@@ -508,6 +508,56 @@ pa_pkinit_validate(astgs_request_t r, const PA_DATA *pa)
|
||||
|
||||
#endif /* PKINIT */
|
||||
|
||||
static krb5_error_code
|
||||
pa_gss_validate(astgs_request_t r, const PA_DATA *pa)
|
||||
{
|
||||
gss_client_params *gcp = NULL;
|
||||
char *client_name = NULL;
|
||||
krb5_error_code ret;
|
||||
int open;
|
||||
|
||||
ret = _kdc_gss_rd_padata(r, pa, &gcp, &open);
|
||||
if (ret) {
|
||||
_kdc_r_log(r, 4, "Failed to decode GSS PA-DATA -- %s",
|
||||
r->cname);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (open) {
|
||||
ret = _kdc_gss_check_client(r, gcp, &client_name);
|
||||
if (ret) {
|
||||
_kdc_set_e_text(r, "GSS-API client not allowed to "
|
||||
"impersonate principal");
|
||||
goto out;
|
||||
}
|
||||
|
||||
r->pa_endtime = _kdc_gss_endtime(r, gcp);
|
||||
|
||||
_kdc_r_log(r, 4, "GSS pre-authentication succeeded -- %s using %s",
|
||||
r->cname, client_name);
|
||||
free(client_name);
|
||||
|
||||
ret = _kdc_gss_mk_composite_name_ad(r, gcp);
|
||||
if (ret) {
|
||||
_kdc_set_e_text(r, "Failed to build GSS authorization data");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = _kdc_gss_mk_pa_reply(r, gcp);
|
||||
if (ret &&
|
||||
ret != KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED) {
|
||||
_kdc_set_e_text(r, "Failed to build GSS pre-authentication reply");
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (gcp)
|
||||
_kdc_gss_free_client_param(r, gcp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
@@ -919,7 +969,12 @@ static const struct kdc_patypes pat[] = {
|
||||
{ KRB5_PADATA_REQ_ENC_PA_REP , "REQ-ENC-PA-REP", 0, NULL },
|
||||
{ KRB5_PADATA_FX_FAST, "FX-FAST", PA_ANNOUNCE, NULL },
|
||||
{ KRB5_PADATA_FX_ERROR, "FX-ERROR", 0, NULL },
|
||||
{ KRB5_PADATA_FX_COOKIE, "FX-COOKIE", 0, NULL }
|
||||
{ KRB5_PADATA_FX_COOKIE, "FX-COOKIE", 0, NULL },
|
||||
{
|
||||
KRB5_PADATA_GSS , "GSS",
|
||||
PA_ANNOUNCE | PA_SYNTHETIC_OK,
|
||||
pa_gss_validate
|
||||
},
|
||||
};
|
||||
|
||||
static void
|
||||
@@ -1808,6 +1863,20 @@ _kdc_is_anonymous(krb5_context context, krb5_const_principal principal)
|
||||
return krb5_principal_is_anonymous(context, principal, KRB5_ANON_MATCH_ANY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns TRUE if principal is the unauthenticated anonymous identity,
|
||||
* i.e. WELLKNOWN/ANONYMOUS@WELLKNOWN:ANONYMOUS. Unfortunately due to
|
||||
* backwards compatibility logic in krb5_principal_is_anonymous() we
|
||||
* have to use our own implementation.
|
||||
*/
|
||||
|
||||
krb5_boolean
|
||||
_kdc_is_anonymous_pkinit(krb5_context context, krb5_const_principal principal)
|
||||
{
|
||||
return _kdc_is_anonymous(context, principal) &&
|
||||
strcmp(principal->realm, KRB5_ANON_REALM) == 0;
|
||||
}
|
||||
|
||||
static int
|
||||
require_preauth_p(astgs_request_t r)
|
||||
{
|
||||
@@ -2521,7 +2590,9 @@ out:
|
||||
* In case of a non proxy error, build an error message.
|
||||
*/
|
||||
if (ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE && r->reply->length == 0)
|
||||
ret = _kdc_fast_mk_error(r, &error_method,
|
||||
ret = _kdc_fast_mk_error(r,
|
||||
(ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
|
||||
? &r->outpadata : &error_method,
|
||||
r->armor_crypto,
|
||||
&req->req_body,
|
||||
ret, r->e_text,
|
||||
|
||||
@@ -304,7 +304,9 @@ negotiate_get_instance(const char *libname)
|
||||
{
|
||||
if (strcmp(libname, "krb5") == 0)
|
||||
return krb5_get_instance(libname);
|
||||
/* XXX gss_get_instance() doesn't exist :( */
|
||||
else if (strcmp(libname, "gssapi") == 0)
|
||||
return gss_get_instance(libname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -227,6 +227,8 @@ kdc_get_instance(const char *libname)
|
||||
return hdb_get_instance(libname);
|
||||
else if (strcmp(libname, "krb5") == 0)
|
||||
return krb5_get_instance(libname);
|
||||
else if (strcmp(libname, "gssapi") == 0)
|
||||
return gss_get_instance(libname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user