gss: port NegoEx implementation from MIT
An implementation of draft-zhu-negoex-04 for MIT Kerberos was developed in 2011. This has been recently integrated, with many fixes from Greg Hudson. This commit ports it to Heimdal. The implementation has been interoperability tested with MIT Kerberos and Windows, using the GSS EAP mechanism developed as part of the Moonshot project. The SPNEGO code was also updated to import the state machine from Apple which improves mechListMIC processing and avoids discarding initial context tokens generated during mechanism probing, that can be used for optimistic tokens. Finally, to aid in testing, the GSS-API mechanism glue configuration file can be changed using the environment variable GSS_MECH_CONFIG. This environment variable name, along with the format of the configuration file, is compatible with MIT (although it would be difficult for a single mechanism binary to support both implementations).
This commit is contained in:
@@ -121,6 +121,12 @@ can point a file with the environment variable @samp{KRB5_CONFIG}.
|
||||
env KRB5_CONFIG=$HOME/etc/krb5.conf kinit user@@REALM
|
||||
@end example
|
||||
|
||||
@cindex GSS_MECH_CONFIG
|
||||
The GSS-API mechanism configuration file can also be changed from the
|
||||
default with the enviornment variable @samp{GSS_MECH_CONFIG}. Note that
|
||||
this file only configures additional plugin mechanisms: Kerberos, NTLM
|
||||
and SPNEGO are built in to the Heimdal GSS-API library.
|
||||
|
||||
@node Creating the database, Modifying the database, Configuration file, Setting up a realm
|
||||
@section Creating the database
|
||||
|
||||
|
1345
doc/standardisation/draft-zhu-negoex-04.txt
Normal file
1345
doc/standardisation/draft-zhu-negoex-04.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -49,6 +49,7 @@ CLEANFILES = \
|
||||
getarg.h \
|
||||
glob.h \
|
||||
gssapi.h \
|
||||
gssapi_asn1.h \
|
||||
gssapi_mech.h \
|
||||
hdb-private.h \
|
||||
hdb-protos.h \
|
||||
|
@@ -14,7 +14,7 @@ AM_CPPFLAGS += \
|
||||
-I$(srcdir)/spnego \
|
||||
$(INCLUDE_libintl)
|
||||
|
||||
lib_LTLIBRARIES = libgssapi.la
|
||||
lib_LTLIBRARIES = libgssapi.la test_negoex_mech.la
|
||||
|
||||
krb5src = \
|
||||
krb5/8003.c \
|
||||
@@ -157,6 +157,9 @@ mechsrc = \
|
||||
mech/gss_wrap.c \
|
||||
mech/gss_wrap_size_limit.c \
|
||||
mech/gss_inquire_sec_context_by_oid.c \
|
||||
mech/gssspi_exchange_meta_data.c \
|
||||
mech/gssspi_query_mechanism_info.c \
|
||||
mech/gssspi_query_meta_data.c \
|
||||
mech/mech_switch.h \
|
||||
mech/mech_locl.h \
|
||||
mech/name.h \
|
||||
@@ -169,7 +172,10 @@ spnegosrc = \
|
||||
spnego/cred_stubs.c \
|
||||
spnego/external.c \
|
||||
spnego/init_sec_context.c \
|
||||
spnego/negoex_ctx.c \
|
||||
spnego/negoex_util.c \
|
||||
spnego/spnego_locl.h \
|
||||
spnego/negoex_locl.h \
|
||||
$(srcdir)/spnego/spnego-private.h
|
||||
|
||||
ntlmsrc = \
|
||||
@@ -217,6 +223,8 @@ dist_libgssapi_la_SOURCES = \
|
||||
nodist_libgssapi_la_SOURCES = \
|
||||
gkrb5_err.c \
|
||||
gkrb5_err.h \
|
||||
negoex_err.c \
|
||||
negoex_err.h \
|
||||
$(BUILT_SOURCES)
|
||||
|
||||
libgssapi_la_DEPENDENCIES = version-script.map
|
||||
@@ -239,6 +247,7 @@ man_MANS = gssapi.3 gss_acquire_cred.3 mech/mech.5 gss-token.1
|
||||
|
||||
include_HEADERS = gssapi.h
|
||||
noinst_HEADERS = \
|
||||
gssapi_asn1.h \
|
||||
gssapi_mech.h \
|
||||
$(srcdir)/ntlm/ntlm-private.h \
|
||||
$(srcdir)/spnego/spnego-private.h \
|
||||
@@ -252,7 +261,7 @@ nobase_include_HEADERS = \
|
||||
gssapi/gssapi_spnego.h
|
||||
|
||||
gssapidir = $(includedir)/gssapi
|
||||
nodist_gssapi_HEADERS = gkrb5_err.h
|
||||
nodist_gssapi_HEADERS = gkrb5_err.h negoex_err.h
|
||||
|
||||
gssapi_files = asn1_GSSAPIContextToken.x
|
||||
|
||||
@@ -265,7 +274,8 @@ spnego_files = \
|
||||
asn1_NegHints.x \
|
||||
asn1_NegTokenInit.x \
|
||||
asn1_NegTokenInitWin.x \
|
||||
asn1_NegTokenResp.x
|
||||
asn1_NegTokenResp.x \
|
||||
asn1_NegResultEnum.x
|
||||
|
||||
BUILTHEADERS = \
|
||||
$(srcdir)/krb5/gsskrb5-private.h \
|
||||
@@ -279,11 +289,12 @@ $(libgssapi_la_OBJECTS): $(srcdir)/version-script.map
|
||||
|
||||
BUILT_SOURCES = $(spnego_files:.x=.c) $(gssapi_files:.x=.c)
|
||||
|
||||
$(libgssapi_la_OBJECTS): gkrb5_err.h
|
||||
$(libgssapi_la_OBJECTS): gkrb5_err.h negoex_err.h
|
||||
gkrb5_err.h: $(srcdir)/krb5/gkrb5_err.et
|
||||
negoex_err.h: $(srcdir)/spnego/negoex_err.et
|
||||
|
||||
CLEANFILES = $(BUILT_SOURCES) \
|
||||
gkrb5_err.h gkrb5_err.c \
|
||||
gkrb5_err.[ch] negoex_err.[ch] \
|
||||
$(spnego_files) spnego_asn1*.h* spnego_asn1_files spnego_asn1-template.[cx] \
|
||||
$(gssapi_files) gssapi_asn1*.h* gssapi_asn1_files gssapi_asn1-template.[cx] \
|
||||
gss-commands.h gss-commands.c
|
||||
@@ -365,6 +376,8 @@ EXTRA_DIST = \
|
||||
mech/gssapi.asn1 \
|
||||
spnego/spnego.asn1 \
|
||||
spnego/spnego.opt \
|
||||
spnego/negoex_err.et \
|
||||
test_negoex_mech.c \
|
||||
version-script.map \
|
||||
gss-commands.in
|
||||
|
||||
@@ -375,6 +388,20 @@ $(libgssapi_la_OBJECTS): $(srcdir)/gssapi/gssapi_oid.h
|
||||
gkrb5_err.h gkrb5_err.c: $(srcdir)/krb5/gkrb5_err.et
|
||||
$(COMPILE_ET) $(srcdir)/krb5/gkrb5_err.et
|
||||
|
||||
negoex_err.h negoex_err.c: $(srcdir)/spnego/negoex_err.et
|
||||
$(COMPILE_ET) $(srcdir)/spnego/negoex_err.et
|
||||
|
||||
$(srcdir)/gssapi/gssapi_oid.h $(srcdir)/mech/gss_oid.c:
|
||||
perl $(srcdir)/gen-oid.pl -b base -h $(srcdir)/oid.txt > $(srcdir)/gssapi/gssapi_oid.h
|
||||
perl $(srcdir)/gen-oid.pl -b base $(srcdir)/oid.txt > $(srcdir)/mech/gss_oid.c
|
||||
|
||||
#
|
||||
# NegoEx test mechanism, uses decode_GSSAPIContextToken
|
||||
#
|
||||
|
||||
test_negoex_mech_la_SOURCES = test_negoex_mech.c $(gssapi_files:.x=.c)
|
||||
test_negoex_mech_la_LDFLAGS = -module
|
||||
test_negoex_mech_la_LIBADD = \
|
||||
$(top_builddir)/lib/asn1/libasn1.la \
|
||||
libgssapi.la
|
||||
|
||||
|
@@ -173,6 +173,9 @@ mechsrc = \
|
||||
mech/gss_wrap.c \
|
||||
mech/gss_wrap_size_limit.c \
|
||||
mech/gss_inquire_sec_context_by_oid.c \
|
||||
mech/gssspi_exchange_meta_data.c \
|
||||
mech/gssspi_query_mechanism_info.c \
|
||||
mech/gssspi_query_meta_data.c \
|
||||
mech/mech_switch.h \
|
||||
mech/mech_locl.h \
|
||||
mech/name.h \
|
||||
@@ -185,7 +188,10 @@ spnegosrc = \
|
||||
spnego/cred_stubs.c \
|
||||
spnego/external.c \
|
||||
spnego/init_sec_context.c \
|
||||
spnego/spnego_locl.h
|
||||
spnego/negoex_ctx.c \
|
||||
spnego/negoex_util.c \
|
||||
spnego/spnego_locl.h \
|
||||
spnego/negoex_locl.h
|
||||
|
||||
ntlmsrc = \
|
||||
ntlm/accept_sec_context.c \
|
||||
@@ -258,6 +264,11 @@ $(OBJ)\gkrb5_err.c $(OBJ)\gkrb5_err.h: krb5\gkrb5_err.et
|
||||
$(BINDIR)\compile_et.exe $(SRCDIR)\krb5\gkrb5_err.et
|
||||
cd $(SRCDIR)
|
||||
|
||||
$(OBJ)\negoex_err.c $(OBJ)\negoex_err.h: spnego\negoex_err.et
|
||||
cd $(OBJ)
|
||||
$(BINDIR)\compile_et.exe $(SRCDIR)\spnego\negoex_err.et
|
||||
cd $(SRCDIR)
|
||||
|
||||
INCFILES= \
|
||||
$(INCDIR)\gssapi.h \
|
||||
$(INCDIR)\gssapi\gssapi.h \
|
||||
@@ -270,6 +281,7 @@ INCFILES= \
|
||||
$(OBJ)\spnego\spnego-private.h \
|
||||
$(OBJ)\krb5\gsskrb5-private.h \
|
||||
$(OBJ)\gkrb5_err.h \
|
||||
$(OBJ)\negoex_err.h \
|
||||
$(OBJ)\gssapi\gssapi_asn1.h \
|
||||
$(OBJ)\gssapi\gssapi_asn1-priv.h \
|
||||
$(OBJ)\spnego\spnego_asn1.h \
|
||||
@@ -409,12 +421,17 @@ libgssapi_OBJs = \
|
||||
$(OBJ)\mech/gss_wrap.obj \
|
||||
$(OBJ)\mech/gss_wrap_size_limit.obj \
|
||||
$(OBJ)\mech/gss_inquire_sec_context_by_oid.obj \
|
||||
$(OBJ)\mech/gssspi_exchange_meta_data.obj \
|
||||
$(OBJ)\mech/gssspi_query_mechanism_info.obj \
|
||||
$(OBJ)\mech/gssspi_query_meta_data.obj \
|
||||
$(OBJ)\spnego/accept_sec_context.obj \
|
||||
$(OBJ)\spnego/compat.obj \
|
||||
$(OBJ)\spnego/context_stubs.obj \
|
||||
$(OBJ)\spnego/cred_stubs.obj \
|
||||
$(OBJ)\spnego/external.obj \
|
||||
$(OBJ)\spnego/init_sec_context.obj \
|
||||
$(OBJ)\spnego/negoex_ctx.obj \
|
||||
$(OBJ)\spnego/negoex_util.obj \
|
||||
$(OBJ)\ntlm/accept_sec_context.obj \
|
||||
$(OBJ)\ntlm/acquire_cred.obj \
|
||||
$(OBJ)\ntlm/add_cred.obj \
|
||||
@@ -447,6 +464,7 @@ libgssapi_OBJs = \
|
||||
$(OBJ)\ntlm/set_sec_context_option.obj \
|
||||
$(OBJ)\ntlm/kdc.obj \
|
||||
$(OBJ)\gkrb5_err.obj \
|
||||
$(OBJ)\negoex_err.obj \
|
||||
$(spnego_files:.x=.obj) \
|
||||
$(gssapi_files:.x=.obj)
|
||||
|
||||
@@ -588,6 +606,7 @@ $(OBJ)\gss-commands.c $(OBJ)\gss-commands.h: gss-commands.in
|
||||
|
||||
(generate-obj-macro "libgssapi_OBJs"
|
||||
(concat "\t$(OBJ)\\gkrb5_err.obj \\\n"
|
||||
"\t$(OBJ)\\negoex_err.obj \\\n"
|
||||
"\t$(spnego_files:.x=.obj) \\\n"
|
||||
"\t$(gssapi_files:.x=.obj)")
|
||||
"krb5src" "mechsrc" "spnegosrc" "ntlmsrc")
|
||||
|
@@ -132,6 +132,12 @@ extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_c_inq_win2k_pac_x_oid_desc;
|
||||
extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_c_inq_sspi_session_key_oid_desc;
|
||||
#define GSS_C_INQ_SSPI_SESSION_KEY (&__gss_c_inq_sspi_session_key_oid_desc)
|
||||
|
||||
extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_c_inq_negoex_key_oid_desc;
|
||||
#define GSS_C_INQ_NEGOEX_KEY (&__gss_c_inq_negoex_key_oid_desc)
|
||||
|
||||
extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_c_inq_negoex_verify_key_oid_desc;
|
||||
#define GSS_C_INQ_NEGOEX_VERIFY_KEY (&__gss_c_inq_negoex_verify_key_oid_desc)
|
||||
|
||||
/*
|
||||
* "Standard" mechs
|
||||
*/
|
||||
@@ -151,6 +157,9 @@ extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_c_peer_has_updated_spnego_oid_desc
|
||||
extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_c_ntlm_reset_crypto_oid_desc;
|
||||
#define GSS_C_NTLM_RESET_CRYPTO (&__gss_c_ntlm_reset_crypto_oid_desc)
|
||||
|
||||
extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_negoex_mechanism_oid_desc;
|
||||
#define GSS_NEGOEX_MECHANISM (&__gss_negoex_mechanism_oid_desc)
|
||||
|
||||
/*
|
||||
* OID mappings with name and short description and and slightly longer description
|
||||
*/
|
||||
@@ -238,4 +247,7 @@ extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_c_ma_compress_oid_desc;
|
||||
extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_c_ma_ctx_trans_oid_desc;
|
||||
#define GSS_C_MA_CTX_TRANS (&__gss_c_ma_ctx_trans_oid_desc)
|
||||
|
||||
extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_c_ma_negoex_and_spnego_oid_desc;
|
||||
#define GSS_C_MA_NEGOEX_AND_SPNEGO (&__gss_c_ma_negoex_and_spnego_oid_desc)
|
||||
|
||||
#endif /* GSSAPI_GSSAPI_OID */
|
||||
|
@@ -50,6 +50,38 @@ extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_spnego_mechanism_oid_desc;
|
||||
#define GSS_SPNEGO_MECHANISM (&__gss_spnego_mechanism_oid_desc)
|
||||
#define gss_mech_spnego GSS_SPNEGO_MECHANISM
|
||||
|
||||
/*
|
||||
* NegoEx extensions, to be implemented by mechanisms
|
||||
*/
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gssspi_query_mechanism_info(
|
||||
OM_uint32 * /* minor_status */,
|
||||
gss_const_OID /* mech_oid */,
|
||||
unsigned char[16] /* auth_scheme */
|
||||
);
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gssspi_query_meta_data(
|
||||
OM_uint32 * /* minor_status */,
|
||||
gss_const_OID /* mech_oid */,
|
||||
gss_cred_id_t /* cred_handle */,
|
||||
gss_ctx_id_t * /* context_handle */,
|
||||
gss_const_name_t /* targ_name */,
|
||||
OM_uint32 /* req_flags */,
|
||||
gss_buffer_t /* meta_data */
|
||||
);
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gssspi_exchange_meta_data(
|
||||
OM_uint32 * /* minor_status */,
|
||||
gss_const_OID /* mech_oid */,
|
||||
gss_cred_id_t /* cred_handle */,
|
||||
gss_ctx_id_t * /* context_handle */,
|
||||
gss_const_name_t /* targ_name */,
|
||||
OM_uint32 /* req_flags */,
|
||||
gss_const_buffer_t /* meta_data */
|
||||
);
|
||||
|
||||
GSSAPI_CPP_END
|
||||
|
||||
#endif /* GSSAPI_SPNEGO_H_ */
|
||||
|
@@ -484,6 +484,29 @@ _gss_get_neg_mechs_t(OM_uint32 *minor_status,
|
||||
gss_const_cred_id_t cred_handle,
|
||||
gss_OID_set *mechs);
|
||||
|
||||
typedef OM_uint32 GSSAPI_CALLCONV
|
||||
_gss_query_mechanism_info_t(OM_uint32 *minor_status,
|
||||
gss_const_OID mech_oid,
|
||||
unsigned char auth_scheme[16]);
|
||||
|
||||
typedef OM_uint32 GSSAPI_CALLCONV
|
||||
_gss_query_meta_data_t(OM_uint32 *minor_status,
|
||||
gss_const_OID mech_oid,
|
||||
gss_cred_id_t cred_handle,
|
||||
gss_ctx_id_t *ctx_handle,
|
||||
gss_const_name_t targ_name,
|
||||
OM_uint32 req_flags,
|
||||
gss_buffer_t meta_data);
|
||||
|
||||
typedef OM_uint32 GSSAPI_CALLCONV
|
||||
_gss_exchange_meta_data_t(OM_uint32 *minor_status,
|
||||
gss_const_OID mech_oid,
|
||||
gss_cred_id_t cred_handle,
|
||||
gss_ctx_id_t *ctx_handle,
|
||||
gss_const_name_t targ_name,
|
||||
OM_uint32 req_flags,
|
||||
gss_const_buffer_t meta_data);
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
@@ -597,6 +620,9 @@ typedef struct gssapi_mech_interface_desc {
|
||||
_gss_store_cred_into_t *gm_store_cred_into;
|
||||
_gss_set_neg_mechs_t *gm_set_neg_mechs;
|
||||
_gss_get_neg_mechs_t *gm_get_neg_mechs;
|
||||
_gss_query_mechanism_info_t *gm_query_mechanism_info;
|
||||
_gss_query_meta_data_t *gm_query_meta_data;
|
||||
_gss_exchange_meta_data_t *gm_exchange_meta_data;
|
||||
struct gss_mech_compat_desc_struct *gm_compat;
|
||||
} gssapi_mech_interface_desc, *gssapi_mech_interface;
|
||||
|
||||
@@ -669,4 +695,17 @@ gss_mg_set_error_string(gss_OID mech,
|
||||
OM_uint32 maj, OM_uint32 min,
|
||||
const char *fmt, ...);
|
||||
|
||||
gss_cred_id_t
|
||||
_gss_mg_find_mech_cred(gss_const_cred_id_t cred_handle,
|
||||
gss_const_OID mech_type);
|
||||
|
||||
#include <krb5.h>
|
||||
|
||||
/*
|
||||
* Mechglue krb5 context for use by NegoEx. This is not shared with the
|
||||
* krb5 GSS mechanism so we don't clobber its error state.
|
||||
*/
|
||||
krb5_context
|
||||
_gss_mg_krb5_context(void);
|
||||
|
||||
#endif /* GSSAPI_MECH_H */
|
||||
|
@@ -401,6 +401,9 @@ static gssapi_mech_interface_desc krb5_mech = {
|
||||
_gsskrb5_store_cred_into,
|
||||
NULL, /* gm_set_neg_mechs */
|
||||
NULL, /* gm_get_neg_mechs */
|
||||
NULL, /* gm_query_mechanism_info */
|
||||
NULL, /* gm_query_meta_data */
|
||||
NULL, /* gm_exchange_meta_data */
|
||||
NULL /* gm_compat */
|
||||
};
|
||||
|
||||
|
@@ -195,3 +195,6 @@ EXPORTS
|
||||
__gss_c_ma_pfs_oid_desc DATA
|
||||
__gss_c_ma_compress_oid_desc DATA
|
||||
__gss_c_ma_ctx_trans_oid_desc DATA
|
||||
__gss_c_ma_negoex_and_spnego_oid_desc DATA
|
||||
__gss_c_inq_negoex_key_oid_desc DATA
|
||||
__gss_c_inq_negoex_verify_key_oid_desc DATA
|
||||
|
@@ -37,6 +37,7 @@
|
||||
#include "heim_threads.h"
|
||||
#include <krb5.h>
|
||||
#include "krb5_locl.h"
|
||||
#include "negoex_err.h"
|
||||
|
||||
struct mg_thread_ctx {
|
||||
gss_OID mech;
|
||||
@@ -99,6 +100,8 @@ _gss_mechglue_thread(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
krb5_add_et_list(ctx->context, initialize_ngex_error_table_r);
|
||||
|
||||
HEIMDAL_setspecific(context_key, ctx, ret);
|
||||
if (ret) {
|
||||
krb5_free_context(ctx->context);
|
||||
@@ -109,6 +112,16 @@ _gss_mechglue_thread(void)
|
||||
return ctx;
|
||||
}
|
||||
|
||||
krb5_context
|
||||
_gss_mg_krb5_context(void)
|
||||
{
|
||||
struct mg_thread_ctx *mg;
|
||||
|
||||
mg = _gss_mechglue_thread();
|
||||
|
||||
return mg ? mg->context : NULL;
|
||||
}
|
||||
|
||||
OM_uint32
|
||||
_gss_mg_get_error(const gss_OID mech,
|
||||
OM_uint32 value,
|
||||
|
@@ -30,8 +30,10 @@
|
||||
|
||||
#include "mech_locl.h"
|
||||
|
||||
static gss_cred_id_t
|
||||
_gss_mech_cred_find(gss_const_cred_id_t cred_handle, gss_OID mech_type)
|
||||
gss_cred_id_t
|
||||
_gss_mg_find_mech_cred(
|
||||
gss_const_cred_id_t cred_handle,
|
||||
gss_const_OID mech_type)
|
||||
{
|
||||
struct _gss_cred *cred = (struct _gss_cred *)cred_handle;
|
||||
struct _gss_mechanism_cred *mc;
|
||||
@@ -227,7 +229,7 @@ gss_init_sec_context(OM_uint32 * minor_status,
|
||||
if (m->gm_flags & GM_USE_MG_CRED)
|
||||
cred_handle = initiator_cred_handle;
|
||||
else
|
||||
cred_handle = _gss_mech_cred_find(initiator_cred_handle, mech_type);
|
||||
cred_handle = _gss_mg_find_mech_cred(initiator_cred_handle, mech_type);
|
||||
|
||||
if (initiator_cred_handle != GSS_C_NO_CREDENTIAL &&
|
||||
cred_handle == NULL) {
|
||||
|
@@ -96,8 +96,8 @@ gss_inquire_cred(OM_uint32 *minor_status,
|
||||
struct _gss_mechanism_cred *mc;
|
||||
|
||||
HEIM_TAILQ_FOREACH(mc, &cred->gc_mc, gmc_link) {
|
||||
gss_name_t mc_name;
|
||||
OM_uint32 mc_lifetime;
|
||||
gss_name_t mc_name = GSS_C_NO_NAME;
|
||||
OM_uint32 mc_lifetime = GSS_C_INDEFINITE;
|
||||
|
||||
if (mc->gmc_mech->gm_inquire_cred == NULL)
|
||||
continue;
|
||||
|
@@ -179,11 +179,20 @@ do { \
|
||||
m->gm_mech.gm_ ## name = NULL; \
|
||||
} while (0)
|
||||
|
||||
/* mech exports gssspi_XXX, internally referred to as gss_XXX */
|
||||
#define OPTSPISYM(name) \
|
||||
do { \
|
||||
m->gm_mech.gm_ ## name = (_gss_##name##_t *)dlsym(so, "gssspi_" #name); \
|
||||
} while (0)
|
||||
|
||||
/* mech exports gssspi_XXX, internally referred to as gssspi_XXX */
|
||||
#define OPTSPISPISYM(name) \
|
||||
do { \
|
||||
m->gm_mech.gm_ ## name = (_gss_##name##_t *)dlsym(so, "gssspi_" #name); \
|
||||
if (m->gm_mech.gm_ ## name == gssspi_ ## name) \
|
||||
m->gm_mech.gm_ ## name = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define COMPATSYM(name) \
|
||||
do { \
|
||||
m->gm_mech.gm_compat->gmc_ ## name = (_gss_##name##_t *)dlsym(so, "gss_" #name); \
|
||||
@@ -262,6 +271,7 @@ _gss_load_mech(void)
|
||||
void *so;
|
||||
gss_OID mech_oid;
|
||||
int found;
|
||||
const char *conf = secure_getenv("GSS_MECH_CONFIG");
|
||||
#endif
|
||||
|
||||
heim_base_once_f(&once, &_gss_mechs, init_mech_switch_list);
|
||||
@@ -285,7 +295,7 @@ _gss_load_mech(void)
|
||||
add_builtin(__gss_ntlm_initialize());
|
||||
|
||||
#ifdef HAVE_DLOPEN
|
||||
fp = fopen(_PATH_GSS_MECH, "r");
|
||||
fp = fopen(conf ? conf : _PATH_GSS_MECH, "r");
|
||||
if (!fp) {
|
||||
HEIMDAL_MUTEX_unlock(&_gss_mech_mutex);
|
||||
return;
|
||||
@@ -410,6 +420,9 @@ _gss_load_mech(void)
|
||||
OPTSYM(set_neg_mechs);
|
||||
OPTSYM(get_neg_mechs);
|
||||
OPTSPISYM(authorize_localname);
|
||||
OPTSPISPISYM(query_mechanism_info);
|
||||
OPTSPISPISYM(query_meta_data);
|
||||
OPTSPISPISYM(exchange_meta_data);
|
||||
|
||||
mi = (_gss_mo_init *)dlsym(so, "gss_mo_init");
|
||||
if (mi != NULL) {
|
||||
|
@@ -124,6 +124,12 @@ gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_inq_win2k_pac_x_oid_desc = { 8, rk_UNCO
|
||||
/* GSS_C_INQ_SSPI_SESSION_KEY - 1.2.840.113554.1.2.2.5.5 */
|
||||
gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_inq_sspi_session_key_oid_desc = { 11, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05") };
|
||||
|
||||
/* GSS_C_INQ_NEGOEX_KEY - 1.2.840.113554.1.2.2.5.16 */
|
||||
gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_inq_negoex_key_oid_desc = { 11, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x10") };
|
||||
|
||||
/* GSS_C_INQ_NEGOEX_VERIFY_KEY - 1.2.840.113554.1.2.2.5.17 */
|
||||
gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_inq_negoex_verify_key_oid_desc = { 11, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x11") };
|
||||
|
||||
/* GSS_KRB5_MECHANISM - 1.2.840.113554.1.2.2 */
|
||||
gss_OID_desc GSSAPI_LIB_VARIABLE __gss_krb5_mechanism_oid_desc = { 9, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") };
|
||||
|
||||
@@ -139,6 +145,9 @@ gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_peer_has_updated_spnego_oid_desc = { 9,
|
||||
/* GSS_C_NTLM_RESET_CRYPTO - 1.3.6.1.4.1.7165.655.1.3 */
|
||||
gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_ntlm_reset_crypto_oid_desc = { 11, rk_UNCONST("\x2b\x06\x01\x04\x01\xb7\x7d\x85\x0f\x01\x03") };
|
||||
|
||||
/* GSS_NEGOEX_MECHANISM - 1.3.6.1.4.1.311.2.2.30 */
|
||||
gss_OID_desc GSSAPI_LIB_VARIABLE __gss_negoex_mechanism_oid_desc = { 10, rk_UNCONST("\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x1e") };
|
||||
|
||||
/* GSS_C_MA_MECH_CONCRETE - 1.3.6.1.5.5.13.1 */
|
||||
gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_ma_mech_concrete_oid_desc = { 7, rk_UNCONST("\x2b\x06\x01\x05\x05\x0d\x01") };
|
||||
|
||||
@@ -220,6 +229,9 @@ gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_ma_compress_oid_desc = { 7, rk_UNCONST(
|
||||
/* GSS_C_MA_CTX_TRANS - 1.3.6.1.5.5.13.27 */
|
||||
gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_ma_ctx_trans_oid_desc = { 7, rk_UNCONST("\x2b\x06\x01\x05\x05\x0d\x1b") };
|
||||
|
||||
/* GSS_C_MA_NEGOEX_AND_SPNEGO - 1.2.840.113554.1.2.2.5.18 */
|
||||
gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_ma_negoex_and_spnego_oid_desc = { 11, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x12") };
|
||||
|
||||
struct _gss_oid_name_table _gss_ont_ma[] = {
|
||||
{ GSS_C_MA_AUTH_INIT, "GSS_C_MA_AUTH_INIT", "auth-init-princ", "" },
|
||||
{ GSS_C_MA_AUTH_INIT_ANON, "GSS_C_MA_AUTH_INIT_ANON", "auth-init-princ-anon", "" },
|
||||
@@ -243,6 +255,7 @@ struct _gss_oid_name_table _gss_ont_ma[] = {
|
||||
{ GSS_C_MA_MECH_NEGO, "GSS_C_MA_MECH_NEGO", "mech-negotiation-mech", "" },
|
||||
{ GSS_C_MA_MECH_PSEUDO, "GSS_C_MA_MECH_PSEUDO", "pseudo-mech", "" },
|
||||
{ GSS_C_MA_MIC, "GSS_C_MA_MIC", "mic", "" },
|
||||
{ GSS_C_MA_NEGOEX_AND_SPNEGO, "GSS_C_MA_NEGOEX_AND_SPNEGO", "negoex-and-spnego", "Indicates that a mechanism supports both NegoEx and SPNEGO" },
|
||||
{ GSS_C_MA_NOT_DFLT_MECH, "GSS_C_MA_NOT_DFLT_MECH", "mech-not-default", "" },
|
||||
{ GSS_C_MA_NOT_MECH, "GSS_C_MA_NOT_MECH", "not-mech", "" },
|
||||
{ GSS_C_MA_OOS_DET, "GSS_C_MA_OOS_DET", "oos-detection", "" },
|
||||
@@ -303,11 +316,14 @@ gss_OID _gss_ot_internal[] = {
|
||||
&__gss_netlogon_nt_netbios_dns_name_oid_desc,
|
||||
&__gss_c_inq_win2k_pac_x_oid_desc,
|
||||
&__gss_c_inq_sspi_session_key_oid_desc,
|
||||
&__gss_c_inq_negoex_key_oid_desc,
|
||||
&__gss_c_inq_negoex_verify_key_oid_desc,
|
||||
&__gss_krb5_mechanism_oid_desc,
|
||||
&__gss_ntlm_mechanism_oid_desc,
|
||||
&__gss_spnego_mechanism_oid_desc,
|
||||
&__gss_c_peer_has_updated_spnego_oid_desc,
|
||||
&__gss_c_ntlm_reset_crypto_oid_desc,
|
||||
&__gss_negoex_mechanism_oid_desc,
|
||||
&__gss_c_ma_mech_concrete_oid_desc,
|
||||
&__gss_c_ma_mech_pseudo_oid_desc,
|
||||
&__gss_c_ma_mech_composite_oid_desc,
|
||||
@@ -335,6 +351,7 @@ gss_OID _gss_ot_internal[] = {
|
||||
&__gss_c_ma_pfs_oid_desc,
|
||||
&__gss_c_ma_compress_oid_desc,
|
||||
&__gss_c_ma_ctx_trans_oid_desc,
|
||||
&__gss_c_ma_negoex_and_spnego_oid_desc,
|
||||
};
|
||||
|
||||
size_t _gss_ot_internal_count = sizeof(_gss_ot_internal) / sizeof(_gss_ot_internal[0]);
|
||||
|
@@ -147,3 +147,62 @@ _gss_copy_buffer(OM_uint32 *minor_status,
|
||||
return (GSS_S_COMPLETE);
|
||||
}
|
||||
|
||||
void
|
||||
_gss_mg_encode_le_uint32(uint32_t n, uint8_t *p)
|
||||
{
|
||||
p[0] = (n >> 0 ) & 0xFF;
|
||||
p[1] = (n >> 8 ) & 0xFF;
|
||||
p[2] = (n >> 16) & 0xFF;
|
||||
p[3] = (n >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
void
|
||||
_gss_mg_decode_le_uint32(const void *ptr, uint32_t *n)
|
||||
{
|
||||
const uint8_t *p = ptr;
|
||||
*n = (p[0] << 0) | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
|
||||
}
|
||||
|
||||
void
|
||||
_gss_mg_encode_be_uint32(uint32_t n, uint8_t *p)
|
||||
{
|
||||
p[0] = (n >> 24) & 0xFF;
|
||||
p[1] = (n >> 16) & 0xFF;
|
||||
p[2] = (n >> 8 ) & 0xFF;
|
||||
p[3] = (n >> 0 ) & 0xFF;
|
||||
}
|
||||
|
||||
void
|
||||
_gss_mg_decode_be_uint32(const void *ptr, uint32_t *n)
|
||||
{
|
||||
const uint8_t *p = ptr;
|
||||
*n = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0);
|
||||
}
|
||||
|
||||
void
|
||||
_gss_mg_encode_le_uint16(uint16_t n, uint8_t *p)
|
||||
{
|
||||
p[0] = (n >> 0 ) & 0xFF;
|
||||
p[1] = (n >> 8 ) & 0xFF;
|
||||
}
|
||||
|
||||
void
|
||||
_gss_mg_decode_le_uint16(const void *ptr, uint16_t *n)
|
||||
{
|
||||
const uint8_t *p = ptr;
|
||||
*n = (p[0] << 0) | (p[1] << 8);
|
||||
}
|
||||
|
||||
void
|
||||
_gss_mg_encode_be_uint16(uint16_t n, uint8_t *p)
|
||||
{
|
||||
p[0] = (n >> 24) & 0xFF;
|
||||
p[1] = (n >> 16) & 0xFF;
|
||||
}
|
||||
|
||||
void
|
||||
_gss_mg_decode_be_uint16(const void *ptr, uint16_t *n)
|
||||
{
|
||||
const uint8_t *p = ptr;
|
||||
*n = (p[0] << 24) | (p[1] << 16);
|
||||
}
|
||||
|
115
lib/gssapi/mech/gssspi_exchange_meta_data.c
Normal file
115
lib/gssapi/mech/gssspi_exchange_meta_data.c
Normal file
@@ -0,0 +1,115 @@
|
||||
/*-
|
||||
* Copyright (c) 2005 Doug Rabson
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2009 Apple Inc. All rights reserved.
|
||||
* Portions Copyright (c) 2019 AuriStor, 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:
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 "mech_locl.h"
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gssspi_exchange_meta_data(
|
||||
OM_uint32 *minor_status,
|
||||
gss_const_OID input_mech_type,
|
||||
gss_cred_id_t input_cred_handle,
|
||||
gss_ctx_id_t *context_handle,
|
||||
gss_const_name_t target_name,
|
||||
OM_uint32 req_flags,
|
||||
gss_const_buffer_t meta_data)
|
||||
{
|
||||
OM_uint32 major_status, junk;
|
||||
gssapi_mech_interface m;
|
||||
struct _gss_name *name = (struct _gss_name *) target_name;
|
||||
struct _gss_mechanism_name *mn;
|
||||
struct _gss_context *ctx = (struct _gss_context *) *context_handle;
|
||||
gss_cred_id_t cred_handle;
|
||||
int allocated_ctx;
|
||||
gss_const_OID mech_type = input_mech_type;
|
||||
|
||||
*minor_status = 0;
|
||||
|
||||
if (mech_type == GSS_C_NO_OID)
|
||||
return GSS_S_BAD_MECH;
|
||||
|
||||
if (ctx == NULL) {
|
||||
ctx = calloc(1, sizeof(struct _gss_context));
|
||||
if (ctx == NULL) {
|
||||
*minor_status = ENOMEM;
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
m = ctx->gc_mech = __gss_get_mechanism(mech_type);
|
||||
if (m == NULL) {
|
||||
free(ctx);
|
||||
return GSS_S_BAD_MECH;
|
||||
}
|
||||
allocated_ctx = 1;
|
||||
} else {
|
||||
m = ctx->gc_mech;
|
||||
mech_type = &m->gm_mech_oid;
|
||||
allocated_ctx = 0;
|
||||
}
|
||||
|
||||
if (m->gm_exchange_meta_data == NULL) {
|
||||
major_status = GSS_S_BAD_MECH;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
major_status = _gss_find_mn(minor_status, name, mech_type, &mn);
|
||||
if (major_status != GSS_S_COMPLETE)
|
||||
goto cleanup;
|
||||
|
||||
if (m->gm_flags & GM_USE_MG_CRED)
|
||||
cred_handle = input_cred_handle;
|
||||
else
|
||||
cred_handle = _gss_mg_find_mech_cred(input_cred_handle, mech_type);
|
||||
|
||||
if (input_cred_handle != GSS_C_NO_CREDENTIAL &&
|
||||
cred_handle == NULL) {
|
||||
major_status = GSS_S_NO_CRED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* note: mechanism is not obligated to allocate a context on success */
|
||||
major_status = m->gm_exchange_meta_data(minor_status,
|
||||
mech_type,
|
||||
cred_handle,
|
||||
&ctx->gc_ctx,
|
||||
mn ? mn->gmn_name : GSS_C_NO_NAME,
|
||||
req_flags,
|
||||
meta_data);
|
||||
if (major_status != GSS_S_COMPLETE)
|
||||
_gss_mg_error(m, *minor_status);
|
||||
|
||||
cleanup:
|
||||
if (major_status != GSS_S_COMPLETE || ctx->gc_ctx == GSS_C_NO_CONTEXT)
|
||||
gss_delete_sec_context(&junk, (gss_ctx_id_t *)&ctx, GSS_C_NO_BUFFER);
|
||||
|
||||
*context_handle = (gss_ctx_id_t) ctx;
|
||||
|
||||
_gss_mg_log(10, "gss-emd: return %d/%d", (int)major_status, (int)*minor_status);
|
||||
|
||||
return major_status;
|
||||
}
|
55
lib/gssapi/mech/gssspi_query_mechanism_info.c
Normal file
55
lib/gssapi/mech/gssspi_query_mechanism_info.c
Normal file
@@ -0,0 +1,55 @@
|
||||
/*-
|
||||
* Copyright (c) 2019 AuriStor, 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:
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 "mech_locl.h"
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gssspi_query_mechanism_info(
|
||||
OM_uint32 *minor_status,
|
||||
gss_const_OID mech_type,
|
||||
unsigned char auth_scheme[16])
|
||||
{
|
||||
OM_uint32 major_status;
|
||||
gssapi_mech_interface m;
|
||||
|
||||
*minor_status = 0;
|
||||
|
||||
if (mech_type == GSS_C_NO_OID)
|
||||
return GSS_S_BAD_MECH;
|
||||
|
||||
m = __gss_get_mechanism(mech_type);
|
||||
if (m == NULL || m->gm_query_mechanism_info == NULL)
|
||||
return GSS_S_BAD_MECH;
|
||||
|
||||
major_status = m->gm_query_mechanism_info(minor_status,
|
||||
mech_type,
|
||||
auth_scheme);
|
||||
|
||||
if (major_status != GSS_S_COMPLETE)
|
||||
_gss_mg_error(m, *minor_status);
|
||||
|
||||
return major_status;
|
||||
}
|
117
lib/gssapi/mech/gssspi_query_meta_data.c
Normal file
117
lib/gssapi/mech/gssspi_query_meta_data.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/*-
|
||||
* Copyright (c) 2005 Doug Rabson
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2009 Apple Inc. All rights reserved.
|
||||
* Portions Copyright (c) 2019 AuriStor, 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:
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 "mech_locl.h"
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gssspi_query_meta_data(
|
||||
OM_uint32 *minor_status,
|
||||
gss_const_OID input_mech_type,
|
||||
gss_cred_id_t input_cred_handle,
|
||||
gss_ctx_id_t *context_handle,
|
||||
gss_const_name_t target_name,
|
||||
OM_uint32 req_flags,
|
||||
gss_buffer_t meta_data)
|
||||
{
|
||||
OM_uint32 major_status, junk;
|
||||
gssapi_mech_interface m;
|
||||
struct _gss_name *name = (struct _gss_name *) target_name;
|
||||
struct _gss_mechanism_name *mn;
|
||||
struct _gss_context *ctx = (struct _gss_context *) *context_handle;
|
||||
gss_cred_id_t cred_handle;
|
||||
int allocated_ctx;
|
||||
gss_const_OID mech_type = input_mech_type;
|
||||
|
||||
*minor_status = 0;
|
||||
|
||||
_mg_buffer_zero(meta_data);
|
||||
|
||||
if (mech_type == GSS_C_NO_OID)
|
||||
return GSS_S_BAD_MECH;
|
||||
|
||||
if (ctx == NULL) {
|
||||
ctx = calloc(1, sizeof(struct _gss_context));
|
||||
if (ctx == NULL) {
|
||||
*minor_status = ENOMEM;
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
m = ctx->gc_mech = __gss_get_mechanism(mech_type);
|
||||
if (m == NULL) {
|
||||
free(ctx);
|
||||
return GSS_S_BAD_MECH;
|
||||
}
|
||||
allocated_ctx = 1;
|
||||
} else {
|
||||
m = ctx->gc_mech;
|
||||
mech_type = &m->gm_mech_oid;
|
||||
allocated_ctx = 0;
|
||||
}
|
||||
|
||||
if (m->gm_query_meta_data == NULL) {
|
||||
major_status = GSS_S_BAD_MECH;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
major_status = _gss_find_mn(minor_status, name, mech_type, &mn);
|
||||
if (major_status != GSS_S_COMPLETE)
|
||||
goto cleanup;
|
||||
|
||||
if (m->gm_flags & GM_USE_MG_CRED)
|
||||
cred_handle = input_cred_handle;
|
||||
else
|
||||
cred_handle = _gss_mg_find_mech_cred(input_cred_handle, mech_type);
|
||||
|
||||
if (input_cred_handle != GSS_C_NO_CREDENTIAL &&
|
||||
cred_handle == NULL) {
|
||||
major_status = GSS_S_NO_CRED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* note: mechanism is not obligated to allocate a context on success */
|
||||
major_status = m->gm_query_meta_data(minor_status,
|
||||
mech_type,
|
||||
cred_handle,
|
||||
&ctx->gc_ctx,
|
||||
mn ? mn->gmn_name : GSS_C_NO_NAME,
|
||||
req_flags,
|
||||
meta_data);
|
||||
if (major_status != GSS_S_COMPLETE)
|
||||
_gss_mg_error(m, *minor_status);
|
||||
|
||||
cleanup:
|
||||
if (major_status != GSS_S_COMPLETE || ctx->gc_ctx == GSS_C_NO_CONTEXT)
|
||||
gss_delete_sec_context(&junk, (gss_ctx_id_t *)&ctx, GSS_C_NO_BUFFER);
|
||||
|
||||
*context_handle = (gss_ctx_id_t) ctx;
|
||||
|
||||
_gss_mg_log(10, "gss-qmd: return %d/%d", (int)major_status, (int)*minor_status);
|
||||
|
||||
return major_status;
|
||||
}
|
@@ -56,6 +56,7 @@
|
||||
#include <gssapi.h>
|
||||
#include <gssapi_mech.h>
|
||||
#include <gssapi_krb5.h>
|
||||
#include <gssapi_spnego.h>
|
||||
|
||||
#include <heimqueue.h>
|
||||
|
||||
|
@@ -31,3 +31,13 @@ OM_uint32 _gss_free_oid(OM_uint32 *, gss_OID);
|
||||
OM_uint32 _gss_intern_oid(OM_uint32 *, gss_const_OID, gss_OID *);
|
||||
OM_uint32 _gss_copy_buffer(OM_uint32 *minor_status,
|
||||
const gss_buffer_t from_buf, gss_buffer_t to_buf);
|
||||
|
||||
void _gss_mg_encode_le_uint32(uint32_t n, uint8_t *p);
|
||||
void _gss_mg_decode_le_uint32(const void *ptr, uint32_t *n);
|
||||
void _gss_mg_encode_be_uint32(uint32_t n, uint8_t *p);
|
||||
void _gss_mg_decode_be_uint32(const void *ptr, uint32_t *n);
|
||||
|
||||
void _gss_mg_encode_le_uint16(uint16_t n, uint8_t *p);
|
||||
void _gss_mg_decode_le_uint16(const void *ptr, uint16_t *n);
|
||||
void _gss_mg_encode_be_uint16(uint16_t n, uint8_t *p);
|
||||
void _gss_mg_decode_be_uint16(const void *ptr, uint16_t *n);
|
||||
|
@@ -127,6 +127,9 @@ static gssapi_mech_interface_desc ntlm_mech = {
|
||||
NULL, /* gm_store_cred_into */
|
||||
NULL, /* gm_set_neg_mechs */
|
||||
NULL, /* gm_get_neg_mechs */
|
||||
NULL, /* gm_query_mechanism_info */
|
||||
NULL, /* gm_query_meta_data */
|
||||
NULL, /* gm_exchange_meta_data */
|
||||
NULL, /* gm_compat */
|
||||
};
|
||||
|
||||
|
@@ -52,6 +52,8 @@ oid base GSS_NETLOGON_NT_NETBIOS_DNS_NAME 1.2.752.43.14.5
|
||||
#/* GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X.128 */
|
||||
oid base GSS_C_INQ_WIN2K_PAC_X 1.2.752.43.13.3.128
|
||||
oid base GSS_C_INQ_SSPI_SESSION_KEY 1.2.840.113554.1.2.2.5.5
|
||||
oid base GSS_C_INQ_NEGOEX_KEY 1.2.840.113554.1.2.2.5.16
|
||||
oid base GSS_C_INQ_NEGOEX_VERIFY_KEY 1.2.840.113554.1.2.2.5.17
|
||||
|
||||
#/*
|
||||
# * "Standard" mechs
|
||||
@@ -65,6 +67,7 @@ oid base GSS_SPNEGO_MECHANISM 1.3.6.1.5.5.2
|
||||
|
||||
oid base GSS_C_PEER_HAS_UPDATED_SPNEGO 1.3.6.1.4.1.5322.19.5
|
||||
oid base GSS_C_NTLM_RESET_CRYPTO 1.3.6.1.4.1.7165.655.1.3
|
||||
oid base GSS_NEGOEX_MECHANISM 1.3.6.1.4.1.311.2.2.30
|
||||
|
||||
|
||||
#/*
|
||||
@@ -110,6 +113,7 @@ oid base GSS_C_MA_CBINDINGS 1.3.6.1.5.5.13.24
|
||||
oid base GSS_C_MA_PFS 1.3.6.1.5.5.13.25
|
||||
oid base GSS_C_MA_COMPRESS 1.3.6.1.5.5.13.26
|
||||
oid base GSS_C_MA_CTX_TRANS 1.3.6.1.5.5.13.27
|
||||
oid base GSS_C_MA_NEGOEX_AND_SPNEGO 1.2.840.113554.1.2.2.5.18
|
||||
|
||||
desc ma GSS_C_MA_MECH_CONCRETE "concrete-mech" "Indicates that a mech is neither a pseudo-mechanism nor a composite mechanism"
|
||||
desc ma GSS_C_MA_MECH_PSEUDO "pseudo-mech" ""
|
||||
@@ -138,3 +142,4 @@ desc ma GSS_C_MA_CBINDINGS "channel-bindings" ""
|
||||
desc ma GSS_C_MA_PFS "pfs" ""
|
||||
desc ma GSS_C_MA_COMPRESS "compress" ""
|
||||
desc ma GSS_C_MA_CTX_TRANS "context-transfer" ""
|
||||
desc ma GSS_C_MA_NEGOEX_AND_SPNEGO "negoex-and-spnego" "Indicates that a mechanism supports both NegoEx and SPNEGO"
|
||||
|
@@ -63,37 +63,60 @@ send_reject (OM_uint32 *minor_status,
|
||||
}
|
||||
|
||||
static OM_uint32
|
||||
acceptor_approved(gss_const_cred_id_t input_cred,
|
||||
gss_name_t target_name,
|
||||
acceptor_approved(OM_uint32 *minor_status,
|
||||
void *userptr,
|
||||
gss_const_name_t target_name,
|
||||
gss_const_cred_id_t cred_handle,
|
||||
gss_OID mech)
|
||||
{
|
||||
gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
|
||||
gss_OID_set oidset;
|
||||
gss_OID_set oidset = GSS_C_NO_OID_SET;
|
||||
OM_uint32 junk, ret;
|
||||
|
||||
if (target_name == GSS_C_NO_NAME)
|
||||
return GSS_S_COMPLETE;
|
||||
|
||||
if (input_cred != GSS_C_NO_CREDENTIAL) {
|
||||
return gss_inquire_cred_by_mech(&junk, input_cred, mech,
|
||||
NULL, NULL, NULL, NULL);
|
||||
if (gss_oid_equal(mech, GSS_NEGOEX_MECHANISM)) {
|
||||
size_t i;
|
||||
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, &oidset);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
|
||||
/* before committing to NegoEx, check we can negotiate a mech */
|
||||
for (i = 0; i < oidset->count; i++) {
|
||||
gss_OID inner_mech = &oidset->elements[i];
|
||||
|
||||
if (_gss_negoex_mech_p(inner_mech)) {
|
||||
ret = acceptor_approved(minor_status, userptr,
|
||||
target_name, cred_handle,
|
||||
inner_mech);
|
||||
if (ret == GSS_S_COMPLETE)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (cred_handle != GSS_C_NO_CREDENTIAL) {
|
||||
ret = gss_inquire_cred_by_mech(minor_status, cred_handle, mech,
|
||||
NULL, NULL, NULL, NULL);
|
||||
} else {
|
||||
ret = gss_create_empty_oid_set(minor_status, &oidset);
|
||||
if (ret == GSS_S_COMPLETE)
|
||||
ret = gss_add_oid_set_member(minor_status, mech, &oidset);
|
||||
if (ret == GSS_S_COMPLETE)
|
||||
ret = gss_acquire_cred(minor_status, target_name,
|
||||
GSS_C_INDEFINITE, oidset,
|
||||
GSS_C_ACCEPT, &cred, NULL, NULL);
|
||||
}
|
||||
|
||||
gss_create_empty_oid_set(&junk, &oidset);
|
||||
gss_add_oid_set_member(&junk, mech, &oidset);
|
||||
|
||||
ret = gss_acquire_cred(&junk, target_name, GSS_C_INDEFINITE, oidset,
|
||||
GSS_C_ACCEPT, &cred, NULL, NULL);
|
||||
gss_release_oid_set(&junk, &oidset);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
gss_release_cred(&junk, &cred);
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static OM_uint32
|
||||
send_supported_mechs (OM_uint32 *minor_status,
|
||||
gssspnego_ctx ctx,
|
||||
gss_const_cred_id_t acceptor_cred,
|
||||
gss_buffer_t output_token)
|
||||
{
|
||||
@@ -109,8 +132,8 @@ send_supported_mechs (OM_uint32 *minor_status,
|
||||
nt.u.negTokenInit.mechToken = NULL;
|
||||
nt.u.negTokenInit.negHints = NULL;
|
||||
|
||||
ret = _gss_spnego_indicate_mechtypelist(minor_status, GSS_C_NO_NAME,
|
||||
acceptor_approved, 1, acceptor_cred,
|
||||
ret = _gss_spnego_indicate_mechtypelist(minor_status, NULL,
|
||||
acceptor_approved, ctx, 1, acceptor_cred,
|
||||
&nt.u.negTokenInit.mechTypes, NULL);
|
||||
if (ret != GSS_S_COMPLETE) {
|
||||
return ret;
|
||||
@@ -160,13 +183,15 @@ send_supported_mechs (OM_uint32 *minor_status,
|
||||
static OM_uint32
|
||||
send_accept (OM_uint32 *minor_status,
|
||||
gssspnego_ctx context_handle,
|
||||
int optimistic_mech_ok,
|
||||
gss_buffer_t mech_token,
|
||||
int initial_response,
|
||||
gss_const_OID selected_mech, /* valid on initial response only */
|
||||
gss_buffer_t mech_buf,
|
||||
gss_buffer_t output_token)
|
||||
{
|
||||
int initial_response = (selected_mech != GSS_C_NO_OID);
|
||||
NegotiationToken nt;
|
||||
OM_uint32 ret;
|
||||
OM_uint32 ret, minor;
|
||||
gss_buffer_desc mech_mic_buf;
|
||||
size_t size;
|
||||
|
||||
@@ -180,7 +205,7 @@ send_accept (OM_uint32 *minor_status,
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
if (context_handle->open) {
|
||||
if (context_handle->flags.open) {
|
||||
if (mech_token != GSS_C_NO_BUFFER
|
||||
&& mech_token->length != 0
|
||||
&& mech_buf != GSS_C_NO_BUFFER)
|
||||
@@ -188,7 +213,7 @@ send_accept (OM_uint32 *minor_status,
|
||||
else
|
||||
*(nt.u.negTokenResp.negResult) = accept_completed;
|
||||
} else {
|
||||
if (initial_response && context_handle->require_mic)
|
||||
if (initial_response && !optimistic_mech_ok)
|
||||
*(nt.u.negTokenResp.negResult) = request_mic;
|
||||
else
|
||||
*(nt.u.negTokenResp.negResult) = accept_incomplete;
|
||||
@@ -197,20 +222,22 @@ send_accept (OM_uint32 *minor_status,
|
||||
if (initial_response) {
|
||||
ALLOC(nt.u.negTokenResp.supportedMech, 1);
|
||||
if (nt.u.negTokenResp.supportedMech == NULL) {
|
||||
free_NegotiationToken(&nt);
|
||||
*minor_status = ENOMEM;
|
||||
return GSS_S_FAILURE;
|
||||
ret = GSS_S_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = der_get_oid(context_handle->preferred_mech_type->elements,
|
||||
context_handle->preferred_mech_type->length,
|
||||
ret = der_get_oid(selected_mech->elements,
|
||||
selected_mech->length,
|
||||
nt.u.negTokenResp.supportedMech,
|
||||
NULL);
|
||||
if (ret) {
|
||||
free_NegotiationToken(&nt);
|
||||
*minor_status = ENOMEM;
|
||||
return GSS_S_FAILURE;
|
||||
ret = GSS_S_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
_gss_spnego_log_mech("acceptor sending selected mech", selected_mech);
|
||||
} else {
|
||||
nt.u.negTokenResp.supportedMech = NULL;
|
||||
}
|
||||
@@ -218,9 +245,9 @@ send_accept (OM_uint32 *minor_status,
|
||||
if (mech_token != GSS_C_NO_BUFFER && mech_token->length != 0) {
|
||||
ALLOC(nt.u.negTokenResp.responseToken, 1);
|
||||
if (nt.u.negTokenResp.responseToken == NULL) {
|
||||
free_NegotiationToken(&nt);
|
||||
*minor_status = ENOMEM;
|
||||
return GSS_S_FAILURE;
|
||||
ret = GSS_S_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
nt.u.negTokenResp.responseToken->length = mech_token->length;
|
||||
nt.u.negTokenResp.responseToken->data = mech_token->value;
|
||||
@@ -236,25 +263,22 @@ send_accept (OM_uint32 *minor_status,
|
||||
0,
|
||||
mech_buf,
|
||||
&mech_mic_buf);
|
||||
if (ret == GSS_S_COMPLETE &&
|
||||
gss_oid_equal(context_handle->negotiated_mech_type,
|
||||
GSS_NTLM_MECHANISM))
|
||||
_gss_spnego_ntlm_reset_crypto(minor_status, context_handle, 0);
|
||||
if (ret == GSS_S_COMPLETE) {
|
||||
_gss_spnego_ntlm_reset_crypto(&minor, context_handle, FALSE);
|
||||
|
||||
ALLOC(nt.u.negTokenResp.mechListMIC, 1);
|
||||
if (nt.u.negTokenResp.mechListMIC == NULL) {
|
||||
gss_release_buffer(minor_status, &mech_mic_buf);
|
||||
free_NegotiationToken(&nt);
|
||||
*minor_status = ENOMEM;
|
||||
return GSS_S_FAILURE;
|
||||
ret = GSS_S_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
nt.u.negTokenResp.mechListMIC->length = mech_mic_buf.length;
|
||||
nt.u.negTokenResp.mechListMIC->data = mech_mic_buf.value;
|
||||
} else if (ret == GSS_S_UNAVAILABLE) {
|
||||
nt.u.negTokenResp.mechListMIC = NULL;
|
||||
} else {
|
||||
free_NegotiationToken(&nt);
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
} else
|
||||
@@ -264,9 +288,9 @@ send_accept (OM_uint32 *minor_status,
|
||||
output_token->value, output_token->length,
|
||||
&nt, &size, ret);
|
||||
if (ret) {
|
||||
free_NegotiationToken(&nt);
|
||||
*minor_status = ret;
|
||||
return GSS_S_FAILURE;
|
||||
*minor_status = ENOMEM;
|
||||
ret = GSS_S_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -279,136 +303,165 @@ send_accept (OM_uint32 *minor_status,
|
||||
ret = GSS_S_COMPLETE;
|
||||
else
|
||||
ret = GSS_S_CONTINUE_NEEDED;
|
||||
|
||||
out:
|
||||
free_NegotiationToken(&nt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the default acceptor identity based on the local hostname
|
||||
* or the GSSAPI_SPNEGO_NAME environment variable.
|
||||
*/
|
||||
|
||||
static OM_uint32
|
||||
verify_mechlist_mic
|
||||
(OM_uint32 *minor_status,
|
||||
gssspnego_ctx context_handle,
|
||||
gss_buffer_t mech_buf,
|
||||
heim_octet_string *mechListMIC
|
||||
)
|
||||
default_acceptor_name(OM_uint32 *minor_status,
|
||||
gss_name_t *namep)
|
||||
{
|
||||
OM_uint32 ret;
|
||||
gss_buffer_desc mic_buf;
|
||||
OM_uint32 major_status;
|
||||
gss_buffer_desc namebuf;
|
||||
char *str = NULL, *host, hostname[MAXHOSTNAMELEN];
|
||||
|
||||
if (context_handle->verified_mic) {
|
||||
/* This doesn't make sense, we've already verified it? */
|
||||
*minor_status = 0;
|
||||
return GSS_S_DUPLICATE_TOKEN;
|
||||
*namep = GSS_C_NO_NAME;
|
||||
|
||||
host = secure_getenv("GSSAPI_SPNEGO_NAME");
|
||||
if (host == NULL) {
|
||||
int rv;
|
||||
|
||||
if (gethostname(hostname, sizeof(hostname)) != 0) {
|
||||
*minor_status = errno;
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
rv = asprintf(&str, "host@%s", hostname);
|
||||
if (rv < 0 || str == NULL) {
|
||||
*minor_status = ENOMEM;
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
host = str;
|
||||
}
|
||||
|
||||
if (mechListMIC == NULL) {
|
||||
*minor_status = 0;
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
}
|
||||
namebuf.length = strlen(host);
|
||||
namebuf.value = host;
|
||||
|
||||
mic_buf.length = mechListMIC->length;
|
||||
mic_buf.value = mechListMIC->data;
|
||||
major_status = gss_import_name(minor_status, &namebuf,
|
||||
GSS_C_NT_HOSTBASED_SERVICE, namep);
|
||||
|
||||
ret = gss_verify_mic(minor_status,
|
||||
context_handle->negotiated_ctx_id,
|
||||
mech_buf,
|
||||
&mic_buf,
|
||||
NULL);
|
||||
free(str);
|
||||
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
ret = GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
return ret;
|
||||
return major_status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine whether the mech in mechType can be negotiated. If the
|
||||
* mech is NegoEx, make NegoEx mechanisms available for negotiation.
|
||||
*/
|
||||
|
||||
static OM_uint32
|
||||
select_mech(OM_uint32 *minor_status, MechType *mechType, int verify_p,
|
||||
gss_OID *mech_p)
|
||||
select_mech(OM_uint32 *minor_status,
|
||||
gssspnego_ctx ctx,
|
||||
gss_const_cred_id_t cred,
|
||||
gss_const_OID_set supported_mechs,
|
||||
MechType *mechType,
|
||||
int verify_p, /* set on non-optimistic tokens */
|
||||
gss_const_OID *advertised_mech_p)
|
||||
{
|
||||
char mechbuf[64];
|
||||
size_t mech_len;
|
||||
gss_OID_desc oid;
|
||||
gss_OID oidp;
|
||||
gss_OID_set mechs;
|
||||
size_t i;
|
||||
gss_OID selected_mech = GSS_C_NO_OID;
|
||||
OM_uint32 ret, junk;
|
||||
int negoex_proposed = FALSE, negoex_selected = FALSE;
|
||||
int includeMSCompatOID = FALSE;
|
||||
size_t i;
|
||||
|
||||
*minor_status = 0;
|
||||
*advertised_mech_p = GSS_C_NO_OID; /* deals with broken MS OID */
|
||||
|
||||
ctx->selected_mech_type = GSS_C_NO_OID;
|
||||
|
||||
ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1,
|
||||
sizeof(mechbuf),
|
||||
mechType,
|
||||
&mech_len);
|
||||
if (ret) {
|
||||
if (ret)
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
}
|
||||
|
||||
oid.length = mech_len;
|
||||
oid.length = (OM_uint32)mech_len;
|
||||
oid.elements = mechbuf + sizeof(mechbuf) - mech_len;
|
||||
|
||||
if (gss_oid_equal(&oid, GSS_SPNEGO_MECHANISM)) {
|
||||
return GSS_S_BAD_MECH;
|
||||
if (gss_oid_equal(&oid, GSS_NEGOEX_MECHANISM))
|
||||
negoex_proposed = TRUE;
|
||||
else if (gss_oid_equal(&oid, &_gss_spnego_mskrb_mechanism_oid_desc))
|
||||
includeMSCompatOID = TRUE;
|
||||
|
||||
for (i = 0; i < supported_mechs->count; i++) {
|
||||
gss_OID iter = &supported_mechs->elements[i];
|
||||
auth_scheme scheme;
|
||||
int is_negoex_mech = /* mechanism is negotiable under NegoEx */
|
||||
gssspi_query_mechanism_info(&junk, iter, scheme) == GSS_S_COMPLETE;
|
||||
|
||||
if (is_negoex_mech && negoex_proposed) {
|
||||
ret = _gss_negoex_add_auth_mech(minor_status, ctx, iter, scheme);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
break;
|
||||
|
||||
negoex_selected = TRUE;
|
||||
}
|
||||
|
||||
if (gss_oid_equal(includeMSCompatOID ? GSS_KRB5_MECHANISM : &oid, iter)) {
|
||||
ret = _gss_intern_oid(minor_status, iter, &selected_mech);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*minor_status = 0;
|
||||
/* always prefer NegoEx if a mechanism supported both */
|
||||
if (negoex_selected)
|
||||
selected_mech = GSS_NEGOEX_MECHANISM;
|
||||
if (selected_mech == GSS_C_NO_OID)
|
||||
ret = GSS_S_BAD_MECH;
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
|
||||
/* Translate broken MS Kebreros OID */
|
||||
if (gss_oid_equal(&oid, &_gss_spnego_mskrb_mechanism_oid_desc))
|
||||
oidp = &_gss_spnego_krb5_mechanism_oid_desc;
|
||||
else
|
||||
oidp = &oid;
|
||||
|
||||
|
||||
ret = gss_indicate_mechs(&junk, &mechs);
|
||||
if (ret)
|
||||
return (ret);
|
||||
|
||||
for (i = 0; i < mechs->count; i++)
|
||||
if (gss_oid_equal(&mechs->elements[i], oidp))
|
||||
break;
|
||||
|
||||
if (i == mechs->count) {
|
||||
gss_release_oid_set(&junk, &mechs);
|
||||
return GSS_S_BAD_MECH;
|
||||
}
|
||||
gss_release_oid_set(&junk, &mechs);
|
||||
|
||||
ret = gss_duplicate_oid(minor_status,
|
||||
&oid, /* possibly this should be oidp */
|
||||
mech_p);
|
||||
heim_assert(!gss_oid_equal(selected_mech, GSS_SPNEGO_MECHANISM),
|
||||
"SPNEGO should not be able to negotiate itself");
|
||||
|
||||
if (verify_p) {
|
||||
gss_name_t name = GSS_C_NO_NAME;
|
||||
gss_buffer_desc namebuf;
|
||||
char *str = NULL, *host, hostname[MAXHOSTNAMELEN];
|
||||
|
||||
host = secure_getenv("GSSAPI_SPNEGO_NAME");
|
||||
if (host == NULL) {
|
||||
int rv;
|
||||
if (gethostname(hostname, sizeof(hostname)) != 0) {
|
||||
*minor_status = errno;
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
rv = asprintf(&str, "host@%s", hostname);
|
||||
if (rv < 0 || str == NULL) {
|
||||
*minor_status = ENOMEM;
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
host = str;
|
||||
/*
|
||||
* If we do not have a credential, acquire a default name as a hint
|
||||
* to acceptor_approved() so it can attempt to acquire a default
|
||||
* credential.
|
||||
*/
|
||||
if (cred == GSS_C_NO_CREDENTIAL) {
|
||||
ret = default_acceptor_name(minor_status, &name);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
}
|
||||
|
||||
namebuf.length = strlen(host);
|
||||
namebuf.value = host;
|
||||
ret = acceptor_approved(minor_status, ctx, name, cred, selected_mech);
|
||||
|
||||
ret = gss_import_name(minor_status, &namebuf,
|
||||
GSS_C_NT_HOSTBASED_SERVICE, &name);
|
||||
if (str)
|
||||
free(str);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
|
||||
ret = acceptor_approved(GSS_C_NO_CREDENTIAL, name, *mech_p);
|
||||
gss_release_name(&junk, &name);
|
||||
}
|
||||
|
||||
/* Stash optimistic mech for use by _gss_spnego_require_mechlist_mic() */
|
||||
if (ret == GSS_S_COMPLETE && !verify_p)
|
||||
ret = gss_duplicate_oid(minor_status, &oid, &ctx->preferred_mech_type);
|
||||
|
||||
if (ret == GSS_S_COMPLETE) {
|
||||
*minor_status = 0;
|
||||
|
||||
*advertised_mech_p = ctx->selected_mech_type = selected_mech;
|
||||
|
||||
/* if the initiator used the broken MS OID, send that instead */
|
||||
if (includeMSCompatOID && gss_oid_equal(selected_mech, GSS_KRB5_MECHANISM))
|
||||
*advertised_mech_p = &_gss_spnego_mskrb_mechanism_oid_desc;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -417,25 +470,19 @@ static OM_uint32
|
||||
acceptor_complete(OM_uint32 * minor_status,
|
||||
gssspnego_ctx ctx,
|
||||
int *get_mic,
|
||||
gss_buffer_t mech_buf,
|
||||
gss_buffer_t mech_input_token,
|
||||
gss_buffer_t mech_output_token,
|
||||
heim_octet_string *mic,
|
||||
gss_buffer_t output_token)
|
||||
{
|
||||
gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
|
||||
OM_uint32 ret;
|
||||
int require_mic, verify_mic;
|
||||
int verify_mic;
|
||||
|
||||
ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic);
|
||||
if (ret)
|
||||
return ret;
|
||||
ctx->flags.require_mic = 1;
|
||||
ctx->flags.safe_omit = _gss_spnego_safe_omit_mechlist_mic(ctx);
|
||||
|
||||
ctx->require_mic = require_mic;
|
||||
|
||||
if (mic != NULL)
|
||||
require_mic = 1;
|
||||
|
||||
if (ctx->open && require_mic) {
|
||||
if (ctx->flags.open) {
|
||||
if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */
|
||||
verify_mic = 1;
|
||||
*get_mic = 0;
|
||||
@@ -447,29 +494,20 @@ acceptor_complete(OM_uint32 * minor_status,
|
||||
*get_mic = 1;
|
||||
}
|
||||
|
||||
if (verify_mic || *get_mic) {
|
||||
int eret;
|
||||
size_t buf_len = 0;
|
||||
|
||||
ASN1_MALLOC_ENCODE(MechTypeList,
|
||||
mech_buf->value, mech_buf->length,
|
||||
&ctx->initiator_mech_types, &buf_len, eret);
|
||||
if (eret) {
|
||||
*minor_status = eret;
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
heim_assert(mech_buf->length == buf_len, "Internal ASN.1 error");
|
||||
UNREACHABLE(return GSS_S_FAILURE);
|
||||
}
|
||||
|
||||
if (verify_mic) {
|
||||
ret = verify_mechlist_mic(minor_status, ctx, mech_buf, mic);
|
||||
if (verify_mic && mic == NULL && ctx->flags.safe_omit) {
|
||||
/*
|
||||
* Peer is old and didn't send a mic while we expected
|
||||
* one, but since it safe to omit, let do that
|
||||
*/
|
||||
} else if (verify_mic) {
|
||||
ret = _gss_spnego_verify_mechtypes_mic(minor_status, ctx, mic);
|
||||
if (ret) {
|
||||
if (*get_mic)
|
||||
send_reject (minor_status, output_token);
|
||||
send_reject(minor_status, output_token);
|
||||
if (buf.value)
|
||||
free(buf.value);
|
||||
return ret;
|
||||
}
|
||||
ctx->verified_mic = 1;
|
||||
}
|
||||
} else
|
||||
*get_mic = 0;
|
||||
@@ -477,6 +515,57 @@ acceptor_complete(OM_uint32 * minor_status,
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call gss_accept_sec_context() via mechglue or NegoEx, depending on
|
||||
* whether mech_oid is NegoEx.
|
||||
*/
|
||||
|
||||
static OM_uint32
|
||||
mech_accept(OM_uint32 *minor_status,
|
||||
gssspnego_ctx ctx,
|
||||
gss_const_cred_id_t acceptor_cred_handle,
|
||||
gss_const_buffer_t input_token_buffer,
|
||||
const gss_channel_bindings_t input_chan_bindings,
|
||||
gss_buffer_t output_token,
|
||||
gss_cred_id_t *delegated_cred_handle)
|
||||
{
|
||||
OM_uint32 ret, junk;
|
||||
|
||||
heim_assert(ctx->selected_mech_type != GSS_C_NO_OID,
|
||||
"mech_accept called with no selected mech");
|
||||
|
||||
if (gss_oid_equal(ctx->selected_mech_type, GSS_NEGOEX_MECHANISM)) {
|
||||
ret = _gss_negoex_accept(minor_status,
|
||||
ctx,
|
||||
(gss_cred_id_t)acceptor_cred_handle,
|
||||
input_token_buffer,
|
||||
input_chan_bindings,
|
||||
output_token,
|
||||
delegated_cred_handle);
|
||||
} else {
|
||||
if (ctx->mech_src_name != GSS_C_NO_NAME)
|
||||
gss_release_name(&junk, &ctx->mech_src_name);
|
||||
|
||||
ret = gss_accept_sec_context(minor_status,
|
||||
&ctx->negotiated_ctx_id,
|
||||
acceptor_cred_handle,
|
||||
(gss_buffer_t)input_token_buffer,
|
||||
input_chan_bindings,
|
||||
&ctx->mech_src_name,
|
||||
&ctx->negotiated_mech_type,
|
||||
output_token,
|
||||
&ctx->mech_flags,
|
||||
&ctx->mech_time_rec,
|
||||
delegated_cred_handle);
|
||||
if (GSS_ERROR(ret))
|
||||
gss_mg_collect_error(ctx->negotiated_mech_type, ret, *minor_status);
|
||||
else if (ctx->negotiated_mech_type != GSS_C_NO_OID &&
|
||||
!gss_oid_equal(ctx->negotiated_mech_type, ctx->selected_mech_type))
|
||||
_gss_mg_log(1, "spnego client didn't send the mech they said they would");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static OM_uint32 GSSAPI_CALLCONV
|
||||
acceptor_start
|
||||
@@ -495,23 +584,24 @@ acceptor_start
|
||||
{
|
||||
OM_uint32 ret, junk;
|
||||
NegotiationToken nt;
|
||||
size_t nt_len;
|
||||
gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
|
||||
size_t size;
|
||||
NegTokenInit *ni;
|
||||
gss_buffer_desc data;
|
||||
gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
|
||||
gss_buffer_desc mech_output_token;
|
||||
gss_buffer_desc mech_buf;
|
||||
gss_OID preferred_mech_type = GSS_C_NO_OID;
|
||||
gssspnego_ctx ctx;
|
||||
int get_mic = 0;
|
||||
int first_ok = 0;
|
||||
gss_const_OID advertised_mech = GSS_C_NO_OID;
|
||||
|
||||
memset(&nt, 0, sizeof(nt));
|
||||
|
||||
mech_output_token.value = NULL;
|
||||
mech_output_token.length = 0;
|
||||
mech_buf.value = NULL;
|
||||
|
||||
if (input_token_buffer->length == 0)
|
||||
return send_supported_mechs (minor_status,
|
||||
return send_supported_mechs (minor_status, NULL,
|
||||
acceptor_cred_handle, output_token);
|
||||
|
||||
ret = _gss_spnego_alloc_sec_context(minor_status, context_handle);
|
||||
@@ -520,6 +610,8 @@ acceptor_start
|
||||
|
||||
ctx = (gssspnego_ctx)*context_handle;
|
||||
|
||||
HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
|
||||
|
||||
/*
|
||||
* The GSS-API encapsulation is only present on the initial
|
||||
* context token (negTokenInit).
|
||||
@@ -528,36 +620,58 @@ acceptor_start
|
||||
GSS_SPNEGO_MECHANISM,
|
||||
&data);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out;
|
||||
|
||||
ret = decode_NegotiationToken(data.value, data.length, &nt, &nt_len);
|
||||
ret = decode_NegotiationToken(data.value, data.length, &nt, &size);
|
||||
gss_release_buffer(minor_status, &data);
|
||||
if (ret) {
|
||||
*minor_status = ret;
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
ret = GSS_S_DEFECTIVE_TOKEN;
|
||||
goto out;
|
||||
}
|
||||
if (nt.element != choice_NegotiationToken_negTokenInit) {
|
||||
*minor_status = 0;
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
ret = GSS_S_DEFECTIVE_TOKEN;
|
||||
goto out;
|
||||
}
|
||||
ni = &nt.u.negTokenInit;
|
||||
|
||||
if (ni->mechTypes.len < 1) {
|
||||
free_NegotiationToken(&nt);
|
||||
*minor_status = 0;
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
ret = GSS_S_DEFECTIVE_TOKEN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
|
||||
_gss_spnego_log_mechTypes(&ni->mechTypes);
|
||||
|
||||
ret = copy_MechTypeList(&ni->mechTypes, &ctx->initiator_mech_types);
|
||||
if (ret) {
|
||||
HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
|
||||
free_NegotiationToken(&nt);
|
||||
*minor_status = ret;
|
||||
return GSS_S_FAILURE;
|
||||
{
|
||||
MechTypeList mt;
|
||||
int kret;
|
||||
|
||||
mt.len = ni->mechTypes.len;
|
||||
mt.val = ni->mechTypes.val;
|
||||
|
||||
ASN1_MALLOC_ENCODE(MechTypeList,
|
||||
ctx->NegTokenInit_mech_types.value,
|
||||
ctx->NegTokenInit_mech_types.length,
|
||||
&mt, &size, kret);
|
||||
if (kret) {
|
||||
*minor_status = kret;
|
||||
ret = GSS_S_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (acceptor_cred_handle != GSS_C_NO_CREDENTIAL)
|
||||
ret = _gss_spnego_inquire_cred_mechs(minor_status,
|
||||
acceptor_cred_handle,
|
||||
&supported_mechs);
|
||||
else
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, &supported_mechs);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* First we try the opportunistic token if we have support for it,
|
||||
* don't try to verify we have credential for the token,
|
||||
@@ -566,41 +680,39 @@ acceptor_start
|
||||
*/
|
||||
|
||||
ret = select_mech(minor_status,
|
||||
ctx,
|
||||
acceptor_cred_handle,
|
||||
supported_mechs,
|
||||
&ni->mechTypes.val[0],
|
||||
0,
|
||||
&preferred_mech_type);
|
||||
0, /* optimistic token */
|
||||
&advertised_mech);
|
||||
|
||||
if (ret == 0 && ni->mechToken != NULL) {
|
||||
if (ret == GSS_S_COMPLETE && ni->mechToken != NULL) {
|
||||
gss_buffer_desc ibuf;
|
||||
|
||||
ibuf.length = ni->mechToken->length;
|
||||
ibuf.value = ni->mechToken->data;
|
||||
mech_input_token = &ibuf;
|
||||
|
||||
if (ctx->mech_src_name != GSS_C_NO_NAME)
|
||||
gss_release_name(&junk, &ctx->mech_src_name);
|
||||
|
||||
ret = gss_accept_sec_context(minor_status,
|
||||
&ctx->negotiated_ctx_id,
|
||||
acceptor_cred_handle,
|
||||
mech_input_token,
|
||||
input_chan_bindings,
|
||||
&ctx->mech_src_name,
|
||||
&ctx->negotiated_mech_type,
|
||||
&mech_output_token,
|
||||
&ctx->mech_flags,
|
||||
&ctx->mech_time_rec,
|
||||
delegated_cred_handle);
|
||||
_gss_spnego_log_mech("acceptor selected opportunistic mech", ctx->selected_mech_type);
|
||||
|
||||
ret = mech_accept(&junk,
|
||||
ctx,
|
||||
acceptor_cred_handle,
|
||||
mech_input_token,
|
||||
input_chan_bindings,
|
||||
&mech_output_token,
|
||||
delegated_cred_handle);
|
||||
if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
|
||||
ctx->preferred_mech_type = preferred_mech_type;
|
||||
if (ret == GSS_S_COMPLETE)
|
||||
ctx->open = 1;
|
||||
first_ok = 1;
|
||||
} else {
|
||||
ctx->selected_mech_type = GSS_C_NO_OID;
|
||||
}
|
||||
|
||||
if (ret == GSS_S_COMPLETE) {
|
||||
ret = acceptor_complete(minor_status,
|
||||
ctx,
|
||||
&get_mic,
|
||||
&mech_buf,
|
||||
mech_input_token,
|
||||
&mech_output_token,
|
||||
ni->mechListMIC,
|
||||
@@ -608,10 +720,14 @@ acceptor_start
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
goto out;
|
||||
|
||||
first_ok = 1;
|
||||
} else {
|
||||
gss_mg_collect_error(preferred_mech_type, ret, *minor_status);
|
||||
ctx->flags.open = 1;
|
||||
}
|
||||
} else {
|
||||
*minor_status = 0;
|
||||
HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
|
||||
return gss_mg_set_error_string(GSS_C_NO_OID, GSS_S_NO_CONTEXT,
|
||||
*minor_status,
|
||||
"SPNEGO acceptor didn't find a prefered mechanism");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -621,24 +737,25 @@ acceptor_start
|
||||
if (!first_ok && ni->mechToken != NULL) {
|
||||
size_t j;
|
||||
|
||||
preferred_mech_type = GSS_C_NO_OID;
|
||||
|
||||
/* Call glue layer to find first mech we support */
|
||||
for (j = 1; j < ni->mechTypes.len; ++j) {
|
||||
ret = select_mech(minor_status,
|
||||
ret = select_mech(&junk,
|
||||
ctx,
|
||||
acceptor_cred_handle,
|
||||
supported_mechs,
|
||||
&ni->mechTypes.val[j],
|
||||
1,
|
||||
&preferred_mech_type);
|
||||
if (ret == 0)
|
||||
1, /* not optimistic token */
|
||||
&advertised_mech);
|
||||
if (ret == GSS_S_COMPLETE) {
|
||||
_gss_spnego_log_mech("acceptor selected non-opportunistic mech", ctx->selected_mech_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (preferred_mech_type == GSS_C_NO_OID) {
|
||||
HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
|
||||
free_NegotiationToken(&nt);
|
||||
return ret;
|
||||
if (ctx->selected_mech_type == GSS_C_NO_OID) {
|
||||
heim_assert(ret != GSS_S_COMPLETE, "no oid and no error code?");
|
||||
*minor_status = junk;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->preferred_mech_type = preferred_mech_type;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -647,20 +764,18 @@ acceptor_start
|
||||
|
||||
ret = send_accept (minor_status,
|
||||
ctx,
|
||||
first_ok,
|
||||
&mech_output_token,
|
||||
1,
|
||||
get_mic ? &mech_buf : NULL,
|
||||
advertised_mech,
|
||||
get_mic ? &ctx->NegTokenInit_mech_types : NULL,
|
||||
output_token);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
gss_release_oid_set(&junk, &supported_mechs);
|
||||
if (mech_output_token.value != NULL)
|
||||
gss_release_buffer(&junk, &mech_output_token);
|
||||
if (mech_buf.value != NULL) {
|
||||
free(mech_buf.value);
|
||||
mech_buf.value = NULL;
|
||||
}
|
||||
free_NegotiationToken(&nt);
|
||||
|
||||
|
||||
@@ -705,18 +820,15 @@ acceptor_continue
|
||||
gss_cred_id_t *delegated_cred_handle
|
||||
)
|
||||
{
|
||||
OM_uint32 ret, ret2, minor;
|
||||
OM_uint32 ret, ret2, minor, junk;
|
||||
NegotiationToken nt;
|
||||
size_t nt_len;
|
||||
NegTokenResp *na;
|
||||
unsigned int negResult = accept_incomplete;
|
||||
gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
|
||||
gss_buffer_t mech_output_token = GSS_C_NO_BUFFER;
|
||||
gss_buffer_desc mech_buf;
|
||||
gssspnego_ctx ctx;
|
||||
|
||||
mech_buf.value = NULL;
|
||||
|
||||
ctx = (gssspnego_ctx)*context_handle;
|
||||
|
||||
/*
|
||||
@@ -745,9 +857,8 @@ acceptor_continue
|
||||
|
||||
{
|
||||
gss_buffer_desc ibuf, obuf;
|
||||
int require_mic, get_mic = 0;
|
||||
int get_mic = 0;
|
||||
int require_response;
|
||||
heim_octet_string *mic;
|
||||
|
||||
if (na->responseToken != NULL) {
|
||||
ibuf.length = na->responseToken->length;
|
||||
@@ -760,53 +871,31 @@ acceptor_continue
|
||||
|
||||
if (mech_input_token != GSS_C_NO_BUFFER) {
|
||||
|
||||
if (ctx->mech_src_name != GSS_C_NO_NAME)
|
||||
gss_release_name(&minor, &ctx->mech_src_name);
|
||||
|
||||
ret = gss_accept_sec_context(&minor,
|
||||
&ctx->negotiated_ctx_id,
|
||||
acceptor_cred_handle,
|
||||
mech_input_token,
|
||||
input_chan_bindings,
|
||||
&ctx->mech_src_name,
|
||||
&ctx->negotiated_mech_type,
|
||||
&obuf,
|
||||
&ctx->mech_flags,
|
||||
&ctx->mech_time_rec,
|
||||
delegated_cred_handle);
|
||||
|
||||
ret = mech_accept(minor_status,
|
||||
ctx,
|
||||
acceptor_cred_handle,
|
||||
mech_input_token,
|
||||
input_chan_bindings,
|
||||
&obuf,
|
||||
delegated_cred_handle);
|
||||
if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
|
||||
mech_output_token = &obuf;
|
||||
}
|
||||
if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) {
|
||||
free_NegotiationToken(&nt);
|
||||
gss_mg_collect_error(ctx->negotiated_mech_type, ret, minor);
|
||||
send_reject (minor_status, output_token);
|
||||
send_reject(&junk, output_token);
|
||||
HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
|
||||
return ret;
|
||||
}
|
||||
if (ret == GSS_S_COMPLETE)
|
||||
ctx->open = 1;
|
||||
ctx->flags.open = 1;
|
||||
} else
|
||||
ret = GSS_S_COMPLETE;
|
||||
|
||||
ret2 = _gss_spnego_require_mechlist_mic(minor_status,
|
||||
ctx,
|
||||
&require_mic);
|
||||
if (ret2)
|
||||
goto out;
|
||||
|
||||
ctx->require_mic = require_mic;
|
||||
|
||||
mic = na->mechListMIC;
|
||||
if (mic != NULL)
|
||||
require_mic = 1;
|
||||
|
||||
if (ret == GSS_S_COMPLETE)
|
||||
ret = acceptor_complete(minor_status,
|
||||
ctx,
|
||||
&get_mic,
|
||||
&mech_buf,
|
||||
mech_input_token,
|
||||
mech_output_token,
|
||||
na->mechListMIC,
|
||||
@@ -823,26 +912,26 @@ acceptor_continue
|
||||
*/
|
||||
if ((mech_output_token != GSS_C_NO_BUFFER &&
|
||||
mech_output_token->length != 0)
|
||||
|| (ctx->open && negResult == accept_incomplete)
|
||||
|| (ctx->flags.open && negResult == accept_incomplete)
|
||||
|| require_response
|
||||
|| get_mic) {
|
||||
ret2 = send_accept (minor_status,
|
||||
ctx,
|
||||
0, /* ignored on subsequent tokens */
|
||||
mech_output_token,
|
||||
0,
|
||||
get_mic ? &mech_buf : NULL,
|
||||
GSS_C_NO_OID,
|
||||
get_mic ? &ctx->NegTokenInit_mech_types : NULL,
|
||||
output_token);
|
||||
if (ret2)
|
||||
goto out;
|
||||
}
|
||||
} else
|
||||
ret2 = GSS_S_COMPLETE;
|
||||
|
||||
out:
|
||||
if (ret2 != GSS_S_COMPLETE)
|
||||
ret = ret2;
|
||||
if (mech_output_token != NULL)
|
||||
gss_release_buffer(&minor, mech_output_token);
|
||||
if (mech_buf.value != NULL)
|
||||
free(mech_buf.value);
|
||||
free_NegotiationToken(&nt);
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,8 @@
|
||||
* Copyright (c) 2004, PADL Software Pty Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2009 Apple 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:
|
||||
@@ -43,9 +45,6 @@
|
||||
gss_OID_desc _gss_spnego_mskrb_mechanism_oid_desc =
|
||||
{9, rk_UNCONST("\x2a\x86\x48\x82\xf7\x12\x01\x02\x02")};
|
||||
|
||||
gss_OID_desc _gss_spnego_krb5_mechanism_oid_desc =
|
||||
{9, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")};
|
||||
|
||||
/*
|
||||
* Allocate a SPNEGO context handle
|
||||
*/
|
||||
@@ -61,28 +60,33 @@ _gss_spnego_alloc_sec_context (OM_uint32 * minor_status,
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
ctx->initiator_mech_types.len = 0;
|
||||
ctx->initiator_mech_types.val = NULL;
|
||||
ctx->NegTokenInit_mech_types.value = NULL;
|
||||
ctx->NegTokenInit_mech_types.length = 0;
|
||||
|
||||
ctx->preferred_mech_type = GSS_C_NO_OID;
|
||||
ctx->selected_mech_type = GSS_C_NO_OID;
|
||||
ctx->negotiated_mech_type = GSS_C_NO_OID;
|
||||
|
||||
ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT;
|
||||
|
||||
/*
|
||||
* Cache these so we can return them before returning
|
||||
* GSS_S_COMPLETE, even if the mechanism has itself
|
||||
* completed earlier
|
||||
*/
|
||||
ctx->mech_flags = 0;
|
||||
ctx->mech_time_rec = 0;
|
||||
ctx->mech_src_name = GSS_C_NO_NAME;
|
||||
|
||||
ctx->open = 0;
|
||||
ctx->local = 0;
|
||||
ctx->require_mic = 0;
|
||||
ctx->verified_mic = 0;
|
||||
ctx->flags.open = 0;
|
||||
ctx->flags.local = 0;
|
||||
ctx->flags.peer_require_mic = 0;
|
||||
ctx->flags.require_mic = 0;
|
||||
ctx->flags.verified_mic = 0;
|
||||
|
||||
HEIMDAL_MUTEX_init(&ctx->ctx_id_mutex);
|
||||
|
||||
ctx->negoex_step = 0;
|
||||
ctx->negoex_transcript = NULL;
|
||||
ctx->negoex_seqnum = 0;
|
||||
HEIM_TAILQ_INIT(&ctx->negoex_mechs);
|
||||
memset(ctx->negoex_conv_id, 0, GUID_LENGTH);
|
||||
|
||||
*context_handle = (gss_ctx_id_t)ctx;
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
@@ -119,11 +123,12 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_internal_delete_sec_context
|
||||
return GSS_S_NO_CONTEXT;
|
||||
}
|
||||
|
||||
if (ctx->initiator_mech_types.val != NULL)
|
||||
free_MechTypeList(&ctx->initiator_mech_types);
|
||||
if (ctx->NegTokenInit_mech_types.value)
|
||||
free(ctx->NegTokenInit_mech_types.value);
|
||||
|
||||
gss_release_oid(&minor, &ctx->preferred_mech_type);
|
||||
ctx->preferred_mech_type = GSS_C_NO_OID;
|
||||
ctx->negotiated_mech_type = GSS_C_NO_OID;
|
||||
ctx->selected_mech_type = GSS_C_NO_OID;
|
||||
|
||||
gss_release_name(&minor, &ctx->target_name);
|
||||
gss_release_name(&minor, &ctx->mech_src_name);
|
||||
@@ -137,6 +142,8 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_internal_delete_sec_context
|
||||
ret = GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
_gss_negoex_release_context(ctx);
|
||||
|
||||
HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
|
||||
HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
|
||||
|
||||
@@ -146,93 +153,121 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_internal_delete_sec_context
|
||||
}
|
||||
|
||||
/*
|
||||
* For compatability with the Windows SPNEGO implementation, the
|
||||
* default is to ignore the mechListMIC unless CFX is used and
|
||||
* a non-preferred mechanism was negotiated
|
||||
* Returns TRUE if it is safe to omit mechListMIC because the preferred
|
||||
* mechanism was selected, and the peer did not require it.
|
||||
*/
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV
|
||||
_gss_spnego_require_mechlist_mic(OM_uint32 *minor_status,
|
||||
gssspnego_ctx ctx,
|
||||
int *require_mic)
|
||||
int
|
||||
_gss_spnego_safe_omit_mechlist_mic(gssspnego_ctx ctx)
|
||||
{
|
||||
gss_buffer_set_t buffer_set = GSS_C_NO_BUFFER_SET;
|
||||
OM_uint32 minor;
|
||||
int safe_omit = 0;
|
||||
|
||||
*minor_status = 0;
|
||||
*require_mic = 0;
|
||||
if (ctx->flags.peer_require_mic == FALSE)
|
||||
safe_omit = gss_oid_equal(ctx->selected_mech_type, ctx->preferred_mech_type);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return GSS_S_COMPLETE;
|
||||
if (safe_omit)
|
||||
_gss_mg_log(10, "spnego: safe to omit mechListMIC, as preferred mechanism selected");
|
||||
else
|
||||
_gss_mg_log(10, "spnego: mechListMIC required");
|
||||
|
||||
return safe_omit;
|
||||
}
|
||||
|
||||
|
||||
static OM_uint32
|
||||
add_mech_type(OM_uint32 *minor_status,
|
||||
gss_OID mech_type,
|
||||
MechTypeList *mechtypelist)
|
||||
{
|
||||
MechType mech;
|
||||
int ret;
|
||||
|
||||
heim_assert(!gss_oid_equal(mech_type, GSS_SPNEGO_MECHANISM),
|
||||
"SPNEGO mechanism not filtered");
|
||||
|
||||
ret = der_get_oid(mech_type->elements, mech_type->length, &mech, NULL);
|
||||
if (ret == 0) {
|
||||
ret = add_MechTypeList(mechtypelist, &mech);
|
||||
free_MechType(&mech);
|
||||
}
|
||||
|
||||
if (ctx->require_mic) {
|
||||
/* Acceptor requested it: mandatory to honour */
|
||||
*require_mic = 1;
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether peer indicated implicit support for updated SPNEGO
|
||||
* (eg. in the Kerberos case by using CFX)
|
||||
*/
|
||||
if (gss_inquire_sec_context_by_oid(&minor, ctx->negotiated_ctx_id,
|
||||
GSS_C_PEER_HAS_UPDATED_SPNEGO,
|
||||
&buffer_set) == GSS_S_COMPLETE) {
|
||||
*require_mic = 1;
|
||||
gss_release_buffer_set(&minor, &buffer_set);
|
||||
}
|
||||
|
||||
/* Safe-to-omit MIC rules follow */
|
||||
if (*require_mic) {
|
||||
if (gss_oid_equal(ctx->negotiated_mech_type, ctx->preferred_mech_type)) {
|
||||
*require_mic = 0;
|
||||
} else if (gss_oid_equal(ctx->negotiated_mech_type, &_gss_spnego_krb5_mechanism_oid_desc) &&
|
||||
gss_oid_equal(ctx->preferred_mech_type, &_gss_spnego_mskrb_mechanism_oid_desc)) {
|
||||
*require_mic = 0;
|
||||
}
|
||||
if (ret) {
|
||||
*minor_status = ret;
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
static int
|
||||
add_mech_type(gss_OID mech_type,
|
||||
int includeMSCompatOID,
|
||||
MechTypeList *mechtypelist)
|
||||
add_mech_if_approved(OM_uint32 *minor_status,
|
||||
gss_const_name_t target_name,
|
||||
OM_uint32 (*func)(OM_uint32 *, void *, gss_const_name_t, gss_const_cred_id_t, gss_OID),
|
||||
void *userptr,
|
||||
int includeMSCompatOID,
|
||||
gss_const_cred_id_t cred_handle,
|
||||
MechTypeList *mechtypelist,
|
||||
gss_OID mech_oid,
|
||||
gss_OID *first_mech,
|
||||
OM_uint32 *first_major,
|
||||
OM_uint32 *first_minor,
|
||||
int *added_negoex)
|
||||
{
|
||||
MechType mech;
|
||||
int ret;
|
||||
OM_uint32 major, minor;
|
||||
|
||||
if (gss_oid_equal(mech_type, GSS_SPNEGO_MECHANISM))
|
||||
return 0;
|
||||
|
||||
if (includeMSCompatOID &&
|
||||
gss_oid_equal(mech_type, &_gss_spnego_krb5_mechanism_oid_desc)) {
|
||||
ret = der_get_oid(_gss_spnego_mskrb_mechanism_oid_desc.elements,
|
||||
_gss_spnego_mskrb_mechanism_oid_desc.length,
|
||||
&mech,
|
||||
NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = add_MechTypeList(mechtypelist, &mech);
|
||||
free_MechType(&mech);
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* Unapproved mechanisms are ignored, but we capture their result
|
||||
* code in case we didn't find any other mechanisms, in which case
|
||||
* we return that to the caller of _gss_spnego_indicate_mechtypelist().
|
||||
*/
|
||||
major = (*func)(&minor, userptr, target_name, cred_handle, mech_oid);
|
||||
if (major != GSS_S_COMPLETE) {
|
||||
if (*first_mech == GSS_C_NO_OID) {
|
||||
*first_major = major;
|
||||
*first_minor = minor;
|
||||
}
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
ret = der_get_oid(mech_type->elements, mech_type->length, &mech, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = add_MechTypeList(mechtypelist, &mech);
|
||||
free_MechType(&mech);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (_gss_negoex_mech_p(mech_oid)) {
|
||||
if (*added_negoex == FALSE) {
|
||||
major = add_mech_type(minor_status, GSS_NEGOEX_MECHANISM, mechtypelist);
|
||||
if (major != GSS_S_COMPLETE)
|
||||
return major;
|
||||
*added_negoex = TRUE;
|
||||
}
|
||||
|
||||
if (*first_mech == GSS_C_NO_OID)
|
||||
*first_mech = GSS_NEGOEX_MECHANISM;
|
||||
|
||||
/* if NegoEx-only mech, we are done */
|
||||
if (!_gss_negoex_and_spnego_mech_p(mech_oid))
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
if (includeMSCompatOID && gss_oid_equal(mech_oid, GSS_KRB5_MECHANISM)) {
|
||||
major = add_mech_type(minor_status,
|
||||
&_gss_spnego_mskrb_mechanism_oid_desc,
|
||||
mechtypelist);
|
||||
if (major != GSS_S_COMPLETE)
|
||||
return major;
|
||||
}
|
||||
|
||||
major = add_mech_type(minor_status, mech_oid, mechtypelist);
|
||||
if (major != GSS_S_COMPLETE)
|
||||
return major;
|
||||
|
||||
if (*first_mech == GSS_C_NO_OID)
|
||||
*first_mech = mech_oid;
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV
|
||||
_gss_spnego_indicate_mechtypelist (OM_uint32 *minor_status,
|
||||
gss_name_t target_name,
|
||||
OM_uint32 (*func)(gss_const_cred_id_t, gss_name_t, gss_OID),
|
||||
gss_const_name_t target_name,
|
||||
OM_uint32 (*func)(OM_uint32 *, void *, gss_const_name_t, gss_const_cred_id_t, gss_OID),
|
||||
void *userptr,
|
||||
int includeMSCompatOID,
|
||||
gss_const_cred_id_t cred_handle,
|
||||
MechTypeList *mechtypelist,
|
||||
@@ -240,94 +275,297 @@ _gss_spnego_indicate_mechtypelist (OM_uint32 *minor_status,
|
||||
{
|
||||
gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
|
||||
gss_OID first_mech = GSS_C_NO_OID;
|
||||
OM_uint32 ret;
|
||||
OM_uint32 ret, minor;
|
||||
OM_uint32 first_major = GSS_S_BAD_MECH, first_minor = 0;
|
||||
size_t i;
|
||||
int present = FALSE;
|
||||
int added_negoex = FALSE;
|
||||
|
||||
mechtypelist->len = 0;
|
||||
mechtypelist->val = NULL;
|
||||
|
||||
if (cred_handle) {
|
||||
ret = gss_inquire_cred(minor_status,
|
||||
cred_handle,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&supported_mechs);
|
||||
} else {
|
||||
ret = gss_indicate_mechs(minor_status, &supported_mechs);
|
||||
}
|
||||
|
||||
if (ret != GSS_S_COMPLETE) {
|
||||
if (cred_handle != GSS_C_NO_CREDENTIAL)
|
||||
ret = _gss_spnego_inquire_cred_mechs(minor_status,
|
||||
cred_handle, &supported_mechs);
|
||||
else
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, &supported_mechs);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
|
||||
heim_assert(supported_mechs != GSS_C_NO_OID_SET,
|
||||
"NULL mech set returned by SPNEGO inquire/indicate mechs");
|
||||
|
||||
/*
|
||||
* Propose Kerberos mech first if we have Kerberos credentials/supported mechs
|
||||
*/
|
||||
|
||||
ret = gss_test_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
|
||||
supported_mechs, &present);
|
||||
if (ret == GSS_S_COMPLETE && present) {
|
||||
ret = add_mech_if_approved(minor_status, target_name,
|
||||
func, userptr, includeMSCompatOID,
|
||||
cred_handle, mechtypelist,
|
||||
GSS_KRB5_MECHANISM, &first_mech,
|
||||
&first_major, &first_minor,
|
||||
&added_negoex);
|
||||
}
|
||||
|
||||
if (supported_mechs->count == 0) {
|
||||
*minor_status = ENOENT;
|
||||
gss_release_oid_set(minor_status, &supported_mechs);
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
ret = (*func)(cred_handle, target_name, GSS_KRB5_MECHANISM);
|
||||
if (ret == GSS_S_COMPLETE) {
|
||||
ret = add_mech_type(GSS_KRB5_MECHANISM,
|
||||
includeMSCompatOID,
|
||||
mechtypelist);
|
||||
if (!GSS_ERROR(ret))
|
||||
first_mech = GSS_KRB5_MECHANISM;
|
||||
}
|
||||
ret = GSS_S_COMPLETE;
|
||||
/*
|
||||
* Now let's check all other mechs
|
||||
*/
|
||||
|
||||
for (i = 0; i < supported_mechs->count; i++) {
|
||||
OM_uint32 subret;
|
||||
if (gss_oid_equal(&supported_mechs->elements[i], GSS_SPNEGO_MECHANISM))
|
||||
continue;
|
||||
if (gss_oid_equal(&supported_mechs->elements[i], GSS_KRB5_MECHANISM))
|
||||
continue;
|
||||
|
||||
subret = (*func)(cred_handle, target_name, &supported_mechs->elements[i]);
|
||||
if (subret != GSS_S_COMPLETE)
|
||||
continue;
|
||||
|
||||
ret = add_mech_type(&supported_mechs->elements[i],
|
||||
includeMSCompatOID,
|
||||
mechtypelist);
|
||||
if (ret != 0) {
|
||||
*minor_status = ret;
|
||||
ret = GSS_S_FAILURE;
|
||||
break;
|
||||
ret = add_mech_if_approved(minor_status, target_name,
|
||||
func, userptr, FALSE,
|
||||
cred_handle, mechtypelist,
|
||||
&supported_mechs->elements[i],
|
||||
&first_mech,
|
||||
&first_major, &first_minor,
|
||||
&added_negoex);
|
||||
if (ret != GSS_S_COMPLETE) {
|
||||
gss_release_oid_set(&minor, &supported_mechs);
|
||||
return ret;
|
||||
}
|
||||
if (first_mech == GSS_C_NO_OID)
|
||||
first_mech = &supported_mechs->elements[i];
|
||||
}
|
||||
|
||||
if (mechtypelist->len == 0) {
|
||||
gss_release_oid_set(minor_status, &supported_mechs);
|
||||
*minor_status = 0;
|
||||
return GSS_S_BAD_MECH;
|
||||
}
|
||||
heim_assert(mechtypelist->len == 0 || first_mech != GSS_C_NO_OID,
|
||||
"mechtypelist non-empty but no mech selected");
|
||||
|
||||
if (preferred_mech != NULL) {
|
||||
ret = gss_duplicate_oid(minor_status, first_mech, preferred_mech);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
free_MechTypeList(mechtypelist);
|
||||
}
|
||||
gss_release_oid_set(minor_status, &supported_mechs);
|
||||
if (first_mech != GSS_C_NO_OID)
|
||||
ret = _gss_intern_oid(minor_status, first_mech, &first_mech);
|
||||
else if (GSS_ERROR(first_major)) {
|
||||
ret = first_major;
|
||||
*minor_status = first_minor;
|
||||
} else
|
||||
ret = GSS_S_BAD_MECH;
|
||||
|
||||
if (preferred_mech != NULL)
|
||||
*preferred_mech = first_mech;
|
||||
|
||||
gss_release_oid_set(&minor, &supported_mechs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
OM_uint32
|
||||
_gss_spnego_verify_mechtypes_mic(OM_uint32 *minor_status,
|
||||
gssspnego_ctx ctx,
|
||||
heim_octet_string *mic)
|
||||
{
|
||||
gss_buffer_desc mic_buf;
|
||||
OM_uint32 major_status;
|
||||
|
||||
if (mic == NULL) {
|
||||
*minor_status = 0;
|
||||
return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
|
||||
GSS_S_DEFECTIVE_TOKEN, 0,
|
||||
"SPNEGO peer failed to send mechListMIC");
|
||||
}
|
||||
|
||||
if (ctx->flags.verified_mic) {
|
||||
/* This doesn't make sense, we've already verified it? */
|
||||
*minor_status = 0;
|
||||
return GSS_S_DUPLICATE_TOKEN;
|
||||
}
|
||||
|
||||
mic_buf.length = mic->length;
|
||||
mic_buf.value = mic->data;
|
||||
|
||||
major_status = gss_verify_mic(minor_status,
|
||||
ctx->negotiated_ctx_id,
|
||||
&ctx->NegTokenInit_mech_types,
|
||||
&mic_buf,
|
||||
NULL);
|
||||
if (major_status == GSS_S_COMPLETE) {
|
||||
_gss_spnego_ntlm_reset_crypto(minor_status, ctx, TRUE);
|
||||
} else if (major_status == GSS_S_UNAVAILABLE) {
|
||||
_gss_mg_log(10, "mech doesn't support MIC, allowing anyway");
|
||||
} else if (major_status) {
|
||||
return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
|
||||
GSS_S_DEFECTIVE_TOKEN, *minor_status,
|
||||
"SPNEGO peer sent invalid mechListMIC");
|
||||
}
|
||||
ctx->flags.verified_mic = 1;
|
||||
|
||||
*minor_status = 0;
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
/*
|
||||
* According to [MS-SPNG] 3.3.5.1 the crypto state for NTLM is reset
|
||||
* before the completed context is returned to the application.
|
||||
*/
|
||||
|
||||
OM_uint32
|
||||
_gss_spnego_ntlm_reset_crypto(OM_uint32 *minor_status,
|
||||
gssspnego_ctx ctx,
|
||||
OM_uint32 verify)
|
||||
{
|
||||
gss_buffer_desc value;
|
||||
if (gss_oid_equal(ctx->negotiated_mech_type, GSS_NTLM_MECHANISM)) {
|
||||
gss_buffer_desc value;
|
||||
|
||||
value.length = sizeof(verify);
|
||||
value.value = &verify;
|
||||
value.length = sizeof(verify);
|
||||
value.value = &verify;
|
||||
|
||||
return gss_set_sec_context_option(minor_status,
|
||||
&ctx->negotiated_ctx_id,
|
||||
GSS_C_NTLM_RESET_CRYPTO,
|
||||
&value);
|
||||
return gss_set_sec_context_option(minor_status,
|
||||
&ctx->negotiated_ctx_id,
|
||||
GSS_C_NTLM_RESET_CRYPTO,
|
||||
&value);
|
||||
}
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
void
|
||||
_gss_spnego_log_mech(const char *prefix, gss_const_OID oid)
|
||||
{
|
||||
gss_buffer_desc oidbuf = GSS_C_EMPTY_BUFFER;
|
||||
OM_uint32 junk;
|
||||
const char *name = NULL;
|
||||
|
||||
if (!_gss_mg_log_level(10))
|
||||
return;
|
||||
|
||||
if (oid == GSS_C_NO_OID ||
|
||||
gss_oid_to_str(&junk, (gss_OID)oid, &oidbuf) != GSS_S_COMPLETE) {
|
||||
_gss_mg_log(10, "spnego: %s (null)", prefix);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gss_oid_equal(oid, GSS_NEGOEX_MECHANISM))
|
||||
name = "negoex"; /* not a real mech */
|
||||
else if (gss_oid_equal(oid, &_gss_spnego_mskrb_mechanism_oid_desc))
|
||||
name = "mskrb";
|
||||
else {
|
||||
gssapi_mech_interface m = __gss_get_mechanism(oid);
|
||||
if (m)
|
||||
name = m->gm_name;
|
||||
}
|
||||
|
||||
_gss_mg_log(10, "spnego: %s %s { %.*s }",
|
||||
prefix,
|
||||
name ? name : "unknown",
|
||||
(int)oidbuf.length, (char *)oidbuf.value);
|
||||
gss_release_buffer(&junk, &oidbuf);
|
||||
}
|
||||
|
||||
void
|
||||
_gss_spnego_log_mechTypes(MechTypeList *mechTypes)
|
||||
{
|
||||
size_t i;
|
||||
char mechbuf[64];
|
||||
size_t mech_len;
|
||||
gss_OID_desc oid;
|
||||
int ret;
|
||||
|
||||
if (!_gss_mg_log_level(10))
|
||||
return;
|
||||
|
||||
for (i = 0; i < mechTypes->len; i++) {
|
||||
ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1,
|
||||
sizeof(mechbuf),
|
||||
&mechTypes->val[i],
|
||||
&mech_len);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
oid.length = (OM_uint32)mech_len;
|
||||
oid.elements = mechbuf + sizeof(mechbuf) - mech_len;
|
||||
|
||||
_gss_spnego_log_mech("initiator proposed mech", &oid);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Indicate mechs negotiable by SPNEGO
|
||||
*/
|
||||
|
||||
OM_uint32
|
||||
_gss_spnego_indicate_mechs(OM_uint32 *minor_status,
|
||||
gss_OID_set *mechs_p)
|
||||
{
|
||||
gss_OID_desc oids[3];
|
||||
gss_OID_set_desc except;
|
||||
|
||||
*mechs_p = GSS_C_NO_OID_SET;
|
||||
|
||||
oids[0] = *GSS_C_MA_DEPRECATED;
|
||||
oids[1] = *GSS_C_MA_NOT_DFLT_MECH;
|
||||
oids[2] = *GSS_C_MA_MECH_NEGO;
|
||||
|
||||
except.count = sizeof(oids) / sizeof(oids[0]);
|
||||
except.elements = oids;
|
||||
|
||||
return gss_indicate_mechs_by_attrs(minor_status,
|
||||
GSS_C_NO_OID_SET,
|
||||
&except,
|
||||
GSS_C_NO_OID_SET,
|
||||
mechs_p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Indicate mechs in cred negotiatble by SPNEGO
|
||||
*/
|
||||
|
||||
OM_uint32
|
||||
_gss_spnego_inquire_cred_mechs(OM_uint32 *minor_status,
|
||||
gss_const_cred_id_t cred,
|
||||
gss_OID_set *mechs_p)
|
||||
{
|
||||
OM_uint32 ret, junk;
|
||||
gss_OID_set cred_mechs = GSS_C_NO_OID_SET;
|
||||
gss_OID_set mechs = GSS_C_NO_OID_SET;
|
||||
size_t i;
|
||||
|
||||
*mechs_p = GSS_C_NO_OID_SET;
|
||||
|
||||
heim_assert(cred != GSS_C_NO_CREDENTIAL, "Invalid null credential handle");
|
||||
|
||||
ret = gss_inquire_cred(minor_status, cred, NULL, NULL, NULL, &cred_mechs);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
goto out;
|
||||
|
||||
heim_assert(cred_mechs != GSS_C_NO_OID_SET,
|
||||
"gss_inquire_cred succeeded but returned null OID set");
|
||||
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, &mechs);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
goto out;
|
||||
|
||||
heim_assert(mechs != GSS_C_NO_OID_SET,
|
||||
"_gss_spnego_indicate_mechs succeeded but returned null OID set");
|
||||
|
||||
ret = gss_create_empty_oid_set(minor_status, mechs_p);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < cred_mechs->count; i++) {
|
||||
gss_OID cred_mech = &cred_mechs->elements[i];
|
||||
int present = 0;
|
||||
|
||||
gss_test_oid_set_member(&junk, cred_mech, mechs, &present);
|
||||
if (!present)
|
||||
continue;
|
||||
|
||||
ret = gss_add_oid_set_member(minor_status, cred_mech, mechs_p);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
gss_release_oid_set(&junk, mechs_p);
|
||||
gss_release_oid_set(&junk, &cred_mechs);
|
||||
gss_release_oid_set(&junk, &mechs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@@ -32,40 +32,6 @@
|
||||
|
||||
#include "spnego_locl.h"
|
||||
|
||||
static OM_uint32
|
||||
spnego_supported_mechs(OM_uint32 *minor_status, gss_OID_set *mechs)
|
||||
{
|
||||
OM_uint32 ret, junk;
|
||||
gss_OID_set m;
|
||||
size_t i;
|
||||
|
||||
ret = gss_indicate_mechs(minor_status, &m);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
|
||||
ret = gss_create_empty_oid_set(minor_status, mechs);
|
||||
if (ret != GSS_S_COMPLETE) {
|
||||
gss_release_oid_set(&junk, &m);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < m->count; i++) {
|
||||
if (gss_oid_equal(&m->elements[i], GSS_SPNEGO_MECHANISM))
|
||||
continue;
|
||||
|
||||
ret = gss_add_oid_set_member(minor_status, &m->elements[i], mechs);
|
||||
if (ret) {
|
||||
gss_release_oid_set(&junk, &m);
|
||||
gss_release_oid_set(&junk, mechs);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
gss_release_oid_set(&junk, &m);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV _gss_spnego_process_context_token
|
||||
(OM_uint32 *minor_status,
|
||||
gss_const_ctx_id_t context_handle,
|
||||
@@ -450,7 +416,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_import_sec_context (
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx->open = 1;
|
||||
ctx->flags.open = 1;
|
||||
/* don't bother filling in the rest of the fields */
|
||||
|
||||
HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
|
||||
@@ -472,7 +438,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_inquire_names_for_mech (
|
||||
|
||||
*name_types = NULL;
|
||||
|
||||
ret = spnego_supported_mechs(minor_status, &mechs);
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, &mechs);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
|
||||
|
@@ -68,48 +68,20 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_acquire_cred_from
|
||||
)
|
||||
{
|
||||
OM_uint32 ret, tmp;
|
||||
gss_OID_set_desc actual_desired_mechs;
|
||||
gss_OID_set mechs;
|
||||
size_t i, j;
|
||||
|
||||
*output_cred_handle = GSS_C_NO_CREDENTIAL;
|
||||
|
||||
ret = gss_indicate_mechs(minor_status, &mechs);
|
||||
ret = _gss_spnego_indicate_mechs(minor_status, &mechs);
|
||||
if (ret != GSS_S_COMPLETE)
|
||||
return ret;
|
||||
|
||||
/* Remove ourselves from this list */
|
||||
actual_desired_mechs.count = mechs->count;
|
||||
actual_desired_mechs.elements = malloc(actual_desired_mechs.count *
|
||||
sizeof(gss_OID_desc));
|
||||
if (actual_desired_mechs.elements == NULL) {
|
||||
*minor_status = ENOMEM;
|
||||
ret = GSS_S_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0, j = 0; i < mechs->count; i++) {
|
||||
if (gss_oid_equal(&mechs->elements[i], GSS_SPNEGO_MECHANISM))
|
||||
continue;
|
||||
|
||||
actual_desired_mechs.elements[j] = mechs->elements[i];
|
||||
j++;
|
||||
}
|
||||
actual_desired_mechs.count = j;
|
||||
|
||||
ret = gss_acquire_cred_from(minor_status, desired_name,
|
||||
time_req, &actual_desired_mechs,
|
||||
time_req, mechs,
|
||||
cred_usage, cred_store,
|
||||
output_cred_handle,
|
||||
actual_mechs, time_rec);
|
||||
gss_release_oid_set(&tmp, &mechs);
|
||||
if (actual_desired_mechs.elements != NULL) {
|
||||
free(actual_desired_mechs.elements);
|
||||
}
|
||||
|
||||
if (ret != GSS_S_COMPLETE) {
|
||||
_gss_spnego_release_cred(&tmp, output_cred_handle);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -235,9 +207,6 @@ _gss_spnego_set_neg_mechs (OM_uint32 *minor_status,
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* for inner negotiation mechs, such as NegoEx */
|
||||
(void) gss_set_neg_mechs(&minor, cred_handle, mech_list);
|
||||
} else {
|
||||
/*
|
||||
* RFC 4178 says that GSS_Set_neg_mechs() on NULL credential sets
|
||||
|
@@ -151,6 +151,9 @@ static gssapi_mech_interface_desc spnego_mech = {
|
||||
NULL, /* gm_store_cred_into */
|
||||
_gss_spnego_set_neg_mechs,
|
||||
_gss_spnego_get_neg_mechs,
|
||||
NULL, /* gm_query_mechanism_info */
|
||||
NULL, /* gm_query_meta_data */
|
||||
NULL, /* gm_exchange_meta_data */
|
||||
NULL /* gm_compat */
|
||||
};
|
||||
|
||||
@@ -159,3 +162,4 @@ __gss_spnego_initialize(void)
|
||||
{
|
||||
return &spnego_mech;
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
1018
lib/gssapi/spnego/negoex_ctx.c
Normal file
1018
lib/gssapi/spnego/negoex_ctx.c
Normal file
File diff suppressed because it is too large
Load Diff
25
lib/gssapi/spnego/negoex_err.et
Normal file
25
lib/gssapi/spnego/negoex_err.et
Normal file
@@ -0,0 +1,25 @@
|
||||
#
|
||||
# NegoEx error messages
|
||||
#
|
||||
|
||||
id "$Id$"
|
||||
|
||||
error_table ngex
|
||||
|
||||
prefix NEGOEX
|
||||
|
||||
error_code INVALID_MESSAGE_SIGNATURE, "Invalid NegoEx signature"
|
||||
error_code INVALID_MESSAGE_TYPE, "Invalid NegoEx message type"
|
||||
error_code INVALID_MESSAGE_SIZE, "Invalid NegoEx message size"
|
||||
error_code INVALID_CONVERSATION_ID, "Invalid NegoEx conversation ID"
|
||||
error_code AUTH_SCHEME_NOT_FOUND, "NegoEx authentication scheme not found"
|
||||
error_code MISSING_NEGO_MESSAGE, "Missing NegoEx negotiate message"
|
||||
error_code MISSING_AP_REQUEST_MESSAGE, "Missing NegoEx authentication protocol request message"
|
||||
error_code NO_AVAILABLE_MECHS, "No mutually supported NegoEx authentication schemes"
|
||||
error_code NO_VERIFY_KEY, "No NegoEx verify key"
|
||||
error_code UNKNOWN_CHECKSUM_SCHEME, "Unknown NegoEx checksum scheme"
|
||||
error_code INVALID_CHECKSUM, "Invalid NegoEx checksum"
|
||||
error_code UNSUPPORTED_CRITICAL_EXTENSION, "Unsupported critical NegoEx extension"
|
||||
error_code UNSUPPORTED_VERSION, "Unsupported NegoEx version"
|
||||
error_code MESSAGE_OUT_OF_SEQUENCE, "NegoEx message out of sequence"
|
||||
|
127
lib/gssapi/spnego/negoex_locl.h
Normal file
127
lib/gssapi/spnego/negoex_locl.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2019 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:
|
||||
*
|
||||
* * 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.
|
||||
*/
|
||||
|
||||
#ifndef NEGOEX_LOCL_H
|
||||
#define NEGOEX_LOCL_H
|
||||
|
||||
#include <negoex_err.h>
|
||||
|
||||
struct gssspnego_ctx_desc;
|
||||
|
||||
#define MESSAGE_SIGNATURE 0x535458454F47454EULL
|
||||
|
||||
#define EXTENSION_LENGTH 12
|
||||
|
||||
#define EXTENSION_FLAG_CRITICAL 0x80000000
|
||||
|
||||
#define CHECKSUM_SCHEME_RFC3961 1
|
||||
|
||||
#define NEGOEX_KEYUSAGE_INITIATOR_CHECKSUM 23
|
||||
#define NEGOEX_KEYUSAGE_ACCEPTOR_CHECKSUM 25
|
||||
|
||||
#define CHECKSUM_HEADER_LENGTH 20
|
||||
|
||||
#define GUID_LENGTH 16
|
||||
|
||||
typedef uint8_t auth_scheme[GUID_LENGTH];
|
||||
typedef uint8_t conversation_id[GUID_LENGTH];
|
||||
#define GUID_EQ(a, b) (memcmp(a, b, GUID_LENGTH) == 0)
|
||||
|
||||
#define NEGO_MESSAGE_HEADER_LENGTH 96
|
||||
#define EXCHANGE_MESSAGE_HEADER_LENGTH 64
|
||||
#define VERIFY_MESSAGE_HEADER_LENGTH 80
|
||||
#define ALERT_MESSAGE_HEADER_LENGTH 72
|
||||
#define ALERT_LENGTH 12
|
||||
#define ALERT_PULSE_LENGTH 8
|
||||
|
||||
#define ALERT_TYPE_PULSE 1
|
||||
#define ALERT_VERIFY_NO_KEY 1
|
||||
|
||||
enum message_type {
|
||||
INITIATOR_NEGO = 0, /* NEGO_MESSAGE */
|
||||
ACCEPTOR_NEGO, /* NEGO_MESSAGE */
|
||||
INITIATOR_META_DATA, /* EXCHANGE_MESSAGE */
|
||||
ACCEPTOR_META_DATA, /* EXCHANGE_MESSAGE */
|
||||
CHALLENGE, /* EXCHANGE_MESSAGE */
|
||||
AP_REQUEST, /* EXCHANGE_MESSAGE */
|
||||
VERIFY, /* VERIFY_MESSAGE */
|
||||
ALERT, /* ALERT */
|
||||
};
|
||||
|
||||
struct nego_message {
|
||||
uint8_t random[32];
|
||||
const uint8_t *schemes;
|
||||
uint16_t nschemes;
|
||||
};
|
||||
|
||||
struct exchange_message {
|
||||
auth_scheme scheme;
|
||||
gss_buffer_desc token;
|
||||
};
|
||||
|
||||
struct verify_message {
|
||||
auth_scheme scheme;
|
||||
uint32_t cksum_type;
|
||||
const uint8_t *cksum;
|
||||
size_t cksum_len;
|
||||
size_t offset_in_token;
|
||||
};
|
||||
|
||||
struct alert_message {
|
||||
auth_scheme scheme;
|
||||
int verify_no_key;
|
||||
};
|
||||
|
||||
struct negoex_message {
|
||||
uint32_t type;
|
||||
union {
|
||||
struct nego_message n;
|
||||
struct exchange_message e;
|
||||
struct verify_message v;
|
||||
struct alert_message a;
|
||||
} u;
|
||||
};
|
||||
|
||||
struct negoex_auth_mech {
|
||||
HEIM_TAILQ_ENTRY(negoex_auth_mech) links;
|
||||
gss_OID oid;
|
||||
auth_scheme scheme;
|
||||
gss_ctx_id_t mech_context;
|
||||
gss_buffer_desc metadata;
|
||||
krb5_crypto crypto;
|
||||
krb5_crypto verify_crypto;
|
||||
int complete;
|
||||
int sent_checksum;
|
||||
int verified_checksum;
|
||||
};
|
||||
|
||||
#define NEGOEX_LOG_LEVEL 10
|
||||
|
||||
#endif /* NEGOEX_LOCL_H */
|
1059
lib/gssapi/spnego/negoex_util.c
Normal file
1059
lib/gssapi/spnego/negoex_util.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -37,14 +37,17 @@ NegTokenInit ::= SEQUENCE {
|
||||
...
|
||||
}
|
||||
|
||||
NegResultEnum ::= ENUMERATED {
|
||||
accept_completed(0),
|
||||
accept_incomplete(1),
|
||||
reject(2),
|
||||
request-mic(3)
|
||||
}
|
||||
|
||||
-- NB: negResult is not OPTIONAL in the new SPNEGO spec but
|
||||
-- Windows clients do not always send it
|
||||
NegTokenResp ::= SEQUENCE {
|
||||
negResult [0] ENUMERATED {
|
||||
accept_completed (0),
|
||||
accept_incomplete (1),
|
||||
reject (2),
|
||||
request-mic (3) } OPTIONAL,
|
||||
negResult [0] NegResultEnum OPTIONAL,
|
||||
supportedMech [1] MechType OPTIONAL,
|
||||
responseToken [2] OCTET STRING OPTIONAL,
|
||||
mechListMIC [3] OCTET STRING OPTIONAL,
|
||||
|
@@ -50,6 +50,7 @@
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#include <krb5.h>
|
||||
#include <gssapi.h>
|
||||
#include <gssapi_krb5.h>
|
||||
#include <gssapi_spnego.h>
|
||||
@@ -63,11 +64,13 @@
|
||||
#endif
|
||||
|
||||
#include <heim_threads.h>
|
||||
#include <heimqueue.h>
|
||||
#include <asn1_err.h>
|
||||
|
||||
#include <gssapi_mech.h>
|
||||
|
||||
#include "spnego_asn1.h"
|
||||
#include "negoex_locl.h"
|
||||
#include "utils.h"
|
||||
#include <der.h>
|
||||
|
||||
@@ -75,30 +78,72 @@
|
||||
|
||||
#define ALLOC(X, N) (X) = calloc((N), sizeof(*(X)))
|
||||
|
||||
typedef struct {
|
||||
MechTypeList initiator_mech_types;
|
||||
struct gssspnego_ctx_desc;
|
||||
typedef struct gssspnego_ctx_desc *gssspnego_ctx;
|
||||
|
||||
typedef OM_uint32
|
||||
(*gssspnego_initiator_state)(OM_uint32 * minor_status,
|
||||
gss_const_cred_id_t cred,
|
||||
gssspnego_ctx ctx,
|
||||
gss_const_name_t name,
|
||||
gss_const_OID mech_type,
|
||||
OM_uint32 req_flags,
|
||||
OM_uint32 time_req,
|
||||
const gss_channel_bindings_t input_chan_bindings,
|
||||
gss_const_buffer_t input_token,
|
||||
gss_buffer_t output_token,
|
||||
OM_uint32 * ret_flags,
|
||||
OM_uint32 * time_rec);
|
||||
|
||||
struct gssspnego_ctx_desc {
|
||||
gss_buffer_desc NegTokenInit_mech_types;
|
||||
gss_OID preferred_mech_type;
|
||||
gss_OID selected_mech_type;
|
||||
gss_OID negotiated_mech_type;
|
||||
gss_ctx_id_t negotiated_ctx_id;
|
||||
OM_uint32 mech_flags;
|
||||
OM_uint32 mech_time_rec;
|
||||
gss_name_t mech_src_name;
|
||||
unsigned int open : 1;
|
||||
unsigned int local : 1;
|
||||
unsigned int require_mic : 1;
|
||||
unsigned int verified_mic : 1;
|
||||
unsigned int maybe_open : 1;
|
||||
struct spnego_flags {
|
||||
unsigned int open : 1;
|
||||
unsigned int local : 1;
|
||||
unsigned int require_mic : 1;
|
||||
unsigned int peer_require_mic : 1;
|
||||
unsigned int sent_mic : 1;
|
||||
unsigned int verified_mic : 1;
|
||||
unsigned int safe_omit : 1;
|
||||
unsigned int maybe_open : 1;
|
||||
unsigned int seen_supported_mech : 1;
|
||||
} flags;
|
||||
HEIMDAL_MUTEX ctx_id_mutex;
|
||||
|
||||
gss_name_t target_name;
|
||||
gssspnego_initiator_state initiator_state;
|
||||
|
||||
u_char oidbuf[17];
|
||||
size_t oidlen;
|
||||
|
||||
} *gssspnego_ctx;
|
||||
int negoex_step;
|
||||
krb5_storage *negoex_transcript;
|
||||
uint32_t negoex_seqnum;
|
||||
conversation_id negoex_conv_id;
|
||||
HEIM_TAILQ_HEAD(negoex_mech_list, negoex_auth_mech) negoex_mechs;
|
||||
};
|
||||
|
||||
extern gss_OID_desc _gss_spnego_mskrb_mechanism_oid_desc;
|
||||
extern gss_OID_desc _gss_spnego_krb5_mechanism_oid_desc;
|
||||
|
||||
struct gssspnego_optimistic_ctx {
|
||||
gssspnego_ctx spnegoctx;
|
||||
OM_uint32 req_flags;
|
||||
gss_name_t target_name;
|
||||
OM_uint32 time_req;
|
||||
gss_channel_bindings_t input_chan_bindings;
|
||||
/* out */
|
||||
gss_OID preferred_mech_type;
|
||||
gss_OID negotiated_mech_type;
|
||||
gss_buffer_desc optimistic_token;
|
||||
OM_uint32 optimistic_flags, optimistic_time_rec;
|
||||
gss_ctx_id_t gssctx;
|
||||
int complete;
|
||||
auth_scheme scheme;
|
||||
};
|
||||
|
||||
#include "spnego-private.h"
|
||||
|
||||
|
@@ -72,6 +72,9 @@ static int help_flag = 0;
|
||||
static krb5_context context;
|
||||
static krb5_enctype limit_enctype = 0;
|
||||
|
||||
static gss_OID_desc test_negoex_1_mech = { 6, "\x69\x85\xa2\xc0\xac\x66" };
|
||||
static gss_OID_desc test_negoex_2_mech = { 6, "\x69\x84\xb0\xd1\xa8\x2c" };
|
||||
|
||||
static struct {
|
||||
const char *name;
|
||||
gss_OID oid;
|
||||
@@ -79,7 +82,9 @@ static struct {
|
||||
{ "krb5", NULL /* GSS_KRB5_MECHANISM */ },
|
||||
{ "spnego", NULL /* GSS_SPNEGO_MECHANISM */ },
|
||||
{ "ntlm", NULL /* GSS_NTLM_MECHANISM */ },
|
||||
{ "sasl-digest-md5", NULL /* GSS_SASL_DIGEST_MD5_MECHANISM */ }
|
||||
{ "sasl-digest-md5", NULL /* GSS_SASL_DIGEST_MD5_MECHANISM */ },
|
||||
{ "test_negoex_1", NULL },
|
||||
{ "test_negoex_2", NULL },
|
||||
};
|
||||
|
||||
static void
|
||||
@@ -89,6 +94,8 @@ init_o2n(void)
|
||||
o2n[1].oid = GSS_SPNEGO_MECHANISM;
|
||||
o2n[2].oid = GSS_NTLM_MECHANISM;
|
||||
o2n[3].oid = GSS_SASL_DIGEST_MD5_MECHANISM;
|
||||
o2n[4].oid = &test_negoex_1_mech;
|
||||
o2n[5].oid = &test_negoex_2_mech;
|
||||
}
|
||||
|
||||
static gss_OID
|
||||
@@ -98,7 +105,7 @@ string_to_oid(const char *name)
|
||||
for (i = 0; i < sizeof(o2n)/sizeof(o2n[0]); i++)
|
||||
if (strcasecmp(name, o2n[i].name) == 0)
|
||||
return o2n[i].oid;
|
||||
errx(1, "name '%s' not unknown", name);
|
||||
errx(1, "name '%s' not known", name);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -612,7 +619,7 @@ main(int argc, char **argv)
|
||||
gss_cred_id_t client_cred = GSS_C_NO_CREDENTIAL, deleg_cred = GSS_C_NO_CREDENTIAL;
|
||||
gss_name_t cname = GSS_C_NO_NAME;
|
||||
gss_buffer_desc credential_data = GSS_C_EMPTY_BUFFER;
|
||||
gss_OID_desc oids[4];
|
||||
gss_OID_desc oids[6];
|
||||
gss_OID_set_desc mechoid_descs;
|
||||
gss_OID_set mechoids = GSS_C_NO_OID_SET;
|
||||
gss_key_value_element_desc client_cred_elements[2];
|
||||
|
572
lib/gssapi/test_negoex_mech.c
Normal file
572
lib/gssapi/test_negoex_mech.c
Normal file
@@ -0,0 +1,572 @@
|
||||
/*
|
||||
* Copyright (C) 2019 by the Massachusetts Institute of Technology.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <roken.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <krb5.h>
|
||||
#include <der.h>
|
||||
#include <gssapi_asn1.h>
|
||||
|
||||
#include <gssapi/gssapi.h>
|
||||
#include <gssapi/gssapi_spnego.h>
|
||||
|
||||
struct test_context {
|
||||
int initiator;
|
||||
uint8_t hops; /* hops remaining; 0 means established */
|
||||
};
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV
|
||||
gss_init_sec_context(OM_uint32 *minor_status,
|
||||
gss_const_cred_id_t claimant_cred_handle,
|
||||
gss_ctx_id_t *context_handle, gss_const_name_t target_name,
|
||||
const gss_OID mech_type, OM_uint32 req_flags,
|
||||
OM_uint32 time_req,
|
||||
const gss_channel_bindings_t input_chan_bindings,
|
||||
const gss_buffer_t input_token, gss_OID *actual_mech,
|
||||
gss_buffer_t output_token, OM_uint32 *ret_flags,
|
||||
OM_uint32 *time_rec)
|
||||
{
|
||||
struct test_context *ctx = (struct test_context *)*context_handle;
|
||||
OM_uint32 major;
|
||||
gss_buffer_desc tok;
|
||||
const char *envstr;
|
||||
uint8_t hops, mech_last_octet;
|
||||
|
||||
major = gss_duplicate_oid(minor_status, mech_type, actual_mech);
|
||||
if (major != GSS_S_COMPLETE)
|
||||
return major;
|
||||
|
||||
if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
|
||||
envstr = getenv("HOPS");
|
||||
hops = (envstr != NULL) ? atoi(envstr) : 1;
|
||||
assert(hops > 0);
|
||||
} else if (input_token->length == 4 &&
|
||||
memcmp(input_token->value, "fail", 4) == 0) {
|
||||
*minor_status = 12345;
|
||||
return GSS_S_FAILURE;
|
||||
} else {
|
||||
hops = ((uint8_t *)input_token->value)[0];
|
||||
}
|
||||
|
||||
mech_last_octet = ((uint8_t *)mech_type->elements)[mech_type->length - 1];
|
||||
envstr = getenv("INIT_FAIL");
|
||||
if (envstr != NULL && atoi(envstr) == mech_last_octet)
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
if (ctx == NULL) {
|
||||
ctx = malloc(sizeof(*ctx));
|
||||
assert(ctx != NULL);
|
||||
ctx->initiator = 1;
|
||||
ctx->hops = hops;
|
||||
*context_handle = (gss_ctx_id_t)ctx;
|
||||
} else if (ctx != NULL) {
|
||||
assert(ctx->initiator);
|
||||
ctx->hops--;
|
||||
assert(ctx->hops == hops);
|
||||
}
|
||||
|
||||
if (ctx->hops > 0) {
|
||||
/* Generate a token containing the remaining hop count. */
|
||||
ctx->hops--;
|
||||
tok.value = &ctx->hops;
|
||||
tok.length = 1;
|
||||
major = gss_encapsulate_token(&tok, mech_type, output_token);
|
||||
assert(major == GSS_S_COMPLETE);
|
||||
}
|
||||
|
||||
return (ctx->hops > 0) ? GSS_S_CONTINUE_NEEDED : GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV
|
||||
gss_accept_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,
|
||||
gss_const_cred_id_t verifier_cred_handle,
|
||||
const gss_buffer_t input_token,
|
||||
const gss_channel_bindings_t input_chan_bindings,
|
||||
gss_name_t *src_name, gss_OID *mech_type,
|
||||
gss_buffer_t output_token, OM_uint32 *ret_flags,
|
||||
OM_uint32 *time_rec,
|
||||
gss_cred_id_t *delegated_cred_handle)
|
||||
{
|
||||
struct test_context *ctx = (struct test_context *)*context_handle;
|
||||
uint8_t hops, mech_last_octet;
|
||||
const char *envstr;
|
||||
unsigned char mechbuf[64];
|
||||
GSSAPIContextToken ct;
|
||||
gss_OID_desc oid;
|
||||
int ret;
|
||||
size_t mech_len;
|
||||
|
||||
ret = decode_GSSAPIContextToken(input_token->value, input_token->length,
|
||||
&ct, NULL);
|
||||
if (ret == 0) {
|
||||
ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1,
|
||||
sizeof(mechbuf),
|
||||
&ct.thisMech,
|
||||
&mech_len);
|
||||
}
|
||||
if (ret) {
|
||||
*minor_status = ret;
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
oid.length = (OM_uint32)mech_len;
|
||||
oid.elements = mechbuf + sizeof(mechbuf) - mech_len;
|
||||
|
||||
gss_duplicate_oid(minor_status, &oid, mech_type);
|
||||
|
||||
/*
|
||||
* The unwrapped token sits at the end and is just one byte giving the
|
||||
* remaining number of hops. The final octet of the mech encoding should
|
||||
* be just prior to it.
|
||||
*/
|
||||
assert(input_token->length >= 2);
|
||||
hops = ((uint8_t *)input_token->value)[input_token->length - 1];
|
||||
mech_last_octet = ((uint8_t *)input_token->value)[input_token->length - 2];
|
||||
|
||||
envstr = getenv("ACCEPT_FAIL");
|
||||
if (envstr != NULL && atoi(envstr) == mech_last_octet) {
|
||||
output_token->value = strdup("fail");
|
||||
assert(output_token->value != NULL);
|
||||
output_token->length = 4;
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
if (*context_handle == GSS_C_NO_CONTEXT) {
|
||||
ctx = malloc(sizeof(*ctx));
|
||||
assert(ctx != NULL);
|
||||
ctx->initiator = 0;
|
||||
ctx->hops = hops;
|
||||
*context_handle = (gss_ctx_id_t)ctx;
|
||||
} else {
|
||||
assert(!ctx->initiator);
|
||||
ctx->hops--;
|
||||
assert(ctx->hops == hops);
|
||||
}
|
||||
|
||||
if (ctx->hops > 0) {
|
||||
/* Generate a token containing the remaining hop count. */
|
||||
ctx->hops--;
|
||||
output_token->value = malloc(1);
|
||||
assert(output_token->value != NULL);
|
||||
memcpy(output_token->value, &ctx->hops, 1);
|
||||
output_token->length = 1;
|
||||
}
|
||||
|
||||
return (ctx->hops > 0) ? GSS_S_CONTINUE_NEEDED : GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV
|
||||
gss_delete_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,
|
||||
gss_buffer_t output_token)
|
||||
{
|
||||
free(*context_handle);
|
||||
*context_handle = GSS_C_NO_CONTEXT;
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
static int dummy_cred;
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV
|
||||
gss_acquire_cred(OM_uint32 *minor_status, gss_const_name_t desired_name,
|
||||
OM_uint32 time_req, const gss_OID_set desired_mechs,
|
||||
gss_cred_usage_t cred_usage,
|
||||
gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs,
|
||||
OM_uint32 *time_rec)
|
||||
{
|
||||
*minor_status = 0;
|
||||
*output_cred_handle = (gss_cred_id_t)&dummy_cred;
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV
|
||||
gss_acquire_cred_with_password(OM_uint32 *minor_status,
|
||||
gss_const_name_t desired_name,
|
||||
const gss_buffer_t password, OM_uint32 time_req,
|
||||
const gss_OID_set desired_mechs,
|
||||
gss_cred_usage_t cred_usage,
|
||||
gss_cred_id_t *output_cred_handle,
|
||||
gss_OID_set *actual_mechs, OM_uint32 *time_rec)
|
||||
{
|
||||
*minor_status = 0;
|
||||
*output_cred_handle = (gss_cred_id_t)&dummy_cred;
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV
|
||||
gss_release_cred(OM_uint32 *minor_status, gss_cred_id_t *cred_handle)
|
||||
{
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
static int dummy_name;
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV
|
||||
gss_import_name(OM_uint32 *minor_status, gss_buffer_t input_name_buffer,
|
||||
gss_OID input_name_type, gss_name_t *output_name)
|
||||
{
|
||||
/*
|
||||
* We don't need to remember anything about names, but we do need to
|
||||
* distinguish them from GSS_C_NO_NAME (to determine the direction of
|
||||
* gss_query_meta_data() and gss_exchange_meta_data()), so assign an
|
||||
* arbitrary data pointer.
|
||||
*/
|
||||
*output_name = (gss_name_t)&dummy_name;
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV
|
||||
gss_release_name(OM_uint32 *minor_status, gss_name_t *input_name)
|
||||
{
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV
|
||||
gss_display_status(OM_uint32 *minor_status, OM_uint32 status_value,
|
||||
int status_type, gss_OID mech_type,
|
||||
OM_uint32 *message_context, gss_buffer_t status_string)
|
||||
{
|
||||
if (status_type == GSS_C_MECH_CODE && status_value == 12345) {
|
||||
status_string->value = strdup("failure from acceptor");
|
||||
assert(status_string->value != NULL);
|
||||
status_string->length = strlen(status_string->value);
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
return GSS_S_BAD_STATUS;
|
||||
}
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV
|
||||
gssspi_query_meta_data(OM_uint32 *minor_status, gss_const_OID mech_oid,
|
||||
gss_cred_id_t cred_handle, gss_ctx_id_t *context_handle,
|
||||
gss_const_name_t targ_name, OM_uint32 req_flags,
|
||||
gss_buffer_t meta_data)
|
||||
{
|
||||
const char *envstr;
|
||||
uint8_t mech_last_octet;
|
||||
int initiator = (targ_name != GSS_C_NO_NAME);
|
||||
|
||||
mech_last_octet = ((uint8_t *)mech_oid->elements)[mech_oid->length - 1];
|
||||
envstr = getenv(initiator ? "INIT_QUERY_FAIL" : "ACCEPT_QUERY_FAIL");
|
||||
if (envstr != NULL && atoi(envstr) == mech_last_octet)
|
||||
return GSS_S_FAILURE;
|
||||
envstr = getenv(initiator ? "INIT_QUERY_NONE" : "ACCEPT_QUERY_NONE");
|
||||
if (envstr != NULL && atoi(envstr) == mech_last_octet)
|
||||
return GSS_S_COMPLETE;
|
||||
|
||||
meta_data->value = strdup("X");
|
||||
meta_data->length = 1;
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV
|
||||
gssspi_exchange_meta_data(OM_uint32 *minor_status, gss_const_OID mech_oid,
|
||||
gss_cred_id_t cred_handle,
|
||||
gss_ctx_id_t *context_handle,
|
||||
gss_const_name_t targ_name, OM_uint32 req_flags,
|
||||
gss_const_buffer_t meta_data)
|
||||
{
|
||||
const char *envstr;
|
||||
uint8_t mech_last_octet;
|
||||
int initiator = (targ_name != GSS_C_NO_NAME);
|
||||
|
||||
mech_last_octet = ((uint8_t *)mech_oid->elements)[mech_oid->length - 1];
|
||||
envstr = getenv(initiator ? "INIT_EXCHANGE_FAIL" : "ACCEPT_EXCHANGE_FAIL");
|
||||
if (envstr != NULL && atoi(envstr) == mech_last_octet)
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
assert(meta_data->length == 1 && memcmp(meta_data->value, "X", 1) == 0);
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV
|
||||
gssspi_query_mechanism_info(OM_uint32 *minor_status, gss_const_OID mech_oid,
|
||||
unsigned char auth_scheme[16])
|
||||
{
|
||||
/* Copy the mech OID encoding and right-pad it with zeros. */
|
||||
memset(auth_scheme, 0, 16);
|
||||
assert(mech_oid->length <= 16);
|
||||
memcpy(auth_scheme, mech_oid->elements, mech_oid->length);
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
OM_uint32 GSSAPI_CALLCONV
|
||||
gss_inquire_sec_context_by_oid(OM_uint32 *minor_status,
|
||||
gss_const_ctx_id_t context_handle,
|
||||
const gss_OID desired_object,
|
||||
gss_buffer_set_t *data_set)
|
||||
{
|
||||
struct test_context *ctx = (struct test_context *)context_handle;
|
||||
OM_uint32 major;
|
||||
uint8_t keybytes[32] = { 0 };
|
||||
uint8_t typebytes[4];
|
||||
gss_buffer_desc key, type;
|
||||
const char *envstr;
|
||||
int ask_verify;
|
||||
|
||||
if (gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_KEY))
|
||||
ask_verify = 0;
|
||||
else if (gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_VERIFY_KEY))
|
||||
ask_verify = 1;
|
||||
else
|
||||
return GSS_S_UNAVAILABLE;
|
||||
|
||||
/*
|
||||
* By default, make a key available only if the context is established.
|
||||
* This can be overridden to "always", "init-always", "accept-always",
|
||||
* or "never".
|
||||
*/
|
||||
envstr = getenv("KEY");
|
||||
if (envstr != NULL && strcmp(envstr, "never") == 0) {
|
||||
return GSS_S_UNAVAILABLE;
|
||||
} else if (ctx->hops > 0) {
|
||||
if (envstr == NULL)
|
||||
return GSS_S_UNAVAILABLE;
|
||||
else if (strcmp(envstr, "init-always") == 0 && !ctx->initiator)
|
||||
return GSS_S_UNAVAILABLE;
|
||||
else if (strcmp(envstr, "accept-always") == 0 && ctx->initiator)
|
||||
return GSS_S_UNAVAILABLE;
|
||||
}
|
||||
|
||||
/* Perturb the key so that each side's verifier key is equal to the other's
|
||||
* checksum key. */
|
||||
keybytes[0] = ask_verify ^ ctx->initiator;
|
||||
|
||||
/* Supply an all-zeros aes256-sha1 negoex key. */
|
||||
if (gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_KEY) ||
|
||||
gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_VERIFY_KEY)) {
|
||||
OM_uint32 n = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
|
||||
|
||||
typebytes[0] = (n >> 0 ) & 0xFF;
|
||||
typebytes[1] = (n >> 8 ) & 0xFF;
|
||||
typebytes[2] = (n >> 16) & 0xFF;
|
||||
typebytes[3] = (n >> 24) & 0xFF;
|
||||
|
||||
key.value = keybytes;
|
||||
key.length = sizeof(keybytes);
|
||||
type.value = typebytes;
|
||||
type.length = sizeof(typebytes);
|
||||
major = gss_add_buffer_set_member(minor_status, &key, data_set);
|
||||
if (major != GSS_S_COMPLETE)
|
||||
return major;
|
||||
return gss_add_buffer_set_member(minor_status, &type, data_set);
|
||||
}
|
||||
|
||||
return GSS_S_UNAVAILABLE;
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gss_process_context_token(OM_uint32 *minor_status,
|
||||
gss_const_ctx_id_t context_handle,
|
||||
const gss_buffer_t token_buffer)
|
||||
{
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gss_context_time(OM_uint32 *minor_status,
|
||||
gss_const_ctx_id_t context_handle,
|
||||
OM_uint32 *time_rec)
|
||||
{
|
||||
*time_rec = 0;
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
/*
|
||||
* We also need to supply a fake MIC in case SPNEGO test negotiates
|
||||
* as non-default mechanism
|
||||
*/
|
||||
#define FAKE_MIC "negoex-fake-mic"
|
||||
#define FAKE_MIC_LEN (sizeof(FAKE_MIC) - 1)
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gss_get_mic(OM_uint32 *minor_status,
|
||||
gss_const_ctx_id_t context_handle,
|
||||
gss_qop_t qop_req,
|
||||
const gss_buffer_t message_buffer,
|
||||
gss_buffer_t message_token)
|
||||
{
|
||||
message_token->value = strdup(FAKE_MIC);
|
||||
message_token->length = FAKE_MIC_LEN;
|
||||
|
||||
*minor_status = 0;
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gss_verify_mic(OM_uint32 *minor_status,
|
||||
gss_const_ctx_id_t context_handle,
|
||||
const gss_buffer_t message_buffer,
|
||||
const gss_buffer_t token_buffer,
|
||||
gss_qop_t *qop_state)
|
||||
{
|
||||
*minor_status = 0;
|
||||
if (token_buffer->length == FAKE_MIC_LEN &&
|
||||
memcmp(token_buffer->value, FAKE_MIC, FAKE_MIC_LEN) == 0)
|
||||
return GSS_S_COMPLETE;
|
||||
else
|
||||
return GSS_S_BAD_MIC;
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gss_wrap(OM_uint32 *minor_status,
|
||||
gss_const_ctx_id_t context_handle,
|
||||
int conf_req_flag,
|
||||
gss_qop_t qop_req,
|
||||
const gss_buffer_t input_message_buffer,
|
||||
int *conf_state,
|
||||
gss_buffer_t output_message_buffer)
|
||||
{
|
||||
return GSS_S_UNAVAILABLE;
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gss_unwrap(OM_uint32 *minor_status,
|
||||
gss_const_ctx_id_t context_handle,
|
||||
const gss_buffer_t input_message_buffer,
|
||||
gss_buffer_t output_message_buffer,
|
||||
int *conf_state,
|
||||
gss_qop_t *qop_state)
|
||||
{
|
||||
return GSS_S_UNAVAILABLE;
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gss_compare_name(OM_uint32 *minor_status,
|
||||
gss_const_name_t name1_arg,
|
||||
gss_const_name_t name2_arg,
|
||||
int *name_equal)
|
||||
{
|
||||
return GSS_S_UNAVAILABLE;
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gss_display_name(OM_uint32 *minor_status,
|
||||
gss_const_name_t input_name,
|
||||
gss_buffer_t output_name_buffer,
|
||||
gss_OID *output_name_type)
|
||||
{
|
||||
return GSS_S_UNAVAILABLE;
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gss_export_name(OM_uint32 *minor_status,
|
||||
gss_const_name_t input_name,
|
||||
gss_buffer_t exported_name)
|
||||
{
|
||||
return GSS_S_UNAVAILABLE;
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gss_inquire_context(OM_uint32 *minor_status,
|
||||
gss_const_ctx_id_t context_handle,
|
||||
gss_name_t *src_name,
|
||||
gss_name_t *targ_name,
|
||||
OM_uint32 *lifetime_rec,
|
||||
gss_OID *mech_type,
|
||||
OM_uint32 *ctx_flags,
|
||||
int *locally_initiated,
|
||||
int *xopen)
|
||||
{
|
||||
*lifetime_rec = GSS_C_INDEFINITE;
|
||||
return GSS_S_UNAVAILABLE;
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gss_wrap_size_limit(OM_uint32 *minor_status,
|
||||
gss_const_ctx_id_t context_handle,
|
||||
int conf_req_flag,
|
||||
gss_qop_t qop_req,
|
||||
OM_uint32 req_output_size,
|
||||
OM_uint32 *max_input_size)
|
||||
{
|
||||
return GSS_S_UNAVAILABLE;
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gss_import_sec_context(OM_uint32 *minor_status,
|
||||
const gss_buffer_t interprocess_token,
|
||||
gss_ctx_id_t *context_handle)
|
||||
{
|
||||
return GSS_S_UNAVAILABLE;
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gss_export_sec_context(OM_uint32 *minor_status,
|
||||
gss_ctx_id_t *context_handle,
|
||||
gss_buffer_t interprocess_token)
|
||||
{
|
||||
return GSS_S_UNAVAILABLE;
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gss_canonicalize_name(OM_uint32 *minor_status,
|
||||
gss_const_name_t input_name,
|
||||
const gss_OID mech_type,
|
||||
gss_name_t *output_name)
|
||||
{
|
||||
return GSS_S_UNAVAILABLE;
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gss_duplicate_name(OM_uint32 *minor_status,
|
||||
gss_const_name_t src_name,
|
||||
gss_name_t *dest_name)
|
||||
{
|
||||
return GSS_S_UNAVAILABLE;
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gss_inquire_cred(OM_uint32 *minor_status,
|
||||
gss_const_cred_id_t cred_handle,
|
||||
gss_name_t *name_ret,
|
||||
OM_uint32 *lifetime,
|
||||
gss_cred_usage_t *cred_usage,
|
||||
gss_OID_set *mechanisms)
|
||||
{
|
||||
if (name_ret)
|
||||
*name_ret = (gss_name_t)&dummy_name;
|
||||
if (lifetime)
|
||||
*lifetime = GSS_C_INDEFINITE;
|
||||
if (cred_usage)
|
||||
*cred_usage = GSS_C_BOTH;
|
||||
if (mechanisms)
|
||||
*mechanisms = GSS_C_NO_OID_SET;
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
@@ -198,6 +198,9 @@ HEIMDAL_GSS_2.0 {
|
||||
__gss_c_ma_pfs_oid_desc;
|
||||
__gss_c_ma_compress_oid_desc;
|
||||
__gss_c_ma_ctx_trans_oid_desc;
|
||||
__gss_c_ma_negoex_and_spnego_oid_desc;
|
||||
__gss_c_inq_negoex_key_oid_desc;
|
||||
__gss_c_inq_negoex_verify_key_oid_desc;
|
||||
|
||||
local:
|
||||
*;
|
||||
|
@@ -6,6 +6,9 @@ export HEIM_PIDFILE_DIR
|
||||
unset KRB5_CONFIG
|
||||
unset KRB5CCNAME
|
||||
|
||||
unset GSS_MECH_CONFIG
|
||||
unset GSSAPI_SPNEGO_NAME
|
||||
|
||||
top_builddir="@top_builddir@"
|
||||
top_srcdir="@top_srcdir@"
|
||||
EGREP="@EGREP@"
|
||||
|
@@ -2,9 +2,9 @@
|
||||
|
||||
include $(top_srcdir)/Makefile.am.common
|
||||
|
||||
noinst_DATA = krb5.conf
|
||||
noinst_DATA = krb5.conf mech
|
||||
|
||||
SCRIPT_TESTS = check-basic check-gss check-gssmask check-context check-spnego check-ntlm
|
||||
SCRIPT_TESTS = check-basic check-gss check-gssmask check-context check-spnego check-ntlm check-negoex
|
||||
|
||||
TESTS = $(SCRIPT_TESTS)
|
||||
|
||||
@@ -49,10 +49,19 @@ check-ntlm: check-ntlm.in Makefile
|
||||
chmod +x check-ntlm.tmp && \
|
||||
mv check-ntlm.tmp check-ntlm
|
||||
|
||||
check-negoex: check-negoex.in Makefile
|
||||
$(do_subst) < $(srcdir)/check-negoex.in > check-negoex.tmp && \
|
||||
chmod +x check-negoex.tmp && \
|
||||
mv check-negoex.tmp check-negoex
|
||||
|
||||
krb5.conf: krb5.conf.in Makefile
|
||||
$(do_subst) < $(srcdir)/krb5.conf.in > krb5.conf.tmp && \
|
||||
mv krb5.conf.tmp krb5.conf
|
||||
|
||||
mech: mech.in Makefile
|
||||
$(do_subst) < $(srcdir)/mech.in > mech.tmp && \
|
||||
mv mech.tmp mech
|
||||
|
||||
CLEANFILES= \
|
||||
$(TESTS) \
|
||||
foopassword \
|
||||
@@ -61,6 +70,7 @@ CLEANFILES= \
|
||||
krb5ccfile-ds \
|
||||
server.keytab \
|
||||
krb5.conf \
|
||||
mech \
|
||||
current-db* \
|
||||
*.log \
|
||||
tempfile \
|
||||
@@ -80,4 +90,5 @@ EXTRA_DIST = \
|
||||
check-ntlm.in \
|
||||
check-context.in \
|
||||
ntlm-user-file.txt \
|
||||
krb5.conf.in
|
||||
krb5.conf.in \
|
||||
mech.in
|
||||
|
@@ -61,6 +61,7 @@ gssmaestro="../../appl/gssmask/gssmaestro"
|
||||
KRB5_CONFIG="${objdir}/krb5.conf"
|
||||
export KRB5_CONFIG
|
||||
|
||||
KRB5CCNAME=${cache}
|
||||
rm -f ${keytabfile}
|
||||
rm -f current-db*
|
||||
rm -f out-*
|
||||
|
183
tests/gss/check-negoex.in
Normal file
183
tests/gss/check-negoex.in
Normal file
@@ -0,0 +1,183 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 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$
|
||||
#
|
||||
|
||||
env_setup="@env_setup@"
|
||||
srcdir="@srcdir@"
|
||||
objdir="@objdir@"
|
||||
|
||||
. ${env_setup}
|
||||
|
||||
R=TEST.H5L.SE
|
||||
|
||||
port=@port@
|
||||
|
||||
keytabfile="${objdir}/server.keytab-no"
|
||||
keytab="FILE:${keytabfile}-no"
|
||||
cache="FILE:krb5ccfile-no"
|
||||
cacheds="FILE:krb5ccfile-ds-no"
|
||||
|
||||
context="${TESTS_ENVIRONMENT} ../../lib/gssapi/test_context"
|
||||
|
||||
KRB5_CONFIG="${objdir}/krb5.conf"
|
||||
export KRB5_CONFIG
|
||||
|
||||
KRB5_KTNAME="${keytab}-no"
|
||||
export KRB5_KTNAME
|
||||
KRB5CCNAME="${cache}-no"
|
||||
export KRB5CCNAME
|
||||
unset NTLM_ACCEPTOR_CCACHE
|
||||
unset NTLM_USER_FILE
|
||||
|
||||
GSSAPI_SPNEGO_NAME=host@host.test.h5l.se
|
||||
export GSSAPI_SPNEGO_NAME
|
||||
|
||||
GSS_MECH_CONFIG="${objdir}/mech"
|
||||
export GSS_MECH_CONFIG
|
||||
|
||||
> messages.log
|
||||
|
||||
exitcode=0
|
||||
|
||||
echo "======context building for negoex"
|
||||
|
||||
for HOPS in 1 2 3 4 5
|
||||
do
|
||||
echo "test_negoex_1 $HOPS hops"
|
||||
${context} \
|
||||
--mech-type=spnego --ret-mech-type=test_negoex_1 \
|
||||
--name-type=hostbased-service \
|
||||
host@host.test.h5l.se || \
|
||||
{ exitcode=1 ; echo test failed; }
|
||||
done
|
||||
|
||||
for HOPS in 1 2 3 4 5
|
||||
do
|
||||
echo "test_negoex_1 $HOPS hops early keys"
|
||||
KEY=always ${context} \
|
||||
--mech-type=spnego --ret-mech-type=test_negoex_1 \
|
||||
--name-type=hostbased-service \
|
||||
host@host.test.h5l.se || \
|
||||
{ exitcode=1 ; echo test failed; }
|
||||
done
|
||||
|
||||
HOPS=1
|
||||
echo "test_negoex_1 no keys"
|
||||
KEY=never ${context} \
|
||||
--mech-type=spnego --ret-mech-type=test_negoex_1 \
|
||||
--name-type=hostbased-service \
|
||||
host@host.test.h5l.se 2>/dev/null && \
|
||||
{ exitcode=1 ; echo test failed; }
|
||||
|
||||
echo "test_negoex_1 no optimistic token"
|
||||
NEGOEX_NO_OPTIMISTIC_TOKEN=1 ${context} \
|
||||
--mech-type=spnego --ret-mech-type=test_negoex_1 \
|
||||
--name-type=hostbased-service \
|
||||
host@host.test.h5l.se || \
|
||||
{ exitcode=1 ; echo test failed; }
|
||||
|
||||
echo "test_negoex_1 initiator query fail, test_negoex_2 pass"
|
||||
INIT_QUERY_FAIL=102 ${context} \
|
||||
--mech-type=spnego --ret-mech-type=test_negoex_2 \
|
||||
--name-type=hostbased-service \
|
||||
host@host.test.h5l.se 2>/dev/null || \
|
||||
{ exitcode=1 ; echo test failed; }
|
||||
|
||||
echo "test_negoex_1 acceptor query fail, test_negoex_2 pass"
|
||||
ACCEPT_QUERY_FAIL=102 ${context} \
|
||||
--mech-type=spnego --ret-mech-type=test_negoex_2 \
|
||||
--name-type=hostbased-service \
|
||||
host@host.test.h5l.se 2>/dev/null || \
|
||||
{ exitcode=1 ; echo test failed; }
|
||||
|
||||
echo "test_negoex_1 acceptor exchange fail, test_negoex_2 pass"
|
||||
ACCEPT_EXCHANGE_FAIL=102 ${context} \
|
||||
--mech-type=spnego --ret-mech-type=test_negoex_2 \
|
||||
--name-type=hostbased-service \
|
||||
host@host.test.h5l.se 2>/dev/null || \
|
||||
{ exitcode=1 ; echo test failed; }
|
||||
|
||||
echo "test_negoex_1 first mech initiator exchange fail"
|
||||
INIT_EXCHANGE_FAIL=102 ${context} \
|
||||
--mech-type=spnego --ret-mech-type=test_negoex_1 \
|
||||
--name-type=hostbased-service \
|
||||
host@host.test.h5l.se 2>/dev/null && \
|
||||
{ exitcode=1 ; echo test failed; }
|
||||
|
||||
echo "test_negoex_1 first mech initiator exchange fail, two hops"
|
||||
HOPS=2 INIT_EXCHANGE_FAIL=102 ${context} \
|
||||
--mech-type=spnego --ret-mech-type=test_negoex_1 \
|
||||
--name-type=hostbased-service \
|
||||
host@host.test.h5l.se 2>/dev/null && \
|
||||
{ exitcode=1 ; echo test failed; }
|
||||
|
||||
echo "test_negoex_1 first mech initiator exchange fail, two hops, early keys"
|
||||
HOPS=2 KEY=always INIT_EXCHANGE_FAIL=102 ${context} \
|
||||
--mech-type=spnego --ret-mech-type=test_negoex_1 \
|
||||
--name-type=hostbased-service \
|
||||
host@host.test.h5l.se 2>/dev/null && \
|
||||
{ exitcode=1 ; echo test failed; }
|
||||
|
||||
echo "test_negoex_1 first mech init_sec_context fail"
|
||||
INIT_FAIL=102 ${context} \
|
||||
--mech-type=spnego --ret-mech-type=test_negoex_1 \
|
||||
--name-type=hostbased-service \
|
||||
host@host.test.h5l.se 2>/dev/null && \
|
||||
{ exitcode=1 ; echo test failed; }
|
||||
|
||||
echo "test_negoex_1 first mech accept_sec_context fail"
|
||||
HOPS=2 ACCEPT_FAIL=102 ${context} \
|
||||
--mech-type=spnego --ret-mech-type=test_negoex_1 \
|
||||
--name-type=hostbased-service \
|
||||
host@host.test.h5l.se 2>/dev/null && \
|
||||
{ exitcode=1 ; echo test failed; }
|
||||
|
||||
echo "test_negoex_1 alert from acceptor to initiator"
|
||||
HOPS=3 KEY=init-always ${context} \
|
||||
--mech-type=spnego --ret-mech-type=test_negoex_1 \
|
||||
--name-type=hostbased-service \
|
||||
host@host.test.h5l.se || \
|
||||
{ exitcode=1 ; echo test failed; }
|
||||
|
||||
echo "test_negoex_1 alert from initiator to acceptor"
|
||||
HOPS=4 KEY=accept-always ${context} \
|
||||
--mech-type=spnego --ret-mech-type=test_negoex_1 \
|
||||
--name-type=hostbased-service \
|
||||
host@host.test.h5l.se || \
|
||||
{ exitcode=1 ; echo test failed; }
|
||||
|
||||
trap "" EXIT
|
||||
|
||||
exit $exitcode
|
@@ -63,6 +63,7 @@ context="${TESTS_ENVIRONMENT} ../../lib/gssapi/test_context"
|
||||
KRB5_CONFIG="${objdir}/krb5.conf"
|
||||
export KRB5_CONFIG
|
||||
|
||||
KRB5CCNAME=${cache}
|
||||
KRB5_KTNAME="${keytab}"
|
||||
export KRB5_KTNAME
|
||||
KRB5CCNAME="${cache}"
|
||||
|
@@ -62,6 +62,7 @@ context="${TESTS_ENVIRONMENT} ../../lib/gssapi/test_context"
|
||||
KRB5_CONFIG="${objdir}/krb5.conf"
|
||||
export KRB5_CONFIG
|
||||
|
||||
KRB5CCNAME=${cache}
|
||||
KRB5_KTNAME="${keytab}"
|
||||
export KRB5_KTNAME
|
||||
KRB5CCNAME="${cache}"
|
||||
|
5
tests/gss/mech.in
Normal file
5
tests/gss/mech.in
Normal file
@@ -0,0 +1,5 @@
|
||||
#
|
||||
# Test GSS-API mechglue configuration file.
|
||||
#
|
||||
test_negoex_1 2.25.1414534758 @objdir@/../../lib/gssapi/.libs/test_negoex_mech.so
|
||||
test_negoex_2 2.25.1175737388 @objdir@/../../lib/gssapi/.libs/test_negoex_mech.so
|
Reference in New Issue
Block a user