From 00784193904880a9e7f73ff477d287cd22d4e158 Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Thu, 17 Dec 2009 04:49:27 +0000 Subject: [PATCH] Add GSS extensions to store credentials, generate random bits Merge /users/lhoward/gssextras-no-cqa to trunk. Adds gss_pseudo_random and gss_store_cred. ticket: 6597 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@23479 dc483132-0cff-0310-8789-dd5450dbe970 --- src/lib/gssapi/generic/gssapi.hin | 25 ++ src/lib/gssapi/krb5/Makefile.in | 6 + src/lib/gssapi/krb5/gssapiP_krb5.h | 18 ++ src/lib/gssapi/krb5/gssapi_err_krb5.et | 1 + src/lib/gssapi/krb5/gssapi_krb5.c | 3 +- src/lib/gssapi/krb5/prf.c | 139 ++++++++ src/lib/gssapi/krb5/store_cred.c | 188 +++++++++++ src/lib/gssapi/libgssapi_krb5.exports | 2 + src/lib/gssapi/mechglue/Makefile.in | 3 + src/lib/gssapi/mechglue/g_initialize.c | 2 + src/lib/gssapi/mechglue/g_prf.c | 84 +++++ src/lib/gssapi/mechglue/g_store_cred.c | 2 +- src/lib/gssapi/mechglue/mglueP.h | 10 + src/lib/gssapi/spnego/gssapiP_spnego.h | 11 + src/lib/gssapi/spnego/spnego_mech.c | 19 ++ src/tests/gssapi/Makefile.in | 14 +- src/tests/gssapi/t_gssexts.c | 420 +++++++++++++++++++++++++ 17 files changed, 939 insertions(+), 8 deletions(-) create mode 100644 src/lib/gssapi/krb5/prf.c create mode 100644 src/lib/gssapi/krb5/store_cred.c create mode 100644 src/lib/gssapi/mechglue/g_prf.c create mode 100644 src/tests/gssapi/t_gssexts.c diff --git a/src/lib/gssapi/generic/gssapi.hin b/src/lib/gssapi/generic/gssapi.hin index 422b4dbef..e60d04d87 100644 --- a/src/lib/gssapi/generic/gssapi.hin +++ b/src/lib/gssapi/generic/gssapi.hin @@ -780,6 +780,31 @@ gss_canonicalize_name( const gss_OID, /* mech_type */ gss_name_t *); /* output_name */ +/* RFC 4401 */ + +#define GSS_C_PRF_KEY_FULL 0 +#define GSS_C_PRF_KEY_PARTIAL 1 + +OM_uint32 KRB5_CALLCONV +gss_pseudo_random( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context */ + int, /* prf_key */ + const gss_buffer_t, /* prf_in */ + ssize_t, /* desired_output_len */ + gss_buffer_t); /* prf_out */ + +OM_uint32 KRB5_CALLCONV +gss_store_cred( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t,/* input_cred_handle */ + gss_cred_usage_t, /* input_usage */ + const gss_OID, /* desired_mech */ + OM_uint32, /* overwrite_cred */ + OM_uint32, /* default_cred */ + gss_OID_set *, /* elements_stored */ + gss_cred_usage_t *);/* cred_usage_stored */ + #if TARGET_OS_MAC # pragma pack(pop) #endif diff --git a/src/lib/gssapi/krb5/Makefile.in b/src/lib/gssapi/krb5/Makefile.in index 6dee42367..01591b2c0 100644 --- a/src/lib/gssapi/krb5/Makefile.in +++ b/src/lib/gssapi/krb5/Makefile.in @@ -68,6 +68,7 @@ SRCS = \ $(srcdir)/krb5_gss_glue.c \ $(srcdir)/lucid_context.c \ $(srcdir)/naming_exts.c \ + $(srcdir)/prf.c \ $(srcdir)/process_context_token.c \ $(srcdir)/rel_cred.c \ $(srcdir)/rel_oid.c \ @@ -78,6 +79,7 @@ SRCS = \ $(srcdir)/ser_sctx.c \ $(srcdir)/set_ccache.c \ $(srcdir)/sign.c \ + $(srcdir)/store_cred.c \ $(srcdir)/unseal.c \ $(srcdir)/util_cksum.c \ $(srcdir)/util_crypt.c \ @@ -120,6 +122,7 @@ OBJS = \ $(OUTPRE)krb5_gss_glue.$(OBJEXT) \ $(OUTPRE)lucid_context.$(OBJEXT) \ $(OUTPRE)naming_exts.$(OBJEXT) \ + $(OUTPRE)prf.$(OBJEXT) \ $(OUTPRE)process_context_token.$(OBJEXT) \ $(OUTPRE)rel_cred.$(OBJEXT) \ $(OUTPRE)rel_oid.$(OBJEXT) \ @@ -130,6 +133,7 @@ OBJS = \ $(OUTPRE)ser_sctx.$(OBJEXT) \ $(OUTPRE)set_ccache.$(OBJEXT) \ $(OUTPRE)sign.$(OBJEXT) \ + $(OUTPRE)store_cred.$(OBJEXT) \ $(OUTPRE)unseal.$(OBJEXT) \ $(OUTPRE)util_cksum.$(OBJEXT) \ $(OUTPRE)util_crypt.$(OBJEXT) \ @@ -175,6 +179,7 @@ STLIBOBJS = \ krb5_gss_glue.o \ lucid_context.o \ naming_exts.o \ + prf.o \ process_context_token.o \ rel_cred.o \ rel_oid.o \ @@ -185,6 +190,7 @@ STLIBOBJS = \ ser_sctx.o \ set_ccache.o \ sign.o \ + store_cred.o \ unseal.o \ util_cksum.o \ util_crypt.o \ diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h index 0127e8ccb..2ac4b3ae2 100644 --- a/src/lib/gssapi/krb5/gssapiP_krb5.h +++ b/src/lib/gssapi/krb5/gssapiP_krb5.h @@ -909,6 +909,24 @@ krb5_gss_release_any_name_mapping(OM_uint32 *minor_status, gss_buffer_t type_id, gss_any_t *input); +OM_uint32 +krb5_gss_pseudo_random(OM_uint32 *minor_status, + gss_ctx_id_t context, + int prf_key, + const gss_buffer_t prf_in, + ssize_t desired_output_len, + gss_buffer_t prf_out); + +OM_uint32 +krb5_gss_store_cred(OM_uint32 *minor_status, + gss_cred_id_t input_cred_handle, + gss_cred_usage_t cred_usage, + const gss_OID desired_mech, + OM_uint32 overwrite_cred, + OM_uint32 default_cred, + gss_OID_set *elements_stored, + gss_cred_usage_t *cred_usage_stored); + /* s4u_gss_glue.c */ OM_uint32 kg_compose_deleg_cred(OM_uint32 *minor_status, diff --git a/src/lib/gssapi/krb5/gssapi_err_krb5.et b/src/lib/gssapi/krb5/gssapi_err_krb5.et index c2a705c84..4cfd68a00 100644 --- a/src/lib/gssapi/krb5/gssapi_err_krb5.et +++ b/src/lib/gssapi/krb5/gssapi_err_krb5.et @@ -37,4 +37,5 @@ error_code KG_BAD_SEQ, "Sequence number in token is corrupt" error_code KG_EMPTY_CCACHE, "Credential cache is empty" error_code KG_NO_CTYPES, "Acceptor and Initiator share no checksum types" error_code KG_LUCID_VERSION, "Requested lucid context version not supported" +error_code KG_INPUT_TOO_LONG, "PRF input too long" end diff --git a/src/lib/gssapi/krb5/gssapi_krb5.c b/src/lib/gssapi/krb5/gssapi_krb5.c index 2892e7885..b68bc9a73 100644 --- a/src/lib/gssapi/krb5/gssapi_krb5.c +++ b/src/lib/gssapi/krb5/gssapi_krb5.c @@ -671,7 +671,7 @@ static struct gss_config krb5_mechanism = { krb5_gss_internal_release_oid, krb5_gss_wrap_size_limit, krb5_gss_export_name, - NULL, /* store_cred */ + krb5_gss_store_cred, krb5_gss_inquire_sec_context_by_oid, krb5_gss_inquire_cred_by_oid, krb5_gss_set_sec_context_option, @@ -693,6 +693,7 @@ static struct gss_config krb5_mechanism = { krb5_gss_export_name_composite, krb5_gss_map_name_to_any, krb5_gss_release_any_name_mapping, + krb5_gss_pseudo_random, }; diff --git a/src/lib/gssapi/krb5/prf.c b/src/lib/gssapi/krb5/prf.c new file mode 100644 index 000000000..f87c6b0c1 --- /dev/null +++ b/src/lib/gssapi/krb5/prf.c @@ -0,0 +1,139 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * lib/gssapi/krb5/prf.c + * + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + */ + +#include +#include "k5-int.h" /* for zap() */ +#include "gssapiP_krb5.h" +#include + +OM_uint32 +krb5_gss_pseudo_random(OM_uint32 *minor_status, + gss_ctx_id_t context, + int prf_key, + const gss_buffer_t prf_in, + ssize_t desired_output_len, + gss_buffer_t prf_out) +{ + krb5_error_code code; + krb5_key key = NULL; + krb5_gss_ctx_id_t ctx; + int i; + OM_uint32 minor; + size_t prflen; + krb5_data t, ns; + unsigned char *p; + + prf_out->length = 0; + prf_out->value = NULL; + + if (!kg_validate_ctx_id(context)) { + *minor_status = G_VALIDATE_FAILED; + return GSS_S_NO_CONTEXT; + } + + t.length = 0; + t.data = NULL; + + ns.length = 0; + ns.data = NULL; + + ctx = (krb5_gss_ctx_id_t)context; + + switch (prf_key) { + case GSS_C_PRF_KEY_FULL: + if (ctx->have_acceptor_subkey) { + key = ctx->acceptor_subkey; + break; + } + /* fallthrough */ + case GSS_C_PRF_KEY_PARTIAL: + key = ctx->subkey; + break; + default: + code = EINVAL; + goto cleanup; + } + + if (key == NULL) { + code = EINVAL; + goto cleanup; + } + + prf_out->value = k5alloc(desired_output_len, &code); + if (prf_out->value == NULL) { + code = KG_INPUT_TOO_LONG; + goto cleanup; + } + prf_out->length = desired_output_len; + + code = krb5_c_prf_length(ctx->k5_context, + krb5_k_key_enctype(ctx->k5_context, key), + &prflen); + if (code != 0) + goto cleanup; + + ns.length = 4 + prf_in->length; + ns.data = k5alloc(ns.length, &code); + if (ns.data == NULL) { + code = KG_INPUT_TOO_LONG; + goto cleanup; + } + + t.length = prflen; + t.data = k5alloc(t.length, &code); + if (t.data == NULL) + goto cleanup; + + memcpy(ns.data + 4, prf_in->value, prf_in->length); + i = 0; + p = (unsigned char *)prf_out->value; + while (desired_output_len > 0) { + store_32_be(i, ns.data); + + code = krb5_k_prf(ctx->k5_context, key, &ns, &t); + if (code != 0) + goto cleanup; + + memcpy(p, t.data, MIN(t.length, desired_output_len)); + + p += t.length; + desired_output_len -= t.length; + i++; + } + +cleanup: + if (code != 0) + gss_release_buffer(&minor, prf_out); + krb5_free_data_contents(ctx->k5_context, &ns); + krb5_free_data_contents(ctx->k5_context, &t); + + *minor_status = (OM_uint32)code; + return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; +} + diff --git a/src/lib/gssapi/krb5/store_cred.c b/src/lib/gssapi/krb5/store_cred.c new file mode 100644 index 000000000..a8ed6ffbb --- /dev/null +++ b/src/lib/gssapi/krb5/store_cred.c @@ -0,0 +1,188 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * lib/gssapi/krb5/store_cred.c + * + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + */ + +#include +#include "k5-int.h" /* for zap() */ +#include "gssapiP_krb5.h" +#include + +static int +has_unexpired_creds(krb5_gss_cred_id_t kcred, + const gss_OID desired_mech, + int default_cred) +{ + OM_uint32 major_status, minor; + gss_name_t cred_name; + gss_OID_set_desc desired_mechs; + gss_cred_id_t tmp_cred = GSS_C_NO_CREDENTIAL; + OM_uint32 time_rec; + + desired_mechs.count = 1; + desired_mechs.elements = (gss_OID)desired_mech; + + if (default_cred) + cred_name = GSS_C_NO_NAME; + else + cred_name = (gss_name_t)kcred->name; + + major_status = krb5_gss_acquire_cred(&minor, cred_name, 0, + &desired_mechs, GSS_C_INITIATE, + &tmp_cred, NULL, &time_rec); + + krb5_gss_release_cred(&minor, &tmp_cred); + + return (GSS_ERROR(major_status) || time_rec); +} + +static OM_uint32 +copy_initiator_creds(OM_uint32 *minor_status, + gss_cred_id_t input_cred_handle, + const gss_OID desired_mech, + OM_uint32 overwrite_cred, + OM_uint32 default_cred) +{ + OM_uint32 major_status; + krb5_error_code code; + krb5_gss_cred_id_t kcred = NULL; + krb5_context context = NULL; + krb5_ccache ccache = NULL; + + if (!default_cred) { + *minor_status = G_STORE_NON_DEFAULT_CRED_NOSUPP; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + major_status = krb5_gss_validate_cred_1(minor_status, + input_cred_handle, + context); + if (GSS_ERROR(major_status)) + goto cleanup; + + kcred = (krb5_gss_cred_id_t)input_cred_handle; + + if (kcred->ccache == NULL || kcred->proxy_cred) { + *minor_status = KG_CCACHE_NOMATCH; + major_status = GSS_S_DEFECTIVE_CREDENTIAL; + goto cleanup; + } + + if (!overwrite_cred && + has_unexpired_creds(kcred, desired_mech, default_cred)) { + major_status = GSS_S_DUPLICATE_ELEMENT; + goto cleanup; + } + + code = krb5int_cc_default(context, &ccache); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + code = krb5_cc_copy_creds(context, kcred->ccache, ccache); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + *minor_status = 0; + major_status = GSS_S_COMPLETE; + +cleanup: + if (kcred != NULL) + k5_mutex_unlock(&kcred->lock); + + krb5_cc_close(context, ccache); + krb5_free_context(context); + + return major_status; +} + +OM_uint32 +krb5_gss_store_cred(OM_uint32 *minor_status, + gss_cred_id_t input_cred_handle, + gss_cred_usage_t cred_usage, + const gss_OID desired_mech, + OM_uint32 overwrite_cred, + OM_uint32 default_cred, + gss_OID_set *elements_stored, + gss_cred_usage_t *cred_usage_stored) +{ + OM_uint32 major_status; + gss_cred_usage_t actual_usage; + OM_uint32 lifetime; + + if (input_cred_handle == GSS_C_NO_CREDENTIAL) + return GSS_S_NO_CRED; + + major_status = GSS_S_FAILURE; + + if (cred_usage == GSS_C_ACCEPT) { + *minor_status = G_STORE_ACCEPTOR_CRED_NOSUPP; + return GSS_S_FAILURE; + } else if (cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) { + *minor_status = G_BAD_USAGE; + return GSS_S_FAILURE; + } + + major_status = krb5_gss_inquire_cred(minor_status, input_cred_handle, + NULL, &lifetime, + &actual_usage, elements_stored); + if (GSS_ERROR(major_status)) + return major_status; + + if (lifetime == 0) + return GSS_S_CREDENTIALS_EXPIRED; + + if (actual_usage != GSS_C_INITIATE && actual_usage != GSS_C_BOTH) { + *minor_status = G_BAD_USAGE; + return GSS_S_FAILURE; + } + + major_status = copy_initiator_creds(minor_status, input_cred_handle, + desired_mech, overwrite_cred, + default_cred); + if (GSS_ERROR(major_status)) + return major_status; + + if (cred_usage_stored != NULL) + *cred_usage_stored = GSS_C_INITIATE; + + return GSS_S_COMPLETE; +} + diff --git a/src/lib/gssapi/libgssapi_krb5.exports b/src/lib/gssapi/libgssapi_krb5.exports index 60754df7a..e7f7b3609 100644 --- a/src/lib/gssapi/libgssapi_krb5.exports +++ b/src/lib/gssapi/libgssapi_krb5.exports @@ -68,6 +68,7 @@ gss_nt_service_name_v2 gss_nt_string_uid_name gss_nt_user_name gss_oid_to_str +gss_pseudo_random gss_process_context_token gss_release_any_name_mapping gss_release_buffer_set @@ -81,6 +82,7 @@ gss_seal gss_set_name_attribute gss_set_sec_context_option gss_sign +gss_store_cred gss_str_to_oid gss_test_oid_set_member gss_unseal diff --git a/src/lib/gssapi/mechglue/Makefile.in b/src/lib/gssapi/mechglue/Makefile.in index adb5c8adf..0858a4a98 100644 --- a/src/lib/gssapi/mechglue/Makefile.in +++ b/src/lib/gssapi/mechglue/Makefile.in @@ -43,6 +43,7 @@ SRCS = \ $(srcdir)/g_mech_invoke.c \ $(srcdir)/g_mechname.c \ $(srcdir)/g_oid_ops.c \ + $(srcdir)/g_prf.c \ $(srcdir)/g_process_context.c \ $(srcdir)/g_rel_buffer.c \ $(srcdir)/g_rel_cred.c \ @@ -96,6 +97,7 @@ OBJS = \ $(OUTPRE)g_mech_invoke.$(OBJEXT) \ $(OUTPRE)g_mechname.$(OBJEXT) \ $(OUTPRE)g_oid_ops.$(OBJEXT) \ + $(OUTPRE)g_prf.$(OBJEXT) \ $(OUTPRE)g_process_context.$(OBJEXT) \ $(OUTPRE)g_rel_buffer.$(OBJEXT) \ $(OUTPRE)g_rel_cred.$(OBJEXT) \ @@ -149,6 +151,7 @@ STLIBOBJS = \ g_mech_invoke.o \ g_mechname.o \ g_oid_ops.o \ + g_prf.o \ g_process_context.o \ g_rel_buffer.o \ g_rel_cred.o \ diff --git a/src/lib/gssapi/mechglue/g_initialize.c b/src/lib/gssapi/mechglue/g_initialize.c index 3929f761b..e01d17474 100644 --- a/src/lib/gssapi/mechglue/g_initialize.c +++ b/src/lib/gssapi/mechglue/g_initialize.c @@ -773,6 +773,8 @@ build_dynamicMech(void *dl, const gss_OID mech_type) GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_export_name_composite); GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_map_name_to_any); GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_release_any_name_mapping); + /* RFC 4401 (introduced in 1.8) */ + GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_pseudo_random); assert(mech_type != GSS_C_NO_OID); diff --git a/src/lib/gssapi/mechglue/g_prf.c b/src/lib/gssapi/mechglue/g_prf.c new file mode 100644 index 000000000..74a3a21b6 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_prf.c @@ -0,0 +1,84 @@ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + */ + +/* + * glue routine for gss_pseudo_random + */ + +#include "mglueP.h" + +OM_uint32 KRB5_CALLCONV +gss_pseudo_random (OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int prf_key, + const gss_buffer_t prf_in, + ssize_t desired_output_len, + gss_buffer_t prf_out) +{ + OM_uint32 status; + gss_union_ctx_id_t ctx; + gss_mechanism mech; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (context_handle == GSS_C_NO_CONTEXT) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT; + + if (prf_in == GSS_C_NO_BUFFER) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT; + + if (prf_out == GSS_C_NO_BUFFER) + return GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CONTEXT; + + prf_out->length = 0; + prf_out->value = NULL; + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + ctx = (gss_union_ctx_id_t) context_handle; + mech = gssint_get_mechanism (ctx->mech_type); + + if (mech != NULL) { + if (mech->gss_pseudo_random != NULL) { + status = mech->gss_pseudo_random(minor_status, + ctx->internal_ctx_id, + prf_key, + prf_in, + desired_output_len, + prf_out); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } else + status = GSS_S_UNAVAILABLE; + + return status; + } + + return GSS_S_BAD_MECH; +} diff --git a/src/lib/gssapi/mechglue/g_store_cred.c b/src/lib/gssapi/mechglue/g_store_cred.c index 1d438c4b9..3b286ecd4 100644 --- a/src/lib/gssapi/mechglue/g_store_cred.c +++ b/src/lib/gssapi/mechglue/g_store_cred.c @@ -63,7 +63,7 @@ OM_uint32 gss_store_cred(minor_status, cred_usage_stored) OM_uint32 *minor_status; -const gss_cred_id_t input_cred_handle; +gss_cred_id_t input_cred_handle; gss_cred_usage_t cred_usage; const gss_OID desired_mech; OM_uint32 overwrite_cred; diff --git a/src/lib/gssapi/mechglue/mglueP.h b/src/lib/gssapi/mechglue/mglueP.h index f35ac1447..517ca481b 100644 --- a/src/lib/gssapi/mechglue/mglueP.h +++ b/src/lib/gssapi/mechglue/mglueP.h @@ -573,6 +573,16 @@ typedef struct gss_config { gss_any_t * /* input */ /* */); + OM_uint32 (*gss_pseudo_random) + ( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context */ + int, /* prf_key */ + const gss_buffer_t, /* prf_in */ + ssize_t, /* desired_output_len */ + gss_buffer_t /* prf_out */ + /* */); + } *gss_mechanism; /* This structure MUST NOT be used by any code outside libgss */ diff --git a/src/lib/gssapi/spnego/gssapiP_spnego.h b/src/lib/gssapi/spnego/gssapiP_spnego.h index 43b004931..80c23e283 100644 --- a/src/lib/gssapi/spnego/gssapiP_spnego.h +++ b/src/lib/gssapi/spnego/gssapiP_spnego.h @@ -519,6 +519,17 @@ spnego_gss_release_any_name_mapping gss_any_t *input ); +OM_uint32 +spnego_gss_pseudo_random +( + OM_uint32 *minor_status, + gss_ctx_id_t context, + int prf_key, + const gss_buffer_t prf_in, + ssize_t desired_output_len, + gss_buffer_t prf_out +); + #ifdef __cplusplus } #endif diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c index 2aa8ad5dd..e0f53d579 100644 --- a/src/lib/gssapi/spnego/spnego_mech.c +++ b/src/lib/gssapi/spnego/spnego_mech.c @@ -268,6 +268,7 @@ static struct gss_config spnego_mechanism = spnego_gss_export_name_composite, spnego_gss_map_name_to_any, spnego_gss_release_any_name_mapping, + spnego_gss_pseudo_random, }; #ifdef _GSS_STATIC_LINK @@ -2485,6 +2486,24 @@ spnego_gss_release_any_name_mapping(OM_uint32 *minor_status, return (ret); } +OM_uint32 +spnego_gss_pseudo_random(OM_uint32 *minor_status, + gss_ctx_id_t context, + int prf_key, + const gss_buffer_t prf_in, + ssize_t desired_output_len, + gss_buffer_t prf_out) +{ + OM_uint32 ret; + ret = gss_pseudo_random(minor_status, + context, + prf_key, + prf_in, + desired_output_len, + prf_out); + return (ret); +} + /* * We will release everything but the ctx_handle so that it * can be passed back to init/accept context. This routine should diff --git a/src/tests/gssapi/Makefile.in b/src/tests/gssapi/Makefile.in index b48e5d97b..98020d449 100644 --- a/src/tests/gssapi/Makefile.in +++ b/src/tests/gssapi/Makefile.in @@ -4,19 +4,21 @@ DEFINES = -DUSE_AUTOCONF_H PROG_LIBPATH=-L$(TOPLIBD) PROG_RPATH=$(KRB5_LIBDIR) -SRCS= $(srcdir)/t_imp_name.c $(srcdir)/t_s4u.c $(srcdir)/t_namingexts.c +SRCS= $(srcdir)/t_imp_name.c $(srcdir)/t_s4u.c $(srcdir)/t_namingexts.c $(srcdir)/t_gssexts.c -OBJS= t_imp_name.o t_s4u.o t_namingexts.o +OBJS= t_imp_name.o t_s4u.o t_namingexts.o t_gssexts.o -all:: t_imp_name t_s4u t_namingexts +all:: t_imp_name t_s4u t_namingexts t_gssexts t_imp_name: t_imp_name.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(CC_LINK) -o t_imp_name t_imp_name.o $(GSS_LIBS) $(KRB5_BASE_LIBS) -t_namingexts: t_namingexts.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS) - $(CC_LINK) -o t_namingexts t_namingexts.o $(GSS_LIBS) $(KRB5_BASE_LIBS) t_s4u: t_s4u.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(CC_LINK) -o t_s4u t_s4u.o $(GSS_LIBS) $(KRB5_BASE_LIBS) +t_namingexts: t_namingexts.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o t_namingexts t_namingexts.o $(GSS_LIBS) $(KRB5_BASE_LIBS) +t_gssexts: t_gssexts.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o t_gssexts t_gssexts.o $(GSS_LIBS) $(KRB5_BASE_LIBS) clean:: - $(RM) t_imp_name t_s4u t_namingexts + $(RM) t_imp_name t_s4u t_namingexts t_gssexts diff --git a/src/tests/gssapi/t_gssexts.c b/src/tests/gssapi/t_gssexts.c new file mode 100644 index 000000000..959378af5 --- /dev/null +++ b/src/tests/gssapi/t_gssexts.c @@ -0,0 +1,420 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + */ + +#include +#include +#include + +#include + +/* + * Test program for protocol transition (S4U2Self) and constrained delegation + * (S4U2Proxy) + * + * Note: because of name canonicalization, the following tips may help + * when configuring with Active Directory: + * + * - Create a computer account FOO$ + * - Set the UPN to host/foo.domain (no suffix); this is necessary to + * be able to send an AS-REQ as this principal, otherwise you would + * need to use the canonical name (FOO$), which will cause principal + * comparison errors in gss_accept_sec_context(). + * - Add a SPN of host/foo.domain + * - Configure the computer account to support constrained delegation with + * protocol transition (Trust this computer for delegation to specified + * services only / Use any authentication protocol) + * - Add host/foo.domain to the keytab (possibly easiest to do this + * with ktadd) + * + * For S4U2Proxy to work the TGT must be forwardable too. + * + * Usage eg: + * + * kinit -k -t test.keytab -f 'host/test.win.mit.edu@WIN.MIT.EDU' + * ./t_s4u delegtest@WIN.MIT.EDU HOST/WIN-EQ7E4AA2WR8.win.mit.edu@WIN.MIT.EDU test.keytab + */ + +static gss_OID_desc spnego_mech = { 6, "\053\006\001\005\005\002" }; + +static int use_spnego = 0; + +static void displayStatus_1(m, code, type) + char *m; + OM_uint32 code; + int type; +{ + OM_uint32 maj_stat, min_stat; + gss_buffer_desc msg; + OM_uint32 msg_ctx; + + msg_ctx = 0; + while (1) { + maj_stat = gss_display_status(&min_stat, code, + type, GSS_C_NULL_OID, + &msg_ctx, &msg); + fprintf(stderr, "%s: %s\n", m, (char *)msg.value); + (void) gss_release_buffer(&min_stat, &msg); + + if (!msg_ctx) + break; + } +} + +static void displayStatus(msg, maj_stat, min_stat) + char *msg; + OM_uint32 maj_stat; + OM_uint32 min_stat; +{ + displayStatus_1(msg, maj_stat, GSS_C_GSS_CODE); + displayStatus_1(msg, min_stat, GSS_C_MECH_CODE); +} + +static OM_uint32 +displayCanonName(OM_uint32 *minor, gss_name_t name, char *tag) +{ + gss_name_t canon; + OM_uint32 major, tmp_minor; + gss_buffer_desc buf; + + major = gss_canonicalize_name(minor, name, + (gss_OID)gss_mech_krb5, &canon); + if (GSS_ERROR(major)) { + displayStatus("gss_canonicalize_name", major, *minor); + return major; + } + + major = gss_display_name(minor, canon, &buf, NULL); + if (GSS_ERROR(major)) { + displayStatus("gss_display_name", major, *minor); + gss_release_name(&tmp_minor, &canon); + return major; + } + + printf("%s:\t%s\n", tag, (char *)buf.value); + + gss_release_buffer(&tmp_minor, &buf); + gss_release_name(&tmp_minor, &canon); + + return GSS_S_COMPLETE; +} + +static OM_uint32 +displayOID(OM_uint32 *minor, gss_OID oid, char *tag) +{ + OM_uint32 major, tmp_minor; + gss_buffer_desc buf; + + major = gss_oid_to_str(minor, oid, &buf); + if (GSS_ERROR(major)) { + displayStatus("gss_oid_to_str", major, *minor); + return major; + } + + printf("%s:\t%s\n", tag, (char *)buf.value); + + gss_release_buffer(&tmp_minor, &buf); + + return GSS_S_COMPLETE; +} + +static OM_uint32 +testPrf(OM_uint32 *minor, + gss_ctx_id_t initiatorContext, + gss_ctx_id_t acceptorContext, + int flags) +{ + gss_buffer_desc constant; + OM_uint32 major, tmp_minor; + unsigned int i; + gss_buffer_desc initiatorPrf; + gss_buffer_desc acceptorPrf; + + constant.value = "gss prf test"; + constant.length = strlen((char *)constant.value); + + initiatorPrf.value = NULL; + acceptorPrf.value = NULL; + + major = gss_pseudo_random(minor, initiatorContext, flags, + &constant, 19, &initiatorPrf); + if (GSS_ERROR(major)) { + displayStatus("gss_pseudo_random", major, *minor); + return major; + } + + printf("%s\n", flags == GSS_C_PRF_KEY_FULL ? + "PRF_KEY_FULL" : "PRF_KEY_PARTIAL"); + + printf("Initiator PRF: "); + for (i = 0; i < initiatorPrf.length; i++) { + printf("%02x ", ((char *)initiatorPrf.value)[i] & 0xFF); + } + printf("\n"); + + major = gss_pseudo_random(minor, acceptorContext, flags, + &constant, 19, &acceptorPrf); + if (GSS_ERROR(major)) { + displayStatus("gss_pseudo_random", major, *minor); + gss_release_buffer(&tmp_minor, &initiatorPrf); + return major; + } + + printf("Acceptor PRF: "); + for (i = 0; i < acceptorPrf.length; i++) { + printf("%02x ", ((char *)acceptorPrf.value)[i] & 0xFF); + } + printf("\n"); + + if (acceptorPrf.length != initiatorPrf.length || + memcmp(acceptorPrf.value, initiatorPrf.value, initiatorPrf.length)) { + fprintf(stderr, "Initiator and acceptor PRF output does not match\n"); + major = GSS_S_FAILURE; + } + + gss_release_buffer(&tmp_minor, &initiatorPrf); + gss_release_buffer(&tmp_minor, &acceptorPrf); + + return major; +} + +static OM_uint32 +initAcceptSecContext(OM_uint32 *minor, + gss_cred_id_t claimant_cred_handle, + gss_cred_id_t verifier_cred_handle, + gss_cred_id_t *deleg_cred_handle) +{ + OM_uint32 major, tmp_minor; + gss_buffer_desc token, tmp; + gss_ctx_id_t initiator_context = GSS_C_NO_CONTEXT; + gss_ctx_id_t acceptor_context = GSS_C_NO_CONTEXT; + gss_name_t source_name = GSS_C_NO_NAME; + gss_name_t target_name = GSS_C_NO_NAME; + OM_uint32 time_rec; + gss_OID mech = GSS_C_NO_OID; + + token.value = NULL; + token.length = 0; + + tmp.value = NULL; + tmp.length = 0; + + *deleg_cred_handle = GSS_C_NO_CREDENTIAL; + + major = gss_inquire_cred(minor, verifier_cred_handle, + &target_name, NULL, NULL, NULL); + if (GSS_ERROR(major)) { + displayStatus("gss_inquire_cred", major, *minor); + return major; + } + + displayCanonName(minor, target_name, "Target name"); + + mech = use_spnego ? (gss_OID)&spnego_mech : (gss_OID)gss_mech_krb5; + displayOID(minor, mech, "Target mech"); + + major = gss_init_sec_context(minor, + claimant_cred_handle, + &initiator_context, + target_name, + mech, + GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG, + GSS_C_INDEFINITE, + GSS_C_NO_CHANNEL_BINDINGS, + GSS_C_NO_BUFFER, + NULL, + &token, + NULL, + &time_rec); + + if (target_name != GSS_C_NO_NAME) + (void) gss_release_name(&tmp_minor, &target_name); + + if (GSS_ERROR(major)) { + displayStatus("gss_init_sec_context", major, *minor); + return major; + } + + mech = GSS_C_NO_OID; + + major = gss_accept_sec_context(minor, + &acceptor_context, + verifier_cred_handle, + &token, + GSS_C_NO_CHANNEL_BINDINGS, + &source_name, + &mech, + &tmp, + NULL, + &time_rec, + deleg_cred_handle); + + if (GSS_ERROR(major)) + displayStatus("gss_accept_sec_context", major, *minor); + else { + testPrf(minor, initiator_context, acceptor_context, GSS_C_PRF_KEY_FULL); + testPrf(minor, initiator_context, acceptor_context, GSS_C_PRF_KEY_PARTIAL); + } + + (void) gss_release_name(&tmp_minor, &source_name); + (void) gss_delete_sec_context(&tmp_minor, &acceptor_context, NULL); + (void) gss_delete_sec_context(minor, &initiator_context, NULL); + (void) gss_release_buffer(&tmp_minor, &token); + (void) gss_release_buffer(&tmp_minor, &tmp); + (void) gss_release_oid(&tmp_minor, &mech); + + return major; +} + +int main(int argc, char *argv[]) +{ + OM_uint32 minor, major; + gss_cred_id_t impersonator_cred_handle = GSS_C_NO_CREDENTIAL; + gss_cred_id_t user_cred_handle = GSS_C_NO_CREDENTIAL; + gss_cred_id_t delegated_cred_handle = GSS_C_NO_CREDENTIAL; + gss_name_t user = GSS_C_NO_NAME, target = GSS_C_NO_NAME; + gss_OID_set_desc mechs; + gss_OID_set actual_mechs = GSS_C_NO_OID_SET; + gss_buffer_desc buf; + + if (argc < 2 || argc > 5) { + fprintf(stderr, "Usage: %s [--spnego] [user] " + "[proxy-target] [keytab]\n", argv[0]); + fprintf(stderr, " proxy-target and keytab are optional\n"); + exit(1); + } + + if (strcmp(argv[1], "--spnego") == 0) { + use_spnego++; + argc--; + argv++; + } + + buf.value = argv[1]; + buf.length = strlen((char *)buf.value); + + major = gss_import_name(&minor, &buf, + (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, + &user); + if (GSS_ERROR(major)) { + displayStatus("gss_import_name(user)", major, minor); + goto out; + } + + if (argc > 2 && strcmp(argv[2], "-")) { + buf.value = argv[2]; + buf.length = strlen((char *)buf.value); + + major = gss_import_name(&minor, &buf, + (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, + &target); + if (GSS_ERROR(major)) { + displayStatus("gss_import_name(target)", major, minor); + goto out; + } + } else { + target = GSS_C_NO_NAME; + } + + if (argc > 3) { + major = krb5_gss_register_acceptor_identity(argv[3]); + if (GSS_ERROR(major)) { + displayStatus("krb5_gss_register_acceptor_identity", + major, minor); + goto out; + } + } + + mechs.elements = use_spnego ? (gss_OID)&spnego_mech : + (gss_OID)gss_mech_krb5; + mechs.count = 1; + + /* get default cred */ + major = gss_acquire_cred(&minor, + GSS_C_NO_NAME, + GSS_C_INDEFINITE, + &mechs, + GSS_C_BOTH, + &impersonator_cred_handle, + &actual_mechs, + NULL); + if (GSS_ERROR(major)) { + displayStatus("gss_acquire_cred", major, minor); + goto out; + } + + (void) gss_release_oid_set(&minor, &actual_mechs); + + printf("Protocol transition tests follow\n"); + printf("-----------------------------------\n\n"); + + /* get S4U2Self cred */ + major = gss_acquire_cred_impersonate_name(&minor, + impersonator_cred_handle, + user, + GSS_C_INDEFINITE, + &mechs, + GSS_C_INITIATE, + &user_cred_handle, + &actual_mechs, + NULL); + if (GSS_ERROR(major)) { + displayStatus("gss_acquire_cred_impersonate_name", major, minor); + goto out; + } + + /* Try to store it in default ccache */ + major = gss_store_cred(&minor, + user_cred_handle, + GSS_C_INITIATE, + &mechs.elements[0], + 1, + 1, + NULL, + NULL); + if (GSS_ERROR(major)) { + displayStatus("gss_store_cred", major, minor); + goto out; + } + + major = initAcceptSecContext(&minor, + user_cred_handle, + impersonator_cred_handle, + &delegated_cred_handle); + if (GSS_ERROR(major)) + goto out; + + printf("\n"); + +out: + (void) gss_release_name(&minor, &user); + (void) gss_release_name(&minor, &target); + (void) gss_release_cred(&minor, &delegated_cred_handle); + (void) gss_release_cred(&minor, &impersonator_cred_handle); + (void) gss_release_cred(&minor, &user_cred_handle); + (void) gss_release_oid_set(&minor, &actual_mechs); + + return GSS_ERROR(major) ? 1 : 0; +} -- 2.26.2