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:
Luke Howard
2019-12-30 13:34:10 +11:00
parent ae5c60286a
commit 4fb6a6adc9
45 changed files with 6241 additions and 863 deletions

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -49,6 +49,7 @@ CLEANFILES = \
getarg.h \
glob.h \
gssapi.h \
gssapi_asn1.h \
gssapi_mech.h \
hdb-private.h \
hdb-protos.h \

View File

@@ -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

View File

@@ -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")

View File

@@ -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 */

View File

@@ -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_ */

View File

@@ -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 */

View File

@@ -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 */
};

View File

@@ -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

View File

@@ -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,

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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]);

View File

@@ -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);
}

View 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;
}

View 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;
}

View 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;
}

View File

@@ -56,6 +56,7 @@
#include <gssapi.h>
#include <gssapi_mech.h>
#include <gssapi_krb5.h>
#include <gssapi_spnego.h>
#include <heimqueue.h>

View File

@@ -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);

View File

@@ -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 */
};

View File

@@ -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"

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View 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"

View 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 */

File diff suppressed because it is too large Load Diff

View File

@@ -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,

View File

@@ -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"

View File

@@ -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];

View 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;
}

View File

@@ -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:
*;

View File

@@ -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@"

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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}"

View File

@@ -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
View 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