From 84f2d3910c1903f583466882fc13cc5061b2a697 Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Mon, 9 May 2011 18:41:03 +0000 Subject: [PATCH] Kernel subset Add a directory containing a "kernel subset" (context import and message functions only) of the gss-krb5 library, with a test framework to exercise the functionality and indicate when unknown dependencies creep in. ticket: 6909 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@24921 dc483132-0cff-0310-8789-dd5450dbe970 --- src/configure.in | 2 +- src/lib/gssapi/krb5/gssapiP_krb5.h | 5 + src/util/Makefile.in | 2 +- src/util/gss-kernel-lib/Makefile.in | 191 ++++++++++++ src/util/gss-kernel-lib/README | 121 ++++++++ src/util/gss-kernel-lib/deps | 104 +++++++ src/util/gss-kernel-lib/kernel_gss.c | 214 +++++++++++++ src/util/gss-kernel-lib/t_kgss.c | 38 +++ src/util/gss-kernel-lib/t_kgss.py | 30 ++ src/util/gss-kernel-lib/t_kgss_common.c | 84 +++++ src/util/gss-kernel-lib/t_kgss_common.h | 30 ++ src/util/gss-kernel-lib/t_kgss_kernel.c | 284 +++++++++++++++++ src/util/gss-kernel-lib/t_kgss_user.c | 393 ++++++++++++++++++++++++ 13 files changed, 1496 insertions(+), 2 deletions(-) create mode 100644 src/util/gss-kernel-lib/Makefile.in create mode 100644 src/util/gss-kernel-lib/README create mode 100644 src/util/gss-kernel-lib/deps create mode 100644 src/util/gss-kernel-lib/kernel_gss.c create mode 100644 src/util/gss-kernel-lib/t_kgss.c create mode 100644 src/util/gss-kernel-lib/t_kgss.py create mode 100644 src/util/gss-kernel-lib/t_kgss_common.c create mode 100644 src/util/gss-kernel-lib/t_kgss_common.h create mode 100644 src/util/gss-kernel-lib/t_kgss_kernel.c create mode 100644 src/util/gss-kernel-lib/t_kgss_user.c diff --git a/src/configure.in b/src/configure.in index bb623e556..547ff0514 100644 --- a/src/configure.in +++ b/src/configure.in @@ -1160,5 +1160,5 @@ dnl ccapi ccapi/lib ccapi/lib/unix ccapi/server ccapi/server/unix ccapi/test tests tests/resolve tests/asn.1 tests/create tests/hammer tests/verify tests/gssapi tests/dejagnu tests/threads tests/shlib tests/gss-threads tests/misc tests/mkeystash_compat - util/collected-client-lib + util/gss-kernel-lib util/collected-client-lib ) diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h index 5453fc03d..79a429d0d 100644 --- a/src/lib/gssapi/krb5/gssapiP_krb5.h +++ b/src/lib/gssapi/krb5/gssapiP_krb5.h @@ -1125,7 +1125,12 @@ krb5_gss_save_error_message(OM_uint32 minor_code, const char *format, ...) #define get_error_message krb5_gss_get_error_message #define save_error_string krb5_gss_save_error_string #define save_error_message krb5_gss_save_error_message +#ifdef KRB5_KERNEL +/* Error messages aren't needed in the kernel, so reduce dependencies. */ +#define save_error_info(x,y) +#else #define save_error_info krb5_gss_save_error_info +#endif extern void krb5_gss_delete_error_info(void *p); /* Prefix concatenated with Kerberos encryption type */ diff --git a/src/util/Makefile.in b/src/util/Makefile.in index cd9dc0e8d..ba14a605f 100644 --- a/src/util/Makefile.in +++ b/src/util/Makefile.in @@ -4,7 +4,7 @@ mydir=util # configure scripts, so hide this. ##WIN32##!if 0 SUBDIRS=support $(MAYBE_ET_@COM_ERR_VERSION@) $(MAYBE_SS_@SS_VERSION@) \ - profile send-pr + profile send-pr gss-kernel-lib ##WIN32##!endif WINSUBDIRS=windows support et profile BUILDTOP=$(REL).. diff --git a/src/util/gss-kernel-lib/Makefile.in b/src/util/gss-kernel-lib/Makefile.in new file mode 100644 index 000000000..17a68de91 --- /dev/null +++ b/src/util/gss-kernel-lib/Makefile.in @@ -0,0 +1,191 @@ +mydir=util/gss-kernel-lib +BUILDTOP=$(REL)..$(S).. + +KRB5_RUN_ENV=@KRB5_RUN_ENV@ + +DEFINES=-DKRB5_KERNEL +ALL_CFLAGS=$(CFLAGS) $(DEFS) $(DEFINES) -I. + +SHLIB_EXPDEPS = \ + $(TOPLIBD)/libk5crypto$(SHLIBEXT) \ + $(TOPLIBD)/libkrb5$(SHLIBEXT) +SHLIB_EXPLIBS=-lgssrpc -lkrb5 -lk5crypto -lcom_err $(SUPPORT_LIB) $(LIBS) + +STOBJLISTS=OBJS.ST + +SRCS= \ + kernel_gss.c \ + k5seal.c \ + k5sealiov.c \ + k5unseal.c \ + k5unsealiov.c \ + k5sealv3.c \ + k5sealv3iov.c \ + util_cksum.c \ + util_crypt.c \ + util_seqnum.c \ + util_seed.c \ + util_token.c \ + util_set.c \ + util_ordering.c + +EXTRADEPSRCS= t_kgss_common.c t_kgss_user.c t_kgss_kernel.c + +OBJS= \ + kernel_gss.o \ + k5seal.o \ + k5sealiov.o \ + k5unseal.o \ + k5unsealiov.o \ + k5sealv3.o \ + k5sealv3iov.o \ + util_cksum.o \ + util_crypt.o \ + util_seqnum.o \ + util_seed.o \ + util_token.o \ + util_set.o \ + util_ordering.o + +HEADERS= \ + gssapiP_krb5.h \ + gssapi_krb5.h \ + gssapi_err_krb5.h \ + gssapiP_generic.h \ + gssapi_generic.h \ + gssapi_err_generic.h \ + gssapi_ext.h \ + k5-int.h \ + k5-int-pkinit.h \ + k5-thread.h \ + k5-platform.h \ + k5-buf.h \ + k5-trace.h \ + k5-err.h \ + k5-plugin.h \ + k5-gmt_mktime.h \ + krb5.h \ + osconf.h \ + autoconf.h \ + port-sockets.h \ + socket-utils.h \ + krb5/preauth_plugin.h \ + krb5/authdata_plugin.h + +check-pytests:: t_kgss_user t_kgss_kernel + $(RUNPYTEST) $(srcdir)/t_kgss.py $(PYTESTFLAGS) + +libkgss.a: $(OBJS) + $(RM) $@ + $(AR) cq $@ $(OBJS) + $(RANLIB) $@ + +t_kgss_user: t_kgss_user.o t_kgss_common.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o t_kgss_user t_kgss_user.o t_kgss_common.o $(GSS_LIBS) \ + $(KRB5_BASE_LIBS) + +t_kgss_kernel: libkgss.a t_kgss_kernel.o t_kgss_common.o $(K5CRYPTO_DEPLIB) + $(CC_LINK) -o $@ t_kgss_kernel.o t_kgss_common.o libkgss.a \ + $(K5CRYPTO_LIB) + +depend:: $(SRCS) $(HEADERS) + +clean:: + $(RM) $(SRCS) $(HEADERS) libkgss.a testlog OBJS.SH + $(RM) -r krb5 testdir + $(RM) t_kgss_user.o t_kgss_kernel.o t_kgss_common.o + $(RM) t_kgss_user t_kgss_kernel + +GSS_KRB5=$(top_srcdir)/lib/gssapi/krb5 +GSS_KRB5_BUILD=$(BUILDTOP)/lib/gssapi/krb5 +GSS_GENERIC=$(top_srcdir)/lib/gssapi/generic +GSS_GENERIC_BUILD=$(BUILDTOP)/lib/gssapi/generic +INCLUDE=$(top_srcdir)/include +INCLUDE_BUILD=$(BUILDTOP)/include + +# Rules to copy sources from their real homes in the source or build tree. +# If we switch to requiring gnu make, we can use $(CP) $< $@ in these rules. +k5seal.c: $(GSS_KRB5)/k5seal.c + $(CP) $(GSS_KRB5)/k5seal.c $@ +k5sealiov.c: $(GSS_KRB5)/k5sealiov.c + $(CP) $(GSS_KRB5)/k5sealiov.c $@ +k5unseal.c: $(GSS_KRB5)/k5unseal.c + $(CP) $(GSS_KRB5)/k5unseal.c $@ +k5unsealiov.c: $(GSS_KRB5)/k5unsealiov.c + $(CP) $(GSS_KRB5)/k5unsealiov.c $@ +k5sealv3.c: $(GSS_KRB5)/k5sealv3.c + $(CP) $(GSS_KRB5)/k5sealv3.c $@ +k5sealv3iov.c: $(GSS_KRB5)/k5sealv3iov.c + $(CP) $(GSS_KRB5)/k5sealv3iov.c $@ +util_cksum.c: $(GSS_KRB5)/util_cksum.c + $(CP) $(GSS_KRB5)/util_cksum.c $@ +util_crypt.c: $(GSS_KRB5)/util_crypt.c + $(CP) $(GSS_KRB5)/util_crypt.c $@ +util_seqnum.c: $(GSS_KRB5)/util_seqnum.c + $(CP) $(GSS_KRB5)/util_seqnum.c $@ +util_seed.c: $(GSS_KRB5)/util_seed.c + $(CP) $(GSS_KRB5)/util_seed.c $@ +util_token.c: $(GSS_GENERIC)/util_token.c + $(CP) $(GSS_GENERIC)/util_token.c $@ +util_set.c: $(GSS_GENERIC)/util_set.c + $(CP) $(GSS_GENERIC)/util_set.c $@ +util_ordering.c: $(GSS_GENERIC)/util_ordering.c + $(CP) $(GSS_GENERIC)/util_ordering.c $@ + +# Rules to copy headers from their real homes in the source or build tree. +gssapiP_krb5.h: $(GSS_KRB5)/gssapiP_krb5.h + $(CP) $(GSS_KRB5)/gssapiP_krb5.h $@ +gssapi_krb5.h: $(GSS_KRB5_BUILD)/gssapi_krb5.h + $(CP) $(GSS_KRB5_BUILD)/gssapi_krb5.h $@ +gssapi_err_krb5.h: $(GSS_KRB5_BUILD)/gssapi_err_krb5.h + $(CP) $(GSS_KRB5_BUILD)/gssapi_err_krb5.h $@ +gssapiP_generic.h: $(GSS_GENERIC)/gssapiP_generic.h + $(CP) $(GSS_GENERIC)/gssapiP_generic.h $@ +gssapi_generic.h: $(GSS_GENERIC)/gssapi_generic.h + $(CP) $(GSS_GENERIC)/gssapi_generic.h $@ +gssapi_err_generic.h: $(GSS_GENERIC_BUILD)/gssapi_err_generic.h + $(CP) $(GSS_GENERIC_BUILD)/gssapi_err_generic.h $@ +gssapi_ext.h: $(GSS_GENERIC)/gssapi_ext.h + $(CP) $(GSS_GENERIC)/gssapi_ext.h $@ +k5-int.h: $(INCLUDE)/k5-int.h + $(CP) $(INCLUDE)/k5-int.h $@ +k5-int-pkinit.h: $(INCLUDE)/k5-int-pkinit.h + $(CP) $(INCLUDE)/k5-int-pkinit.h $@ +k5-thread.h: $(INCLUDE)/k5-thread.h + $(CP) $(INCLUDE)/k5-thread.h $@ +k5-platform.h: $(INCLUDE)/k5-platform.h + $(CP) $(INCLUDE)/k5-platform.h $@ +k5-buf.h: $(INCLUDE)/k5-buf.h + $(CP) $(INCLUDE)/k5-buf.h $@ +k5-trace.h: $(INCLUDE)/k5-trace.h + $(CP) $(INCLUDE)/k5-trace.h $@ +k5-err.h: $(INCLUDE)/k5-err.h + $(CP) $(INCLUDE)/k5-err.h $@ +k5-plugin.h: $(INCLUDE)/k5-plugin.h + $(CP) $(INCLUDE)/k5-plugin.h $@ +k5-gmt_mktime.h: $(INCLUDE)/k5-gmt_mktime.h + $(CP) $(INCLUDE)/k5-gmt_mktime.h $@ +krb5.h: $(INCLUDE_BUILD)/krb5/krb5.h + $(CP) $(INCLUDE_BUILD)/krb5/krb5.h $@ +osconf.h: $(INCLUDE_BUILD)/osconf.h + $(CP) $(INCLUDE_BUILD)/osconf.h $@ +autoconf.h: $(INCLUDE_BUILD)/autoconf.h + $(CP) $(INCLUDE_BUILD)/autoconf.h $@ +port-sockets.h: $(INCLUDE)/port-sockets.h + $(CP) $(INCLUDE)/port-sockets.h $@ +socket-utils.h: $(INCLUDE)/socket-utils.h + $(CP) $(INCLUDE)/socket-utils.h $@ +krb5/preauth_plugin.h: krb5 $(INCLUDE)/krb5/preauth_plugin.h + $(CP) $(INCLUDE)/krb5/preauth_plugin.h krb5 +krb5/authdata_plugin.h: krb5 $(INCLUDE)/krb5/authdata_plugin.h + $(CP) $(INCLUDE)/krb5/authdata_plugin.h krb5 + +krb5: + test -d krb5 || mkdir krb5 + +LIBBASE=kgss +LIBMAJOR=1 +LIBMINOR=0 + +LIBINITFUNC= +LIBFINIFUNC= diff --git a/src/util/gss-kernel-lib/README b/src/util/gss-kernel-lib/README new file mode 100644 index 000000000..b2adf2b4f --- /dev/null +++ b/src/util/gss-kernel-lib/README @@ -0,0 +1,121 @@ +This directory is intended to help integrators of MIT krb5 code into +the kernel by: + +1. Identifying the GSSAPI source files necessary for wrapping and +unwrapping messages. + +2. Providing a test framework to ensuring that these source files do +not grow addtional dependencies without alerting the developers. + +3. Providing code for importing a Lucid sec context. + +Nothing is built in this directory during "make all". The following +happens durng "make check": + +1. Sources and headers are copied here from other parts of the tree. + +2. Sources are compiled and built, together with some additional code +in kernel_gss.c, into a static library named libkgss.a. Sources are +built with -DKRB5_KERNEL, which is used (very sparingly) to eliminate +dependencies such as the code to save error messages. + +3. A test program is built in two parts: t_kgss_user is built against +the regular ("user-space") GSSAPI libraries, and t_kgss_kernel is +built against libkgss.a. + +4. A Python test executes t_kgss_user, which runs t_kgss_kernel in a +child process and exercises the functionality of libkgss.a. + +Limitations +----------- + +Lucid contexts are used to transport the acceptor context from +user-space to kernel-space, because the code overhead of normal +export/import is large (it requires the libkrb5 serialization +framework). Kernel integrators should be aware of two issues with +Lucid contexts: + +1. They are not a flat data blob. It is up to the user/kernelspace +interface to define a format for transporting the lucid context +structure. + +2. Lucid contexts do not convey the do-replay or do-sequence flags +from the original context. RPC security does not need replay or +sequence detection, so the krb5_gss_import_lucid_sec_context +implementation in kernel_gss.c simply assumes the flags should be +turned off. If the kernel GSS code is being used for a protocol which +does need replay or sequence detection, those flags should be +determined separately and set in the krb5 GSS context. + +Crypto library +-------------- + +libkgss.a does not include crypto code. Almost all of the crypto +library is required for a kernel integration, so it would not be +productive to duplicate almost all of the crypto build infrastructure +to demonstrate the kernel subset. + +A kernel integrator will almost certainly want to use the kernel's +native PRNG instead of the default lib/crypto/krb/prng_fortuna.c, and +may also wish to write a back end module implementing standard crypto +primitives in terms of the kernel's crypto primitives, instead of +using lib/crypto/builtin. + +A few pieces of crypto functionality can be omitted from a kernel +subset. String-to-key is not needed, and consequently neither is +PBKDF2. PRF is not needed, unless the integrator is adding +krb5_gss_pseudo_random to the subset. The enctype utility APIs are +not needed. DES and DES3 keys are only used via raw enctypes, so the +functions in enc_old.c won't be reached. Because of the way the +crypto library uses vtables internally, removing the unreached code is +not simply a matter of selecting source files, and it may be simpler +to just leave the small amount of unreached code in. + +A complete inventory of crypto APIs used by the kernel subset can be +made with: + + nm libkgss.a | awk '/U .*_[ck]_/ {print $2}' | sort -u + +Currently, that list is: + + krb5_c_block_size + krb5_c_checksum_length + krb5_c_crypto_length + krb5_c_make_checksum + krb5_c_padding_length + krb5_c_random_make_octets + krb5int_c_free_keyblock + krb5int_c_mandatory_cksumtype + krb5_k_create_key + krb5_k_decrypt + krb5_k_decrypt_iov + krb5_k_encrypt + krb5_k_encrypt_iov + krb5_k_free_key + krb5_k_key_keyblock + krb5_k_make_checksum + krb5_k_make_checksum_iov + krb5_k_verify_checksum + krb5_k_verify_checksum_iov + +Debugging test failures +----------------------- + +If an error occurs in t_kgss_user, it can be debugged in the same way +as any program running under the Python test framework. Start by +re-running the Python script with the -v flag, then add a --debug +option for the failing command, then set breakpoints or step through +the process execution as necessary. + +If an error occurs in t_kgss_kernel, it is harder to debug, since +t_kgss_user runs it as a subprocess. On Linux with gdb, it is +possible to interactively debug t_kgss_kernel by starting an +interactive gdb session for t_kgss_user and doing: + + set follow-fork-mode child + break main + run + cont + +You should get a breakpoint in the main() of t_kgss_kernel and should +be able to set breakpoints from there. diff --git a/src/util/gss-kernel-lib/deps b/src/util/gss-kernel-lib/deps new file mode 100644 index 000000000..08a5d8aeb --- /dev/null +++ b/src/util/gss-kernel-lib/deps @@ -0,0 +1,104 @@ +# +# Generated makefile dependencies follow. +# +$(OUTPRE)kernel_gss.$(OBJEXT): autoconf.h gssapiP_generic.h \ + gssapiP_krb5.h gssapi_err_generic.h gssapi_err_krb5.h \ + gssapi_ext.h gssapi_generic.h gssapi_krb5.h k5-buf.h \ + k5-err.h k5-gmt_mktime.h k5-int-pkinit.h k5-int.h k5-platform.h \ + k5-plugin.h k5-thread.h k5-trace.h kernel_gss.c krb5.h \ + krb5/authdata_plugin.h krb5/preauth_plugin.h osconf.h \ + port-sockets.h socket-utils.h +$(OUTPRE)k5seal.$(OBJEXT): autoconf.h gssapiP_generic.h \ + gssapiP_krb5.h gssapi_err_generic.h gssapi_err_krb5.h \ + gssapi_ext.h gssapi_generic.h gssapi_krb5.h k5-buf.h \ + k5-err.h k5-gmt_mktime.h k5-int-pkinit.h k5-int.h k5-platform.h \ + k5-plugin.h k5-thread.h k5-trace.h k5seal.c krb5.h \ + krb5/authdata_plugin.h krb5/preauth_plugin.h osconf.h \ + port-sockets.h socket-utils.h +$(OUTPRE)k5sealiov.$(OBJEXT): autoconf.h gssapiP_generic.h \ + gssapiP_krb5.h gssapi_err_generic.h gssapi_err_krb5.h \ + gssapi_ext.h gssapi_generic.h gssapi_krb5.h k5-buf.h \ + k5-err.h k5-gmt_mktime.h k5-int-pkinit.h k5-int.h k5-platform.h \ + k5-plugin.h k5-thread.h k5-trace.h k5sealiov.c krb5.h \ + krb5/authdata_plugin.h krb5/preauth_plugin.h osconf.h \ + port-sockets.h socket-utils.h +$(OUTPRE)k5unseal.$(OBJEXT): autoconf.h gssapiP_generic.h \ + gssapiP_krb5.h gssapi_err_generic.h gssapi_err_krb5.h \ + gssapi_ext.h gssapi_generic.h gssapi_krb5.h k5-buf.h \ + k5-err.h k5-gmt_mktime.h k5-int-pkinit.h k5-int.h k5-platform.h \ + k5-plugin.h k5-thread.h k5-trace.h k5unseal.c krb5.h \ + krb5/authdata_plugin.h krb5/preauth_plugin.h osconf.h \ + port-sockets.h socket-utils.h +$(OUTPRE)k5unsealiov.$(OBJEXT): autoconf.h gssapiP_generic.h \ + gssapiP_krb5.h gssapi_err_generic.h gssapi_err_krb5.h \ + gssapi_ext.h gssapi_generic.h gssapi_krb5.h k5-buf.h \ + k5-err.h k5-gmt_mktime.h k5-int-pkinit.h k5-int.h k5-platform.h \ + k5-plugin.h k5-thread.h k5-trace.h k5unsealiov.c krb5.h \ + krb5/authdata_plugin.h krb5/preauth_plugin.h osconf.h \ + port-sockets.h socket-utils.h +$(OUTPRE)k5sealv3.$(OBJEXT): autoconf.h gssapiP_generic.h \ + gssapiP_krb5.h gssapi_err_generic.h gssapi_err_krb5.h \ + gssapi_ext.h gssapi_generic.h gssapi_krb5.h k5-buf.h \ + k5-err.h k5-gmt_mktime.h k5-int-pkinit.h k5-int.h k5-platform.h \ + k5-plugin.h k5-thread.h k5-trace.h k5sealv3.c krb5.h \ + krb5/authdata_plugin.h krb5/preauth_plugin.h osconf.h \ + port-sockets.h socket-utils.h +$(OUTPRE)k5sealv3iov.$(OBJEXT): autoconf.h gssapiP_generic.h \ + gssapiP_krb5.h gssapi_err_generic.h gssapi_err_krb5.h \ + gssapi_ext.h gssapi_generic.h gssapi_krb5.h k5-buf.h \ + k5-err.h k5-gmt_mktime.h k5-int-pkinit.h k5-int.h k5-platform.h \ + k5-plugin.h k5-thread.h k5-trace.h k5sealv3iov.c krb5.h \ + krb5/authdata_plugin.h krb5/preauth_plugin.h osconf.h \ + port-sockets.h socket-utils.h +$(OUTPRE)util_cksum.$(OBJEXT): autoconf.h gssapiP_generic.h \ + gssapiP_krb5.h gssapi_err_generic.h gssapi_err_krb5.h \ + gssapi_ext.h gssapi_generic.h gssapi_krb5.h k5-buf.h \ + k5-err.h k5-gmt_mktime.h k5-int-pkinit.h k5-int.h k5-platform.h \ + k5-plugin.h k5-thread.h k5-trace.h krb5.h krb5/authdata_plugin.h \ + krb5/preauth_plugin.h osconf.h port-sockets.h socket-utils.h \ + util_cksum.c +$(OUTPRE)util_crypt.$(OBJEXT): autoconf.h gssapiP_generic.h \ + gssapiP_krb5.h gssapi_err_generic.h gssapi_err_krb5.h \ + gssapi_ext.h gssapi_generic.h gssapi_krb5.h k5-buf.h \ + k5-err.h k5-gmt_mktime.h k5-int-pkinit.h k5-int.h k5-platform.h \ + k5-plugin.h k5-thread.h k5-trace.h krb5.h krb5/authdata_plugin.h \ + krb5/preauth_plugin.h osconf.h port-sockets.h socket-utils.h \ + util_crypt.c +$(OUTPRE)util_seqnum.$(OBJEXT): autoconf.h gssapiP_generic.h \ + gssapiP_krb5.h gssapi_err_generic.h gssapi_err_krb5.h \ + gssapi_ext.h gssapi_generic.h gssapi_krb5.h k5-buf.h \ + k5-err.h k5-gmt_mktime.h k5-int-pkinit.h k5-int.h k5-platform.h \ + k5-plugin.h k5-thread.h k5-trace.h krb5.h krb5/authdata_plugin.h \ + krb5/preauth_plugin.h osconf.h port-sockets.h socket-utils.h \ + util_seqnum.c +$(OUTPRE)util_seed.$(OBJEXT): autoconf.h gssapiP_generic.h \ + gssapiP_krb5.h gssapi_err_generic.h gssapi_err_krb5.h \ + gssapi_ext.h gssapi_generic.h gssapi_krb5.h k5-buf.h \ + k5-err.h k5-gmt_mktime.h k5-int-pkinit.h k5-int.h k5-platform.h \ + k5-plugin.h k5-thread.h k5-trace.h krb5.h krb5/authdata_plugin.h \ + krb5/preauth_plugin.h osconf.h port-sockets.h socket-utils.h \ + util_seed.c +$(OUTPRE)util_token.$(OBJEXT): autoconf.h gssapiP_generic.h \ + gssapi_err_generic.h gssapi_ext.h gssapi_generic.h \ + k5-buf.h k5-platform.h k5-thread.h util_token.c +$(OUTPRE)util_set.$(OBJEXT): autoconf.h gssapiP_generic.h \ + gssapi_err_generic.h gssapi_ext.h gssapi_generic.h \ + k5-buf.h k5-platform.h k5-thread.h util_set.c +$(OUTPRE)util_ordering.$(OBJEXT): autoconf.h gssapiP_generic.h \ + gssapi_err_generic.h gssapi_ext.h gssapi_generic.h \ + k5-buf.h k5-platform.h k5-thread.h util_ordering.c +$(OUTPRE)t_kgss_common.$(OBJEXT): autoconf.h k5-buf.h \ + k5-err.h k5-gmt_mktime.h k5-int-pkinit.h k5-int.h k5-platform.h \ + k5-plugin.h k5-thread.h k5-trace.h krb5.h krb5/authdata_plugin.h \ + krb5/preauth_plugin.h osconf.h port-sockets.h socket-utils.h \ + t_kgss_common.c +$(OUTPRE)t_kgss_user.$(OBJEXT): autoconf.h k5-buf.h \ + k5-err.h k5-gmt_mktime.h k5-int-pkinit.h k5-int.h k5-platform.h \ + k5-plugin.h k5-thread.h k5-trace.h krb5.h krb5/authdata_plugin.h \ + krb5/preauth_plugin.h osconf.h port-sockets.h socket-utils.h \ + t_kgss_common.h t_kgss_user.c +$(OUTPRE)t_kgss_kernel.$(OBJEXT): autoconf.h gssapi_krb5.h \ + k5-buf.h k5-err.h k5-gmt_mktime.h k5-int-pkinit.h k5-int.h \ + k5-platform.h k5-plugin.h k5-thread.h k5-trace.h krb5.h \ + krb5/authdata_plugin.h krb5/preauth_plugin.h osconf.h \ + port-sockets.h socket-utils.h t_kgss_kernel.c diff --git a/src/util/gss-kernel-lib/kernel_gss.c b/src/util/gss-kernel-lib/kernel_gss.c new file mode 100644 index 000000000..9c3ac77ab --- /dev/null +++ b/src/util/gss-kernel-lib/kernel_gss.c @@ -0,0 +1,214 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* util/gss-kernel-lib/gss_kernel.c - Extra pieces for GSS kernel library */ +/* + * Copyright (C) 2011 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. + */ + +/* + * This file includes a few symbols cherry-picked from larger files, as well as + * a function to import a lucid sec context. + */ + +#include "gssapiP_krb5.h" + +/* Normally defined in lib/gssapi/krb5/gssapi_krb5.c. */ +static const gss_OID_desc oid_array[] = { + {GSS_MECH_KRB5_OID_LENGTH, GSS_MECH_KRB5_OID}, + {GSS_MECH_KRB5_OLD_OID_LENGTH, GSS_MECH_KRB5_OLD_OID} +}; +const gss_OID_desc *const gss_mech_krb5 = oid_array+0; +const gss_OID_desc *const gss_mech_krb5_old = oid_array+1; + +/* Create a key from key data in a lucid context. */ +static krb5_error_code +lkey_to_key(const gss_krb5_lucid_key_t *lkey, krb5_key *key_out) +{ + krb5_keyblock kb; + krb5_key key; + + kb.enctype = lkey->type; + kb.length = lkey->length; + kb.contents = lkey->data; + return krb5_k_create_key(NULL, &kb, key_out); +} + +/* Get the RFC3961 mandator cksumtype for key. */ +static inline krb5_error_code +get_cksumtype(krb5_key key, krb5_cksumtype *out) +{ + return krb5int_c_mandatory_cksumtype(NULL, key->keyblock.enctype, out); +} + +/* Import a lucid context structure, creating a krb5 GSS context structure + * sufficient for use by by wrap/unwrap/get_mic/verify_mic operations. */ +static krb5_error_code +import_lucid_sec_context_v1(const gss_krb5_lucid_context_v1_t *lctx, + gss_ctx_id_t *context_handle_out) +{ + krb5_error_code ret; + krb5_gss_ctx_id_t gctx; + OM_uint32 tmpmin; + krb5_key key = NULL; + + gctx = k5alloc(sizeof(*gctx), &ret); + if (gctx == NULL) + return ret; + + gctx->initiate = lctx->initiate; + gctx->krb_times.endtime = lctx->endtime; + gctx->seq_send = lctx->send_seq; + gctx->seq_recv = lctx->recv_seq; + gctx->proto = lctx->protocol; + if (lctx->protocol == 0) { + /* Ignore sign_alg and seal_alg since they follow from the enctype. */ + ret = lkey_to_key(&lctx->rfc1964_kd.ctx_key, &key); + if (ret) + goto cleanup; + /* For raw enctypes, choose an enctype expected by kg_setup_keys. */ + if (key->keyblock.enctype == ENCTYPE_DES_CBC_RAW) + key->keyblock.enctype = ENCTYPE_DES_CBC_CRC; + else if (key->keyblock.enctype == ENCTYPE_DES3_CBC_RAW) + key->keyblock.enctype = ENCTYPE_DES3_CBC_SHA1; + ret = kg_setup_keys(NULL, gctx, key, &gctx->cksumtype); + if (ret) + goto cleanup; + if (gctx->proto != 0) { /* ctx_key did not have a pre-CFX enctype. */ + ret = EINVAL; + goto cleanup; + } + } else if (lctx->protocol == 1) { + ret = lkey_to_key(&lctx->cfx_kd.ctx_key, &gctx->subkey); + if (ret) + goto cleanup; + ret = get_cksumtype(gctx->subkey, &gctx->cksumtype); + if (ret) + goto cleanup; + if (lctx->cfx_kd.have_acceptor_subkey) { + gctx->have_acceptor_subkey = 1; + ret = lkey_to_key(&lctx->cfx_kd.acceptor_subkey, + &gctx->acceptor_subkey); + if (ret) + goto cleanup; + ret = get_cksumtype(gctx->acceptor_subkey, + &gctx->acceptor_subkey_cksumtype); + if (ret) + goto cleanup; + } + } + + /* Assume the proper krb5 mech and no big-endian compatibility. */ + gctx->big_endian = 0; + gctx->seed_init = 0; + gctx->established = 1; + gctx->mech_used = (gss_OID_desc *)gss_mech_krb5; + + /* + * The lucid context doesn't convey the gss_flags which indicate whether + * the protocol needs replay or sequence protection. Assume we don't + * (because RPCSEC_GSS doesn't). + */ + g_order_init(&gctx->seqstate, gctx->seq_recv, 0, 0, gctx->proto); + + *context_handle_out = (gss_ctx_id_t)gctx; + gctx = NULL; + +cleanup: + krb5_k_free_key(NULL, key); + krb5_gss_delete_sec_context(&tmpmin, (gss_ctx_id_t *)&gctx, NULL); + return ret; +} + +OM_uint32 +krb5_gss_import_lucid_sec_context(OM_uint32 *minor_status, void *lctx, + gss_ctx_id_t *context_handle_out) +{ + OM_uint32 vers = ((gss_krb5_lucid_context_version_t *)lctx)->version; + krb5_error_code ret; + + if (vers == 1) + ret = import_lucid_sec_context_v1((gss_krb5_lucid_context_v1_t *)lctx, + context_handle_out); + else + ret = KG_LUCID_VERSION; + *minor_status = ret; + return (ret == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; +} + +/* + * Normally defined in lib/gssapi/krb5/delete_sec_context.c; this version + * is tailored for imported lucid contexts and has fewer dependencies. + * Does not handle output tokens. + */ +OM_uint32 +krb5_gss_delete_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token) +{ + krb5_gss_ctx_id_t ctx; + + if (output_token) { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + + *minor_status = 0; + if (*context_handle == GSS_C_NO_CONTEXT) + return GSS_S_COMPLETE; + + ctx = (krb5_gss_ctx_id_t)*context_handle; + g_order_free(&ctx->seqstate); + krb5_k_free_key(NULL, ctx->enc); + krb5_k_free_key(NULL, ctx->seq); + krb5_k_free_key(NULL, ctx->subkey); + krb5_k_free_key(NULL, ctx->acceptor_subkey); + memset(ctx, 0, sizeof(*ctx)); + free(ctx); + *context_handle = GSS_C_NO_CONTEXT; + return GSS_S_COMPLETE; +} + +/* Normally defined in lib/krb5/krb/kfree.c. */ + +void KRB5_CALLCONV +krb5_free_checksum_contents(krb5_context context, register krb5_checksum *val) +{ + if (val == NULL) + return; + free(val->contents); + val->contents = NULL; +} + +void KRB5_CALLCONV +krb5_free_keyblock(krb5_context context, register krb5_keyblock *val) +{ + krb5int_c_free_keyblock (context, val); +} + +void KRB5_CALLCONV +krb5_free_data(krb5_context context, krb5_data *val) +{ + if (val == NULL) + return; + free(val->data); + free(val); +} diff --git a/src/util/gss-kernel-lib/t_kgss.c b/src/util/gss-kernel-lib/t_kgss.c new file mode 100644 index 000000000..623be1217 --- /dev/null +++ b/src/util/gss-kernel-lib/t_kgss.c @@ -0,0 +1,38 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* util/gss-kernel-lib/t_kgss.c - Kernel GSS library test program */ +/* + * Copyright (C) 2011 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. + */ + +int +main(int argc, char **argv) +{ + krb5_gss_wrap_iov(); + krb5_gss_wrap_iov_length(); + krb5_gss_wrap(); + krb5_gss_unwrap(); + krb5_gss_unwrap_iov(); + krb5_gss_get_mic(); + krb5_gss_verify_mic(); + return 0; +} diff --git a/src/util/gss-kernel-lib/t_kgss.py b/src/util/gss-kernel-lib/t_kgss.py new file mode 100644 index 000000000..86cd340a1 --- /dev/null +++ b/src/util/gss-kernel-lib/t_kgss.py @@ -0,0 +1,30 @@ +# Copyright (C) 2011 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. + +#!/usr/bin/python +from k5test import * + +# Test krb5 negotiation under SPNEGO for all enctype configurations. +for realm in multipass_realms(): + realm.run_as_client(['./t_kgss_user', realm.host_princ]) + +success('Kernel GSSAPI subset tests.') diff --git a/src/util/gss-kernel-lib/t_kgss_common.c b/src/util/gss-kernel-lib/t_kgss_common.c new file mode 100644 index 000000000..1b31634c2 --- /dev/null +++ b/src/util/gss-kernel-lib/t_kgss_common.c @@ -0,0 +1,84 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* util/gss-kernel-lib/t_kgss_common.c - Common functions for tests */ +/* + * Copyright (C) 2011 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 "k5-int.h" +#include +#include + +/* Write len bytes of data to fd, aborting on failure. */ +void +rewrite(int fd, const void *data, size_t len) +{ + ssize_t r; + + while (len > 0) { + r = write(fd, data, len); + if (r == -1 && errno == EINTR) + continue; + assert(r > 0); + data = (char *)data +r; + len -= r; + } +} + +/* Read len bytes into buf from fd, aborting on failure. */ +void +reread(int fd, void *buf, size_t len) +{ + ssize_t r; + + while (len > 0) { + r = read(fd, buf, len); + if (r == -1 && errno == EINTR) + continue; + assert(r > 0); + buf = (char *)buf + r; + len -= r; + } +} + +/* Send a data packet to fd using a machine-dependent length/value encoding. */ +void +send_data(int fd, const void *data, size_t len) +{ + rewrite(fd, &len, sizeof(len)); + rewrite(fd, data, len); +} + +/* Read a packet from fd into an allocated buffer. */ +void +read_data(int fd, void **data_out, size_t *len_out) +{ + size_t len; + void *data; + + reread(fd, &len, sizeof(len)); + data = malloc(len); + assert(data != NULL); + reread(fd, data, len); + *data_out = data; + *len_out = len; +} diff --git a/src/util/gss-kernel-lib/t_kgss_common.h b/src/util/gss-kernel-lib/t_kgss_common.h new file mode 100644 index 000000000..8225bef28 --- /dev/null +++ b/src/util/gss-kernel-lib/t_kgss_common.h @@ -0,0 +1,30 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* util/gss-kernel-lib/t_kgss_common.h - Common declarations for tests */ +/* + * Copyright (C) 2011 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. + */ + +void rewrite(int fd, const void *data, size_t len); +void reread(int fd, void *buf, size_t len); +void send_data(int fd, const void *data, size_t len); +void read_data(int fd, void **data_out, size_t *len_out); diff --git a/src/util/gss-kernel-lib/t_kgss_kernel.c b/src/util/gss-kernel-lib/t_kgss_kernel.c new file mode 100644 index 000000000..0d70f1f38 --- /dev/null +++ b/src/util/gss-kernel-lib/t_kgss_kernel.c @@ -0,0 +1,284 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* util/gss-kernel-lib/t_kgss_kernel.c - Kernel portion of test program */ +/* + * Copyright (C) 2011 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. + */ + +/* + * This program links against libkgss.a and is run as a child process of + * t_kgss_user. It receives an exported acceptor context from its parent and + * then exchanges wrap, MIC, and IOV tokens with the parent. + */ + +#include "k5-int.h" +#include +#include "gssapi_krb5.h" + +/* If major represents an error, display an error message and exit. */ +static void +check(OM_uint32 major, OM_uint32 minor, const char *fn) +{ + if (!GSS_ERROR(major)) + return; + fprintf(stderr, "t_kgss_kernel: %s: major %u, minor %u\n", fn, major, + minor); + /* libkgss doesn't have gss_display_status. */ + exit(1); +} + +#define READ(p, f) (memcpy(&f, p, sizeof(f)), p += sizeof(f)) + +/* Read fields from p into lkey and return the updated pointer. */ +static const unsigned char * +read_lucid_key(const unsigned char *p, gss_krb5_lucid_key_t *lkey) +{ + READ(p, lkey->type); + READ(p, lkey->length); + lkey->data = malloc(lkey->length); + assert(lkey->data != NULL); + memcpy(lkey->data, p, lkey->length); + return p + lkey->length; +} + +/* Read a data packet from stdin, unmarshal it into a lucid context, and import + * the lucid context into a GSS-krb5 acceptor context. */ +static void +read_lucid_context(gss_ctx_id_t *ctx_out) +{ + void *data; + size_t len; + const unsigned char *p; + gss_krb5_lucid_context_v1_t lctx; + OM_uint32 major, minor; + + /* No length checking; totally unsafe outside of this test program. */ + read_data(STDIN_FILENO, &data, &len); + p = data; + READ(p, lctx.version); + READ(p, lctx.initiate); + READ(p, lctx.endtime); + READ(p, lctx.send_seq); + READ(p, lctx.recv_seq); + READ(p, lctx.protocol); + if (lctx.protocol == 0) { + READ(p, lctx.rfc1964_kd.sign_alg); + READ(p, lctx.rfc1964_kd.seal_alg); + p = read_lucid_key(p, &lctx.rfc1964_kd.ctx_key); + } else if (lctx.protocol == 1) { + READ(p, lctx.cfx_kd.have_acceptor_subkey); + p = read_lucid_key(p, &lctx.cfx_kd.ctx_key); + if (lctx.cfx_kd.have_acceptor_subkey) + p = read_lucid_key(p, &lctx.cfx_kd.acceptor_subkey); + } else + abort(); + + major = krb5_gss_import_lucid_sec_context(&minor, &lctx, ctx_out); + check(major, minor, "krb5_gss_import_lucid_sec_context"); +} + +/* Read a wrap token from stdin and verify that it says "userwrap". */ +static void +read_wrap_token(gss_ctx_id_t ctx) +{ + OM_uint32 major, minor; + unsigned char *data; + size_t len; + gss_buffer_desc wrapped, buf; + + read_data(STDIN_FILENO, &wrapped.value, &wrapped.length); + major = krb5_gss_unwrap(&minor, ctx, &wrapped, &buf, NULL, NULL); + check(major, minor, "krb5_gss_unwrap"); + assert(buf.length == 8 && memcmp(buf.value, "userwrap", 8) == 0); + free(buf.value); + free(wrapped.value); +} + +/* Read a MIC token from stdin and verify that it is for "usermic". */ +static void +read_mic_token(gss_ctx_id_t ctx) +{ + OM_uint32 major, minor; + unsigned char *data; + size_t len; + gss_buffer_desc mic, buf; + + read_data(STDIN_FILENO, &mic.value, &mic.length); + buf.value = "usermic"; + buf.length = 7; + major = krb5_gss_verify_mic(&minor, ctx, &buf, &mic, NULL); + check(major, minor, "krb5_gss_verify_mic"); + free(mic.value); +} + +/* Read an IOV token from stdin and verify that it is for "userwrapmic" with + * only the "wrap" part wrapped. */ +static void +read_iov_token(gss_ctx_id_t ctx) +{ + OM_uint32 major, minor; + gss_iov_buffer_desc iov[6]; + + /* Read in buffers and lay out the IOVs. */ + iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; + read_data(STDIN_FILENO, &iov[0].buffer.value, &iov[0].buffer.length); + iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; + iov[1].buffer.value = "user"; + iov[1].buffer.length = 4; + iov[2].type = GSS_IOV_BUFFER_TYPE_DATA; + read_data(STDIN_FILENO, &iov[2].buffer.value, &iov[2].buffer.length); + iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; + iov[3].buffer.value = "mic"; + iov[3].buffer.length = 3; + iov[4].type = GSS_IOV_BUFFER_TYPE_PADDING; + read_data(STDIN_FILENO, &iov[4].buffer.value, &iov[4].buffer.length); + iov[5].type = GSS_IOV_BUFFER_TYPE_TRAILER; + read_data(STDIN_FILENO, &iov[5].buffer.value, &iov[5].buffer.length); + + /* Unwrap and check the data contents. */ + major = krb5_gss_unwrap_iov(&minor, ctx, NULL, NULL, iov, 6); + check(major, minor, "gss_unwrap_iov"); + assert(iov[2].buffer.length == 4); + assert(memcmp(iov[2].buffer.value, "wrap", 4) == 0); + + free(iov[0].buffer.value); + free(iov[2].buffer.value); + free(iov[4].buffer.value); + free(iov[5].buffer.value); +} + +/* Create a wrap token for the text "kernelwrap" and send it to stdout. */ +static void +send_wrap_token(gss_ctx_id_t ctx) +{ + OM_uint32 major, minor; + gss_buffer_desc buf, wrapped; + + buf.value = "kernelwrap"; + buf.length = 10; + major = krb5_gss_wrap(&minor, ctx, 1, GSS_C_QOP_DEFAULT, &buf, NULL, + &wrapped); + check(major, minor, "krb5_gss_wrap"); + send_data(STDOUT_FILENO, wrapped.value, wrapped.length); + free(wrapped.value); +} + +/* Create a wrap token for the text "kernelmic" and send it to stdout. */ +static void +send_mic_token(gss_ctx_id_t ctx) +{ + OM_uint32 major, minor; + gss_buffer_desc buf, mic; + + buf.value = "kernelmic"; + buf.length = 9; + major = krb5_gss_get_mic(&minor, ctx, GSS_C_QOP_DEFAULT, &buf, &mic); + check(major, minor, "krb5_gss_get_mic"); + send_data(STDOUT_FILENO, mic.value, mic.length); + free(mic.value); +} + +/* Create an IOV token for "kernelwrapmic", wrapping only the "wrap" part, and + * send the header/data/padding/trailer buffers to stdout. */ +static void +send_iov_token(gss_ctx_id_t ctx) +{ + OM_uint32 major, minor; + gss_iov_buffer_desc iov[6]; + char *buf, *p; + + /* Lay out skeleton IOVs and compute header, padding, trailer lengths. */ + iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; + iov[0].buffer.value = NULL; + iov[0].buffer.length = 0; + iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; + iov[1].buffer.value = "kernel"; + iov[1].buffer.length = 6; + iov[2].type = GSS_IOV_BUFFER_TYPE_DATA; + iov[2].buffer.value = "wrap"; + iov[2].buffer.length = 4; + iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; + iov[3].buffer.value = "mic"; + iov[3].buffer.length = 3; + iov[4].type = GSS_IOV_BUFFER_TYPE_PADDING; + iov[4].buffer.value = NULL; + iov[4].buffer.length = 0; + iov[5].type = GSS_IOV_BUFFER_TYPE_TRAILER; + iov[5].buffer.value = NULL; + iov[5].buffer.length = 0; + major = krb5_gss_wrap_iov_length(&minor, ctx, 1, GSS_C_QOP_DEFAULT, NULL, + iov, 6); + check(major, minor, "krb5_gss_wrap_iov_length"); + + /* Create a payload and set header/data/padding/trailer IOV pointers. */ + buf = malloc(iov[0].buffer.length + iov[2].buffer.length + + iov[4].buffer.length + iov[5].buffer.length); + assert(buf != NULL); + p = buf; + iov[0].buffer.value = p; + p += iov[0].buffer.length; + memcpy(p, "wrap", 4); + iov[2].buffer.value = p; + p += iov[2].buffer.length; + iov[4].buffer.value = p; + p += iov[4].buffer.length; + iov[5].buffer.value = p; + + /* Wrap the payload and send it to fd in chunks. */ + major = krb5_gss_wrap_iov(&minor, ctx, 1, GSS_C_QOP_DEFAULT, NULL, iov, 6); + check(major, minor, "gss_wrap_iov"); + send_data(STDOUT_FILENO, iov[0].buffer.value, iov[0].buffer.length); + send_data(STDOUT_FILENO, iov[2].buffer.value, iov[2].buffer.length); + send_data(STDOUT_FILENO, iov[4].buffer.value, iov[4].buffer.length); + send_data(STDOUT_FILENO, iov[5].buffer.value, iov[5].buffer.length); + free(buf); +} + +/* Delete the krb5 security context ctx. */ +static void +cleanup_context(gss_ctx_id_t ctx) +{ + OM_uint32 major, minor; + + major = krb5_gss_delete_sec_context(&minor, &ctx, GSS_C_NO_BUFFER); + check(major, minor, "gss_delete_sec_context"); +} + +int +main(int argc, char **argv) +{ + gss_ctx_id_t acceptor; + int dummy; + + /* Make the PRNG work since we're not using krb5_init_context. */ + krb5_c_random_os_entropy(NULL, 0, &dummy); + + read_lucid_context(&acceptor); + read_wrap_token(acceptor); + read_mic_token(acceptor); + read_iov_token(acceptor); + send_wrap_token(acceptor); + send_mic_token(acceptor); + send_iov_token(acceptor); + cleanup_context(acceptor); + return 0; +} diff --git a/src/util/gss-kernel-lib/t_kgss_user.c b/src/util/gss-kernel-lib/t_kgss_user.c new file mode 100644 index 000000000..42f835df6 --- /dev/null +++ b/src/util/gss-kernel-lib/t_kgss_user.c @@ -0,0 +1,393 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* util/gss-kernel-lib/t_kgss_user.c - Userspace portion of test program */ +/* + * Copyright (C) 2011 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. + */ + +/* + * This program is run from t_kgss.py. It establishes initiator and acceptor + * contexts, then exports the acceptor context to a child program running + * t_kgss_kernel, which is linked against libkgss.a. Wrap, MIC, and IOV tokens + * are then exchanged with the child process to test the libkgss functionality. + */ + +#include "k5-int.h" +#include +#include +#include +#include +#include "t_kgss_common.h" + +/* If major represents an error, display an error message and exit. */ +static void +check(OM_uint32 major, OM_uint32 minor, const char *fn) +{ + OM_uint32 msg_ctx, tmpmin; + gss_buffer_desc msg; + + if (!GSS_ERROR(major)) + return; + fprintf(stderr, "fn: major %u, minor %u\n", major, minor); + gss_display_status(&tmpmin, minor, GSS_C_MECH_CODE, GSS_C_NULL_OID, + &msg_ctx, &msg); + fprintf(stderr, "%.*s\n", (int)msg.length, (char *)msg.value); + exit(1); +} + +/* Establish initiator and acceptor security krb5 contexts using default + * initiator/acceptor creds and a target krb5 principal named tprinc. */ +static void +establish_contexts(const char *tprinc, gss_ctx_id_t *initiator_out, + gss_ctx_id_t *acceptor_out) +{ + OM_uint32 major, minor; + gss_buffer_desc buf, itoken, rtoken; + gss_name_t target_name; + gss_ctx_id_t initiator = GSS_C_NO_CONTEXT, acceptor = GSS_C_NO_CONTEXT; + + /* Import the target principal. */ + buf.value = (void *)tprinc; + buf.length = strlen(tprinc); + major = gss_import_name(&minor, &buf, (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, + &target_name); + check(major, minor, "gss_import_name"); + + /* Create initiator context and get initiator token. */ + itoken.value = NULL; + itoken.length = 0; + major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &initiator, + target_name, (gss_OID)gss_mech_krb5, + GSS_C_MUTUAL_FLAG, GSS_C_INDEFINITE, + GSS_C_NO_CHANNEL_BINDINGS, GSS_C_NO_BUFFER, + NULL, &itoken, NULL, NULL); + check(major, minor, "gss_init_sec_context(1)"); + assert(major == GSS_S_CONTINUE_NEEDED); + + /* Create acceptor context and get response token. */ + rtoken.value = NULL; + rtoken.length = 0; + major = gss_accept_sec_context(&minor, &acceptor, GSS_C_NO_CREDENTIAL, + &itoken, GSS_C_NO_CHANNEL_BINDINGS, + NULL, NULL, &rtoken, NULL, NULL, NULL); + check(major, minor, "gss_accept_sec_context"); + assert(major == GSS_S_COMPLETE); + + /* Complete initiator context using response token. */ + gss_release_buffer(&minor, &itoken); + itoken.value = NULL; + itoken.length = 0; + major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &initiator, + target_name, (gss_OID)gss_mech_krb5, + GSS_C_MUTUAL_FLAG, GSS_C_INDEFINITE, + GSS_C_NO_CHANNEL_BINDINGS, &rtoken, + NULL, &itoken, NULL, NULL); + check(major, minor, "gss_init_sec_context(2)"); + assert(major == GSS_S_COMPLETE); + gss_release_buffer(&minor, &rtoken); + gss_release_buffer(&minor, &itoken); + + *initiator_out = initiator; + *acceptor_out = acceptor; +} + +/* Start t_kgss_kernel in a child process with input and output pipes. */ +static void +start_child(int *to_child_out, int *from_child_out, pid_t *pid_out) +{ + pid_t pid; + int stdin_pipe[2], stdout_pipe[2]; + + assert(pipe(stdin_pipe) == 0); + assert(pipe(stdout_pipe) == 0); + pid = fork(); + if (pid == 0) { + /* Child. */ + dup2(stdin_pipe[0], STDIN_FILENO); + dup2(stdout_pipe[1], STDOUT_FILENO); + close(stdin_pipe[0]); + close(stdin_pipe[1]); + close(stdout_pipe[0]); + close(stdout_pipe[1]); + execl("./t_kgss_kernel", "./t_kgss_kernel", (char *)NULL); + _exit(1); + } + close(stdin_pipe[0]); + close(stdout_pipe[1]); + *to_child_out = stdin_pipe[1]; + *from_child_out = stdout_pipe[0]; + *pid_out = pid; +} + +#define WRITE(b, d) krb5int_buf_add_len(b, (char *)&d, sizeof(d)) + +/* Add the fields of lkey to bufp. */ +static void +add_lucid_key(struct k5buf *bufp, const gss_krb5_lucid_key_t *lkey) +{ + WRITE(bufp, lkey->type); + WRITE(bufp, lkey->length); + krb5int_buf_add_len(bufp, lkey->data, lkey->length); +} + +/* Using a machine-dependent format, marshal the fields of lctx into an + * allocated buffer. */ +static void +marshal_lucid_context(const gss_krb5_lucid_context_v1_t *lctx, + unsigned char **data_out, size_t *len_out) +{ + struct k5buf buf; + + krb5int_buf_init_dynamic(&buf); + WRITE(&buf, lctx->version); + WRITE(&buf, lctx->initiate); + WRITE(&buf, lctx->endtime); + WRITE(&buf, lctx->send_seq); + WRITE(&buf, lctx->recv_seq); + WRITE(&buf, lctx->protocol); + if (lctx->protocol == 0) { + WRITE(&buf, lctx->rfc1964_kd.sign_alg); + WRITE(&buf, lctx->rfc1964_kd.seal_alg); + add_lucid_key(&buf, &lctx->rfc1964_kd.ctx_key); + } else if (lctx->protocol == 1) { + WRITE(&buf, lctx->cfx_kd.have_acceptor_subkey); + add_lucid_key(&buf, &lctx->cfx_kd.ctx_key); + if (lctx->cfx_kd.have_acceptor_subkey) + add_lucid_key(&buf, &lctx->cfx_kd.acceptor_subkey); + } else + abort(); + assert(krb5int_buf_data(&buf) != NULL); + *data_out = krb5int_buf_data(&buf); + *len_out = krb5int_buf_len(&buf); +} + +/* Export ctx as a lucid context, marshal it, and write it to fd. */ +static void +send_lucid_context(gss_ctx_id_t ctx, int fd) +{ + OM_uint32 major, minor; + void *result; + gss_krb5_lucid_context_v1_t *lctx; + unsigned char *data; + size_t len; + + major = gss_krb5_export_lucid_sec_context(&minor, &ctx, 1, &result); + check(major, minor, "gss_krb5_export_lucid_sec_context"); + lctx = result; + marshal_lucid_context(lctx, &data, &len); + send_data(fd, data, len); + free(data); +} + +/* Create a GSS wrap token of the text "userwrap" and send it to fd. */ +static void +send_wrap_token(gss_ctx_id_t ctx, int fd) +{ + OM_uint32 major, minor; + gss_buffer_desc buf, wrapped; + + buf.value = "userwrap"; + buf.length = 8; + major = gss_wrap(&minor, ctx, 1, GSS_C_QOP_DEFAULT, &buf, NULL, &wrapped); + check(major, minor, "gss_wrap"); + send_data(fd, wrapped.value, wrapped.length); + gss_release_buffer(&minor, &wrapped); +} + +/* Create a MIC token for the text "usermic" and send it to fd. */ +static void +send_mic_token(gss_ctx_id_t ctx, int fd) +{ + OM_uint32 major, minor; + gss_buffer_desc buf, mic; + + buf.value = "usermic"; + buf.length = 7; + major = gss_get_mic(&minor, ctx, GSS_C_QOP_DEFAULT, &buf, &mic); + check(major, minor, "gss_get_mic"); + send_data(fd, mic.value, mic.length); + gss_release_buffer(&minor, &mic); +} + +/* Create an IOV token for "userwrapmic", wrapping only the "wrap" part, and + * send the header/data/padding/trailer buffers to fd. */ +static void +send_iov_token(gss_ctx_id_t ctx, int fd) +{ + OM_uint32 major, minor; + gss_iov_buffer_desc iov[6]; + char *buf, *p; + + /* Lay out skeleton IOVs and compute header, padding, trailer lengths. */ + iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; + iov[0].buffer.value = NULL; + iov[0].buffer.length = 0; + iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; + iov[1].buffer.value = "user"; + iov[1].buffer.length = 4; + iov[2].type = GSS_IOV_BUFFER_TYPE_DATA; + iov[2].buffer.value = "wrap"; + iov[2].buffer.length = 4; + iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; + iov[3].buffer.value = "mic"; + iov[3].buffer.length = 3; + iov[4].type = GSS_IOV_BUFFER_TYPE_PADDING; + iov[4].buffer.value = NULL; + iov[4].buffer.length = 0; + iov[5].type = GSS_IOV_BUFFER_TYPE_TRAILER; + iov[5].buffer.value = NULL; + iov[5].buffer.length = 0; + major = gss_wrap_iov_length(&minor, ctx, 1, GSS_C_QOP_DEFAULT, NULL, + iov, 6); + check(major, minor, "gss_wrap_iov_length"); + + /* Create a payload and set header/data/padding/trailer IOV pointers. */ + buf = malloc(iov[0].buffer.length + iov[2].buffer.length + + iov[4].buffer.length + iov[5].buffer.length); + assert(buf != NULL); + p = buf; + iov[0].buffer.value = p; + p += iov[0].buffer.length; + memcpy(p, "wrap", 4); + iov[2].buffer.value = p; + p += iov[2].buffer.length; + iov[4].buffer.value = p; + p += iov[4].buffer.length; + iov[5].buffer.value = p; + + /* Wrap the payload and send it to fd in chunks. */ + major = gss_wrap_iov(&minor, ctx, 1, GSS_C_QOP_DEFAULT, NULL, iov, 6); + check(major, minor, "gss_wrap_iov"); + send_data(fd, iov[0].buffer.value, iov[0].buffer.length); + send_data(fd, iov[2].buffer.value, iov[2].buffer.length); + send_data(fd, iov[4].buffer.value, iov[4].buffer.length); + send_data(fd, iov[5].buffer.value, iov[5].buffer.length); + free(buf); +} + +/* Read a wrap token from fd and verify that it says "kernelwrap". */ +static void +read_wrap_token(gss_ctx_id_t ctx, int fd) +{ + OM_uint32 major, minor; + unsigned char *data; + size_t len; + gss_buffer_desc wrapped, buf; + + read_data(fd, &wrapped.value, &wrapped.length); + major = gss_unwrap(&minor, ctx, &wrapped, &buf, NULL, NULL); + check(major, minor, "gss_unwrap"); + assert(buf.length == 10 && memcmp(buf.value, "kernelwrap", 10) == 0); + gss_release_buffer(&minor, &buf); + free(wrapped.value); +} + +/* Read a MIC token from fd and verify that it was for "kernelmic". */ +static void +read_mic_token(gss_ctx_id_t ctx, int fd) +{ + OM_uint32 major, minor; + unsigned char *data; + size_t len; + gss_buffer_desc mic, buf; + + read_data(fd, &mic.value, &mic.length); + buf.value = "kernelmic"; + buf.length = 9; + major = gss_verify_mic(&minor, ctx, &buf, &mic, NULL); + check(major, minor, "gss_verify_mic"); + free(mic.value); +} + +/* Read an IOV token from fd and verify that it is for "kernelwrapmic" with + * only the "wrap" part wrapped. */ +static void +read_iov_token(gss_ctx_id_t ctx, int fd) +{ + OM_uint32 major, minor; + gss_iov_buffer_desc iov[6]; + + /* Read in buffers and lay out the IOVs. */ + iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; + read_data(fd, &iov[0].buffer.value, &iov[0].buffer.length); + iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; + iov[1].buffer.value = "kernel"; + iov[1].buffer.length = 6; + iov[2].type = GSS_IOV_BUFFER_TYPE_DATA; + read_data(fd, &iov[2].buffer.value, &iov[2].buffer.length); + iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; + iov[3].buffer.value = "mic"; + iov[3].buffer.length = 3; + iov[4].type = GSS_IOV_BUFFER_TYPE_PADDING; + read_data(fd, &iov[4].buffer.value, &iov[4].buffer.length); + iov[5].type = GSS_IOV_BUFFER_TYPE_TRAILER; + read_data(fd, &iov[5].buffer.value, &iov[5].buffer.length); + + /* Unwrap and check the data contents. */ + major = gss_unwrap_iov(&minor, ctx, NULL, NULL, iov, 6); + check(major, minor, "gss_unwrap_iov"); + assert(iov[2].buffer.length == 4); + assert(memcmp(iov[2].buffer.value, "wrap", 4) == 0); + + free(iov[0].buffer.value); + free(iov[2].buffer.value); + free(iov[4].buffer.value); + free(iov[5].buffer.value); +} + +/* Delete the security context ctx. */ +static void +cleanup_context(gss_ctx_id_t ctx) +{ + OM_uint32 major, minor; + + major = gss_delete_sec_context(&minor, &ctx, GSS_C_NO_BUFFER); + check(major, minor, "gss_delete_sec_context"); +} + +int +main(int argc, char **argv) +{ + gss_ctx_id_t initiator, acceptor; + int to_child, from_child, status; + pid_t child_pid; + + if (argc != 2) { + fprintf(stderr, "Usage: %s target-princ\n", argv[0]); + return 1; + } + + establish_contexts(argv[1], &initiator, &acceptor); + start_child(&to_child, &from_child, &child_pid); + send_lucid_context(acceptor, to_child); + send_wrap_token(initiator, to_child); + send_mic_token(initiator, to_child); + send_iov_token(initiator, to_child); + read_wrap_token(initiator, from_child); + read_mic_token(initiator, from_child); + cleanup_context(initiator); + close(to_child); + close(from_child); + assert(wait(&status) == child_pid); + assert(WIFEXITED(status) && WEXITSTATUS(status) == 0); + return 0; +} -- 2.26.2