Kernel subset
authorGreg Hudson <ghudson@mit.edu>
Mon, 9 May 2011 18:41:03 +0000 (18:41 +0000)
committerGreg Hudson <ghudson@mit.edu>
Mon, 9 May 2011 18:41:03 +0000 (18:41 +0000)
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

13 files changed:
src/configure.in
src/lib/gssapi/krb5/gssapiP_krb5.h
src/util/Makefile.in
src/util/gss-kernel-lib/Makefile.in [new file with mode: 0644]
src/util/gss-kernel-lib/README [new file with mode: 0644]
src/util/gss-kernel-lib/deps [new file with mode: 0644]
src/util/gss-kernel-lib/kernel_gss.c [new file with mode: 0644]
src/util/gss-kernel-lib/t_kgss.c [new file with mode: 0644]
src/util/gss-kernel-lib/t_kgss.py [new file with mode: 0644]
src/util/gss-kernel-lib/t_kgss_common.c [new file with mode: 0644]
src/util/gss-kernel-lib/t_kgss_common.h [new file with mode: 0644]
src/util/gss-kernel-lib/t_kgss_kernel.c [new file with mode: 0644]
src/util/gss-kernel-lib/t_kgss_user.c [new file with mode: 0644]

index bb623e5567d94e53b1aba7fefe0e57ddebf99521..547ff0514990aa85fe7f5ebe1ce369ed27e10709 100644 (file)
@@ -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
 )
index 5453fc03d44792bd3f1e7bfed0dc3d8c67e38167..79a429d0d6433c9a631c76c4b37036c574123507 100644 (file)
@@ -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 */
index cd9dc0e8d27f348813e97bd04abaf6802a10a87c..ba14a605fe3159f904fb62e0e1e88ed1b4b47a46 100644 (file)
@@ -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 (file)
index 0000000..17a68de
--- /dev/null
@@ -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 (file)
index 0000000..b2adf2b
--- /dev/null
@@ -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 (file)
index 0000000..08a5d8a
--- /dev/null
@@ -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 (file)
index 0000000..9c3ac77
--- /dev/null
@@ -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 (file)
index 0000000..623be12
--- /dev/null
@@ -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 (file)
index 0000000..86cd340
--- /dev/null
@@ -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 (file)
index 0000000..1b31634
--- /dev/null
@@ -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 <unistd.h>
+#include <gssapi/gssapi_krb5.h>
+
+/* 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 (file)
index 0000000..8225bef
--- /dev/null
@@ -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 (file)
index 0000000..0d70f1f
--- /dev/null
@@ -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 <unistd.h>
+#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 (file)
index 0000000..42f835d
--- /dev/null
@@ -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 <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <gssapi/gssapi_krb5.h>
+#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;
+}