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:
@@ -192,7 +192,9 @@ PADATA-TYPE ::= INTEGER {
|
||||
KRB5-PADATA-PKINIT-KX(147), -- krb-wg-anon
|
||||
KRB5-PADATA-PKU2U-NAME(148), -- zhu-pku2u
|
||||
KRB5-PADATA-REQ-ENC-PA-REP(149), --
|
||||
KRB5-PADATA-SUPPORTED-ETYPES(165) -- MS-KILE
|
||||
KRB5-PADATA-SUPPORTED-ETYPES(165), -- MS-KILE
|
||||
KRB5-PADATA-GSS(655) -- krb-wg-gss-preauth
|
||||
|
||||
}
|
||||
|
||||
AUTHDATA-TYPE ::= INTEGER {
|
||||
@@ -221,8 +223,9 @@ AUTHDATA-TYPE ::= INTEGER {
|
||||
KRB5-AUTHDATA-BEARER-TOKEN-JWT(581), -- JWT token
|
||||
KRB5-AUTHDATA-BEARER-TOKEN-SAML(582), -- SAML token
|
||||
KRB5-AUTHDATA-BEARER-TOKEN-OIDC(583), -- OIDC token
|
||||
KRB5-AUTHDATA-CSR-AUTHORIZED(584) -- Proxy has authorized client
|
||||
KRB5-AUTHDATA-CSR-AUTHORIZED(584), -- Proxy has authorized client
|
||||
-- to requested exts in CSR
|
||||
KRB5-AUTHDATA-GSS-COMPOSITE-NAME(655) -- gss_export_name_composite
|
||||
}
|
||||
|
||||
-- checksumtypes
|
||||
@@ -925,7 +928,6 @@ KERB-ARMOR-SERVICE-REPLY ::= SEQUENCE {
|
||||
armor-key [1] EncryptionKey
|
||||
}
|
||||
|
||||
|
||||
END
|
||||
|
||||
-- etags -r '/\([A-Za-z][-A-Za-z0-9]*\).*::=/\1/' k5.asn1
|
||||
|
||||
@@ -13,6 +13,7 @@ AM_CPPFLAGS += \
|
||||
-I$(srcdir)/krb5 \
|
||||
-I$(srcdir)/spnego \
|
||||
-I$(srcdir)/sanon \
|
||||
-I$(srcdir)/preauth \
|
||||
$(INCLUDE_libintl)
|
||||
|
||||
lib_LTLIBRARIES = libgssapi.la test_negoex_mech.la
|
||||
@@ -250,12 +251,17 @@ sanonsrc = \
|
||||
sanon/release_name.c \
|
||||
sanon/sanon-private.h
|
||||
|
||||
preauthsrc = \
|
||||
preauth/pa_client.c \
|
||||
preauth/pa_common.c
|
||||
|
||||
dist_libgssapi_la_SOURCES = \
|
||||
$(krb5src) \
|
||||
$(mechsrc) \
|
||||
$(ntlmsrc) \
|
||||
$(spnegosrc) \
|
||||
$(sanonsrc)
|
||||
$(sanonsrc) \
|
||||
$(preauthsrc)
|
||||
|
||||
nodist_libgssapi_la_SOURCES = \
|
||||
gkrb5_err.c \
|
||||
@@ -289,6 +295,7 @@ noinst_HEADERS = \
|
||||
$(srcdir)/ntlm/ntlm-private.h \
|
||||
$(srcdir)/spnego/spnego-private.h \
|
||||
$(srcdir)/sanon/sanon-private.h \
|
||||
$(srcdir)/preauth/pa-private.h \
|
||||
$(srcdir)/krb5/gsskrb5-private.h
|
||||
|
||||
nobase_include_HEADERS = \
|
||||
@@ -296,6 +303,7 @@ nobase_include_HEADERS = \
|
||||
gssapi/gssapi_krb5.h \
|
||||
gssapi/gssapi_ntlm.h \
|
||||
gssapi/gssapi_oid.h \
|
||||
gssapi/gssapi_preauth.h \
|
||||
gssapi/gssapi_spnego.h
|
||||
|
||||
gssapidir = $(includedir)/gssapi
|
||||
@@ -319,7 +327,8 @@ BUILTHEADERS = \
|
||||
$(srcdir)/krb5/gsskrb5-private.h \
|
||||
$(srcdir)/spnego/spnego-private.h \
|
||||
$(srcdir)/sanon/sanon-private.h \
|
||||
$(srcdir)/ntlm/ntlm-private.h
|
||||
$(srcdir)/ntlm/ntlm-private.h \
|
||||
$(srcdir)/preauth/pa-private.h
|
||||
|
||||
$(libgssapi_la_OBJECTS): $(BUILTHEADERS)
|
||||
$(test_context_OBJECTS): $(BUILTHEADERS)
|
||||
@@ -356,6 +365,9 @@ $(srcdir)/spnego/spnego-private.h:
|
||||
$(srcdir)/sanon/sanon-private.h:
|
||||
cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p sanon/sanon-private.h $(sanonsrc) || rm -f sanon/sanon-private.h
|
||||
|
||||
$(srcdir)/preauth/pa-private.h:
|
||||
cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p preauth/pa-private.h $(preauthsrc) || rm -f preauth/pa-private.h
|
||||
|
||||
TESTS = test_oid test_names test_cfx
|
||||
# test_sequence
|
||||
|
||||
|
||||
@@ -261,6 +261,10 @@ sanonsrc = \
|
||||
sanon/release_cred.c \
|
||||
sanon/release_name.c
|
||||
|
||||
preauthsrc = \
|
||||
preauth/pa_client.c \
|
||||
preauth/pa_common.c
|
||||
|
||||
$(OBJ)\ntlm\ntlm-private.h: $(ntlmsrc)
|
||||
$(PERL) ../../cf/make-proto.pl -q -P remove -p $@ $(ntlmsrc)
|
||||
|
||||
@@ -273,6 +277,9 @@ $(OBJ)\spnego\spnego-private.h: $(spnegosrc)
|
||||
$(OBJ)\sanon\sanon-private.h: $(sanonsrc)
|
||||
$(PERL) ../../cf/make-proto.pl -q -P remove -p $@ $(sanonsrc)
|
||||
|
||||
$(OBJ)\preauth\pa-private.h: $(preauthsrc)
|
||||
$(PERL) ../../cf/make-proto.pl -q -P remove -p $@ $(preauthsrc)
|
||||
|
||||
gssapi_files = $(OBJ)\gssapi\asn1_gssapi_asn1.x
|
||||
|
||||
spnego_files = $(OBJ)\spnego\asn1_spnego_asn1.x
|
||||
@@ -317,6 +324,7 @@ INCFILES= \
|
||||
$(OBJ)\ntlm\ntlm-private.h \
|
||||
$(OBJ)\spnego\spnego-private.h \
|
||||
$(OBJ)\sanon\sanon-private.h \
|
||||
$(OBJ)\preauth\pa-private.h \
|
||||
$(OBJ)\krb5\gsskrb5-private.h \
|
||||
$(OBJ)\gkrb5_err.h \
|
||||
$(OBJ)\negoex_err.h \
|
||||
@@ -533,6 +541,8 @@ libgssapi_OBJs = \
|
||||
$(OBJ)\sanon/process_context_token.obj \
|
||||
$(OBJ)\sanon/release_cred.obj \
|
||||
$(OBJ)\sanon/release_name.obj \
|
||||
$(OBJ)\preauth/pa_client.obj \
|
||||
$(OBJ)\preauth/pa_common.obj \
|
||||
$(OBJ)\gkrb5_err.obj \
|
||||
$(OBJ)\negoex_err.obj \
|
||||
$(spnego_files:.x=.obj) \
|
||||
@@ -570,6 +580,12 @@ GCOPTS=-I$(SRCDIR) -I$(OBJ) -Igssapi -DBUILD_GSSAPI_LIB
|
||||
{sanon}.c{$(OBJ)\sanon}.obj::
|
||||
$(C2OBJ_NP) -Fo$(OBJ)\sanon\ -Fd$(OBJ)\sanon\ -I$(OBJ)\sanon -I$(OBJ) -I$(OBJ)\krb5 -I$(OBJ)\gssapi -Ikrb5 -Imech -Igssapi $(GCOPTS) -DASN1_LIB
|
||||
|
||||
{$(OBJ)\preauth}.c{$(OBJ)\preauth}.obj::
|
||||
$(C2OBJ_NP) -Fo$(OBJ)\preauth\ -Fd$(OBJ)\pa\ -I$(OBJ)\pa -I$(OBJ) -I$(OBJ)\krb5 -I$(OBJ)\gssapi -Ikrb5 -Imech -Igssapi $(GCOPTS)
|
||||
|
||||
{preauth}.c{$(OBJ)\preauth}.obj::
|
||||
$(C2OBJ_NP) -Fo$(OBJ)\preauth\ -Fd$(OBJ)\pa\ -I$(OBJ)\pa -I$(OBJ) -I$(OBJ)\krb5 -I$(OBJ)\gssapi -Ikrb5 -Imech -Igssapi $(GCOPTS) -DASN1_LIB
|
||||
|
||||
{$(OBJ)\gssapi}.c{$(OBJ)\gssapi}.obj::
|
||||
$(C2OBJ_NP) -Fo$(OBJ)\gssapi\ -Fd$(OBJ)\gssapi\ -I$(OBJ)\gssapi $(GCOPTS)
|
||||
|
||||
@@ -660,6 +676,9 @@ mkdirs-gss:
|
||||
!if !exist($(OBJ)\gssapi)
|
||||
$(MKDIR) $(OBJ)\gssapi
|
||||
!endif
|
||||
!if !exist($(OBJ)\preauth)
|
||||
$(MKDIR) $(OBJ)\preauth
|
||||
!endif
|
||||
|
||||
clean::
|
||||
-$(RM) $(OBJ)\ntlm\*.*
|
||||
@@ -668,6 +687,7 @@ clean::
|
||||
-$(RM) $(OBJ)\mech\*.*
|
||||
-$(RM) $(OBJ)\sanon\*.*
|
||||
-$(RM) $(OBJ)\gssapi\*.*
|
||||
-$(RM) $(OBJ)\preauth\*.*
|
||||
|
||||
all-tools:: $(BINDIR)\gsstool.exe
|
||||
|
||||
|
||||
@@ -1240,6 +1240,9 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gss_destroy_cred(OM_uint32 *minor_status,
|
||||
gss_cred_id_t *cred_handle);
|
||||
|
||||
GSSAPI_LIB_FUNCTION uintptr_t GSSAPI_CALLCONV
|
||||
gss_get_instance(const char *libname);
|
||||
|
||||
GSSAPI_CPP_END
|
||||
|
||||
#if defined(__APPLE__) && (defined(__ppc__) || defined(__ppc64__) || defined(__i386__) || defined(__x86_64__))
|
||||
|
||||
55
lib/gssapi/gssapi/gssapi_preauth.h
Normal file
55
lib/gssapi/gssapi/gssapi_preauth.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 1997 - 2006 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.
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef GSSAPI_PREAUTH_H_
|
||||
#define GSSAPI_PREAUTH_H_
|
||||
|
||||
#include <gssapi.h>
|
||||
#include <krb5.h>
|
||||
|
||||
GSSAPI_CPP_START
|
||||
|
||||
#if !defined(__GNUC__) && !defined(__attribute__)
|
||||
#define __attribute__(x)
|
||||
#endif
|
||||
|
||||
GSSAPI_LIB_FUNCTION krb5_error_code GSSAPI_LIB_CALL
|
||||
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);
|
||||
|
||||
|
||||
#endif /* GSSAPI_PREAUTH_H_ */
|
||||
@@ -40,6 +40,7 @@ EXPORTS
|
||||
gss_export_name
|
||||
gss_export_name_composite
|
||||
gss_export_sec_context
|
||||
gss_get_instance
|
||||
gss_get_mic
|
||||
gss_get_neg_mechs
|
||||
gss_get_name_attribute
|
||||
@@ -122,6 +123,14 @@ EXPORTS
|
||||
gsskrb5_set_send_to_kdc
|
||||
gsskrb5_set_time_offset
|
||||
krb5_gss_register_acceptor_identity
|
||||
krb5_gss_set_init_creds
|
||||
|
||||
_krb5_gss_data_to_buffer
|
||||
_krb5_gss_buffer_to_data
|
||||
_krb5_gss_map_error
|
||||
_krb5_gss_pa_parse_name
|
||||
_krb5_gss_pa_unparse_name
|
||||
_krb5_gss_pa_derive_key
|
||||
|
||||
; _gsskrb5cfx_ are really internal symbols, but export
|
||||
; then now to make testing easier.
|
||||
|
||||
@@ -563,3 +563,16 @@ gss_oid_to_name(gss_const_OID oid)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION uintptr_t GSSAPI_CALLCONV
|
||||
gss_get_instance(const char *libname)
|
||||
{
|
||||
static const char *instance = "libgssapi";
|
||||
|
||||
if (strcmp(libname, "gssapi") == 0)
|
||||
return (uintptr_t)instance;
|
||||
else if (strcmp(libname, "krb5") == 0)
|
||||
return krb5_get_instance(libname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
119
lib/gssapi/preauth/README.md
Normal file
119
lib/gssapi/preauth/README.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# 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.
|
||||
|
||||
## Protocol changes
|
||||
|
||||
- The pre-authentication type KRB5-PADATA-GSS is 655
|
||||
- Pre-authentication data is the raw context token rather than being
|
||||
wrapped in another ASN.1 type
|
||||
- Acceptor GSS state is stored in FX-COOKIE rather than alongside the
|
||||
context token
|
||||
- Key derivation salt is the string "KRB-GSS\0" || nonce
|
||||
|
||||
## 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.
|
||||
251
lib/gssapi/preauth/pa_client.c
Normal file
251
lib/gssapi/preauth/pa_client.c
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* 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 <gssapi/gssapi_preauth.h>
|
||||
|
||||
#include <preauth/pa-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,
|
||||
KDCOptions flags,
|
||||
krb5_data *enc_as_req,
|
||||
krb5_data *in,
|
||||
krb5_data *out)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
|
||||
OM_uint32 major, minor;
|
||||
gss_cred_id_t cred;
|
||||
gss_ctx_id_t ctx;
|
||||
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 = { 0 };
|
||||
gss_buffer_desc input_token, output_token = GSS_C_EMPTY_BUFFER;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
ctx = (gss_ctx_id_t)_krb5_init_creds_get_gss_context(context, gssic);
|
||||
|
||||
ret = _krb5_gss_pa_unparse_name(context, kcred->server, &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_init_creds_set_gss_context(context, gssic, ctx);
|
||||
|
||||
_krb5_gss_buffer_to_data(&output_token, out);
|
||||
_mg_buffer_zero(&output_token);
|
||||
|
||||
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);
|
||||
gss_release_buffer(&minor, &output_token);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code KRB5_LIB_CALL
|
||||
pa_gss_finish(krb5_context context,
|
||||
krb5_gss_init_ctx gssic,
|
||||
const krb5_creds *kcred,
|
||||
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;
|
||||
gss_ctx_id_t ctx = (gss_ctx_id_t)_krb5_init_creds_get_gss_context(context, gssic);
|
||||
|
||||
*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);
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION krb5_error_code GSSAPI_LIB_CALL
|
||||
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);
|
||||
}
|
||||
215
lib/gssapi/preauth/pa_common.c
Normal file
215
lib/gssapi/preauth/pa_common.c
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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 <gssapi/gssapi_preauth.h>
|
||||
|
||||
#include <preauth/pa-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 = KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED;
|
||||
break;
|
||||
case GSS_S_BAD_NAME:
|
||||
case GSS_S_BAD_NAMETYPE:
|
||||
ret = KRB5_PRINC_NOMATCH;
|
||||
break;
|
||||
case GSS_S_BAD_MIC:
|
||||
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;
|
||||
|
||||
_gss_mg_encode_le_uint32(nonce, &saltdata[8]);
|
||||
|
||||
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);
|
||||
|
||||
_gss_secure_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;
|
||||
gss_OID name_type;
|
||||
|
||||
*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;
|
||||
|
||||
if (principal->name.name_type == KRB5_NT_PRINCIPAL ||
|
||||
principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL)
|
||||
name_type = GSS_C_NT_USER_NAME;
|
||||
else
|
||||
name_type = GSS_KRB5_NT_PRINCIPAL_NAME;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ HEIMDAL_GSS_2.0 {
|
||||
gss_export_name;
|
||||
gss_export_name_composite;
|
||||
gss_export_sec_context;
|
||||
gss_get_instance;
|
||||
gss_get_mic;
|
||||
gss_get_neg_mechs;
|
||||
gss_get_name_attribute;
|
||||
@@ -116,6 +117,7 @@ HEIMDAL_GSS_2.0 {
|
||||
gsskrb5_set_send_to_kdc;
|
||||
gsskrb5_set_time_offset;
|
||||
krb5_gss_register_acceptor_identity;
|
||||
krb5_gss_set_init_creds;
|
||||
gss_display_mech_attr;
|
||||
gss_inquire_attrs_for_mech;
|
||||
gss_indicate_mechs_by_attrs;
|
||||
@@ -133,6 +135,13 @@ HEIMDAL_GSS_2.0 {
|
||||
_gsskrb5cfx_wrap_length_cfx;
|
||||
_gssapi_wrap_size_cfx;
|
||||
|
||||
_krb5_gss_data_to_buffer;
|
||||
_krb5_gss_buffer_to_data;
|
||||
_krb5_gss_map_error;
|
||||
_krb5_gss_pa_parse_name;
|
||||
_krb5_gss_pa_unparse_name;
|
||||
_krb5_gss_pa_derive_key;
|
||||
|
||||
__gss_krb5_copy_ccache_x_oid_desc;
|
||||
__gss_krb5_get_tkt_flags_x_oid_desc;
|
||||
__gss_krb5_extract_authz_data_from_sec_context_x_oid_desc;
|
||||
|
||||
@@ -38,6 +38,21 @@
|
||||
#include <heim-ipc.h>
|
||||
#endif /* WIN32 */
|
||||
|
||||
struct krb5_gss_init_ctx_data {
|
||||
unsigned int flags;
|
||||
#define GSSIC_FLAG_RELEASE_CRED (1 << 0)
|
||||
#define GSSIC_FLAG_CONTEXT_OPEN (1 << 1)
|
||||
|
||||
krb5_gssic_step step;
|
||||
krb5_gssic_finish finish;
|
||||
krb5_gssic_release_cred release_cred;
|
||||
krb5_gssic_delete_sec_context delete_sec_context;
|
||||
|
||||
const struct gss_OID_desc_struct *mech;
|
||||
struct gss_cred_id_t_desc_struct *cred;
|
||||
struct gss_ctx_id_t_desc_struct *ctx;
|
||||
};
|
||||
|
||||
typedef struct krb5_get_init_creds_ctx {
|
||||
KDCOptions flags;
|
||||
krb5_creds cred;
|
||||
@@ -62,6 +77,7 @@ typedef struct krb5_get_init_creds_ctx {
|
||||
krb5_get_init_creds_tristate req_pac;
|
||||
|
||||
krb5_pk_init_ctx pk_init_ctx;
|
||||
krb5_gss_init_ctx gss_init_ctx;
|
||||
int ic_flags;
|
||||
|
||||
struct {
|
||||
@@ -106,6 +122,7 @@ typedef struct krb5_get_init_creds_ctx {
|
||||
krb5_keyblock *strengthen_key;
|
||||
krb5_get_init_creds_opt *anon_pkinit_opt;
|
||||
krb5_init_creds_context anon_pkinit_ctx;
|
||||
krb5_data cookie;
|
||||
} fast_state;
|
||||
} krb5_get_init_creds_ctx;
|
||||
|
||||
@@ -155,6 +172,15 @@ default_s2k_func(krb5_context context, krb5_enctype type,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
free_gss_init_ctx(krb5_context context, krb5_gss_init_ctx gssic)
|
||||
{
|
||||
if (gssic->flags & GSSIC_FLAG_RELEASE_CRED)
|
||||
gssic->release_cred(context, gssic, gssic->cred);
|
||||
gssic->delete_sec_context(context, gssic, gssic->ctx);
|
||||
free(gssic);
|
||||
}
|
||||
|
||||
static void
|
||||
free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
|
||||
{
|
||||
@@ -172,6 +198,8 @@ free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
|
||||
memset_s(ctx->password, len, 0, len);
|
||||
free(ctx->password);
|
||||
}
|
||||
if (ctx->gss_init_ctx)
|
||||
free_gss_init_ctx(context, ctx->gss_init_ctx);
|
||||
/*
|
||||
* FAST state (we don't close the armor_ccache because we might have
|
||||
* to destroy it, and how would we know? also, the caller should
|
||||
@@ -183,6 +211,7 @@ free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
|
||||
krb5_crypto_destroy(context, ctx->fast_state.armor_crypto);
|
||||
if (ctx->fast_state.strengthen_key)
|
||||
krb5_free_keyblock(context, ctx->fast_state.strengthen_key);
|
||||
krb5_data_free(&ctx->fast_state.cookie);
|
||||
krb5_free_keyblock_contents(context, &ctx->fast_state.armor_key);
|
||||
if (ctx->fast_state.flags & KRB5_FAST_ANON_PKINIT_ARMOR)
|
||||
krb5_cc_destroy(context, ctx->fast_state.armor_ccache);
|
||||
@@ -1169,6 +1198,105 @@ pa_data_to_md_pkinit(krb5_context context,
|
||||
#endif
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
gss_pa_step(krb5_context context,
|
||||
const krb5_creds *creds,
|
||||
const krb5_get_init_creds_ctx *ctx,
|
||||
const METHOD_DATA *md,
|
||||
krb5_data *output_token)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
size_t len = 0;
|
||||
krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
|
||||
krb5_data req_body;
|
||||
PA_DATA *pa;
|
||||
krb5_data *input_token;
|
||||
|
||||
krb5_data_zero(&req_body);
|
||||
krb5_data_zero(output_token);
|
||||
|
||||
pa = find_pa_data(md, KRB5_PADATA_GSS);
|
||||
input_token = pa ? &pa->padata_value : NULL;
|
||||
|
||||
if (input_token == NULL || input_token->length == 0) {
|
||||
if (gssic->ctx == NULL)
|
||||
ret = 0; /* initial context token */
|
||||
else {
|
||||
krb5_set_error_message(context, KRB5_PREAUTH_BAD_TYPE,
|
||||
"Missing GSS preauthentication data from KDC");
|
||||
ret = KRB5_PREAUTH_BAD_TYPE;
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (input_token && input_token->length &&
|
||||
(gssic->flags & GSSIC_FLAG_CONTEXT_OPEN)) {
|
||||
krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
|
||||
"Already completed GSS authentication, looping");
|
||||
ret = KRB5_GET_IN_TKT_LOOP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ASN1_MALLOC_ENCODE(KDC_REQ_BODY, req_body.data, req_body.length,
|
||||
&ctx->as_req.req_body, &len, ret);
|
||||
if (ret)
|
||||
goto out;
|
||||
heim_assert(req_body.length == len, "ASN.1 internal error");
|
||||
|
||||
ret = gssic->step(context, gssic, creds, ctx->flags, &req_body,
|
||||
input_token, output_token);
|
||||
|
||||
/*
|
||||
* If FAST authenticated the KDC (which will be the case unless anonymous
|
||||
* PKINIT was used without KDC certificate validation) then we can relax
|
||||
* the mutual authentication requirement.
|
||||
*/
|
||||
if (ret == KRB5_MUTUAL_FAILED &&
|
||||
(ctx->fast_state.flags & KRB5_FAST_EXPECTED) &&
|
||||
(ctx->fast_state.flags & KRB5_FAST_KDC_VERIFIED))
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
krb5_data_free(&req_body);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
pa_data_to_md_gss(krb5_context context,
|
||||
const AS_REQ *a,
|
||||
const krb5_creds *creds,
|
||||
krb5_get_init_creds_ctx *ctx,
|
||||
METHOD_DATA *in_md,
|
||||
METHOD_DATA *out_md)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_data output_token;
|
||||
krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
|
||||
|
||||
heim_assert(gssic != NULL, "invalid context passed to pa_data_to_md_gss");
|
||||
|
||||
ret = gss_pa_step(context, creds, ctx, in_md, &output_token);
|
||||
if (ret == 0)
|
||||
gssic->flags |= GSSIC_FLAG_CONTEXT_OPEN;
|
||||
else if (ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
|
||||
ret = 0;
|
||||
|
||||
if (output_token.length) {
|
||||
ret = krb5_padata_add(context, out_md, KRB5_PADATA_GSS,
|
||||
output_token.data, output_token.length);
|
||||
if (ret) {
|
||||
krb5_data_free(&output_token);
|
||||
return ret;
|
||||
}
|
||||
krb5_data_zero(&output_token);
|
||||
} else
|
||||
krb5_data_free(&output_token);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
pa_data_add_pac_request(krb5_context context,
|
||||
krb5_get_init_creds_ctx *ctx,
|
||||
@@ -1261,6 +1389,12 @@ process_pa_data_to_md(krb5_context context,
|
||||
else
|
||||
ctx->used_pa_types |= USED_PKINIT;
|
||||
|
||||
} else if (ctx->gss_init_ctx) {
|
||||
_krb5_debug(context, 5, "krb5_get_init_creds: preparing GSS credentials");
|
||||
|
||||
ret = pa_data_to_md_gss(context, a, creds, ctx, in_md, *out_md);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (in_md->len != 0) {
|
||||
struct pa_info_data *paid, *ppaid;
|
||||
unsigned flag;
|
||||
@@ -1317,6 +1451,61 @@ process_pa_data_to_md(krb5_context context,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
gss_pa_data_to_key(krb5_context context,
|
||||
krb5_get_init_creds_ctx *ctx,
|
||||
krb5_creds *creds,
|
||||
krb5_enctype etype,
|
||||
METHOD_DATA *md,
|
||||
krb5_keyblock **key)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_principal cname = NULL;
|
||||
krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
|
||||
|
||||
/*
|
||||
* Finish the GSS authentication and extract the reply key. If the GSS mechanism
|
||||
* required an odd number of legs, the context may already be open, but we still
|
||||
* needed to wait for a reply from the KDC.
|
||||
*/
|
||||
if ((gssic->flags & GSSIC_FLAG_CONTEXT_OPEN) == 0) {
|
||||
krb5_data output_token;
|
||||
|
||||
ret = gss_pa_step(context, creds, ctx, md, &output_token);
|
||||
if (ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED ||
|
||||
output_token.length) {
|
||||
krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED,
|
||||
"KDC sent AS-REP before GSS preauthentication completed");
|
||||
ret = KRB5_PREAUTH_FAILED;
|
||||
}
|
||||
krb5_data_free(&output_token);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
gssic->flags |= GSSIC_FLAG_CONTEXT_OPEN;
|
||||
}
|
||||
|
||||
ret = gssic->finish(context, gssic, creds, ctx->nonce, etype, &cname, key);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (krb5_principal_is_federated(context, creds->client)) {
|
||||
/* replace the wellknown federated name with the initiator name */
|
||||
krb5_free_principal(context, creds->client);
|
||||
creds->client = cname;
|
||||
cname = NULL;
|
||||
|
||||
/* allow the KDC to canonicalize the name */
|
||||
if (ctx->flags.canonicalize)
|
||||
ctx->ic_flags |= KRB5_INIT_CREDS_NO_C_CANON_CHECK;
|
||||
}
|
||||
|
||||
out:
|
||||
krb5_free_principal(context, cname);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
process_pa_data_to_key(krb5_context context,
|
||||
krb5_get_init_creds_ctx *ctx,
|
||||
@@ -1353,18 +1542,9 @@ process_pa_data_to_key(krb5_context context,
|
||||
|
||||
pa = NULL;
|
||||
if (rep->padata) {
|
||||
int idx = 0;
|
||||
pa = krb5_find_padata(rep->padata->val,
|
||||
rep->padata->len,
|
||||
KRB5_PADATA_PK_AS_REP,
|
||||
&idx);
|
||||
if (pa == NULL) {
|
||||
idx = 0;
|
||||
pa = krb5_find_padata(rep->padata->val,
|
||||
rep->padata->len,
|
||||
KRB5_PADATA_PK_AS_REP_19,
|
||||
&idx);
|
||||
}
|
||||
pa = find_pa_data(rep->padata, KRB5_PADATA_PK_AS_REP);
|
||||
if (pa == NULL)
|
||||
pa = find_pa_data(rep->padata, KRB5_PADATA_PK_AS_REP_19);
|
||||
}
|
||||
if (pa && ctx->pk_init_ctx) {
|
||||
#ifdef PKINIT
|
||||
@@ -1383,6 +1563,9 @@ process_pa_data_to_key(krb5_context context,
|
||||
ret = EINVAL;
|
||||
krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", ""));
|
||||
#endif
|
||||
} else if (ctx->gss_init_ctx) {
|
||||
_krb5_debug(context, 5, "krb5_get_init_creds: using GSS for reply key");
|
||||
ret = gss_pa_data_to_key(context, ctx, creds, etype, rep->padata, key);
|
||||
} else if (ctx->keyseed) {
|
||||
_krb5_debug(context, 5, "krb5_get_init_creds: using keyproc");
|
||||
ret = pa_data_to_key_plain(context, creds->client, ctx,
|
||||
@@ -1765,15 +1948,12 @@ fast_unwrap_as_rep(krb5_context context, int32_t nonce,
|
||||
KrbFastResponse fastrep;
|
||||
krb5_error_code ret;
|
||||
PA_DATA *pa = NULL;
|
||||
int idx = 0;
|
||||
|
||||
if (state->armor_crypto == NULL || rep->padata == NULL)
|
||||
return check_fast(context, state);
|
||||
|
||||
/* find PA_FX_FAST_REPLY */
|
||||
|
||||
pa = krb5_find_padata(rep->padata->val, rep->padata->len,
|
||||
KRB5_PADATA_FX_FAST, &idx);
|
||||
pa = find_pa_data(rep->padata, KRB5_PADATA_FX_FAST);
|
||||
if (pa == NULL)
|
||||
return check_fast(context, state);
|
||||
|
||||
@@ -1863,17 +2043,105 @@ fast_unwrap_as_rep(krb5_context context, int32_t nonce,
|
||||
|
||||
out:
|
||||
free_PA_FX_FAST_REPLY(&fxfastrep);
|
||||
free_KrbFastResponse(&fastrep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
fast_unwrap_error(krb5_context context, struct fast_state *state, KRB_ERROR *error)
|
||||
fast_unwrap_error(krb5_context context,
|
||||
int32_t nonce,
|
||||
struct fast_state *state,
|
||||
KRB_ERROR *error,
|
||||
METHOD_DATA *error_method)
|
||||
{
|
||||
if (state->armor_crypto == NULL)
|
||||
return check_fast(context, state);
|
||||
METHOD_DATA md;
|
||||
PA_FX_FAST_REPLY fxfastrep;
|
||||
KrbFastResponse fastrep;
|
||||
krb5_error_code ret;
|
||||
PA_DATA *pa;
|
||||
KRB_ERROR fast_error;
|
||||
|
||||
return 0;
|
||||
memset(&md, 0, sizeof(md));
|
||||
memset(&fxfastrep, 0, sizeof(fxfastrep));
|
||||
memset(&fastrep, 0, sizeof(fastrep));
|
||||
memset(&fast_error, 0, sizeof(fast_error));
|
||||
|
||||
if (error->e_data) {
|
||||
ret = decode_METHOD_DATA(error->e_data->data,
|
||||
error->e_data->length, &md, NULL);
|
||||
if (ret) {
|
||||
krb5_set_error_message(context, ret,
|
||||
N_("Failed to decode METHOD-DATA", ""));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (state->armor_crypto == NULL ||
|
||||
(pa = find_pa_data(&md, KRB5_PADATA_FX_FAST)) == NULL) {
|
||||
free_METHOD_DATA(error_method);
|
||||
*error_method = md;
|
||||
return check_fast(context, state);
|
||||
}
|
||||
|
||||
ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data,
|
||||
pa->padata_value.length,
|
||||
&fxfastrep,
|
||||
NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) {
|
||||
krb5_data data;
|
||||
ret = krb5_decrypt_EncryptedData(context,
|
||||
state->armor_crypto,
|
||||
KRB5_KU_FAST_REP,
|
||||
&fxfastrep.u.armored_data.enc_fast_rep,
|
||||
&data);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = decode_KrbFastResponse(data.data, data.length, &fastrep, NULL);
|
||||
krb5_data_free(&data);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
} else {
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* RFC 6113 5.4.3: strengthen key must be absent in error reply */
|
||||
if (fastrep.strengthen_key || nonce != fastrep.nonce) {
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pa = find_pa_data(&fastrep.padata, KRB5_PADATA_FX_ERROR);
|
||||
if (pa == NULL) {
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = krb5_rd_error(context, &pa->padata_value, &fast_error);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
free_KRB_ERROR(error);
|
||||
*error = fast_error;
|
||||
memset(&fast_error, 0, sizeof(fast_error));
|
||||
|
||||
free_METHOD_DATA(error_method);
|
||||
*error_method = fastrep.padata;
|
||||
memset(&fastrep.padata, 0, sizeof(fastrep.padata));
|
||||
|
||||
out:
|
||||
free_METHOD_DATA(&md);
|
||||
free_PA_FX_FAST_REPLY(&fxfastrep);
|
||||
free_KrbFastResponse(&fastrep);
|
||||
free_KRB_ERROR(&fast_error);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
@@ -2058,7 +2326,9 @@ make_fast_ap_fxarmor(krb5_context context,
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
|
||||
fast_wrap_req(krb5_context context,
|
||||
struct fast_state *state,
|
||||
KDC_REQ *req)
|
||||
{
|
||||
KrbFastArmor *fxarmor = NULL;
|
||||
PA_FX_FAST_REQUEST fxreq;
|
||||
@@ -2067,6 +2337,16 @@ fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
|
||||
krb5_data data;
|
||||
size_t size;
|
||||
|
||||
/* FX-COOKIE can be used without FAST armor */
|
||||
if (state->cookie.data) {
|
||||
ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_COOKIE,
|
||||
state->cookie.data, state->cookie.length);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
krb5_data_zero(&state->cookie);
|
||||
}
|
||||
|
||||
if (state->flags & KRB5_FAST_DISABLED) {
|
||||
_krb5_debug(context, 10, "fast disabled, not doing any fast wrapping");
|
||||
return 0;
|
||||
@@ -2196,6 +2476,43 @@ fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
fast_validate_conversation(METHOD_DATA *md, struct fast_state *state)
|
||||
{
|
||||
PA_DATA *pa;
|
||||
|
||||
/*
|
||||
* RFC 6113 5.4.3: PA-FX-COOKIE MUST be included if the KDC
|
||||
* expects at least one more message from the client.
|
||||
*/
|
||||
pa = find_pa_data(md, KRB5_PADATA_FX_COOKIE);
|
||||
if (pa == NULL)
|
||||
return KRB5_PREAUTH_FAILED;
|
||||
|
||||
krb5_data_free(&state->cookie);
|
||||
state->cookie = pa->padata_value;
|
||||
krb5_data_zero(&pa->padata_value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t
|
||||
available_padata_count(METHOD_DATA *md)
|
||||
{
|
||||
size_t i, count = 0;
|
||||
|
||||
for (i = 0; i < md->len; i++) {
|
||||
PA_DATA *pa = &md->val[i];
|
||||
|
||||
if (pa->padata_type == KRB5_PADATA_FX_COOKIE ||
|
||||
pa->padata_type == KRB5_PADATA_FX_ERROR)
|
||||
continue;
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
init_creds_step(krb5_context context,
|
||||
@@ -2219,7 +2536,7 @@ init_creds_step(krb5_context context,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define MAX_PA_COUNTER 10
|
||||
#define MAX_PA_COUNTER 15
|
||||
if (ctx->pa_counter > MAX_PA_COUNTER) {
|
||||
krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
|
||||
N_("Looping %d times while getting "
|
||||
@@ -2296,13 +2613,8 @@ init_creds_step(krb5_context context,
|
||||
NULL);
|
||||
if (ret == 0 && ctx->pk_init_ctx) {
|
||||
PA_DATA *pa_pkinit_kx;
|
||||
int idx = 0;
|
||||
|
||||
pa_pkinit_kx =
|
||||
krb5_find_padata(rep.kdc_rep.padata->val,
|
||||
rep.kdc_rep.padata->len,
|
||||
KRB5_PADATA_PKINIT_KX,
|
||||
&idx);
|
||||
pa_pkinit_kx = find_pa_data(rep.kdc_rep.padata, KRB5_PADATA_PKINIT_KX);
|
||||
|
||||
ret = _krb5_pk_kx_confirm(context, ctx->pk_init_ctx,
|
||||
ctx->fast_state.reply_key,
|
||||
@@ -2344,7 +2656,8 @@ init_creds_step(krb5_context context,
|
||||
/*
|
||||
* Unwrap KRB-ERROR
|
||||
*/
|
||||
ret = fast_unwrap_error(context, &ctx->fast_state, &ctx->error);
|
||||
ret = fast_unwrap_error(context, ctx->nonce, &ctx->fast_state,
|
||||
&ctx->error, &ctx->md);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
@@ -2361,24 +2674,17 @@ init_creds_step(krb5_context context,
|
||||
* more try.
|
||||
*/
|
||||
|
||||
if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
|
||||
|
||||
free_METHOD_DATA(&ctx->md);
|
||||
memset_s(&ctx->md, sizeof(ctx->md), 0, sizeof(ctx->md));
|
||||
|
||||
if (ctx->error.e_data) {
|
||||
ret = decode_METHOD_DATA(ctx->error.e_data->data,
|
||||
ctx->error.e_data->length,
|
||||
&ctx->md,
|
||||
NULL);
|
||||
if (ret)
|
||||
krb5_set_error_message(context, ret,
|
||||
N_("Failed to decode METHOD-DATA", ""));
|
||||
} else {
|
||||
if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED ||
|
||||
ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED) {
|
||||
if (available_padata_count(&ctx->md) == 0) {
|
||||
krb5_set_error_message(context, ret,
|
||||
N_("Preauth required but no preauth "
|
||||
"options send by KDC", ""));
|
||||
}
|
||||
if (ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
|
||||
ret = fast_validate_conversation(&ctx->md, &ctx->fast_state);
|
||||
else
|
||||
ret = 0;
|
||||
} else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
|
||||
/*
|
||||
* Try adapt to timeskrew when we are using pre-auth, and
|
||||
@@ -2819,7 +3125,7 @@ krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx)
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if ((flags & 1) == 0)
|
||||
if ((flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE) == 0)
|
||||
break;
|
||||
|
||||
ret = krb5_sendto_context (context, stctx, &out,
|
||||
@@ -3056,3 +3362,88 @@ krb5_get_init_creds_keytab(krb5_context context,
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
|
||||
_krb5_init_creds_set_gss_mechanism(krb5_context context,
|
||||
krb5_gss_init_ctx gssic,
|
||||
const struct gss_OID_desc_struct *gss_mech)
|
||||
{
|
||||
gssic->mech = gss_mech; /* OIDs are interned, so no copy required */
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION const struct gss_OID_desc_struct * KRB5_LIB_CALL
|
||||
_krb5_init_creds_get_gss_mechanism(krb5_context context,
|
||||
krb5_gss_init_ctx gssic)
|
||||
{
|
||||
return gssic->mech;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
|
||||
_krb5_init_creds_set_gss_cred(krb5_context context,
|
||||
krb5_gss_init_ctx gssic,
|
||||
struct gss_cred_id_t_desc_struct *gss_cred)
|
||||
{
|
||||
if (gssic->cred != gss_cred &&
|
||||
(gssic->flags & GSSIC_FLAG_RELEASE_CRED))
|
||||
gssic->release_cred(context, gssic, gssic->cred);
|
||||
|
||||
gssic->cred = gss_cred;
|
||||
gssic->flags |= GSSIC_FLAG_RELEASE_CRED;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION const struct gss_cred_id_t_desc_struct * KRB5_LIB_CALL
|
||||
_krb5_init_creds_get_gss_cred(krb5_context context,
|
||||
krb5_gss_init_ctx gssic)
|
||||
{
|
||||
return gssic->cred;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
|
||||
_krb5_init_creds_set_gss_context(krb5_context context,
|
||||
krb5_gss_init_ctx gssic,
|
||||
struct gss_ctx_id_t_desc_struct *gss_ctx)
|
||||
{
|
||||
if (gssic->ctx != gss_ctx)
|
||||
gssic->delete_sec_context(context, gssic, gssic->ctx);
|
||||
gssic->ctx = gss_ctx;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION const struct gss_ctx_id_t_desc_struct * KRB5_LIB_CALL
|
||||
_krb5_init_creds_get_gss_context(krb5_context context,
|
||||
krb5_gss_init_ctx gssic)
|
||||
{
|
||||
return gssic->ctx;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
_krb5_init_creds_init_gss(krb5_context context,
|
||||
krb5_init_creds_context ctx,
|
||||
krb5_gssic_step step,
|
||||
krb5_gssic_finish finish,
|
||||
krb5_gssic_release_cred release_cred,
|
||||
krb5_gssic_delete_sec_context delete_sec_context,
|
||||
const struct gss_cred_id_t_desc_struct *gss_cred,
|
||||
const struct gss_OID_desc_struct *gss_mech,
|
||||
unsigned int flags)
|
||||
{
|
||||
krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
|
||||
|
||||
gssic = calloc(1, sizeof(*gssic));
|
||||
if (gssic == NULL)
|
||||
return krb5_enomem(context);
|
||||
|
||||
if (ctx->gss_init_ctx)
|
||||
free_gss_init_ctx(context, ctx->gss_init_ctx);
|
||||
ctx->gss_init_ctx = gssic;
|
||||
|
||||
gssic->cred = (struct gss_cred_id_t_desc_struct *)gss_cred;
|
||||
gssic->mech = gss_mech;
|
||||
gssic->flags = flags;
|
||||
|
||||
gssic->step = step;
|
||||
gssic->finish = finish;
|
||||
gssic->release_cred = release_cred;
|
||||
gssic->delete_sec_context = delete_sec_context;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -901,6 +901,32 @@ can be given as a number followed by a unit, such as
|
||||
.Dq 2d
|
||||
for
|
||||
.Dq two days .
|
||||
.It Li enable_gss_preauth = Va boolean
|
||||
Enables pre-authentication using a GSS-API mechanism supported by the client and KDC.
|
||||
The GSS-API initiator and AS request client names must match, unless the
|
||||
.Li
|
||||
WELLKNOWN/FEDERATED
|
||||
name was used in the AS request, in which case the AS reply will contain the
|
||||
GSS-API initiator name. Authorization and mapping behavior may be customized
|
||||
by plugins. If synthetic clients are enabled, then the GSS-API initiator need
|
||||
not exist in the local database. GSS-API pre-authentication is disabled by
|
||||
default.
|
||||
.It Li enable_gss_auth_data = Va boolean
|
||||
When using GSS-API pre-authentication, includes a Kerberos authorization data
|
||||
element containing naming attributes associated with the GSS-API initiator. This
|
||||
is disabled by default as it may significantly increase the size of returned
|
||||
tickets.
|
||||
.It Li gss_mechanisms_allowed = Va mechs ...
|
||||
A list of GSS-API mechanisms that may be used for GSS-API pre-authentication.
|
||||
.It Li gss_cross_realm_mechanisms_allowed = Va mechs ...
|
||||
A list of GSS-API mechanisms that, when using the default authorization
|
||||
mechanism, will be permitted to map Kerberos principals in foreign realms. The
|
||||
list is empty by default. Initiator names from mechanisms not on this list will
|
||||
be mapped to an enterprise principal in the AS-REQ realm. This option is
|
||||
intended to avoid conflating GSS-API pre-authentication and Kerberos
|
||||
cross-realm authentication. The behavior is provided by the default
|
||||
authorization mechanism and will be overridden by an authorization plugin.
|
||||
Mechanisms may be identified by dot-separated OID or a short name.
|
||||
.It Li historical_anon_realm = Va boolean
|
||||
Enables pre-7.0 non-RFC-comformant KDC behavior.
|
||||
With this option set to
|
||||
|
||||
@@ -715,10 +715,11 @@ typedef EncAPRepPart krb5_ap_rep_enc_part;
|
||||
#define KRB5_WELLKNOWN_NAME ("WELLKNOWN")
|
||||
#define KRB5_ANON_NAME ("ANONYMOUS")
|
||||
#define KRB5_ANON_REALM ("WELLKNOWN:ANONYMOUS")
|
||||
#define KRB5_FEDERATED_NAME ("FEDERATED")
|
||||
#define KRB5_FEDERATED_REALM ("WELLKNOWN:FEDERATED")
|
||||
#define KRB5_WELLKNOWN_ORG_H5L_REALM ("WELLKNOWN:ORG.H5L")
|
||||
#define KRB5_DIGEST_NAME ("digest")
|
||||
|
||||
|
||||
#define KRB5_PKU2U_REALM_NAME ("WELLKNOWN:PKU2U")
|
||||
#define KRB5_LKDC_REALM_NAME ("WELLKNOWN:COM.APPLE.LKDC")
|
||||
|
||||
|
||||
@@ -101,12 +101,13 @@ error_code DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED, "Digest in signedData not accepte
|
||||
error_code PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED, "Public key encryption not supported"
|
||||
|
||||
## these are never used
|
||||
#index 80
|
||||
#index 85
|
||||
#prefix KRB5_IAKERB
|
||||
#error_code ERR_KDC_NOT_FOUND, "IAKERB proxy could not find a KDC"
|
||||
#error_code ERR_KDC_NO_RESPONSE, "IAKERB proxy never reeived a response from a KDC"
|
||||
|
||||
# 82-93 are reserved
|
||||
index 91
|
||||
error_code MORE_PREAUTH_DATA_REQUIRED, "More pre-authentication data required"
|
||||
|
||||
index 94
|
||||
error_code INVALID_HASH_ALG, "Invalid OTP digest algorithm"
|
||||
|
||||
@@ -140,6 +140,13 @@ struct krb5_dh_moduli;
|
||||
/* v4 glue */
|
||||
struct _krb5_krb_auth_data;
|
||||
|
||||
struct krb5_gss_init_ctx_data;
|
||||
typedef struct krb5_gss_init_ctx_data *krb5_gss_init_ctx;
|
||||
|
||||
struct gss_ctx_id_t_desc_struct;
|
||||
struct gss_cred_id_t_desc_struct;
|
||||
struct gss_OID_desc_struct;
|
||||
|
||||
#include <der.h>
|
||||
|
||||
#include <krb5.h>
|
||||
@@ -152,6 +159,34 @@ struct _krb5_krb_auth_data;
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
typedef krb5_error_code (KRB5_LIB_CALL *krb5_gssic_step)(
|
||||
krb5_context,
|
||||
krb5_gss_init_ctx,
|
||||
const krb5_creds *,
|
||||
KDCOptions options,
|
||||
krb5_data *,
|
||||
krb5_data *,
|
||||
krb5_data *);
|
||||
|
||||
typedef krb5_error_code (KRB5_LIB_CALL *krb5_gssic_finish)(
|
||||
krb5_context,
|
||||
krb5_gss_init_ctx,
|
||||
const krb5_creds *,
|
||||
krb5int32,
|
||||
krb5_enctype,
|
||||
krb5_principal *,
|
||||
krb5_keyblock **);
|
||||
|
||||
typedef void (KRB5_LIB_CALL *krb5_gssic_release_cred)(
|
||||
krb5_context,
|
||||
krb5_gss_init_ctx,
|
||||
struct gss_cred_id_t_desc_struct *);
|
||||
|
||||
typedef void (KRB5_LIB_CALL *krb5_gssic_delete_sec_context)(
|
||||
krb5_context,
|
||||
krb5_gss_init_ctx,
|
||||
struct gss_ctx_id_t_desc_struct *);
|
||||
|
||||
#include <krb5-private.h>
|
||||
|
||||
#include "heim_threads.h"
|
||||
|
||||
@@ -525,6 +525,7 @@ EXPORTS
|
||||
krb5_principal_get_realm
|
||||
krb5_principal_get_type
|
||||
krb5_principal_is_anonymous
|
||||
krb5_principal_is_federated
|
||||
krb5_principal_is_krbtgt
|
||||
krb5_principal_is_root_krbtgt
|
||||
krb5_principal_match
|
||||
@@ -792,6 +793,15 @@ EXPORTS
|
||||
_krb5_SP800_108_HMAC_KDF
|
||||
_krb5_get_ad
|
||||
|
||||
; Shared with GSSAPI preauth wrapper
|
||||
_krb5_init_creds_set_gss_mechanism
|
||||
_krb5_init_creds_get_gss_mechanism
|
||||
_krb5_init_creds_set_gss_cred
|
||||
_krb5_init_creds_get_gss_cred
|
||||
_krb5_init_creds_set_gss_context
|
||||
_krb5_init_creds_get_gss_context
|
||||
_krb5_init_creds_init_gss
|
||||
|
||||
; Shared with libkadm5
|
||||
_krb5_load_plugins
|
||||
_krb5_unload_plugins
|
||||
|
||||
@@ -1312,6 +1312,28 @@ krb5_principal_is_anonymous(krb5_context context,
|
||||
return strcmp(p->realm, KRB5_ANON_REALM) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true iff name is WELLKNOWN/FEDERATED
|
||||
*
|
||||
* @ingroup krb5_principal
|
||||
*/
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
|
||||
krb5_principal_is_federated(krb5_context context,
|
||||
krb5_const_principal p)
|
||||
{
|
||||
if (p->name.name_type != KRB5_NT_WELLKNOWN &&
|
||||
p->name.name_type != KRB5_NT_UNKNOWN)
|
||||
return FALSE;
|
||||
|
||||
if (p->name.name_string.len != 2 ||
|
||||
strcmp(p->name.name_string.val[0], KRB5_WELLKNOWN_NAME) != 0 ||
|
||||
strcmp(p->name.name_string.val[1], KRB5_FEDERATED_NAME) != 0)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
tolower_ascii(int c)
|
||||
{
|
||||
|
||||
@@ -522,6 +522,7 @@ HEIMDAL_KRB5_2.0 {
|
||||
krb5_principal_set_realm;
|
||||
krb5_principal_set_type;
|
||||
krb5_principal_is_anonymous;
|
||||
krb5_principal_is_federated;
|
||||
krb5_principal_is_krbtgt;
|
||||
krb5_principal_is_root_krbtgt;
|
||||
krb5_print_address;
|
||||
@@ -784,6 +785,15 @@ HEIMDAL_KRB5_2.0 {
|
||||
_krb5_SP800_108_HMAC_KDF;
|
||||
_krb5_get_ad;
|
||||
|
||||
# Shared with GSSAPI preauth wrapper
|
||||
_krb5_init_creds_set_gss_mechanism;
|
||||
_krb5_init_creds_get_gss_mechanism;
|
||||
_krb5_init_creds_set_gss_cred;
|
||||
_krb5_init_creds_get_gss_cred;
|
||||
_krb5_init_creds_set_gss_context;
|
||||
_krb5_init_creds_get_gss_context;
|
||||
_krb5_init_creds_init_gss;
|
||||
|
||||
# Shared with libkadm5
|
||||
_krb5_load_plugins;
|
||||
_krb5_unload_plugins;
|
||||
|
||||
Reference in New Issue
Block a user