aboutsummaryrefslogtreecommitdiff
path: root/lib/gss_preauth
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2023-06-26 22:56:52 +0000
committerCy Schubert <cy@FreeBSD.org>2023-06-26 22:56:52 +0000
commitb6a943f7197af1a5eb6bb028b9b808ec5016e30c (patch)
treecfbb91e940dd89d0e1d46095f43c228d7d079fa0 /lib/gss_preauth
parent6f4e10db3298f6d65e1e646fe52aaafc3682b788 (diff)
Heimdal 7.8.0 does not support OpenSSL 3.0. 7.9.0 will but it hasn't been released yet. We are importing f62e2f278 for its OpenSSL 3.0 support.
Diffstat (limited to 'lib/gss_preauth')
-rw-r--r--lib/gss_preauth/Makefile.am25
-rw-r--r--lib/gss_preauth/NTMakefile70
-rw-r--r--lib/gss_preauth/README.md110
-rw-r--r--lib/gss_preauth/pa_client.c252
-rw-r--r--lib/gss_preauth/pa_common.c255
5 files changed, 712 insertions, 0 deletions
diff --git a/lib/gss_preauth/Makefile.am b/lib/gss_preauth/Makefile.am
new file mode 100644
index 000000000000..65031194e147
--- /dev/null
+++ b/lib/gss_preauth/Makefile.am
@@ -0,0 +1,25 @@
+# $Id$
+
+include $(top_srcdir)/Makefile.am.common
+
+AM_CPPFLAGS += \
+ -I$(srcdir)/../krb5 \
+ -I$(srcdir)/../gssapi \
+ -I$(srcdir)/../gssapi/mech \
+ -I$(top_srcdir)/include/gssapi \
+ -I$(top_builddir)/include/gssapi
+
+noinst_LTLIBRARIES = libgss_preauth.la
+include_HEADERS = $(srcdir)/gss-preauth-protos.h $(srcdir)/gss-preauth-private.h
+
+libgss_preauth_la_SOURCES = pa_client.c pa_common.c
+
+ALL_OBJECTS = $(include_HEADERS) $(libgss_preauth_la_OBJECTS)
+
+$(libgss_preauth_la_OBJECTS): $(include_HEADERS)
+
+$(srcdir)/gss-preauth-protos.h: $(libgss_preauth_la_SOURCES)
+ cd $(srcdir); perl ../../cf/make-proto.pl -q -P comment -o gss-preauth-protos.h $(libgss_preauth_la_SOURCES) || rm -f gss-preauth-protos.h
+
+$(srcdir)/gss-preauth-private.h: $(libgss_preauth_la_SOURCES)
+ cd $(srcdir); perl ../../cf/make-proto.pl -q -P comment -p gss-preauth-private.h $(libgss_preauth_la_SOURCES) || rm -f gss-preauth-private.h
diff --git a/lib/gss_preauth/NTMakefile b/lib/gss_preauth/NTMakefile
new file mode 100644
index 000000000000..60c963b2e235
--- /dev/null
+++ b/lib/gss_preauth/NTMakefile
@@ -0,0 +1,70 @@
+########################################################################
+#
+# Copyright (c) 2021, Secure Endpoints Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# - 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.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+# COPYRIGHT HOLDER 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.
+#
+
+RELDIR=lib\gss_preauth
+
+intcflags=-I$(SRCDIR) \
+ -I$(SRCDIR)\..\krb5 \
+ -I$(SRCDIR)\..\gssapi \
+ -I$(SRCDIR)\..\gssapi\mech \
+ -I$(OBJ) \
+ -I$(OBJDIR)\lib\gssapi \
+ -I$(OBJDIR)\lib\gssapi\gssapi \
+ -I$(INCDIR) \
+ -I$(INCDIR)\gssapi \
+
+!include ../../windows/NTMakefile.w32
+
+INCFILES= \
+ $(OBJ)\gss-preauth-protos.h \
+ $(OBJ)\gss-preauth-private.h
+
+libgss_preauth_SOURCES = \
+ pa_client.c \
+ pa_common.c
+
+libgss_preauth_OBJS = \
+ $(OBJ)\pa_client.obj \
+ $(OBJ)\pa_common.obj
+
+$(LIBGSS_PREAUTH): $(libgss_preauth_OBJS)
+ $(LIBCON)
+
+$(OBJ)\gss-preauth-protos.h: $(libgss_preauth_SOURCES)
+ $(PERL) ..\..\cf\make-proto.pl -E KRB5_LIB -q -P remove -o $(OBJ)\gss-preauth-protos.h $(libgss_preauth_SOURCES) || $(RM) -f $(OBJ)\gss-preauth-protos.h
+
+$(OBJ)\gss-preauth-private.h: $(libgss_preauth_SOURCES)
+ $(PERL) ..\..\cf\make-proto.pl -q -P remove -p $(OBJ)\gss-preauth-private.h $(libgss_preauth_SOURCES) || $(RM) -f $(OBJ)\gss-preauth-private.h
+
+all:: $(INCFILES) $(LIBGSS_PREAUTH)
+
+clean::
+ -$(RM) $(INCFILES) $(LIBGSS_PREAUTH)
diff --git a/lib/gss_preauth/README.md b/lib/gss_preauth/README.md
new file mode 100644
index 000000000000..2a3b142594ef
--- /dev/null
+++ b/lib/gss_preauth/README.md
@@ -0,0 +1,110 @@
+# GSS-API Pre-authentication in Heimdal
+
+GSS-API pre-authentication in Heimdal is based on
+[draft-perez-krb-wg-gss-preauth](https://datatracker.ietf.org/doc/html/draft-perez-krb-wg-gss-preauth)
+but with some simplifications to the protocol.
+
+The following text assumes the reader is familiar with the draft.
+
+## Client side
+
+Because libkrb5 cannot have a recursive dependency on libgssapi, it instead
+exports the function `_krb5_init_creds_init_gss()` which allows libgssapi to
+register a set of function pointers for:
+
+ - Generating context tokens
+ - Finalizing a context (inquiring the initiator name and reply key)
+ - Releasing context and credential handles
+
+This is a private API.
+
+This architecture also means that the libkrb5 implementation could be used with
+an alternative GSS-API implementation such as SSPI, without too much work. The
+bulk of the pre-authentication logic remains in libkrb5, however, in
+[`init_creds_pw.c`](../../krb5/init_creds_pw.c).
+
+libgssapi itself exports `krb5_gss_set_init_creds()`, which is the public
+interface for GSS-API pre-authentication.
+
+`krb5_gss_set_init_creds()` enables GSS-API pre-authentication on an initial
+credentials context, taking a GSS-API credential handle and mechanism. Both are
+optional; defaults will be used if absent. These two parameters are exposed as
+the `--gss-name` and `--gss-mech` options to `kinit` (see
+[kinit(1)](../../../kuser/kinit.1) for further details). `kinit` supports
+acquiring anonymous, keytab- and password-based GSS-API credentials using the
+same arguments as regular Kerberos.
+
+The selected GSS-API mechanism must support mutual authentication (ie.
+authenticating the KDC) as it replaces the AS-REP reply key, However, if FAST
+was used, and we know that the KDC was verified, then this requirement is
+removed.
+
+If the client does not know its initiator name, it can specify the last
+arugment to `kinit` as `@REALM`, and the initiator name will be filled in when
+the authentication is complete. (The realm is required to select a KDC.)
+
+## KDC side
+
+The KDC implements the acceptor side of the GSS-API authentication exchange.
+The selected GSS-API mechanism must allow `gss_export_sec_context()` to be
+called by the acceptor before the context is established, if it needs more than
+a single round trip of token exchanges.
+
+### Configuration
+
+Configuration directives live in the [kdc] section of
+[krb5.conf(5)](../../krb5/krb5.conf.5).
+
+The `enable_gss_preauth` krb5.conf option must be set in order to enable
+GSS-API pre-authentication in the KDC. When authenticating federated principals
+which may not exist in the KDC, the `synthetic_clients` option should also be
+set.
+
+The `gss_mechanisms_allowed` option can be used to limit the set of GSS-API
+mechanisms which are allowed to perform pre-authentication. Mechanisms are
+specified as dot-separated OIDs or by a short name, such as `sanon-x25519`.
+
+The `enable_gss_auth_data` option will include a composite GSS name in the
+authorization data of returned tickets.
+
+### Authorization
+
+The default is that the initiator is permitted to authenticate to the Kerberos
+principal that directly corresponds to it. The correspondence is governed as
+follows: if the authenticating mechanism is in the list of mechanisms in the
+`gss_cross_realm_mechanisms_allowed` configuration option, then the principal
+is mapped identically: an initiator named `lukeh@AAA.PADL.COM` will be mapped
+to the Kerberos principal `lukeh@AAA.PADL.COM`.
+
+If the authenticating mechanism is not in this list, then the initiator will be
+mapped to an enterprise principal in the service realm. For example,
+`lukeh@AAA.PADL.COM` might be mapped to `lukeh\@AAA.PADL.COM@PADL.COM`
+(enterprise principal name type);
+
+This mapping has no effect for principals that exist in the HDB, because
+enterprise principal names are always looked up by their first component (as if
+they were an ordinary principal name). This logic is instead useful when
+synthetic principals are enabled as we wish to avoid issuing tickets with a
+client name in a foreign Kerberos realm, as that would conflate GSS-API
+"realms" with Kerberos realms.
+
+A custom authorization plugin installed in `$prefix/lib/plugin/kdc` will
+replace this mapping and authorization logic. The plugin interface is defined in
+[`gss_preauth_authorizer_plugin.h`](../../../kdc/gss_preauth_authorizer_plugin.h)).
+
+### Anonymous authentication
+
+A further note on the interaction of anonymous GSS-API authentication and
+pre-authentication. Initiator contexts that set `GSS_C_ANON_FLAG` and a
+`GSS_C_NT_ANONYMOUS` name are mapped to the unauthenticated anonymous Kerberos
+principal, `WELLKNOWN/ANONYMOUS@WELLKNOWN:ANONYMOUS`. However, the local
+`WELLKNOWN/ANONYMOUS` HDB entry is used to perform any authorization decisions
+(as it would be for anonymous PKINIT). The AP-REP will contain the well-known
+anonymous realm.
+
+If `GSS_C_NT_ANONYMOUS` was set but a different name type was returned, then
+the initiator is treated as authenticated anonymous, and the client realm will
+be present in the AP-REP.
+
+The `request-anonymous` AP-REQ flag must also be set for GSS-API anonymous
+authentication to succeed.
diff --git a/lib/gss_preauth/pa_client.c b/lib/gss_preauth/pa_client.c
new file mode 100644
index 000000000000..de2d7b5cbe6d
--- /dev/null
+++ b/lib/gss_preauth/pa_client.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2021, PADL Software Pty Ltd.
+ * 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 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 <krb5_locl.h>
+#include <mech_locl.h>
+
+#include "gss-preauth-protos.h"
+#include "gss-preauth-private.h"
+
+static krb5_error_code
+pa_gss_acquire_initiator_cred(krb5_context context,
+ krb5_gss_init_ctx gssic,
+ const krb5_creds *kcred,
+ gss_cred_id_t *cred)
+{
+ krb5_error_code ret;
+
+ OM_uint32 major, minor;
+ gss_const_OID mech;
+ gss_OID_set_desc mechs;
+ gss_name_t initiator_name = GSS_C_NO_NAME;
+ OM_uint32 time_req;
+ krb5_timestamp now;
+
+ *cred = GSS_C_NO_CREDENTIAL;
+
+ mech = _krb5_init_creds_get_gss_mechanism(context, gssic);
+
+ mechs.count = 1;
+ mechs.elements = (gss_OID)mech;
+
+ ret = _krb5_gss_pa_unparse_name(context, kcred->client, &initiator_name);
+ if (ret)
+ return ret;
+
+ krb5_timeofday(context, &now);
+ if (kcred->times.endtime && kcred->times.endtime > now)
+ time_req = kcred->times.endtime - now;
+ else
+ time_req = GSS_C_INDEFINITE;
+
+ major = gss_acquire_cred(&minor, initiator_name, time_req, &mechs,
+ GSS_C_INITIATE, cred, NULL, NULL);
+ ret = _krb5_gss_map_error(major, minor);
+
+ gss_release_name(&major, &initiator_name);
+
+ return ret;
+}
+
+static krb5_error_code KRB5_LIB_CALL
+pa_gss_step(krb5_context context,
+ krb5_gss_init_ctx gssic,
+ const krb5_creds *kcred,
+ gss_ctx_id_t *ctx,
+ KDCOptions flags,
+ krb5_data *enc_as_req,
+ krb5_data *in,
+ krb5_data *out)
+{
+ krb5_error_code ret;
+ krb5_principal tgs_name = NULL;
+
+ OM_uint32 major, minor;
+ gss_cred_id_t cred;
+ gss_name_t target_name = GSS_C_NO_NAME;
+ OM_uint32 req_flags = GSS_C_MUTUAL_FLAG;
+ OM_uint32 ret_flags;
+ struct gss_channel_bindings_struct cb;
+ gss_buffer_desc input_token, output_token = GSS_C_EMPTY_BUFFER;
+
+ memset(&cb, 0, sizeof(cb));
+ krb5_data_zero(out);
+
+ if (flags.request_anonymous)
+ req_flags |= GSS_C_ANON_FLAG;
+
+ cred = (gss_cred_id_t)_krb5_init_creds_get_gss_cred(context, gssic);
+
+ if (cred == GSS_C_NO_CREDENTIAL) {
+ ret = pa_gss_acquire_initiator_cred(context, gssic, kcred, &cred);
+ if (ret)
+ goto out;
+
+ _krb5_init_creds_set_gss_cred(context, gssic, cred);
+ }
+
+ ret = krb5_make_principal(context, &tgs_name, kcred->server->realm,
+ KRB5_TGS_NAME, kcred->server->realm, NULL);
+ if (ret)
+ goto out;
+
+ ret = _krb5_gss_pa_unparse_name(context, tgs_name, &target_name);
+ if (ret)
+ goto out;
+
+ _krb5_gss_data_to_buffer(enc_as_req, &cb.application_data);
+ _krb5_gss_data_to_buffer(in, &input_token);
+
+ major = gss_init_sec_context(&minor,
+ cred,
+ ctx,
+ target_name,
+ (gss_OID)_krb5_init_creds_get_gss_mechanism(context, gssic),
+ req_flags,
+ GSS_C_INDEFINITE,
+ &cb,
+ &input_token,
+ NULL,
+ &output_token,
+ &ret_flags,
+ NULL);
+
+ _krb5_gss_buffer_to_data(&output_token, out);
+
+ if (major == GSS_S_COMPLETE) {
+ if ((ret_flags & GSS_C_MUTUAL_FLAG) == 0)
+ ret = KRB5_MUTUAL_FAILED;
+ else if ((ret_flags & req_flags) != req_flags)
+ ret = KRB5KDC_ERR_BADOPTION;
+ else
+ ret = 0;
+ } else
+ ret = _krb5_gss_map_error(major, minor);
+
+out:
+ gss_release_name(&minor, &target_name);
+ krb5_free_principal(context, tgs_name);
+
+ return ret;
+}
+
+static krb5_error_code KRB5_LIB_CALL
+pa_gss_finish(krb5_context context,
+ krb5_gss_init_ctx gssic,
+ const krb5_creds *kcred,
+ gss_ctx_id_t ctx,
+ krb5int32 nonce,
+ krb5_enctype enctype,
+ krb5_principal *client_p,
+ krb5_keyblock **reply_key_p)
+{
+ krb5_error_code ret;
+ krb5_principal client = NULL;
+ krb5_keyblock *reply_key = NULL;
+
+ OM_uint32 major, minor;
+ gss_name_t initiator_name = GSS_C_NO_NAME;
+
+ *client_p = NULL;
+ *reply_key_p = NULL;
+
+ major = gss_inquire_context(&minor,
+ ctx,
+ &initiator_name,
+ NULL, /* target_name */
+ NULL, /* lifetime_req */
+ NULL, /* mech_type */
+ NULL, /* ctx_flags */
+ NULL, /* locally_initiated */
+ NULL); /* open */
+
+ if (GSS_ERROR(major))
+ return _krb5_gss_map_error(major, minor);
+
+ ret = _krb5_gss_pa_parse_name(context, initiator_name, 0, &client);
+ if (ret)
+ goto out;
+
+ ret = _krb5_gss_pa_derive_key(context, ctx, nonce, enctype, &reply_key);
+ if (ret)
+ goto out;
+
+ *client_p = client;
+ client = NULL;
+
+ *reply_key_p = reply_key;
+ reply_key = NULL;
+
+out:
+ krb5_free_principal(context, client);
+ if (reply_key)
+ krb5_free_keyblock(context, reply_key);
+ gss_release_name(&minor, &initiator_name);
+
+ return ret;
+}
+
+static void KRB5_LIB_CALL
+pa_gss_delete_sec_context(krb5_context context,
+ krb5_gss_init_ctx gssic,
+ gss_ctx_id_t ctx)
+{
+ OM_uint32 minor;
+
+ gss_delete_sec_context(&minor, &ctx, GSS_C_NO_BUFFER);
+}
+
+static void KRB5_LIB_CALL
+pa_gss_release_cred(krb5_context context,
+ krb5_gss_init_ctx gssic,
+ gss_cred_id_t cred)
+{
+ OM_uint32 minor;
+
+ gss_release_cred(&minor, &cred);
+}
+
+krb5_error_code
+krb5_gss_set_init_creds(krb5_context context,
+ krb5_init_creds_context ctx,
+ gss_const_cred_id_t gss_cred,
+ gss_const_OID gss_mech)
+{
+ return _krb5_init_creds_init_gss(context,ctx,
+ pa_gss_step,
+ pa_gss_finish,
+ pa_gss_release_cred,
+ pa_gss_delete_sec_context,
+ gss_cred,
+ gss_mech,
+ 0);
+}
diff --git a/lib/gss_preauth/pa_common.c b/lib/gss_preauth/pa_common.c
new file mode 100644
index 000000000000..00efde72d662
--- /dev/null
+++ b/lib/gss_preauth/pa_common.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2021, PADL Software Pty Ltd.
+ * 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 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 <krb5_locl.h>
+#include <mech_locl.h>
+#include <heimntlm.h>
+
+#include "gss-preauth-protos.h"
+#include "gss-preauth-private.h"
+
+krb5_error_code
+_krb5_gss_map_error(OM_uint32 major, OM_uint32 minor)
+{
+ krb5_error_code ret;
+
+ if (minor != 0)
+ return (krb5_error_code)minor;
+
+ switch (major) {
+ case GSS_S_COMPLETE:
+ ret = 0;
+ break;
+ case GSS_S_CONTINUE_NEEDED:
+ ret = HEIM_ERR_PA_CONTINUE_NEEDED;
+ break;
+ case GSS_S_BAD_NAME:
+ case GSS_S_BAD_NAMETYPE:
+ ret = KRB5_PRINC_NOMATCH;
+ break;
+ case GSS_S_NO_CRED:
+ ret = KRB5_CC_NOTFOUND;
+ break;
+ case GSS_S_BAD_MIC:
+ case GSS_S_DEFECTIVE_CREDENTIAL:
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ break;
+ case GSS_S_FAILURE:
+ default:
+ ret = KRB5KDC_ERR_PREAUTH_FAILED;
+ break;
+ }
+
+ return ret;
+}
+
+krb5_error_code
+_krb5_gss_pa_derive_key(krb5_context context,
+ gss_ctx_id_t ctx,
+ krb5int32 nonce,
+ krb5_enctype enctype,
+ krb5_keyblock **keyblock)
+{
+ krb5_error_code ret;
+ u_char saltdata[12] = "KRB-GSS";
+ krb5_keyblock kdkey;
+ size_t keysize;
+
+ OM_uint32 major, minor;
+ gss_buffer_desc salt, dkey = GSS_C_EMPTY_BUFFER;
+
+ *keyblock = NULL;
+
+ ret = krb5_enctype_keysize(context, enctype, &keysize);
+ if (ret)
+ return ret;
+
+ saltdata[ 8] = (nonce >> 0 ) & 0xFF;
+ saltdata[ 9] = (nonce >> 8 ) & 0xFF;
+ saltdata[10] = (nonce >> 16) & 0xFF;
+ saltdata[11] = (nonce >> 24) & 0xFF;
+
+ salt.value = saltdata;
+ salt.length = sizeof(saltdata);
+
+ major = gss_pseudo_random(&minor, ctx, GSS_C_PRF_KEY_FULL,
+ &salt, keysize, &dkey);
+ if (GSS_ERROR(major))
+ return KRB5_PREAUTH_NO_KEY;
+
+ kdkey.keytype = enctype;
+ kdkey.keyvalue.data = dkey.value;
+ kdkey.keyvalue.length = dkey.length;
+
+ ret = krb5_copy_keyblock(context, &kdkey, keyblock);
+
+ if (dkey.value) {
+ memset_s(dkey.value, dkey.length, 0, dkey.length);
+ gss_release_buffer(&minor, &dkey);
+ }
+
+ return ret;
+}
+
+krb5_error_code
+_krb5_gss_pa_unparse_name(krb5_context context,
+ krb5_const_principal principal,
+ gss_name_t *namep)
+{
+ krb5_error_code ret;
+ char *name = NULL;
+
+ OM_uint32 major, minor;
+ gss_buffer_desc name_buf;
+
+ *namep = GSS_C_NO_NAME;
+
+ if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
+ if (principal->name.name_string.len != 1)
+ return EINVAL;
+
+ name = principal->name.name_string.val[0];
+ } else {
+ ret = krb5_unparse_name(context, principal, &name);
+ if (ret)
+ return ret;
+ }
+
+ name_buf.length = strlen(name);
+ name_buf.value = name;
+
+ major = gss_import_name(&minor, &name_buf,
+ GSS_KRB5_NT_PRINCIPAL_NAME, namep);
+ if (major == GSS_S_BAD_NAMETYPE) {
+ gss_OID name_type = GSS_C_NO_OID;
+ int flags = 0;
+
+ if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
+ name_type = GSS_C_NT_USER_NAME;
+ } else if (principal->name.name_type == KRB5_NT_PRINCIPAL) {
+ flags = KRB5_PRINCIPAL_UNPARSE_SHORT;
+ name_type = GSS_C_NT_USER_NAME;
+ } else if ((principal->name.name_type == KRB5_NT_SRV_HST ||
+ principal->name.name_type == KRB5_NT_SRV_INST) &&
+ principal->name.name_string.len == 2) {
+ flags = KRB5_PRINCIPAL_UNPARSE_NO_REALM;
+ name_type = GSS_C_NT_HOSTBASED_SERVICE;
+ }
+
+ if (flags) {
+ krb5_xfree(name);
+
+ ret = krb5_unparse_name_flags(context, principal, flags, &name);
+ if (ret)
+ return ret;
+
+ if (gss_oid_equal(name_type, GSS_C_NT_HOSTBASED_SERVICE)) {
+ char *inst = strchr(name, '/');
+ if (inst)
+ *inst = '@';
+ }
+
+ name_buf.length = strlen(name);
+ name_buf.value = name;
+ }
+
+ if (name_type)
+ major = gss_import_name(&minor, &name_buf, name_type, namep);
+ }
+
+ if (name != principal->name.name_string.val[0])
+ krb5_xfree(name);
+
+ return _krb5_gss_map_error(major, minor);
+}
+
+krb5_error_code
+_krb5_gss_pa_parse_name(krb5_context context,
+ gss_const_name_t name,
+ int flags,
+ krb5_principal *principal)
+{
+ krb5_error_code ret;
+ char *displayed_name0;
+
+ OM_uint32 major, minor;
+ gss_OID name_type = GSS_C_NO_OID;
+ gss_buffer_desc displayed_name = GSS_C_EMPTY_BUFFER;
+
+ major = gss_display_name(&minor, name, &displayed_name, &name_type);
+ if (GSS_ERROR(major))
+ return _krb5_gss_map_error(major, minor);
+
+ if (gss_oid_equal(name_type, GSS_C_NT_ANONYMOUS)) {
+ ret = krb5_make_principal(context, principal, KRB5_ANON_REALM,
+ KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL);
+ if (ret == 0)
+ (*principal)->name.name_type = KRB5_NT_WELLKNOWN;
+ } else {
+ displayed_name0 = malloc(displayed_name.length + 1);
+ if (displayed_name0 == NULL)
+ return krb5_enomem(context);
+
+ memcpy(displayed_name0, displayed_name.value, displayed_name.length);
+ displayed_name0[displayed_name.length] = '\0';
+
+ ret = krb5_parse_name_flags(context, displayed_name0, flags, principal);
+ gss_release_buffer(&minor, &displayed_name);
+ free(displayed_name0);
+ }
+
+ gss_release_buffer(&minor, &displayed_name);
+
+ return ret;
+}
+
+void
+_krb5_gss_data_to_buffer(const krb5_data *data, gss_buffer_t buffer)
+{
+ if (data) {
+ buffer->length = data->length;
+ buffer->value = data->data;
+ } else {
+ _mg_buffer_zero(buffer);
+ }
+}
+
+void
+_krb5_gss_buffer_to_data(gss_const_buffer_t buffer, krb5_data *data)
+{
+ if (buffer) {
+ data->length = buffer->length;
+ data->data = buffer->value;
+ } else {
+ krb5_data_zero(data);
+ }
+}