diff options
| author | Cy Schubert <cy@FreeBSD.org> | 2017-07-07 17:03:42 +0000 |
|---|---|---|
| committer | Cy Schubert <cy@FreeBSD.org> | 2017-07-07 17:03:42 +0000 |
| commit | 33a9b234e7087f573ef08cd7318c6497ba08b439 (patch) | |
| tree | d0ea40ad3bf5463a3c55795977c71bcb7d781b4b /src/lib/gssapi | |
Diffstat (limited to 'src/lib/gssapi')
153 files changed, 44751 insertions, 0 deletions
diff --git a/src/lib/gssapi/LICENSE b/src/lib/gssapi/LICENSE new file mode 100644 index 000000000000..612f8ad6ef7b --- /dev/null +++ b/src/lib/gssapi/LICENSE @@ -0,0 +1,91 @@ +[ NOTE: MIT has only incorporated the mechglue and spnego change, and +not the incremental propagation changes. The filenames are different +between the Sun and MIT sources. The actual MIT filenames appear in +the top-level README file. Original text of Sun's LICENSE file +follows. ] + +Subject to the license set forth below, Sun Microsystems, Inc. donates +the attached files to MIT for the purpose of including these +modifications and additions in future versions of the Kerberos system. + +Many of the files attached are subject to licenses issued by other +entities, including OpenVision, MIT, and FundsXpress. See the +individual files, and/or related Readme files, for these licenses. + +In addition Sun requires that the license set forth below be +incorporated into any future version of the Kerberos system which +contains portions of the files attached. The following files must be +listed, in the top level Readme file, as being provided subject to such +license: + +cmd/krb5/iprop/iprop.x +cmd/krb5/iprop/iprop_hdr.h +cmd/krb5/kadmin/server/ipropd_svc.c +cmd/krb5/kproplog/kproplog.c +cmd/krb5/slave/kpropd_rpc.c +lib/gss_mechs/mech_krb5/et/kdb5_err.c +lib/gss_mechs/mech_spnego/mech/gssapiP_spnego.h +lib/gss_mechs/mech_spnego/mech/spnego_mech.c +lib/krb5/kadm5/kadm_host_srv_names.c +lib/krb5/kdb/kdb_convert.c +lib/krb5/kdb/kdb_hdr.h +lib/krb5/kdb/kdb_log.c +lib/krb5/kdb/kdb_log.h +lib/libgss/g_accept_sec_context.c +lib/libgss/g_acquire_cred.c +lib/libgss/g_canon_name.c +lib/libgss/g_compare_name.c +lib/libgss/g_context_time.c +lib/libgss/g_delete_sec_context.c +lib/libgss/g_dsp_name.c +lib/libgss/g_dsp_status.c +lib/libgss/g_dup_name.c +lib/libgss/g_exp_sec_context.c +lib/libgss/g_export_name.c +lib/libgss/g_glue.c +lib/libgss/g_imp_name.c +lib/libgss/g_imp_sec_context.c +lib/libgss/g_init_sec_context.c +lib/libgss/g_initialize.c +lib/libgss/g_inquire_context.c +lib/libgss/g_inquire_cred.c +lib/libgss/g_inquire_names.c +lib/libgss/g_process_context.c +lib/libgss/g_rel_buffer.c +lib/libgss/g_rel_cred.c +lib/libgss/g_rel_name.c +lib/libgss/g_rel_oid_set.c +lib/libgss/g_seal.c +lib/libgss/g_sign.c +lib/libgss/g_store_cred.c +lib/libgss/g_unseal.c +lib/libgss/g_userok.c +lib/libgss/g_utils.c +lib/libgss/g_verify.c +lib/libgss/gssd_pname_to_uid.c +uts/common/gssapi/include/gssapi_err_generic.h +uts/common/gssapi/include/mechglueP.h + +Sun's License is as follows: + +Copyright (c) 2004 Sun Microsystems, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/src/lib/gssapi/Makefile.in b/src/lib/gssapi/Makefile.in new file mode 100644 index 000000000000..1e8db4e9796e --- /dev/null +++ b/src/lib/gssapi/Makefile.in @@ -0,0 +1,104 @@ +mydir=lib$(S)gssapi +BUILDTOP=$(REL)..$(S).. +SUBDIRS= generic krb5 spnego mechglue +DEFINES=-D_GSS_STATIC_LINK=1 + +##DOSLIBNAME=$(OUTPRE)gssapi.lib +##DOSOBJFILELIST=@$(OUTPRE)mechglue.lst @$(OUTPRE)spnego.lst @$(OUTPRE)generic.lst @$(OUTPRE)krb5.lst # @$(OUTPRE)gssapi.lst +##DOSOBJFILEDEP=$(OUTPRE)mechglue.lst $(OUTPRE)spnego.lst $(OUTPRE)generic.lst $(OUTPRE)krb5.lst # $(OUTPRE)gssapi.lst + +###DOSOBJFILE=$(OUTPRE)gssapi.lst +##DOSLIBOBJS=$(OBJS) + +##DOS##DLL_EXP_TYPE=GSS + +LOCALINCLUDES = -Igeneric -I$(srcdir)/generic -Ikrb5 -I$(srcdir)/krb5 -I$(srcdir)/mechglue +STLIBOBJS= + +OBJS= +SRCS= + +LIBBASE=gssapi_krb5 +LIBMAJOR=2 +LIBMINOR=2 +#LIBINITFUNC=gssint_lib_init +#LIBFINIFUNC=gssint_lib_fini +STOBJLISTS=OBJS.ST generic/OBJS.ST mechglue/OBJS.ST krb5/OBJS.ST spnego/OBJS.ST +SUBDIROBJLISTS=generic/OBJS.ST mechglue/OBJS.ST krb5/OBJS.ST spnego/OBJS.ST +SHLIB_EXPDEPS=\ + $(KRB5_DEPLIB) $(CRYPTO_DEPLIB) $(SUPPORT_DEPLIB) $(COM_ERR_DEPLIB) +SHLIB_EXPLIBS=-lkrb5 -lk5crypto -lcom_err $(SUPPORT_LIB) $(DL_LIB) $(LIBS) +RELDIR=gssapi + +all-unix: all-liblinks @MAINT@ verify-calling-conventions-gssapi + +install-unix: install-libs + +clean-unix:: clean-liblinks clean-libs clean-libobjs clean-misc-unix + +clean-windows:: + $(RM) gssapi.lib gssapi.bak + +clean-misc-unix: clean-recurse + $(RM) merged-gssapi-header.h + $(RM) -r $(EHDRDIR) + +EHDRDIR=$(BUILDTOP)$(S)include$(S)gssapi +EXPORTED_HEADERS= \ + $(EHDRDIR)$(S)gssapi_krb5.h \ + $(EHDRDIR)$(S)gssapi_generic.h \ + $(EHDRDIR)$(S)gssapi.h \ + $(EHDRDIR)$(S)gssapi_alloc.h \ + $(EHDRDIR)$(S)gssapi_ext.h +merged-gssapi-header.h: $(EXPORTED_HEADERS) + cat $(EXPORTED_HEADERS) > merged.tmp + $(MV) merged.tmp merged-gssapi-header.h +verify-calling-conventions-gssapi: merged-gssapi-header.h + $(PERL) -w $(top_srcdir)/util/def-check.pl merged-gssapi-header.h $(srcdir)/../gssapi32.def + +# These rules are an attempt to handle several different problems: +# +# Certain files in subdirectories must be made current by the +# recursion step before we can build files in this directory that +# depend on them. Existing but out-of-date versions must not be used. +# +# In a parallel make, nothing should be built more than once. This +# effect can be exaggerated for testing by sticking "sleep 5" into the +# rules for generating the files in subdirectories. For example, in +# between testing for a directory and creating it -- do you then get +# mkdir complaining that the directory exists? Adding the sleep +# command may also exaggerate the build-with-outdated-headers problem, +# by causing the timestamp on the newly generated header to be several +# seconds newer than object files built with its old version, even on +# fast machines where the UNIX filesystem's one-second granularity +# would mask the problem. +# +# We must not cause these files to always be considered newly updated +# when it comes time to build the object files in this directory. +# Otherwise, we wind up recompiling some files every time we run make. + +# This set of rules fails the parallel make case; it can build +# gssapi-include and all-recurse at the same time, and both will +# create include/gssapi and gssapi.h. +#$(BUILDTOP)/include/gssapi/gssapi.h: generic/gssapi.h +# (cd generic && $(MAKE) gssapi-include) +#generic/gssapi.h: generic/gssapi.hin +# (cd generic && $(MAKE) gssapi.h) +#generic/gssapi_err_generic.h: generic/gssapi_err_generic.et +# (cd generic && $(MAKE) gssapi_err_generic.h) +#krb5/gssapi_err_krb5.h: krb5/gssapi_err_krb5.et +# (cd krb5 && $(MAKE) gssapi_err_krb5.h) + +# This version, without the no-op command to run, reportedly caused +# repeated rebuilds in certain cases. With the no-op command, it +# appears to be properly serializing the subdir processing and local +# compiles... so far. +##DOS##!if 0 +$(EXPORTED_HEADERS) generic/gssapi.h krb5/gssapi_err_krb5.h generic/gssapi_err_generic.h krb5/gssapi_krb5.h: all-recurse + : $@ updated by recursion rule +##DOS##!endif + + +@lib_frag@ +@libobj_frag@ + diff --git a/src/lib/gssapi/README_SAMPLE_APP b/src/lib/gssapi/README_SAMPLE_APP new file mode 100644 index 000000000000..c26bb09adebf --- /dev/null +++ b/src/lib/gssapi/README_SAMPLE_APP @@ -0,0 +1,4 @@ +A sample GSS-API client and server application can be found in the +directory ./appl/gss-sample in this distribution. Read the file +./appl/gss-sample/README for information on how it works and how to +build it. diff --git a/src/lib/gssapi/deps b/src/lib/gssapi/deps new file mode 100644 index 000000000000..2feac3c9d388 --- /dev/null +++ b/src/lib/gssapi/deps @@ -0,0 +1 @@ +# No dependencies here. diff --git a/src/lib/gssapi/generic/Makefile.in b/src/lib/gssapi/generic/Makefile.in new file mode 100644 index 000000000000..a9e894899d1a --- /dev/null +++ b/src/lib/gssapi/generic/Makefile.in @@ -0,0 +1,172 @@ +mydir=lib$(S)gssapi$(S)generic +BUILDTOP=$(REL)..$(S)..$(S).. +LOCALINCLUDES = -I. -I$(srcdir) -I$(srcdir)/.. + +##DOS##BUILDTOP = ..\..\.. +##DOS##PREFIXDIR=generic +##DOS##OBJFILE=..\$(OUTPRE)generic.lst + +##DOS##DLL_EXP_TYPE=GSS + +ETSRCS= gssapi_err_generic.c +ETOBJS= $(OUTPRE)gssapi_err_generic.$(OBJEXT) +ETHDRS= gssapi_err_generic.h + +EHDRDIR= $(BUILDTOP)$(S)include$(S)gssapi + +HDRS= $(EHDRDIR)$(S)gssapi.h \ + $(EHDRDIR)$(S)gssapi_generic.h \ + $(EHDRDIR)$(S)gssapi_alloc.h \ + $(EHDRDIR)$(S)gssapi_ext.h + +MK_EHDRDIR=if test -d $(EHDRDIR); then :; else (set -x; mkdir $(EHDRDIR)); fi +##DOS##MK_EHDRDIR=rem + +gssapi-include: $(EHDRDIR)$(S)gssapi.h + +$(EHDRDIR)$(S)gssapi.h: $(EHDRDIR)$(S)timestamp gssapi.h + $(CP) gssapi.h $@ +$(EHDRDIR)$(S)gssapi_generic.h: $(EHDRDIR)$(S)timestamp $(srcdir)$(S)gssapi_generic.h + $(CP) $(srcdir)$(S)gssapi_generic.h $@ +$(EHDRDIR)$(S)gssapi_alloc.h: $(EHDRDIR)$(S)timestamp $(srcdir)$(S)gssapi_alloc.h + $(CP) $(srcdir)$(S)gssapi_alloc.h $@ +$(EHDRDIR)$(S)gssapi_ext.h: $(EHDRDIR)$(S)timestamp $(srcdir)$(S)gssapi_ext.h + $(CP) $(srcdir)$(S)gssapi_ext.h $@ + +$(EHDRDIR)$(S)timestamp: + $(MK_EHDRDIR) + echo timestamp > $(EHDRDIR)$(S)timestamp + +$(OUTPRE)gssapi_err_generic.$(OBJEXT): gssapi_err_generic.c +gssapi_err_generic.h: gssapi_err_generic.et +gssapi_err_generic.c: gssapi_err_generic.et + +##DOS##!if 0 +include_xom=@include_xom@ +##DOS##include_xom=rem +gssapi.h: gssapi.hin + @echo "Creating gssapi.h" ; \ + h=gss$$$$; $(RM) $$h; \ + (echo "/* This is the gssapi.h prologue. */"; \ + $(include_xom) && \ + echo "/* End of gssapi.h prologue. */"&& \ + cat $(srcdir)/gssapi.hin )> $$h && \ + (set -x; $(MV) $$h $@) ; e=$$?; $(RM) $$h; exit $$e +##DOS##!endif +##DOS##gssapi.h: gssapi.hin +##DOS## $(CP) $** $@ + +SRCS = \ + $(srcdir)/disp_com_err_status.c \ + $(srcdir)/disp_major_status.c \ + $(srcdir)/gssapi_generic.c \ + $(srcdir)/oid_ops.c \ + $(srcdir)/rel_buffer.c \ + $(srcdir)/rel_oid_set.c \ + $(srcdir)/util_buffer.c \ + $(srcdir)/util_buffer_set.c \ + $(srcdir)/util_errmap.c \ + $(srcdir)/util_set.c \ + $(srcdir)/util_seqstate.c \ + $(srcdir)/util_token.c \ + gssapi_err_generic.c + +EXTRADEPSRCS = t_seqstate.c + +OBJS = \ + $(OUTPRE)disp_com_err_status.$(OBJEXT) \ + $(OUTPRE)disp_major_status.$(OBJEXT) \ + $(OUTPRE)gssapi_generic.$(OBJEXT) \ + $(OUTPRE)oid_ops.$(OBJEXT) \ + $(OUTPRE)rel_buffer.$(OBJEXT) \ + $(OUTPRE)rel_oid_set.$(OBJEXT) \ + $(OUTPRE)util_buffer.$(OBJEXT) \ + $(OUTPRE)util_buffer_set.$(OBJEXT) \ + $(OUTPRE)util_errmap.$(OBJEXT) \ + $(OUTPRE)util_set.$(OBJEXT) \ + $(OUTPRE)util_seqstate.$(OBJEXT) \ + $(OUTPRE)util_token.$(OBJEXT) \ + $(OUTPRE)gssapi_err_generic.$(OBJEXT) + +STLIBOBJS = \ + disp_com_err_status.o \ + disp_major_status.o \ + gssapi_generic.o \ + oid_ops.o \ + rel_buffer.o \ + rel_oid_set.o \ + util_buffer.o \ + util_buffer_set.o \ + util_errmap.o \ + util_set.o \ + util_seqstate.o \ + util_token.o \ + gssapi_err_generic.o + +EXPORTED_HEADERS= gssapi_generic.h gssapi_ext.h +EXPORTED_BUILT_HEADERS= gssapi.h + +$(OBJS): $(EXPORTED_HEADERS) $(ETHDRS) + +all-unix: $(EXPORTED_HEADERS) $(ETHDRS) $(HDRS) +all-unix: all-libobjs + +errmap.h: $(top_srcdir)/util/gen.pl $(top_srcdir)/util/t_array.pm \ + $(top_srcdir)/util/t_bimap.pm + $(PERL) -w -I$(top_srcdir)/util $(top_srcdir)/util/gen.pl bimap \ + errmap.h \ + NAME=mecherrmap LEFT=OM_uint32 RIGHT="struct mecherror" \ + LEFTPRINT=print_OM_uint32 RIGHTPRINT=mecherror_print \ + LEFTCMP=cmp_OM_uint32 RIGHTCMP=mecherror_cmp + +maptest.h: $(top_srcdir)/util/gen.pl $(top_srcdir)/util/t_array.pm \ + $(top_srcdir)/util/t_bimap.pm + $(PERL) -w -I$(top_srcdir)/util $(top_srcdir)/util/gen.pl bimap \ + maptest.h \ + NAME=foo LEFT=int RIGHT=elt LEFTPRINT=intprt \ + RIGHTPRINT=eltprt LEFTCMP=intcmp RIGHTCMP=eltcmp +maptest.o: maptest.c maptest.h +maptest: maptest.o + $(CC_LINK) -o maptest maptest.o + +##DOS##LIBOBJS = $(OBJS) + +all-windows: win-create-ehdrdir +all-windows: $(HDRS) + +win-create-ehdrdir: + if not exist $(EHDRDIR)\nul mkdir $(EHDRDIR) + +clean-unix:: clean-libobjs + $(RM) $(ETHDRS) $(ETSRCS) $(HDRS) $(EXPORTED_BUILT_HEADERS) \ + $(EHDRDIR)$(S)timestamp errmap.h maptest.h + $(RM) t_seqstate.o t_seqstate + +clean-windows:: + $(RM) $(HDRS) maptest.h + -if exist $(EHDRDIR)\nul rmdir $(EHDRDIR) + +t_seqstate: t_seqstate.o util_seqstate.o $(SUPPORT_DEPLIB) + $(CC_LINK) $(ALL_CFLAGS) -o $@ t_seqstate.o util_seqstate.o \ + $(SUPPORT_LIB) + +check-unix: t_seqstate + $(RUN_TEST) ./t_seqstate + +generate-files-mac: gssapi.h errmap.h + +# Krb5InstallHeaders($(EXPORTED_HEADERS), $(KRB5_INCDIR)/krb5) +install-headers-unix install: gssapi.h + @set -x; for f in $(EXPORTED_HEADERS) ; \ + do $(INSTALL_DATA) $(srcdir)/$$f \ + $(DESTDIR)$(KRB5_INCDIR)/gssapi/$$f ; \ + done + @set -x; for f in $(EXPORTED_BUILT_HEADERS) ; \ + do $(INSTALL_DATA) $$f \ + $(DESTDIR)$(KRB5_INCDIR)/gssapi/$$f ; \ + done + +depend: $(ETSRCS) $(ETHDRS) $(HDRS) errmap.h maptest.h + +@libobj_frag@ + diff --git a/src/lib/gssapi/generic/deps b/src/lib/gssapi/generic/deps new file mode 100644 index 000000000000..5b80e7f3876c --- /dev/null +++ b/src/lib/gssapi/generic/deps @@ -0,0 +1,96 @@ +# +# Generated makefile dependencies follow. +# +disp_com_err_status.so disp_com_err_status.po $(OUTPRE)disp_com_err_status.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(COM_ERR_DEPS) \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h disp_com_err_status.c \ + gssapiP_generic.h gssapi_err_generic.h gssapi_ext.h \ + gssapi_generic.h +disp_major_status.so disp_major_status.po $(OUTPRE)disp_major_status.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(COM_ERR_DEPS) \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h disp_major_status.c \ + gssapiP_generic.h gssapi_err_generic.h gssapi_ext.h \ + gssapi_generic.h +gssapi_generic.so gssapi_generic.po $(OUTPRE)gssapi_generic.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(COM_ERR_DEPS) \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h gssapiP_generic.h \ + gssapi_err_generic.h gssapi_ext.h gssapi_generic.c \ + gssapi_generic.h +oid_ops.so oid_ops.po $(OUTPRE)oid_ops.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ + $(BUILDTOP)/include/gssapi/gssapi_generic.h $(COM_ERR_DEPS) \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h gssapiP_generic.h \ + gssapi_err_generic.h gssapi_ext.h gssapi_generic.h \ + oid_ops.c +rel_buffer.so rel_buffer.po $(OUTPRE)rel_buffer.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(COM_ERR_DEPS) \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h gssapiP_generic.h \ + gssapi_err_generic.h gssapi_ext.h gssapi_generic.h \ + rel_buffer.c +rel_oid_set.so rel_oid_set.po $(OUTPRE)rel_oid_set.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(COM_ERR_DEPS) \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h gssapiP_generic.h \ + gssapi_err_generic.h gssapi_ext.h gssapi_generic.h \ + rel_oid_set.c +util_buffer.so util_buffer.po $(OUTPRE)util_buffer.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(COM_ERR_DEPS) \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h gssapiP_generic.h \ + gssapi_err_generic.h gssapi_ext.h gssapi_generic.h \ + util_buffer.c +util_buffer_set.so util_buffer_set.po $(OUTPRE)util_buffer_set.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(COM_ERR_DEPS) \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h gssapiP_generic.h \ + gssapi_err_generic.h gssapi_ext.h gssapi_generic.h \ + util_buffer_set.c +util_errmap.so util_errmap.po $(OUTPRE)util_errmap.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/krb5/krb5.h \ + $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/krb5.h \ + errmap.h gssapiP_generic.h gssapi_err_generic.h gssapi_ext.h \ + gssapi_generic.h util_errmap.c +util_set.so util_set.po $(OUTPRE)util_set.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(COM_ERR_DEPS) \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h gssapiP_generic.h \ + gssapi_err_generic.h gssapi_ext.h gssapi_generic.h \ + util_set.c +util_seqstate.so util_seqstate.po $(OUTPRE)util_seqstate.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(COM_ERR_DEPS) \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h gssapiP_generic.h \ + gssapi_err_generic.h gssapi_ext.h gssapi_generic.h \ + util_seqstate.c +util_token.so util_token.po $(OUTPRE)util_token.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(COM_ERR_DEPS) \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h gssapiP_generic.h \ + gssapi_err_generic.h gssapi_ext.h gssapi_generic.h \ + util_token.c +gssapi_err_generic.so gssapi_err_generic.po $(OUTPRE)gssapi_err_generic.$(OBJEXT): \ + $(COM_ERR_DEPS) gssapi_err_generic.c +t_seqstate.so t_seqstate.po $(OUTPRE)t_seqstate.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(COM_ERR_DEPS) \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h gssapiP_generic.h \ + gssapi_err_generic.h gssapi_ext.h gssapi_generic.h \ + t_seqstate.c diff --git a/src/lib/gssapi/generic/disp_com_err_status.c b/src/lib/gssapi/generic/disp_com_err_status.c new file mode 100644 index 000000000000..bc416107e74e --- /dev/null +++ b/src/lib/gssapi/generic/disp_com_err_status.c @@ -0,0 +1,62 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * $Id$ + */ + +#include "gssapiP_generic.h" +#include "com_err.h" + +/* XXXX internationalization!! */ + +/**/ + +static const char * const no_error = "No error"; + +/**/ + +/* if status_type == GSS_C_GSS_CODE, return up to three error messages, + for routine errors, call error, and status, in that order. + message_context == 0 : print the routine error + message_context == 1 : print the calling error + message_context > 2 : print supplementary info bit (message_context-2) + if status_type == GSS_C_MECH_CODE, return the output from error_message() +*/ + +OM_uint32 +g_display_com_err_status(OM_uint32 *minor_status, OM_uint32 status_value, + gss_buffer_t status_string) +{ + status_string->length = 0; + status_string->value = NULL; + + if (! g_make_string_buffer(((status_value == 0)?no_error: + error_message(status_value)), + status_string)) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + *minor_status = 0; + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/generic/disp_major_status.c b/src/lib/gssapi/generic/disp_major_status.c new file mode 100644 index 000000000000..848af8fa5526 --- /dev/null +++ b/src/lib/gssapi/generic/disp_major_status.c @@ -0,0 +1,301 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_generic.h" +#include <string.h> +#include <stdio.h> + +/* + * $Id$ + */ + +/* This code has knowledge of the min and max errors of each type + within the gssapi major status */ + +#define GSS_ERROR_STR(value, array, select, min, max, num) \ + (((select(value) < (min)) || (select(value) > (max))) ? NULL : \ + _((array)[num(value)])) + +/**/ + +static const char * const calling_error_string[] = { + NULL, + N_("A required input parameter could not be read"), + N_("A required input parameter could not be written"), + N_("A parameter was malformed"), +}; + +static const char * const calling_error = N_("calling error"); + +#define GSS_CALLING_ERROR_STR(x) \ + GSS_ERROR_STR((x), calling_error_string, GSS_CALLING_ERROR, \ + GSS_S_CALL_INACCESSIBLE_READ, GSS_S_CALL_BAD_STRUCTURE, \ + GSS_CALLING_ERROR_FIELD) + +/**/ + +static const char * const routine_error_string[] = { + NULL, + N_("An unsupported mechanism was requested"), + N_("An invalid name was supplied"), + N_("A supplied name was of an unsupported type"), + N_("Incorrect channel bindings were supplied"), + N_("An invalid status code was supplied"), + N_("A token had an invalid signature"), + N_("No credentials were supplied"), + N_("No context has been established"), + N_("A token was invalid"), + N_("A credential was invalid"), + N_("The referenced credentials have expired"), + N_("The context has expired"), + N_("Miscellaneous failure"), + N_("The quality-of-protection requested could not be provided"), + N_("The operation is forbidden by the local security policy"), + N_("The operation or option is not available"), +}; + +static const char * const routine_error = N_("routine error"); + +#define GSS_ROUTINE_ERROR_STR(x) \ + GSS_ERROR_STR((x), routine_error_string, GSS_ROUTINE_ERROR, \ + GSS_S_BAD_MECH, GSS_S_FAILURE, \ + GSS_ROUTINE_ERROR_FIELD) + +/**/ + +/* this becomes overly gross after about 4 strings */ + +static const char * const sinfo_string[] = { + N_("The routine must be called again to complete its function"), + N_("The token was a duplicate of an earlier token"), + N_("The token's validity period has expired"), + N_("A later token has already been processed"), +}; + +static const char * const sinfo_code = N_("supplementary info code"); + +#define LSBGET(x) ((((x)^((x)-1))+1)>>1) +#define LSBMASK(n) ((1<<(n))^((1<<(n))-1)) + +#define GSS_SINFO_STR(x) \ + ((((1<<(x)) < GSS_S_CONTINUE_NEEDED) || ((1<<(x)) > GSS_S_UNSEQ_TOKEN)) ? \ + /**/NULL:sinfo_string[(x)]) + +/**/ + +static const char * const no_error = N_("No error"); +static const char * const unknown_error = N_("Unknown %s (field = %d)"); + +/**/ + +static int +display_unknown(const char *kind, OM_uint32 value, gss_buffer_t buffer) +{ + char *str; + + if (asprintf(&str, _(unknown_error), kind, value) < 0) + return(0); + + buffer->length = strlen(str); + buffer->value = str; + + return(1); +} + +/* code should be set to the calling error field */ + +static OM_uint32 +display_calling(OM_uint32 *minor_status, OM_uint32 code, + gss_buffer_t status_string) +{ + const char *str; + + if ((str = GSS_CALLING_ERROR_STR(code))) { + if (! g_make_string_buffer(str, status_string)) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + } else { + if (! display_unknown(_(calling_error), GSS_CALLING_ERROR_FIELD(code), + status_string)) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + } + *minor_status = 0; + return(GSS_S_COMPLETE); +} + +/* code should be set to the routine error field */ + +static OM_uint32 +display_routine(OM_uint32 *minor_status, OM_uint32 code, + gss_buffer_t status_string) +{ + const char *str; + + if ((str = GSS_ROUTINE_ERROR_STR(code))) { + if (! g_make_string_buffer(str, status_string)) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + } else { + if (! display_unknown(_(routine_error), GSS_ROUTINE_ERROR_FIELD(code), + status_string)) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + } + *minor_status = 0; + return(GSS_S_COMPLETE); +} + +/* code should be set to the bit offset (log_2) of a supplementary info bit */ + +static OM_uint32 +display_bit(OM_uint32 *minor_status, OM_uint32 code, + gss_buffer_t status_string) +{ + const char *str; + + if ((str = GSS_SINFO_STR(code))) { + if (! g_make_string_buffer(str, status_string)) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + } else { + if (! display_unknown(_(sinfo_code), 1<<code, status_string)) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + } + *minor_status = 0; + return(GSS_S_COMPLETE); +} + +/**/ + +/* return error messages, for routine errors, call error, and status, + in that order. + message_context == 0 : print the routine error + message_context == 1 : print the calling error + message_context > 2 : print supplementary info bit (message_context-2) +*/ + +OM_uint32 +g_display_major_status(OM_uint32 *minor_status, OM_uint32 status_value, + OM_uint32 *message_context, gss_buffer_t status_string) +{ + OM_uint32 ret, tmp; + int bit; + + /*** deal with no error at all specially */ + + if (status_value == 0) { + if (! g_make_string_buffer(no_error, status_string)) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + *message_context = 0; + *minor_status = 0; + return(GSS_S_COMPLETE); + } + + /*** do routine error */ + + if (*message_context == 0) { + if ((tmp = GSS_ROUTINE_ERROR(status_value))) { + status_value -= tmp; + if ((ret = display_routine(minor_status, tmp, status_string))) + return(ret); + *minor_status = 0; + if (status_value) { + (*message_context)++; + return(GSS_S_COMPLETE); + } else { + *message_context = 0; + return(GSS_S_COMPLETE); + } + } else { + (*message_context)++; + } + } else { + status_value -= GSS_ROUTINE_ERROR(status_value); + } + + /*** do calling error */ + + if (*message_context == 1) { + if ((tmp = GSS_CALLING_ERROR(status_value))) { + status_value -= tmp; + if ((ret = display_calling(minor_status, tmp, status_string))) + return(ret); + *minor_status = 0; + if (status_value) { + (*message_context)++; + return(GSS_S_COMPLETE); + } else { + *message_context = 0; + return(GSS_S_COMPLETE); + } + } else { + (*message_context)++; + } + } else { + status_value -= GSS_CALLING_ERROR(status_value); + } + + /*** do sinfo bits (*message_context == 2 + number of bits done) */ + + tmp = GSS_SUPPLEMENTARY_INFO_FIELD(status_value); + /* mask off the bits which have been done */ + if (*message_context > 2) { + tmp &= ~LSBMASK(*message_context-3); + status_value &= ~LSBMASK(*message_context-3); + } + + if (!tmp) { + /* bogon input - there should be something left */ + *minor_status = (OM_uint32) G_BAD_MSG_CTX; + return(GSS_S_FAILURE); + } + + /* compute the bit offset */ + /*SUPPRESS 570*/ + for (bit=0; (((OM_uint32) 1)<<bit) != LSBGET(tmp); bit++) ; + + /* print it */ + if ((ret = display_bit(minor_status, bit, status_string))) + return(ret); + + /* compute the new status_value/message_context */ + status_value -= ((OM_uint32) 1)<<bit; + + if (status_value) { + *message_context = bit+3; + return(GSS_S_COMPLETE); + } else { + *message_context = 0; + return(GSS_S_COMPLETE); + } +} diff --git a/src/lib/gssapi/generic/gssapi.hin b/src/lib/gssapi/generic/gssapi.hin new file mode 100644 index 000000000000..59cd93e09f92 --- /dev/null +++ b/src/lib/gssapi/generic/gssapi.hin @@ -0,0 +1,920 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _GSSAPI_H_ +#define _GSSAPI_H_ + +/* + * Determine platform-dependent configuration. + */ + +#if defined(__MACH__) && defined(__APPLE__) +# include <TargetConditionals.h> +# if TARGET_RT_MAC_CFM +# error "Use KfM 4.0 SDK headers for CFM compilation." +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#if TARGET_OS_MAC +# pragma pack(push,2) +#endif + +#if defined(_MSDOS) || defined(_WIN32) +#include <win-mac.h> +#endif + +#ifndef KRB5_CALLCONV +#define KRB5_CALLCONV +#define KRB5_CALLCONV_C +#endif + +#include <stdint.h> + +/* + * First, include stddef.h to get size_t defined. + */ +#include <stddef.h> + +/* + * POSIX says that sys/types.h is where size_t is defined. + */ +#include <sys/types.h> + +/* + * $Id$ + */ + +/* + * First, define the three platform-dependent pointer types. + */ + +struct gss_name_struct; +typedef struct gss_name_struct * gss_name_t; + +struct gss_cred_id_struct; +typedef struct gss_cred_id_struct * gss_cred_id_t; + +struct gss_ctx_id_struct; +typedef struct gss_ctx_id_struct * gss_ctx_id_t; + +/* + * The following type must be defined as the smallest natural unsigned integer + * supported by the platform that has at least 32 bits of precision. + */ +typedef uint32_t gss_uint32; +typedef int32_t gss_int32; + +#ifdef OM_STRING +/* + * We have included the xom.h header file. Use the definition for + * OM_object identifier. + */ +typedef OM_object_identifier gss_OID_desc, *gss_OID; +#else /* OM_STRING */ +/* + * We can't use X/Open definitions, so roll our own. + */ +typedef gss_uint32 OM_uint32; + +typedef struct gss_OID_desc_struct { + OM_uint32 length; + void *elements; +} gss_OID_desc, *gss_OID; +#endif /* OM_STRING */ + +typedef struct gss_OID_set_desc_struct { + size_t count; + gss_OID elements; +} gss_OID_set_desc, *gss_OID_set; + +typedef struct gss_buffer_desc_struct { + size_t length; + void *value; +} gss_buffer_desc, *gss_buffer_t; + +typedef struct gss_channel_bindings_struct { + OM_uint32 initiator_addrtype; + gss_buffer_desc initiator_address; + OM_uint32 acceptor_addrtype; + gss_buffer_desc acceptor_address; + gss_buffer_desc application_data; +} *gss_channel_bindings_t; + +/* + * For now, define a QOP-type as an OM_uint32 (pending resolution of ongoing + * discussions). + */ +typedef OM_uint32 gss_qop_t; +typedef int gss_cred_usage_t; + +/* + * Flag bits for context-level services. + */ +#define GSS_C_DELEG_FLAG 1 +#define GSS_C_MUTUAL_FLAG 2 +#define GSS_C_REPLAY_FLAG 4 +#define GSS_C_SEQUENCE_FLAG 8 +#define GSS_C_CONF_FLAG 16 +#define GSS_C_INTEG_FLAG 32 +#define GSS_C_ANON_FLAG 64 +#define GSS_C_PROT_READY_FLAG 128 +#define GSS_C_TRANS_FLAG 256 +#define GSS_C_DELEG_POLICY_FLAG 32768 + +/* + * Credential usage options + */ +#define GSS_C_BOTH 0 +#define GSS_C_INITIATE 1 +#define GSS_C_ACCEPT 2 + +/* + * Status code types for gss_display_status + */ +#define GSS_C_GSS_CODE 1 +#define GSS_C_MECH_CODE 2 + +/* + * The constant definitions for channel-bindings address families + */ +#define GSS_C_AF_UNSPEC 0 +#define GSS_C_AF_LOCAL 1 +#define GSS_C_AF_INET 2 +#define GSS_C_AF_IMPLINK 3 +#define GSS_C_AF_PUP 4 +#define GSS_C_AF_CHAOS 5 +#define GSS_C_AF_NS 6 +#define GSS_C_AF_NBS 7 +#define GSS_C_AF_ECMA 8 +#define GSS_C_AF_DATAKIT 9 +#define GSS_C_AF_CCITT 10 +#define GSS_C_AF_SNA 11 +#define GSS_C_AF_DECnet 12 +#define GSS_C_AF_DLI 13 +#define GSS_C_AF_LAT 14 +#define GSS_C_AF_HYLINK 15 +#define GSS_C_AF_APPLETALK 16 +#define GSS_C_AF_BSC 17 +#define GSS_C_AF_DSS 18 +#define GSS_C_AF_OSI 19 +#define GSS_C_AF_NETBIOS 20 +#define GSS_C_AF_X25 21 + +#define GSS_C_AF_NULLADDR 255 + +/* + * Various Null values. + */ +#define GSS_C_NO_NAME ((gss_name_t) 0) +#define GSS_C_NO_BUFFER ((gss_buffer_t) 0) +#define GSS_C_NO_OID ((gss_OID) 0) +#define GSS_C_NO_OID_SET ((gss_OID_set) 0) +#define GSS_C_NO_CONTEXT ((gss_ctx_id_t) 0) +#define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) 0) +#define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) 0) +#define GSS_C_EMPTY_BUFFER {0, NULL} + +/* + * Some alternate names for a couple of the above values. These are defined + * for V1 compatibility. + */ +#define GSS_C_NULL_OID GSS_C_NO_OID +#define GSS_C_NULL_OID_SET GSS_C_NO_OID_SET + +/* + * Define the default Quality of Protection for per-message services. Note + * that an implementation that offers multiple levels of QOP may either reserve + * a value (for example zero, as assumed here) to mean "default protection", or + * alternatively may simply equate GSS_C_QOP_DEFAULT to a specific explicit + * QOP value. However a value of 0 should always be interpreted by a GSSAPI + * implementation as a request for the default protection level. + */ +#define GSS_C_QOP_DEFAULT 0 + +/* + * Expiration time of 2^32-1 seconds means infinite lifetime for a + * credential or security context + */ +#define GSS_C_INDEFINITE ((OM_uint32) 0xfffffffful) + + +/* Major status codes */ + +#define GSS_S_COMPLETE 0 + +/* + * Some "helper" definitions to make the status code macros obvious. + */ +#define GSS_C_CALLING_ERROR_OFFSET 24 +#define GSS_C_ROUTINE_ERROR_OFFSET 16 +#define GSS_C_SUPPLEMENTARY_OFFSET 0 +#define GSS_C_CALLING_ERROR_MASK ((OM_uint32) 0377ul) +#define GSS_C_ROUTINE_ERROR_MASK ((OM_uint32) 0377ul) +#define GSS_C_SUPPLEMENTARY_MASK ((OM_uint32) 0177777ul) + +/* + * The macros that test status codes for error conditions. Note that the + * GSS_ERROR() macro has changed slightly from the V1 GSSAPI so that it now + * evaluates its argument only once. + */ +#define GSS_CALLING_ERROR(x) \ + ((x) & (GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET)) +#define GSS_ROUTINE_ERROR(x) \ + ((x) & (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET)) +#define GSS_SUPPLEMENTARY_INFO(x) \ + ((x) & (GSS_C_SUPPLEMENTARY_MASK << GSS_C_SUPPLEMENTARY_OFFSET)) +#define GSS_ERROR(x) \ + ((x) & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) | \ + (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))) + +/* + * Now the actual status code definitions + */ + +/* + * Calling errors: + */ +#define GSS_S_CALL_INACCESSIBLE_READ \ + (((OM_uint32) 1ul) << GSS_C_CALLING_ERROR_OFFSET) +#define GSS_S_CALL_INACCESSIBLE_WRITE \ + (((OM_uint32) 2ul) << GSS_C_CALLING_ERROR_OFFSET) +#define GSS_S_CALL_BAD_STRUCTURE \ + (((OM_uint32) 3ul) << GSS_C_CALLING_ERROR_OFFSET) + +/* + * Routine errors: + */ +#define GSS_S_BAD_MECH (((OM_uint32) 1ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_BAD_NAME (((OM_uint32) 2ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_BAD_NAMETYPE (((OM_uint32) 3ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_BAD_BINDINGS (((OM_uint32) 4ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_BAD_STATUS (((OM_uint32) 5ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_BAD_SIG (((OM_uint32) 6ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_BAD_MIC GSS_S_BAD_SIG +#define GSS_S_NO_CRED (((OM_uint32) 7ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_NO_CONTEXT (((OM_uint32) 8ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_DEFECTIVE_TOKEN (((OM_uint32) 9ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_DEFECTIVE_CREDENTIAL \ + (((OM_uint32) 10ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_CREDENTIALS_EXPIRED \ + (((OM_uint32) 11ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_CONTEXT_EXPIRED \ + (((OM_uint32) 12ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_FAILURE (((OM_uint32) 13ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_BAD_QOP (((OM_uint32) 14ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_UNAUTHORIZED (((OM_uint32) 15ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_UNAVAILABLE (((OM_uint32) 16ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_DUPLICATE_ELEMENT \ + (((OM_uint32) 17ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_NAME_NOT_MN \ + (((OM_uint32) 18ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_BAD_MECH_ATTR \ + (((OM_uint32) 19ul) << GSS_C_ROUTINE_ERROR_OFFSET) + +/* + * Supplementary info bits: + */ +#define GSS_S_CONTINUE_NEEDED (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 0)) +#define GSS_S_DUPLICATE_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 1)) +#define GSS_S_OLD_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 2)) +#define GSS_S_UNSEQ_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 3)) +#define GSS_S_GAP_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 4)) + + +/* + * Finally, function prototypes for the GSSAPI routines. + */ + +#if defined (_WIN32) && defined (_MSC_VER) +# ifdef GSS_DLL_FILE +# define GSS_DLLIMP __declspec(dllexport) +# else +# define GSS_DLLIMP __declspec(dllimport) +# endif +#else +# define GSS_DLLIMP +#endif + +/* Reserved static storage for GSS_oids. Comments are quotes from RFC 2744. + * + * The implementation must reserve static storage for a + * gss_OID_desc object containing the value + * {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01"}, + * corresponding to an object-identifier value of + * {iso(1) member-body(2) United States(840) mit(113554) + * infosys(1) gssapi(2) generic(1) user_name(1)}. The constant + * GSS_C_NT_USER_NAME should be initialized to point + * to that gss_OID_desc. + */ +GSS_DLLIMP extern gss_OID GSS_C_NT_USER_NAME; + +/* + * The implementation must reserve static storage for a + * gss_OID_desc object containing the value + * {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"}, + * corresponding to an object-identifier value of + * {iso(1) member-body(2) United States(840) mit(113554) + * infosys(1) gssapi(2) generic(1) machine_uid_name(2)}. + * The constant GSS_C_NT_MACHINE_UID_NAME should be + * initialized to point to that gss_OID_desc. + */ +GSS_DLLIMP extern gss_OID GSS_C_NT_MACHINE_UID_NAME; + +/* + * The implementation must reserve static storage for a + * gss_OID_desc object containing the value + * {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03"}, + * corresponding to an object-identifier value of + * {iso(1) member-body(2) United States(840) mit(113554) + * infosys(1) gssapi(2) generic(1) string_uid_name(3)}. + * The constant GSS_C_NT_STRING_UID_NAME should be + * initialized to point to that gss_OID_desc. + */ +GSS_DLLIMP extern gss_OID GSS_C_NT_STRING_UID_NAME; + +/* + * The implementation must reserve static storage for a + * gss_OID_desc object containing the value + * {6, (void *)"\x2b\x06\x01\x05\x06\x02"}, + * corresponding to an object-identifier value of + * {iso(1) org(3) dod(6) internet(1) security(5) + * nametypes(6) gss-host-based-services(2)). The constant + * GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point + * to that gss_OID_desc. This is a deprecated OID value, and + * implementations wishing to support hostbased-service names + * should instead use the GSS_C_NT_HOSTBASED_SERVICE OID, + * defined below, to identify such names; + * GSS_C_NT_HOSTBASED_SERVICE_X should be accepted a synonym + * for GSS_C_NT_HOSTBASED_SERVICE when presented as an input + * parameter, but should not be emitted by GSS-API + * implementations + */ +GSS_DLLIMP extern gss_OID GSS_C_NT_HOSTBASED_SERVICE_X; + +/* + * The implementation must reserve static storage for a + * gss_OID_desc object containing the value + * {10, (void *)"\x2a\x86\x48\x86\xf7\x12" + * "\x01\x02\x01\x04"}, corresponding to an + * object-identifier value of {iso(1) member-body(2) + * Unites States(840) mit(113554) infosys(1) gssapi(2) + * generic(1) service_name(4)}. The constant + * GSS_C_NT_HOSTBASED_SERVICE should be initialized + * to point to that gss_OID_desc. + */ +GSS_DLLIMP extern gss_OID GSS_C_NT_HOSTBASED_SERVICE; + +/* + * The implementation must reserve static storage for a + * gss_OID_desc object containing the value + * {6, (void *)"\x2b\x06\01\x05\x06\x03"}, + * corresponding to an object identifier value of + * {1(iso), 3(org), 6(dod), 1(internet), 5(security), + * 6(nametypes), 3(gss-anonymous-name)}. The constant + * and GSS_C_NT_ANONYMOUS should be initialized to point + * to that gss_OID_desc. + */ +GSS_DLLIMP extern gss_OID GSS_C_NT_ANONYMOUS; + + +/* + * The implementation must reserve static storage for a + * gss_OID_desc object containing the value + * {6, (void *)"\x2b\x06\x01\x05\x06\x04"}, + * corresponding to an object-identifier value of + * {1(iso), 3(org), 6(dod), 1(internet), 5(security), + * 6(nametypes), 4(gss-api-exported-name)}. The constant + * GSS_C_NT_EXPORT_NAME should be initialized to point + * to that gss_OID_desc. + */ +GSS_DLLIMP extern gss_OID GSS_C_NT_EXPORT_NAME; + +/* Function Prototypes */ + +OM_uint32 KRB5_CALLCONV +gss_acquire_cred( + OM_uint32 *, /* minor_status */ + gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *); /* time_rec */ + +OM_uint32 KRB5_CALLCONV +gss_release_cred( + OM_uint32 *, /* minor_status */ + gss_cred_id_t *); /* cred_handle */ + +OM_uint32 KRB5_CALLCONV +gss_init_sec_context( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* claimant_cred_handle */ + gss_ctx_id_t *, /* context_handle */ + gss_name_t, /* target_name */ + gss_OID, /* mech_type (used to be const) */ + OM_uint32, /* req_flags */ + OM_uint32, /* time_req */ + gss_channel_bindings_t, /* input_chan_bindings */ + gss_buffer_t, /* input_token */ + gss_OID *, /* actual_mech_type */ + gss_buffer_t, /* output_token */ + OM_uint32 *, /* ret_flags */ + OM_uint32 *); /* time_rec */ + +OM_uint32 KRB5_CALLCONV +gss_accept_sec_context( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t *, /* context_handle */ + gss_cred_id_t, /* acceptor_cred_handle */ + gss_buffer_t, /* input_token_buffer */ + gss_channel_bindings_t, /* input_chan_bindings */ + gss_name_t *, /* src_name */ + gss_OID *, /* mech_type */ + gss_buffer_t, /* output_token */ + OM_uint32 *, /* ret_flags */ + OM_uint32 *, /* time_rec */ + gss_cred_id_t *); /* delegated_cred_handle */ + +OM_uint32 KRB5_CALLCONV +gss_process_context_token( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_buffer_t); /* token_buffer */ + + +OM_uint32 KRB5_CALLCONV +gss_delete_sec_context( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t *, /* context_handle */ + gss_buffer_t); /* output_token */ + + +OM_uint32 KRB5_CALLCONV +gss_context_time( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + OM_uint32 *); /* time_rec */ + + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_get_mic( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_qop_t, /* qop_req */ + gss_buffer_t, /* message_buffer */ + gss_buffer_t); /* message_token */ + + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_verify_mic(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_buffer_t, /* message_buffer */ + gss_buffer_t, /* message_token */ + gss_qop_t * /* qop_state */ +); + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_wrap( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* conf_req_flag */ + gss_qop_t, /* qop_req */ + gss_buffer_t, /* input_message_buffer */ + int *, /* conf_state */ + gss_buffer_t); /* output_message_buffer */ + + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_unwrap( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_buffer_t, /* input_message_buffer */ + gss_buffer_t, /* output_message_buffer */ + int *, /* conf_state */ + gss_qop_t *); /* qop_state */ + + +OM_uint32 KRB5_CALLCONV +gss_display_status( + OM_uint32 *, /* minor_status */ + OM_uint32, /* status_value */ + int, /* status_type */ + gss_OID, /* mech_type (used to be const) */ + OM_uint32 *, /* message_context */ + gss_buffer_t); /* status_string */ + + +OM_uint32 KRB5_CALLCONV +gss_indicate_mechs( + OM_uint32 *, /* minor_status */ + gss_OID_set *); /* mech_set */ + + +OM_uint32 KRB5_CALLCONV +gss_compare_name( + OM_uint32 *, /* minor_status */ + gss_name_t, /* name1 */ + gss_name_t, /* name2 */ + int *); /* name_equal */ + + +OM_uint32 KRB5_CALLCONV +gss_display_name( + OM_uint32 *, /* minor_status */ + gss_name_t, /* input_name */ + gss_buffer_t, /* output_name_buffer */ + gss_OID *); /* output_name_type */ + + +OM_uint32 KRB5_CALLCONV +gss_import_name( + OM_uint32 *, /* minor_status */ + gss_buffer_t, /* input_name_buffer */ + gss_OID, /* input_name_type(used to be const) */ + gss_name_t *); /* output_name */ + +OM_uint32 KRB5_CALLCONV +gss_release_name( + OM_uint32 *, /* minor_status */ + gss_name_t *); /* input_name */ + +OM_uint32 KRB5_CALLCONV +gss_release_buffer( + OM_uint32 *, /* minor_status */ + gss_buffer_t); /* buffer */ + +OM_uint32 KRB5_CALLCONV +gss_release_oid_set( + OM_uint32 *, /* minor_status */ + gss_OID_set *); /* set */ + +OM_uint32 KRB5_CALLCONV +gss_inquire_cred( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* cred_handle */ + gss_name_t *, /* name */ + OM_uint32 *, /* lifetime */ + gss_cred_usage_t *, /* cred_usage */ + gss_OID_set *); /* mechanisms */ + +/* Last argument new for V2 */ +OM_uint32 KRB5_CALLCONV +gss_inquire_context( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_name_t *, /* src_name */ + gss_name_t *, /* targ_name */ + OM_uint32 *, /* lifetime_rec */ + gss_OID *, /* mech_type */ + OM_uint32 *, /* ctx_flags */ + int *, /* locally_initiated */ + int *); /* open */ + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_wrap_size_limit( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* conf_req_flag */ + gss_qop_t, /* qop_req */ + OM_uint32, /* req_output_size */ + OM_uint32 *); /* max_input_size */ + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_import_name_object( + OM_uint32 *, /* minor_status */ + void *, /* input_name */ + gss_OID, /* input_name_type */ + gss_name_t *); /* output_name */ + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_export_name_object( + OM_uint32 *, /* minor_status */ + gss_name_t, /* input_name */ + gss_OID, /* desired_name_type */ + void **); /* output_name */ + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_add_cred( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* input_cred_handle */ + gss_name_t, /* desired_name */ + gss_OID, /* desired_mech */ + gss_cred_usage_t, /* cred_usage */ + OM_uint32, /* initiator_time_req */ + OM_uint32, /* acceptor_time_req */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *, /* initiator_time_rec */ + OM_uint32 *); /* acceptor_time_rec */ + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_inquire_cred_by_mech( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* cred_handle */ + gss_OID, /* mech_type */ + gss_name_t *, /* name */ + OM_uint32 *, /* initiator_lifetime */ + OM_uint32 *, /* acceptor_lifetime */ + gss_cred_usage_t *); /* cred_usage */ + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_export_sec_context( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t *, /* context_handle */ + gss_buffer_t); /* interprocess_token */ + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_import_sec_context( + OM_uint32 *, /* minor_status */ + gss_buffer_t, /* interprocess_token */ + gss_ctx_id_t *); /* context_handle */ + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_release_oid( + OM_uint32 *, /* minor_status */ + gss_OID *); /* oid */ + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_create_empty_oid_set( + OM_uint32 *, /* minor_status */ + gss_OID_set *); /* oid_set */ + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_add_oid_set_member( + OM_uint32 *, /* minor_status */ + gss_OID, /* member_oid */ + gss_OID_set *); /* oid_set */ + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_test_oid_set_member( + OM_uint32 *, /* minor_status */ + gss_OID, /* member */ + gss_OID_set, /* set */ + int *); /* present */ + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_str_to_oid( + OM_uint32 *, /* minor_status */ + gss_buffer_t, /* oid_str */ + gss_OID *); /* oid */ + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_oid_to_str( + OM_uint32 *, /* minor_status */ + gss_OID, /* oid */ + gss_buffer_t); /* oid_str */ + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_inquire_names_for_mech( + OM_uint32 *, /* minor_status */ + gss_OID, /* mechanism */ + gss_OID_set *); /* name_types */ + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_inquire_mechs_for_name( + OM_uint32 *, /* minor_status */ + const gss_name_t, /* input_name */ + gss_OID_set *); /* mech_types */ + +/* + * The following routines are obsolete variants of gss_get_mic, gss_wrap, + * gss_verify_mic and gss_unwrap. They should be provided by GSSAPI V2 + * implementations for backwards compatibility with V1 applications. Distinct + * entrypoints (as opposed to #defines) should be provided, to allow GSSAPI + * V1 applications to link against GSSAPI V2 implementations. + */ +OM_uint32 KRB5_CALLCONV +gss_sign( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* qop_req */ + gss_buffer_t, /* message_buffer */ + gss_buffer_t); /* message_token */ + +OM_uint32 KRB5_CALLCONV +gss_verify( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_buffer_t, /* message_buffer */ + gss_buffer_t, /* token_buffer */ + int *); /* qop_state */ + +OM_uint32 KRB5_CALLCONV +gss_seal( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* conf_req_flag */ + int, /* qop_req */ + gss_buffer_t, /* input_message_buffer */ + int *, /* conf_state */ + gss_buffer_t); /* output_message_buffer */ + +OM_uint32 KRB5_CALLCONV +gss_unseal( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_buffer_t, /* input_message_buffer */ + gss_buffer_t, /* output_message_buffer */ + int *, /* conf_state */ + int *); /* qop_state */ + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_export_name( + OM_uint32 *, /* minor_status */ + const gss_name_t, /* input_name */ + gss_buffer_t); /* exported_name */ + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_duplicate_name( + OM_uint32 *, /* minor_status */ + const gss_name_t, /* input_name */ + gss_name_t *); /* dest_name */ + +/* New for V2 */ +OM_uint32 KRB5_CALLCONV +gss_canonicalize_name( + OM_uint32 *, /* minor_status */ + const gss_name_t, /* input_name */ + const gss_OID, /* mech_type */ + gss_name_t *); /* output_name */ + +/* RFC 4401 */ + +#define GSS_C_PRF_KEY_FULL 0 +#define GSS_C_PRF_KEY_PARTIAL 1 + +OM_uint32 KRB5_CALLCONV +gss_pseudo_random( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context */ + int, /* prf_key */ + const gss_buffer_t, /* prf_in */ + ssize_t, /* desired_output_len */ + gss_buffer_t); /* prf_out */ + +OM_uint32 KRB5_CALLCONV +gss_store_cred( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t,/* input_cred_handle */ + gss_cred_usage_t, /* input_usage */ + const gss_OID, /* desired_mech */ + OM_uint32, /* overwrite_cred */ + OM_uint32, /* default_cred */ + gss_OID_set *, /* elements_stored */ + gss_cred_usage_t *);/* cred_usage_stored */ + +OM_uint32 KRB5_CALLCONV +gss_set_neg_mechs( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* cred_handle */ + const gss_OID_set); /* mech_set */ + +#if TARGET_OS_MAC +# pragma pack(pop) +#endif + +#ifdef __cplusplus +} +#endif + +/* XXXX these are not part of the GSSAPI C bindings! (but should be) */ + +#define GSS_CALLING_ERROR_FIELD(x) \ + (((x) >> GSS_C_CALLING_ERROR_OFFSET) & GSS_C_CALLING_ERROR_MASK) +#define GSS_ROUTINE_ERROR_FIELD(x) \ + (((x) >> GSS_C_ROUTINE_ERROR_OFFSET) & GSS_C_ROUTINE_ERROR_MASK) +#define GSS_SUPPLEMENTARY_INFO_FIELD(x) \ + (((x) >> GSS_C_SUPPLEMENTARY_OFFSET) & GSS_C_SUPPLEMENTARY_MASK) + +/* XXXX This is a necessary evil until the spec is fixed */ +#define GSS_S_CRED_UNAVAIL GSS_S_FAILURE + +/* + * RFC 5587 + */ +typedef const gss_buffer_desc *gss_const_buffer_t; +typedef const struct gss_channel_bindings_struct *gss_const_channel_bindings_t; +typedef const struct gss_ctx_id_struct *gss_const_ctx_id_t; +typedef const struct gss_cred_id_struct *gss_const_cred_id_t; +typedef const struct gss_name_struct *gss_const_name_t; +typedef const gss_OID_desc *gss_const_OID; +typedef const gss_OID_set_desc *gss_const_OID_set; + +OM_uint32 KRB5_CALLCONV +gss_indicate_mechs_by_attrs( + OM_uint32 *, /* minor_status */ + gss_const_OID_set, /* desired_mech_attrs */ + gss_const_OID_set, /* except_mech_attrs */ + gss_const_OID_set, /* critical_mech_attrs */ + gss_OID_set *); /* mechs */ + +OM_uint32 KRB5_CALLCONV +gss_inquire_attrs_for_mech( + OM_uint32 *, /* minor_status */ + gss_const_OID, /* mech */ + gss_OID_set *, /* mech_attrs */ + gss_OID_set *); /* known_mech_attrs */ + +OM_uint32 KRB5_CALLCONV +gss_display_mech_attr( + OM_uint32 *, /* minor_status */ + gss_const_OID, /* mech_attr */ + gss_buffer_t, /* name */ + gss_buffer_t, /* short_desc */ + gss_buffer_t); /* long_desc */ + +GSS_DLLIMP extern gss_const_OID GSS_C_MA_MECH_CONCRETE; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_MECH_PSEUDO; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_MECH_COMPOSITE; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_MECH_NEGO; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_MECH_GLUE; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_NOT_MECH; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_DEPRECATED; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_NOT_DFLT_MECH; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_ITOK_FRAMED; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_AUTH_INIT; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_AUTH_TARG; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_AUTH_INIT_INIT; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_AUTH_TARG_INIT; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_AUTH_INIT_ANON; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_AUTH_TARG_ANON; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_DELEG_CRED; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_INTEG_PROT; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_CONF_PROT; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_MIC; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_WRAP; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_PROT_READY; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_REPLAY_DET; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_OOS_DET; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_CBINDINGS; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_PFS; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_COMPRESS; +GSS_DLLIMP extern gss_const_OID GSS_C_MA_CTX_TRANS; + +/* + * RFC 5801 + */ +OM_uint32 KRB5_CALLCONV +gss_inquire_saslname_for_mech( + OM_uint32 *, /* minor_status */ + const gss_OID, /* desired_mech */ + gss_buffer_t, /* sasl_mech_name */ + gss_buffer_t, /* mech_name */ + gss_buffer_t /* mech_description */ +); + +OM_uint32 KRB5_CALLCONV +gss_inquire_mech_for_saslname( + OM_uint32 *, /* minor_status */ + const gss_buffer_t, /* sasl_mech_name */ + gss_OID * /* mech_type */ +); + +#endif /* _GSSAPI_H_ */ diff --git a/src/lib/gssapi/generic/gssapiP_generic.h b/src/lib/gssapi/generic/gssapiP_generic.h new file mode 100644 index 000000000000..686a21772db8 --- /dev/null +++ b/src/lib/gssapi/generic/gssapiP_generic.h @@ -0,0 +1,330 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _GSSAPIP_GENERIC_H_ +#define _GSSAPIP_GENERIC_H_ + +/* + * $Id$ + */ + +#if defined(_WIN32) +#include "k5-int.h" +#else +#include "autoconf.h" +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#endif + +#include "k5-thread.h" + +#include "gssapi_generic.h" +#include "gssapi_ext.h" +#include <gssapi/gssapi_alloc.h> +#include "gssapi_err_generic.h" +#include <errno.h> + +#include "k5-platform.h" +#include "k5-buf.h" + +/** helper macros **/ + +#define g_OID_equal(o1, o2) \ + (((o1)->length == (o2)->length) && \ + (memcmp((o1)->elements, (o2)->elements, (o1)->length) == 0)) + +/* this code knows that an int on the wire is 32 bits. The type of + num should be at least this big, or the extra shifts may do weird + things */ + +#define TWRITE_INT(ptr, num, bigend) \ + if (bigend) store_32_be(num, ptr); else store_32_le(num, ptr); \ + (ptr) += 4; + +#define TWRITE_INT16(ptr, num, bigend) \ + if (bigend) store_16_be((num)>>16, ptr); else store_16_le(num, ptr); \ + (ptr) += 2; + +#define TREAD_INT(ptr, num, bigend) \ + (num) = ((bigend) ? load_32_be(ptr) : load_32_le(ptr)); \ + (ptr) += 4; + +#define TREAD_INT16(ptr, num, bigend) \ + (num) = ((bigend) ? (load_16_be(ptr) << 16) : load_16_le(ptr)); \ + (ptr) += 2; + +#define TWRITE_STR(ptr, str, len) \ + memcpy((ptr), (str), (len)); \ + (ptr) += (len); + +#define TREAD_STR(ptr, str, len) \ + (str) = (ptr); \ + (ptr) += (len); + +#define TWRITE_BUF(ptr, buf, bigend) \ + TWRITE_INT((ptr), (buf).length, (bigend)); \ + TWRITE_STR((ptr), (buf).value, (buf).length); + +/** malloc wrappers; these may actually do something later */ + +#define xmalloc(n) malloc(n) +#define xrealloc(p,n) realloc(p,n) +#ifdef xfree +#undef xfree +#endif +#define xfree(p) free(p) + +/** helper functions **/ + +/* hide names from applications, especially glib applications */ +#define g_set_init gssint_g_set_init +#define g_set_destroy gssint_g_set_destroy +#define g_set_entry_add gssint_g_set_entry_add +#define g_set_entry_delete gssint_g_set_entry_delete +#define g_set_entry_get gssint_g_set_entry_get +#define g_make_string_buffer gssint_g_make_string_buffer +#define g_token_size gssint_g_token_size +#define g_make_token_header gssint_g_make_token_header +#define g_verify_token_header gssint_g_verify_token_header +#define g_display_major_status gssint_g_display_major_status +#define g_display_com_err_status gssint_g_display_com_err_status +#define g_seqstate_init gssint_g_seqstate_init +#define g_seqstate_check gssint_g_seqstate_check +#define g_seqstate_free gssint_g_seqstate_free +#define g_seqstate_size gssint_g_seqstate_size +#define g_seqstate_externalize gssint_g_seqstate_externalize +#define g_seqstate_internalize gssint_g_seqstate_internalize +#define g_canonicalize_host gssint_g_canonicalize_host +#define g_local_host_name gssint_g_local_host_name +#define g_strdup gssint_g_strdup + +typedef struct _g_set_elt *g_set_elt; +typedef struct { + k5_mutex_t mutex; + void *data; +} g_set; +#define G_SET_INIT { K5_MUTEX_PARTIAL_INITIALIZER, 0 } + +typedef struct g_seqnum_state_st *g_seqnum_state; + +int g_set_init (g_set_elt *s); +int g_set_destroy (g_set_elt *s); +int g_set_entry_add (g_set_elt *s, void *key, void *value); +int g_set_entry_delete (g_set_elt *s, void *key); +int g_set_entry_get (g_set_elt *s, void *key, void **value); + +int g_save_name (g_set *vdb, gss_name_t name); +int g_save_cred_id (g_set *vdb, gss_cred_id_t cred); +int g_save_ctx_id (g_set *vdb, gss_ctx_id_t ctx); +int g_save_lucidctx_id (g_set *vdb, void *lctx); + +int g_validate_name (g_set *vdb, gss_name_t name); +int g_validate_cred_id (g_set *vdb, gss_cred_id_t cred); +int g_validate_ctx_id (g_set *vdb, gss_ctx_id_t ctx); +int g_validate_lucidctx_id (g_set *vdb, void *lctx); + +int g_delete_name (g_set *vdb, gss_name_t name); +int g_delete_cred_id (g_set *vdb, gss_cred_id_t cred); +int g_delete_ctx_id (g_set *vdb, gss_ctx_id_t ctx); +int g_delete_lucidctx_id (g_set *vdb, void *lctx); + +int g_make_string_buffer (const char *str, gss_buffer_t buffer); + +unsigned int g_token_size (const gss_OID_desc * mech, unsigned int body_size); + +void g_make_token_header (const gss_OID_desc * mech, unsigned int body_size, + unsigned char **buf, int tok_type); + +/* flags for g_verify_token_header() */ +#define G_VFY_TOKEN_HDR_WRAPPER_REQUIRED 0x01 + +gss_int32 g_verify_token_header (const gss_OID_desc * mech, + unsigned int *body_size, + unsigned char **buf, int tok_type, + unsigned int toksize_in, + int flags); + +OM_uint32 g_display_major_status (OM_uint32 *minor_status, + OM_uint32 status_value, + OM_uint32 *message_context, + gss_buffer_t status_string); + +OM_uint32 g_display_com_err_status (OM_uint32 *minor_status, + OM_uint32 status_value, + gss_buffer_t status_string); + +long g_seqstate_init(g_seqnum_state *state_out, uint64_t seqnum, + int do_replay, int do_sequence, int wide); +OM_uint32 g_seqstate_check(g_seqnum_state state, uint64_t seqnum); +void g_seqstate_free(g_seqnum_state state); +void g_seqstate_size(g_seqnum_state state, size_t *sizep); +long g_seqstate_externalize(g_seqnum_state state, unsigned char **buf, + size_t *lenremain); +long g_seqstate_internalize(g_seqnum_state *state_out, unsigned char **buf, + size_t *lenremain); + +char *g_strdup (char *str); + +/** declarations of internal name mechanism functions **/ + +OM_uint32 +generic_gss_release_buffer( + OM_uint32 *, /* minor_status */ + gss_buffer_t); /* buffer */ + +OM_uint32 +generic_gss_release_oid_set( + OM_uint32 *, /* minor_status */ + gss_OID_set *); /* set */ + +OM_uint32 +generic_gss_release_oid( + OM_uint32 *, /* minor_status */ + gss_OID *); /* set */ + +OM_uint32 +generic_gss_copy_oid( + OM_uint32 *, /* minor_status */ + const gss_OID_desc * const, /* oid */ + gss_OID *); /* new_oid */ + +OM_uint32 +generic_gss_create_empty_oid_set( + OM_uint32 *, /* minor_status */ + gss_OID_set *); /* oid_set */ + +OM_uint32 +generic_gss_add_oid_set_member( + OM_uint32 *, /* minor_status */ + const gss_OID_desc * const, /* member_oid */ + gss_OID_set *); /* oid_set */ + +OM_uint32 +generic_gss_test_oid_set_member( + OM_uint32 *, /* minor_status */ + const gss_OID_desc * const, /* member */ + gss_OID_set, /* set */ + int *); /* present */ + +OM_uint32 +generic_gss_oid_to_str( + OM_uint32 *, /* minor_status */ + const gss_OID_desc * const, /* oid */ + gss_buffer_t); /* oid_str */ + +OM_uint32 +generic_gss_str_to_oid( + OM_uint32 *, /* minor_status */ + gss_buffer_t, /* oid_str */ + gss_OID *); /* oid */ + +OM_uint32 +generic_gss_oid_compose( + OM_uint32 *, /* minor_status */ + const char *, /* prefix */ + size_t, /* prefix_len */ + int, /* suffix */ + gss_OID_desc *); /* oid */ + +OM_uint32 +generic_gss_oid_decompose( + OM_uint32 *, /* minor_status */ + const char *, /*prefix */ + size_t, /* prefix_len */ + gss_OID_desc *, /* oid */ + int *); /* suffix */ + +int gssint_mecherrmap_init(void); +void gssint_mecherrmap_destroy(void); +OM_uint32 gssint_mecherrmap_map(OM_uint32 minor, const gss_OID_desc *oid); +int gssint_mecherrmap_get(OM_uint32 minor, gss_OID mech_oid, + OM_uint32 *mech_minor); +OM_uint32 gssint_mecherrmap_map_errcode(OM_uint32 errcode); + +/* + * Transfer contents of a k5buf to a gss_buffer and invalidate the source + * On unix, this is a simple pointer copy + * On windows, memory is reallocated and copied. + */ +static inline OM_uint32 +k5buf_to_gss(OM_uint32 *minor, + struct k5buf *input_k5buf, + gss_buffer_t output_buffer) +{ + OM_uint32 status = GSS_S_COMPLETE; + + if (k5_buf_status(input_k5buf) != 0) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + output_buffer->length = input_k5buf->len; +#if defined(_WIN32) || defined(DEBUG_GSSALLOC) + if (output_buffer->length > 0) { + output_buffer->value = gssalloc_malloc(output_buffer->length); + if (output_buffer->value) { + memcpy(output_buffer->value, input_k5buf->data, + output_buffer->length); + } else { + status = GSS_S_FAILURE; + *minor = ENOMEM; + } + } else { + output_buffer->value = NULL; + } + k5_buf_free(input_k5buf); +#else + output_buffer->value = input_k5buf->data; + memset(input_k5buf, 0, sizeof(*input_k5buf)); +#endif + return status; +} + +OM_uint32 generic_gss_create_empty_buffer_set +(OM_uint32 * /*minor_status*/, + gss_buffer_set_t * /*buffer_set*/); + +OM_uint32 generic_gss_add_buffer_set_member +(OM_uint32 * /*minor_status*/, + const gss_buffer_t /*member_buffer*/, + gss_buffer_set_t * /*buffer_set*/); + +OM_uint32 generic_gss_release_buffer_set +(OM_uint32 * /*minor_status*/, + gss_buffer_set_t * /*buffer_set*/); + +OM_uint32 generic_gss_copy_oid_set +(OM_uint32 *, /* minor_status */ + const gss_OID_set_desc * const /*oidset*/, + gss_OID_set * /*new_oidset*/); + +extern gss_OID_set gss_ma_known_attrs; + +OM_uint32 generic_gss_display_mech_attr( + OM_uint32 *minor_status, + gss_const_OID mech_attr, + gss_buffer_t name, + gss_buffer_t short_desc, + gss_buffer_t long_desc); + +#endif /* _GSSAPIP_GENERIC_H_ */ diff --git a/src/lib/gssapi/generic/gssapi_alloc.h b/src/lib/gssapi/generic/gssapi_alloc.h new file mode 100644 index 000000000000..9a5cd9892c2c --- /dev/null +++ b/src/lib/gssapi/generic/gssapi_alloc.h @@ -0,0 +1,128 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* To the extent possible under law, Painless Security, LLC has waived + * all copyright and related or neighboring rights to GSS-API Memory + * Management Header. This work is published from: United States. + */ + +#ifndef GSSAPI_ALLOC_H +#define GSSAPI_ALLOC_H + +#ifdef _WIN32 +#include "winbase.h" +#endif +#include <string.h> + +#if defined(_WIN32) + +static inline void +gssalloc_free(void *value) +{ + if (value) + HeapFree(GetProcessHeap(), 0, value); +} + +static inline void * +gssalloc_malloc(size_t size) +{ + return HeapAlloc(GetProcessHeap(), 0, size); +} + +static inline void * +gssalloc_calloc(size_t count, size_t size) +{ + return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, count * size); +} + +static inline void * +gssalloc_realloc(void *value, size_t size) +{ + return HeapReAlloc(GetProcessHeap(), 0, value, size); +} + +#elif defined(DEBUG_GSSALLOC) + +/* Be deliberately incompatible with malloc and free, to allow us to detect + * mismatched malloc/gssalloc usage on Unix. */ + +static inline void +gssalloc_free(void *value) +{ + char *p = (char *)value - 8; + + if (value == NULL) + return; + if (memcmp(p, "gssalloc", 8) != 0) + abort(); + free(p); +} + +static inline void * +gssalloc_malloc(size_t size) +{ + char *p = calloc(size + 8, 1); + + memcpy(p, "gssalloc", 8); + return p + 8; +} + +static inline void * +gssalloc_calloc(size_t count, size_t size) +{ + return gssalloc_malloc(count * size); +} + +static inline void * +gssalloc_realloc(void *value, size_t size) +{ + char *p = (char *)value - 8; + + if (value == NULL) + return gssalloc_malloc(size); + if (memcmp(p, "gssalloc", 8) != 0) + abort(); + return (char *)realloc(p, size) + 8; +} + +#else /* not _WIN32 or DEBUG_GSSALLOC */ + +/* Normal Unix case, just use free/malloc/calloc/realloc. */ + +static inline void +gssalloc_free(void *value) +{ + free(value); +} + +static inline void * +gssalloc_malloc(size_t size) +{ + return malloc(size); +} + +static inline void * +gssalloc_calloc(size_t count, size_t size) +{ + return calloc(count, size); +} + +static inline void * +gssalloc_realloc(void *value, size_t size) +{ + return realloc(value, size); +} + +#endif /* not _WIN32 or DEBUG_GSSALLOC */ + +static inline char * +gssalloc_strdup(const char *str) +{ + size_t size = strlen(str)+1; + char *copy = gssalloc_malloc(size); + if (copy) { + memcpy(copy, str, size); + copy[size-1] = '\0'; + } + return copy; +} + +#endif diff --git a/src/lib/gssapi/generic/gssapi_err_generic.et b/src/lib/gssapi/generic/gssapi_err_generic.et new file mode 100644 index 000000000000..3e976e3dbf50 --- /dev/null +++ b/src/lib/gssapi/generic/gssapi_err_generic.et @@ -0,0 +1,49 @@ +# +# Copyright 1993 by OpenVision Technologies, Inc. +# +# Permission to use, copy, modify, distribute, and sell this software +# and its documentation for any purpose is hereby granted without fee, +# provided that the above copyright notice appears in all copies and +# that both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of OpenVision not be used +# in advertising or publicity pertaining to distribution of the software +# without specific, written prior permission. OpenVision makes no +# representations about the suitability of this software for any +# purpose. It is provided "as is" without express or implied warranty. +# +# OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +# EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR +# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +# USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +# + +# +# $Id$ +# + +error_table ggss + +error_code G_BAD_SERVICE_NAME, "No @ in SERVICE-NAME name string" +error_code G_BAD_STRING_UID, "STRING-UID-NAME contains nondigits" +error_code G_NOUSER, "UID does not resolve to username" +error_code G_VALIDATE_FAILED, "Validation error" +error_code G_BUFFER_ALLOC, "Couldn't allocate gss_buffer_t data" +error_code G_BAD_MSG_CTX, "Message context invalid" +error_code G_WRONG_SIZE, "Buffer is the wrong size" +error_code G_BAD_USAGE, "Credential usage type is unknown" +error_code G_UNKNOWN_QOP, "Unknown quality of protection specified" +error_code G_NO_HOSTNAME, "Local host name could not be determined" +error_code G_BAD_HOSTNAME, "Hostname in SERVICE-NAME string could not be canonicalized" +error_code G_WRONG_MECH, "Mechanism is incorrect" +error_code G_BAD_TOK_HEADER, "Token header is malformed or corrupt" +error_code G_BAD_DIRECTION, "Packet was replayed in wrong direction" +error_code G_TOK_TRUNC, "Token is missing data" +error_code G_REFLECT, "Token was reflected" +error_code G_WRONG_TOKID, "Received token ID does not match expected token ID" +error_code G_CRED_USAGE_MISMATCH, "The given credential's usage does not match the requested usage" +error_code G_STORE_ACCEPTOR_CRED_NOSUPP, "Storing of acceptor credentials is not supported by the mechanism" +error_code G_STORE_NON_DEFAULT_CRED_NOSUPP, "Storing of non-default credentials is not supported by the mechanism" +end diff --git a/src/lib/gssapi/generic/gssapi_ext.h b/src/lib/gssapi/generic/gssapi_ext.h new file mode 100644 index 000000000000..9ad44216d05e --- /dev/null +++ b/src/lib/gssapi/generic/gssapi_ext.h @@ -0,0 +1,578 @@ +/* + * Copyright 2008 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. + */ + +#ifndef GSSAPI_EXT_H_ +#define GSSAPI_EXT_H_ + +#include <gssapi/gssapi.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Solaris extensions + */ +#ifndef _WIN32 +OM_uint32 KRB5_CALLCONV +gss_pname_to_uid + (OM_uint32 *minor, + const gss_name_t name, + const gss_OID mech_type, + uid_t *uidOut); +#endif + +/** + * Provides a platform-specific name for a GSSAPI name as interpreted by a + * given mechanism. + * + * @param [out] minor Minor status code + * @param [in] name The gss name resulting from accept_sec_context + * @param [in] mech_type The mechanism that will be asked to map @a name to a + * local name + * @param [out] localname Caller-allocated buffer to be filled in with the + * local name on success + */ +OM_uint32 KRB5_CALLCONV +gss_localname + (OM_uint32 *minor, + const gss_name_t name, + gss_const_OID mech_type, + gss_buffer_t localname); + +/** + * Determine whether a mechanism name is authorized to act as a username. + * + * @param [in] name Mechanism name + * @param [in] username System username + * + * This is a simple wrapper around gss_authorize_localname(). It only supports + * system usernames as local names, and cannot distinguish between lack of + * authorization and other errors. + * + * @retval 1 @a name is authorized to act as @a username + * @retval 0 @a name is not authorized or an error occurred + */ +int KRB5_CALLCONV +gss_userok(const gss_name_t name, + const char *username); + +/** + * Determine whether a mechanism name is authorized to act as a local name. + * + * @param [out] minor Minor status code + * @param [in] name Mechanism name + * @param [in] user Local name + * + * @a name is a mechanism name, typically the result of a completed + * gss_accept_sec_context(). @a user is an internal name representing a local + * name, such as a name imported by gss_import_name() with an @a + * input_name_type of @c GSS_C_NT_USER_NAME. + * + * @return Return GSS_S_COMPLETE if @a name is authorized to act as @a user, + * GSS_S_UNAUTHORIZED if not, or an appropriate GSS error code if an error + * occured. + * + * @sa gss_userok + */ +OM_uint32 KRB5_CALLCONV +gss_authorize_localname(OM_uint32 *minor, + const gss_name_t name, + const gss_name_t user); + +OM_uint32 KRB5_CALLCONV +gss_acquire_cred_with_password( + OM_uint32 *, /* minor_status */ + const gss_name_t, /* desired_name */ + const gss_buffer_t, /* password */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *); /* time_rec */ + +OM_uint32 KRB5_CALLCONV +gss_add_cred_with_password( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t,/* input_cred_handle */ + const gss_name_t, /* desired_name */ + const gss_OID, /* desired_mech */ + const gss_buffer_t, /* password */ + gss_cred_usage_t, /* cred_usage */ + OM_uint32, /* initiator_time_req */ + OM_uint32, /* acceptor_time_req */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *, /* initiator_time_rec */ + OM_uint32 *); /* acceptor_time_rec */ + +/* + * GGF extensions + */ +typedef struct gss_buffer_set_desc_struct { + size_t count; + gss_buffer_desc *elements; +} gss_buffer_set_desc, *gss_buffer_set_t; + +#define GSS_C_NO_BUFFER_SET ((gss_buffer_set_t) 0) + +OM_uint32 KRB5_CALLCONV gss_create_empty_buffer_set + (OM_uint32 * /*minor_status*/, + gss_buffer_set_t * /*buffer_set*/); + +OM_uint32 KRB5_CALLCONV gss_add_buffer_set_member + (OM_uint32 * /*minor_status*/, + const gss_buffer_t /*member_buffer*/, + gss_buffer_set_t * /*buffer_set*/); + +OM_uint32 KRB5_CALLCONV gss_release_buffer_set + (OM_uint32 * /*minor_status*/, + gss_buffer_set_t * /*buffer_set*/); + +OM_uint32 KRB5_CALLCONV gss_inquire_sec_context_by_oid + (OM_uint32 * /*minor_status*/, + const gss_ctx_id_t /*context_handle*/, + const gss_OID /*desired_object*/, + gss_buffer_set_t * /*data_set*/); + +OM_uint32 KRB5_CALLCONV gss_inquire_cred_by_oid + (OM_uint32 * /*minor_status*/, + const gss_cred_id_t /*cred_handle*/, + const gss_OID /*desired_object*/, + gss_buffer_set_t * /*data_set*/); + +OM_uint32 KRB5_CALLCONV gss_set_sec_context_option + (OM_uint32 * /*minor_status*/, + gss_ctx_id_t * /*cred_handle*/, + const gss_OID /*desired_object*/, + const gss_buffer_t /*value*/); + +OM_uint32 KRB5_CALLCONV gss_set_cred_option + (OM_uint32 * /*minor_status*/, + gss_cred_id_t * /*cred*/, + const gss_OID /*desired_object*/, + const gss_buffer_t /*value*/); + +OM_uint32 KRB5_CALLCONV gssspi_mech_invoke + (OM_uint32 * /*minor_status*/, + const gss_OID /*desired_mech*/, + const gss_OID /*desired_object*/, + gss_buffer_t /*value*/); + +/* + * AEAD extensions + */ + +OM_uint32 KRB5_CALLCONV gss_wrap_aead + (OM_uint32 * /*minor_status*/, + gss_ctx_id_t /*context_handle*/, + int /*conf_req_flag*/, + gss_qop_t /*qop_req*/, + gss_buffer_t /*input_assoc_buffer*/, + gss_buffer_t /*input_payload_buffer*/, + int * /*conf_state*/, + gss_buffer_t /*output_message_buffer*/); + +OM_uint32 KRB5_CALLCONV gss_unwrap_aead + (OM_uint32 * /*minor_status*/, + gss_ctx_id_t /*context_handle*/, + gss_buffer_t /*input_message_buffer*/, + gss_buffer_t /*input_assoc_buffer*/, + gss_buffer_t /*output_payload_buffer*/, + int * /*conf_state*/, + gss_qop_t * /*qop_state*/); + +/* + * SSPI extensions + */ +#define GSS_C_DCE_STYLE 0x1000 +#define GSS_C_IDENTIFY_FLAG 0x2000 +#define GSS_C_EXTENDED_ERROR_FLAG 0x4000 + +/* + * Returns a buffer set with the first member containing the + * session key for SSPI compatibility. The optional second + * member contains an OID identifying the session key type. + */ +GSS_DLLIMP extern gss_OID GSS_C_INQ_SSPI_SESSION_KEY; + +OM_uint32 KRB5_CALLCONV gss_complete_auth_token + (OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + gss_buffer_t input_message_buffer); + +typedef struct gss_iov_buffer_desc_struct { + OM_uint32 type; + gss_buffer_desc buffer; +} gss_iov_buffer_desc, *gss_iov_buffer_t; + +#define GSS_C_NO_IOV_BUFFER ((gss_iov_buffer_t)0) + +#define GSS_IOV_BUFFER_TYPE_EMPTY 0 +#define GSS_IOV_BUFFER_TYPE_DATA 1 /* Packet data */ +#define GSS_IOV_BUFFER_TYPE_HEADER 2 /* Mechanism header */ +#define GSS_IOV_BUFFER_TYPE_MECH_PARAMS 3 /* Mechanism specific parameters */ +#define GSS_IOV_BUFFER_TYPE_TRAILER 7 /* Mechanism trailer */ +#define GSS_IOV_BUFFER_TYPE_PADDING 9 /* Padding */ +#define GSS_IOV_BUFFER_TYPE_STREAM 10 /* Complete wrap token */ +#define GSS_IOV_BUFFER_TYPE_SIGN_ONLY 11 /* Sign only packet data */ +#define GSS_IOV_BUFFER_TYPE_MIC_TOKEN 12 /* MIC token destination */ + +#define GSS_IOV_BUFFER_FLAG_MASK 0xFFFF0000 +#define GSS_IOV_BUFFER_FLAG_ALLOCATE 0x00010000 /* indicates GSS should allocate */ +#define GSS_IOV_BUFFER_FLAG_ALLOCATED 0x00020000 /* indicates caller should free */ + +#define GSS_IOV_BUFFER_TYPE(_type) ((_type) & ~(GSS_IOV_BUFFER_FLAG_MASK)) +#define GSS_IOV_BUFFER_FLAGS(_type) ((_type) & GSS_IOV_BUFFER_FLAG_MASK) + +/* + * Sign and optionally encrypt a sequence of buffers. The buffers + * shall be ordered HEADER | DATA | PADDING | TRAILER. Suitable + * space for the header, padding and trailer should be provided + * by calling gss_wrap_iov_length(), or the ALLOCATE flag should + * be set on those buffers. + * + * Encryption is in-place. SIGN_ONLY buffers are untouched. Only + * a single PADDING buffer should be provided. The order of the + * buffers in memory does not matter. Buffers in the IOV should + * be arranged in the order above, and in the case of multiple + * DATA buffers the sender and receiver should agree on the + * order. + * + * With GSS_C_DCE_STYLE it is acceptable to not provide PADDING + * and TRAILER, but the caller must guarantee the plaintext data + * being encrypted is correctly padded, otherwise an error will + * be returned. + * + * While applications that have knowledge of the underlying + * cryptosystem may request a specific configuration of data + * buffers, the only generally supported configurations are: + * + * HEADER | DATA | PADDING | TRAILER + * + * which will emit GSS_Wrap() compatible tokens, and: + * + * HEADER | SIGN_ONLY | DATA | PADDING | TRAILER + * + * for AEAD. + * + * The typical (special cased) usage for DCE is as follows: + * + * SIGN_ONLY_1 | DATA | SIGN_ONLY_2 | HEADER + */ +OM_uint32 KRB5_CALLCONV gss_wrap_iov +( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* conf_req_flag */ + gss_qop_t, /* qop_req */ + int *, /* conf_state */ + gss_iov_buffer_desc *, /* iov */ + int); /* iov_count */ + +/* + * Verify and optionally decrypt a sequence of buffers. To process + * a GSS-API message without separate buffer, pass STREAM | DATA. + * Upon return DATA will contain the decrypted or integrity + * protected message. Only a single DATA buffer may be provided + * with this usage. DATA by default will point into STREAM, but if + * the ALLOCATE flag is set a copy will be returned. + * + * Otherwise, decryption is in-place. SIGN_ONLY buffers are + * untouched. + */ +OM_uint32 KRB5_CALLCONV gss_unwrap_iov +( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int *, /* conf_state */ + gss_qop_t *, /* qop_state */ + gss_iov_buffer_desc *, /* iov */ + int); /* iov_count */ + +/* + * Query HEADER, PADDING and TRAILER buffer lengths. DATA buffers + * should be provided so the correct padding length can be determined. + */ +OM_uint32 KRB5_CALLCONV gss_wrap_iov_length +( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* conf_req_flag */ + gss_qop_t, /* qop_req */ + int *, /* conf_state */ + gss_iov_buffer_desc *, /* iov */ + int); /* iov_count */ + +/* + * Produce a GSSAPI MIC token for a sequence of buffers. All SIGN_ONLY and + * DATA buffers will be signed, in the order they appear. One MIC_TOKEN buffer + * must be included for the result. Suitable space should be provided for the + * MIC_TOKEN buffer by calling gss_get_mic_iov_length, or the ALLOCATE flag + * should be set on that buffer. If the ALLOCATE flag is used, use + * gss_release_iov_buffer to free the allocated buffer within the iov list when + * it is no longer needed. + */ +OM_uint32 KRB5_CALLCONV gss_get_mic_iov +( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_qop_t, /* qop_req */ + gss_iov_buffer_desc *, /* iov */ + int); /* iov_count */ + +/* + * Query the MIC_TOKEN buffer length within the iov list. + */ +OM_uint32 KRB5_CALLCONV gss_get_mic_iov_length( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_qop_t, /* qop_req */ + gss_iov_buffer_desc *, /* iov */ + int); /* iov_count */ + +/* + * Verify the MIC_TOKEN buffer within the iov list against the SIGN_ONLY and + * DATA buffers in the order they appear. Return values are the same as for + * gss_verify_mic. + */ +OM_uint32 KRB5_CALLCONV gss_verify_mic_iov +( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_qop_t *, /* qop_state */ + gss_iov_buffer_desc *, /* iov */ + int); /* iov_count */ + +/* + * Release buffers that have the ALLOCATED flag set. + */ +OM_uint32 KRB5_CALLCONV gss_release_iov_buffer +( + OM_uint32 *, /* minor_status */ + gss_iov_buffer_desc *, /* iov */ + int); /* iov_count */ + +/* + * Protocol transition + */ +OM_uint32 KRB5_CALLCONV +gss_acquire_cred_impersonate_name( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *); /* time_rec */ + +OM_uint32 KRB5_CALLCONV +gss_add_cred_impersonate_name( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* input_cred_handle */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_name_t, /* desired_name */ + const gss_OID, /* desired_mech */ + gss_cred_usage_t, /* cred_usage */ + OM_uint32, /* initiator_time_req */ + OM_uint32, /* acceptor_time_req */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *, /* initiator_time_rec */ + OM_uint32 *); /* acceptor_time_rec */ + +/* + * Naming extensions + */ +GSS_DLLIMP extern gss_buffer_t GSS_C_ATTR_LOCAL_LOGIN_USER; +GSS_DLLIMP extern gss_OID GSS_C_NT_COMPOSITE_EXPORT; + +OM_uint32 KRB5_CALLCONV gss_display_name_ext +( + OM_uint32 *, /* minor_status */ + gss_name_t, /* name */ + gss_OID, /* display_as_name_type */ + gss_buffer_t /* display_name */ +); + +OM_uint32 KRB5_CALLCONV gss_inquire_name +( + OM_uint32 *, /* minor_status */ + gss_name_t, /* name */ + int *, /* name_is_MN */ + gss_OID *, /* MN_mech */ + gss_buffer_set_t * /* attrs */ +); + +OM_uint32 KRB5_CALLCONV gss_get_name_attribute +( + OM_uint32 *, /* minor_status */ + gss_name_t, /* name */ + gss_buffer_t, /* attr */ + int *, /* authenticated */ + int *, /* complete */ + gss_buffer_t, /* value */ + gss_buffer_t, /* display_value */ + int * /* more */ +); + +OM_uint32 KRB5_CALLCONV gss_set_name_attribute +( + OM_uint32 *, /* minor_status */ + gss_name_t, /* name */ + int, /* complete */ + gss_buffer_t, /* attr */ + gss_buffer_t /* value */ +); + +OM_uint32 KRB5_CALLCONV gss_delete_name_attribute +( + OM_uint32 *, /* minor_status */ + gss_name_t, /* name */ + gss_buffer_t /* attr */ +); + +OM_uint32 KRB5_CALLCONV gss_export_name_composite +( + OM_uint32 *, /* minor_status */ + gss_name_t, /* name */ + gss_buffer_t /* exp_composite_name */ +); + +typedef struct gss_any *gss_any_t; + +OM_uint32 KRB5_CALLCONV gss_map_name_to_any +( + OM_uint32 *, /* minor_status */ + gss_name_t, /* name */ + int, /* authenticated */ + gss_buffer_t, /* type_id */ + gss_any_t * /* output */ +); + +OM_uint32 KRB5_CALLCONV gss_release_any_name_mapping +( + OM_uint32 *, /* minor_status */ + gss_name_t, /* name */ + gss_buffer_t, /* type_id */ + gss_any_t * /* input */ +); + +/* draft-josefsson-gss-capsulate */ +OM_uint32 KRB5_CALLCONV gss_encapsulate_token +( + gss_const_buffer_t, /* input_token */ + gss_const_OID, /* token_oid */ + gss_buffer_t /* output_token */ +); + +OM_uint32 KRB5_CALLCONV gss_decapsulate_token +( + gss_const_buffer_t, /* input_token */ + gss_const_OID, /* token_oid */ + gss_buffer_t /* output_token */ +); + +int KRB5_CALLCONV gss_oid_equal +( + gss_const_OID, /* first_oid */ + gss_const_OID /* second_oid */ +); + +/* Credential store extensions */ + +struct gss_key_value_element_struct { + const char *key; + const char *value; +}; +typedef struct gss_key_value_element_struct gss_key_value_element_desc; + +struct gss_key_value_set_struct { + OM_uint32 count; + gss_key_value_element_desc *elements; +}; +typedef struct gss_key_value_set_struct gss_key_value_set_desc; +typedef const gss_key_value_set_desc *gss_const_key_value_set_t; + +#define GSS_C_NO_CRED_STORE ((gss_const_key_value_set_t) 0) + +OM_uint32 KRB5_CALLCONV +gss_acquire_cred_from( + OM_uint32 *, /* minor_status */ + gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_const_key_value_set_t, /* cred_store */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *); /* time_rec */ + +OM_uint32 KRB5_CALLCONV +gss_add_cred_from( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* input_cred_handle */ + gss_name_t, /* desired_name */ + gss_OID, /* desired_mech */ + gss_cred_usage_t, /* cred_usage */ + OM_uint32, /* initiator_time_req */ + OM_uint32, /* acceptor_time_req */ + gss_const_key_value_set_t, /* cred_store */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *, /* initiator_time_rec */ + OM_uint32 *); /* acceptor_time_rec */ + +OM_uint32 KRB5_CALLCONV +gss_store_cred_into( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* input_cred_handle */ + gss_cred_usage_t, /* input_usage */ + gss_OID, /* desired_mech */ + OM_uint32, /* overwrite_cred */ + OM_uint32, /* default_cred */ + gss_const_key_value_set_t, /* cred_store */ + gss_OID_set *, /* elements_stored */ + gss_cred_usage_t *); /* cred_usage_stored */ + +OM_uint32 KRB5_CALLCONV +gss_export_cred( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* cred_handle */ + gss_buffer_t); /* token */ + +OM_uint32 KRB5_CALLCONV +gss_import_cred( + OM_uint32 *, /* minor_status */ + gss_buffer_t, /* token */ + gss_cred_id_t *); /* cred_handle */ + +#ifdef __cplusplus +} +#endif + +#endif /* GSSAPI_EXT_H_ */ diff --git a/src/lib/gssapi/generic/gssapi_generic.c b/src/lib/gssapi/generic/gssapi_generic.c new file mode 100644 index 000000000000..5496aa33582c --- /dev/null +++ b/src/lib/gssapi/generic/gssapi_generic.c @@ -0,0 +1,450 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * $Id$ + */ + +#include "gssapiP_generic.h" + +/* + * See krb5/gssapi_krb5.c for a description of the algorithm for + * encoding an object identifier. + */ + +/* Reserved static storage for GSS_oids. Comments are quotes from RFC 2744. */ + +#define oids ((gss_OID_desc *)const_oids) +static const gss_OID_desc const_oids[] = { + /* + * The implementation must reserve static storage for a + * gss_OID_desc object containing the value */ + {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01"}, + /* corresponding to an object-identifier value of + * {iso(1) member-body(2) United States(840) mit(113554) + * infosys(1) gssapi(2) generic(1) user_name(1)}. The constant + * GSS_C_NT_USER_NAME should be initialized to point + * to that gss_OID_desc. + */ + + /* + * The implementation must reserve static storage for a + * gss_OID_desc object containing the value */ + {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"}, + /* corresponding to an object-identifier value of + * {iso(1) member-body(2) United States(840) mit(113554) + * infosys(1) gssapi(2) generic(1) machine_uid_name(2)}. + * The constant GSS_C_NT_MACHINE_UID_NAME should be + * initialized to point to that gss_OID_desc. + */ + + /* + * The implementation must reserve static storage for a + * gss_OID_desc object containing the value */ + {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03"}, + /* corresponding to an object-identifier value of + * {iso(1) member-body(2) United States(840) mit(113554) + * infosys(1) gssapi(2) generic(1) string_uid_name(3)}. + * The constant GSS_C_NT_STRING_UID_NAME should be + * initialized to point to that gss_OID_desc. + */ + + /* + * The implementation must reserve static storage for a + * gss_OID_desc object containing the value */ + {6, (void *)"\x2b\x06\x01\x05\x06\x02"}, + /* corresponding to an object-identifier value of + * {iso(1) org(3) dod(6) internet(1) security(5) + * nametypes(6) gss-host-based-services(2)). The constant + * GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point + * to that gss_OID_desc. This is a deprecated OID value, and + * implementations wishing to support hostbased-service names + * should instead use the GSS_C_NT_HOSTBASED_SERVICE OID, + * defined below, to identify such names; + * GSS_C_NT_HOSTBASED_SERVICE_X should be accepted a synonym + * for GSS_C_NT_HOSTBASED_SERVICE when presented as an input + * parameter, but should not be emitted by GSS-API + * implementations + */ + + /* + * The implementation must reserve static storage for a + * gss_OID_desc object containing the value */ + {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"}, + /* corresponding to an object-identifier value of + * {iso(1) member-body(2) Unites States(840) mit(113554) + * infosys(1) gssapi(2) generic(1) service_name(4)}. + * The constant GSS_C_NT_HOSTBASED_SERVICE should be + * initialized to point to that gss_OID_desc. + */ + + /* + * The implementation must reserve static storage for a + * gss_OID_desc object containing the value */ + {6, (void *)"\x2b\x06\01\x05\x06\x03"}, + /* corresponding to an object identifier value of + * {1(iso), 3(org), 6(dod), 1(internet), 5(security), + * 6(nametypes), 3(gss-anonymous-name)}. The constant + * and GSS_C_NT_ANONYMOUS should be initialized to point + * to that gss_OID_desc. + */ + + /* + * The implementation must reserve static storage for a + * gss_OID_desc object containing the value */ + {6, (void *)"\x2b\x06\x01\x05\x06\x04"}, + /* corresponding to an object-identifier value of + * {1(iso), 3(org), 6(dod), 1(internet), 5(security), + * 6(nametypes), 4(gss-api-exported-name)}. The constant + * GSS_C_NT_EXPORT_NAME should be initialized to point + * to that gss_OID_desc. + */ + {6, (void *)"\x2b\x06\x01\x05\x06\x06"}, + /* corresponding to an object-identifier value of + * {1(iso), 3(org), 6(dod), 1(internet), 5(security), + * 6(nametypes), 6(gss-composite-export)}. The constant + * GSS_C_NT_COMPOSITE_EXPORT should be initialized to point + * to that gss_OID_desc. + */ + /* GSS_C_INQ_SSPI_SESSION_KEY 1.2.840.113554.1.2.2.5.5 */ + {11, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05"}, + + /* RFC 5587 attributes, see below */ + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x01"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x02"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x03"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x04"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x05"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x06"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x07"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x08"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x09"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x0a"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x0b"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x0c"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x0d"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x0e"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x0f"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x10"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x11"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x12"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x13"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x14"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x15"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x16"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x17"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x18"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x19"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x1a"}, + {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x1b"}, +}; + +/* Here are the constants which point to the static structure above. + * + * Constants of the form GSS_C_NT_* are specified by rfc 2744. + * + * Constants of the form gss_nt_* are the original MIT krb5 names + * found in gssapi_generic.h. They are provided for compatibility. */ + +GSS_DLLIMP gss_OID GSS_C_NT_USER_NAME = oids+0; +GSS_DLLIMP gss_OID gss_nt_user_name = oids+0; + +GSS_DLLIMP gss_OID GSS_C_NT_MACHINE_UID_NAME = oids+1; +GSS_DLLIMP gss_OID gss_nt_machine_uid_name = oids+1; + +GSS_DLLIMP gss_OID GSS_C_NT_STRING_UID_NAME = oids+2; +GSS_DLLIMP gss_OID gss_nt_string_uid_name = oids+2; + +GSS_DLLIMP gss_OID GSS_C_NT_HOSTBASED_SERVICE_X = oids+3; +gss_OID gss_nt_service_name_v2 = oids+3; + +GSS_DLLIMP gss_OID GSS_C_NT_HOSTBASED_SERVICE = oids+4; +GSS_DLLIMP gss_OID gss_nt_service_name = oids+4; + +GSS_DLLIMP gss_OID GSS_C_NT_ANONYMOUS = oids+5; + +GSS_DLLIMP gss_OID GSS_C_NT_EXPORT_NAME = oids+6; +gss_OID gss_nt_exported_name = oids+6; + +GSS_DLLIMP gss_OID GSS_C_NT_COMPOSITE_EXPORT = oids+7; + +GSS_DLLIMP gss_OID GSS_C_INQ_SSPI_SESSION_KEY = oids+8; + +GSS_DLLIMP gss_const_OID GSS_C_MA_MECH_CONCRETE = oids+9; +GSS_DLLIMP gss_const_OID GSS_C_MA_MECH_PSEUDO = oids+10; +GSS_DLLIMP gss_const_OID GSS_C_MA_MECH_COMPOSITE = oids+11; +GSS_DLLIMP gss_const_OID GSS_C_MA_MECH_NEGO = oids+12; +GSS_DLLIMP gss_const_OID GSS_C_MA_MECH_GLUE = oids+13; +GSS_DLLIMP gss_const_OID GSS_C_MA_NOT_MECH = oids+14; +GSS_DLLIMP gss_const_OID GSS_C_MA_DEPRECATED = oids+15; +GSS_DLLIMP gss_const_OID GSS_C_MA_NOT_DFLT_MECH = oids+16; +GSS_DLLIMP gss_const_OID GSS_C_MA_ITOK_FRAMED = oids+17; +GSS_DLLIMP gss_const_OID GSS_C_MA_AUTH_INIT = oids+18; +GSS_DLLIMP gss_const_OID GSS_C_MA_AUTH_TARG = oids+19; +GSS_DLLIMP gss_const_OID GSS_C_MA_AUTH_INIT_INIT = oids+20; +GSS_DLLIMP gss_const_OID GSS_C_MA_AUTH_TARG_INIT = oids+21; +GSS_DLLIMP gss_const_OID GSS_C_MA_AUTH_INIT_ANON = oids+22; +GSS_DLLIMP gss_const_OID GSS_C_MA_AUTH_TARG_ANON = oids+23; +GSS_DLLIMP gss_const_OID GSS_C_MA_DELEG_CRED = oids+24; +GSS_DLLIMP gss_const_OID GSS_C_MA_INTEG_PROT = oids+25; +GSS_DLLIMP gss_const_OID GSS_C_MA_CONF_PROT = oids+26; +GSS_DLLIMP gss_const_OID GSS_C_MA_MIC = oids+27; +GSS_DLLIMP gss_const_OID GSS_C_MA_WRAP = oids+28; +GSS_DLLIMP gss_const_OID GSS_C_MA_PROT_READY = oids+29; +GSS_DLLIMP gss_const_OID GSS_C_MA_REPLAY_DET = oids+30; +GSS_DLLIMP gss_const_OID GSS_C_MA_OOS_DET = oids+31; +GSS_DLLIMP gss_const_OID GSS_C_MA_CBINDINGS = oids+32; +GSS_DLLIMP gss_const_OID GSS_C_MA_PFS = oids+33; +GSS_DLLIMP gss_const_OID GSS_C_MA_COMPRESS = oids+34; +GSS_DLLIMP gss_const_OID GSS_C_MA_CTX_TRANS = oids+35; + +static gss_OID_set_desc gss_ma_known_attrs_desc = { 27, oids+9 }; +gss_OID_set gss_ma_known_attrs = &gss_ma_known_attrs_desc; + +static struct mech_attr_info_desc { + gss_OID mech_attr; + const char *name; + const char *short_desc; + const char *long_desc; +} mech_attr_info[] = { + { + oids+9, + "GSS_C_MA_MECH_CONCRETE", + "concrete-mech", + "Mechanism is neither a pseudo-mechanism nor a composite mechanism.", + }, + { + oids+10, + "GSS_C_MA_MECH_PSEUDO", + "pseudo-mech", + "Mechanism is a pseudo-mechanism.", + }, + { + oids+11, + "GSS_C_MA_MECH_COMPOSITE", + "composite-mech", + "Mechanism is a composite of other mechanisms.", + }, + { + oids+12, + "GSS_C_MA_MECH_NEGO", + "mech-negotiation-mech", + "Mechanism negotiates other mechanisms.", + }, + { + oids+13, + "GSS_C_MA_MECH_GLUE", + "mech-glue", + "OID is not a mechanism but the GSS-API itself.", + }, + { + oids+14, + "GSS_C_MA_NOT_MECH", + "not-mech", + "Known OID but not a mechanism OID.", + }, + { + oids+15, + "GSS_C_MA_DEPRECATED", + "mech-deprecated", + "Mechanism is deprecated.", + }, + { + oids+16, + "GSS_C_MA_NOT_DFLT_MECH", + "mech-not-default", + "Mechanism must not be used as a default mechanism.", + }, + { + oids+17, + "GSS_C_MA_ITOK_FRAMED", + "initial-is-framed", + "Mechanism's initial contexts are properly framed.", + }, + { + oids+18, + "GSS_C_MA_AUTH_INIT", + "auth-init-princ", + "Mechanism supports authentication of initiator to acceptor.", + }, + { + oids+19, + "GSS_C_MA_AUTH_TARG", + "auth-targ-princ", + "Mechanism supports authentication of acceptor to initiator.", + }, + { + oids+20, + "GSS_C_MA_AUTH_INIT_INIT", + "auth-init-princ-initial", + "Mechanism supports authentication of initiator using " + "initial credentials.", + }, + { + oids+21, + "GSS_C_MA_AUTH_TARG_INIT", + "auth-target-princ-initial", + "Mechanism supports authentication of acceptor using " + "initial credentials.", + }, + { + oids+22, + "GSS_C_MA_AUTH_INIT_ANON", + "auth-init-princ-anon", + "Mechanism supports GSS_C_NT_ANONYMOUS as an initiator name.", + }, + { + oids+23, + "GSS_C_MA_AUTH_TARG_ANON", + "auth-targ-princ-anon", + "Mechanism supports GSS_C_NT_ANONYMOUS as an acceptor name.", + }, + { + oids+24, + "GSS_C_MA_DELEG_CRED", + "deleg-cred", + "Mechanism supports credential delegation.", + }, + { + oids+25, + "GSS_C_MA_INTEG_PROT", + "integ-prot", + "Mechanism supports per-message integrity protection.", + }, + { + oids+26, + "GSS_C_MA_CONF_PROT", + "conf-prot", + "Mechanism supports per-message confidentiality protection.", + }, + { + oids+27, + "GSS_C_MA_MIC", + "mic", + "Mechanism supports Message Integrity Code (MIC) tokens.", + }, + { + oids+28, + "GSS_C_MA_WRAP", + "wrap", + "Mechanism supports wrap tokens.", + }, + { + oids+29, + "GSS_C_MA_PROT_READY", + "prot-ready", + "Mechanism supports per-message proteciton prior to " + "full context establishment.", + }, + { + oids+30, + "GSS_C_MA_REPLAY_DET", + "replay-detection", + "Mechanism supports replay detection.", + }, + { + oids+31, + "GSS_C_MA_OOS_DET", + "oos-detection", + "Mechanism supports out-of-sequence detection.", + }, + { + oids+32, + "GSS_C_MA_CBINDINGS", + "channel-bindings", + "Mechanism supports channel bindings.", + }, + { + oids+33, + "GSS_C_MA_PFS", + "pfs", + "Mechanism supports Perfect Forward Security.", + }, + { + oids+34, + "GSS_C_MA_COMPRESS", + "compress", + "Mechanism supports compression of data inputs to gss_wrap().", + }, + { + oids+35, + "GSS_C_MA_CTX_TRANS", + "context-transfer", + "Mechanism supports security context export/import.", + }, +}; + +OM_uint32 +generic_gss_display_mech_attr( + OM_uint32 *minor_status, + gss_const_OID mech_attr, + gss_buffer_t name, + gss_buffer_t short_desc, + gss_buffer_t long_desc) +{ + size_t i; + + if (name != GSS_C_NO_BUFFER) { + name->length = 0; + name->value = NULL; + } + if (short_desc != GSS_C_NO_BUFFER) { + short_desc->length = 0; + short_desc->value = NULL; + } + if (long_desc != GSS_C_NO_BUFFER) { + long_desc->length = 0; + long_desc->value = NULL; + } + for (i = 0; i < sizeof(mech_attr_info)/sizeof(mech_attr_info[0]); i++) { + struct mech_attr_info_desc *mai = &mech_attr_info[i]; + + if (g_OID_equal(mech_attr, mai->mech_attr)) { + if (name != GSS_C_NO_BUFFER && + !g_make_string_buffer(mai->name, name)) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + if (short_desc != GSS_C_NO_BUFFER && + !g_make_string_buffer(mai->short_desc, short_desc)) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + if (long_desc != GSS_C_NO_BUFFER && + !g_make_string_buffer(mai->long_desc, long_desc)) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + return GSS_S_COMPLETE; + } + } + + return GSS_S_BAD_MECH_ATTR; +} + +static gss_buffer_desc const_attrs[] = { + { sizeof("local-login-user") - 1, + "local-login-user" }, +}; + +GSS_DLLIMP gss_buffer_t GSS_C_ATTR_LOCAL_LOGIN_USER = &const_attrs[0]; diff --git a/src/lib/gssapi/generic/gssapi_generic.h b/src/lib/gssapi/generic/gssapi_generic.h new file mode 100644 index 000000000000..e11f938c672b --- /dev/null +++ b/src/lib/gssapi/generic/gssapi_generic.h @@ -0,0 +1,59 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _GSSAPI_GENERIC_H_ +#define _GSSAPI_GENERIC_H_ + +/* + * $Id$ + */ + +#include <gssapi/gssapi.h> + +#if defined(__cplusplus) && !defined(GSSAPIGENERIC_BEGIN_DECLS) +#define GSSAPIGENERIC_BEGIN_DECLS extern "C" { +#define GSSAPIGENERIC_END_DECLS } +#else +#define GSSAPIGENERIC_BEGIN_DECLS +#define GSSAPIGENERIC_END_DECLS +#endif + +#define GSS_EMPTY_BUFFER(buf) ((buf) == NULL || \ + (buf)->value == NULL || (buf)->length == 0) + +GSSAPIGENERIC_BEGIN_DECLS + +/* Deprecated MIT krb5 oid names provided for compatibility. + * The correct oids (GSS_C_NT_USER_NAME, etc) from rfc 2744 + * are defined in gssapi.h. */ + +GSS_DLLIMP extern gss_OID gss_nt_user_name; +GSS_DLLIMP extern gss_OID gss_nt_machine_uid_name; +GSS_DLLIMP extern gss_OID gss_nt_string_uid_name; +extern gss_OID gss_nt_service_name_v2; +GSS_DLLIMP extern gss_OID gss_nt_service_name; +extern gss_OID gss_nt_exported_name; + +GSSAPIGENERIC_END_DECLS + +#endif /* _GSSAPI_GENERIC_H_ */ diff --git a/src/lib/gssapi/generic/maptest.c b/src/lib/gssapi/generic/maptest.c new file mode 100644 index 000000000000..566d88c316f1 --- /dev/null +++ b/src/lib/gssapi/generic/maptest.c @@ -0,0 +1,68 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> + +typedef struct { int a, b; } elt; +static int eltcp(elt *dest, elt src) +{ + *dest = src; + return 0; +} +static int eltcmp(elt left, elt right) +{ + if (left.a < right.a) + return -1; + if (left.a > right.a) + return 1; + if (left.b < right.b) + return -1; + if (left.b > right.b) + return 1; + return 0; +} +static void eltprt(elt v, FILE *f) +{ + fprintf(f, "{%d,%d}", v.a, v.b); +} +static int intcmp(int left, int right) +{ + if (left < right) + return -1; + if (left > right) + return 1; + return 0; +} +static void intprt(int v, FILE *f) +{ + fprintf(f, "%d", v); +} + +#include "maptest.h" + +foo foo1; + +int main () +{ + elt v1 = { 1, 2 }, v2 = { 3, 4 }; + const elt *vp; + const int *ip; + + assert(0 == foo_init(&foo1)); + vp = foo_findleft(&foo1, 47); + assert(vp == NULL); + assert(0 == foo_add(&foo1, 47, v1)); + vp = foo_findleft(&foo1, 47); + assert(vp != NULL); + assert(0 == eltcmp(*vp, v1)); + vp = foo_findleft(&foo1, 3); + assert(vp == NULL); + assert(0 == foo_add(&foo1, 93, v2)); + ip = foo_findright(&foo1, v1); + assert(ip != NULL); + assert(*ip == 47); + printf("Map content: "); + foo_printmap(&foo1, stdout); + printf("\n"); + return 0; +} diff --git a/src/lib/gssapi/generic/oid_ops.c b/src/lib/gssapi/generic/oid_ops.c new file mode 100644 index 000000000000..6e294b9bcf94 --- /dev/null +++ b/src/lib/gssapi/generic/oid_ops.c @@ -0,0 +1,553 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/generic/oid_ops.c */ +/* + * Copyright 1995 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. + */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* GSS-API V2 interfaces to manipulate OIDs */ + +#include "gssapiP_generic.h" +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <gssapi/gssapi_generic.h> +#include <errno.h> +#include <ctype.h> + +/* + * The functions for allocating and releasing individual OIDs use malloc and + * free instead of the gssalloc wrappers, because the mechglue currently mixes + * generic_gss_copy_oid() with hand-freeing of OIDs. We do not need to free + * free OIDs allocated by mechanisms, so this should not be a problem. + */ + +OM_uint32 +generic_gss_release_oid(OM_uint32 *minor_status, gss_OID *oid) +{ + if (minor_status) + *minor_status = 0; + + if (oid == NULL || *oid == GSS_C_NO_OID) + return(GSS_S_COMPLETE); + + /* + * The V2 API says the following! + * + * gss_release_oid[()] will recognize any of the GSSAPI's own OID values, + * and will silently ignore attempts to free these OIDs; for other OIDs + * it will call the C free() routine for both the OID data and the + * descriptor. This allows applications to freely mix their own heap- + * allocated OID values with OIDs returned by GSS-API. + */ + + /* + * We use the official OID definitions instead of the unofficial OID + * defintions. But we continue to support the unofficial OID + * gss_nt_service_name just in case if some gss applications use + * the old OID. + */ + + if ((*oid != GSS_C_NT_USER_NAME) && + (*oid != GSS_C_NT_MACHINE_UID_NAME) && + (*oid != GSS_C_NT_STRING_UID_NAME) && + (*oid != GSS_C_NT_HOSTBASED_SERVICE) && + (*oid != GSS_C_NT_ANONYMOUS) && + (*oid != GSS_C_NT_EXPORT_NAME) && + (*oid != GSS_C_NT_COMPOSITE_EXPORT) && + (*oid != gss_nt_service_name)) { + free((*oid)->elements); + free(*oid); + } + *oid = GSS_C_NO_OID; + return(GSS_S_COMPLETE); +} + +OM_uint32 +generic_gss_copy_oid(OM_uint32 *minor_status, + const gss_OID_desc * const oid, + gss_OID *new_oid) +{ + gss_OID p; + + *minor_status = 0; + + p = (gss_OID) malloc(sizeof(gss_OID_desc)); + if (!p) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + p->length = oid->length; + p->elements = malloc(p->length); + if (!p->elements) { + free(p); + return GSS_S_FAILURE; + } + memcpy(p->elements, oid->elements, p->length); + *new_oid = p; + return(GSS_S_COMPLETE); +} + + +OM_uint32 +generic_gss_create_empty_oid_set(OM_uint32 *minor_status, gss_OID_set *oid_set) +{ + *minor_status = 0; + + if (oid_set == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if ((*oid_set = (gss_OID_set) gssalloc_malloc(sizeof(gss_OID_set_desc)))) { + memset(*oid_set, 0, sizeof(gss_OID_set_desc)); + return(GSS_S_COMPLETE); + } + else { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } +} + +OM_uint32 +generic_gss_add_oid_set_member(OM_uint32 *minor_status, + const gss_OID_desc * const member_oid, + gss_OID_set *oid_set) +{ + gss_OID elist; + gss_OID lastel; + + *minor_status = 0; + + if (member_oid == NULL || member_oid->length == 0 || + member_oid->elements == NULL) + return (GSS_S_CALL_INACCESSIBLE_READ); + + if (oid_set == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + elist = (*oid_set)->elements; + /* Get an enlarged copy of the array */ + if (((*oid_set)->elements = (gss_OID) gssalloc_malloc(((*oid_set)->count+1) * + sizeof(gss_OID_desc)))) { + /* Copy in the old junk */ + if (elist) + memcpy((*oid_set)->elements, + elist, + ((*oid_set)->count * sizeof(gss_OID_desc))); + + /* Duplicate the input element */ + lastel = &(*oid_set)->elements[(*oid_set)->count]; + if ((lastel->elements = + (void *) gssalloc_malloc((size_t) member_oid->length))) { + /* Success - copy elements */ + memcpy(lastel->elements, member_oid->elements, + (size_t) member_oid->length); + /* Set length */ + lastel->length = member_oid->length; + + /* Update count */ + (*oid_set)->count++; + if (elist) + gssalloc_free(elist); + *minor_status = 0; + return(GSS_S_COMPLETE); + } + else + gssalloc_free((*oid_set)->elements); + } + /* Failure - restore old contents of list */ + (*oid_set)->elements = elist; + *minor_status = ENOMEM; + return(GSS_S_FAILURE); +} + +OM_uint32 +generic_gss_test_oid_set_member(OM_uint32 *minor_status, + const gss_OID_desc * const member, + gss_OID_set set, + int * present) +{ + OM_uint32 i; + int result; + + *minor_status = 0; + + if (member == NULL || set == NULL) + return (GSS_S_CALL_INACCESSIBLE_READ); + + if (present == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + result = 0; + for (i=0; i<set->count; i++) { + if ((set->elements[i].length == member->length) && + !memcmp(set->elements[i].elements, + member->elements, + (size_t) member->length)) { + result = 1; + break; + } + } + *present = result; + return(GSS_S_COMPLETE); +} + +OM_uint32 +generic_gss_oid_to_str(OM_uint32 *minor_status, + const gss_OID_desc * const oid, + gss_buffer_t oid_str) +{ + unsigned long number, n; + OM_uint32 i; + int first; + unsigned char *cp; + struct k5buf buf; + + if (minor_status != NULL) + *minor_status = 0; + + if (oid_str != GSS_C_NO_BUFFER) { + oid_str->length = 0; + oid_str->value = NULL; + } + + if (oid == NULL || oid->length == 0 || oid->elements == NULL) + return (GSS_S_CALL_INACCESSIBLE_READ); + + if (oid_str == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + /* Decoded according to krb5/gssapi_krb5.c */ + + cp = (unsigned char *) oid->elements; + number = (unsigned long) cp[0]; + k5_buf_init_dynamic(&buf); + k5_buf_add(&buf, "{ "); + number = 0; + cp = (unsigned char *) oid->elements; + first = 1; + for (i = 0; i < oid->length; i++) { + number = (number << 7) | (cp[i] & 0x7f); + if ((cp[i] & 0x80) == 0) { + if (first) { + n = (number < 40) ? 0 : (number < 80) ? 1 : 2; + k5_buf_add_fmt(&buf, "%lu %lu ", n, number - (n * 40)); + first = 0; + } else { + k5_buf_add_fmt(&buf, "%lu ", number); + } + number = 0; + } + } + k5_buf_add_len(&buf, "}\0", 2); + return k5buf_to_gss(minor_status, &buf, oid_str); +} + +/* Return the length of a DER OID subidentifier encoding. */ +static size_t +arc_encoded_length(unsigned long arc) +{ + size_t len = 1; + + for (arc >>= 7; arc; arc >>= 7) + len++; + return len; +} + +/* Encode a subidentifier into *bufp and advance it to the encoding's end. */ +static void +arc_encode(unsigned long arc, unsigned char **bufp) +{ + unsigned char *p; + + /* Advance to the end and encode backwards. */ + p = *bufp = *bufp + arc_encoded_length(arc); + *--p = arc & 0x7f; + for (arc >>= 7; arc; arc >>= 7) + *--p = (arc & 0x7f) | 0x80; +} + +/* Fetch an arc value from *bufp and advance past it and any following spaces + * or periods. Return 1 on success, 0 if *bufp is not at a valid arc value. */ +static int +get_arc(const unsigned char **bufp, const unsigned char *end, + unsigned long *arc_out) +{ + const unsigned char *p = *bufp; + unsigned long arc = 0, newval; + + if (p == end || !isdigit(*p)) + return 0; + for (; p < end && isdigit(*p); p++) { + newval = arc * 10 + (*p - '0'); + if (newval < arc) + return 0; + arc = newval; + } + while (p < end && (isspace(*p) || *p == '.')) + p++; + *bufp = p; + *arc_out = arc; + return 1; +} + +/* + * Convert a sequence of two or more decimal arc values into a DER-encoded OID. + * The values may be separated by any combination of whitespace and period + * characters, and may be optionally surrounded with braces. Leading + * whitespace and trailing garbage is allowed. The first arc value must be 0, + * 1, or 2, and the second value must be less than 40 if the first value is not + * 2. + */ +OM_uint32 +generic_gss_str_to_oid(OM_uint32 *minor_status, + gss_buffer_t oid_str, + gss_OID *oid_out) +{ + const unsigned char *p, *end, *arc3_start; + unsigned char *out; + unsigned long arc, arc1, arc2; + size_t nbytes; + int brace = 0; + gss_OID oid; + + if (minor_status != NULL) + *minor_status = 0; + + if (oid_out != NULL) + *oid_out = GSS_C_NO_OID; + + if (GSS_EMPTY_BUFFER(oid_str)) + return (GSS_S_CALL_INACCESSIBLE_READ); + + if (oid_out == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + /* Skip past initial spaces and, optionally, an open brace. */ + brace = 0; + p = oid_str->value; + end = p + oid_str->length; + while (p < end && isspace(*p)) + p++; + if (p < end && *p == '{') { + brace = 1; + p++; + } + while (p < end && isspace(*p)) + p++; + + /* Get the first two arc values, to be encoded as one subidentifier. */ + if (!get_arc(&p, end, &arc1) || !get_arc(&p, end, &arc2)) + return (GSS_S_FAILURE); + if (arc1 > 2 || (arc1 < 2 && arc2 > 39) || arc2 > ULONG_MAX - 80) + return (GSS_S_FAILURE); + arc3_start = p; + + /* Compute the total length of the encoding while checking syntax. */ + nbytes = arc_encoded_length(arc1 * 40 + arc2); + while (get_arc(&p, end, &arc)) + nbytes += arc_encoded_length(arc); + if (brace && (p == end || *p != '}')) + return (GSS_S_FAILURE); + + /* Allocate an oid structure. */ + oid = malloc(sizeof(*oid)); + if (oid == NULL) + return (GSS_S_FAILURE); + oid->elements = malloc(nbytes); + if (oid->elements == NULL) { + free(oid); + return (GSS_S_FAILURE); + } + oid->length = nbytes; + + out = oid->elements; + arc_encode(arc1 * 40 + arc2, &out); + p = arc3_start; + while (get_arc(&p, end, &arc)) + arc_encode(arc, &out); + assert(out - nbytes == oid->elements); + *oid_out = oid; + return(GSS_S_COMPLETE); +} + +/* Compose an OID of a prefix and an integer suffix */ +OM_uint32 +generic_gss_oid_compose(OM_uint32 *minor_status, + const char *prefix, + size_t prefix_len, + int suffix, + gss_OID_desc *oid) +{ + int osuffix, i; + size_t nbytes; + unsigned char *op; + + if (oid == GSS_C_NO_OID) { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + if (oid->length < prefix_len) { + *minor_status = ERANGE; + return GSS_S_FAILURE; + } + + memcpy(oid->elements, prefix, prefix_len); + + nbytes = 0; + osuffix = suffix; + while (suffix) { + nbytes++; + suffix >>= 7; + } + suffix = osuffix; + + if (oid->length < prefix_len + nbytes) { + *minor_status = ERANGE; + return GSS_S_FAILURE; + } + + op = (unsigned char *) oid->elements + prefix_len + nbytes; + i = -1; + while (suffix) { + op[i] = (unsigned char)suffix & 0x7f; + if (i != -1) + op[i] |= 0x80; + i--; + suffix >>= 7; + } + + oid->length = prefix_len + nbytes; + + *minor_status = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +generic_gss_oid_decompose(OM_uint32 *minor_status, + const char *prefix, + size_t prefix_len, + gss_OID_desc *oid, + int *suffix) +{ + size_t i, slen; + unsigned char *op; + + if (oid->length < prefix_len || + memcmp(oid->elements, prefix, prefix_len) != 0) { + return GSS_S_BAD_MECH; + } + + op = (unsigned char *) oid->elements + prefix_len; + + *suffix = 0; + + slen = oid->length - prefix_len; + + for (i = 0; i < slen; i++) { + *suffix = (*suffix << 7) | (op[i] & 0x7f); + if (i + 1 != slen && (op[i] & 0x80) == 0) { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + } + + return GSS_S_COMPLETE; +} + +OM_uint32 +generic_gss_copy_oid_set(OM_uint32 *minor_status, + const gss_OID_set_desc * const oidset, + gss_OID_set *new_oidset) +{ + gss_OID_set_desc *copy; + OM_uint32 minor = 0; + OM_uint32 major = GSS_S_COMPLETE; + OM_uint32 i; + + if (minor_status != NULL) + *minor_status = 0; + + if (new_oidset != NULL) + *new_oidset = GSS_C_NO_OID_SET; + + if (oidset == GSS_C_NO_OID_SET) + return (GSS_S_CALL_INACCESSIBLE_READ); + + if (new_oidset == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if ((copy = (gss_OID_set_desc *) gssalloc_calloc(1, sizeof (*copy))) == NULL) { + major = GSS_S_FAILURE; + goto done; + } + + if ((copy->elements = (gss_OID_desc *) + gssalloc_calloc(oidset->count, sizeof (*copy->elements))) == NULL) { + major = GSS_S_FAILURE; + goto done; + } + copy->count = oidset->count; + + for (i = 0; i < copy->count; i++) { + gss_OID_desc *out = ©->elements[i]; + gss_OID_desc *in = &oidset->elements[i]; + + if ((out->elements = (void *) gssalloc_malloc(in->length)) == NULL) { + major = GSS_S_FAILURE; + goto done; + } + (void) memcpy(out->elements, in->elements, in->length); + out->length = in->length; + } + + *new_oidset = copy; +done: + if (major != GSS_S_COMPLETE) { + (void) generic_gss_release_oid_set(&minor, ©); + } + + return (major); +} diff --git a/src/lib/gssapi/generic/rel_buffer.c b/src/lib/gssapi/generic/rel_buffer.c new file mode 100644 index 000000000000..44dc98157b79 --- /dev/null +++ b/src/lib/gssapi/generic/rel_buffer.c @@ -0,0 +1,57 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* #ident "@(#)g_rel_buffer.c 1.2 96/02/06 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_release_buffer + */ + +#include "gssapiP_generic.h" + +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +OM_uint32 +generic_gss_release_buffer( + OM_uint32 *minor_status, + gss_buffer_t buffer) +{ + if (minor_status) + *minor_status = 0; + + /* if buffer is NULL, return */ + + if (buffer == GSS_C_NO_BUFFER) + return(GSS_S_COMPLETE); + + if (buffer->value) { + gssalloc_free(buffer->value); + buffer->length = 0; + buffer->value = NULL; + } + + return (GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/generic/rel_oid_set.c b/src/lib/gssapi/generic/rel_oid_set.c new file mode 100644 index 000000000000..954542e40754 --- /dev/null +++ b/src/lib/gssapi/generic/rel_oid_set.c @@ -0,0 +1,61 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* #ident "@(#)gss_release_oid_set.c 1.12 95/08/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_release_oid_set + */ + +#include "gssapiP_generic.h" + +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +OM_uint32 +generic_gss_release_oid_set( + OM_uint32 *minor_status, + gss_OID_set *set) +{ + size_t i; + if (minor_status) + *minor_status = 0; + + if (set == NULL) + return(GSS_S_COMPLETE); + + if (*set == GSS_C_NULL_OID_SET) + return(GSS_S_COMPLETE); + + for (i=0; i<(*set)->count; i++) + gssalloc_free((*set)->elements[i].elements); + + gssalloc_free((*set)->elements); + gssalloc_free(*set); + + *set = GSS_C_NULL_OID_SET; + + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/generic/t_seqstate.c b/src/lib/gssapi/generic/t_seqstate.c new file mode 100644 index 000000000000..8f44fcf3edb2 --- /dev/null +++ b/src/lib/gssapi/generic/t_seqstate.c @@ -0,0 +1,197 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/generic/t_seqstate.c - Test program for sequence number state */ +/* + * Copyright (C) 2014 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "gssapiP_generic.h" + +enum resultcode { + NOERR = GSS_S_COMPLETE, + GAP = GSS_S_GAP_TOKEN, + UNSEQ = GSS_S_UNSEQ_TOKEN, + OLD = GSS_S_OLD_TOKEN, + REPLAY = GSS_S_DUPLICATE_TOKEN +}; + +enum replayflag { NO_REPLAY = 0, DO_REPLAY = 1 }; +enum sequenceflag { NO_SEQUENCE = 0, DO_SEQUENCE = 1 }; +enum width { NARROW = 0, WIDE = 1, BOTH = 2 }; + +struct test { + uint64_t initial; + enum replayflag do_replay; + enum sequenceflag do_sequence; + enum width wide_seqnums; + size_t nseqs; + struct { + uint64_t seqnum; + enum resultcode result; + } seqs[10]; +} tests[] = { + /* No replay or sequence checking. */ + { + 10, NO_REPLAY, NO_SEQUENCE, BOTH, + 4, { { 11, NOERR }, { 10, NOERR }, { 10, NOERR }, { 9, NOERR } } + }, + + /* Basic sequence checking, no wraparound. */ + { + 100, NO_REPLAY, DO_SEQUENCE, BOTH, + 4, { { 100, NOERR }, { 102, GAP }, { 103, NOERR }, { 101, UNSEQ } } + }, + + /* Initial gap sequence checking, no wraparound. */ + { + 200, NO_REPLAY, DO_SEQUENCE, BOTH, + 4, { { 201, GAP }, { 202, NOERR }, { 200, UNSEQ }, { 203, NOERR } } + }, + + /* Sequence checking with wraparound. */ + { + UINT32_MAX - 1, NO_REPLAY, DO_SEQUENCE, NARROW, + 4, { { UINT32_MAX - 1, NOERR }, { UINT32_MAX, NOERR }, { 0, NOERR }, + { 1, NOERR } } + }, + { + UINT32_MAX - 1, NO_REPLAY, DO_SEQUENCE, NARROW, + 4, { { UINT32_MAX - 1, NOERR }, { 0, GAP }, { UINT32_MAX, UNSEQ }, + { 1, NOERR } } + }, + { + UINT64_MAX - 1, NO_REPLAY, DO_SEQUENCE, WIDE, + 4, { { UINT64_MAX - 1, NOERR }, { UINT64_MAX, NOERR }, { 0, NOERR }, + { 1, NOERR } } + }, + { + UINT64_MAX - 1, NO_REPLAY, DO_SEQUENCE, WIDE, + 4, { { UINT64_MAX - 1, NOERR }, { 0, GAP }, { UINT64_MAX, UNSEQ }, + { 1, NOERR } } + }, + + /* 64-bit sequence checking beyond 32-bit range */ + { + UINT32_MAX - 1, NO_REPLAY, DO_SEQUENCE, WIDE, + 4, { { UINT32_MAX - 1, NOERR }, + { UINT32_MAX, NOERR }, + { (uint64_t)UINT32_MAX + 1, NOERR }, + { (uint64_t)UINT32_MAX + 2, NOERR } } + }, + { + UINT32_MAX - 1, NO_REPLAY, DO_SEQUENCE, WIDE, + 4, { { UINT32_MAX - 1, NOERR }, + { (uint64_t)UINT32_MAX + 1, GAP }, + { UINT32_MAX, UNSEQ }, + { (uint64_t)UINT32_MAX + 2, NOERR } } + }, + { + UINT32_MAX - 1, NO_REPLAY, DO_SEQUENCE, WIDE, + 3, { { UINT32_MAX - 1, NOERR }, { UINT32_MAX, NOERR }, { 0, GAP } } + }, + + /* Replay without the replay flag set. */ + { + 250, NO_REPLAY, DO_SEQUENCE, BOTH, + 2, { { 250, NOERR }, { 250, UNSEQ } } + }, + + /* Basic replay detection with and without sequence checking. */ + { + 0, DO_REPLAY, DO_SEQUENCE, BOTH, + 10, { { 5, GAP }, { 3, UNSEQ }, { 8, GAP }, { 3, REPLAY }, + { 0, UNSEQ }, { 0, REPLAY }, { 5, REPLAY }, { 3, REPLAY }, + { 8, REPLAY }, { 9, NOERR } } + }, + { + 0, DO_REPLAY, NO_SEQUENCE, BOTH, + 10, { { 5, NOERR }, { 3, NOERR }, { 8, NOERR }, { 3, REPLAY }, + { 0, NOERR }, { 0, REPLAY }, { 5, REPLAY }, { 3, REPLAY }, + { 8, REPLAY }, { 9, NOERR } } + }, + + /* Replay and sequence detection with wraparound. The last seqnum produces + * GAP because it is before the initial sequence number. */ + { + UINT64_MAX - 5, DO_REPLAY, DO_SEQUENCE, WIDE, + 10, { { UINT64_MAX, GAP }, { UINT64_MAX - 2, UNSEQ }, { 0, NOERR }, + { UINT64_MAX, REPLAY }, { UINT64_MAX, REPLAY }, + { 2, GAP }, { 0, REPLAY }, { 1, UNSEQ }, + { UINT64_MAX - 2, REPLAY }, { UINT64_MAX - 6, GAP } } + }, + { + UINT32_MAX - 5, DO_REPLAY, DO_SEQUENCE, NARROW, + 10, { { UINT32_MAX, GAP }, { UINT32_MAX - 2, UNSEQ }, { 0, NOERR }, + { UINT32_MAX, REPLAY }, { UINT32_MAX, REPLAY }, + { 2, GAP }, { 0, REPLAY }, { 1, UNSEQ }, + { UINT32_MAX - 2, REPLAY }, { UINT32_MAX - 6, GAP } } + }, + + /* Old token edge cases. The current code can detect replays up to 64 + * numbers behind the expected sequence number (1164 in this case). */ + { + 1000, DO_REPLAY, NO_SEQUENCE, BOTH, + 10, { { 1163, NOERR }, { 1100, NOERR }, { 1100, REPLAY }, + { 1163, REPLAY }, { 1099, OLD }, { 1100, REPLAY }, + { 1150, NOERR }, { 1150, REPLAY }, { 1000, OLD }, + { 999, NOERR } } + }, +}; + +int +main() +{ + size_t i, j; + enum width w; + struct test *t; + g_seqnum_state seqstate; + OM_uint32 status; + + for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) { + t = &tests[i]; + /* Try both widths if t->wide_seqnums is both, otherwise just one. */ + for (w = NARROW; w <= WIDE; w++) { + if (t->wide_seqnums != BOTH && t->wide_seqnums != w) + continue; + if (g_seqstate_init(&seqstate, t->initial, t->do_replay, + t->do_sequence, w)) + abort(); + for (j = 0; j < t->nseqs; j++) { + status = g_seqstate_check(seqstate, t->seqs[j].seqnum); + if (status != t->seqs[j].result) { + fprintf(stderr, "Test %d seq %d failed: %d != %d\n", + (int)i, (int)j, status, t->seqs[j].result); + return 1; + } + } + g_seqstate_free(seqstate); + } + } + + return 0; +} diff --git a/src/lib/gssapi/generic/util_buffer.c b/src/lib/gssapi/generic/util_buffer.c new file mode 100644 index 000000000000..da2d83291370 --- /dev/null +++ b/src/lib/gssapi/generic/util_buffer.c @@ -0,0 +1,48 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * $Id$ + */ + +#include "gssapiP_generic.h" +#include <string.h> + +/* return nonzero on success, 0 on failure + make sure that buffer is consistent (release'able) when this + function exits, no matter what the exit value */ + +int g_make_string_buffer(const char *str, gss_buffer_t buffer) +{ + if (buffer == GSS_C_NO_BUFFER) + return (1); + + buffer->length = strlen(str); + + if ((buffer->value = gssalloc_strdup(str)) == NULL) { + buffer->length = 0; + return(0); + } + + return(1); +} diff --git a/src/lib/gssapi/generic/util_buffer_set.c b/src/lib/gssapi/generic/util_buffer_set.c new file mode 100644 index 000000000000..f7472a0807ba --- /dev/null +++ b/src/lib/gssapi/generic/util_buffer_set.c @@ -0,0 +1,126 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2008 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 "gssapiP_generic.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <errno.h> + +OM_uint32 +generic_gss_create_empty_buffer_set(OM_uint32 * minor_status, + gss_buffer_set_t *buffer_set) +{ + gss_buffer_set_t set; + + set = (gss_buffer_set_desc *) gssalloc_malloc(sizeof(*set)); + if (set == GSS_C_NO_BUFFER_SET) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + + set->count = 0; + set->elements = NULL; + + *buffer_set = set; + + *minor_status = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +generic_gss_add_buffer_set_member(OM_uint32 * minor_status, + const gss_buffer_t member_buffer, + gss_buffer_set_t *buffer_set) +{ + gss_buffer_set_t set; + gss_buffer_t p; + OM_uint32 ret; + + if (*buffer_set == GSS_C_NO_BUFFER_SET) { + ret = generic_gss_create_empty_buffer_set(minor_status, + buffer_set); + if (ret) { + return ret; + } + } + + set = *buffer_set; + set->elements = (gss_buffer_desc *)gssalloc_realloc(set->elements, + (set->count + 1) * + sizeof(gss_buffer_desc)); + if (set->elements == NULL) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + + p = &set->elements[set->count]; + + p->value = gssalloc_malloc(member_buffer->length); + if (p->value == NULL) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + memcpy(p->value, member_buffer->value, member_buffer->length); + p->length = member_buffer->length; + + set->count++; + + *minor_status = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +generic_gss_release_buffer_set(OM_uint32 * minor_status, + gss_buffer_set_t *buffer_set) +{ + size_t i; + OM_uint32 minor; + + *minor_status = 0; + + if (*buffer_set == GSS_C_NO_BUFFER_SET) { + return GSS_S_COMPLETE; + } + + for (i = 0; i < (*buffer_set)->count; i++) { + generic_gss_release_buffer(&minor, &((*buffer_set)->elements[i])); + } + + if ((*buffer_set)->elements != NULL) { + gssalloc_free((*buffer_set)->elements); + (*buffer_set)->elements = NULL; + } + + (*buffer_set)->count = 0; + + gssalloc_free(*buffer_set); + *buffer_set = GSS_C_NO_BUFFER_SET; + + return GSS_S_COMPLETE; +} diff --git a/src/lib/gssapi/generic/util_errmap.c b/src/lib/gssapi/generic/util_errmap.c new file mode 100644 index 000000000000..628a455d2ad4 --- /dev/null +++ b/src/lib/gssapi/generic/util_errmap.c @@ -0,0 +1,264 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2007, 2008 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 "gssapiP_generic.h" +#include <string.h> +#ifndef _WIN32 +#include <unistd.h> +#endif + +/* The mapping table is 0-based, but let's export codes that are + 1-based, keeping 0 for errors or unknown errors. + + The elements in the mapping table currently have separate copies of + each OID stored. This is a bit wasteful, but we are assuming the + table isn't likely to grow very large. */ + +struct mecherror { + gss_OID_desc mech; + OM_uint32 code; +}; + +static inline int +cmp_OM_uint32(OM_uint32 m1, OM_uint32 m2) +{ + if (m1 < m2) + return -1; + else if (m1 > m2) + return 1; + else + return 0; +} + +static inline int +mecherror_cmp(struct mecherror m1, struct mecherror m2) +{ + if (m1.code < m2.code) + return -1; + if (m1.code > m2.code) + return 1; + if (m1.mech.length < m2.mech.length) + return -1; + if (m1.mech.length > m2.mech.length) + return 1; + if (m1.mech.length == 0) + return 0; + return memcmp(m1.mech.elements, m2.mech.elements, m1.mech.length); +} + +static void +print_OM_uint32 (OM_uint32 value, FILE *f) +{ + fprintf(f, "%lu", (unsigned long) value); +} + +static inline int +mecherror_copy(struct mecherror *dest, struct mecherror src) +{ + *dest = src; + if (src.mech.length > 0) { + dest->mech.elements = malloc(src.mech.length); + if (dest->mech.elements == NULL) + return ENOMEM; + memcpy(dest->mech.elements, src.mech.elements, src.mech.length); + } else { + dest->mech.elements = NULL; + } + return 0; +} + +static void +mecherror_print(struct mecherror value, FILE *f) +{ + OM_uint32 minor; + gss_buffer_desc str; + static const struct { + const char *oidstr, *name; + } mechnames[] = { + { "{ 1 2 840 113554 1 2 2 }", "krb5-new" }, + { "{ 1 3 5 1 5 2 }", "krb5-old" }, + { "{ 1 2 840 48018 1 2 2 }", "krb5-microsoft" }, + { "{ 1 3 6 1 5 5 2 }", "spnego" }, + }; + unsigned int i; + + fprintf(f, "%lu@", (unsigned long) value.code); + + if (value.mech.length == 0) { + fprintf(f, "(com_err)"); + return; + } + fprintf(f, "%p=", value.mech.elements); + if (generic_gss_oid_to_str(&minor, &value.mech, &str)) { + fprintf(f, "(error in conversion)"); + return; + } + /* Note: generic_gss_oid_to_str returns a null-terminated string. */ + for (i = 0; i < sizeof(mechnames)/sizeof(mechnames[0]); i++) { + if (!strcmp(str.value, mechnames[i].oidstr) && mechnames[i].name != 0) { + fprintf(f, "%s", mechnames[i].name); + break; + } + } + if (i == sizeof(mechnames)/sizeof(mechnames[0])) + fprintf(f, "%s", (char *) str.value); + generic_gss_release_buffer(&minor, &str); +} + +#include "errmap.h" +#include "krb5.h" /* for KRB5KRB_AP_WRONG_PRINC */ + +static mecherrmap m; +static k5_mutex_t mutex = K5_MUTEX_PARTIAL_INITIALIZER; +static OM_uint32 next_fake = 100000; + +int gssint_mecherrmap_init(void) +{ + int err; + + err = mecherrmap_init(&m); + if (err) + return err; + err = k5_mutex_finish_init(&mutex); + if (err) { + mecherrmap_destroy(&m); + return err; + } + + return 0; +} + +/* Currently the enumeration template doesn't handle freeing + element storage when destroying the collection. */ +static int free_one(OM_uint32 i, struct mecherror value, void *p) +{ + free(value.mech.elements); + return 0; +} + +void gssint_mecherrmap_destroy(void) +{ + mecherrmap_foreach(&m, free_one, NULL); + mecherrmap_destroy(&m); + k5_mutex_destroy(&mutex); +} + +OM_uint32 gssint_mecherrmap_map(OM_uint32 minor, const gss_OID_desc * oid) +{ + const struct mecherror *mep; + struct mecherror me, me_copy; + const OM_uint32 *p; + int err; + OM_uint32 new_status; + +#ifdef DEBUG + FILE *f; + f = fopen("/dev/pts/9", "w+"); + if (f == NULL) + f = stderr; +#endif + + me.code = minor; + me.mech = *oid; + k5_mutex_lock(&mutex); + + /* Is this status+oid already mapped? */ + p = mecherrmap_findright(&m, me); + if (p != NULL) { + k5_mutex_unlock(&mutex); +#ifdef DEBUG + fprintf(f, "%s: found ", __FUNCTION__); + mecherror_print(me, f); + fprintf(f, " in map as %lu\n", (unsigned long) *p); + if (f != stderr) fclose(f); +#endif + return *p; + } + /* Is this status code already mapped to something else + mech-specific? */ + mep = mecherrmap_findleft(&m, minor); + if (mep == NULL) { + /* Map it to itself plus this mech-oid. */ + new_status = minor; + } else { + /* Already assigned. Pick a fake new value and map it. */ + /* There's a theoretical infinite loop risk here, if we fill + in 2**32 values. Also, returning 0 has a special + meaning. */ + do { + next_fake++; + new_status = next_fake; + if (new_status == 0) + /* ??? */; + } while (mecherrmap_findleft(&m, new_status) != NULL); + } + err = mecherror_copy(&me_copy, me); + if (err) { + k5_mutex_unlock(&mutex); + return err; + } + err = mecherrmap_add(&m, new_status, me_copy); + k5_mutex_unlock(&mutex); + if (err) + free(me_copy.mech.elements); +#ifdef DEBUG + fprintf(f, "%s: mapping ", __FUNCTION__); + mecherror_print(me, f); + fprintf(f, " to %lu: err=%d\nnew map: ", (unsigned long) new_status, err); + mecherrmap_printmap(&m, f); + fprintf(f, "\n"); + if (f != stderr) fclose(f); +#endif + if (err) + return 0; + else + return new_status; +} + +static gss_OID_desc no_oid = { 0, 0 }; +OM_uint32 gssint_mecherrmap_map_errcode(OM_uint32 errcode) +{ + return gssint_mecherrmap_map(errcode, &no_oid); +} + +int gssint_mecherrmap_get(OM_uint32 minor, gss_OID mech_oid, + OM_uint32 *mech_minor) +{ + const struct mecherror *p; + + if (minor == 0) { + return EINVAL; + } + k5_mutex_lock(&mutex); + p = mecherrmap_findleft(&m, minor); + k5_mutex_unlock(&mutex); + if (!p) { + return EINVAL; + } + *mech_oid = p->mech; + *mech_minor = p->code; + return 0; +} diff --git a/src/lib/gssapi/generic/util_seqstate.c b/src/lib/gssapi/generic/util_seqstate.c new file mode 100644 index 000000000000..a0bc2cc1c1d4 --- /dev/null +++ b/src/lib/gssapi/generic/util_seqstate.c @@ -0,0 +1,163 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/generic/util_seqstate.c - sequence number checking */ +/* + * Copyright (C) 2014 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "gssapiP_generic.h" +#include <string.h> + +struct g_seqnum_state_st { + /* Flags to indicate whether we are supposed to check for replays or + * enforce strict sequencing. */ + int do_replay; + int do_sequence; + + /* UINT32_MAX for 32-bit sequence numbers, UINT64_MAX for 64-bit. Mask + * against this after arithmetic to stay within the correct range. */ + uint64_t seqmask; + + /* The initial sequence number for this context. This value will be + * subtracted from all received sequence numbers to simplify wraparound. */ + uint64_t base; + + /* The expected next sequence number (one more than the highest previously + * seen sequence number), relative to base. */ + uint64_t next; + + /* + * A bitmap for the 64 sequence numbers prior to next. If the 1<<(i-1) bit + * is set, then we have seen seqnum next-i relative to base. The least + * significant bit is always set if we have received any sequence numbers, + * and indicates the highest sequence number we have seen (next-1). When + * we advance next, we shift recvmap to the left. + */ + uint64_t recvmap; +}; + +long +g_seqstate_init(g_seqnum_state *state_out, uint64_t seqnum, int do_replay, + int do_sequence, int wide) +{ + g_seqnum_state state; + + *state_out = NULL; + state = malloc(sizeof(*state)); + if (state == NULL) + return ENOMEM; + state->do_replay = do_replay; + state->do_sequence = do_sequence; + state->seqmask = wide ? UINT64_MAX : UINT32_MAX; + state->base = seqnum; + state->next = state->recvmap = 0; + *state_out = state; + return 0; +} + +OM_uint32 +g_seqstate_check(g_seqnum_state state, uint64_t seqnum) +{ + uint64_t rel_seqnum, offset, bit; + + if (!state->do_replay && !state->do_sequence) + return GSS_S_COMPLETE; + + /* Use the difference from the base seqnum, to simplify wraparound. */ + rel_seqnum = (seqnum - state->base) & state->seqmask; + + if (rel_seqnum >= state->next) { + /* seqnum is the expected sequence number or in the future. Update the + * received bitmap and expected next sequence number. */ + offset = rel_seqnum - state->next; + state->recvmap = (state->recvmap << (offset + 1)) | 1; + state->next = (rel_seqnum + 1) & state->seqmask; + + return (offset > 0 && state->do_sequence) ? GSS_S_GAP_TOKEN : + GSS_S_COMPLETE; + } + + /* seqnum is in the past. Check if it's too old for replay detection. */ + offset = state->next - rel_seqnum; + if (offset > 64) + return state->do_sequence ? GSS_S_UNSEQ_TOKEN : GSS_S_OLD_TOKEN; + + /* Check for replay and mark as received. */ + bit = (uint64_t)1 << (offset - 1); + if (state->do_replay && (state->recvmap & bit)) + return GSS_S_DUPLICATE_TOKEN; + state->recvmap |= bit; + + return state->do_sequence ? GSS_S_UNSEQ_TOKEN : GSS_S_COMPLETE; +} + +void +g_seqstate_free(g_seqnum_state state) +{ + free(state); +} + +/* + * These support functions are for the serialization routines + */ +void +g_seqstate_size(g_seqnum_state state, size_t *sizep) +{ + *sizep += sizeof(*state); +} + +long +g_seqstate_externalize(g_seqnum_state state, unsigned char **buf, + size_t *lenremain) +{ + if (*lenremain < sizeof(*state)) + return ENOMEM; + memcpy(*buf, state, sizeof(*state)); + *buf += sizeof(*state); + *lenremain -= sizeof(*state); + return 0; +} + +long +g_seqstate_internalize(g_seqnum_state *state_out, unsigned char **buf, + size_t *lenremain) +{ + g_seqnum_state state; + + *state_out = NULL; + if (*lenremain < sizeof(*state)) + return EINVAL; + state = malloc(sizeof(*state)); + if (state == NULL) + return ENOMEM; + memcpy(state, *buf, sizeof(*state)); + *buf += sizeof(*state); + *lenremain -= sizeof(*state); + *state_out = state; + return 0; +} diff --git a/src/lib/gssapi/generic/util_set.c b/src/lib/gssapi/generic/util_set.c new file mode 100644 index 000000000000..8866f525fbc9 --- /dev/null +++ b/src/lib/gssapi/generic/util_set.c @@ -0,0 +1,106 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1995 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * $Id$ + */ + +#include "gssapiP_generic.h" + +struct _g_set_elt { + void *key; + void *value; + struct _g_set_elt *next; +}; + +int g_set_init(g_set_elt *s) +{ + *s = NULL; + + return(0); +} + +#if 0 +int g_set_destroy(g_set_elt *s) +{ + g_set next; + + while (*s) { + next = (*s)->next; + free(*s); + *s = next; + } + + return(0); +} +#endif + +int g_set_entry_add(g_set_elt *s, void *key, void *value) +{ + g_set_elt first; + + if ((first = (struct _g_set_elt *) malloc(sizeof(struct _g_set_elt))) == NULL) + return(ENOMEM); + + first->key = key; + first->value = value; + first->next = *s; + + *s = first; + + return(0); +} + +int g_set_entry_delete(g_set_elt *s, void *key) +{ + g_set_elt *p; + + for (p=s; *p; p = &((*p)->next)) { + if ((*p)->key == key) { + g_set_elt next = (*p)->next; + free(*p); + *p = next; + + return(0); + } + } + + return(-1); +} + +int g_set_entry_get(g_set_elt *s, void *key, void **value) +{ + g_set_elt p; + + for (p = *s; p; p = p->next) { + if (p->key == key) { + *value = p->value; + + return(0); + } + } + + *value = NULL; + + return(-1); +} diff --git a/src/lib/gssapi/generic/util_token.c b/src/lib/gssapi/generic/util_token.c new file mode 100644 index 000000000000..6e339f4ac7f7 --- /dev/null +++ b/src/lib/gssapi/generic/util_token.c @@ -0,0 +1,229 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_generic.h" +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif +#include <limits.h> + +/* + * $Id$ + */ + +/* XXXX this code currently makes the assumption that a mech oid will + never be longer than 127 bytes. This assumption is not inherent in + the interfaces, so the code can be fixed if the OSI namespace + balloons unexpectedly. */ + +/* + * Each token looks like this: + * 0x60 tag for APPLICATION 0, SEQUENCE + * (constructed, definite-length) + * <length> possible multiple bytes, need to parse/generate + * 0x06 tag for OBJECT IDENTIFIER + * <moid_length> compile-time constant string (assume 1 byte) + * <moid_bytes> compile-time constant string + * <inner_bytes> the ANY containing the application token + * bytes 0,1 are the token type + * bytes 2,n are the token data + * + * Note that the token type field is a feature of RFC 1964 mechanisms and + * is not used by other GSSAPI mechanisms. As such, a token type of -1 + * is interpreted to mean that no token type should be expected or + * generated. + * + * For the purposes of this abstraction, the token "header" consists of + * the sequence tag and length octets, the mech OID DER encoding, and the + * first two inner bytes, which indicate the token type. The token + * "body" consists of everything else. + */ +static unsigned int +der_length_size(int length) +{ + if (length < (1<<7)) + return(1); + else if (length < (1<<8)) + return(2); +#if INT_MAX == 0x7fff + else + return(3); +#else + else if (length < (1<<16)) + return(3); + else if (length < (1<<24)) + return(4); + else + return(5); +#endif +} + +static void +der_write_length(unsigned char **buf, int length) +{ + if (length < (1<<7)) { + *(*buf)++ = (unsigned char) length; + } else { + *(*buf)++ = (unsigned char) (der_length_size(length)+127); +#if INT_MAX > 0x7fff + if (length >= (1<<24)) + *(*buf)++ = (unsigned char) (length>>24); + if (length >= (1<<16)) + *(*buf)++ = (unsigned char) ((length>>16)&0xff); +#endif + if (length >= (1<<8)) + *(*buf)++ = (unsigned char) ((length>>8)&0xff); + *(*buf)++ = (unsigned char) (length&0xff); + } +} + +/* returns decoded length, or < 0 on failure. Advances buf and + decrements bufsize */ + +static int +der_read_length(unsigned char **buf, int *bufsize) +{ + unsigned char sf; + int ret; + + if (*bufsize < 1) + return(-1); + sf = *(*buf)++; + (*bufsize)--; + if (sf & 0x80) { + if ((sf &= 0x7f) > ((*bufsize)-1)) + return(-1); + if (sf > sizeof(int)) + return (-1); + ret = 0; + for (; sf; sf--) { + ret = (ret<<8) + (*(*buf)++); + (*bufsize)--; + } + } else { + ret = sf; + } + + return(ret); +} + +/* returns the length of a token, given the mech oid and the body size */ + +unsigned int +g_token_size(const gss_OID_desc * mech, unsigned int body_size) +{ + /* set body_size to sequence contents size */ + body_size += 4 + (unsigned int)mech->length; /* NEED overflow check */ + return(1 + der_length_size(body_size) + body_size); +} + +/* fills in a buffer with the token header. The buffer is assumed to + be the right size. buf is advanced past the token header */ + +void +g_make_token_header( + const gss_OID_desc * mech, + unsigned int body_size, + unsigned char **buf, + int tok_type) +{ + *(*buf)++ = 0x60; + der_write_length(buf, ((tok_type == -1) ? 2 : 4) + mech->length + body_size); + *(*buf)++ = 0x06; + *(*buf)++ = (unsigned char) mech->length; + TWRITE_STR(*buf, mech->elements, mech->length); + if (tok_type != -1) { + *(*buf)++ = (unsigned char) ((tok_type>>8)&0xff); + *(*buf)++ = (unsigned char) (tok_type&0xff); + } +} + +/* + * Given a buffer containing a token, reads and verifies the token, + * leaving buf advanced past the token header, and setting body_size + * to the number of remaining bytes. Returns 0 on success, + * G_BAD_TOK_HEADER for a variety of errors, and G_WRONG_MECH if the + * mechanism in the token does not match the mech argument. buf and + * *body_size are left unmodified on error. + */ + +gss_int32 +g_verify_token_header( + const gss_OID_desc * mech, + unsigned int *body_size, + unsigned char **buf_in, + int tok_type, + unsigned int toksize_in, + int flags) +{ + unsigned char *buf = *buf_in; + int seqsize; + gss_OID_desc toid; + int toksize = toksize_in; + + if ((toksize-=1) < 0) + return(G_BAD_TOK_HEADER); + if (*buf++ != 0x60) { + if (flags & G_VFY_TOKEN_HDR_WRAPPER_REQUIRED) + return(G_BAD_TOK_HEADER); + buf--; + toksize++; + goto skip_wrapper; + } + + if ((seqsize = der_read_length(&buf, &toksize)) < 0) + return(G_BAD_TOK_HEADER); + + if (seqsize != toksize) + return(G_BAD_TOK_HEADER); + + if ((toksize-=1) < 0) + return(G_BAD_TOK_HEADER); + if (*buf++ != 0x06) + return(G_BAD_TOK_HEADER); + + if ((toksize-=1) < 0) + return(G_BAD_TOK_HEADER); + toid.length = *buf++; + + if ((toksize-=toid.length) < 0) + return(G_BAD_TOK_HEADER); + toid.elements = buf; + buf+=toid.length; + + if (! g_OID_equal(&toid, mech)) + return G_WRONG_MECH; +skip_wrapper: + if (tok_type != -1) { + if ((toksize-=2) < 0) + return(G_BAD_TOK_HEADER); + + if ((*buf++ != ((tok_type>>8)&0xff)) || + (*buf++ != (tok_type&0xff))) + return(G_WRONG_TOKID); + } + *buf_in = buf; + *body_size = toksize; + + return 0; +} diff --git a/src/lib/gssapi/krb5/Makefile.in b/src/lib/gssapi/krb5/Makefile.in new file mode 100644 index 000000000000..364a7a4c9b25 --- /dev/null +++ b/src/lib/gssapi/krb5/Makefile.in @@ -0,0 +1,259 @@ +mydir=lib$(S)gssapi$(S)krb5 +BUILDTOP=$(REL)..$(S)..$(S).. +LOCALINCLUDES = -I. -I$(srcdir) -I$(srcdir)/.. -I../generic -I$(srcdir)/../generic -I../mechglue -I$(srcdir)/../mechglue +DEFINES=-D_GSS_STATIC_LINK=1 + +#MODULE_INSTALL_DIR = $(GSS_MODULE_DIR) +#LIBBASE=mech_krb5 +#LIBMAJOR=0 +#LIBMINOR=0 +#LIBINITFUNC=gss_krb5int_init +#LIBFINIFUNC=gss_krb5int_fini +#STOBJLISTS=../generic/OBJS.ST OBJS.ST +#SUBDIROBJLISTS=../generic/OBJS.ST +#SHLIB_EXPDEPS=$(KRB5_DEPLIB) $(CRYPTO_DEPLIB) $(SUPPORT_DEPLIB) $(COM_ERR_DEPLIB) +#SHLIB_EXPLIBS=-lkrb5 -lk5crypto -lcom_err $(SUPPORT_LIB) $(DL_LIB) $(LIBS) +#RELDIR=gssapi/krb5 + +##DOS##BUILDTOP = ..\..\.. +##DOS##PREFIXDIR=krb5 +##DOS##OBJFILE = ..\$(OUTPRE)krb5.lst + +##DOS##DLL_EXP_TYPE=GSS + +ETSRCS= gssapi_err_krb5.c +ETOBJS= $(OUTPRE)gssapi_err_krb5.$(OBJEXT) +ETHDRS= gssapi_err_krb5.h + +$(OUTPRE)gssapi_err_krb5.$(OBJEXT): gssapi_err_krb5.c +gssapi_err_krb5.h: gssapi_err_krb5.et +gssapi_err_krb5.c: gssapi_err_krb5.et + +SRCS = \ + $(srcdir)/accept_sec_context.c \ + $(srcdir)/acquire_cred.c \ + $(srcdir)/canon_name.c \ + $(srcdir)/compare_name.c \ + $(srcdir)/context_time.c \ + $(srcdir)/copy_ccache.c \ + $(srcdir)/cred_store.c \ + $(srcdir)/delete_sec_context.c \ + $(srcdir)/disp_name.c \ + $(srcdir)/disp_status.c \ + $(srcdir)/duplicate_name.c \ + $(srcdir)/export_cred.c \ + $(srcdir)/export_name.c \ + $(srcdir)/export_sec_context.c \ + $(srcdir)/get_tkt_flags.c \ + $(srcdir)/gssapi_krb5.c \ + $(srcdir)/iakerb.c \ + $(srcdir)/import_cred.c \ + $(srcdir)/import_name.c \ + $(srcdir)/import_sec_context.c \ + $(srcdir)/indicate_mechs.c \ + $(srcdir)/init_sec_context.c \ + $(srcdir)/inq_context.c \ + $(srcdir)/inq_cred.c \ + $(srcdir)/inq_names.c \ + $(srcdir)/k5seal.c \ + $(srcdir)/k5sealiov.c \ + $(srcdir)/k5sealv3.c \ + $(srcdir)/k5sealv3iov.c \ + $(srcdir)/k5unseal.c \ + $(srcdir)/k5unsealiov.c \ + $(srcdir)/krb5_gss_glue.c \ + $(srcdir)/lucid_context.c \ + $(srcdir)/naming_exts.c \ + $(srcdir)/prf.c \ + $(srcdir)/process_context_token.c \ + $(srcdir)/rel_cred.c \ + $(srcdir)/rel_oid.c \ + $(srcdir)/rel_name.c \ + $(srcdir)/s4u_gss_glue.c \ + $(srcdir)/set_allowable_enctypes.c \ + $(srcdir)/ser_sctx.c \ + $(srcdir)/set_ccache.c \ + $(srcdir)/store_cred.c \ + $(srcdir)/util_cksum.c \ + $(srcdir)/util_crypt.c \ + $(srcdir)/util_seed.c \ + $(srcdir)/util_seqnum.c \ + $(srcdir)/val_cred.c \ + $(srcdir)/wrap_size_limit.c + + +OBJS = \ + $(OUTPRE)accept_sec_context.$(OBJEXT) \ + $(OUTPRE)acquire_cred.$(OBJEXT) \ + $(OUTPRE)canon_name.$(OBJEXT) \ + $(OUTPRE)compare_name.$(OBJEXT) \ + $(OUTPRE)context_time.$(OBJEXT) \ + $(OUTPRE)copy_ccache.$(OBJEXT) \ + $(OUTPRE)cred_store.$(OBJEXT) \ + $(OUTPRE)delete_sec_context.$(OBJEXT) \ + $(OUTPRE)disp_name.$(OBJEXT) \ + $(OUTPRE)disp_status.$(OBJEXT) \ + $(OUTPRE)duplicate_name.$(OBJEXT) \ + $(OUTPRE)export_cred.$(OBJEXT) \ + $(OUTPRE)export_name.$(OBJEXT) \ + $(OUTPRE)export_sec_context.$(OBJEXT) \ + $(OUTPRE)get_tkt_flags.$(OBJEXT) \ + $(OUTPRE)gssapi_krb5.$(OBJEXT) \ + $(OUTPRE)iakerb.$(OBJEXT) \ + $(OUTPRE)import_cred.$(OBJEXT) \ + $(OUTPRE)import_name.$(OBJEXT) \ + $(OUTPRE)import_sec_context.$(OBJEXT) \ + $(OUTPRE)indicate_mechs.$(OBJEXT) \ + $(OUTPRE)init_sec_context.$(OBJEXT) \ + $(OUTPRE)inq_context.$(OBJEXT) \ + $(OUTPRE)inq_cred.$(OBJEXT) \ + $(OUTPRE)inq_names.$(OBJEXT) \ + $(OUTPRE)k5seal.$(OBJEXT) \ + $(OUTPRE)k5sealiov.$(OBJEXT) \ + $(OUTPRE)k5sealv3.$(OBJEXT) \ + $(OUTPRE)k5sealv3iov.$(OBJEXT) \ + $(OUTPRE)k5unseal.$(OBJEXT) \ + $(OUTPRE)k5unsealiov.$(OBJEXT) \ + $(OUTPRE)krb5_gss_glue.$(OBJEXT) \ + $(OUTPRE)lucid_context.$(OBJEXT) \ + $(OUTPRE)naming_exts.$(OBJEXT) \ + $(OUTPRE)prf.$(OBJEXT) \ + $(OUTPRE)process_context_token.$(OBJEXT) \ + $(OUTPRE)rel_cred.$(OBJEXT) \ + $(OUTPRE)rel_oid.$(OBJEXT) \ + $(OUTPRE)rel_name.$(OBJEXT) \ + $(OUTPRE)s4u_gss_glue.$(OBJEXT) \ + $(OUTPRE)set_allowable_enctypes.$(OBJEXT) \ + $(OUTPRE)ser_sctx.$(OBJEXT) \ + $(OUTPRE)set_ccache.$(OBJEXT) \ + $(OUTPRE)store_cred.$(OBJEXT) \ + $(OUTPRE)util_cksum.$(OBJEXT) \ + $(OUTPRE)util_crypt.$(OBJEXT) \ + $(OUTPRE)util_seed.$(OBJEXT) \ + $(OUTPRE)util_seqnum.$(OBJEXT) \ + $(OUTPRE)val_cred.$(OBJEXT) \ + $(OUTPRE)wrap_size_limit.$(OBJEXT) \ + $(OUTPRE)gssapi_err_krb5.$(OBJEXT) + +# k5mech.$(OBJEXT) \ +# pname_to_uid.$(OBJEXT) + +STLIBOBJS = \ + accept_sec_context.o \ + acquire_cred.o \ + canon_name.o \ + compare_name.o \ + context_time.o \ + copy_ccache.o \ + cred_store.o \ + delete_sec_context.o \ + disp_name.o \ + disp_status.o \ + duplicate_name.o \ + export_cred.o \ + export_name.o \ + export_sec_context.o \ + get_tkt_flags.o \ + gssapi_krb5.o \ + iakerb.o \ + import_cred.o \ + import_name.o \ + import_sec_context.o \ + indicate_mechs.o \ + init_sec_context.o \ + inq_context.o \ + inq_cred.o \ + inq_names.o \ + k5seal.o \ + k5sealiov.o \ + k5sealv3.o \ + k5sealv3iov.o \ + k5unseal.o \ + k5unsealiov.o \ + krb5_gss_glue.o \ + lucid_context.o \ + naming_exts.o \ + prf.o \ + process_context_token.o \ + rel_cred.o \ + rel_oid.o \ + rel_name.o \ + s4u_gss_glue.o \ + set_allowable_enctypes.o \ + ser_sctx.o \ + set_ccache.o \ + store_cred.o \ + util_cksum.o \ + util_crypt.o \ + util_seed.o \ + util_seqnum.o \ + val_cred.o \ + wrap_size_limit.o \ + gssapi_err_krb5.o + +# k5mech.o \ +# pname_to_uid.o + +HDRS= $(ETHDRS) + +EHDRDIR=$(BUILDTOP)$(S)include$(S)gssapi +EXPORTED_HEADERS= gssapi_krb5.h + +##DOS##LIBOBJS = $(OBJS) + +GSSAPI_KRB5_HDR=$(EHDRDIR)$(S)gssapi_krb5.h + +all-windows: $(EHDRDIR) $(GSSAPI_KRB5_HDR) $(SRCS) $(HDRS) + +##DOS##$(EHDRDIR): +##DOS## mkdir $(EHDRDIR) + +MK_EHDRDIR=if test -d $(EHDRDIR); then :; else (set -x; mkdir $(EHDRDIR)); fi +##DOS##MK_EHDRDIR=rem + +$(GSSAPI_KRB5_HDR): $(srcdir)$(S)gssapi_krb5.h + @$(MK_EHDRDIR) + $(CP) $(srcdir)$(S)gssapi_krb5.h "$@" + +all-unix: $(SRCS) $(HDRS) $(GSSAPI_KRB5_HDR) +all-unix: all-libobjs + +error_map.h: $(top_srcdir)/util/gen-map.pl \ + $(top_srcdir)/util/ktemplate.pm Makefile + $(PERL) -I$(top_srcdir)/util $(top_srcdir)/util/gen-map.pl \ + -oerror_map.new \ + NAME=gsserrmap \ + KEY=OM_uint32 \ + VALUE="char *" \ + COMPARE=compare_OM_uint32 \ + FREEVALUE=free_string + $(RM) $@ + $(MV) error_map.new $@ + +clean-unix:: + $(RM) $(BUILDTOP)/include/gssapi/gssapi_krb5.h + -$(RM) error_map.h + +clean-unix:: clean-libobjs + $(RM) $(ETHDRS) $(ETSRCS) + +clean-windows:: + $(RM) $(EHDRDIR)\gssapi_krb5.h + $(RM) error_map.h + -if exist $(EHDRDIR)\nul rmdir $(EHDRDIR) + + +generate-files-mac: error_map.h + +install-headers-unix install: + @set -x; for f in $(EXPORTED_HEADERS) ; \ + do $(INSTALL_DATA) $(srcdir)/$$f \ + $(DESTDIR)$(KRB5_INCDIR)/gssapi/$$f ; \ + done + +depend: $(ETSRCS) $(ETHDRS) $(GSSAPI_KRB5_HDR) error_map.h + +install: + +@libobj_frag@ + diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c new file mode 100644 index 000000000000..580d08cbf53e --- /dev/null +++ b/src/lib/gssapi/krb5/accept_sec_context.c @@ -0,0 +1,1347 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2000, 2004, 2007, 2008 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. + */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * 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 FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +/* + * Copyright (c) 2006-2008, Novell, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The copyright holder's name is not used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif +#include <assert.h> + +#ifdef CFX_EXERCISE +#define CFX_ACCEPTOR_SUBKEY (time(0) & 1) +#else +#define CFX_ACCEPTOR_SUBKEY 1 +#endif + +#ifndef LEAN_CLIENT + +static OM_uint32 +create_constrained_deleg_creds(OM_uint32 *minor_status, + krb5_gss_cred_id_t verifier_cred_handle, + krb5_ticket *ticket, + krb5_gss_cred_id_t *out_cred, + krb5_context context) +{ + OM_uint32 major_status; + krb5_creds krb_creds; + krb5_data *data; + krb5_error_code code; + + assert(out_cred != NULL); + assert(verifier_cred_handle->usage == GSS_C_BOTH); + + memset(&krb_creds, 0, sizeof(krb_creds)); + krb_creds.client = ticket->enc_part2->client; + krb_creds.server = ticket->server; + krb_creds.keyblock = *(ticket->enc_part2->session); + krb_creds.ticket_flags = ticket->enc_part2->flags; + krb_creds.times = ticket->enc_part2->times; + krb_creds.magic = KV5M_CREDS; + krb_creds.authdata = NULL; + + code = encode_krb5_ticket(ticket, &data); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } + + krb_creds.ticket = *data; + + major_status = kg_compose_deleg_cred(minor_status, + verifier_cred_handle, + &krb_creds, + GSS_C_INDEFINITE, + out_cred, + NULL, + context); + + krb5_free_data(context, data); + + return major_status; +} + +/* Decode, decrypt and store the forwarded creds in the local ccache. */ +static krb5_error_code +rd_and_store_for_creds(context, auth_context, inbuf, out_cred) + krb5_context context; + krb5_auth_context auth_context; + krb5_data *inbuf; + krb5_gss_cred_id_t *out_cred; +{ + krb5_creds ** creds = NULL; + krb5_error_code retval; + krb5_ccache ccache = NULL; + krb5_gss_cred_id_t cred = NULL; + krb5_auth_context new_auth_ctx = NULL; + krb5_int32 flags_org; + + if ((retval = krb5_auth_con_getflags(context, auth_context, &flags_org))) + return retval; + krb5_auth_con_setflags(context, auth_context, + 0); + + /* + * By the time krb5_rd_cred is called here (after krb5_rd_req has been + * called in krb5_gss_accept_sec_context), the "keyblock" field of + * auth_context contains a pointer to the session key, and the + * "recv_subkey" field might contain a session subkey. Either of + * these (the "recv_subkey" if it isn't NULL, otherwise the + * "keyblock") might have been used to encrypt the encrypted part of + * the KRB_CRED message that contains the forwarded credentials. (The + * Java Crypto and Security Implementation from the DSTC in Australia + * always uses the session key. But apparently it never negotiates a + * subkey, so this code works fine against a JCSI client.) Up to the + * present, though, GSSAPI clients linked against the MIT code (which + * is almost all GSSAPI clients) don't encrypt the KRB_CRED message at + * all -- at this level. So if the first call to krb5_rd_cred fails, + * we should call it a second time with another auth context freshly + * created by krb5_auth_con_init. All of its keyblock fields will be + * NULL, so krb5_rd_cred will assume that the KRB_CRED message is + * unencrypted. (The MIT code doesn't actually send the KRB_CRED + * message in the clear -- the "authenticator" whose "checksum" ends up + * containing the KRB_CRED message does get encrypted.) + */ + if (krb5_rd_cred(context, auth_context, inbuf, &creds, NULL)) { + if ((retval = krb5_auth_con_init(context, &new_auth_ctx))) + goto cleanup; + krb5_auth_con_setflags(context, new_auth_ctx, 0); + if ((retval = krb5_rd_cred(context, new_auth_ctx, inbuf, + &creds, NULL))) + goto cleanup; + } + + if ((retval = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache))) { + ccache = NULL; + goto cleanup; + } + + if ((retval = krb5_cc_initialize(context, ccache, creds[0]->client))) + goto cleanup; + + if ((retval = krb5_cc_store_cred(context, ccache, creds[0]))) + goto cleanup; + + /* generate a delegated credential handle */ + if (out_cred) { + /* allocate memory for a cred_t... */ + if (!(cred = + (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec)))) { + retval = ENOMEM; /* out of memory? */ + goto cleanup; + } + + /* zero it out... */ + memset(cred, 0, sizeof(krb5_gss_cred_id_rec)); + + retval = k5_mutex_init(&cred->lock); + if (retval) { + xfree(cred); + cred = NULL; + goto cleanup; + } + + /* copy the client principle into it... */ + if ((retval = + kg_init_name(context, creds[0]->client, NULL, NULL, NULL, 0, + &cred->name))) { + k5_mutex_destroy(&cred->lock); + retval = ENOMEM; /* out of memory? */ + xfree(cred); /* clean up memory on failure */ + cred = NULL; + goto cleanup; + } + + cred->usage = GSS_C_INITIATE; /* we can't accept with this */ + /* cred->name already set */ + cred->keytab = NULL; /* no keytab associated with this... */ + cred->expire = creds[0]->times.endtime; /* store the end time */ + cred->ccache = ccache; /* the ccache containing the credential */ + cred->destroy_ccache = 1; + ccache = NULL; /* cred takes ownership so don't destroy */ + } + + /* If there were errors, there might have been a memory leak + if (!cred) + if ((retval = krb5_cc_close(context, ccache))) + goto cleanup; + */ +cleanup: + if (creds) + krb5_free_tgt_creds(context, creds); + + if (ccache) + (void)krb5_cc_destroy(context, ccache); + + if (out_cred) + *out_cred = cred; /* return credential */ + + if (new_auth_ctx) + krb5_auth_con_free(context, new_auth_ctx); + + krb5_auth_con_setflags(context, auth_context, flags_org); + + return retval; +} + + +/* + * Performs third leg of DCE authentication + */ +static OM_uint32 +kg_accept_dce(minor_status, context_handle, verifier_cred_handle, + input_token, input_chan_bindings, src_name, mech_type, + output_token, ret_flags, time_rec, delegated_cred_handle) + OM_uint32 *minor_status; + gss_ctx_id_t *context_handle; + gss_cred_id_t verifier_cred_handle; + gss_buffer_t input_token; + gss_channel_bindings_t input_chan_bindings; + gss_name_t *src_name; + gss_OID *mech_type; + gss_buffer_t output_token; + OM_uint32 *ret_flags; + OM_uint32 *time_rec; + gss_cred_id_t *delegated_cred_handle; +{ + krb5_error_code code; + krb5_gss_ctx_id_rec *ctx = 0; + krb5_timestamp now; + krb5_gss_name_t name = NULL; + krb5_ui_4 nonce = 0; + krb5_data ap_rep; + OM_uint32 major_status = GSS_S_FAILURE; + + output_token->length = 0; + output_token->value = NULL; + + if (mech_type) + *mech_type = GSS_C_NULL_OID; + /* return a bogus cred handle */ + if (delegated_cred_handle) + *delegated_cred_handle = GSS_C_NO_CREDENTIAL; + + ctx = (krb5_gss_ctx_id_rec *)*context_handle; + + code = krb5_timeofday(ctx->k5_context, &now); + if (code != 0) { + major_status = GSS_S_FAILURE; + goto fail; + } + + ap_rep.data = input_token->value; + ap_rep.length = input_token->length; + + code = krb5_rd_rep_dce(ctx->k5_context, + ctx->auth_context, + &ap_rep, + &nonce); + if (code != 0) { + major_status = GSS_S_FAILURE; + goto fail; + } + + ctx->established = 1; + + if (src_name) { + code = kg_duplicate_name(ctx->k5_context, ctx->there, &name); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + *src_name = (gss_name_t) name; + } + + if (mech_type) + *mech_type = ctx->mech_used; + + if (time_rec) + *time_rec = ctx->krb_times.endtime + ctx->k5_context->clockskew - now; + + /* Never return GSS_C_DELEG_FLAG since we don't support DCE credential + * delegation yet. */ + if (ret_flags) + *ret_flags = (ctx->gss_flags & ~GSS_C_DELEG_FLAG); + + *minor_status = 0; + + return GSS_S_COMPLETE; + +fail: + /* real failure code follows */ + + (void) krb5_gss_delete_sec_context(minor_status, (gss_ctx_id_t *) &ctx, + NULL); + *context_handle = GSS_C_NO_CONTEXT; + *minor_status = code; + + return major_status; +} + +static krb5_error_code +kg_process_extension(krb5_context context, + krb5_auth_context auth_context, + int ext_type, + krb5_data *ext_data, + krb5_gss_ctx_ext_t exts) +{ + krb5_error_code code = 0; + + assert(exts != NULL); + + switch (ext_type) { + case KRB5_GSS_EXTS_IAKERB_FINISHED: + if (exts->iakerb.conv == NULL) { + code = KRB5KRB_AP_ERR_MSG_TYPE; /* XXX */ + } else { + krb5_key key; + + code = krb5_auth_con_getrecvsubkey_k(context, auth_context, &key); + if (code != 0) + break; + + code = iakerb_verify_finished(context, key, exts->iakerb.conv, + ext_data); + if (code == 0) + exts->iakerb.verified = 1; + + krb5_k_free_key(context, key); + } + break; + default: + break; + } + + return code; +} + +static OM_uint32 +kg_accept_krb5(minor_status, context_handle, + verifier_cred_handle, input_token, + input_chan_bindings, src_name, mech_type, + output_token, ret_flags, time_rec, + delegated_cred_handle, exts) + OM_uint32 *minor_status; + gss_ctx_id_t *context_handle; + gss_cred_id_t verifier_cred_handle; + gss_buffer_t input_token; + gss_channel_bindings_t input_chan_bindings; + gss_name_t *src_name; + gss_OID *mech_type; + gss_buffer_t output_token; + OM_uint32 *ret_flags; + OM_uint32 *time_rec; + gss_cred_id_t *delegated_cred_handle; + krb5_gss_ctx_ext_t exts; +{ + krb5_context context; + unsigned char *ptr, *ptr2; + char *sptr; + OM_uint32 tmp; + size_t md5len; + krb5_gss_cred_id_t cred = 0; + krb5_data ap_rep, ap_req; + unsigned int i; + krb5_error_code code; + krb5_address addr, *paddr; + krb5_authenticator *authdat = 0; + krb5_checksum reqcksum; + krb5_gss_name_t name = NULL; + krb5_ui_4 gss_flags = 0; + krb5_gss_ctx_id_rec *ctx = NULL; + krb5_timestamp now; + gss_buffer_desc token; + krb5_auth_context auth_context = NULL; + krb5_ticket * ticket = NULL; + int option_id; + krb5_data option; + const gss_OID_desc *mech_used = NULL; + OM_uint32 major_status = GSS_S_FAILURE; + OM_uint32 tmp_minor_status; + krb5_error krb_error_data; + krb5_data scratch; + gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL; + krb5_gss_cred_id_t deleg_cred = NULL; + krb5int_access kaccess; + int cred_rcache = 0; + int no_encap = 0; + int token_deleg_flag = 0; + krb5_flags ap_req_options = 0; + krb5_enctype negotiated_etype; + krb5_authdata_context ad_context = NULL; + krb5_principal accprinc = NULL; + krb5_ap_req *request = NULL; + + code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION); + if (code) { + *minor_status = code; + return(GSS_S_FAILURE); + } + + code = krb5_gss_init_context(&context); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } + + /* set up returns to be freeable */ + + if (src_name) + *src_name = (gss_name_t) NULL; + output_token->length = 0; + output_token->value = NULL; + token.value = 0; + reqcksum.contents = 0; + ap_req.data = 0; + ap_rep.data = 0; + + if (mech_type) + *mech_type = GSS_C_NULL_OID; + /* return a bogus cred handle */ + if (delegated_cred_handle) + *delegated_cred_handle = GSS_C_NO_CREDENTIAL; + + /* handle default cred handle */ + if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) { + major_status = krb5_gss_acquire_cred(minor_status, GSS_C_NO_NAME, + GSS_C_INDEFINITE, GSS_C_NO_OID_SET, + GSS_C_ACCEPT, &defcred, + NULL, NULL); + if (major_status != GSS_S_COMPLETE) { + code = *minor_status; + goto fail; + } + verifier_cred_handle = defcred; + } + + /* Resolve any initiator state in the verifier cred and lock it. */ + major_status = kg_cred_resolve(minor_status, context, verifier_cred_handle, + GSS_C_NO_NAME); + if (GSS_ERROR(major_status)) { + code = *minor_status; + goto fail; + } + cred = (krb5_gss_cred_id_t)verifier_cred_handle; + + /* make sure the supplied credentials are valid for accept */ + + if ((cred->usage != GSS_C_ACCEPT) && + (cred->usage != GSS_C_BOTH)) { + code = 0; + major_status = GSS_S_NO_CRED; + goto fail; + } + + /* verify the token's integrity, and leave the token in ap_req. + figure out which mech oid was used, and save it */ + + ptr = (unsigned char *) input_token->value; + + if (!(code = g_verify_token_header(gss_mech_krb5, + &(ap_req.length), + &ptr, KG_TOK_CTX_AP_REQ, + input_token->length, 1))) { + mech_used = gss_mech_krb5; + } else if ((code == G_WRONG_MECH) + &&!(code = g_verify_token_header((gss_OID) gss_mech_iakerb, + &(ap_req.length), + &ptr, KG_TOK_CTX_AP_REQ, + input_token->length, 1))) { + mech_used = gss_mech_iakerb; + } else if ((code == G_WRONG_MECH) + &&!(code = g_verify_token_header((gss_OID) gss_mech_krb5_wrong, + &(ap_req.length), + &ptr, KG_TOK_CTX_AP_REQ, + input_token->length, 1))) { + mech_used = gss_mech_krb5_wrong; + } else if ((code == G_WRONG_MECH) && + !(code = g_verify_token_header(gss_mech_krb5_old, + &(ap_req.length), + &ptr, KG_TOK_CTX_AP_REQ, + input_token->length, 1))) { + /* + * Previous versions of this library used the old mech_id + * and some broken behavior (wrong IV on checksum + * encryption). We support the old mech_id for + * compatibility, and use it to decide when to use the + * old behavior. + */ + mech_used = gss_mech_krb5_old; + } else if (code == G_WRONG_TOKID) { + major_status = GSS_S_CONTINUE_NEEDED; + code = KRB5KRB_AP_ERR_MSG_TYPE; + mech_used = gss_mech_krb5; + goto fail; + } else if (code == G_BAD_TOK_HEADER) { + /* DCE style not encapsulated */ + ap_req.length = input_token->length; + ap_req.data = input_token->value; + mech_used = gss_mech_krb5; + no_encap = 1; + } else { + major_status = GSS_S_DEFECTIVE_TOKEN; + goto fail; + } + + sptr = (char *) ptr; + TREAD_STR(sptr, ap_req.data, ap_req.length); + + /* construct the sender_addr */ + + if ((input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) && + (input_chan_bindings->initiator_addrtype == GSS_C_AF_INET)) { + /* XXX is this right? */ + addr.addrtype = ADDRTYPE_INET; + addr.length = input_chan_bindings->initiator_address.length; + addr.contents = input_chan_bindings->initiator_address.value; + + paddr = &addr; + } else { + paddr = NULL; + } + + /* decode the AP_REQ message */ + code = decode_krb5_ap_req(&ap_req, &request); + if (code) { + major_status = GSS_S_FAILURE; + goto done; + } + ticket = request->ticket; + + /* decode the message */ + + if ((code = krb5_auth_con_init(context, &auth_context))) { + major_status = GSS_S_FAILURE; + save_error_info((OM_uint32)code, context); + goto fail; + } + if (cred->rcache) { + cred_rcache = 1; + if ((code = krb5_auth_con_setrcache(context, auth_context, cred->rcache))) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + if ((code = krb5_auth_con_setaddrs(context, auth_context, NULL, paddr))) { + major_status = GSS_S_FAILURE; + goto fail; + } + + /* Limit the encryption types negotiated (if requested). */ + if (cred->req_enctypes) { + if ((code = krb5_auth_con_setpermetypes(context, auth_context, + cred->req_enctypes))) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + + if (!cred->default_identity) { + if ((code = kg_acceptor_princ(context, cred->name, &accprinc))) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + + code = krb5_rd_req_decoded(context, &auth_context, request, accprinc, + cred->keytab, &ap_req_options, NULL); + + krb5_free_principal(context, accprinc); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + krb5_auth_con_setflags(context, auth_context, + KRB5_AUTH_CONTEXT_DO_SEQUENCE); + + krb5_auth_con_getauthenticator(context, auth_context, &authdat); + +#if 0 + /* make sure the necessary parts of the authdat are present */ + + if ((authdat->authenticator->subkey == NULL) || + (authdat->ticket->enc_part2 == NULL)) { + code = KG_NO_SUBKEY; + major_status = GSS_S_FAILURE; + goto fail; + } +#endif + + if (authdat->checksum == NULL) { + /* + * Some SMB client implementations use handcrafted GSSAPI code that + * does not provide a checksum. MS-KILE documents that the Microsoft + * implementation considers a missing checksum acceptable; the server + * assumes all flags are unset in this case, and does not check channel + * bindings. + */ + gss_flags = 0; + } else if (authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) { + /* Samba does not send 0x8003 GSS-API checksums */ + krb5_boolean valid; + krb5_key subkey; + krb5_data zero; + + code = krb5_auth_con_getkey_k(context, auth_context, &subkey); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + + zero.length = 0; + zero.data = ""; + + code = krb5_k_verify_checksum(context, + subkey, + KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM, + &zero, + authdat->checksum, + &valid); + krb5_k_free_key(context, subkey); + if (code || !valid) { + major_status = GSS_S_BAD_SIG; + goto fail; + } + + /* Use ap_options from the request to guess the mutual flag. */ + gss_flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG; + if (ap_req_options & AP_OPTS_MUTUAL_REQUIRED) + gss_flags |= GSS_C_MUTUAL_FLAG; + } else { + /* gss krb5 v1 */ + + /* stash this now, for later. */ + code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, &md5len); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + + /* verify that the checksum is correct */ + + /* + The checksum may be either exactly 24 bytes, in which case + no options are specified, or greater than 24 bytes, in which case + one or more options are specified. Currently, the only valid + option is KRB5_GSS_FOR_CREDS_OPTION ( = 1 ). + */ + + if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) || + (authdat->checksum->length < 24)) { + code = 0; + major_status = GSS_S_BAD_BINDINGS; + goto fail; + } + + ptr = (unsigned char *) authdat->checksum->contents; + + TREAD_INT(ptr, tmp, 0); + + if (tmp != md5len) { + code = KG_BAD_LENGTH; + major_status = GSS_S_FAILURE; + goto fail; + } + + /* + The following section of code attempts to implement the + optional channel binding facility as described in RFC2743. + + Since this facility is optional channel binding may or may + not have been provided by either the client or the server. + + If the server has specified input_chan_bindings equal to + GSS_C_NO_CHANNEL_BINDINGS then we skip the check. If + the server does provide channel bindings then we compute + a checksum and compare against those provided by the + client. */ + + if ((code = kg_checksum_channel_bindings(context, + input_chan_bindings, + &reqcksum))) { + major_status = GSS_S_BAD_BINDINGS; + goto fail; + } + + /* Always read the clients bindings - eventhough we might ignore them */ + TREAD_STR(ptr, ptr2, reqcksum.length); + + if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS ) { + if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) { + xfree(reqcksum.contents); + reqcksum.contents = 0; + code = 0; + major_status = GSS_S_BAD_BINDINGS; + goto fail; + } + + } + + xfree(reqcksum.contents); + reqcksum.contents = 0; + + /* Read the token flags. Remember if GSS_C_DELEG_FLAG was set, but + * mask it out until we actually read a delegated credential. */ + TREAD_INT(ptr, gss_flags, 0); + token_deleg_flag = (gss_flags & GSS_C_DELEG_FLAG); + gss_flags &= ~GSS_C_DELEG_FLAG; + + /* if the checksum length > 24, there are options to process */ + + i = authdat->checksum->length - 24; + if (i && token_deleg_flag) { + if (i >= 4) { + TREAD_INT16(ptr, option_id, 0); + TREAD_INT16(ptr, option.length, 0); + i -= 4; + + if (i < option.length) { + code = KG_BAD_LENGTH; + major_status = GSS_S_FAILURE; + goto fail; + } + + /* have to use ptr2, since option.data is wrong type and + macro uses ptr as both lvalue and rvalue */ + + TREAD_STR(ptr, ptr2, option.length); + option.data = (char *) ptr2; + + i -= option.length; + + if (option_id != KRB5_GSS_FOR_CREDS_OPTION) { + major_status = GSS_S_FAILURE; + goto fail; + } + + /* store the delegated credential */ + + code = rd_and_store_for_creds(context, auth_context, &option, + (delegated_cred_handle) ? + &deleg_cred : NULL); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + + gss_flags |= GSS_C_DELEG_FLAG; + } /* if i >= 4 */ + /* ignore any additional trailing data, for now */ + } + while (i > 0) { + /* Process Type-Length-Data options */ + if (i < 8) { + code = KG_BAD_LENGTH; + major_status = GSS_S_FAILURE; + goto fail; + } + TREAD_INT(ptr, option_id, 1); + TREAD_INT(ptr, option.length, 1); + i -= 8; + if (i < option.length) { + code = KG_BAD_LENGTH; + major_status = GSS_S_FAILURE; + goto fail; + } + TREAD_STR(ptr, ptr2, option.length); + option.data = (char *)ptr2; + + i -= option.length; + + code = kg_process_extension(context, auth_context, + option_id, &option, exts); + if (code != 0) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + } + + if (exts->iakerb.conv && !exts->iakerb.verified) { + major_status = GSS_S_BAD_SIG; + goto fail; + } + + /* only DCE_STYLE clients are allowed to send raw AP-REQs */ + if (no_encap != ((gss_flags & GSS_C_DCE_STYLE) != 0)) { + major_status = GSS_S_DEFECTIVE_TOKEN; + goto fail; + } + + /* create the ctx struct and start filling it in */ + + if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec))) + == NULL) { + code = ENOMEM; + major_status = GSS_S_FAILURE; + goto fail; + } + + memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec)); + ctx->magic = KG_CONTEXT; + ctx->mech_used = (gss_OID) mech_used; + ctx->auth_context = auth_context; + ctx->initiate = 0; + ctx->gss_flags = (GSS_C_TRANS_FLAG | + ((gss_flags) & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG | + GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | + GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG | + GSS_C_DCE_STYLE | GSS_C_IDENTIFY_FLAG | + GSS_C_EXTENDED_ERROR_FLAG))); + ctx->seed_init = 0; + ctx->cred_rcache = cred_rcache; + + /* XXX move this into gss_name_t */ + if ( (code = krb5_merge_authdata(context, + ticket->enc_part2->authorization_data, + authdat->authorization_data, + &ctx->authdata))) { + major_status = GSS_S_FAILURE; + goto fail; + } + if ((code = kg_init_name(context, ticket->server, NULL, NULL, NULL, 0, + &ctx->here))) { + major_status = GSS_S_FAILURE; + goto fail; + } + if ((code = krb5_auth_con_get_authdata_context(context, auth_context, + &ad_context))) { + major_status = GSS_S_FAILURE; + goto fail; + } + if ((code = kg_init_name(context, authdat->client, NULL, NULL, + ad_context, KG_INIT_NAME_NO_COPY, &ctx->there))) { + major_status = GSS_S_FAILURE; + goto fail; + } + /* Now owned by ctx->there */ + authdat->client = NULL; + krb5_auth_con_set_authdata_context(context, auth_context, NULL); + + if ((code = krb5_auth_con_getrecvsubkey_k(context, auth_context, + &ctx->subkey))) { + major_status = GSS_S_FAILURE; + goto fail; + } + + /* use the session key if the subkey isn't present */ + + if (ctx->subkey == NULL) { + if ((code = krb5_auth_con_getkey_k(context, auth_context, + &ctx->subkey))) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + + if (ctx->subkey == NULL) { + /* this isn't a very good error, but it's not clear to me this + can actually happen */ + major_status = GSS_S_FAILURE; + code = KRB5KDC_ERR_NULL_KEY; + goto fail; + } + + ctx->enc = NULL; + ctx->seq = NULL; + ctx->have_acceptor_subkey = 0; + /* DCE_STYLE implies acceptor_subkey */ + if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0) { + code = kg_setup_keys(context, ctx, ctx->subkey, &ctx->cksumtype); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + ctx->krb_times = ticket->enc_part2->times; /* struct copy */ + ctx->krb_flags = ticket->enc_part2->flags; + + if (delegated_cred_handle != NULL && + deleg_cred == NULL && /* no unconstrained delegation */ + cred->usage == GSS_C_BOTH && + (ticket->enc_part2->flags & TKT_FLG_FORWARDABLE)) { + /* + * Now, we always fabricate a delegated credentials handle + * containing the service ticket to ourselves, which can be + * used for S4U2Proxy. + */ + major_status = create_constrained_deleg_creds(minor_status, cred, + ticket, &deleg_cred, + context); + if (GSS_ERROR(major_status)) + goto fail; + ctx->gss_flags |= GSS_C_DELEG_FLAG; + } + + { + krb5_int32 seq_temp; + krb5_auth_con_getremoteseqnumber(context, auth_context, &seq_temp); + ctx->seq_recv = seq_temp; + } + + if ((code = krb5_timeofday(context, &now))) { + major_status = GSS_S_FAILURE; + goto fail; + } + + code = g_seqstate_init(&ctx->seqstate, ctx->seq_recv, + (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0, + (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, + ctx->proto); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + + /* DCE_STYLE implies mutual authentication */ + if (ctx->gss_flags & GSS_C_DCE_STYLE) + ctx->gss_flags |= GSS_C_MUTUAL_FLAG; + + /* at this point, the entire context structure is filled in, + so it can be released. */ + + /* generate an AP_REP if necessary */ + + if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) { + unsigned char * ptr3; + krb5_int32 seq_temp; + int cfx_generate_subkey; + + /* + * Do not generate a subkey per RFC 4537 unless we are upgrading to CFX, + * because pre-CFX tokens do not indicate which key to use. (Note that + * DCE_STYLE implies that we will use a subkey.) + */ + if (ctx->proto == 0 && + (ctx->gss_flags & GSS_C_DCE_STYLE) == 0 && + (ap_req_options & AP_OPTS_USE_SUBKEY)) { + code = (*kaccess.auth_con_get_subkey_enctype)(context, + auth_context, + &negotiated_etype); + if (code != 0) { + major_status = GSS_S_FAILURE; + goto fail; + } + + switch (negotiated_etype) { + case ENCTYPE_DES_CBC_MD5: + case ENCTYPE_DES_CBC_MD4: + case ENCTYPE_DES_CBC_CRC: + case ENCTYPE_DES3_CBC_SHA1: + case ENCTYPE_ARCFOUR_HMAC: + case ENCTYPE_ARCFOUR_HMAC_EXP: + /* RFC 4121 accidentally omits RC4-HMAC-EXP as a "not-newer" + * enctype, even though RFC 4757 treats it as one. */ + ap_req_options &= ~(AP_OPTS_USE_SUBKEY); + break; + } + } + + if (ctx->proto == 1 || (ctx->gss_flags & GSS_C_DCE_STYLE) || + (ap_req_options & AP_OPTS_USE_SUBKEY)) + cfx_generate_subkey = CFX_ACCEPTOR_SUBKEY; + else + cfx_generate_subkey = 0; + + if (cfx_generate_subkey) { + krb5_int32 acflags; + code = krb5_auth_con_getflags(context, auth_context, &acflags); + if (code == 0) { + acflags |= KRB5_AUTH_CONTEXT_USE_SUBKEY; + code = krb5_auth_con_setflags(context, auth_context, acflags); + } + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + + if ((code = krb5_mk_rep(context, auth_context, &ap_rep))) { + major_status = GSS_S_FAILURE; + goto fail; + } + + krb5_auth_con_getlocalseqnumber(context, auth_context, &seq_temp); + ctx->seq_send = seq_temp & 0xffffffffL; + + if (cfx_generate_subkey) { + /* Get the new acceptor subkey. With the code above, there + should always be one if we make it to this point. */ + code = krb5_auth_con_getsendsubkey_k(context, auth_context, + &ctx->acceptor_subkey); + if (code != 0) { + major_status = GSS_S_FAILURE; + goto fail; + } + ctx->have_acceptor_subkey = 1; + + code = kg_setup_keys(context, ctx, ctx->acceptor_subkey, + &ctx->acceptor_subkey_cksumtype); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + + /* the reply token hasn't been sent yet, but that's ok. */ + if (ctx->gss_flags & GSS_C_DCE_STYLE) { + assert(ctx->have_acceptor_subkey); + + /* in order to force acceptor subkey to be used, don't set PROT_READY */ + + /* Raw AP-REP is returned */ + code = data_to_gss(&ap_rep, output_token); + if (code) + { + major_status = GSS_S_FAILURE; + goto fail; + } + + ctx->established = 0; + + *context_handle = (gss_ctx_id_t)ctx; + *minor_status = 0; + major_status = GSS_S_CONTINUE_NEEDED; + + /* Only last leg should set return arguments */ + goto fail; + } else + ctx->gss_flags |= GSS_C_PROT_READY_FLAG; + + ctx->established = 1; + + token.length = g_token_size(mech_used, ap_rep.length); + + if ((token.value = (unsigned char *) gssalloc_malloc(token.length)) + == NULL) { + major_status = GSS_S_FAILURE; + code = ENOMEM; + goto fail; + } + ptr3 = token.value; + g_make_token_header(mech_used, ap_rep.length, + &ptr3, KG_TOK_CTX_AP_REP); + + TWRITE_STR(ptr3, ap_rep.data, ap_rep.length); + + ctx->established = 1; + + } else { + token.length = 0; + token.value = NULL; + ctx->seq_send = ctx->seq_recv; + + ctx->established = 1; + } + + /* set the return arguments */ + + if (src_name) { + code = kg_duplicate_name(context, ctx->there, &name); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + + if (mech_type) + *mech_type = (gss_OID) mech_used; + + /* Add the maximum allowable clock skew as a grace period for context + * expiration, just as we do for the ticket. */ + if (time_rec) + *time_rec = ctx->krb_times.endtime + context->clockskew - now; + + if (ret_flags) + *ret_flags = ctx->gss_flags; + + *context_handle = (gss_ctx_id_t)ctx; + *output_token = token; + + if (src_name) + *src_name = (gss_name_t) name; + + if (delegated_cred_handle) + *delegated_cred_handle = (gss_cred_id_t) deleg_cred; + + /* finally! */ + + *minor_status = 0; + major_status = GSS_S_COMPLETE; + +fail: + if (authdat) + krb5_free_authenticator(context, authdat); + /* The ctx structure has the handle of the auth_context */ + if (auth_context && !ctx) { + if (cred_rcache) + (void)krb5_auth_con_setrcache(context, auth_context, NULL); + + krb5_auth_con_free(context, auth_context); + } + if (reqcksum.contents) + xfree(reqcksum.contents); + if (ap_rep.data) + krb5_free_data_contents(context, &ap_rep); + if (major_status == GSS_S_COMPLETE || + (major_status == GSS_S_CONTINUE_NEEDED && code != KRB5KRB_AP_ERR_MSG_TYPE)) { + ctx->k5_context = context; + context = NULL; + goto done; + } + + /* from here on is the real "fail" code */ + + if (ctx) + (void) krb5_gss_delete_sec_context(&tmp_minor_status, + (gss_ctx_id_t *) &ctx, NULL); + if (deleg_cred) { /* free memory associated with the deleg credential */ + if (deleg_cred->ccache) + (void)krb5_cc_close(context, deleg_cred->ccache); + if (deleg_cred->name) + kg_release_name(context, &deleg_cred->name); + xfree(deleg_cred); + } + if (token.value) + xfree(token.value); + if (name) { + (void) kg_release_name(context, &name); + } + + *minor_status = code; + + /* We may have failed before being able to read the GSS flags from the + * authenticator, so also check the request AP options. */ + if (cred != NULL && request != NULL && + ((gss_flags & GSS_C_MUTUAL_FLAG) || + (request->ap_options & AP_OPTS_MUTUAL_REQUIRED) || + major_status == GSS_S_CONTINUE_NEEDED)) { + unsigned int tmsglen; + int toktype; + + /* + * The client is expecting a response, so we can send an + * error token back + */ + memset(&krb_error_data, 0, sizeof(krb_error_data)); + + code -= ERROR_TABLE_BASE_krb5; + if (code < 0 || code > KRB_ERR_MAX) + code = 60 /* KRB_ERR_GENERIC */; + + krb_error_data.error = code; + (void) krb5_us_timeofday(context, &krb_error_data.stime, + &krb_error_data.susec); + + krb_error_data.server = ticket->server; + code = krb5_mk_error(context, &krb_error_data, &scratch); + if (code) + goto done; + + tmsglen = scratch.length; + toktype = KG_TOK_CTX_ERROR; + + token.length = g_token_size(mech_used, tmsglen); + token.value = gssalloc_malloc(token.length); + if (!token.value) + goto done; + + ptr = token.value; + g_make_token_header(mech_used, tmsglen, &ptr, toktype); + + TWRITE_STR(ptr, scratch.data, scratch.length); + krb5_free_data_contents(context, &scratch); + + *output_token = token; + } + +done: + krb5_free_ap_req(context, request); + if (cred) + k5_mutex_unlock(&cred->lock); + if (defcred) + krb5_gss_release_cred(&tmp_minor_status, &defcred); + if (context) { + if (major_status && *minor_status) + save_error_info(*minor_status, context); + krb5_free_context(context); + } + return (major_status); +} +#endif /* LEAN_CLIENT */ + +OM_uint32 KRB5_CALLCONV +krb5_gss_accept_sec_context_ext( + OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_cred_id_t verifier_cred_handle, + gss_buffer_t input_token, + gss_channel_bindings_t input_chan_bindings, + gss_name_t *src_name, + gss_OID *mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + gss_cred_id_t *delegated_cred_handle, + krb5_gss_ctx_ext_t exts) +{ + krb5_gss_ctx_id_rec *ctx = (krb5_gss_ctx_id_rec *)*context_handle; + + /* + * Context handle must be unspecified. Actually, it must be + * non-established, but currently, accept_sec_context never returns + * a non-established context handle. + */ + /*SUPPRESS 29*/ + if (ctx != NULL) { + if (ctx->established == 0 && (ctx->gss_flags & GSS_C_DCE_STYLE)) { + return kg_accept_dce(minor_status, context_handle, + verifier_cred_handle, input_token, + input_chan_bindings, src_name, mech_type, + output_token, ret_flags, time_rec, + delegated_cred_handle); + } else { + *minor_status = EINVAL; + save_error_string(EINVAL, "accept_sec_context called with existing context handle"); + return GSS_S_FAILURE; + } + } + + return kg_accept_krb5(minor_status, context_handle, + verifier_cred_handle, input_token, + input_chan_bindings, src_name, mech_type, + output_token, ret_flags, time_rec, + delegated_cred_handle, exts); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_accept_sec_context(minor_status, context_handle, + verifier_cred_handle, input_token, + input_chan_bindings, src_name, mech_type, + output_token, ret_flags, time_rec, + delegated_cred_handle) + OM_uint32 *minor_status; + gss_ctx_id_t *context_handle; + gss_cred_id_t verifier_cred_handle; + gss_buffer_t input_token; + gss_channel_bindings_t input_chan_bindings; + gss_name_t *src_name; + gss_OID *mech_type; + gss_buffer_t output_token; + OM_uint32 *ret_flags; + OM_uint32 *time_rec; + gss_cred_id_t *delegated_cred_handle; +{ + krb5_gss_ctx_ext_rec exts; + + memset(&exts, 0, sizeof(exts)); + + return krb5_gss_accept_sec_context_ext(minor_status, + context_handle, + verifier_cred_handle, + input_token, + input_chan_bindings, + src_name, + mech_type, + output_token, + ret_flags, + time_rec, + delegated_cred_handle, + &exts); +} diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c new file mode 100644 index 000000000000..03ee25ec1861 --- /dev/null +++ b/src/lib/gssapi/krb5/acquire_cred.c @@ -0,0 +1,1261 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2000, 2007-2010 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. + */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * 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 FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" +#ifdef HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif + +#ifdef USE_LEASH +#ifdef _WIN64 +#define LEASH_DLL "leashw64.dll" +#else +#define LEASH_DLL "leashw32.dll" +#endif +static void (*pLeash_AcquireInitialTicketsIfNeeded)(krb5_context,krb5_principal,char*,int) = NULL; +static HANDLE hLeashDLL = INVALID_HANDLE_VALUE; +#endif + +#ifndef LEAN_CLIENT +k5_mutex_t gssint_krb5_keytab_lock = K5_MUTEX_PARTIAL_INITIALIZER; +static char *krb5_gss_keytab = NULL; + +/* Heimdal calls this gsskrb5_register_acceptor_identity. */ +OM_uint32 +gss_krb5int_register_acceptor_identity(OM_uint32 *minor_status, + const gss_OID desired_mech, + const gss_OID desired_object, + gss_buffer_t value) +{ + char *new = NULL, *old; + int err; + + err = gss_krb5int_initialize_library(); + if (err != 0) + return GSS_S_FAILURE; + + if (value->value != NULL) { + new = strdup((char *)value->value); + if (new == NULL) + return GSS_S_FAILURE; + } + + k5_mutex_lock(&gssint_krb5_keytab_lock); + old = krb5_gss_keytab; + krb5_gss_keytab = new; + k5_mutex_unlock(&gssint_krb5_keytab_lock); + free(old); + return GSS_S_COMPLETE; +} + +/* Try to verify that keytab contains at least one entry for name. Return 0 if + * it does, KRB5_KT_NOTFOUND if it doesn't, or another error as appropriate. */ +static krb5_error_code +check_keytab(krb5_context context, krb5_keytab kt, krb5_gss_name_t name) +{ + krb5_error_code code; + krb5_keytab_entry ent; + krb5_kt_cursor cursor; + krb5_principal accprinc = NULL; + krb5_boolean match; + char *princname; + + if (name->service == NULL) { + code = krb5_kt_get_entry(context, kt, name->princ, 0, 0, &ent); + if (code == 0) + krb5_kt_free_entry(context, &ent); + return code; + } + + /* If we can't iterate through the keytab, skip this check. */ + if (kt->ops->start_seq_get == NULL) + return 0; + + /* Get the partial principal for the acceptor name. */ + code = kg_acceptor_princ(context, name, &accprinc); + if (code) + return code; + + /* Scan the keytab for host-based entries matching accprinc. */ + code = krb5_kt_start_seq_get(context, kt, &cursor); + if (code) + goto cleanup; + while ((code = krb5_kt_next_entry(context, kt, &ent, &cursor)) == 0) { + match = krb5_sname_match(context, accprinc, ent.principal); + (void)krb5_free_keytab_entry_contents(context, &ent); + if (match) + break; + } + (void)krb5_kt_end_seq_get(context, kt, &cursor); + if (code == KRB5_KT_END) { + code = KRB5_KT_NOTFOUND; + if (krb5_unparse_name(context, accprinc, &princname) == 0) { + k5_setmsg(context, code, _("No key table entry found matching %s"), + princname); + free(princname); + } + } + +cleanup: + krb5_free_principal(context, accprinc); + return code; +} + +/* get credentials corresponding to a key in the krb5 keytab. + If successful, set the keytab-specific fields in cred +*/ + +static OM_uint32 +acquire_accept_cred(krb5_context context, OM_uint32 *minor_status, + krb5_keytab req_keytab, const char *rcname, + krb5_gss_cred_id_rec *cred) +{ + OM_uint32 major; + krb5_error_code code; + krb5_keytab kt = NULL; + krb5_rcache rc = NULL; + + assert(cred->keytab == NULL); + + /* If we have an explicit rcache name, open it. */ + if (rcname != NULL) { + code = krb5_rc_resolve_full(context, &rc, rcname); + if (code) { + major = GSS_S_FAILURE; + goto cleanup; + } + code = krb5_rc_recover_or_initialize(context, rc, context->clockskew); + if (code) { + major = GSS_S_FAILURE; + goto cleanup; + } + } + + if (req_keytab != NULL) { + code = krb5_kt_dup(context, req_keytab, &kt); + } else { + k5_mutex_lock(&gssint_krb5_keytab_lock); + if (krb5_gss_keytab != NULL) { + code = krb5_kt_resolve(context, krb5_gss_keytab, &kt); + k5_mutex_unlock(&gssint_krb5_keytab_lock); + } else { + k5_mutex_unlock(&gssint_krb5_keytab_lock); + code = krb5_kt_default(context, &kt); + } + } + if (code) { + major = GSS_S_CRED_UNAVAIL; + goto cleanup; + } + + if (cred->name != NULL) { + /* Make sure we have keys matching the desired name in the keytab. */ + code = check_keytab(context, kt, cred->name); + if (code) { + if (code == KRB5_KT_NOTFOUND) { + k5_change_error_message_code(context, code, KG_KEYTAB_NOMATCH); + code = KG_KEYTAB_NOMATCH; + } + major = GSS_S_CRED_UNAVAIL; + goto cleanup; + } + + if (rc == NULL) { + /* Open the replay cache for this principal. */ + code = krb5_get_server_rcache(context, &cred->name->princ->data[0], + &rc); + if (code) { + major = GSS_S_FAILURE; + goto cleanup; + } + } + } else { + /* Make sure we have a keytab with keys in it. */ + code = krb5_kt_have_content(context, kt); + if (code) { + major = GSS_S_CRED_UNAVAIL; + goto cleanup; + } + } + + cred->keytab = kt; + kt = NULL; + cred->rcache = rc; + rc = NULL; + major = GSS_S_COMPLETE; + +cleanup: + if (kt != NULL) + krb5_kt_close(context, kt); + if (rc != NULL) + krb5_rc_close(context, rc); + *minor_status = code; + return major; +} +#endif /* LEAN_CLIENT */ + +#ifdef USE_LEASH +static krb5_error_code +get_ccache_leash(krb5_context context, krb5_principal desired_princ, + krb5_ccache *ccache_out) +{ + krb5_error_code code; + krb5_ccache ccache; + char ccname[256] = ""; + + *ccache_out = NULL; + + if (hLeashDLL == INVALID_HANDLE_VALUE) { + hLeashDLL = LoadLibrary(LEASH_DLL); + if (hLeashDLL != INVALID_HANDLE_VALUE) { + (FARPROC) pLeash_AcquireInitialTicketsIfNeeded = + GetProcAddress(hLeashDLL, "not_an_API_Leash_AcquireInitialTicketsIfNeeded"); + } + } + + if (pLeash_AcquireInitialTicketsIfNeeded) { + pLeash_AcquireInitialTicketsIfNeeded(context, desired_princ, ccname, + sizeof(ccname)); + if (!ccname[0]) + return KRB5_CC_NOTFOUND; + + code = krb5_cc_resolve(context, ccname, &ccache); + if (code) + return code; + } else { + /* leash dll not available, open the default credential cache. */ + code = krb5int_cc_default(context, &ccache); + if (code) + return code; + } + + *ccache_out = ccache; + return 0; +} +#endif /* USE_LEASH */ + +/* Set fields in cred according to a ccache config entry whose key (in + * principal form) is config_princ and whose value is value. */ +static krb5_error_code +scan_cc_config(krb5_context context, krb5_gss_cred_id_rec *cred, + krb5_const_principal config_princ, const krb5_data *value) +{ + krb5_error_code code; + krb5_data data0 = empty_data(); + + if (config_princ->length != 2) + return 0; + if (data_eq_string(config_princ->data[1], KRB5_CC_CONF_PROXY_IMPERSONATOR) + && cred->impersonator == NULL) { + code = krb5int_copy_data_contents_add0(context, value, &data0); + if (code) + return code; + code = krb5_parse_name(context, data0.data, &cred->impersonator); + krb5_free_data_contents(context, &data0); + if (code) + return code; + } else if (data_eq_string(config_princ->data[1], KRB5_CC_CONF_REFRESH_TIME) + && cred->refresh_time == 0) { + code = krb5int_copy_data_contents_add0(context, value, &data0); + if (code) + return code; + cred->refresh_time = atol(data0.data); + krb5_free_data_contents(context, &data0); + } + return 0; +} + +/* Return true if it appears that we can non-interactively get initial + * tickets for cred. */ +static krb5_boolean +can_get_initial_creds(krb5_context context, krb5_gss_cred_id_rec *cred) +{ + krb5_error_code code; + krb5_keytab_entry entry; + + if (cred->password != NULL) + return TRUE; + + if (cred->client_keytab == NULL) + return FALSE; + + /* If we don't know the client principal yet, check for any keytab keys. */ + if (cred->name == NULL) + return !krb5_kt_have_content(context, cred->client_keytab); + + /* Check if we have a keytab key for the client principal. */ + code = krb5_kt_get_entry(context, cred->client_keytab, cred->name->princ, + 0, 0, &entry); + if (code) { + krb5_clear_error_message(context); + return FALSE; + } + krb5_free_keytab_entry_contents(context, &entry); + return TRUE; +} + +/* Scan cred->ccache for name, expiry time, impersonator, refresh time. */ +static krb5_error_code +scan_ccache(krb5_context context, krb5_gss_cred_id_rec *cred) +{ + krb5_error_code code; + krb5_ccache ccache = cred->ccache; + krb5_principal ccache_princ = NULL, tgt_princ = NULL; + krb5_data *realm; + krb5_cc_cursor cursor; + krb5_creds creds; + krb5_timestamp endtime; + krb5_boolean is_tgt; + + /* Turn on NOTICKET, as we don't need session keys here. */ + code = krb5_cc_set_flags(context, ccache, KRB5_TC_NOTICKET); + if (code) + return code; + + /* Credentials cache principal must match the initiator name. */ + code = krb5_cc_get_principal(context, ccache, &ccache_princ); + if (code != 0) + goto cleanup; + if (cred->name != NULL && + !krb5_principal_compare(context, ccache_princ, cred->name->princ)) { + code = KG_CCACHE_NOMATCH; + goto cleanup; + } + + /* Save the ccache principal as the credential name if not already set. */ + if (!cred->name) { + code = kg_init_name(context, ccache_princ, NULL, NULL, NULL, + KG_INIT_NAME_NO_COPY, &cred->name); + if (code) + goto cleanup; + ccache_princ = NULL; + } + + assert(cred->name->princ != NULL); + realm = krb5_princ_realm(context, cred->name->princ); + code = krb5_build_principal_ext(context, &tgt_princ, + realm->length, realm->data, + KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME, + realm->length, realm->data, + 0); + if (code) + return code; + + /* If there's a tgt for the principal's local realm in here, use its expiry + * time. Otherwise use the first key. */ + code = krb5_cc_start_seq_get(context, ccache, &cursor); + if (code) { + krb5_free_principal(context, tgt_princ); + return code; + } + while (!(code = krb5_cc_next_cred(context, ccache, &cursor, &creds))) { + if (krb5_is_config_principal(context, creds.server)) { + code = scan_cc_config(context, cred, creds.server, &creds.ticket); + krb5_free_cred_contents(context, &creds); + if (code) + break; + continue; + } + is_tgt = krb5_principal_compare(context, tgt_princ, creds.server); + endtime = creds.times.endtime; + krb5_free_cred_contents(context, &creds); + if (is_tgt) + cred->have_tgt = TRUE; + if (is_tgt || cred->expire == 0) + cred->expire = endtime; + } + krb5_cc_end_seq_get(context, ccache, &cursor); + if (code && code != KRB5_CC_END) + goto cleanup; + code = 0; + + if (cred->expire == 0 && !can_get_initial_creds(context, cred)) { + code = KG_EMPTY_CCACHE; + goto cleanup; + } + +cleanup: + (void)krb5_cc_set_flags(context, ccache, 0); + krb5_free_principal(context, ccache_princ); + krb5_free_principal(context, tgt_princ); + return code; +} + +/* Find an existing or destination ccache for cred->name. */ +static krb5_error_code +get_cache_for_name(krb5_context context, krb5_gss_cred_id_rec *cred) +{ + krb5_error_code code; + krb5_boolean can_get, have_collection; + krb5_ccache defcc = NULL; + krb5_principal princ = NULL; + const char *cctype; + + assert(cred->name != NULL && cred->ccache == NULL); +#ifdef USE_LEASH + code = get_ccache_leash(context, cred->name->princ, &cred->ccache); + return code ? code : scan_ccache(context, cred); +#else + /* Check first whether we can acquire tickets, to avoid overwriting the + * extended error message from krb5_cc_cache_match. */ + can_get = can_get_initial_creds(context, cred); + + /* Look for an existing cache for the client principal. */ + code = krb5_cc_cache_match(context, cred->name->princ, &cred->ccache); + if (code == 0) + return scan_ccache(context, cred); + if (code != KRB5_CC_NOTFOUND || !can_get) + return code; + krb5_clear_error_message(context); + + /* There is no existing ccache, but we can acquire credentials. Get the + * default ccache to help decide where we should put them. */ + code = krb5_cc_default(context, &defcc); + if (code) + return code; + cctype = krb5_cc_get_type(context, defcc); + have_collection = krb5_cc_support_switch(context, cctype); + + /* We can use an empty default ccache if we're using a password or if + * there's no collection. */ + if (cred->password != NULL || !have_collection) { + if (krb5_cc_get_principal(context, defcc, &princ) == KRB5_FCC_NOFILE) { + cred->ccache = defcc; + defcc = NULL; + } + krb5_clear_error_message(context); + } + + /* Otherwise, try to use a new cache in the collection. */ + if (cred->ccache == NULL) { + if (!have_collection) { + code = KG_CCACHE_NOMATCH; + goto cleanup; + } + code = krb5_cc_new_unique(context, cctype, NULL, &cred->ccache); + if (code) + goto cleanup; + } + +cleanup: + krb5_free_principal(context, princ); + if (defcc != NULL) + krb5_cc_close(context, defcc); + return code; +#endif /* not USE_LEASH */ +} + +/* Try to set cred->name using the client keytab. */ +static krb5_error_code +get_name_from_client_keytab(krb5_context context, krb5_gss_cred_id_rec *cred) +{ + krb5_error_code code; + krb5_principal princ; + + assert(cred->name == NULL); + + if (cred->client_keytab == NULL) + return KRB5_KT_NOTFOUND; + + code = k5_kt_get_principal(context, cred->client_keytab, &princ); + if (code) + return code; + code = kg_init_name(context, princ, NULL, NULL, NULL, KG_INIT_NAME_NO_COPY, + &cred->name); + if (code) { + krb5_free_principal(context, princ); + return code; + } + return 0; +} + +/* Make a note in ccache that we should attempt to refresh it from the client + * keytab at refresh_time. */ +static void +set_refresh_time(krb5_context context, krb5_ccache ccache, + krb5_timestamp refresh_time) +{ + char buf[128]; + krb5_data d; + + snprintf(buf, sizeof(buf), "%ld", (long)refresh_time); + d = string2data(buf); + (void)krb5_cc_set_config(context, ccache, NULL, KRB5_CC_CONF_REFRESH_TIME, + &d); + krb5_clear_error_message(context); +} + +/* Return true if it's time to refresh cred from the client keytab. If + * returning true, avoid retrying for 30 seconds. */ +krb5_boolean +kg_cred_time_to_refresh(krb5_context context, krb5_gss_cred_id_rec *cred) +{ + krb5_timestamp now; + + if (krb5_timeofday(context, &now)) + return FALSE; + if (cred->refresh_time != 0 && now >= cred->refresh_time) { + set_refresh_time(context, cred->ccache, cred->refresh_time + 30); + return TRUE; + } + return FALSE; +} + +/* If appropriate, make a note to refresh cred from the client keytab when it + * is halfway to expired. */ +void +kg_cred_set_initial_refresh(krb5_context context, krb5_gss_cred_id_rec *cred, + krb5_ticket_times *times) +{ + krb5_timestamp refresh; + + /* For now, we only mark keytab-acquired credentials for refresh. */ + if (cred->password != NULL) + return; + + /* Make a note to refresh these when they are halfway to expired. */ + refresh = times->starttime + (times->endtime - times->starttime) / 2; + set_refresh_time(context, cred->ccache, refresh); +} + +/* Get initial credentials using the supplied password or client keytab. */ +static krb5_error_code +get_initial_cred(krb5_context context, krb5_gss_cred_id_rec *cred) +{ + krb5_error_code code; + krb5_get_init_creds_opt *opt = NULL; + krb5_creds creds; + + code = krb5_get_init_creds_opt_alloc(context, &opt); + if (code) + return code; + code = krb5_get_init_creds_opt_set_out_ccache(context, opt, cred->ccache); + if (code) + goto cleanup; + if (cred->password != NULL) { + code = krb5_get_init_creds_password(context, &creds, cred->name->princ, + cred->password, NULL, NULL, 0, + NULL, opt); + } else if (cred->client_keytab != NULL) { + code = krb5_get_init_creds_keytab(context, &creds, cred->name->princ, + cred->client_keytab, 0, NULL, opt); + } else { + code = KRB5_KT_NOTFOUND; + } + if (code) + goto cleanup; + kg_cred_set_initial_refresh(context, cred, &creds.times); + cred->have_tgt = TRUE; + cred->expire = creds.times.endtime; + krb5_free_cred_contents(context, &creds); +cleanup: + krb5_get_init_creds_opt_free(context, opt); + return code; +} + +/* Get initial credentials if we ought to and are able to. */ +static krb5_error_code +maybe_get_initial_cred(krb5_context context, krb5_gss_cred_id_rec *cred) +{ + krb5_error_code code; + + /* Don't get creds if we don't know the name or are doing IAKERB. */ + if (cred->name == NULL || cred->iakerb_mech) + return 0; + + /* Get creds if we have none or if it's time to refresh. */ + if (cred->expire == 0 || kg_cred_time_to_refresh(context, cred)) { + code = get_initial_cred(context, cred); + /* If we were trying to refresh and failed, we can keep going. */ + if (code && cred->expire == 0) + return code; + krb5_clear_error_message(context); + } + return 0; +} + +static OM_uint32 +acquire_init_cred(krb5_context context, + OM_uint32 *minor_status, + krb5_ccache req_ccache, + gss_buffer_t password, + krb5_keytab client_keytab, + krb5_gss_cred_id_rec *cred) +{ + krb5_error_code code; + krb5_data pwdata, pwcopy; + int caller_ccname = 0; + + /* Get ccache from caller if available. */ + if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) + return GSS_S_FAILURE; + if (GSS_ERROR(kg_caller_provided_ccache_name(minor_status, + &caller_ccname))) + return GSS_S_FAILURE; + + if (password != GSS_C_NO_BUFFER) { + pwdata = make_data(password->value, password->length); + code = krb5int_copy_data_contents_add0(context, &pwdata, &pwcopy); + if (code) + goto error; + cred->password = pwcopy.data; + + /* We will fetch the credential into a private memory ccache. */ + assert(req_ccache == NULL); + code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache); + if (code) + goto error; + cred->destroy_ccache = 1; + } else if (req_ccache != NULL) { + code = krb5_cc_dup(context, req_ccache, &cred->ccache); + if (code) + goto error; + } else if (caller_ccname) { + /* Caller's ccache name has been set as the context default. */ + code = krb5int_cc_default(context, &cred->ccache); + if (code) + goto error; + } + + if (client_keytab != NULL) { + code = krb5_kt_dup(context, client_keytab, &cred->client_keytab); + } else { + code = krb5_kt_client_default(context, &cred->client_keytab); + if (code) { + /* Treat resolution failure similarly to a client keytab which + * resolves but doesn't exist or has no content. */ + TRACE_GSS_CLIENT_KEYTAB_FAIL(context, code); + krb5_clear_error_message(context); + code = 0; + } + } + if (code) + goto error; + + if (cred->ccache != NULL) { + /* The caller specified a ccache; check what's in it. */ + code = scan_ccache(context, cred); + if (code == KRB5_FCC_NOFILE) { + /* See if we can get initial creds. If the caller didn't specify + * a name, pick one from the client keytab. */ + if (cred->name == NULL) { + if (!get_name_from_client_keytab(context, cred)) + code = 0; + } else if (can_get_initial_creds(context, cred)) { + code = 0; + } + } + if (code) + goto error; + } else if (cred->name != NULL) { + /* The caller specified a name but not a ccache; pick a cache. */ + code = get_cache_for_name(context, cred); + if (code) + goto error; + } + +#ifndef USE_LEASH + /* If we haven't picked a name, make sure we have or can get any creds, + * unless we're using Leash and might be able to get them interactively. */ + if (cred->name == NULL && !can_get_initial_creds(context, cred)) { + code = krb5_cccol_have_content(context); + if (code) + goto error; + } +#endif + + code = maybe_get_initial_cred(context, cred); + if (code) + goto error; + + *minor_status = 0; + return GSS_S_COMPLETE; + +error: + *minor_status = code; + return GSS_S_CRED_UNAVAIL; +} + +static OM_uint32 +acquire_cred_context(krb5_context context, OM_uint32 *minor_status, + gss_name_t desired_name, gss_buffer_t password, + OM_uint32 time_req, gss_cred_usage_t cred_usage, + krb5_ccache ccache, krb5_keytab client_keytab, + krb5_keytab keytab, const char *rcname, + krb5_boolean iakerb, gss_cred_id_t *output_cred_handle, + OM_uint32 *time_rec) +{ + krb5_gss_cred_id_t cred = NULL; + krb5_gss_name_t name = (krb5_gss_name_t)desired_name; + OM_uint32 ret; + krb5_error_code code = 0; + + /* make sure all outputs are valid */ + *output_cred_handle = GSS_C_NO_CREDENTIAL; + if (time_rec) + *time_rec = 0; + + /* create the gss cred structure */ + cred = k5alloc(sizeof(krb5_gss_cred_id_rec), &code); + if (cred == NULL) + goto krb_error_out; + + cred->usage = cred_usage; + cred->name = NULL; + cred->impersonator = NULL; + cred->iakerb_mech = iakerb; + cred->default_identity = (name == NULL); +#ifndef LEAN_CLIENT + cred->keytab = NULL; +#endif /* LEAN_CLIENT */ + cred->destroy_ccache = 0; + cred->suppress_ci_flags = 0; + cred->ccache = NULL; + + code = k5_mutex_init(&cred->lock); + if (code) + goto krb_error_out; + + switch (cred_usage) { + case GSS_C_INITIATE: + case GSS_C_ACCEPT: + case GSS_C_BOTH: + break; + default: + ret = GSS_S_FAILURE; + *minor_status = (OM_uint32) G_BAD_USAGE; + goto error_out; + } + + if (name != NULL) { + code = kg_duplicate_name(context, name, &cred->name); + if (code) + goto krb_error_out; + } + +#ifndef LEAN_CLIENT + /* + * If requested, acquire credentials for accepting. This will fill + * in cred->name if desired_princ is specified. + */ + if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) { + ret = acquire_accept_cred(context, minor_status, keytab, rcname, cred); + if (ret != GSS_S_COMPLETE) + goto error_out; + } +#endif /* LEAN_CLIENT */ + + /* + * If requested, acquire credentials for initiation. This will fill + * in cred->name if it wasn't set above. + */ + if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) { + ret = acquire_init_cred(context, minor_status, ccache, password, + client_keytab, cred); + if (ret != GSS_S_COMPLETE) + goto error_out; + } + + assert(cred->default_identity || cred->name != NULL); + + /*** at this point, the cred structure has been completely created */ + + if (cred_usage == GSS_C_ACCEPT) { + if (time_rec) + *time_rec = GSS_C_INDEFINITE; + } else { + krb5_timestamp now; + + code = krb5_timeofday(context, &now); + if (code != 0) + goto krb_error_out; + + if (time_rec) { + /* Resolve cred now to determine the expiration time. */ + ret = kg_cred_resolve(minor_status, context, (gss_cred_id_t)cred, + GSS_C_NO_NAME); + if (GSS_ERROR(ret)) + goto error_out; + *time_rec = (cred->expire > now) ? (cred->expire - now) : 0; + k5_mutex_unlock(&cred->lock); + } + } + + *minor_status = 0; + *output_cred_handle = (gss_cred_id_t) cred; + + return GSS_S_COMPLETE; + +krb_error_out: + *minor_status = code; + ret = GSS_S_FAILURE; + +error_out: + if (cred != NULL) { + if (cred->ccache) { + if (cred->destroy_ccache) + krb5_cc_destroy(context, cred->ccache); + else + krb5_cc_close(context, cred->ccache); + } + if (cred->client_keytab) + krb5_kt_close(context, cred->client_keytab); +#ifndef LEAN_CLIENT + if (cred->keytab) + krb5_kt_close(context, cred->keytab); +#endif /* LEAN_CLIENT */ + if (cred->rcache) + krb5_rc_close(context, cred->rcache); + if (cred->name) + kg_release_name(context, &cred->name); + krb5_free_principal(context, cred->impersonator); + zapfreestr(cred->password); + k5_mutex_destroy(&cred->lock); + xfree(cred); + } + save_error_info(*minor_status, context); + return ret; +} + +static OM_uint32 +acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name, + gss_buffer_t password, OM_uint32 time_req, + gss_cred_usage_t cred_usage, krb5_ccache ccache, + krb5_keytab keytab, krb5_boolean iakerb, + gss_cred_id_t *output_cred_handle, OM_uint32 *time_rec) +{ + krb5_context context = NULL; + krb5_error_code code = 0; + OM_uint32 ret; + + code = gss_krb5int_initialize_library(); + if (code) { + *minor_status = code; + ret = GSS_S_FAILURE; + goto out; + } + + code = krb5_gss_init_context(&context); + if (code) { + *minor_status = code; + ret = GSS_S_FAILURE; + goto out; + } + + ret = acquire_cred_context(context, minor_status, desired_name, password, + time_req, cred_usage, ccache, NULL, keytab, + NULL, iakerb, output_cred_handle, time_rec); + +out: + krb5_free_context(context); + return ret; +} + +/* + * Resolve the name and ccache for an initiator credential if it has not yet + * been done. If specified, use the target name to pick an appropriate ccache + * within the collection. Validates cred_handle and leaves it locked on + * success. + */ +OM_uint32 +kg_cred_resolve(OM_uint32 *minor_status, krb5_context context, + gss_cred_id_t cred_handle, gss_name_t target_name) +{ + OM_uint32 maj; + krb5_error_code code; + krb5_gss_cred_id_t cred = (krb5_gss_cred_id_t)cred_handle; + krb5_gss_name_t tname = (krb5_gss_name_t)target_name; + krb5_principal client_princ; + + *minor_status = 0; + + maj = krb5_gss_validate_cred_1(minor_status, cred_handle, context); + if (maj != 0) + return maj; + k5_mutex_assert_locked(&cred->lock); + + if (cred->usage == GSS_C_ACCEPT || cred->name != NULL) + return GSS_S_COMPLETE; + /* acquire_init_cred should have set both name and ccache, or neither. */ + assert(cred->ccache == NULL); + + if (tname != NULL) { + /* Use the target name to select an existing ccache or a principal. */ + code = krb5_cc_select(context, tname->princ, &cred->ccache, + &client_princ); + if (code && code != KRB5_CC_NOTFOUND) + goto kerr; + if (client_princ != NULL) { + code = kg_init_name(context, client_princ, NULL, NULL, NULL, + KG_INIT_NAME_NO_COPY, &cred->name); + if (code) { + krb5_free_principal(context, client_princ); + goto kerr; + } + } + if (cred->ccache != NULL) { + code = scan_ccache(context, cred); + if (code) + goto kerr; + } + } + + /* If we still haven't picked a client principal, try using an existing + * default ccache. (On Windows, this may acquire initial creds.) */ + if (cred->name == NULL) { + code = krb5int_cc_default(context, &cred->ccache); + if (code) + goto kerr; + code = scan_ccache(context, cred); + if (code == KRB5_FCC_NOFILE) { + /* Default ccache doesn't exist; fall through to client keytab. */ + krb5_cc_close(context, cred->ccache); + cred->ccache = NULL; + } else if (code) { + goto kerr; + } + } + + /* If that didn't work, try getting a name from the client keytab. */ + if (cred->name == NULL) { + code = get_name_from_client_keytab(context, cred); + if (code) { + code = KG_EMPTY_CCACHE; + goto kerr; + } + } + + if (cred->name != NULL && cred->ccache == NULL) { + /* Pick a cache for the name we chose (from krb5_cc_select or from the + * client keytab). */ + code = get_cache_for_name(context, cred); + if (code) + goto kerr; + } + + /* Resolve name to ccache and possibly get initial credentials. */ + code = maybe_get_initial_cred(context, cred); + if (code) + goto kerr; + + return GSS_S_COMPLETE; + +kerr: + k5_mutex_unlock(&cred->lock); + save_error_info(code, context); + *minor_status = code; + return GSS_S_CRED_UNAVAIL; +} + +OM_uint32 +gss_krb5int_set_cred_rcache(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_oid, + const gss_buffer_t value) +{ + krb5_gss_cred_id_t cred; + krb5_error_code code; + krb5_context context; + krb5_rcache rcache; + + assert(value->length == sizeof(rcache)); + + if (value->length != sizeof(rcache)) + return GSS_S_FAILURE; + + rcache = (krb5_rcache)value->value; + + cred = (krb5_gss_cred_id_t)*cred_handle; + + code = krb5_gss_init_context(&context); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } + if (cred->rcache != NULL) { + code = krb5_rc_close(context, cred->rcache); + if (code) { + *minor_status = code; + krb5_free_context(context); + return GSS_S_FAILURE; + } + } + + cred->rcache = rcache; + + krb5_free_context(context); + + *minor_status = 0; + return GSS_S_COMPLETE; +} + +/* + * krb5 and IAKERB mech API functions follow. The mechglue always passes null + * desired_mechs and actual_mechs, so we ignore those parameters. + */ + +OM_uint32 KRB5_CALLCONV +krb5_gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name, + OM_uint32 time_req, gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, OM_uint32 *time_rec) +{ + return acquire_cred(minor_status, desired_name, NULL, time_req, cred_usage, + NULL, NULL, FALSE, output_cred_handle, time_rec); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name, + OM_uint32 time_req, gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, OM_uint32 *time_rec) +{ + return acquire_cred(minor_status, desired_name, NULL, time_req, cred_usage, + NULL, NULL, TRUE, output_cred_handle, time_rec); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_acquire_cred_with_password(OM_uint32 *minor_status, + const gss_name_t desired_name, + const gss_buffer_t password, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + int cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + return acquire_cred(minor_status, desired_name, password, time_req, + cred_usage, NULL, NULL, FALSE, output_cred_handle, + time_rec); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_acquire_cred_with_password(OM_uint32 *minor_status, + const gss_name_t desired_name, + const gss_buffer_t password, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + int cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + return acquire_cred(minor_status, desired_name, password, time_req, + cred_usage, NULL, NULL, TRUE, output_cred_handle, + time_rec); +} + +OM_uint32 +gss_krb5int_import_cred(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_oid, + const gss_buffer_t value) +{ + struct krb5_gss_import_cred_req *req; + krb5_gss_name_rec name; + OM_uint32 time_rec; + krb5_error_code code; + gss_cred_usage_t usage; + gss_name_t desired_name = GSS_C_NO_NAME; + + assert(value->length == sizeof(*req)); + + if (value->length != sizeof(*req)) + return GSS_S_FAILURE; + + req = (struct krb5_gss_import_cred_req *)value->value; + + if (req->id != NULL) { + usage = (req->keytab != NULL) ? GSS_C_BOTH : GSS_C_INITIATE; + } else if (req->keytab != NULL) { + usage = GSS_C_ACCEPT; + } else { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + + if (req->keytab_principal != NULL) { + memset(&name, 0, sizeof(name)); + code = k5_mutex_init(&name.lock); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + name.princ = req->keytab_principal; + desired_name = (gss_name_t)&name; + } + + code = acquire_cred(minor_status, desired_name, NULL, GSS_C_INDEFINITE, + usage, req->id, req->keytab, FALSE, cred_handle, + &time_rec); + if (req->keytab_principal != NULL) + k5_mutex_destroy(&name.lock); + return code; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_acquire_cred_from(OM_uint32 *minor_status, + const gss_name_t desired_name, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_const_key_value_set_t cred_store, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + krb5_context context = NULL; + krb5_error_code code = 0; + krb5_keytab client_keytab = NULL; + krb5_keytab keytab = NULL; + krb5_ccache ccache = NULL; + const char *rcname, *value; + OM_uint32 ret; + + code = gss_krb5int_initialize_library(); + if (code) { + *minor_status = code; + ret = GSS_S_FAILURE; + goto out; + } + + code = krb5_gss_init_context(&context); + if (code) { + *minor_status = code; + ret = GSS_S_FAILURE; + goto out; + } + + ret = kg_value_from_cred_store(cred_store, KRB5_CS_CCACHE_URN, &value); + if (GSS_ERROR(ret)) + goto out; + + if (value) { + code = krb5_cc_resolve(context, value, &ccache); + if (code != 0) { + *minor_status = code; + ret = GSS_S_CRED_UNAVAIL; + goto out; + } + } + + ret = kg_value_from_cred_store(cred_store, KRB5_CS_CLI_KEYTAB_URN, &value); + if (GSS_ERROR(ret)) + goto out; + + if (value) { + code = krb5_kt_resolve(context, value, &client_keytab); + if (code != 0) { + *minor_status = code; + ret = GSS_S_CRED_UNAVAIL; + goto out; + } + } + + ret = kg_value_from_cred_store(cred_store, KRB5_CS_KEYTAB_URN, &value); + if (GSS_ERROR(ret)) + goto out; + + if (value) { + code = krb5_kt_resolve(context, value, &keytab); + if (code != 0) { + *minor_status = code; + ret = GSS_S_CRED_UNAVAIL; + goto out; + } + } + + ret = kg_value_from_cred_store(cred_store, KRB5_CS_RCACHE_URN, &rcname); + if (GSS_ERROR(ret)) + goto out; + + ret = acquire_cred_context(context, minor_status, desired_name, NULL, + time_req, cred_usage, ccache, client_keytab, + keytab, rcname, 0, output_cred_handle, + time_rec); + +out: + if (ccache != NULL) + krb5_cc_close(context, ccache); + if (client_keytab != NULL) + krb5_kt_close(context, client_keytab); + if (keytab != NULL) + krb5_kt_close(context, keytab); + krb5_free_context(context); + return ret; +} diff --git a/src/lib/gssapi/krb5/canon_name.c b/src/lib/gssapi/krb5/canon_name.c new file mode 100644 index 000000000000..81b08a534ec1 --- /dev/null +++ b/src/lib/gssapi/krb5/canon_name.c @@ -0,0 +1,44 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/canon_name.c */ +/* + * Copyright 1997 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 "gssapiP_krb5.h" + +/* This is trivial since we're a single mechanism implementation */ + +OM_uint32 krb5_gss_canonicalize_name(OM_uint32 *minor_status, + const gss_name_t input_name, + const gss_OID mech_type, + gss_name_t *output_name) +{ + if ((mech_type != GSS_C_NULL_OID) && + !g_OID_equal(gss_mech_krb5, mech_type) && + !g_OID_equal(gss_mech_krb5_old, mech_type)) { + *minor_status = 0; + return(GSS_S_BAD_MECH); + } + + return(krb5_gss_duplicate_name(minor_status, input_name, output_name)); +} diff --git a/src/lib/gssapi/krb5/compare_name.c b/src/lib/gssapi/krb5/compare_name.c new file mode 100644 index 000000000000..3f3788d2bf1c --- /dev/null +++ b/src/lib/gssapi/krb5/compare_name.c @@ -0,0 +1,52 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * $Id$ + */ + +#include "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_compare_name(minor_status, name1, name2, name_equal) + OM_uint32 *minor_status; + gss_name_t name1; + gss_name_t name2; + int *name_equal; +{ + krb5_context context; + krb5_error_code code; + + code = krb5_gss_init_context(&context); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } + + *minor_status = 0; + *name_equal = kg_compare_name(context, + (krb5_gss_name_t)name1, + (krb5_gss_name_t)name2); + krb5_free_context(context); + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/krb5/context_time.c b/src/lib/gssapi/krb5/context_time.c new file mode 100644 index 000000000000..a18cfb05b743 --- /dev/null +++ b/src/lib/gssapi/krb5/context_time.c @@ -0,0 +1,63 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" + +/* + * $Id$ + */ + +OM_uint32 KRB5_CALLCONV +krb5_gss_context_time(minor_status, context_handle, time_rec) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + OM_uint32 *time_rec; +{ + krb5_error_code code; + krb5_gss_ctx_id_rec *ctx; + krb5_timestamp now; + krb5_deltat lifetime; + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + + if (ctx->terminated || !ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return(GSS_S_NO_CONTEXT); + } + + if ((code = krb5_timeofday(ctx->k5_context, &now))) { + *minor_status = code; + save_error_info(*minor_status, ctx->k5_context); + return(GSS_S_FAILURE); + } + + if ((lifetime = ctx->krb_times.endtime - now) <= 0) { + *time_rec = 0; + *minor_status = 0; + return(GSS_S_CONTEXT_EXPIRED); + } else { + *time_rec = lifetime; + *minor_status = 0; + return(GSS_S_COMPLETE); + } +} diff --git a/src/lib/gssapi/krb5/copy_ccache.c b/src/lib/gssapi/krb5/copy_ccache.c new file mode 100644 index 000000000000..f3d7666135cd --- /dev/null +++ b/src/lib/gssapi/krb5/copy_ccache.c @@ -0,0 +1,60 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +#include "gssapiP_krb5.h" + +OM_uint32 +gss_krb5int_copy_ccache(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_object, + const gss_buffer_t value) +{ + krb5_gss_cred_id_t k5creds; + krb5_cc_cursor cursor; + krb5_creds creds; + krb5_error_code code; + krb5_context context; + krb5_ccache out_ccache; + + assert(value->length == sizeof(out_ccache)); + + if (value->length != sizeof(out_ccache)) + return GSS_S_FAILURE; + + out_ccache = (krb5_ccache)value->value; + + /* cred handle will have been validated by gssspi_set_cred_option() */ + k5creds = (krb5_gss_cred_id_t) *cred_handle; + k5_mutex_lock(&k5creds->lock); + if (k5creds->usage == GSS_C_ACCEPT) { + k5_mutex_unlock(&k5creds->lock); + *minor_status = (OM_uint32) G_BAD_USAGE; + return(GSS_S_FAILURE); + } + + code = krb5_gss_init_context(&context); + if (code) { + k5_mutex_unlock(&k5creds->lock); + *minor_status = code; + return GSS_S_FAILURE; + } + + code = krb5_cc_start_seq_get(context, k5creds->ccache, &cursor); + if (code) { + k5_mutex_unlock(&k5creds->lock); + *minor_status = code; + save_error_info(*minor_status, context); + krb5_free_context(context); + return(GSS_S_FAILURE); + } + while (!code && !krb5_cc_next_cred(context, k5creds->ccache, &cursor, + &creds)) { + code = krb5_cc_store_cred(context, out_ccache, &creds); + krb5_free_cred_contents(context, &creds); + } + krb5_cc_end_seq_get(context, k5creds->ccache, &cursor); + k5_mutex_unlock(&k5creds->lock); + *minor_status = code; + if (code) + save_error_info(*minor_status, context); + krb5_free_context(context); + return code ? GSS_S_FAILURE : GSS_S_COMPLETE; +} diff --git a/src/lib/gssapi/krb5/cred_store.c b/src/lib/gssapi/krb5/cred_store.c new file mode 100644 index 000000000000..008dd208bf0c --- /dev/null +++ b/src/lib/gssapi/krb5/cred_store.c @@ -0,0 +1,50 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2011 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "gssapiP_krb5.h" + +OM_uint32 +kg_value_from_cred_store(gss_const_key_value_set_t cred_store, + const char *type, const char **value) +{ + OM_uint32 i; + + if (value == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + *value = NULL; + + if (cred_store == GSS_C_NO_CRED_STORE) + return GSS_S_COMPLETE; + + for (i = 0; i < cred_store->count; i++) { + if (strcmp(cred_store->elements[i].key, type) == 0) { + if (*value != NULL) + return GSS_S_DUPLICATE_ELEMENT; + *value = cred_store->elements[i].value; + } + } + + return GSS_S_COMPLETE; +} diff --git a/src/lib/gssapi/krb5/delete_sec_context.c b/src/lib/gssapi/krb5/delete_sec_context.c new file mode 100644 index 000000000000..4b9dfae0d515 --- /dev/null +++ b/src/lib/gssapi/krb5/delete_sec_context.c @@ -0,0 +1,99 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" + +/* + * $Id$ + */ + +OM_uint32 KRB5_CALLCONV +krb5_gss_delete_sec_context(minor_status, context_handle, output_token) + OM_uint32 *minor_status; + gss_ctx_id_t *context_handle; + gss_buffer_t output_token; +{ + krb5_context context; + krb5_gss_ctx_id_rec *ctx; + + if (output_token) { + output_token->length = 0; + output_token->value = NULL; + } + + /*SUPPRESS 29*/ + if (*context_handle == GSS_C_NO_CONTEXT) { + *minor_status = 0; + return(GSS_S_COMPLETE); + } + + ctx = (krb5_gss_ctx_id_t) *context_handle; + context = ctx->k5_context; + + /* free all the context state */ + + if (ctx->seqstate) + g_seqstate_free(ctx->seqstate); + + if (ctx->enc) + krb5_k_free_key(context, ctx->enc); + + if (ctx->seq) + krb5_k_free_key(context, ctx->seq); + + if (ctx->here) + kg_release_name(context, &ctx->here); + if (ctx->there) + kg_release_name(context, &ctx->there); + if (ctx->subkey) + krb5_k_free_key(context, ctx->subkey); + if (ctx->acceptor_subkey) + krb5_k_free_key(context, ctx->acceptor_subkey); + + if (ctx->auth_context) { + if (ctx->cred_rcache) + (void)krb5_auth_con_setrcache(context, ctx->auth_context, NULL); + + krb5_auth_con_free(context, ctx->auth_context); + } + + if (ctx->mech_used) + krb5_gss_release_oid(minor_status, &ctx->mech_used); + + if (ctx->authdata) + krb5_free_authdata(context, ctx->authdata); + + if (ctx->k5_context) + krb5_free_context(ctx->k5_context); + + /* Zero out context */ + zap(ctx, sizeof(*ctx)); + xfree(ctx); + + /* zero the handle itself */ + + *context_handle = GSS_C_NO_CONTEXT; + + *minor_status = 0; + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/krb5/deps b/src/lib/gssapi/krb5/deps new file mode 100644 index 000000000000..f42f40ae7727 --- /dev/null +++ b/src/lib/gssapi/krb5/deps @@ -0,0 +1,756 @@ +# +# Generated makefile dependencies follow. +# +accept_sec_context.so accept_sec_context.po $(OUTPRE)accept_sec_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h accept_sec_context.c \ + gssapiP_krb5.h gssapi_err_krb5.h gssapi_krb5.h +acquire_cred.so acquire_cred.po $(OUTPRE)acquire_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h acquire_cred.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +canon_name.so canon_name.po $(OUTPRE)canon_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h canon_name.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +compare_name.so compare_name.po $(OUTPRE)compare_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h compare_name.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +context_time.so context_time.po $(OUTPRE)context_time.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h context_time.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +copy_ccache.so copy_ccache.po $(OUTPRE)copy_ccache.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h copy_ccache.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +cred_store.so cred_store.po $(OUTPRE)cred_store.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h cred_store.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +delete_sec_context.so delete_sec_context.po $(OUTPRE)delete_sec_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h delete_sec_context.c \ + gssapiP_krb5.h gssapi_err_krb5.h gssapi_krb5.h +disp_name.so disp_name.po $(OUTPRE)disp_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h disp_name.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +disp_status.so disp_status.po $(OUTPRE)disp_status.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h disp_status.c error_map.h \ + gssapiP_krb5.h gssapi_err_krb5.h gssapi_krb5.h +duplicate_name.so duplicate_name.po $(OUTPRE)duplicate_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h duplicate_name.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +export_cred.so export_cred.po $(OUTPRE)export_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-json.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h ../generic/gssapi_err_generic.h \ + export_cred.c gssapiP_krb5.h gssapi_err_krb5.h gssapi_krb5.h +export_name.so export_name.po $(OUTPRE)export_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h export_name.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +export_sec_context.so export_sec_context.po $(OUTPRE)export_sec_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h export_sec_context.c \ + gssapiP_krb5.h gssapi_err_krb5.h gssapi_krb5.h +get_tkt_flags.so get_tkt_flags.po $(OUTPRE)get_tkt_flags.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h get_tkt_flags.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +gssapi_krb5.so gssapi_krb5.po $(OUTPRE)gssapi_krb5.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(srcdir)/../mechglue/mechglue.h $(srcdir)/../mechglue/mglueP.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.c gssapi_krb5.h +iakerb.so iakerb.po $(OUTPRE)iakerb.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h iakerb.c +import_cred.so import_cred.po $(OUTPRE)import_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-json.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h ../generic/gssapi_err_generic.h \ + gssapiP_krb5.h gssapi_err_krb5.h gssapi_krb5.h import_cred.c +import_name.so import_name.po $(OUTPRE)import_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h import_name.c +import_sec_context.so import_sec_context.po $(OUTPRE)import_sec_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h import_sec_context.c +indicate_mechs.so indicate_mechs.po $(OUTPRE)indicate_mechs.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(srcdir)/../mechglue/mechglue.h $(srcdir)/../mechglue/mglueP.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h indicate_mechs.c +init_sec_context.so init_sec_context.po $(OUTPRE)init_sec_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h init_sec_context.c +inq_context.so inq_context.po $(OUTPRE)inq_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h inq_context.c +inq_cred.so inq_cred.po $(OUTPRE)inq_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h inq_cred.c +inq_names.so inq_names.po $(OUTPRE)inq_names.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h inq_names.c +k5seal.so k5seal.po $(OUTPRE)k5seal.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h k5seal.c +k5sealiov.so k5sealiov.po $(OUTPRE)k5sealiov.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h k5sealiov.c +k5sealv3.so k5sealv3.po $(OUTPRE)k5sealv3.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h k5sealv3.c +k5sealv3iov.so k5sealv3iov.po $(OUTPRE)k5sealv3iov.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h k5sealv3iov.c +k5unseal.so k5unseal.po $(OUTPRE)k5unseal.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h k5unseal.c +k5unsealiov.so k5unsealiov.po $(OUTPRE)k5unsealiov.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h k5unsealiov.c +krb5_gss_glue.so krb5_gss_glue.po $(OUTPRE)krb5_gss_glue.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h krb5_gss_glue.c +lucid_context.so lucid_context.po $(OUTPRE)lucid_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h lucid_context.c +naming_exts.so naming_exts.po $(OUTPRE)naming_exts.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h naming_exts.c +prf.so prf.po $(OUTPRE)prf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h prf.c +process_context_token.so process_context_token.po $(OUTPRE)process_context_token.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h process_context_token.c +rel_cred.so rel_cred.po $(OUTPRE)rel_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h rel_cred.c +rel_oid.so rel_oid.po $(OUTPRE)rel_oid.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h rel_oid.c +rel_name.so rel_name.po $(OUTPRE)rel_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h rel_name.c +s4u_gss_glue.so s4u_gss_glue.po $(OUTPRE)s4u_gss_glue.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h s4u_gss_glue.c +set_allowable_enctypes.so set_allowable_enctypes.po \ + $(OUTPRE)set_allowable_enctypes.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h set_allowable_enctypes.c +ser_sctx.so ser_sctx.po $(OUTPRE)ser_sctx.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h ser_sctx.c +set_ccache.so set_ccache.po $(OUTPRE)set_ccache.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h set_ccache.c +store_cred.so store_cred.po $(OUTPRE)store_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h store_cred.c +util_cksum.so util_cksum.po $(OUTPRE)util_cksum.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h util_cksum.c +util_crypt.so util_crypt.po $(OUTPRE)util_crypt.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h util_crypt.c +util_seed.so util_seed.po $(OUTPRE)util_seed.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h util_seed.c +util_seqnum.so util_seqnum.po $(OUTPRE)util_seqnum.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h util_seqnum.c +val_cred.so val_cred.po $(OUTPRE)val_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h val_cred.c +wrap_size_limit.so wrap_size_limit.po $(OUTPRE)wrap_size_limit.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h wrap_size_limit.c diff --git a/src/lib/gssapi/krb5/disp_name.c b/src/lib/gssapi/krb5/disp_name.c new file mode 100644 index 000000000000..b097bf0e2181 --- /dev/null +++ b/src/lib/gssapi/krb5/disp_name.c @@ -0,0 +1,81 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_display_name(minor_status, input_name, output_name_buffer, + output_name_type) + OM_uint32 *minor_status; + gss_name_t input_name; + gss_buffer_t output_name_buffer; + gss_OID *output_name_type; +{ + krb5_context context; + krb5_error_code code; + char *str; + krb5_gss_name_t k5name = (krb5_gss_name_t) input_name; + gss_OID nametype = (gss_OID) gss_nt_krb5_name; + + output_name_buffer->length = 0; + output_name_buffer->value = NULL; + if (output_name_type) + *output_name_type = GSS_C_NO_OID; + + code = krb5_gss_init_context(&context); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } + + if (krb5_princ_type(context, k5name->princ) == KRB5_NT_WELLKNOWN) { + if (krb5_principal_compare(context, k5name->princ, + krb5_anonymous_principal())) + nametype = GSS_C_NT_ANONYMOUS; + } + + if ((code = krb5_unparse_name(context, + ((krb5_gss_name_t) input_name)->princ, + &str))) { + *minor_status = code; + save_error_info(*minor_status, context); + krb5_free_context(context); + return(GSS_S_FAILURE); + } + + if (! g_make_string_buffer(str, output_name_buffer)) { + krb5_free_unparsed_name(context, str); + krb5_free_context(context); + + *minor_status = (OM_uint32) G_BUFFER_ALLOC; + return(GSS_S_FAILURE); + } + + krb5_free_unparsed_name(context, str); + krb5_free_context(context); + + *minor_status = 0; + if (output_name_type) + *output_name_type = (gss_OID) nametype; + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/krb5/disp_status.c b/src/lib/gssapi/krb5/disp_status.c new file mode 100644 index 000000000000..6ff62a9d8415 --- /dev/null +++ b/src/lib/gssapi/krb5/disp_status.c @@ -0,0 +1,200 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" +#include "com_err.h" + +/* XXXX internationalization!! */ + +static inline int +compare_OM_uint32 (OM_uint32 a, OM_uint32 b) +{ + if (a < b) + return -1; + else if (a == b) + return 0; + else + return 1; +} +static inline void +free_string (char *s) +{ + free(s); +} +#include "error_map.h" +#include <stdio.h> +char *get_error_message(OM_uint32 minor_code) +{ + gsserrmap *p = k5_getspecific(K5_KEY_GSS_KRB5_ERROR_MESSAGE); + char *msg = NULL; +#ifdef DEBUG + fprintf(stderr, "%s(%lu, p=%p)", __FUNCTION__, (unsigned long) minor_code, + (void *) p); +#endif + if (p) { + char **v = gsserrmap_find(p, minor_code); + if (v) { + msg = *v; +#ifdef DEBUG + fprintf(stderr, " FOUND!"); +#endif + } + } + if (msg == 0) + msg = (char *)error_message((krb5_error_code)minor_code); +#ifdef DEBUG + fprintf(stderr, " -> %p/%s\n", (void *) msg, msg); +#endif + return msg; +} +#define save_error_string_nocopy gss_krb5_save_error_string_nocopy +static int save_error_string_nocopy(OM_uint32 minor_code, char *msg) +{ + gsserrmap *p; + int ret; + +#ifdef DEBUG + fprintf(stderr, "%s(%lu, %s)", __FUNCTION__, (unsigned long) minor_code, msg); +#endif + p = k5_getspecific(K5_KEY_GSS_KRB5_ERROR_MESSAGE); + if (!p) { + p = malloc(sizeof(*p)); + if (p == NULL) { + ret = 1; + goto fail; + } + if (gsserrmap_init(p) != 0) { + free(p); + p = NULL; + ret = 1; + goto fail; + } + if (k5_setspecific(K5_KEY_GSS_KRB5_ERROR_MESSAGE, p) != 0) { + gsserrmap_destroy(p); + free(p); + p = NULL; + ret = 1; + goto fail; + } + } + ret = gsserrmap_replace_or_insert(p, minor_code, msg); +fail: +#ifdef DEBUG + fprintf(stderr, " p=%p %s\n", (void *)p, ret ? "FAIL" : "SUCCESS"); +#endif + return ret; +} +void save_error_string(OM_uint32 minor_code, char *msg) +{ + char *s = strdup(msg); + if (s) { + if (save_error_string_nocopy(minor_code, s) != 0) + free(s); + } +} +void save_error_message(OM_uint32 minor_code, const char *format, ...) +{ + char *s; + int n; + va_list ap; + + va_start(ap, format); + n = vasprintf(&s, format, ap); + va_end(ap); + if (n >= 0) { + if (save_error_string_nocopy(minor_code, s) != 0) + free(s); + } +} +void krb5_gss_save_error_info(OM_uint32 minor_code, krb5_context ctx) +{ + char *s; + +#ifdef DEBUG + fprintf(stderr, "%s(%lu, ctx=%p)\n", __FUNCTION__, + (unsigned long) minor_code, (void *)ctx); +#endif + s = (char *)krb5_get_error_message(ctx, (krb5_error_code)minor_code); +#ifdef DEBUG + fprintf(stderr, "%s(%lu, ctx=%p) saving: %s\n", __FUNCTION__, + (unsigned long) minor_code, (void *)ctx, s); +#endif + save_error_string(minor_code, s); + /* The get_error_message call above resets the error message in + ctx. Put it back, in case we make this call again *sigh*. */ + k5_setmsg(ctx, (krb5_error_code)minor_code, "%s", s); + krb5_free_error_message(ctx, s); +} +void krb5_gss_delete_error_info(void *p) +{ + gsserrmap_destroy(p); + free(p); +} + +/**/ + +OM_uint32 KRB5_CALLCONV +krb5_gss_display_status(minor_status, status_value, status_type, + mech_type, message_context, status_string) + OM_uint32 *minor_status; + OM_uint32 status_value; + int status_type; + gss_OID mech_type; + OM_uint32 *message_context; + gss_buffer_t status_string; +{ + status_string->length = 0; + status_string->value = NULL; + + if ((mech_type != GSS_C_NULL_OID) && + !g_OID_equal(gss_mech_krb5, mech_type) && + !g_OID_equal(gss_mech_krb5_old, mech_type) && + !g_OID_equal(gss_mech_iakerb, mech_type)) { + *minor_status = 0; + return(GSS_S_BAD_MECH); + } + + if (status_type == GSS_C_GSS_CODE) { + return(g_display_major_status(minor_status, status_value, + message_context, status_string)); + } else if (status_type == GSS_C_MECH_CODE) { + (void) gss_krb5int_initialize_library(); + + if (*message_context) { + *minor_status = (OM_uint32) G_BAD_MSG_CTX; + return(GSS_S_FAILURE); + } + + /* If this fails, there's not much we can do... */ + if (!g_make_string_buffer(krb5_gss_get_error_message(status_value), + status_string)) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + *minor_status = 0; + return(GSS_S_COMPLETE); + } else { + *minor_status = 0; + return(GSS_S_BAD_STATUS); + } +} diff --git a/src/lib/gssapi/krb5/duplicate_name.c b/src/lib/gssapi/krb5/duplicate_name.c new file mode 100644 index 000000000000..b88d97d9d617 --- /dev/null +++ b/src/lib/gssapi/krb5/duplicate_name.c @@ -0,0 +1,59 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/duplicate_name.c */ +/* + * Copyright 1997,2007 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 "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_duplicate_name(OM_uint32 *minor_status, const gss_name_t input_name, + gss_name_t *dest_name) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t princ, outprinc; + + if (minor_status) + *minor_status = 0; + + code = krb5_gss_init_context(&context); + if (code) { + if (minor_status) + *minor_status = code; + return GSS_S_FAILURE; + } + + princ = (krb5_gss_name_t)input_name; + code = kg_duplicate_name(context, princ, &outprinc); + if (code) { + *minor_status = code; + save_error_info(*minor_status, context); + krb5_free_context(context); + return(GSS_S_FAILURE); + } + krb5_free_context(context); + *dest_name = (gss_name_t) outprinc; + return(GSS_S_COMPLETE); + +} diff --git a/src/lib/gssapi/krb5/export_cred.c b/src/lib/gssapi/krb5/export_cred.c new file mode 100644 index 000000000000..652b2604bda3 --- /dev/null +++ b/src/lib/gssapi/krb5/export_cred.c @@ -0,0 +1,481 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/export_cred.c - krb5 export_cred implementation */ +/* + * Copyright (C) 2012 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "k5-int.h" +#include "k5-json.h" +#include "gssapiP_krb5.h" + +/* Return a JSON null or array value representing princ. */ +static krb5_error_code +json_principal(krb5_context context, krb5_principal princ, + k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_string str = NULL; + char *princname; + + *val_out = NULL; + if (princ == NULL) + return k5_json_null_create_val(val_out); + ret = krb5_unparse_name(context, princ, &princname); + if (ret) + return ret; + ret = k5_json_string_create(princname, &str); + krb5_free_unparsed_name(context, princname); + *val_out = str; + return ret; +} + +/* Return a json null or array value representing etypes. */ +static krb5_error_code +json_etypes(krb5_enctype *etypes, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_number num; + k5_json_array array; + + *val_out = NULL; + if (etypes == NULL) + return k5_json_null_create_val(val_out); + ret = k5_json_array_create(&array); + if (ret) + return ret; + for (; *etypes != 0; etypes++) { + ret = k5_json_number_create(*etypes, &num); + if (ret) + goto err; + ret = k5_json_array_add(array, num); + k5_json_release(num); + if (ret) + goto err; + } + *val_out = array; + return 0; +err: + k5_json_release(array); + return ret; +} + +/* Return a JSON null or array value representing name. */ +static krb5_error_code +json_kgname(krb5_context context, krb5_gss_name_t name, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_array array = NULL; + k5_json_value princ; + + *val_out = NULL; + if (name == NULL) + return k5_json_null_create_val(val_out); + ret = json_principal(context, name->princ, &princ); + if (ret) + return ret; + ret = k5_json_array_fmt(&array, "vss", princ, name->service, name->host); + k5_json_release(princ); + *val_out = array; + return ret; +} + +/* Return a JSON null or string value representing keytab. */ +static krb5_error_code +json_keytab(krb5_context context, krb5_keytab keytab, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_string str; + char name[1024]; + + *val_out = NULL; + if (keytab == NULL) + return k5_json_null_create_val(val_out); + ret = krb5_kt_get_name(context, keytab, name, sizeof(name)); + if (ret) + return ret; + ret = k5_json_string_create(name, &str); + *val_out = str; + return ret; +} + +/* Return a JSON null or string value representing rcache. */ +static krb5_error_code +json_rcache(krb5_context context, krb5_rcache rcache, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_string str = NULL; + char *name; + + if (rcache == NULL) + return k5_json_null_create_val(val_out); + if (asprintf(&name, "%s:%s", krb5_rc_get_type(context, rcache), + krb5_rc_get_name(context, rcache)) < 0) + return ENOMEM; + ret = k5_json_string_create(name, &str); + free(name); + *val_out = str; + return ret; +} + +/* Return a JSON array value representing keyblock. */ +static krb5_error_code +json_keyblock(krb5_keyblock *kb, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_array array; + + *val_out = NULL; + ret = k5_json_array_fmt(&array, "iB", kb->enctype, (void *)kb->contents, + (size_t)kb->length); + if (ret) + return ret; + *val_out = array; + return 0; +} + +/* Return a JSON array value representing addr. */ +static krb5_error_code +json_address(krb5_address *addr, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_array array; + + *val_out = NULL; + ret = k5_json_array_fmt(&array, "iB", addr->addrtype, + (void *)addr->contents, (size_t)addr->length); + if (ret) + return ret; + *val_out = array; + return 0; +} + +/* Return a JSON null or array value representing addrs. */ +static krb5_error_code +json_addresses(krb5_address **addrs, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_array array; + k5_json_value val; + + *val_out = NULL; + if (addrs == NULL) + return k5_json_null_create_val(val_out); + ret = k5_json_array_create(&array); + if (ret) + return ret; + for (; *addrs != NULL; addrs++) { + ret = json_address(*addrs, &val); + if (ret) + goto err; + ret = k5_json_array_add(array, val); + k5_json_release(val); + if (ret) + goto err; + } + *val_out = array; + return 0; +err: + k5_json_release(array); + return ret; +} + +/* Return a JSON array value representing ad. */ +static krb5_error_code +json_authdata_element(krb5_authdata *ad, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_array array; + + *val_out = NULL; + ret = k5_json_array_fmt(&array, "iB", ad->ad_type, (void *)ad->contents, + (size_t)ad->length); + if (ret) + return ret; + *val_out = array; + return 0; +} + +/* Return a JSON null or array value representing authdata. */ +static krb5_error_code +json_authdata(krb5_authdata **authdata, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_array array; + k5_json_value val; + + *val_out = NULL; + if (authdata == NULL) + return k5_json_null_create_val(val_out); + ret = k5_json_array_create(&array); + if (ret) + return ret; + for (; *authdata != NULL; authdata++) { + ret = json_authdata_element(*authdata, &val); + if (ret) + goto err; + ret = k5_json_array_add(array, val); + k5_json_release(val); + if (ret) + goto err; + } + *val_out = array; + return 0; +err: + k5_json_release(array); + return ret; +} + +/* Return a JSON array value representing creds. */ +static krb5_error_code +json_creds(krb5_context context, krb5_creds *creds, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_array array; + k5_json_value client = NULL, server = NULL, keyblock = NULL, addrs = NULL; + k5_json_value authdata = NULL; + + *val_out = NULL; + ret = json_principal(context, creds->client, &client); + if (ret) + goto cleanup; + ret = json_principal(context, creds->server, &server); + if (ret) + goto cleanup; + ret = json_keyblock(&creds->keyblock, &keyblock); + if (ret) + goto cleanup; + ret = json_addresses(creds->addresses, &addrs); + if (ret) + goto cleanup; + ret = json_authdata(creds->authdata, &authdata); + if (ret) + goto cleanup; + + ret = k5_json_array_fmt(&array, "vvviiiibivBBv", client, server, keyblock, + creds->times.authtime, creds->times.starttime, + creds->times.endtime, creds->times.renew_till, + creds->is_skey, creds->ticket_flags, addrs, + (void *)creds->ticket.data, + (size_t)creds->ticket.length, + (void *)creds->second_ticket.data, + (size_t)creds->second_ticket.length, authdata); + if (ret) + goto cleanup; + *val_out = array; + +cleanup: + k5_json_release(client); + k5_json_release(server); + k5_json_release(keyblock); + k5_json_release(addrs); + k5_json_release(authdata); + return ret; +} + +/* Return a JSON array value representing the contents of ccache. */ +static krb5_error_code +json_ccache_contents(krb5_context context, krb5_ccache ccache, + k5_json_value *val_out) +{ + krb5_error_code ret; + krb5_principal princ; + krb5_cc_cursor cursor; + krb5_creds creds; + k5_json_array array; + k5_json_value val; + + *val_out = NULL; + ret = k5_json_array_create(&array); + if (ret) + return ret; + + /* Put the principal in the first array entry. */ + ret = krb5_cc_get_principal(context, ccache, &princ); + if (ret) + goto err; + ret = json_principal(context, princ, &val); + krb5_free_principal(context, princ); + if (ret) + goto err; + ret = k5_json_array_add(array, val); + k5_json_release(val); + if (ret) + goto err; + + /* Put credentials in the remaining array entries. */ + ret = krb5_cc_start_seq_get(context, ccache, &cursor); + if (ret) + goto err; + while ((ret = krb5_cc_next_cred(context, ccache, &cursor, &creds)) == 0) { + ret = json_creds(context, &creds, &val); + krb5_free_cred_contents(context, &creds); + if (ret) + break; + ret = k5_json_array_add(array, val); + k5_json_release(val); + if (ret) + break; + } + krb5_cc_end_seq_get(context, ccache, &cursor); + if (ret != KRB5_CC_END) + goto err; + *val_out = array; + return 0; + +err: + k5_json_release(array); + return ret; +} + +/* Return a JSON null, string, or array value representing ccache. */ +static krb5_error_code +json_ccache(krb5_context context, krb5_ccache ccache, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_string str; + char *name; + + *val_out = NULL; + if (ccache == NULL) + return k5_json_null_create_val(val_out); + if (strcmp(krb5_cc_get_type(context, ccache), "MEMORY") == 0) { + return json_ccache_contents(context, ccache, val_out); + } else { + ret = krb5_cc_get_full_name(context, ccache, &name); + if (ret) + return ret; + ret = k5_json_string_create(name, &str); + free(name); + *val_out = str; + return ret; + } +} + +/* Return a JSON array value representing cred. */ +static krb5_error_code +json_kgcred(krb5_context context, krb5_gss_cred_id_t cred, + k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_array array; + k5_json_value name = NULL, imp = NULL, keytab = NULL, rcache = NULL; + k5_json_value ccache = NULL, ckeytab = NULL, etypes = NULL; + + *val_out = NULL; + ret = json_kgname(context, cred->name, &name); + if (ret) + goto cleanup; + ret = json_principal(context, cred->impersonator, &imp); + if (ret) + goto cleanup; + ret = json_keytab(context, cred->keytab, &keytab); + if (ret) + goto cleanup; + ret = json_rcache(context, cred->rcache, &rcache); + if (ret) + goto cleanup; + ret = json_ccache(context, cred->ccache, &ccache); + if (ret) + goto cleanup; + ret = json_keytab(context, cred->client_keytab, &ckeytab); + if (ret) + goto cleanup; + ret = json_etypes(cred->req_enctypes, &etypes); + if (ret) + goto cleanup; + + ret = k5_json_array_fmt(&array, "ivvbbvvvvbiivs", cred->usage, name, imp, + cred->default_identity, cred->iakerb_mech, keytab, + rcache, ccache, ckeytab, cred->have_tgt, + cred->expire, cred->refresh_time, etypes, + cred->password); + if (ret) + goto cleanup; + *val_out = array; + +cleanup: + k5_json_release(name); + k5_json_release(imp); + k5_json_release(keytab); + k5_json_release(rcache); + k5_json_release(ccache); + k5_json_release(ckeytab); + k5_json_release(etypes); + return ret; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_export_cred(OM_uint32 *minor_status, gss_cred_id_t cred_handle, + gss_buffer_t token) +{ + OM_uint32 status = GSS_S_COMPLETE; + krb5_context context; + krb5_error_code ret; + krb5_gss_cred_id_t cred; + k5_json_array array = NULL; + k5_json_value jcred = NULL; + char *str = NULL; + krb5_data d; + + ret = krb5_gss_init_context(&context); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + + /* Validate and lock cred_handle. */ + status = krb5_gss_validate_cred_1(minor_status, cred_handle, context); + if (status != GSS_S_COMPLETE) + return status; + cred = (krb5_gss_cred_id_t)cred_handle; + + if (json_kgcred(context, cred, &jcred)) + goto oom; + if (k5_json_array_fmt(&array, "sv", CRED_EXPORT_MAGIC, jcred)) + goto oom; + if (k5_json_encode(array, &str)) + goto oom; + d = string2data(str); + if (data_to_gss(&d, token)) + goto oom; + str = NULL; + +cleanup: + free(str); + k5_mutex_unlock(&cred->lock); + k5_json_release(array); + k5_json_release(jcred); + krb5_free_context(context); + return status; + +oom: + *minor_status = ENOMEM; + status = GSS_S_FAILURE; + goto cleanup; +} diff --git a/src/lib/gssapi/krb5/export_name.c b/src/lib/gssapi/krb5/export_name.c new file mode 100644 index 000000000000..485ebfb6cdf3 --- /dev/null +++ b/src/lib/gssapi/krb5/export_name.c @@ -0,0 +1,88 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/export_name.c */ +/* + * Copyright 1997, 2007 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 "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_export_name(OM_uint32 *minor_status, const gss_name_t input_name, + gss_buffer_t exported_name) +{ + krb5_context context; + krb5_error_code code; + size_t length; + char *str; + unsigned char *cp; + + if (minor_status) + *minor_status = 0; + + code = krb5_gss_init_context(&context); + if (code) { + if (minor_status) + *minor_status = code; + return GSS_S_FAILURE; + } + + exported_name->length = 0; + exported_name->value = NULL; + + if ((code = krb5_unparse_name(context, ((krb5_gss_name_t) input_name)->princ, + &str))) { + if (minor_status) + *minor_status = code; + save_error_info((OM_uint32)code, context); + krb5_free_context(context); + return(GSS_S_FAILURE); + } + + krb5_free_context(context); + length = strlen(str); + exported_name->length = 10 + length + gss_mech_krb5->length; + exported_name->value = gssalloc_malloc(exported_name->length); + if (!exported_name->value) { + free(str); + if (minor_status) + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + cp = exported_name->value; + + /* Note: we assume the OID will be less than 128 bytes... */ + *cp++ = 0x04; *cp++ = 0x01; + store_16_be(gss_mech_krb5->length+2, cp); + cp += 2; + *cp++ = 0x06; + *cp++ = (gss_mech_krb5->length) & 0xFF; + memcpy(cp, gss_mech_krb5->elements, gss_mech_krb5->length); + cp += gss_mech_krb5->length; + store_32_be(length, cp); + cp += 4; + memcpy(cp, str, length); + + free(str); + + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/krb5/export_sec_context.c b/src/lib/gssapi/krb5/export_sec_context.c new file mode 100644 index 000000000000..49bd76d2bc21 --- /dev/null +++ b/src/lib/gssapi/krb5/export_sec_context.c @@ -0,0 +1,101 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/export_sec_context.c - Externalize a security context */ +/* + * Copyright 1995, 2008 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 "gssapiP_krb5.h" +#ifndef LEAN_CLIENT +OM_uint32 KRB5_CALLCONV +krb5_gss_export_sec_context(minor_status, context_handle, interprocess_token) + OM_uint32 *minor_status; + gss_ctx_id_t *context_handle; + gss_buffer_t interprocess_token; +{ + krb5_context context = NULL; + krb5_error_code kret; + OM_uint32 retval; + size_t bufsize, blen; + krb5_gss_ctx_id_t ctx; + krb5_octet *obuffer, *obp; + + /* Assume a tragic failure */ + obuffer = (krb5_octet *) NULL; + retval = GSS_S_FAILURE; + *minor_status = 0; + + ctx = (krb5_gss_ctx_id_t) *context_handle; + if (ctx->terminated) { + *minor_status = KG_CTX_INCOMPLETE; + return (GSS_S_NO_CONTEXT); + } + + context = ctx->k5_context; + kret = krb5_gss_ser_init(context); + if (kret) + goto error_out; + + /* Determine size needed for externalization of context */ + bufsize = 0; + if ((kret = kg_ctx_size(context, (krb5_pointer) ctx, + &bufsize))) + goto error_out; + + /* Allocate the buffer */ + if ((obuffer = gssalloc_malloc(bufsize)) == NULL) { + kret = ENOMEM; + goto error_out; + } + + obp = obuffer; + blen = bufsize; + /* Externalize the context */ + if ((kret = kg_ctx_externalize(context, + (krb5_pointer) ctx, &obp, &blen))) + goto error_out; + + /* Success! Return the buffer */ + interprocess_token->length = bufsize - blen; + interprocess_token->value = obuffer; + *minor_status = 0; + retval = GSS_S_COMPLETE; + + /* Now, clean up the context state */ + (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL); + *context_handle = GSS_C_NO_CONTEXT; + + return (GSS_S_COMPLETE); + +error_out: + if (retval != GSS_S_COMPLETE) + if (kret != 0 && context != 0) + save_error_info((OM_uint32)kret, context); + if (obuffer && bufsize) { + zap(obuffer, bufsize); + xfree(obuffer); + } + if (*minor_status == 0) + *minor_status = (OM_uint32) kret; + return(retval); +} +#endif /* LEAN_CLIENT */ diff --git a/src/lib/gssapi/krb5/get_tkt_flags.c b/src/lib/gssapi/krb5/get_tkt_flags.c new file mode 100644 index 000000000000..636fc04db44c --- /dev/null +++ b/src/lib/gssapi/krb5/get_tkt_flags.c @@ -0,0 +1,45 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" + +/* + * $Id$ + */ + +OM_uint32 +gss_krb5int_get_tkt_flags(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + krb5_gss_ctx_id_rec *ctx; + gss_buffer_desc rep; + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + + rep.value = &ctx->krb_flags; + rep.length = sizeof(ctx->krb_flags); + + return generic_gss_add_buffer_set_member(minor_status, &rep, data_set); +} diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h new file mode 100644 index 000000000000..d7bdef7e28a3 --- /dev/null +++ b/src/lib/gssapi/krb5/gssapiP_krb5.h @@ -0,0 +1,1428 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * Copyright 2000, 2008 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. + * + */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _GSSAPIP_KRB5_H_ +#define _GSSAPIP_KRB5_H_ + +#include <k5-int.h> + +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif + +/* work around sunos braindamage */ +#ifdef major +#undef major +#endif +#ifdef minor +#undef minor +#endif + +#include "gssapiP_generic.h" + +/* The include of gssapi_krb5.h will dtrt with the above #defines in + * effect. + */ +#include "gssapi_krb5.h" +#include "gssapi_err_krb5.h" +#include "gssapi_ext.h" + +/* for debugging */ +#undef CFX_EXERCISE + +/** constants **/ + +#define GSS_MECH_KRB5_OID_LENGTH 9 +#define GSS_MECH_KRB5_OID "\052\206\110\206\367\022\001\002\002" + +#define GSS_MECH_KRB5_OLD_OID_LENGTH 5 +#define GSS_MECH_KRB5_OLD_OID "\053\005\001\005\002" + +/* Incorrect krb5 mech OID emitted by MS. */ +#define GSS_MECH_KRB5_WRONG_OID_LENGTH 9 +#define GSS_MECH_KRB5_WRONG_OID "\052\206\110\202\367\022\001\002\002" + +/* IAKERB variant */ +#define GSS_MECH_IAKERB_OID_LENGTH 6 +#define GSS_MECH_IAKERB_OID "\053\006\001\005\002\005" + +extern const gss_OID_set kg_all_mechs; + +#define CKSUMTYPE_KG_CB 0x8003 + +#define KG_TOK_CTX_AP_REQ 0x0100 +#define KG_TOK_CTX_AP_REP 0x0200 +#define KG_TOK_CTX_ERROR 0x0300 +#define KG_TOK_SIGN_MSG 0x0101 +#define KG_TOK_SEAL_MSG 0x0201 +#define KG_TOK_MIC_MSG 0x0101 +#define KG_TOK_WRAP_MSG 0x0201 +#define KG_TOK_DEL_CTX 0x0102 +#define KG2_TOK_MIC_MSG 0x0404 +#define KG2_TOK_WRAP_MSG 0x0504 +#define KG2_TOK_DEL_CTX 0x0405 +#define IAKERB_TOK_PROXY 0x0501 + +#define KRB5_GSS_FOR_CREDS_OPTION 1 + +#define KG2_RESP_FLAG_ERROR 0x0001 +#define KG2_RESP_FLAG_DELEG_OK 0x0002 + +/** CFX flags **/ +#define FLAG_SENDER_IS_ACCEPTOR 0x01 +#define FLAG_WRAP_CONFIDENTIAL 0x02 +#define FLAG_ACCEPTOR_SUBKEY 0x04 + +/* These are to be stored in little-endian order, i.e., des-mac is + stored as 02 00. */ +enum sgn_alg { + SGN_ALG_DES_MAC_MD5 = 0x0000, + SGN_ALG_MD2_5 = 0x0001, + SGN_ALG_DES_MAC = 0x0002, + SGN_ALG_3 = 0x0003, /* not published */ + SGN_ALG_HMAC_MD5 = 0x0011, /* microsoft w2k; */ + SGN_ALG_HMAC_SHA1_DES3_KD = 0x0004 +}; +enum seal_alg { + SEAL_ALG_NONE = 0xffff, + SEAL_ALG_DES = 0x0000, + SEAL_ALG_1 = 0x0001, /* not published */ + SEAL_ALG_MICROSOFT_RC4 = 0x0010, /* microsoft w2k; */ + SEAL_ALG_DES3KD = 0x0002 +}; + +/* for 3DES */ +#define KG_USAGE_SEAL 22 +#define KG_USAGE_SIGN 23 +#define KG_USAGE_SEQ 24 + +/* for draft-ietf-krb-wg-gssapi-cfx-01 */ +#define KG_USAGE_ACCEPTOR_SEAL 22 +#define KG_USAGE_ACCEPTOR_SIGN 23 +#define KG_USAGE_INITIATOR_SEAL 24 +#define KG_USAGE_INITIATOR_SIGN 25 + +enum qop { + GSS_KRB5_INTEG_C_QOP_MD5 = 0x0001, /* *partial* MD5 = "MD2.5" */ + GSS_KRB5_INTEG_C_QOP_DES_MD5 = 0x0002, + GSS_KRB5_INTEG_C_QOP_DES_MAC = 0x0003, + GSS_KRB5_INTEG_C_QOP_HMAC_SHA1 = 0x0004, + GSS_KRB5_INTEG_C_QOP_MASK = 0x00ff, + GSS_KRB5_CONF_C_QOP_DES = 0x0100, + GSS_KRB5_CONF_C_QOP_DES3_KD = 0x0200, + GSS_KRB5_CONF_C_QOP_MASK = 0xff00 +}; + +/** internal types **/ + +typedef struct _krb5_gss_name_rec { + krb5_principal princ; /* immutable */ + char *service; /* immutable */ + char *host; /* immutable */ + k5_mutex_t lock; /* protects ad_context only for now */ + krb5_authdata_context ad_context; +} krb5_gss_name_rec, *krb5_gss_name_t; + +typedef struct _krb5_gss_cred_id_rec { + /* protect against simultaneous accesses */ + k5_mutex_t lock; + + /* name/type of credential */ + gss_cred_usage_t usage; + krb5_gss_name_t name; + krb5_principal impersonator; + unsigned int default_identity : 1; + unsigned int iakerb_mech : 1; + unsigned int destroy_ccache : 1; + unsigned int suppress_ci_flags : 1; + + /* keytab (accept) data */ + krb5_keytab keytab; + krb5_rcache rcache; + + /* ccache (init) data */ + krb5_ccache ccache; + krb5_keytab client_keytab; + krb5_boolean have_tgt; + krb5_timestamp expire; + krb5_timestamp refresh_time; + krb5_enctype *req_enctypes; /* limit negotiated enctypes to this list */ + char *password; +} krb5_gss_cred_id_rec, *krb5_gss_cred_id_t; + +typedef struct _krb5_gss_ctx_ext_rec { + struct { + krb5_data *conv; + int verified; + } iakerb; +} krb5_gss_ctx_ext_rec, *krb5_gss_ctx_ext_t; + +typedef struct _krb5_gss_ctx_id_rec { + krb5_magic magic; + unsigned int initiate : 1; /* nonzero if initiating, zero if accepting */ + unsigned int established : 1; + unsigned int have_acceptor_subkey : 1; + unsigned int seed_init : 1; /* XXX tested but never actually set */ + unsigned int terminated : 1; + OM_uint32 gss_flags; + unsigned char seed[16]; + krb5_gss_name_t here; + krb5_gss_name_t there; + krb5_key subkey; /* One of two potential keys to use with RFC 4121 + * packets; this key must always be set. */ + int signalg; + size_t cksum_size; + int sealalg; + krb5_key enc; /* RFC 1964 encryption key; seq xored with a constant + * for DES, seq for other RFC 1964 enctypes */ + krb5_key seq; /* RFC 1964 sequencing key */ + krb5_ticket_times krb_times; + krb5_flags krb_flags; + /* XXX these used to be signed. the old spec is inspecific, and + the new spec specifies unsigned. I don't believe that the change + affects the wire encoding. */ + uint64_t seq_send; + uint64_t seq_recv; + g_seqnum_state seqstate; + krb5_context k5_context; + krb5_auth_context auth_context; + gss_OID_desc *mech_used; + /* Protocol spec revision for sending packets + 0 => RFC 1964 with 3DES and RC4 enhancements + 1 => RFC 4121 + No others defined so far. It is always permitted to receive + tokens in RFC 4121 format. If enc is non-null, receiving RFC + 1964 tokens is permitted.*/ + int proto; + krb5_cksumtype cksumtype; /* for "main" subkey */ + krb5_key acceptor_subkey; /* CFX only */ + krb5_cksumtype acceptor_subkey_cksumtype; + int cred_rcache; /* did we get rcache from creds? */ + krb5_authdata **authdata; +} krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t; + +extern g_set kg_vdb; + +#ifndef LEAN_CLIENT +extern k5_mutex_t gssint_krb5_keytab_lock; +#endif /* LEAN_CLIENT */ + +/** helper functions **/ + +OM_uint32 kg_get_defcred +(OM_uint32 *minor_status, + gss_cred_id_t *cred); + +krb5_error_code kg_checksum_channel_bindings +(krb5_context context, gss_channel_bindings_t cb, + krb5_checksum *cksum); + +krb5_error_code kg_make_seq_num (krb5_context context, + krb5_key key, + int direction, krb5_ui_4 seqnum, unsigned char *cksum, + unsigned char *buf); + +krb5_error_code kg_get_seq_num (krb5_context context, + krb5_key key, + unsigned char *cksum, unsigned char *buf, int *direction, + krb5_ui_4 *seqnum); + +krb5_error_code kg_make_seed (krb5_context context, + krb5_key key, + unsigned char *seed); + +krb5_error_code +kg_setup_keys(krb5_context context, + krb5_gss_ctx_id_rec *ctx, + krb5_key subkey, + krb5_cksumtype *cksumtype); + +int kg_confounder_size (krb5_context context, krb5_enctype enctype); + +krb5_error_code kg_make_confounder (krb5_context context, + krb5_enctype enctype, unsigned char *buf); + +krb5_error_code kg_encrypt (krb5_context context, + krb5_key key, int usage, + krb5_pointer iv, + krb5_const_pointer in, + krb5_pointer out, + unsigned int length); + +/* Encrypt length bytes at ptr in place, with the given key and usage. If + * iv is not NULL, use it as the cipher state. */ +krb5_error_code kg_encrypt_inplace(krb5_context context, krb5_key key, + int usage, krb5_pointer iv, + krb5_pointer ptr, unsigned int length); + +krb5_error_code kg_encrypt_iov (krb5_context context, + int proto, int dce_style, + size_t ec, size_t rrc, + krb5_key key, int usage, + krb5_pointer iv, + gss_iov_buffer_desc *iov, + int iov_count); + +krb5_error_code +kg_arcfour_docrypt (const krb5_keyblock *keyblock, int usage, + const unsigned char *kd_data, size_t kd_data_len, + const unsigned char *input_buf, size_t input_len, + unsigned char *output_buf); + +krb5_error_code +kg_arcfour_docrypt_iov (krb5_context context, + const krb5_keyblock *keyblock, int usage, + const unsigned char *kd_data, size_t kd_data_len, + gss_iov_buffer_desc *iov, + int iov_count); + +krb5_error_code kg_decrypt (krb5_context context, + krb5_key key, int usage, + krb5_pointer iv, + krb5_const_pointer in, + krb5_pointer out, + unsigned int length); + +krb5_error_code kg_decrypt_iov (krb5_context context, + int proto, int dce_style, + size_t ec, size_t rrc, + krb5_key key, int usage, + krb5_pointer iv, + gss_iov_buffer_desc *iov, + int iov_count); + +OM_uint32 kg_seal (OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + gss_buffer_t input_message_buffer, + int *conf_state, + gss_buffer_t output_message_buffer, + int toktype); + +OM_uint32 kg_unseal (OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_buffer_t input_token_buffer, + gss_buffer_t message_buffer, + int *conf_state, + gss_qop_t *qop_state, + int toktype); + +OM_uint32 kg_seal_size (OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + OM_uint32 output_size, + OM_uint32 *input_size); + +krb5_error_code kg_ctx_size (krb5_context kcontext, + krb5_pointer arg, + size_t *sizep); + +krb5_error_code kg_ctx_externalize (krb5_context kcontext, + krb5_pointer arg, + krb5_octet **buffer, + size_t *lenremain); + +krb5_error_code kg_ctx_internalize (krb5_context kcontext, + krb5_pointer *argp, + krb5_octet **buffer, + size_t *lenremain); + +OM_uint32 kg_sync_ccache_name (krb5_context context, OM_uint32 *minor_status); + +OM_uint32 kg_caller_provided_ccache_name (OM_uint32 *minor_status, + int *out_caller_provided_name); + +OM_uint32 kg_get_ccache_name (OM_uint32 *minor_status, + const char **out_name); + +OM_uint32 kg_set_ccache_name (OM_uint32 *minor_status, + const char *name); + +/* AEAD */ + +krb5_error_code gss_krb5int_make_seal_token_v3_iov(krb5_context context, + krb5_gss_ctx_id_rec *ctx, + int conf_req_flag, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype); + +OM_uint32 gss_krb5int_unseal_v3_iov(krb5_context context, + OM_uint32 *minor_status, + krb5_gss_ctx_id_rec *ctx, + gss_iov_buffer_desc *iov, + int iov_count, + int *conf_state, + gss_qop_t *qop_state, + int toktype); + +gss_iov_buffer_t kg_locate_iov (gss_iov_buffer_desc *iov, + int iov_count, + OM_uint32 type); + +gss_iov_buffer_t kg_locate_header_iov(gss_iov_buffer_desc *iov, int iov_count, + int toktype); + +void kg_iov_msglen(gss_iov_buffer_desc *iov, + int iov_count, + size_t *data_length, + size_t *assoc_data_length); + +void kg_release_iov(gss_iov_buffer_desc *iov, + int iov_count); + +krb5_error_code kg_make_checksum_iov_v1(krb5_context context, + krb5_cksumtype type, + size_t token_cksum_len, + krb5_key seq, + krb5_key enc, /* for conf len */ + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype, + krb5_checksum *checksum); + +krb5_error_code kg_make_checksum_iov_v3(krb5_context context, + krb5_cksumtype type, + size_t rrc, + krb5_key key, + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype); + +krb5_error_code kg_verify_checksum_iov_v3(krb5_context context, + krb5_cksumtype type, + size_t rrc, + krb5_key key, + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype, + krb5_boolean *valid); + +OM_uint32 kg_seal_iov (OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype); + +OM_uint32 kg_unseal_iov (OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype); + +OM_uint32 kg_seal_iov_length(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype); + +krb5_cryptotype kg_translate_flag_iov(OM_uint32 type); + +OM_uint32 kg_fixup_padding_iov(OM_uint32 *minor_status, + gss_iov_buffer_desc *iov, + int iov_count); + +krb5_boolean kg_integ_only_iov(gss_iov_buffer_desc *iov, int iov_count); + +krb5_error_code kg_allocate_iov(gss_iov_buffer_t iov, size_t size); + +krb5_error_code +krb5_to_gss_cred(krb5_context context, + krb5_creds *creds, + krb5_gss_cred_id_t *out_cred); + +krb5_boolean +kg_cred_time_to_refresh(krb5_context context, krb5_gss_cred_id_rec *cred); + +void +kg_cred_set_initial_refresh(krb5_context context, krb5_gss_cred_id_rec *cred, + krb5_ticket_times *times); + +OM_uint32 +kg_cred_resolve(OM_uint32 *minor_status, krb5_context context, + gss_cred_id_t cred_handle, gss_name_t target_name); + +/** declarations of internal name mechanism functions **/ + +OM_uint32 KRB5_CALLCONV krb5_gss_acquire_cred +(OM_uint32*, /* minor_status */ + gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t*, /* output_cred_handle */ + gss_OID_set*, /* actual_mechs */ + OM_uint32* /* time_rec */ +); + +OM_uint32 KRB5_CALLCONV iakerb_gss_acquire_cred +(OM_uint32*, /* minor_status */ + gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t*, /* output_cred_handle */ + gss_OID_set*, /* actual_mechs */ + OM_uint32* /* time_rec */ +); + +OM_uint32 KRB5_CALLCONV +krb5_gss_acquire_cred_with_password( + OM_uint32 *minor_status, + const gss_name_t desired_name, + const gss_buffer_t password, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + int cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_acquire_cred_with_password( + OM_uint32 *minor_status, + const gss_name_t desired_name, + const gss_buffer_t password, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + int cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec); + +OM_uint32 KRB5_CALLCONV krb5_gss_release_cred +(OM_uint32*, /* minor_status */ + gss_cred_id_t* /* cred_handle */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_init_sec_context +(OM_uint32*, /* minor_status */ + gss_cred_id_t, /* claimant_cred_handle */ + gss_ctx_id_t*, /* context_handle */ + gss_name_t, /* target_name */ + gss_OID, /* mech_type */ + OM_uint32, /* req_flags */ + OM_uint32, /* time_req */ + gss_channel_bindings_t, + /* input_chan_bindings */ + gss_buffer_t, /* input_token */ + gss_OID*, /* actual_mech_type */ + gss_buffer_t, /* output_token */ + OM_uint32*, /* ret_flags */ + OM_uint32* /* time_rec */ +); + +OM_uint32 krb5_gss_init_sec_context_ext +(OM_uint32*, /* minor_status */ + gss_cred_id_t, /* claimant_cred_handle */ + gss_ctx_id_t*, /* context_handle */ + gss_name_t, /* target_name */ + gss_OID, /* mech_type */ + OM_uint32, /* req_flags */ + OM_uint32, /* time_req */ + gss_channel_bindings_t, + /* input_chan_bindings */ + gss_buffer_t, /* input_token */ + gss_OID*, /* actual_mech_type */ + gss_buffer_t, /* output_token */ + OM_uint32*, /* ret_flags */ + OM_uint32*, /* time_rec */ + krb5_gss_ctx_ext_t /* exts */ +); + +#ifndef LEAN_CLIENT +OM_uint32 KRB5_CALLCONV krb5_gss_accept_sec_context +(OM_uint32*, /* minor_status */ + gss_ctx_id_t*, /* context_handle */ + gss_cred_id_t, /* verifier_cred_handle */ + gss_buffer_t, /* input_token_buffer */ + gss_channel_bindings_t, + /* input_chan_bindings */ + gss_name_t*, /* src_name */ + gss_OID*, /* mech_type */ + gss_buffer_t, /* output_token */ + OM_uint32*, /* ret_flags */ + OM_uint32*, /* time_rec */ + gss_cred_id_t* /* delegated_cred_handle */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_accept_sec_context_ext +(OM_uint32*, /* minor_status */ + gss_ctx_id_t*, /* context_handle */ + gss_cred_id_t, /* verifier_cred_handle */ + gss_buffer_t, /* input_token_buffer */ + gss_channel_bindings_t, + /* input_chan_bindings */ + gss_name_t*, /* src_name */ + gss_OID*, /* mech_type */ + gss_buffer_t, /* output_token */ + OM_uint32*, /* ret_flags */ + OM_uint32*, /* time_rec */ + gss_cred_id_t*, /* delegated_cred_handle */ + krb5_gss_ctx_ext_t/*exts */ +); +#endif /* LEAN_CLIENT */ + +OM_uint32 KRB5_CALLCONV krb5_gss_inquire_sec_context_by_oid +(OM_uint32*, /* minor_status */ + const gss_ctx_id_t, + /* context_handle */ + const gss_OID, /* desired_object */ + gss_buffer_set_t* /* data_set */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_set_sec_context_option +(OM_uint32*, /* minor_status */ + gss_ctx_id_t*, /* context_handle */ + const gss_OID, /* desired_object */ + const gss_buffer_t/* value */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_process_context_token +(OM_uint32*, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_buffer_t /* token_buffer */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_delete_sec_context +(OM_uint32*, /* minor_status */ + gss_ctx_id_t*, /* context_handle */ + gss_buffer_t /* output_token */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_context_time +(OM_uint32*, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + OM_uint32* /* time_rec */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_display_status +(OM_uint32*, /* minor_status */ + OM_uint32, /* status_value */ + int, /* status_type */ + gss_OID, /* mech_type */ + OM_uint32*, /* message_context */ + gss_buffer_t /* status_string */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_indicate_mechs +(OM_uint32*, /* minor_status */ + gss_OID_set* /* mech_set */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_compare_name +(OM_uint32*, /* minor_status */ + gss_name_t, /* name1 */ + gss_name_t, /* name2 */ + int* /* name_equal */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_display_name +(OM_uint32*, /* minor_status */ + gss_name_t, /* input_name */ + gss_buffer_t, /* output_name_buffer */ + gss_OID* /* output_name_type */ +); + + +OM_uint32 KRB5_CALLCONV krb5_gss_import_name +(OM_uint32*, /* minor_status */ + gss_buffer_t, /* input_name_buffer */ + gss_OID, /* input_name_type */ + gss_name_t* /* output_name */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_release_name +(OM_uint32*, /* minor_status */ + gss_name_t* /* input_name */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_inquire_cred +(OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* cred_handle */ + gss_name_t *, /* name */ + OM_uint32 *, /* lifetime */ + gss_cred_usage_t*,/* cred_usage */ + gss_OID_set * /* mechanisms */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_inquire_context +(OM_uint32*, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_name_t*, /* initiator_name */ + gss_name_t*, /* acceptor_name */ + OM_uint32*, /* lifetime_rec */ + gss_OID*, /* mech_type */ + OM_uint32*, /* ret_flags */ + int*, /* locally_initiated */ + int* /* open */ +); + +/* New V2 entry points */ +OM_uint32 KRB5_CALLCONV krb5_gss_get_mic +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_qop_t, /* qop_req */ + gss_buffer_t, /* message_buffer */ + gss_buffer_t /* message_token */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_get_mic_iov +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_qop_t, /* qop_req */ + gss_iov_buffer_desc *, /* iov */ + int /* iov_count */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_get_mic_iov_length +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_qop_t, /* qop_req */ + gss_iov_buffer_desc *, /* iov */ + int /* iov_count */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_verify_mic +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_buffer_t, /* message_buffer */ + gss_buffer_t, /* message_token */ + gss_qop_t * /* qop_state */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_verify_mic_iov +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_qop_t *, /* qop_state */ + gss_iov_buffer_desc *, /* iov */ + int /* iov_count */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_wrap +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* conf_req_flag */ + gss_qop_t, /* qop_req */ + gss_buffer_t, /* input_message_buffer */ + int *, /* conf_state */ + gss_buffer_t /* output_message_buffer */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_wrap_iov +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* conf_req_flag */ + gss_qop_t, /* qop_req */ + int *, /* conf_state */ + gss_iov_buffer_desc *, /* iov */ + int /* iov_count */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_wrap_iov_length +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* conf_req_flag */ + gss_qop_t, /* qop_req */ + int *, /* conf_state */ + gss_iov_buffer_desc *, /* iov */ + int /* iov_count */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_unwrap +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_buffer_t, /* input_message_buffer */ + gss_buffer_t, /* output_message_buffer */ + int *, /* conf_state */ + gss_qop_t * /* qop_state */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_unwrap_iov +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int *, /* conf_state */ + gss_qop_t *, /* qop_state */ + gss_iov_buffer_desc *, /* iov */ + int /* iov_count */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_wrap_size_limit +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* conf_req_flag */ + gss_qop_t, /* qop_req */ + OM_uint32, /* req_output_size */ + OM_uint32 * /* max_input_size */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_import_name_object +(OM_uint32 *, /* minor_status */ + void *, /* input_name */ + gss_OID, /* input_name_type */ + gss_name_t * /* output_name */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_export_name_object +(OM_uint32 *, /* minor_status */ + gss_name_t, /* input_name */ + gss_OID, /* desired_name_type */ + void * * /* output_name */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_inquire_cred_by_mech +(OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* cred_handle */ + gss_OID, /* mech_type */ + gss_name_t *, /* name */ + OM_uint32 *, /* initiator_lifetime */ + OM_uint32 *, /* acceptor_lifetime */ + gss_cred_usage_t * /* cred_usage */ +); +#ifndef LEAN_CLIENT +OM_uint32 KRB5_CALLCONV krb5_gss_export_sec_context +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t *, /* context_handle */ + gss_buffer_t /* interprocess_token */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_import_sec_context +(OM_uint32 *, /* minor_status */ + gss_buffer_t, /* interprocess_token */ + gss_ctx_id_t * /* context_handle */ +); +#endif /* LEAN_CLIENT */ + +krb5_error_code krb5_gss_ser_init(krb5_context); + +OM_uint32 krb5_gss_release_oid +(OM_uint32 *, /* minor_status */ + gss_OID * /* oid */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_internal_release_oid +(OM_uint32 *, /* minor_status */ + gss_OID * /* oid */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_inquire_names_for_mech +(OM_uint32 *, /* minor_status */ + gss_OID, /* mechanism */ + gss_OID_set * /* name_types */ +); + +OM_uint32 krb5_gss_canonicalize_name +(OM_uint32 *, /* minor_status */ + const gss_name_t, /* input_name */ + const gss_OID, /* mech_type */ + gss_name_t * /* output_name */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_export_name +(OM_uint32 *, /* minor_status */ + const gss_name_t, /* input_name */ + gss_buffer_t /* exported_name */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_duplicate_name +(OM_uint32 *, /* minor_status */ + const gss_name_t, /* input_name */ + gss_name_t * /* dest_name */ +); + +OM_uint32 krb5_gss_validate_cred +(OM_uint32 *, /* minor_status */ + gss_cred_id_t /* cred */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_acquire_cred_impersonate_name( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *); /* time_rec */ + +OM_uint32 +krb5_gss_validate_cred_1(OM_uint32 * /* minor_status */, + gss_cred_id_t /* cred_handle */, + krb5_context /* context */); + +gss_OID krb5_gss_convert_static_mech_oid(gss_OID oid); + +krb5_error_code gss_krb5int_make_seal_token_v3(krb5_context, + krb5_gss_ctx_id_rec *, + const gss_buffer_desc *, + gss_buffer_t, + int, int); + +OM_uint32 gss_krb5int_unseal_token_v3(krb5_context *contextptr, + OM_uint32 *minor_status, + krb5_gss_ctx_id_rec *ctx, + unsigned char *ptr, + unsigned int bodysize, + gss_buffer_t message_buffer, + int *conf_state, gss_qop_t *qop_state, + int toktype); + +int gss_krb5int_rotate_left (void *ptr, size_t bufsiz, size_t rc); + +/* naming_exts.c */ +#define KG_INIT_NAME_NO_COPY 0x1 + +krb5_error_code +kg_init_name(krb5_context context, krb5_principal principal, + char *service, char *host, krb5_authdata_context ad_context, + krb5_flags flags, krb5_gss_name_t *name); + +krb5_error_code +kg_release_name(krb5_context context, krb5_gss_name_t *name); + +krb5_error_code +kg_duplicate_name(krb5_context context, const krb5_gss_name_t src, + krb5_gss_name_t *dst); + +krb5_boolean +kg_compare_name(krb5_context context, + krb5_gss_name_t name1, + krb5_gss_name_t name2); + +krb5_boolean +kg_acceptor_princ(krb5_context context, krb5_gss_name_t name, + krb5_principal *princ_out); + +OM_uint32 KRB5_CALLCONV +krb5_gss_display_name_ext(OM_uint32 *minor_status, + gss_name_t name, + gss_OID display_as_name_type, + gss_buffer_t display_name); + +OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_name(OM_uint32 *minor_status, + gss_name_t name, + int *name_is_MN, + gss_OID *MN_mech, + gss_buffer_set_t *attrs); + +OM_uint32 KRB5_CALLCONV +krb5_gss_get_name_attribute(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more); + +OM_uint32 KRB5_CALLCONV +krb5_gss_set_name_attribute(OM_uint32 *minor_status, + gss_name_t name, + int complete, + gss_buffer_t attr, + gss_buffer_t value); + +OM_uint32 KRB5_CALLCONV +krb5_gss_delete_name_attribute(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t attr); + +OM_uint32 KRB5_CALLCONV +krb5_gss_export_name_composite(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t exp_composite_name); + +OM_uint32 KRB5_CALLCONV +krb5_gss_map_name_to_any(OM_uint32 *minor_status, + gss_name_t name, + int authenticated, + gss_buffer_t type_id, + gss_any_t *output); + +OM_uint32 KRB5_CALLCONV +krb5_gss_release_any_name_mapping(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t type_id, + gss_any_t *input); + +OM_uint32 KRB5_CALLCONV +krb5_gss_pseudo_random(OM_uint32 *minor_status, + gss_ctx_id_t context, + int prf_key, + const gss_buffer_t prf_in, + ssize_t desired_output_len, + gss_buffer_t prf_out); + +OM_uint32 KRB5_CALLCONV +krb5_gss_store_cred(OM_uint32 *minor_status, + gss_cred_id_t input_cred_handle, + gss_cred_usage_t cred_usage, + const gss_OID desired_mech, + OM_uint32 overwrite_cred, + OM_uint32 default_cred, + gss_OID_set *elements_stored, + gss_cred_usage_t *cred_usage_stored); + +/* s4u_gss_glue.c */ +OM_uint32 +kg_compose_deleg_cred(OM_uint32 *minor_status, + krb5_gss_cred_id_t impersonator_cred, + krb5_creds *subject_creds, + OM_uint32 time_req, + krb5_gss_cred_id_t *output_cred, + OM_uint32 *time_rec, + krb5_context context); + +/* + * These take unglued krb5-mech-specific contexts. + */ + +#define GSS_KRB5_GET_TKT_FLAGS_OID_LENGTH 11 +#define GSS_KRB5_GET_TKT_FLAGS_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x01" + +OM_uint32 gss_krb5int_get_tkt_flags +(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set); + +#define GSS_KRB5_COPY_CCACHE_OID_LENGTH 11 +#define GSS_KRB5_COPY_CCACHE_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x02" + +OM_uint32 gss_krb5int_copy_ccache +(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_oid, + const gss_buffer_t value); + +#define GSS_KRB5_CCACHE_NAME_OID_LENGTH 11 +#define GSS_KRB5_CCACHE_NAME_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x03" + +struct krb5_gss_ccache_name_req { + const char *name; + const char **out_name; +}; + +OM_uint32 +gss_krb5int_ccache_name(OM_uint32 *minor_status, const gss_OID, const gss_OID, + const gss_buffer_t); + +#define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH 11 +#define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05" + +OM_uint32 +gss_krb5int_inq_session_key(OM_uint32 *, const gss_ctx_id_t, const gss_OID, gss_buffer_set_t *); + +#define GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID_LENGTH 11 +#define GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x04" + +struct krb5_gss_set_allowable_enctypes_req { + OM_uint32 num_ktypes; + krb5_enctype *ktypes; +}; + +OM_uint32 +gss_krb5int_set_allowable_enctypes(OM_uint32 *minor_status, + gss_cred_id_t *cred, + const gss_OID desired_oid, + const gss_buffer_t value); + +#define GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID_LENGTH 11 +#define GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x06" + +OM_uint32 +gss_krb5int_export_lucid_sec_context(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set); + +#define GSS_KRB5_FREE_LUCID_SEC_CONTEXT_OID_LENGTH 11 +#define GSS_KRB5_FREE_LUCID_SEC_CONTEXT_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x07" + +OM_uint32 +gss_krb5int_free_lucid_sec_context(OM_uint32 *, const gss_OID, + const gss_OID, gss_buffer_t); + +extern k5_mutex_t kg_kdc_flag_mutex; +krb5_error_code krb5_gss_init_context (krb5_context *ctxp); + +#define GSS_KRB5_USE_KDC_CONTEXT_OID_LENGTH 11 +#define GSS_KRB5_USE_KDC_CONTEXT_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x08" + +OM_uint32 krb5int_gss_use_kdc_context(OM_uint32 *, const gss_OID, + const gss_OID, gss_buffer_t); + +krb5_error_code krb5_gss_use_kdc_context(void); + +#define GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_OID_LENGTH 11 +#define GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x09" + +OM_uint32 +gss_krb5int_register_acceptor_identity(OM_uint32 *, const gss_OID, const gss_OID, gss_buffer_t); + +#define GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH 11 +#define GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0a" + +OM_uint32 +gss_krb5int_extract_authz_data_from_sec_context(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *ad_data); + +#define GSS_KRB5_SET_CRED_RCACHE_OID_LENGTH 11 +#define GSS_KRB5_SET_CRED_RCACHE_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0b" + +OM_uint32 +gss_krb5int_set_cred_rcache(OM_uint32 *, gss_cred_id_t *, const gss_OID, const gss_buffer_t); + +#define GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID_LENGTH 11 +#define GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0c" + +OM_uint32 +gss_krb5int_extract_authtime_from_sec_context(OM_uint32 *, + const gss_ctx_id_t, + const gss_OID, + gss_buffer_set_t *); + +#define GSS_KRB5_IMPORT_CRED_OID_LENGTH 11 +#define GSS_KRB5_IMPORT_CRED_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0d" + +struct krb5_gss_import_cred_req { + krb5_ccache id; + krb5_principal keytab_principal; + krb5_keytab keytab; +}; + +OM_uint32 +gss_krb5int_import_cred(OM_uint32 *minor_status, + gss_cred_id_t *cred, + const gss_OID desired_oid, + const gss_buffer_t value); + +#ifdef _GSS_STATIC_LINK +int gss_krb5int_lib_init(void); +void gss_krb5int_lib_fini(void); +#endif /* _GSS_STATIC_LINK */ + +OM_uint32 gss_krb5int_initialize_library(void); +void gss_krb5int_cleanup_library(void); + +/* For error message handling. */ +/* Returns a shared string, not a private copy! */ +extern char * +krb5_gss_get_error_message(OM_uint32 minor_code); +extern void +krb5_gss_save_error_string(OM_uint32 minor_code, char *msg); +extern void +krb5_gss_save_error_message(OM_uint32 minor_code, const char *format, ...) +#if !defined(__cplusplus) && (__GNUC__ > 2) + __attribute__((__format__(__printf__, 2, 3))) +#endif + ; + extern void + krb5_gss_save_error_info(OM_uint32 minor_code, krb5_context ctx); +#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 */ +#define GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH 10 +#define GSS_KRB5_SESSION_KEY_ENCTYPE_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x04" + +/* IAKERB */ + +OM_uint32 KRB5_CALLCONV +iakerb_gss_init_sec_context(OM_uint32 *minor_status, + gss_cred_id_t claimant_cred_handle, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_accept_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handler, + gss_cred_id_t verifier_cred_handle, + gss_buffer_t input_token, + gss_channel_bindings_t input_chan_bindings, + gss_name_t *src_name, + gss_OID *mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + gss_cred_id_t *delegated_cred_handle); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_delete_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token); + +krb5_error_code +iakerb_make_finished(krb5_context context, + krb5_key key, + const krb5_data *conv, + krb5_data **finished); + +krb5_error_code +iakerb_verify_finished(krb5_context context, + krb5_key key, + const krb5_data *conv, + const krb5_data *finished); + +/* + * Transfer contents of a krb5_data to a gss_buffer and invalidate the source + * On unix, this is a simple pointer copy + * On windows, memory is reallocated and copied. + */ +static inline krb5_error_code +data_to_gss(krb5_data *input_k5data, gss_buffer_t output_buffer) +{ + krb5_error_code code = 0; + output_buffer->length = input_k5data->length; +#if defined(_WIN32) || defined(DEBUG_GSSALLOC) + if (output_buffer->length > 0) { + output_buffer->value = gssalloc_malloc(output_buffer->length); + if (output_buffer->value) + memcpy(output_buffer->value, input_k5data->data, output_buffer->length); + else + code = ENOMEM; + } else { + output_buffer->value = NULL; + } + free(input_k5data->data); +#else + output_buffer->value = input_k5data->data; +#endif + *input_k5data = empty_data(); + return code; +} + +#define KRB5_GSS_EXTS_IAKERB_FINISHED 1 + + +/* Credential store extensions */ + +#define KRB5_CS_CLI_KEYTAB_URN "client_keytab" +#define KRB5_CS_KEYTAB_URN "keytab" +#define KRB5_CS_CCACHE_URN "ccache" +#define KRB5_CS_RCACHE_URN "rcache" + +OM_uint32 +kg_value_from_cred_store(gss_const_key_value_set_t cred_store, + const char *type, const char **value); + +OM_uint32 KRB5_CALLCONV +krb5_gss_acquire_cred_from( + OM_uint32 *, /* minor_status */ + const gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_const_key_value_set_t, /* cred_store */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *); /* time_rec */ + +OM_uint32 KRB5_CALLCONV +krb5_gss_store_cred_into( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* input_cred_handle */ + gss_cred_usage_t, /* input_usage */ + const gss_OID, /* desired_mech */ + OM_uint32, /* overwrite_cred */ + OM_uint32, /* default_cred */ + gss_const_key_value_set_t, /* cred_store */ + gss_OID_set *, /* elements_stored */ + gss_cred_usage_t *); /* cred_usage_stored */ + +OM_uint32 KRB5_CALLCONV +krb5_gss_export_cred(OM_uint32 *minor_status, gss_cred_id_t cred_handle, + gss_buffer_t token); + +OM_uint32 KRB5_CALLCONV +krb5_gss_import_cred(OM_uint32 *minor_status, gss_buffer_t token, + gss_cred_id_t *cred_handle); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_process_context_token(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t token_buffer); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_context_time(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + OM_uint32 *time_rec); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_inquire_context(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, gss_name_t *src_name, + gss_name_t *targ_name, OM_uint32 *lifetime_rec, + gss_OID *mech_type, OM_uint32 *ctx_flags, + int *locally_initiated, int *opened); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_get_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_qop_t qop_req, gss_buffer_t message_buffer, + gss_buffer_t message_token); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_get_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_qop_t qop_req, gss_iov_buffer_desc *iov, + int iov_count); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_get_mic_iov_length(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, gss_qop_t qop_req, + gss_iov_buffer_desc *iov, int iov_count); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_verify_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_buffer_t msg_buffer, gss_buffer_t token_buffer, + gss_qop_t *qop_state); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_verify_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_qop_t *qop_state, gss_iov_buffer_desc *iov, + int iov_count); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_wrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + int conf_req_flag, gss_qop_t qop_req, + gss_buffer_t input_message_buffer, int *conf_state, + gss_buffer_t output_message_buffer); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_wrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + int conf_req_flag, gss_qop_t qop_req, int *conf_state, + gss_iov_buffer_desc *iov, int iov_count); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_wrap_iov_length(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, int conf_req_flag, + gss_qop_t qop_req, int *conf_state, + gss_iov_buffer_desc *iov, int iov_count); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_unwrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_buffer_t input_message_buffer, + gss_buffer_t output_message_buffer, int *conf_state, + gss_qop_t *qop_state); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_unwrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + int *conf_state, gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, int iov_count); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_wrap_size_limit(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, int conf_req_flag, + gss_qop_t qop_req, OM_uint32 req_output_size, + OM_uint32 *max_input_size); + +#ifndef LEAN_CLIENT +OM_uint32 KRB5_CALLCONV +iakerb_gss_export_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t interprocess_token); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_import_sec_context(OM_uint32 *minor_status, + const gss_buffer_t interprocess_token, + gss_ctx_id_t *context_handle); +#endif /* LEAN_CLIENT */ + +OM_uint32 KRB5_CALLCONV +iakerb_gss_inquire_sec_context_by_oid(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_set_sec_context_option(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + const gss_OID desired_object, + const gss_buffer_t value); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_pseudo_random(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + int prf_key, const gss_buffer_t prf_in, + ssize_t desired_output_len, gss_buffer_t prf_out); + +/* Magic string to identify exported krb5 GSS credentials. Increment this if + * the format changes. */ +#define CRED_EXPORT_MAGIC "K5C1" + +#endif /* _GSSAPIP_KRB5_H_ */ diff --git a/src/lib/gssapi/krb5/gssapi_err_krb5.et b/src/lib/gssapi/krb5/gssapi_err_krb5.et new file mode 100644 index 000000000000..6396d5902009 --- /dev/null +++ b/src/lib/gssapi/krb5/gssapi_err_krb5.et @@ -0,0 +1,42 @@ +# +# Copyright 1993 by OpenVision Technologies, Inc. +# +# Permission to use, copy, modify, distribute, and sell this software +# and its documentation for any purpose is hereby granted without fee, +# provided that the above copyright notice appears in all copies and +# that both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of OpenVision not be used +# in advertising or publicity pertaining to distribution of the software +# without specific, written prior permission. OpenVision makes no +# representations about the suitability of this software for any +# purpose. It is provided "as is" without express or implied warranty. +# +# OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +# EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR +# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +# USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +# + +error_table k5g + +error_code KG_CCACHE_NOMATCH, "Principal in credential cache does not match desired name" +error_code KG_KEYTAB_NOMATCH, "No principal in keytab matches desired name" +error_code KG_TGT_MISSING, "Credential cache has no TGT" +error_code KG_NO_SUBKEY, "Authenticator has no subkey" +error_code KG_CONTEXT_ESTABLISHED, "Context is already fully established" +error_code KG_BAD_SIGN_TYPE, "Unknown signature type in token" +error_code KG_BAD_LENGTH, "Invalid field length in token" +error_code KG_CTX_INCOMPLETE, "Attempt to use incomplete security context" +error_code KG_CONTEXT, "Bad magic number for krb5_gss_ctx_id_t" +error_code KG_CRED, "Bad magic number for krb5_gss_cred_id_t" +error_code KG_ENC_DESC, "Bad magic number for krb5_gss_enc_desc" +error_code KG_BAD_SEQ, "Sequence number in token is corrupt" +error_code KG_EMPTY_CCACHE, "Credential cache is empty" +error_code KG_NO_CTYPES, "Acceptor and Initiator share no checksum types" +error_code KG_LUCID_VERSION, "Requested lucid context version not supported" +error_code KG_INPUT_TOO_LONG, "PRF input too long" +error_code KG_IAKERB_CONTEXT, "Bad magic number for iakerb_ctx_id_t" +end diff --git a/src/lib/gssapi/krb5/gssapi_krb5.c b/src/lib/gssapi/krb5/gssapi_krb5.c new file mode 100644 index 000000000000..99092ccab16b --- /dev/null +++ b/src/lib/gssapi/krb5/gssapi_krb5.c @@ -0,0 +1,1159 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * 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 FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +/* + * Copyright (c) 2006-2008, Novell, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The copyright holder's name is not used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + + +/* For declaration of krb5_ser_context_init */ +#include "k5-int.h" +#include "gssapiP_krb5.h" +#include "mglueP.h" + +#ifndef NO_PASSWORD +#include <pwd.h> +#endif + +/** exported constants defined in gssapi_krb5{,_nx}.h **/ + +/* these are bogus, but will compile */ + +/* + * The OID of the draft krb5 mechanism, assigned by IETF, is: + * iso(1) org(3) dod(5) internet(1) security(5) + * kerberosv5(2) = 1.3.5.1.5.2 + * The OID of the krb5_name type is: + * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) + * krb5(2) krb5_name(1) = 1.2.840.113554.1.2.2.1 + * The OID of the krb5_principal type is: + * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) + * krb5(2) krb5_principal(2) = 1.2.840.113554.1.2.2.2 + * The OID of the proposed standard krb5 mechanism is: + * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) + * krb5(2) = 1.2.840.113554.1.2.2 + * The OID of the proposed standard krb5 v2 mechanism is: + * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) + * krb5v2(3) = 1.2.840.113554.1.2.3 + * Provisionally reserved for Kerberos session key algorithm + * identifiers is: + * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) + * krb5(2) krb5_enctype(4) = 1.2.840.113554.1.2.2.4 + * Provisionally reserved for Kerberos mechanism-specific APIs: + * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) + * krb5(2) krb5_gssapi_ext(5) = 1.2.840.113554.1.2.2.5 + */ + +/* + * Encoding rules: The first two values are encoded in one byte as 40 + * * value1 + value2. Subsequent values are encoded base 128, most + * significant digit first, with the high bit (\200) set on all octets + * except the last in each value's encoding. + */ + +#define NO_CI_FLAGS_X_OID_LENGTH 6 +#define NO_CI_FLAGS_X_OID "\x2a\x85\x70\x2b\x0d\x1d" + +const gss_OID_desc krb5_gss_oid_array[] = { + /* this is the official, rfc-specified OID */ + {GSS_MECH_KRB5_OID_LENGTH, GSS_MECH_KRB5_OID}, + /* this pre-RFC mech OID */ + {GSS_MECH_KRB5_OLD_OID_LENGTH, GSS_MECH_KRB5_OLD_OID}, + /* this is the unofficial, incorrect mech OID emitted by MS */ + {GSS_MECH_KRB5_WRONG_OID_LENGTH, GSS_MECH_KRB5_WRONG_OID}, + /* IAKERB OID */ + {GSS_MECH_IAKERB_OID_LENGTH, GSS_MECH_IAKERB_OID}, + /* this is the v2 assigned OID */ + {9, "\052\206\110\206\367\022\001\002\003"}, + /* these two are name type OID's */ + /* 2.1.1. Kerberos Principal Name Form: (rfc 1964) + * This name form shall be represented by the Object Identifier {iso(1) + * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) + * krb5(2) krb5_name(1)}. The recommended symbolic name for this type + * is "GSS_KRB5_NT_PRINCIPAL_NAME". */ + {10, "\052\206\110\206\367\022\001\002\002\001"}, + /* gss_nt_krb5_principal. Object identifier for a krb5_principal. Do not use. */ + {10, "\052\206\110\206\367\022\001\002\002\002"}, + {NO_CI_FLAGS_X_OID_LENGTH, NO_CI_FLAGS_X_OID}, + { 0, 0 } +}; + +#define kg_oids ((gss_OID)krb5_gss_oid_array) + +const gss_OID gss_mech_krb5 = &kg_oids[0]; +const gss_OID gss_mech_krb5_old = &kg_oids[1]; +const gss_OID gss_mech_krb5_wrong = &kg_oids[2]; +const gss_OID gss_mech_iakerb = &kg_oids[3]; + + +const gss_OID gss_nt_krb5_name = &kg_oids[5]; +const gss_OID gss_nt_krb5_principal = &kg_oids[6]; +const gss_OID GSS_KRB5_NT_PRINCIPAL_NAME = &kg_oids[5]; + +const gss_OID GSS_KRB5_CRED_NO_CI_FLAGS_X = &kg_oids[7]; + +static const gss_OID_set_desc oidsets[] = { + {1, &kg_oids[0]}, /* RFC OID */ + {1, &kg_oids[1]}, /* pre-RFC OID */ + {3, &kg_oids[0]}, /* all names for krb5 mech */ + {4, &kg_oids[0]}, /* all krb5 names and IAKERB */ +}; + +#define kg_oidsets ((gss_OID_set)oidsets) + +const gss_OID_set gss_mech_set_krb5 = &kg_oidsets[0]; +const gss_OID_set gss_mech_set_krb5_old = &kg_oidsets[1]; +const gss_OID_set gss_mech_set_krb5_both = &kg_oidsets[2]; +const gss_OID_set kg_all_mechs = &kg_oidsets[3]; + +g_set kg_vdb = G_SET_INIT; + +/** default credential support */ + +/* + * init_sec_context() will explicitly re-acquire default credentials, + * so handling the expiration/invalidation condition here isn't needed. + */ +OM_uint32 +kg_get_defcred(minor_status, cred) + OM_uint32 *minor_status; + gss_cred_id_t *cred; +{ + OM_uint32 major; + + if ((major = krb5_gss_acquire_cred(minor_status, + (gss_name_t) NULL, GSS_C_INDEFINITE, + GSS_C_NULL_OID_SET, GSS_C_INITIATE, + cred, NULL, NULL)) && GSS_ERROR(major)) { + return(major); + } + *minor_status = 0; + return(GSS_S_COMPLETE); +} + +OM_uint32 +kg_sync_ccache_name (krb5_context context, OM_uint32 *minor_status) +{ + OM_uint32 err = 0; + + /* + * Sync up the context ccache name with the GSSAPI ccache name. + * If kg_ccache_name is NULL -- normal unless someone has called + * gss_krb5_ccache_name() -- then the system default ccache will + * be picked up and used by resetting the context default ccache. + * This is needed for platforms which support multiple ccaches. + */ + + if (!err) { + /* if NULL, resets the context default ccache */ + err = krb5_cc_set_default_name(context, + (char *) k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME)); + } + + *minor_status = err; + return (*minor_status == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; +} + +/* This function returns whether or not the caller set a cccache name. Used by + * gss_acquire_cred to figure out if the caller wants to only look at this + * ccache or search the cache collection for the desired name */ +OM_uint32 +kg_caller_provided_ccache_name (OM_uint32 *minor_status, + int *out_caller_provided_name) +{ + if (out_caller_provided_name) { + *out_caller_provided_name = + (k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME) != NULL); + } + + *minor_status = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +kg_get_ccache_name (OM_uint32 *minor_status, const char **out_name) +{ + const char *name = NULL; + OM_uint32 err = 0; + char *kg_ccache_name; + + kg_ccache_name = k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME); + + if (kg_ccache_name != NULL) { + name = strdup(kg_ccache_name); + if (name == NULL) + err = ENOMEM; + } else { + krb5_context context = NULL; + + /* Reset the context default ccache (see text above), and then + retrieve it. */ + err = krb5_gss_init_context(&context); + if (!err) + err = krb5_cc_set_default_name (context, NULL); + if (!err) { + name = krb5_cc_default_name(context); + if (name) { + name = strdup(name); + if (name == NULL) + err = ENOMEM; + } + } + if (err && context) + save_error_info(err, context); + if (context) + krb5_free_context(context); + } + + if (!err) { + if (out_name) { + *out_name = name; + } + } + + *minor_status = err; + return (*minor_status == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; +} + +OM_uint32 +kg_set_ccache_name (OM_uint32 *minor_status, const char *name) +{ + char *new_name = NULL; + char *swap = NULL; + char *kg_ccache_name; + krb5_error_code kerr; + + if (name) { + new_name = strdup(name); + if (new_name == NULL) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + } + + kg_ccache_name = k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME); + swap = kg_ccache_name; + kg_ccache_name = new_name; + new_name = swap; + kerr = k5_setspecific(K5_KEY_GSS_KRB5_CCACHE_NAME, kg_ccache_name); + if (kerr != 0) { + /* Can't store, so free up the storage. */ + free(kg_ccache_name); + /* ??? free(new_name); */ + *minor_status = kerr; + return GSS_S_FAILURE; + } + + free (new_name); + *minor_status = 0; + return GSS_S_COMPLETE; +} + +#define g_OID_prefix_equal(o1, o2) \ + (((o1)->length >= (o2)->length) && \ + (memcmp((o1)->elements, (o2)->elements, (o2)->length) == 0)) + +/* + * gss_inquire_sec_context_by_oid() methods + */ +static struct { + gss_OID_desc oid; + OM_uint32 (*func)(OM_uint32 *, const gss_ctx_id_t, const gss_OID, gss_buffer_set_t *); +} krb5_gss_inquire_sec_context_by_oid_ops[] = { + { + {GSS_KRB5_GET_TKT_FLAGS_OID_LENGTH, GSS_KRB5_GET_TKT_FLAGS_OID}, + gss_krb5int_get_tkt_flags + }, + { + {GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH, GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID}, + gss_krb5int_extract_authz_data_from_sec_context + }, + { + {GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH, GSS_KRB5_INQ_SSPI_SESSION_KEY_OID}, + gss_krb5int_inq_session_key + }, + { + {GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID_LENGTH, GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID}, + gss_krb5int_export_lucid_sec_context + }, + { + {GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID_LENGTH, GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID}, + gss_krb5int_extract_authtime_from_sec_context + } +}; + +OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_sec_context_by_oid (OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + krb5_gss_ctx_id_rec *ctx; + size_t i; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor_status = 0; + + if (desired_object == GSS_C_NO_OID) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (data_set == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *data_set = GSS_C_NO_BUFFER_SET; + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + + if (ctx->terminated || !ctx->established) + return GSS_S_NO_CONTEXT; + + for (i = 0; i < sizeof(krb5_gss_inquire_sec_context_by_oid_ops)/ + sizeof(krb5_gss_inquire_sec_context_by_oid_ops[0]); i++) { + if (g_OID_prefix_equal(desired_object, &krb5_gss_inquire_sec_context_by_oid_ops[i].oid)) { + return (*krb5_gss_inquire_sec_context_by_oid_ops[i].func)(minor_status, + context_handle, + desired_object, + data_set); + } + } + + *minor_status = EINVAL; + + return GSS_S_UNAVAILABLE; +} + +/* + * gss_inquire_cred_by_oid() methods + */ +#if 0 +static struct { + gss_OID_desc oid; + OM_uint32 (*func)(OM_uint32 *, const gss_cred_id_t, const gss_OID, gss_buffer_set_t *); +} krb5_gss_inquire_cred_by_oid_ops[] = { +}; +#endif + +static OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_cred_by_oid(OM_uint32 *minor_status, + const gss_cred_id_t cred_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + OM_uint32 major_status = GSS_S_FAILURE; +#if 0 + size_t i; +#endif + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor_status = 0; + + if (desired_object == GSS_C_NO_OID) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (data_set == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *data_set = GSS_C_NO_BUFFER_SET; + if (cred_handle == GSS_C_NO_CREDENTIAL) { + *minor_status = (OM_uint32)KRB5_NOCREDS_SUPPLIED; + return GSS_S_NO_CRED; + } + + major_status = krb5_gss_validate_cred(minor_status, cred_handle); + if (GSS_ERROR(major_status)) + return major_status; + +#if 0 + for (i = 0; i < sizeof(krb5_gss_inquire_cred_by_oid_ops)/ + sizeof(krb5_gss_inquire_cred_by_oid_ops[0]); i++) { + if (g_OID_prefix_equal(desired_object, &krb5_gss_inquire_cred_by_oid_ops[i].oid)) { + return (*krb5_gss_inquire_cred_by_oid_ops[i].func)(minor_status, + cred_handle, + desired_object, + data_set); + } + } +#endif + + *minor_status = EINVAL; + + return GSS_S_UNAVAILABLE; +} + +/* + * gss_set_sec_context_option() methods + * (Disabled until we have something to populate the array.) + */ +#if 0 +static struct { + gss_OID_desc oid; + OM_uint32 (*func)(OM_uint32 *, gss_ctx_id_t *, const gss_OID, const gss_buffer_t); +} krb5_gss_set_sec_context_option_ops[] = { +}; +#endif + +OM_uint32 KRB5_CALLCONV +krb5_gss_set_sec_context_option (OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + const gss_OID desired_object, + const gss_buffer_t value) +{ +#if 0 + size_t i; +#endif + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor_status = 0; + + if (context_handle == NULL) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (desired_object == GSS_C_NO_OID) + return GSS_S_CALL_INACCESSIBLE_READ; + +#if 0 + for (i = 0; i < sizeof(krb5_gss_set_sec_context_option_ops)/ + sizeof(krb5_gss_set_sec_context_option_ops[0]); i++) { + if (g_OID_prefix_equal(desired_object, &krb5_gss_set_sec_context_option_ops[i].oid)) { + return (*krb5_gss_set_sec_context_option_ops[i].func)(minor_status, + context_handle, + desired_object, + value); + } + } +#endif + + *minor_status = EINVAL; + + return GSS_S_UNAVAILABLE; +} + +static OM_uint32 +no_ci_flags(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_oid, + const gss_buffer_t value) +{ + krb5_gss_cred_id_t cred; + + cred = (krb5_gss_cred_id_t) *cred_handle; + cred->suppress_ci_flags = 1; + + *minor_status = 0; + return GSS_S_COMPLETE; +} +/* + * gssspi_set_cred_option() methods + */ +static struct { + gss_OID_desc oid; + OM_uint32 (*func)(OM_uint32 *, gss_cred_id_t *, const gss_OID, const gss_buffer_t); +} krb5_gssspi_set_cred_option_ops[] = { + { + {GSS_KRB5_COPY_CCACHE_OID_LENGTH, GSS_KRB5_COPY_CCACHE_OID}, + gss_krb5int_copy_ccache + }, + { + {GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID_LENGTH, GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID}, + gss_krb5int_set_allowable_enctypes + }, + { + {GSS_KRB5_SET_CRED_RCACHE_OID_LENGTH, GSS_KRB5_SET_CRED_RCACHE_OID}, + gss_krb5int_set_cred_rcache + }, + { + {GSS_KRB5_IMPORT_CRED_OID_LENGTH, GSS_KRB5_IMPORT_CRED_OID}, + gss_krb5int_import_cred + }, + { + {NO_CI_FLAGS_X_OID_LENGTH, NO_CI_FLAGS_X_OID}, + no_ci_flags + }, +}; + +static OM_uint32 KRB5_CALLCONV +krb5_gssspi_set_cred_option(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_object, + const gss_buffer_t value) +{ + OM_uint32 major_status = GSS_S_FAILURE; + size_t i; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (cred_handle == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor_status = 0; + + if (desired_object == GSS_C_NO_OID) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (*cred_handle != GSS_C_NO_CREDENTIAL) { + major_status = krb5_gss_validate_cred(minor_status, *cred_handle); + if (GSS_ERROR(major_status)) + return major_status; + } + + for (i = 0; i < sizeof(krb5_gssspi_set_cred_option_ops)/ + sizeof(krb5_gssspi_set_cred_option_ops[0]); i++) { + if (g_OID_prefix_equal(desired_object, &krb5_gssspi_set_cred_option_ops[i].oid)) { + return (*krb5_gssspi_set_cred_option_ops[i].func)(minor_status, + cred_handle, + desired_object, + value); + } + } + + *minor_status = EINVAL; + + return GSS_S_UNAVAILABLE; +} + +/* + * gssspi_mech_invoke() methods + */ +static struct { + gss_OID_desc oid; + OM_uint32 (*func)(OM_uint32 *, const gss_OID, const gss_OID, gss_buffer_t); +} krb5_gssspi_mech_invoke_ops[] = { + { + {GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_OID_LENGTH, GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_OID}, + gss_krb5int_register_acceptor_identity + }, + { + {GSS_KRB5_CCACHE_NAME_OID_LENGTH, GSS_KRB5_CCACHE_NAME_OID}, + gss_krb5int_ccache_name + }, + { + {GSS_KRB5_FREE_LUCID_SEC_CONTEXT_OID_LENGTH, GSS_KRB5_FREE_LUCID_SEC_CONTEXT_OID}, + gss_krb5int_free_lucid_sec_context + }, +#ifndef _WIN32 + { + {GSS_KRB5_USE_KDC_CONTEXT_OID_LENGTH, GSS_KRB5_USE_KDC_CONTEXT_OID}, + krb5int_gss_use_kdc_context + }, +#endif +}; + +static OM_uint32 KRB5_CALLCONV +krb5_gssspi_mech_invoke (OM_uint32 *minor_status, + const gss_OID desired_mech, + const gss_OID desired_object, + gss_buffer_t value) +{ + size_t i; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor_status = 0; + + if (desired_mech == GSS_C_NO_OID) + return GSS_S_BAD_MECH; + + if (desired_object == GSS_C_NO_OID) + return GSS_S_CALL_INACCESSIBLE_READ; + + for (i = 0; i < sizeof(krb5_gssspi_mech_invoke_ops)/ + sizeof(krb5_gssspi_mech_invoke_ops[0]); i++) { + if (g_OID_prefix_equal(desired_object, &krb5_gssspi_mech_invoke_ops[i].oid)) { + return (*krb5_gssspi_mech_invoke_ops[i].func)(minor_status, + desired_mech, + desired_object, + value); + } + } + + *minor_status = EINVAL; + + return GSS_S_UNAVAILABLE; +} + +#define GS2_KRB5_SASL_NAME "GS2-KRB5" +#define GS2_KRB5_SASL_NAME_LEN (sizeof(GS2_KRB5_SASL_NAME) - 1) + +#define GS2_IAKERB_SASL_NAME "GS2-IAKERB" +#define GS2_IAKERB_SASL_NAME_LEN (sizeof(GS2_IAKERB_SASL_NAME) - 1) + +static OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_mech_for_saslname(OM_uint32 *minor_status, + const gss_buffer_t sasl_mech_name, + gss_OID *mech_type) +{ + *minor_status = 0; + + if (sasl_mech_name->length == GS2_KRB5_SASL_NAME_LEN && + memcmp(sasl_mech_name->value, + GS2_KRB5_SASL_NAME, GS2_KRB5_SASL_NAME_LEN) == 0) { + if (mech_type != NULL) + *mech_type = (gss_OID)gss_mech_krb5; + return GSS_S_COMPLETE; + } else if (sasl_mech_name->length == GS2_IAKERB_SASL_NAME_LEN && + memcmp(sasl_mech_name->value, + GS2_IAKERB_SASL_NAME, GS2_IAKERB_SASL_NAME_LEN) == 0) { + if (mech_type != NULL) + *mech_type = (gss_OID)gss_mech_iakerb; + return GSS_S_COMPLETE; + } + + return GSS_S_BAD_MECH; +} + +static OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_saslname_for_mech(OM_uint32 *minor_status, + const gss_OID desired_mech, + gss_buffer_t sasl_mech_name, + gss_buffer_t mech_name, + gss_buffer_t mech_description) +{ + if (g_OID_equal(desired_mech, gss_mech_iakerb)) { + if (!g_make_string_buffer(GS2_IAKERB_SASL_NAME, sasl_mech_name) || + !g_make_string_buffer("iakerb", mech_name) || + !g_make_string_buffer("Initial and Pass Through Authentication " + "Kerberos Mechanism (IAKERB)", + mech_description)) + goto fail; + } else { + if (!g_make_string_buffer(GS2_KRB5_SASL_NAME, sasl_mech_name) || + !g_make_string_buffer("krb5", mech_name) || + !g_make_string_buffer("Kerberos 5 GSS-API Mechanism", + mech_description)) + goto fail; + } + + *minor_status = 0; + return GSS_S_COMPLETE; + +fail: + *minor_status = ENOMEM; + return GSS_S_FAILURE; +} + +static OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_attrs_for_mech(OM_uint32 *minor_status, + gss_const_OID mech, + gss_OID_set *mech_attrs, + gss_OID_set *known_mech_attrs) +{ + OM_uint32 major, tmpMinor; + + if (mech_attrs == NULL) { + *minor_status = 0; + return GSS_S_COMPLETE; + } + + major = gss_create_empty_oid_set(minor_status, mech_attrs); + if (GSS_ERROR(major)) + goto cleanup; + +#define MA_SUPPORTED(ma) do { \ + major = gss_add_oid_set_member(minor_status, (gss_OID)ma, \ + mech_attrs); \ + if (GSS_ERROR(major)) \ + goto cleanup; \ + } while (0) + + MA_SUPPORTED(GSS_C_MA_MECH_CONCRETE); + MA_SUPPORTED(GSS_C_MA_ITOK_FRAMED); + MA_SUPPORTED(GSS_C_MA_AUTH_INIT); + MA_SUPPORTED(GSS_C_MA_AUTH_TARG); + MA_SUPPORTED(GSS_C_MA_DELEG_CRED); + MA_SUPPORTED(GSS_C_MA_INTEG_PROT); + MA_SUPPORTED(GSS_C_MA_CONF_PROT); + MA_SUPPORTED(GSS_C_MA_MIC); + MA_SUPPORTED(GSS_C_MA_WRAP); + MA_SUPPORTED(GSS_C_MA_PROT_READY); + MA_SUPPORTED(GSS_C_MA_REPLAY_DET); + MA_SUPPORTED(GSS_C_MA_OOS_DET); + MA_SUPPORTED(GSS_C_MA_CBINDINGS); + MA_SUPPORTED(GSS_C_MA_CTX_TRANS); + + if (g_OID_equal(mech, gss_mech_iakerb)) { + MA_SUPPORTED(GSS_C_MA_AUTH_INIT_INIT); + MA_SUPPORTED(GSS_C_MA_NOT_DFLT_MECH); + } else if (!g_OID_equal(mech, gss_mech_krb5)) { + MA_SUPPORTED(GSS_C_MA_DEPRECATED); + } + +cleanup: + if (GSS_ERROR(major)) + gss_release_oid_set(&tmpMinor, mech_attrs); + + return major; +} + +static OM_uint32 KRB5_CALLCONV +krb5_gss_localname(OM_uint32 *minor, + const gss_name_t pname, + const gss_const_OID mech_type, + gss_buffer_t localname) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t kname; + char lname[BUFSIZ]; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor = code; + return GSS_S_FAILURE; + } + + kname = (krb5_gss_name_t)pname; + + code = krb5_aname_to_localname(context, kname->princ, + sizeof(lname), lname); + if (code != 0) { + *minor = KRB5_NO_LOCALNAME; + krb5_free_context(context); + return GSS_S_FAILURE; + } + + + krb5_free_context(context); + localname->value = gssalloc_strdup(lname); + localname->length = strlen(lname); + + return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; +} + + +static OM_uint32 KRB5_CALLCONV +krb5_gss_authorize_localname(OM_uint32 *minor, + const gss_name_t pname, + gss_const_buffer_t local_user, + gss_const_OID name_type) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t kname; + char *user; + int user_ok; + + if (name_type != GSS_C_NO_OID && + !g_OID_equal(name_type, GSS_C_NT_USER_NAME)) { + return GSS_S_BAD_NAMETYPE; + } + + kname = (krb5_gss_name_t)pname; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor = code; + return GSS_S_FAILURE; + } + + user = k5memdup0(local_user->value, local_user->length, &code); + if (user == NULL) { + *minor = code; + krb5_free_context(context); + return GSS_S_FAILURE; + } + + user_ok = krb5_kuserok(context, kname->princ, user); + + free(user); + krb5_free_context(context); + + *minor = 0; + return user_ok ? GSS_S_COMPLETE : GSS_S_UNAUTHORIZED; +} + +static struct gss_config krb5_mechanism = { + { GSS_MECH_KRB5_OID_LENGTH, GSS_MECH_KRB5_OID }, + NULL, + krb5_gss_acquire_cred, + krb5_gss_release_cred, + krb5_gss_init_sec_context, +#ifdef LEAN_CLIENT + NULL, +#else + krb5_gss_accept_sec_context, +#endif + krb5_gss_process_context_token, + krb5_gss_delete_sec_context, + krb5_gss_context_time, + krb5_gss_get_mic, + krb5_gss_verify_mic, +#if defined(IOV_SHIM_EXERCISE_WRAP) || defined(IOV_SHIM_EXERCISE) + NULL, +#else + krb5_gss_wrap, +#endif +#if defined(IOV_SHIM_EXERCISE_UNWRAP) || defined(IOV_SHIM_EXERCISE) + NULL, +#else + krb5_gss_unwrap, +#endif + krb5_gss_display_status, + krb5_gss_indicate_mechs, + krb5_gss_compare_name, + krb5_gss_display_name, + krb5_gss_import_name, + krb5_gss_release_name, + krb5_gss_inquire_cred, + NULL, /* add_cred */ +#ifdef LEAN_CLIENT + NULL, + NULL, +#else + krb5_gss_export_sec_context, + krb5_gss_import_sec_context, +#endif + krb5_gss_inquire_cred_by_mech, + krb5_gss_inquire_names_for_mech, + krb5_gss_inquire_context, + krb5_gss_internal_release_oid, + krb5_gss_wrap_size_limit, + krb5_gss_localname, + + krb5_gss_authorize_localname, + krb5_gss_export_name, + krb5_gss_duplicate_name, + krb5_gss_store_cred, + krb5_gss_inquire_sec_context_by_oid, + krb5_gss_inquire_cred_by_oid, + krb5_gss_set_sec_context_option, + krb5_gssspi_set_cred_option, + krb5_gssspi_mech_invoke, + NULL, /* wrap_aead */ + NULL, /* unwrap_aead */ + krb5_gss_wrap_iov, + krb5_gss_unwrap_iov, + krb5_gss_wrap_iov_length, + NULL, /* complete_auth_token */ + krb5_gss_acquire_cred_impersonate_name, + NULL, /* krb5_gss_add_cred_impersonate_name */ + NULL, /* display_name_ext */ + krb5_gss_inquire_name, + krb5_gss_get_name_attribute, + krb5_gss_set_name_attribute, + krb5_gss_delete_name_attribute, + krb5_gss_export_name_composite, + krb5_gss_map_name_to_any, + krb5_gss_release_any_name_mapping, + krb5_gss_pseudo_random, + NULL, /* set_neg_mechs */ + krb5_gss_inquire_saslname_for_mech, + krb5_gss_inquire_mech_for_saslname, + krb5_gss_inquire_attrs_for_mech, + krb5_gss_acquire_cred_from, + krb5_gss_store_cred_into, + krb5_gss_acquire_cred_with_password, + krb5_gss_export_cred, + krb5_gss_import_cred, + NULL, /* import_sec_context_by_mech */ + NULL, /* import_name_by_mech */ + NULL, /* import_cred_by_mech */ + krb5_gss_get_mic_iov, + krb5_gss_verify_mic_iov, + krb5_gss_get_mic_iov_length, +}; + +/* Functions which use security contexts or acquire creds are IAKERB-specific; + * other functions can borrow from the krb5 mech. */ +static struct gss_config iakerb_mechanism = { + { GSS_MECH_KRB5_OID_LENGTH, GSS_MECH_KRB5_OID }, + NULL, + iakerb_gss_acquire_cred, + krb5_gss_release_cred, + iakerb_gss_init_sec_context, +#ifdef LEAN_CLIENT + NULL, +#else + iakerb_gss_accept_sec_context, +#endif + iakerb_gss_process_context_token, + iakerb_gss_delete_sec_context, + iakerb_gss_context_time, + iakerb_gss_get_mic, + iakerb_gss_verify_mic, +#if defined(IOV_SHIM_EXERCISE_WRAP) || defined(IOV_SHIM_EXERCISE) + NULL, +#else + iakerb_gss_wrap, +#endif +#if defined(IOV_SHIM_EXERCISE_UNWRAP) || defined(IOV_SHIM_EXERCISE) + NULL, +#else + iakerb_gss_unwrap, +#endif + krb5_gss_display_status, + krb5_gss_indicate_mechs, + krb5_gss_compare_name, + krb5_gss_display_name, + krb5_gss_import_name, + krb5_gss_release_name, + krb5_gss_inquire_cred, + NULL, /* add_cred */ +#ifdef LEAN_CLIENT + NULL, + NULL, +#else + iakerb_gss_export_sec_context, + iakerb_gss_import_sec_context, +#endif + krb5_gss_inquire_cred_by_mech, + krb5_gss_inquire_names_for_mech, + iakerb_gss_inquire_context, + krb5_gss_internal_release_oid, + iakerb_gss_wrap_size_limit, + krb5_gss_localname, + krb5_gss_authorize_localname, + krb5_gss_export_name, + krb5_gss_duplicate_name, + krb5_gss_store_cred, + iakerb_gss_inquire_sec_context_by_oid, + krb5_gss_inquire_cred_by_oid, + iakerb_gss_set_sec_context_option, + krb5_gssspi_set_cred_option, + krb5_gssspi_mech_invoke, + NULL, /* wrap_aead */ + NULL, /* unwrap_aead */ + iakerb_gss_wrap_iov, + iakerb_gss_unwrap_iov, + iakerb_gss_wrap_iov_length, + NULL, /* complete_auth_token */ + NULL, /* acquire_cred_impersonate_name */ + NULL, /* add_cred_impersonate_name */ + NULL, /* display_name_ext */ + krb5_gss_inquire_name, + krb5_gss_get_name_attribute, + krb5_gss_set_name_attribute, + krb5_gss_delete_name_attribute, + krb5_gss_export_name_composite, + krb5_gss_map_name_to_any, + krb5_gss_release_any_name_mapping, + iakerb_gss_pseudo_random, + NULL, /* set_neg_mechs */ + krb5_gss_inquire_saslname_for_mech, + krb5_gss_inquire_mech_for_saslname, + krb5_gss_inquire_attrs_for_mech, + krb5_gss_acquire_cred_from, + krb5_gss_store_cred_into, + iakerb_gss_acquire_cred_with_password, + krb5_gss_export_cred, + krb5_gss_import_cred, + NULL, /* import_sec_context_by_mech */ + NULL, /* import_name_by_mech */ + NULL, /* import_cred_by_mech */ + iakerb_gss_get_mic_iov, + iakerb_gss_verify_mic_iov, + iakerb_gss_get_mic_iov_length, +}; + +#ifdef _GSS_STATIC_LINK +#include "mglueP.h" +static int gss_iakerbmechglue_init(void) +{ + struct gss_mech_config mech_iakerb; + + memset(&mech_iakerb, 0, sizeof(mech_iakerb)); + mech_iakerb.mech = &iakerb_mechanism; + + mech_iakerb.mechNameStr = "iakerb"; + mech_iakerb.mech_type = (gss_OID)gss_mech_iakerb; + gssint_register_mechinfo(&mech_iakerb); + + return 0; +} + +static int gss_krb5mechglue_init(void) +{ + struct gss_mech_config mech_krb5; + + memset(&mech_krb5, 0, sizeof(mech_krb5)); + mech_krb5.mech = &krb5_mechanism; + + mech_krb5.mechNameStr = "kerberos_v5"; + mech_krb5.mech_type = (gss_OID)gss_mech_krb5; + gssint_register_mechinfo(&mech_krb5); + + mech_krb5.mechNameStr = "kerberos_v5_old"; + mech_krb5.mech_type = (gss_OID)gss_mech_krb5_old; + gssint_register_mechinfo(&mech_krb5); + + mech_krb5.mechNameStr = "mskrb"; + mech_krb5.mech_type = (gss_OID)gss_mech_krb5_wrong; + gssint_register_mechinfo(&mech_krb5); + + return 0; +} +#else +MAKE_INIT_FUNCTION(gss_krb5int_lib_init); +MAKE_FINI_FUNCTION(gss_krb5int_lib_fini); + +gss_mechanism KRB5_CALLCONV +gss_mech_initialize(void) +{ + return &krb5_mechanism; +} +#endif /* _GSS_STATIC_LINK */ + +int gss_krb5int_lib_init(void) +{ + int err; + +#ifdef SHOW_INITFINI_FUNCS + printf("gss_krb5int_lib_init\n"); +#endif + + add_error_table(&et_k5g_error_table); + +#ifndef LEAN_CLIENT + err = k5_mutex_finish_init(&gssint_krb5_keytab_lock); + if (err) + return err; +#endif /* LEAN_CLIENT */ + err = k5_key_register(K5_KEY_GSS_KRB5_SET_CCACHE_OLD_NAME, free); + if (err) + return err; + err = k5_key_register(K5_KEY_GSS_KRB5_CCACHE_NAME, free); + if (err) + return err; + err = k5_key_register(K5_KEY_GSS_KRB5_ERROR_MESSAGE, + krb5_gss_delete_error_info); + if (err) + return err; +#ifndef _WIN32 + err = k5_mutex_finish_init(&kg_kdc_flag_mutex); + if (err) + return err; + err = k5_mutex_finish_init(&kg_vdb.mutex); + if (err) + return err; +#endif +#ifdef _GSS_STATIC_LINK + err = gss_krb5mechglue_init(); + if (err) + return err; + err = gss_iakerbmechglue_init(); + if (err) + return err; +#endif + + return 0; +} + +void gss_krb5int_lib_fini(void) +{ +#ifndef _GSS_STATIC_LINK + if (!INITIALIZER_RAN(gss_krb5int_lib_init) || PROGRAM_EXITING()) { +# ifdef SHOW_INITFINI_FUNCS + printf("gss_krb5int_lib_fini: skipping\n"); +# endif + return; + } +#endif +#ifdef SHOW_INITFINI_FUNCS + printf("gss_krb5int_lib_fini\n"); +#endif + remove_error_table(&et_k5g_error_table); + + k5_key_delete(K5_KEY_GSS_KRB5_SET_CCACHE_OLD_NAME); + k5_key_delete(K5_KEY_GSS_KRB5_CCACHE_NAME); + k5_key_delete(K5_KEY_GSS_KRB5_ERROR_MESSAGE); + k5_mutex_destroy(&kg_vdb.mutex); +#ifndef _WIN32 + k5_mutex_destroy(&kg_kdc_flag_mutex); +#endif +#ifndef LEAN_CLIENT + k5_mutex_destroy(&gssint_krb5_keytab_lock); +#endif /* LEAN_CLIENT */ +} + +#ifdef _GSS_STATIC_LINK +extern OM_uint32 gssint_lib_init(void); +#endif + +OM_uint32 gss_krb5int_initialize_library (void) +{ +#ifdef _GSS_STATIC_LINK + return gssint_mechglue_initialize_library(); +#else + return CALL_INIT_FUNCTION(gss_krb5int_lib_init); +#endif +} diff --git a/src/lib/gssapi/krb5/gssapi_krb5.h b/src/lib/gssapi/krb5/gssapi_krb5.h new file mode 100644 index 000000000000..390b00032cce --- /dev/null +++ b/src/lib/gssapi/krb5/gssapi_krb5.h @@ -0,0 +1,281 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _GSSAPI_KRB5_H_ +#define _GSSAPI_KRB5_H_ + +#include <gssapi/gssapi.h> +#include <gssapi/gssapi_ext.h> +#include <krb5.h> +#include <stdint.h> + +/* C++ friendlyness */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Reserved static storage for GSS_oids. See rfc 1964 for more details. */ + +/* 2.1.1. Kerberos Principal Name Form: */ +GSS_DLLIMP extern const gss_OID GSS_KRB5_NT_PRINCIPAL_NAME; +/* This name form shall be represented by the Object Identifier {iso(1) + * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) + * krb5(2) krb5_name(1)}. The recommended symbolic name for this type + * is "GSS_KRB5_NT_PRINCIPAL_NAME". */ + +/* 2.1.2. Host-Based Service Name Form */ +#define GSS_KRB5_NT_HOSTBASED_SERVICE_NAME GSS_C_NT_HOSTBASED_SERVICE +/* This name form shall be represented by the Object Identifier {iso(1) + * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) + * generic(1) service_name(4)}. The previously recommended symbolic + * name for this type is "GSS_KRB5_NT_HOSTBASED_SERVICE_NAME". The + * currently preferred symbolic name for this type is + * "GSS_C_NT_HOSTBASED_SERVICE". */ + +/* 2.2.1. User Name Form */ +#define GSS_KRB5_NT_USER_NAME GSS_C_NT_USER_NAME +/* This name form shall be represented by the Object Identifier {iso(1) + * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) + * generic(1) user_name(1)}. The recommended symbolic name for this + * type is "GSS_KRB5_NT_USER_NAME". */ + +/* 2.2.2. Machine UID Form */ +#define GSS_KRB5_NT_MACHINE_UID_NAME GSS_C_NT_MACHINE_UID_NAME +/* This name form shall be represented by the Object Identifier {iso(1) + * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) + * generic(1) machine_uid_name(2)}. The recommended symbolic name for + * this type is "GSS_KRB5_NT_MACHINE_UID_NAME". */ + +/* 2.2.3. String UID Form */ +#define GSS_KRB5_NT_STRING_UID_NAME GSS_C_NT_STRING_UID_NAME +/* This name form shall be represented by the Object Identifier {iso(1) + * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) + * generic(1) string_uid_name(3)}. The recommended symbolic name for + * this type is "GSS_KRB5_NT_STRING_UID_NAME". */ + +GSS_DLLIMP extern const gss_OID gss_mech_krb5; +GSS_DLLIMP extern const gss_OID gss_mech_krb5_old; +GSS_DLLIMP extern const gss_OID gss_mech_krb5_wrong; +GSS_DLLIMP extern const gss_OID gss_mech_iakerb; +GSS_DLLIMP extern const gss_OID_set gss_mech_set_krb5; +GSS_DLLIMP extern const gss_OID_set gss_mech_set_krb5_old; +GSS_DLLIMP extern const gss_OID_set gss_mech_set_krb5_both; + +GSS_DLLIMP extern const gss_OID gss_nt_krb5_name; +GSS_DLLIMP extern const gss_OID gss_nt_krb5_principal; + +GSS_DLLIMP extern const gss_OID_desc krb5_gss_oid_array[]; + +/* + * This OID can be used with gss_set_cred_option() to suppress the + * confidentiality and integrity flags from being asserted in initial context + * tokens. + * + * iso(1) member-body(2) Sweden(752) Stockholm University(43) Heimdal GSS-API + * Extensions(13) no_ci_flags(29) + */ +GSS_DLLIMP extern const gss_OID GSS_KRB5_CRED_NO_CI_FLAGS_X; + +#define gss_krb5_nt_general_name gss_nt_krb5_name +#define gss_krb5_nt_principal gss_nt_krb5_principal +#define gss_krb5_nt_service_name gss_nt_service_name +#define gss_krb5_nt_user_name gss_nt_user_name +#define gss_krb5_nt_machine_uid_name gss_nt_machine_uid_name +#define gss_krb5_nt_string_uid_name gss_nt_string_uid_name + +typedef struct gss_krb5_lucid_key { + OM_uint32 type; /* key encryption type */ + OM_uint32 length; /* length of key data */ + void * data; /* actual key data */ +} gss_krb5_lucid_key_t; + +typedef struct gss_krb5_rfc1964_keydata { + OM_uint32 sign_alg; /* signing algorthm */ + OM_uint32 seal_alg; /* seal/encrypt algorithm */ + gss_krb5_lucid_key_t ctx_key; + /* Context key + (Kerberos session key or subkey) */ +} gss_krb5_rfc1964_keydata_t; + +typedef struct gss_krb5_cfx_keydata { + OM_uint32 have_acceptor_subkey; + /* 1 if there is an acceptor_subkey + present, 0 otherwise */ + gss_krb5_lucid_key_t ctx_key; + /* Context key + (Kerberos session key or subkey) */ + gss_krb5_lucid_key_t acceptor_subkey; + /* acceptor-asserted subkey or + 0's if no acceptor subkey */ +} gss_krb5_cfx_keydata_t; + +typedef struct gss_krb5_lucid_context_v1 { + OM_uint32 version; /* Structure version number (1) + MUST be at beginning of struct! */ + OM_uint32 initiate; /* Are we the initiator? */ + OM_uint32 endtime; /* expiration time of context */ + uint64_t send_seq; /* sender sequence number */ + uint64_t recv_seq; /* receive sequence number */ + OM_uint32 protocol; /* 0: rfc1964, + 1: draft-ietf-krb-wg-gssapi-cfx-07 */ + /* + * if (protocol == 0) rfc1964_kd should be used + * and cfx_kd contents are invalid and should be zero + * if (protocol == 1) cfx_kd should be used + * and rfc1964_kd contents are invalid and should be zero + */ + gss_krb5_rfc1964_keydata_t rfc1964_kd; + gss_krb5_cfx_keydata_t cfx_kd; +} gss_krb5_lucid_context_v1_t; + +/* + * Mask for determining the version of a lucid context structure. Callers + * should not require this. + */ +typedef struct gss_krb5_lucid_context_version { + OM_uint32 version; /* Structure version number */ +} gss_krb5_lucid_context_version_t; + + + + +/* Alias for Heimdal compat. */ +#define gsskrb5_register_acceptor_identity krb5_gss_register_acceptor_identity + +OM_uint32 KRB5_CALLCONV krb5_gss_register_acceptor_identity(const char *); + +OM_uint32 KRB5_CALLCONV gss_krb5_get_tkt_flags( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + krb5_flags *ticket_flags); + +OM_uint32 KRB5_CALLCONV gss_krb5_copy_ccache( + OM_uint32 *minor_status, + gss_cred_id_t cred_handle, + krb5_ccache out_ccache); + +OM_uint32 KRB5_CALLCONV gss_krb5_ccache_name( + OM_uint32 *minor_status, const char *name, + const char **out_name); + +/* + * gss_krb5_set_allowable_enctypes + * + * This function may be called by a context initiator after calling + * gss_acquire_cred(), but before calling gss_init_sec_context(), + * to restrict the set of enctypes which will be negotiated during + * context establishment to those in the provided array. + * + * 'cred' must be a valid credential handle obtained via + * gss_acquire_cred(). It may not be GSS_C_NO_CREDENTIAL. + * gss_acquire_cred() may have been called to get a handle to + * the default credential. + * + * The purpose of this function is to limit the keys that may + * be exported via gss_krb5_export_lucid_sec_context(); thus it + * should limit the enctypes of all keys that will be needed + * after the security context has been established. + * (i.e. context establishment may use a session key with a + * stronger enctype than in the provided array, however a + * subkey must be established within the enctype limits + * established by this function.) + * + */ +OM_uint32 KRB5_CALLCONV +gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status, + gss_cred_id_t cred, + OM_uint32 num_ktypes, + krb5_enctype *ktypes); + +/* + * Returns a non-opaque (lucid) version of the internal context + * information. + * + * Note that context_handle must not be used again by the caller + * after this call. The GSS implementation is free to release any + * resources associated with the original context. It is up to the + * GSS implementation whether it returns pointers to existing data, + * or copies of the data. The caller should treat the returned + * lucid context as read-only. + * + * The caller must call gss_krb5_free_lucid_context() to free + * the context and allocated resources when it is finished with it. + * + * 'version' is an integer indicating the requested version of the lucid + * context. If the implementation does not understand the requested version, + * it will return an error. + * + * For example: + * void *return_ctx; + * gss_krb5_lucid_context_v1_t *ctx; + * OM_uint32 min_stat, maj_stat; + * OM_uint32 vers; + * gss_ctx_id_t *ctx_handle; + * + * maj_stat = gss_krb5_export_lucid_sec_context(&min_stat, + * ctx_handle, 1, &return_ctx); + * // Verify success + * ctx = (gss_krb5_lucid_context_v1_t *) return_ctx; + */ + +OM_uint32 KRB5_CALLCONV +gss_krb5_export_lucid_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + OM_uint32 version, + void **kctx); + +/* + * Frees the allocated storage associated with an + * exported struct gss_krb5_lucid_context. + */ +OM_uint32 KRB5_CALLCONV +gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, + void *kctx); + + +OM_uint32 KRB5_CALLCONV +gsskrb5_extract_authz_data_from_sec_context(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + int ad_type, + gss_buffer_t ad_data); + +OM_uint32 KRB5_CALLCONV +gss_krb5_set_cred_rcache(OM_uint32 *minor_status, + gss_cred_id_t cred, + krb5_rcache rcache); + +OM_uint32 KRB5_CALLCONV +gsskrb5_extract_authtime_from_sec_context(OM_uint32 *, gss_ctx_id_t, krb5_timestamp *); + +OM_uint32 KRB5_CALLCONV +gss_krb5_import_cred(OM_uint32 *minor_status, + krb5_ccache id, + krb5_principal keytab_principal, + krb5_keytab keytab, + gss_cred_id_t *cred); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _GSSAPI_KRB5_H_ */ diff --git a/src/lib/gssapi/krb5/iakerb.c b/src/lib/gssapi/krb5/iakerb.c new file mode 100644 index 000000000000..2dc4d0c1a4d5 --- /dev/null +++ b/src/lib/gssapi/krb5/iakerb.c @@ -0,0 +1,1329 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ +#include "k5-int.h" +#include "gssapiP_krb5.h" + +/* + * IAKERB implementation + */ + +extern int gssint_get_der_length(unsigned char **, OM_uint32, unsigned int*); + +enum iakerb_state { + IAKERB_AS_REQ, /* acquiring ticket with initial creds */ + IAKERB_TGS_REQ, /* acquiring ticket with TGT */ + IAKERB_AP_REQ /* hand-off to normal GSS AP-REQ exchange */ +}; + +struct _iakerb_ctx_id_rec { + krb5_magic magic; /* KG_IAKERB_CONTEXT */ + krb5_context k5c; + gss_cred_id_t defcred; /* Initiator only */ + enum iakerb_state state; /* Initiator only */ + krb5_init_creds_context icc; /* Initiator only */ + krb5_tkt_creds_context tcc; /* Initiator only */ + gss_ctx_id_t gssc; + krb5_data conv; /* conversation for checksumming */ + unsigned int count; /* number of round trips */ + int initiate; + int established; + krb5_get_init_creds_opt *gic_opts; +}; + +#define IAKERB_MAX_HOPS ( 16 /* MAX_IN_TKT_LOOPS */ + KRB5_REFERRAL_MAXHOPS ) + +typedef struct _iakerb_ctx_id_rec iakerb_ctx_id_rec; +typedef iakerb_ctx_id_rec *iakerb_ctx_id_t; + +/* + * Release an IAKERB context + */ +static void +iakerb_release_context(iakerb_ctx_id_t ctx) +{ + OM_uint32 tmp; + + if (ctx == NULL) + return; + + krb5_gss_release_cred(&tmp, &ctx->defcred); + krb5_init_creds_free(ctx->k5c, ctx->icc); + krb5_tkt_creds_free(ctx->k5c, ctx->tcc); + krb5_gss_delete_sec_context(&tmp, &ctx->gssc, NULL); + krb5_free_data_contents(ctx->k5c, &ctx->conv); + krb5_get_init_creds_opt_free(ctx->k5c, ctx->gic_opts); + krb5_free_context(ctx->k5c); + free(ctx); +} + +/* + * Create a IAKERB-FINISHED structure containing a checksum of + * the entire IAKERB exchange. + */ +krb5_error_code +iakerb_make_finished(krb5_context context, + krb5_key key, + const krb5_data *conv, + krb5_data **finished) +{ + krb5_error_code code; + krb5_iakerb_finished iaf; + + *finished = NULL; + + memset(&iaf, 0, sizeof(iaf)); + + if (key == NULL) + return KRB5KDC_ERR_NULL_KEY; + + code = krb5_k_make_checksum(context, 0, key, KRB5_KEYUSAGE_IAKERB_FINISHED, + conv, &iaf.checksum); + if (code != 0) + return code; + + code = encode_krb5_iakerb_finished(&iaf, finished); + + krb5_free_checksum_contents(context, &iaf.checksum); + + return code; +} + +/* + * Verify a IAKERB-FINISHED structure submitted by the initiator + */ +krb5_error_code +iakerb_verify_finished(krb5_context context, + krb5_key key, + const krb5_data *conv, + const krb5_data *finished) +{ + krb5_error_code code; + krb5_iakerb_finished *iaf; + krb5_boolean valid = FALSE; + + if (key == NULL) + return KRB5KDC_ERR_NULL_KEY; + + code = decode_krb5_iakerb_finished(finished, &iaf); + if (code != 0) + return code; + + code = krb5_k_verify_checksum(context, key, KRB5_KEYUSAGE_IAKERB_FINISHED, + conv, &iaf->checksum, &valid); + if (code == 0 && valid == FALSE) + code = KRB5KRB_AP_ERR_BAD_INTEGRITY; + + krb5_free_iakerb_finished(context, iaf); + + return code; +} + +/* + * Save a token for future checksumming. + */ +static krb5_error_code +iakerb_save_token(iakerb_ctx_id_t ctx, const gss_buffer_t token) +{ + char *p; + + p = realloc(ctx->conv.data, ctx->conv.length + token->length); + if (p == NULL) + return ENOMEM; + + memcpy(p + ctx->conv.length, token->value, token->length); + ctx->conv.data = p; + ctx->conv.length += token->length; + + return 0; +} + +/* + * Parse a token into IAKERB-HEADER and KRB-KDC-REQ/REP + */ +static krb5_error_code +iakerb_parse_token(iakerb_ctx_id_t ctx, + int initialContextToken, + const gss_buffer_t token, + krb5_data *realm, + krb5_data **cookie, + krb5_data *request) +{ + krb5_error_code code; + krb5_iakerb_header *iah = NULL; + unsigned int bodysize, lenlen; + int length; + unsigned char *ptr; + int flags = 0; + krb5_data data; + + if (token == GSS_C_NO_BUFFER || token->length == 0) { + code = KRB5_BAD_MSIZE; + goto cleanup; + } + + if (initialContextToken) + flags |= G_VFY_TOKEN_HDR_WRAPPER_REQUIRED; + + ptr = token->value; + + code = g_verify_token_header(gss_mech_iakerb, + &bodysize, &ptr, + IAKERB_TOK_PROXY, + token->length, flags); + if (code != 0) + goto cleanup; + + data.data = (char *)ptr; + + if (bodysize-- == 0 || *ptr++ != 0x30 /* SEQUENCE */) { + code = ASN1_BAD_ID; + goto cleanup; + } + + length = gssint_get_der_length(&ptr, bodysize, &lenlen); + if (length < 0 || bodysize - lenlen < (unsigned int)length) { + code = KRB5_BAD_MSIZE; + goto cleanup; + } + data.length = 1 /* SEQUENCE */ + lenlen + length; + + ptr += length; + bodysize -= (lenlen + length); + + code = decode_krb5_iakerb_header(&data, &iah); + if (code != 0) + goto cleanup; + + if (realm != NULL) { + *realm = iah->target_realm; + iah->target_realm.data = NULL; + } + + if (cookie != NULL) { + *cookie = iah->cookie; + iah->cookie = NULL; + } + + request->data = (char *)ptr; + request->length = bodysize; + + assert(request->data + request->length == + (char *)token->value + token->length); + +cleanup: + krb5_free_iakerb_header(ctx->k5c, iah); + + return code; +} + +/* + * Create a token from IAKERB-HEADER and KRB-KDC-REQ/REP + */ +static krb5_error_code +iakerb_make_token(iakerb_ctx_id_t ctx, + krb5_data *realm, + krb5_data *cookie, + krb5_data *request, + int initialContextToken, + gss_buffer_t token) +{ + krb5_error_code code; + krb5_iakerb_header iah; + krb5_data *data = NULL; + char *p; + unsigned int tokenSize; + unsigned char *q; + + token->value = NULL; + token->length = 0; + + /* + * Assemble the IAKERB-HEADER from the realm and cookie + */ + memset(&iah, 0, sizeof(iah)); + iah.target_realm = *realm; + iah.cookie = cookie; + + code = encode_krb5_iakerb_header(&iah, &data); + if (code != 0) + goto cleanup; + + /* + * Concatenate Kerberos request. + */ + p = realloc(data->data, data->length + request->length); + if (p == NULL) { + code = ENOMEM; + goto cleanup; + } + data->data = p; + + if (request->length > 0) + memcpy(data->data + data->length, request->data, request->length); + data->length += request->length; + + if (initialContextToken) + tokenSize = g_token_size(gss_mech_iakerb, data->length); + else + tokenSize = 2 + data->length; + + token->value = q = gssalloc_malloc(tokenSize); + if (q == NULL) { + code = ENOMEM; + goto cleanup; + } + token->length = tokenSize; + + if (initialContextToken) { + g_make_token_header(gss_mech_iakerb, data->length, &q, + IAKERB_TOK_PROXY); + } else { + store_16_be(IAKERB_TOK_PROXY, q); + q += 2; + } + memcpy(q, data->data, data->length); + q += data->length; + + assert(q == (unsigned char *)token->value + token->length); + +cleanup: + krb5_free_data(ctx->k5c, data); + + return code; +} + +/* + * Parse the IAKERB token in input_token and send the contained KDC + * request to the KDC for the realm. + * + * Wrap the KDC reply in output_token. + */ +static krb5_error_code +iakerb_acceptor_step(iakerb_ctx_id_t ctx, + int initialContextToken, + const gss_buffer_t input_token, + gss_buffer_t output_token) +{ + krb5_error_code code; + krb5_data request = empty_data(), reply = empty_data(); + krb5_data realm = empty_data(); + OM_uint32 tmp; + int tcp_only, use_master; + krb5_ui_4 kdc_code; + + output_token->length = 0; + output_token->value = NULL; + + if (ctx->count >= IAKERB_MAX_HOPS) { + code = KRB5_KDC_UNREACH; + goto cleanup; + } + + code = iakerb_parse_token(ctx, initialContextToken, input_token, &realm, + NULL, &request); + if (code != 0) + goto cleanup; + + if (realm.length == 0 || request.length == 0) { + code = KRB5_BAD_MSIZE; + goto cleanup; + } + + code = iakerb_save_token(ctx, input_token); + if (code != 0) + goto cleanup; + + for (tcp_only = 0; tcp_only <= 1; tcp_only++) { + use_master = 0; + code = krb5_sendto_kdc(ctx->k5c, &request, &realm, + &reply, &use_master, tcp_only); + if (code == 0 && krb5_is_krb_error(&reply)) { + krb5_error *error; + + code = decode_krb5_error(&reply, &error); + if (code != 0) + goto cleanup; + kdc_code = error->error; + krb5_free_error(ctx->k5c, error); + if (kdc_code == KRB_ERR_RESPONSE_TOO_BIG) { + krb5_free_data_contents(ctx->k5c, &reply); + reply = empty_data(); + continue; + } + } + break; + } + + if (code == KRB5_KDC_UNREACH || code == KRB5_REALM_UNKNOWN) { + krb5_error error; + + memset(&error, 0, sizeof(error)); + if (code == KRB5_KDC_UNREACH) + error.error = KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE; + else if (code == KRB5_REALM_UNKNOWN) + error.error = KRB_AP_ERR_IAKERB_KDC_NOT_FOUND; + + code = krb5_mk_error(ctx->k5c, &error, &reply); + if (code != 0) + goto cleanup; + } else if (code != 0) + goto cleanup; + + code = iakerb_make_token(ctx, &realm, NULL, &reply, 0, output_token); + if (code != 0) + goto cleanup; + + code = iakerb_save_token(ctx, output_token); + if (code != 0) + goto cleanup; + + ctx->count++; + +cleanup: + if (code != 0) + gss_release_buffer(&tmp, output_token); + /* request is a pointer into input_token, no need to free */ + krb5_free_data_contents(ctx->k5c, &realm); + krb5_free_data_contents(ctx->k5c, &reply); + + return code; +} + +/* + * Initialise the krb5_init_creds context for the IAKERB context + */ +static krb5_error_code +iakerb_init_creds_ctx(iakerb_ctx_id_t ctx, + krb5_gss_cred_id_t cred, + OM_uint32 time_req) +{ + krb5_error_code code; + + if (cred->iakerb_mech == 0) { + code = EINVAL; + goto cleanup; + } + + assert(cred->name != NULL); + assert(cred->name->princ != NULL); + + code = krb5_get_init_creds_opt_alloc(ctx->k5c, &ctx->gic_opts); + if (code != 0) + goto cleanup; + + if (time_req != 0 && time_req != GSS_C_INDEFINITE) + krb5_get_init_creds_opt_set_tkt_life(ctx->gic_opts, time_req); + + code = krb5_get_init_creds_opt_set_out_ccache(ctx->k5c, ctx->gic_opts, + cred->ccache); + if (code != 0) + goto cleanup; + + code = krb5_init_creds_init(ctx->k5c, + cred->name->princ, + NULL, /* prompter */ + NULL, /* data */ + 0, /* start_time */ + ctx->gic_opts, + &ctx->icc); + if (code != 0) + goto cleanup; + + if (cred->password != NULL) { + code = krb5_init_creds_set_password(ctx->k5c, ctx->icc, + cred->password); + } else if (cred->client_keytab != NULL) { + code = krb5_init_creds_set_keytab(ctx->k5c, ctx->icc, + cred->client_keytab); + } else { + code = KRB5_KT_NOTFOUND; + } + if (code != 0) + goto cleanup; + +cleanup: + return code; +} + +/* + * Initialise the krb5_tkt_creds context for the IAKERB context + */ +static krb5_error_code +iakerb_tkt_creds_ctx(iakerb_ctx_id_t ctx, + krb5_gss_cred_id_t cred, + krb5_gss_name_t name, + OM_uint32 time_req) + +{ + krb5_error_code code; + krb5_creds creds; + krb5_timestamp now; + + assert(cred->name != NULL); + assert(cred->name->princ != NULL); + + memset(&creds, 0, sizeof(creds)); + + creds.client = cred->name->princ; + creds.server = name->princ; + + if (time_req != 0 && time_req != GSS_C_INDEFINITE) { + code = krb5_timeofday(ctx->k5c, &now); + if (code != 0) + goto cleanup; + + creds.times.endtime = now + time_req; + } + + if (cred->name->ad_context != NULL) { + code = krb5_authdata_export_authdata(ctx->k5c, + cred->name->ad_context, + AD_USAGE_TGS_REQ, + &creds.authdata); + if (code != 0) + goto cleanup; + } + + code = krb5_tkt_creds_init(ctx->k5c, cred->ccache, &creds, 0, &ctx->tcc); + if (code != 0) + goto cleanup; + +cleanup: + krb5_free_authdata(ctx->k5c, creds.authdata); + + return code; +} + +/* + * Parse the IAKERB token in input_token and process the KDC + * response. + * + * Emit the next KDC request, if any, in output_token. + */ +static krb5_error_code +iakerb_initiator_step(iakerb_ctx_id_t ctx, + krb5_gss_cred_id_t cred, + krb5_gss_name_t name, + OM_uint32 time_req, + const gss_buffer_t input_token, + gss_buffer_t output_token) +{ + krb5_error_code code = 0; + krb5_data in = empty_data(), out = empty_data(), realm = empty_data(); + krb5_data *cookie = NULL; + OM_uint32 tmp; + unsigned int flags = 0; + krb5_ticket_times times; + + output_token->length = 0; + output_token->value = NULL; + + if (input_token != GSS_C_NO_BUFFER) { + code = iakerb_parse_token(ctx, 0, input_token, NULL, &cookie, &in); + if (code != 0) + goto cleanup; + + code = iakerb_save_token(ctx, input_token); + if (code != 0) + goto cleanup; + } + + switch (ctx->state) { + case IAKERB_AS_REQ: + if (ctx->icc == NULL) { + code = iakerb_init_creds_ctx(ctx, cred, time_req); + if (code != 0) + goto cleanup; + } + + code = krb5_init_creds_step(ctx->k5c, ctx->icc, &in, &out, &realm, + &flags); + if (code != 0) { + if (cred->have_tgt) { + /* We were trying to refresh; keep going with current creds. */ + ctx->state = IAKERB_TGS_REQ; + krb5_clear_error_message(ctx->k5c); + } else { + goto cleanup; + } + } else if (!(flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE)) { + krb5_init_creds_get_times(ctx->k5c, ctx->icc, ×); + kg_cred_set_initial_refresh(ctx->k5c, cred, ×); + cred->expire = times.endtime; + + krb5_init_creds_free(ctx->k5c, ctx->icc); + ctx->icc = NULL; + + ctx->state = IAKERB_TGS_REQ; + } else + break; + in = empty_data(); + /* Done with AS request; fall through to TGS request. */ + case IAKERB_TGS_REQ: + if (ctx->tcc == NULL) { + code = iakerb_tkt_creds_ctx(ctx, cred, name, time_req); + if (code != 0) + goto cleanup; + } + + code = krb5_tkt_creds_step(ctx->k5c, ctx->tcc, &in, &out, &realm, + &flags); + if (code != 0) + goto cleanup; + if (!(flags & KRB5_TKT_CREDS_STEP_FLAG_CONTINUE)) { + krb5_tkt_creds_get_times(ctx->k5c, ctx->tcc, ×); + cred->expire = times.endtime; + + krb5_tkt_creds_free(ctx->k5c, ctx->tcc); + ctx->tcc = NULL; + + ctx->state = IAKERB_AP_REQ; + } else + break; + /* Done with TGS request; fall through to AP request. */ + case IAKERB_AP_REQ: + break; + } + + if (out.length != 0) { + assert(ctx->state != IAKERB_AP_REQ); + + code = iakerb_make_token(ctx, &realm, cookie, &out, + (input_token == GSS_C_NO_BUFFER), + output_token); + if (code != 0) + goto cleanup; + + /* Save the token for generating a future checksum */ + code = iakerb_save_token(ctx, output_token); + if (code != 0) + goto cleanup; + + ctx->count++; + } + +cleanup: + if (code != 0) + gss_release_buffer(&tmp, output_token); + krb5_free_data(ctx->k5c, cookie); + krb5_free_data_contents(ctx->k5c, &out); + krb5_free_data_contents(ctx->k5c, &realm); + + return code; +} + +/* + * Determine the starting IAKERB state for a context. If we already + * have a ticket, we may not need to do IAKERB at all. + */ +static krb5_error_code +iakerb_get_initial_state(iakerb_ctx_id_t ctx, + krb5_gss_cred_id_t cred, + krb5_gss_name_t target, + OM_uint32 time_req, + enum iakerb_state *state) +{ + krb5_creds in_creds, *out_creds = NULL; + krb5_error_code code; + + memset(&in_creds, 0, sizeof(in_creds)); + + in_creds.client = cred->name->princ; + in_creds.server = target->princ; + + if (cred->name->ad_context != NULL) { + code = krb5_authdata_export_authdata(ctx->k5c, + cred->name->ad_context, + AD_USAGE_TGS_REQ, + &in_creds.authdata); + if (code != 0) + goto cleanup; + } + + if (time_req != 0 && time_req != GSS_C_INDEFINITE) { + krb5_timestamp now; + + code = krb5_timeofday(ctx->k5c, &now); + if (code != 0) + goto cleanup; + + in_creds.times.endtime = now + time_req; + } + + /* Make an AS request if we have no creds or it's time to refresh them. */ + if (cred->expire == 0 || kg_cred_time_to_refresh(ctx->k5c, cred)) { + *state = IAKERB_AS_REQ; + code = 0; + goto cleanup; + } + + code = krb5_get_credentials(ctx->k5c, KRB5_GC_CACHED, cred->ccache, + &in_creds, &out_creds); + if (code == KRB5_CC_NOTFOUND || code == KRB5_CC_NOT_KTYPE) { + *state = cred->have_tgt ? IAKERB_TGS_REQ : IAKERB_AS_REQ; + code = 0; + } else if (code == 0) { + *state = IAKERB_AP_REQ; + krb5_free_creds(ctx->k5c, out_creds); + } + +cleanup: + krb5_free_authdata(ctx->k5c, in_creds.authdata); + + return code; +} + +/* + * Allocate and initialise an IAKERB context + */ +static krb5_error_code +iakerb_alloc_context(iakerb_ctx_id_t *pctx, int initiate) +{ + iakerb_ctx_id_t ctx; + krb5_error_code code; + + *pctx = NULL; + + ctx = k5alloc(sizeof(*ctx), &code); + if (ctx == NULL) + goto cleanup; + ctx->defcred = GSS_C_NO_CREDENTIAL; + ctx->magic = KG_IAKERB_CONTEXT; + ctx->state = IAKERB_AS_REQ; + ctx->count = 0; + ctx->initiate = initiate; + ctx->established = 0; + + code = krb5_gss_init_context(&ctx->k5c); + if (code != 0) + goto cleanup; + + *pctx = ctx; + +cleanup: + if (code != 0) + iakerb_release_context(ctx); + + return code; +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_delete_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token) +{ + iakerb_ctx_id_t iakerb_ctx = (iakerb_ctx_id_t)*context_handle; + + if (output_token != GSS_C_NO_BUFFER) { + output_token->length = 0; + output_token->value = NULL; + } + + *minor_status = 0; + *context_handle = GSS_C_NO_CONTEXT; + iakerb_release_context(iakerb_ctx); + + return GSS_S_COMPLETE; +} + +static krb5_boolean +iakerb_is_iakerb_token(const gss_buffer_t token) +{ + krb5_error_code code; + unsigned int bodysize = token->length; + unsigned char *ptr = token->value; + + code = g_verify_token_header(gss_mech_iakerb, + &bodysize, &ptr, + IAKERB_TOK_PROXY, + token->length, 0); + + return (code == 0); +} + +static void +iakerb_make_exts(iakerb_ctx_id_t ctx, krb5_gss_ctx_ext_rec *exts) +{ + memset(exts, 0, sizeof(*exts)); + + if (ctx->conv.length != 0) + exts->iakerb.conv = &ctx->conv; +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_accept_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_cred_id_t verifier_cred_handle, + gss_buffer_t input_token, + gss_channel_bindings_t input_chan_bindings, + gss_name_t *src_name, + gss_OID *mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + gss_cred_id_t *delegated_cred_handle) +{ + OM_uint32 major_status = GSS_S_FAILURE; + OM_uint32 code; + iakerb_ctx_id_t ctx; + int initialContextToken = (*context_handle == GSS_C_NO_CONTEXT); + + if (initialContextToken) { + code = iakerb_alloc_context(&ctx, 0); + if (code != 0) + goto cleanup; + + } else + ctx = (iakerb_ctx_id_t)*context_handle; + + if (iakerb_is_iakerb_token(input_token)) { + if (ctx->gssc != GSS_C_NO_CONTEXT) { + /* We shouldn't get an IAKERB token now. */ + code = G_WRONG_TOKID; + major_status = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + code = iakerb_acceptor_step(ctx, initialContextToken, + input_token, output_token); + if (code == (OM_uint32)KRB5_BAD_MSIZE) + major_status = GSS_S_DEFECTIVE_TOKEN; + if (code != 0) + goto cleanup; + if (initialContextToken) { + *context_handle = (gss_ctx_id_t)ctx; + ctx = NULL; + } + if (src_name != NULL) + *src_name = GSS_C_NO_NAME; + if (mech_type != NULL) + *mech_type = (gss_OID)gss_mech_iakerb; + if (ret_flags != NULL) + *ret_flags = 0; + if (time_rec != NULL) + *time_rec = 0; + if (delegated_cred_handle != NULL) + *delegated_cred_handle = GSS_C_NO_CREDENTIAL; + major_status = GSS_S_CONTINUE_NEEDED; + } else { + krb5_gss_ctx_ext_rec exts; + + iakerb_make_exts(ctx, &exts); + + major_status = krb5_gss_accept_sec_context_ext(&code, + &ctx->gssc, + verifier_cred_handle, + input_token, + input_chan_bindings, + src_name, + NULL, + output_token, + ret_flags, + time_rec, + delegated_cred_handle, + &exts); + if (major_status == GSS_S_COMPLETE) + ctx->established = 1; + if (mech_type != NULL) + *mech_type = (gss_OID)gss_mech_krb5; + } + +cleanup: + if (initialContextToken && GSS_ERROR(major_status)) { + iakerb_release_context(ctx); + *context_handle = GSS_C_NO_CONTEXT; + } + + *minor_status = code; + return major_status; +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_init_sec_context(OM_uint32 *minor_status, + gss_cred_id_t claimant_cred_handle, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec) +{ + OM_uint32 major_status = GSS_S_FAILURE; + krb5_error_code code; + iakerb_ctx_id_t ctx; + krb5_gss_cred_id_t kcred; + krb5_gss_name_t kname; + krb5_boolean cred_locked = FALSE; + int initialContextToken = (*context_handle == GSS_C_NO_CONTEXT); + + if (initialContextToken) { + code = iakerb_alloc_context(&ctx, 1); + if (code != 0) { + *minor_status = code; + goto cleanup; + } + if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) { + major_status = iakerb_gss_acquire_cred(minor_status, NULL, + GSS_C_INDEFINITE, + GSS_C_NULL_OID_SET, + GSS_C_INITIATE, + &ctx->defcred, NULL, NULL); + if (GSS_ERROR(major_status)) + goto cleanup; + claimant_cred_handle = ctx->defcred; + } + } else { + ctx = (iakerb_ctx_id_t)*context_handle; + if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) + claimant_cred_handle = ctx->defcred; + } + + kname = (krb5_gss_name_t)target_name; + + major_status = kg_cred_resolve(minor_status, ctx->k5c, + claimant_cred_handle, target_name); + if (GSS_ERROR(major_status)) + goto cleanup; + cred_locked = TRUE; + kcred = (krb5_gss_cred_id_t)claimant_cred_handle; + + major_status = GSS_S_FAILURE; + + if (initialContextToken) { + code = iakerb_get_initial_state(ctx, kcred, kname, time_req, + &ctx->state); + if (code != 0) { + *minor_status = code; + goto cleanup; + } + *context_handle = (gss_ctx_id_t)ctx; + } + + if (ctx->state != IAKERB_AP_REQ) { + /* We need to do IAKERB. */ + code = iakerb_initiator_step(ctx, + kcred, + kname, + time_req, + input_token, + output_token); + if (code == KRB5_BAD_MSIZE) + major_status = GSS_S_DEFECTIVE_TOKEN; + if (code != 0) { + *minor_status = code; + goto cleanup; + } + } + + if (ctx->state == IAKERB_AP_REQ) { + krb5_gss_ctx_ext_rec exts; + + if (cred_locked) { + k5_mutex_unlock(&kcred->lock); + cred_locked = FALSE; + } + + iakerb_make_exts(ctx, &exts); + + if (ctx->gssc == GSS_C_NO_CONTEXT) + input_token = GSS_C_NO_BUFFER; + + /* IAKERB is finished, or we skipped to Kerberos directly. */ + major_status = krb5_gss_init_sec_context_ext(minor_status, + (gss_cred_id_t) kcred, + &ctx->gssc, + target_name, + (gss_OID)gss_mech_iakerb, + req_flags, + time_req, + input_chan_bindings, + input_token, + NULL, + output_token, + ret_flags, + time_rec, + &exts); + if (major_status == GSS_S_COMPLETE) + ctx->established = 1; + if (actual_mech_type != NULL) + *actual_mech_type = (gss_OID)gss_mech_krb5; + } else { + if (actual_mech_type != NULL) + *actual_mech_type = (gss_OID)gss_mech_iakerb; + if (ret_flags != NULL) + *ret_flags = 0; + if (time_rec != NULL) + *time_rec = 0; + major_status = GSS_S_CONTINUE_NEEDED; + } + +cleanup: + if (cred_locked) + k5_mutex_unlock(&kcred->lock); + if (initialContextToken && GSS_ERROR(major_status)) { + iakerb_release_context(ctx); + *context_handle = GSS_C_NO_CONTEXT; + } + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_unwrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_buffer_t input_message_buffer, + gss_buffer_t output_message_buffer, int *conf_state, + gss_qop_t *qop_state) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_unwrap(minor_status, ctx->gssc, input_message_buffer, + output_message_buffer, conf_state, qop_state); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_wrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + int conf_req_flag, gss_qop_t qop_req, + gss_buffer_t input_message_buffer, int *conf_state, + gss_buffer_t output_message_buffer) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_wrap(minor_status, ctx->gssc, conf_req_flag, qop_req, + input_message_buffer, conf_state, + output_message_buffer); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_process_context_token(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t token_buffer) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_DEFECTIVE_TOKEN; + + return krb5_gss_process_context_token(minor_status, ctx->gssc, + token_buffer); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_context_time(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + OM_uint32 *time_rec) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_context_time(minor_status, ctx->gssc, time_rec); +} + +#ifndef LEAN_CLIENT + +OM_uint32 KRB5_CALLCONV +iakerb_gss_export_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t interprocess_token) +{ + OM_uint32 maj; + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)*context_handle; + + /* We don't currently support exporting partially established contexts. */ + if (!ctx->established) + return GSS_S_UNAVAILABLE; + + maj = krb5_gss_export_sec_context(minor_status, &ctx->gssc, + interprocess_token); + if (ctx->gssc == GSS_C_NO_CONTEXT) { + iakerb_release_context(ctx); + *context_handle = GSS_C_NO_CONTEXT; + } + return maj; +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_import_sec_context(OM_uint32 *minor_status, + gss_buffer_t interprocess_token, + gss_ctx_id_t *context_handle) +{ + OM_uint32 maj, tmpmin; + krb5_error_code code; + gss_ctx_id_t gssc; + krb5_gss_ctx_id_t kctx; + iakerb_ctx_id_t ctx; + + maj = krb5_gss_import_sec_context(minor_status, interprocess_token, &gssc); + if (maj != GSS_S_COMPLETE) + return maj; + kctx = (krb5_gss_ctx_id_t)gssc; + + if (!kctx->established) { + /* We don't currently support importing partially established + * contexts. */ + krb5_gss_delete_sec_context(&tmpmin, &gssc, GSS_C_NO_BUFFER); + return GSS_S_FAILURE; + } + + code = iakerb_alloc_context(&ctx, kctx->initiate); + if (code != 0) { + krb5_gss_delete_sec_context(&tmpmin, &gssc, GSS_C_NO_BUFFER); + *minor_status = code; + return GSS_S_FAILURE; + } + + ctx->gssc = gssc; + ctx->established = 1; + *context_handle = (gss_ctx_id_t)ctx; + return GSS_S_COMPLETE; +} +#endif /* LEAN_CLIENT */ + +OM_uint32 KRB5_CALLCONV +iakerb_gss_inquire_context(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, gss_name_t *src_name, + gss_name_t *targ_name, OM_uint32 *lifetime_rec, + gss_OID *mech_type, OM_uint32 *ctx_flags, + int *initiate, int *opened) +{ + OM_uint32 ret; + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (src_name != NULL) + *src_name = GSS_C_NO_NAME; + if (targ_name != NULL) + *targ_name = GSS_C_NO_NAME; + if (lifetime_rec != NULL) + *lifetime_rec = 0; + if (mech_type != NULL) + *mech_type = (gss_OID)gss_mech_iakerb; + if (ctx_flags != NULL) + *ctx_flags = 0; + if (initiate != NULL) + *initiate = ctx->initiate; + if (opened != NULL) + *opened = ctx->established; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_COMPLETE; + + ret = krb5_gss_inquire_context(minor_status, ctx->gssc, src_name, + targ_name, lifetime_rec, mech_type, + ctx_flags, initiate, opened); + + if (!ctx->established) { + /* Report IAKERB as the mech OID until the context is established. */ + if (mech_type != NULL) + *mech_type = (gss_OID)gss_mech_iakerb; + + /* We don't support exporting partially-established contexts. */ + if (ctx_flags != NULL) + *ctx_flags &= ~GSS_C_TRANS_FLAG; + } + + return ret; +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_wrap_size_limit(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, int conf_req_flag, + gss_qop_t qop_req, OM_uint32 req_output_size, + OM_uint32 *max_input_size) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_wrap_size_limit(minor_status, ctx->gssc, conf_req_flag, + qop_req, req_output_size, max_input_size); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_get_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_qop_t qop_req, gss_buffer_t message_buffer, + gss_buffer_t message_token) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_get_mic(minor_status, ctx->gssc, qop_req, message_buffer, + message_token); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_verify_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_buffer_t msg_buffer, gss_buffer_t token_buffer, + gss_qop_t *qop_state) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_verify_mic(minor_status, ctx->gssc, msg_buffer, + token_buffer, qop_state); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_inquire_sec_context_by_oid(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_UNAVAILABLE; + + return krb5_gss_inquire_sec_context_by_oid(minor_status, ctx->gssc, + desired_object, data_set); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_set_sec_context_option(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + const gss_OID desired_object, + const gss_buffer_t value) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)*context_handle; + + if (ctx == NULL || ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_UNAVAILABLE; + + return krb5_gss_set_sec_context_option(minor_status, &ctx->gssc, + desired_object, value); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_wrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + int conf_req_flag, gss_qop_t qop_req, int *conf_state, + gss_iov_buffer_desc *iov, int iov_count) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_wrap_iov(minor_status, ctx->gssc, conf_req_flag, qop_req, + conf_state, iov, iov_count); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_unwrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + int *conf_state, gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, int iov_count) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_unwrap_iov(minor_status, ctx->gssc, conf_state, qop_state, + iov, iov_count); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_wrap_iov_length(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, int conf_req_flag, + gss_qop_t qop_req, int *conf_state, + gss_iov_buffer_desc *iov, int iov_count) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_wrap_iov_length(minor_status, ctx->gssc, conf_req_flag, + qop_req, conf_state, iov, iov_count); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_pseudo_random(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + int prf_key, const gss_buffer_t prf_in, + ssize_t desired_output_len, gss_buffer_t prf_out) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_pseudo_random(minor_status, ctx->gssc, prf_key, prf_in, + desired_output_len, prf_out); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_get_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_qop_t qop_req, gss_iov_buffer_desc *iov, + int iov_count) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_get_mic_iov(minor_status, ctx->gssc, qop_req, iov, + iov_count); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_verify_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_qop_t *qop_state, gss_iov_buffer_desc *iov, + int iov_count) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_verify_mic_iov(minor_status, ctx->gssc, qop_state, iov, + iov_count); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_get_mic_iov_length(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, gss_qop_t qop_req, + gss_iov_buffer_desc *iov, int iov_count) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_get_mic_iov_length(minor_status, ctx->gssc, qop_req, iov, + iov_count); +} diff --git a/src/lib/gssapi/krb5/import_cred.c b/src/lib/gssapi/krb5/import_cred.c new file mode 100644 index 000000000000..f0a0373bfb79 --- /dev/null +++ b/src/lib/gssapi/krb5/import_cred.c @@ -0,0 +1,648 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/import_cred.c - krb5 import_cred implementation */ +/* + * Copyright (C) 2012 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "k5-int.h" +#include "k5-json.h" +#include "gssapiP_krb5.h" + +/* Return the idx element of array if it is of type tid; otherwise return + * NULL. The caller is responsible for checking the array length. */ +static k5_json_value +check_element(k5_json_array array, size_t idx, k5_json_tid tid) +{ + k5_json_value v; + + v = k5_json_array_get(array, idx); + return (k5_json_get_tid(v) == tid) ? v : NULL; +} + +/* All of the json_to_x functions return 0 on success, -1 on failure (either + * from running out of memory or from defective input). */ + +/* Convert a JSON value to a C string or to NULL. */ +static int +json_to_optional_string(k5_json_value v, char **string_out) +{ + *string_out = NULL; + if (k5_json_get_tid(v) == K5_JSON_TID_NULL) + return 0; + if (k5_json_get_tid(v) != K5_JSON_TID_STRING) + return -1; + *string_out = strdup(k5_json_string_utf8(v)); + return (*string_out == NULL) ? -1 : 0; +} + +/* Convert a JSON value to a principal or to NULL. */ +static int +json_to_principal(krb5_context context, k5_json_value v, + krb5_principal *princ_out) +{ + *princ_out = NULL; + if (k5_json_get_tid(v) == K5_JSON_TID_NULL) + return 0; + if (k5_json_get_tid(v) != K5_JSON_TID_STRING) + return -1; + if (krb5_parse_name(context, k5_json_string_utf8(v), princ_out)) + return -1; + return 0; +} + +/* Convert a JSON value to a zero-terminated enctypes list or to NULL. */ +static int +json_to_etypes(k5_json_value v, krb5_enctype **etypes_out) +{ + krb5_enctype *etypes = NULL; + k5_json_array array; + k5_json_number n; + size_t len, i; + + *etypes_out = NULL; + if (k5_json_get_tid(v) == K5_JSON_TID_NULL) + return 0; + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + return -1; + array = v; + len = k5_json_array_length(array); + etypes = calloc(len + 1, sizeof(*etypes)); + for (i = 0; i < len; i++) { + n = check_element(array, i, K5_JSON_TID_NUMBER); + if (n == NULL) + goto invalid; + etypes[i] = k5_json_number_value(n); + } + *etypes_out = etypes; + return 0; + +invalid: + free(etypes); + return -1; +} + +/* Convert a JSON value to a krb5 GSS name or to NULL. */ +static int +json_to_kgname(krb5_context context, k5_json_value v, + krb5_gss_name_t *name_out) +{ + k5_json_array array; + krb5_gss_name_t name = NULL; + + *name_out = NULL; + if (k5_json_get_tid(v) == K5_JSON_TID_NULL) + return 0; + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + return -1; + array = v; + if (k5_json_array_length(array) != 3) + return -1; + name = calloc(1, sizeof(*name)); + if (name == NULL) + return -1; + if (k5_mutex_init(&name->lock)) { + free(name); + return -1; + } + + if (json_to_principal(context, k5_json_array_get(array, 0), &name->princ)) + goto invalid; + if (json_to_optional_string(k5_json_array_get(array, 1), &name->service)) + goto invalid; + if (json_to_optional_string(k5_json_array_get(array, 2), &name->host)) + goto invalid; + + *name_out = name; + return 0; + +invalid: + kg_release_name(context, &name); + return -1; +} + +/* Convert a JSON value to a keytab handle or to NULL. */ +static int +json_to_keytab(krb5_context context, k5_json_value v, krb5_keytab *keytab_out) +{ + *keytab_out = NULL; + if (k5_json_get_tid(v) == K5_JSON_TID_NULL) + return 0; + if (k5_json_get_tid(v) != K5_JSON_TID_STRING) + return -1; + if (krb5_kt_resolve(context, k5_json_string_utf8(v), keytab_out)) + return -1; + return 0; +} + +/* Convert a JSON value to an rcache handle or to NULL. */ +static int +json_to_rcache(krb5_context context, k5_json_value v, krb5_rcache *rcache_out) +{ + krb5_rcache rcache; + + *rcache_out = NULL; + if (k5_json_get_tid(v) == K5_JSON_TID_NULL) + return 0; + if (k5_json_get_tid(v) != K5_JSON_TID_STRING) + return -1; + if (krb5_rc_resolve_full(context, &rcache, (char *)k5_json_string_utf8(v))) + return -1; + if (krb5_rc_recover_or_initialize(context, rcache, context->clockskew)) { + krb5_rc_close(context, rcache); + return -1; + } + *rcache_out = rcache; + return 0; +} + +/* Convert a JSON value to a keyblock, filling in keyblock. */ +static int +json_to_keyblock(k5_json_value v, krb5_keyblock *keyblock) +{ + k5_json_array array; + k5_json_number n; + k5_json_string s; + size_t len; + + memset(keyblock, 0, sizeof(*keyblock)); + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + return -1; + array = v; + if (k5_json_array_length(array) != 2) + return -1; + + n = check_element(array, 0, K5_JSON_TID_NUMBER); + if (n == NULL) + return -1; + keyblock->enctype = k5_json_number_value(n); + + s = check_element(array, 1, K5_JSON_TID_STRING); + if (s == NULL) + return -1; + if (k5_json_string_unbase64(s, &keyblock->contents, &len)) + return -1; + keyblock->length = len; + keyblock->magic = KV5M_KEYBLOCK; + return 0; +} + +/* Convert a JSON value to a krb5 address. */ +static int +json_to_address(k5_json_value v, krb5_address **addr_out) +{ + k5_json_array array; + krb5_address *addr = NULL; + k5_json_number n; + k5_json_string s; + size_t len; + + *addr_out = NULL; + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + return -1; + array = v; + if (k5_json_array_length(array) != 2) + return -1; + + n = check_element(array, 0, K5_JSON_TID_NUMBER); + if (n == NULL) + return -1; + s = check_element(array, 1, K5_JSON_TID_STRING); + if (s == NULL) + return -1; + + addr = malloc(sizeof(*addr)); + if (addr == NULL) + return -1; + addr->addrtype = k5_json_number_value(n); + if (k5_json_string_unbase64(s, &addr->contents, &len)) { + free(addr); + return -1; + } + addr->length = len; + addr->magic = KV5M_ADDRESS; + *addr_out = addr; + return 0; +} + +/* Convert a JSON value to a null-terminated list of krb5 addresses or to + * NULL. */ +static int +json_to_addresses(krb5_context context, k5_json_value v, + krb5_address ***addresses_out) +{ + k5_json_array array; + krb5_address **addrs = NULL; + size_t len, i; + + *addresses_out = NULL; + if (k5_json_get_tid(v) == K5_JSON_TID_NULL) + return 0; + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + return -1; + array = v; + len = k5_json_array_length(array); + addrs = calloc(len + 1, sizeof(*addrs)); + for (i = 0; i < len; i++) { + if (json_to_address(k5_json_array_get(array, i), &addrs[i])) + goto invalid; + } + addrs[i] = NULL; + *addresses_out = addrs; + return 0; + +invalid: + krb5_free_addresses(context, addrs); + return -1; +} + +/* Convert a JSON value to an authdata element. */ +static int +json_to_authdata_element(k5_json_value v, krb5_authdata **ad_out) +{ + k5_json_array array; + krb5_authdata *ad = NULL; + k5_json_number n; + k5_json_string s; + size_t len; + + *ad_out = NULL; + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + return -1; + array = v; + if (k5_json_array_length(array) != 2) + return -1; + + n = check_element(array, 0, K5_JSON_TID_NUMBER); + if (n == NULL) + return -1; + s = check_element(array, 1, K5_JSON_TID_STRING); + if (s == NULL) + return -1; + + ad = malloc(sizeof(*ad)); + if (ad == NULL) + return -1; + ad->ad_type = k5_json_number_value(n); + if (k5_json_string_unbase64(s, &ad->contents, &len)) { + free(ad); + return -1; + } + ad->length = len; + ad->magic = KV5M_AUTHDATA; + *ad_out = ad; + return 0; +} + +/* Convert a JSON value to a null-terminated authdata list or to NULL. */ +static int +json_to_authdata(krb5_context context, k5_json_value v, + krb5_authdata ***authdata_out) +{ + k5_json_array array; + krb5_authdata **authdata = NULL; + size_t len, i; + + *authdata_out = NULL; + if (k5_json_get_tid(v) == K5_JSON_TID_NULL) + return 0; + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + return -1; + array = v; + len = k5_json_array_length(array); + authdata = calloc(len + 1, sizeof(*authdata)); + for (i = 0; i < len; i++) { + if (json_to_authdata_element(k5_json_array_get(array, i), + &authdata[i])) + goto invalid; + } + authdata[i] = NULL; + *authdata_out = authdata; + return 0; + +invalid: + krb5_free_authdata(context, authdata); + return -1; +} + +/* Convert a JSON value to a krb5 credential structure, filling in creds. */ +static int +json_to_creds(krb5_context context, k5_json_value v, krb5_creds *creds) +{ + k5_json_array array; + k5_json_number n; + k5_json_bool b; + k5_json_string s; + unsigned char *data; + size_t len; + + memset(creds, 0, sizeof(*creds)); + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + return -1; + array = v; + if (k5_json_array_length(array) != 13) + return -1; + + if (json_to_principal(context, k5_json_array_get(array, 0), + &creds->client)) + goto invalid; + + if (json_to_principal(context, k5_json_array_get(array, 1), + &creds->server)) + goto invalid; + + if (json_to_keyblock(k5_json_array_get(array, 2), &creds->keyblock)) + goto invalid; + + n = check_element(array, 3, K5_JSON_TID_NUMBER); + if (n == NULL) + goto invalid; + creds->times.authtime = k5_json_number_value(n); + + n = check_element(array, 4, K5_JSON_TID_NUMBER); + if (n == NULL) + goto invalid; + creds->times.starttime = k5_json_number_value(n); + + n = check_element(array, 5, K5_JSON_TID_NUMBER); + if (n == NULL) + goto invalid; + creds->times.endtime = k5_json_number_value(n); + + n = check_element(array, 6, K5_JSON_TID_NUMBER); + if (n == NULL) + goto invalid; + creds->times.renew_till = k5_json_number_value(n); + + b = check_element(array, 7, K5_JSON_TID_BOOL); + if (b == NULL) + goto invalid; + creds->is_skey = k5_json_bool_value(b); + + n = check_element(array, 8, K5_JSON_TID_NUMBER); + if (n == NULL) + goto invalid; + creds->ticket_flags = k5_json_number_value(n); + + if (json_to_addresses(context, k5_json_array_get(array, 9), + &creds->addresses)) + goto invalid; + + s = check_element(array, 10, K5_JSON_TID_STRING); + if (s == NULL) + goto invalid; + if (k5_json_string_unbase64(s, &data, &len)) + goto invalid; + creds->ticket.data = (char *)data; + creds->ticket.length = len; + + s = check_element(array, 11, K5_JSON_TID_STRING); + if (s == NULL) + goto invalid; + if (k5_json_string_unbase64(s, &data, &len)) + goto invalid; + creds->second_ticket.data = (char *)data; + creds->second_ticket.length = len; + + if (json_to_authdata(context, k5_json_array_get(array, 12), + &creds->authdata)) + goto invalid; + + creds->magic = KV5M_CREDS; + return 0; + +invalid: + krb5_free_cred_contents(context, creds); + memset(creds, 0, sizeof(*creds)); + return -1; +} + +/* Convert a JSON value to a ccache handle or to NULL. Set *new_out to true if + * the ccache handle is a newly created memory ccache, false otherwise. */ +static int +json_to_ccache(krb5_context context, k5_json_value v, krb5_ccache *ccache_out, + krb5_boolean *new_out) +{ + krb5_error_code ret; + krb5_ccache ccache = NULL; + krb5_principal princ; + krb5_creds creds; + k5_json_array array; + size_t i, len; + + *ccache_out = NULL; + *new_out = FALSE; + if (k5_json_get_tid(v) == K5_JSON_TID_NULL) + return 0; + if (k5_json_get_tid(v) == K5_JSON_TID_STRING) { + /* We got a reference to an external ccache; just resolve it. */ + return krb5_cc_resolve(context, k5_json_string_utf8(v), ccache_out) ? + -1 : 0; + } + + /* We got the contents of a memory ccache. */ + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + return -1; + array = v; + len = k5_json_array_length(array); + if (len < 1) + return -1; + + /* Initialize a new memory ccache using the principal in the first array + * entry.*/ + if (krb5_cc_new_unique(context, "MEMORY", NULL, &ccache)) + return -1; + if (json_to_principal(context, k5_json_array_get(array, 0), &princ)) + goto invalid; + ret = krb5_cc_initialize(context, ccache, princ); + krb5_free_principal(context, princ); + if (ret) + goto invalid; + + /* Add remaining array entries to the ccache as credentials. */ + for (i = 1; i < len; i++) { + if (json_to_creds(context, k5_json_array_get(array, i), &creds)) + goto invalid; + ret = krb5_cc_store_cred(context, ccache, &creds); + krb5_free_cred_contents(context, &creds); + if (ret) + goto invalid; + } + + *ccache_out = ccache; + *new_out = TRUE; + return 0; + +invalid: + (void)krb5_cc_destroy(context, ccache); + return -1; +} + +/* Convert a JSON array value to a krb5 GSS credential. */ +static int +json_to_kgcred(krb5_context context, k5_json_array array, + krb5_gss_cred_id_t *cred_out) +{ + krb5_gss_cred_id_t cred; + k5_json_number n; + k5_json_bool b; + krb5_boolean is_new; + OM_uint32 tmp; + + *cred_out = NULL; + if (k5_json_array_length(array) != 14) + return -1; + + cred = calloc(1, sizeof(*cred)); + if (cred == NULL) + return -1; + if (k5_mutex_init(&cred->lock)) { + free(cred); + return -1; + } + + n = check_element(array, 0, K5_JSON_TID_NUMBER); + if (n == NULL) + goto invalid; + cred->usage = k5_json_number_value(n); + + if (json_to_kgname(context, k5_json_array_get(array, 1), &cred->name)) + goto invalid; + + if (json_to_principal(context, k5_json_array_get(array, 2), + &cred->impersonator)) + goto invalid; + + b = check_element(array, 3, K5_JSON_TID_BOOL); + if (b == NULL) + goto invalid; + cred->default_identity = k5_json_bool_value(b); + + b = check_element(array, 4, K5_JSON_TID_BOOL); + if (b == NULL) + goto invalid; + cred->iakerb_mech = k5_json_bool_value(b); + + if (json_to_keytab(context, k5_json_array_get(array, 5), &cred->keytab)) + goto invalid; + + if (json_to_rcache(context, k5_json_array_get(array, 6), &cred->rcache)) + goto invalid; + + if (json_to_ccache(context, k5_json_array_get(array, 7), &cred->ccache, + &is_new)) + goto invalid; + cred->destroy_ccache = is_new; + + if (json_to_keytab(context, k5_json_array_get(array, 8), + &cred->client_keytab)) + goto invalid; + + b = check_element(array, 9, K5_JSON_TID_BOOL); + if (b == NULL) + goto invalid; + cred->have_tgt = k5_json_bool_value(b); + + n = check_element(array, 10, K5_JSON_TID_NUMBER); + if (n == NULL) + goto invalid; + cred->expire = k5_json_number_value(n); + + n = check_element(array, 11, K5_JSON_TID_NUMBER); + if (n == NULL) + goto invalid; + cred->refresh_time = k5_json_number_value(n); + + if (json_to_etypes(k5_json_array_get(array, 12), &cred->req_enctypes)) + goto invalid; + + if (json_to_optional_string(k5_json_array_get(array, 13), &cred->password)) + goto invalid; + + *cred_out = cred; + return 0; + +invalid: + (void)krb5_gss_release_cred(&tmp, (gss_cred_id_t *)&cred); + return -1; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_import_cred(OM_uint32 *minor_status, gss_buffer_t token, + gss_cred_id_t *cred_handle) +{ + OM_uint32 status = GSS_S_COMPLETE; + krb5_context context; + krb5_error_code ret; + krb5_gss_cred_id_t cred; + k5_json_value v = NULL; + k5_json_array array; + k5_json_string str; + char *copy = NULL; + + ret = krb5_gss_init_context(&context); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + + /* Decode token. */ + copy = k5memdup0(token->value, token->length, &ret); + if (copy == NULL) { + status = GSS_S_FAILURE; + *minor_status = ret; + goto cleanup; + } + if (k5_json_decode(copy, &v)) + goto invalid; + + /* Decode the CRED_EXPORT_MAGIC array wrapper. */ + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + goto invalid; + array = v; + if (k5_json_array_length(array) != 2) + goto invalid; + str = check_element(array, 0, K5_JSON_TID_STRING); + if (str == NULL || + strcmp(k5_json_string_utf8(str), CRED_EXPORT_MAGIC) != 0) + goto invalid; + if (json_to_kgcred(context, k5_json_array_get(array, 1), &cred)) + goto invalid; + + *cred_handle = (gss_cred_id_t)cred; + +cleanup: + free(copy); + k5_json_release(v); + krb5_free_context(context); + return status; + +invalid: + status = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; +} diff --git a/src/lib/gssapi/krb5/import_name.c b/src/lib/gssapi/krb5/import_name.c new file mode 100644 index 000000000000..3f5492b99f6b --- /dev/null +++ b/src/lib/gssapi/krb5/import_name.c @@ -0,0 +1,332 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * $Id$ + */ + +#include "gssapiP_krb5.h" + +#ifndef NO_PASSWORD +#include <pwd.h> +#include <stdio.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif + +/* + * errors: + * GSS_S_BAD_NAMETYPE if the type is bogus + * GSS_S_BAD_NAME if the type is good but the name is bogus + * GSS_S_FAILURE if memory allocation fails + */ + +/* + * Import serialized authdata context + */ +static krb5_error_code +import_name_composite(krb5_context context, + unsigned char *enc_data, size_t enc_length, + krb5_authdata_context *pad_context) +{ + krb5_authdata_context ad_context; + krb5_error_code code; + krb5_data data; + + if (enc_length == 0) + return 0; + + code = krb5_authdata_context_init(context, &ad_context); + if (code != 0) + return code; + + data.data = (char *)enc_data; + data.length = enc_length; + + code = krb5_authdata_import_attributes(context, + ad_context, + AD_USAGE_MASK, + &data); + if (code != 0) { + krb5_authdata_context_free(context, ad_context); + return code; + } + + *pad_context = ad_context; + + return 0; +} + +/* Split a host-based name "service[@host]" into allocated strings + * placed in *service_out and *host_out (possibly NULL). */ +static krb5_error_code +parse_hostbased(const char *str, size_t len, + char **service_out, char **host_out) +{ + const char *at; + size_t servicelen, hostlen; + char *service, *host = NULL; + + *service_out = *host_out = NULL; + + /* Find the bound of the service name and copy it. */ + at = memchr(str, '@', len); + servicelen = (at == NULL) ? len : (size_t)(at - str); + service = xmalloc(servicelen + 1); + if (service == NULL) + return ENOMEM; + memcpy(service, str, servicelen); + service[servicelen] = '\0'; + + /* If present, copy the hostname. */ + if (at != NULL) { + hostlen = len - servicelen - 1; + host = malloc(hostlen + 1); + if (host == NULL) { + free(service); + return ENOMEM; + } + memcpy(host, at + 1, hostlen); + host[hostlen] = '\0'; + } + + *service_out = service; + *host_out = host; + return 0; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_import_name(minor_status, input_name_buffer, + input_name_type, output_name) + OM_uint32 *minor_status; + gss_buffer_t input_name_buffer; + gss_OID input_name_type; + gss_name_t *output_name; +{ + krb5_context context; + krb5_principal princ = NULL; + krb5_error_code code; + unsigned char *cp, *end; + char *tmp = NULL, *tmp2 = NULL, *service = NULL, *host = NULL, *stringrep; + ssize_t length; +#ifndef NO_PASSWORD + struct passwd *pw; +#endif + int is_composite = 0; + krb5_authdata_context ad_context = NULL; + OM_uint32 status = GSS_S_FAILURE; + krb5_gss_name_t name; + + *output_name = NULL; + *minor_status = 0; + + code = krb5_gss_init_context(&context); + if (code) + goto cleanup; + + if ((input_name_type != GSS_C_NULL_OID) && + (g_OID_equal(input_name_type, gss_nt_service_name) || + g_OID_equal(input_name_type, gss_nt_service_name_v2))) { + /* Split the name into service and host (or NULL). */ + code = parse_hostbased(input_name_buffer->value, + input_name_buffer->length, &service, &host); + if (code) + goto cleanup; + + /* + * Compute the initiator target name. In some cases this is a waste of + * getaddrinfo/getnameinfo queries, but computing the name when we need + * it would require a lot of code changes. + */ + code = krb5_sname_to_principal(context, host, service, KRB5_NT_SRV_HST, + &princ); + if (code) + goto cleanup; + } else if ((input_name_type != GSS_C_NULL_OID) && + (g_OID_equal(input_name_type, gss_nt_krb5_principal))) { + krb5_principal input; + + if (input_name_buffer->length != sizeof(krb5_principal)) { + code = G_WRONG_SIZE; + status = GSS_S_BAD_NAME; + goto cleanup; + } + + input = *((krb5_principal *) input_name_buffer->value); + + code = krb5_copy_principal(context, input, &princ); + if (code) + goto cleanup; + } else if ((input_name_type != NULL) && + g_OID_equal(input_name_type, GSS_C_NT_ANONYMOUS)) { + code = krb5_copy_principal(context, krb5_anonymous_principal(), + &princ); + if (code) + goto cleanup; + } else { +#ifndef NO_PASSWORD + uid_t uid; + struct passwd pwx; + char pwbuf[BUFSIZ]; +#endif + + stringrep = NULL; + + tmp = k5memdup0(input_name_buffer->value, input_name_buffer->length, + &code); + if (tmp == NULL) + goto cleanup; + tmp2 = NULL; + + /* Find the appropriate string rep to pass into parse_name. */ + if ((input_name_type == GSS_C_NULL_OID) || + g_OID_equal(input_name_type, gss_nt_krb5_name) || + g_OID_equal(input_name_type, gss_nt_user_name)) { + stringrep = (char *) tmp; +#ifndef NO_PASSWORD + } else if (g_OID_equal(input_name_type, gss_nt_machine_uid_name)) { + uid = *(uid_t *) input_name_buffer->value; + do_getpwuid: + if (k5_getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf), &pw) == 0) + stringrep = pw->pw_name; + else + code = G_NOUSER; + } else if (g_OID_equal(input_name_type, gss_nt_string_uid_name)) { + uid = atoi(tmp); + goto do_getpwuid; +#endif + } else if (g_OID_equal(input_name_type, gss_nt_exported_name) || + g_OID_equal(input_name_type, GSS_C_NT_COMPOSITE_EXPORT)) { +#define BOUNDS_CHECK(cp, end, n) \ + do { if ((end) - (cp) < (n)) goto fail_name; } while (0) + cp = (unsigned char *)tmp; + end = cp + input_name_buffer->length; + + BOUNDS_CHECK(cp, end, 2); + if (*cp++ != 0x04) + goto fail_name; + switch (*cp++) { + case 0x01: + break; + case 0x02: + is_composite++; + break; + default: + goto fail_name; + } + + BOUNDS_CHECK(cp, end, 2); + if (*cp++ != 0x00) + goto fail_name; + length = *cp++; + if (length != (ssize_t)gss_mech_krb5->length+2) + goto fail_name; + + BOUNDS_CHECK(cp, end, 2); + if (*cp++ != 0x06) + goto fail_name; + length = *cp++; + if (length != (ssize_t)gss_mech_krb5->length) + goto fail_name; + + BOUNDS_CHECK(cp, end, length); + if (memcmp(cp, gss_mech_krb5->elements, length) != 0) + goto fail_name; + cp += length; + + BOUNDS_CHECK(cp, end, 4); + length = *cp++; + length = (length << 8) | *cp++; + length = (length << 8) | *cp++; + length = (length << 8) | *cp++; + + BOUNDS_CHECK(cp, end, length); + tmp2 = k5alloc(length + 1, &code); + if (tmp2 == NULL) + goto cleanup; + strncpy(tmp2, (char *)cp, length); + tmp2[length] = 0; + stringrep = tmp2; + cp += length; + + if (is_composite) { + BOUNDS_CHECK(cp, end, 4); + length = *cp++; + length = (length << 8) | *cp++; + length = (length << 8) | *cp++; + length = (length << 8) | *cp++; + + BOUNDS_CHECK(cp, end, length); + code = import_name_composite(context, + cp, length, + &ad_context); + if (code != 0) + goto fail_name; + cp += length; + } + assert(cp == end); + } else { + status = GSS_S_BAD_NAMETYPE; + goto cleanup; + } + + /* At this point, stringrep is set, or if not, code is. */ + if (stringrep) { + code = krb5_parse_name(context, (char *)stringrep, &princ); + if (code) + goto cleanup; + } else { + fail_name: + status = GSS_S_BAD_NAME; + goto cleanup; + } + } + + /* Create a name and save it in the validation database. */ + code = kg_init_name(context, princ, service, host, ad_context, + KG_INIT_NAME_NO_COPY, &name); + if (code) + goto cleanup; + princ = NULL; + ad_context = NULL; + service = host = NULL; + *output_name = (gss_name_t)name; + status = GSS_S_COMPLETE; + +cleanup: + *minor_status = (OM_uint32)code; + if (*minor_status) + save_error_info(*minor_status, context); + krb5_free_principal(context, princ); + krb5_authdata_context_free(context, ad_context); + krb5_free_context(context); + free(tmp); + free(tmp2); + free(service); + free(host); + return status; +} diff --git a/src/lib/gssapi/krb5/import_sec_context.c b/src/lib/gssapi/krb5/import_sec_context.c new file mode 100644 index 000000000000..4190adf35cab --- /dev/null +++ b/src/lib/gssapi/krb5/import_sec_context.c @@ -0,0 +1,118 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/import_sec_context.c - Internalize the security context */ +/* + * Copyright 1995,2004,2007,2008 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 "gssapiP_krb5.h" +/* for serialization initialization functions */ +#include "k5-int.h" + +/* + * Fix up the OID of the mechanism so that uses the static version of + * the OID if possible. + */ +gss_OID krb5_gss_convert_static_mech_oid(oid) + gss_OID oid; +{ + const gss_OID_desc *p; + OM_uint32 minor_status; + + for (p = krb5_gss_oid_array; p->length; p++) { + if ((oid->length == p->length) && + (memcmp(oid->elements, p->elements, p->length) == 0)) { + generic_gss_release_oid(&minor_status, &oid); + return (gss_OID) p; + } + } + return oid; +} + +krb5_error_code +krb5_gss_ser_init (krb5_context context) +{ + krb5_error_code code; + static krb5_error_code (KRB5_CALLCONV *const fns[])(krb5_context) = { + krb5_ser_context_init, krb5_ser_auth_context_init, + krb5_ser_ccache_init, krb5_ser_rcache_init, krb5_ser_keytab_init, + }; + unsigned int i; + + for (i = 0; i < sizeof(fns)/sizeof(fns[0]); i++) + if ((code = (fns[i])(context)) != 0) + return code; + return 0; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_import_sec_context(minor_status, interprocess_token, context_handle) + OM_uint32 *minor_status; + gss_buffer_t interprocess_token; + gss_ctx_id_t *context_handle; +{ + krb5_context context; + krb5_error_code kret = 0; + size_t blen; + krb5_gss_ctx_id_t ctx; + krb5_octet *ibp; + + /* This is a bit screwy. We create a krb5 context because we need + one when calling the serialization code. However, one of the + objects we're unpacking is a krb5 context, so when we finish, + we can throw this one away. */ + kret = krb5_gss_init_context(&context); + if (kret) { + *minor_status = kret; + return GSS_S_FAILURE; + } + kret = krb5_gss_ser_init(context); + if (kret) { + *minor_status = kret; + save_error_info(*minor_status, context); + krb5_free_context(context); + return GSS_S_FAILURE; + } + + /* Assume a tragic failure */ + ctx = (krb5_gss_ctx_id_t) NULL; + *minor_status = 0; + + /* Internalize the context */ + ibp = (krb5_octet *) interprocess_token->value; + blen = (size_t) interprocess_token->length; + kret = kg_ctx_internalize(context, (krb5_pointer *) &ctx, &ibp, &blen); + if (kret) { + *minor_status = (OM_uint32) kret; + save_error_info(*minor_status, context); + krb5_free_context(context); + return(GSS_S_FAILURE); + } + krb5_free_context(context); + + ctx->mech_used = krb5_gss_convert_static_mech_oid(ctx->mech_used); + + *context_handle = (gss_ctx_id_t) ctx; + + *minor_status = 0; + return (GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/krb5/indicate_mechs.c b/src/lib/gssapi/krb5/indicate_mechs.c new file mode 100644 index 000000000000..45538cb779aa --- /dev/null +++ b/src/lib/gssapi/krb5/indicate_mechs.c @@ -0,0 +1,37 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * $Id$ + */ + +#include "gssapiP_krb5.h" +#include "mglueP.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_indicate_mechs(minor_status, mech_set) + OM_uint32 *minor_status; + gss_OID_set *mech_set; +{ + return generic_gss_copy_oid_set(minor_status, kg_all_mechs, mech_set); +} diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c new file mode 100644 index 000000000000..70f7955ae1ae --- /dev/null +++ b/src/lib/gssapi/krb5/init_sec_context.c @@ -0,0 +1,1104 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2000, 2002, 2003, 2007, 2008 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. + */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * 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 FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +/* + * Copyright (c) 2006-2008, Novell, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The copyright holder's name is not used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif +#include <stdlib.h> +#include <assert.h> + +/* + * $Id$ + */ + +/* XXX This is for debugging only!!! Should become a real bitfield + at some point */ +int krb5_gss_dbg_client_expcreds = 0; + +/* + * Common code which fetches the correct krb5 credentials from the + * ccache. + */ +static krb5_error_code get_credentials(context, cred, server, now, + endtime, out_creds) + krb5_context context; + krb5_gss_cred_id_t cred; + krb5_gss_name_t server; + krb5_timestamp now; + krb5_timestamp endtime; + krb5_creds **out_creds; +{ + krb5_error_code code; + krb5_creds in_creds, evidence_creds, mcreds, *result_creds = NULL; + krb5_flags flags = 0; + + *out_creds = NULL; + + k5_mutex_assert_locked(&cred->lock); + memset(&in_creds, 0, sizeof(krb5_creds)); + memset(&evidence_creds, 0, sizeof(krb5_creds)); + in_creds.client = in_creds.server = NULL; + + assert(cred->name != NULL); + + in_creds.client = cred->name->princ; + in_creds.server = server->princ; + in_creds.times.endtime = endtime; + in_creds.authdata = NULL; + in_creds.keyblock.enctype = 0; + + /* + * cred->name is immutable, so there is no need to acquire + * cred->name->lock. + */ + if (cred->name->ad_context != NULL) { + code = krb5_authdata_export_authdata(context, + cred->name->ad_context, + AD_USAGE_TGS_REQ, + &in_creds.authdata); + if (code != 0) + goto cleanup; + } + + /* + * For IAKERB or constrained delegation, only check the cache in this step. + * For IAKERB we will ask the server to make any necessary TGS requests; + * for constrained delegation we will adjust in_creds and make an S4U2Proxy + * request below if the cache lookup fails. + */ + if (cred->impersonator != NULL || cred->iakerb_mech) + flags |= KRB5_GC_CACHED; + + code = krb5_get_credentials(context, flags, cred->ccache, + &in_creds, &result_creds); + + /* + * Try constrained delegation if we have proxy credentials, unless + * we are trying to get a ticket to ourselves (in which case we could + * just use the evidence ticket directly from cache). + */ + if (code == KRB5_CC_NOTFOUND && cred->impersonator != NULL && + !cred->iakerb_mech && + !krb5_principal_compare(context, cred->impersonator, server->princ)) { + + memset(&mcreds, 0, sizeof(mcreds)); + mcreds.magic = KV5M_CREDS; + mcreds.server = cred->impersonator; + mcreds.client = cred->name->princ; + code = krb5_cc_retrieve_cred(context, cred->ccache, + KRB5_TC_MATCH_AUTHDATA, &mcreds, + &evidence_creds); + if (code) + goto cleanup; + + assert(evidence_creds.ticket_flags & TKT_FLG_FORWARDABLE); + in_creds.client = cred->impersonator; + in_creds.second_ticket = evidence_creds.ticket; + flags = KRB5_GC_CANONICALIZE | KRB5_GC_CONSTRAINED_DELEGATION; + code = krb5_get_credentials(context, flags, cred->ccache, + &in_creds, &result_creds); + } + + if (code) + goto cleanup; + + if (flags & KRB5_GC_CONSTRAINED_DELEGATION) { + if (!krb5_principal_compare(context, cred->name->princ, + result_creds->client)) { + /* server did not support constrained delegation */ + code = KRB5_KDCREP_MODIFIED; + goto cleanup; + } + } + + /* + * Enforce a stricter limit (without timeskew forgiveness at the + * boundaries) because accept_sec_context code is also similarly + * non-forgiving. + */ + if (!krb5_gss_dbg_client_expcreds && result_creds->times.endtime < now) { + code = KRB5KRB_AP_ERR_TKT_EXPIRED; + goto cleanup; + } + + *out_creds = result_creds; + result_creds = NULL; + +cleanup: + krb5_free_authdata(context, in_creds.authdata); + krb5_free_cred_contents(context, &evidence_creds); + krb5_free_creds(context, result_creds); + + return code; +} +struct gss_checksum_data { + krb5_gss_ctx_id_rec *ctx; + krb5_gss_cred_id_t cred; + krb5_checksum md5; + krb5_data checksum_data; + krb5_gss_ctx_ext_t exts; +}; + +#ifdef CFX_EXERCISE +#include "../../krb5/krb/auth_con.h" +#endif +static krb5_error_code KRB5_CALLCONV +make_gss_checksum (krb5_context context, krb5_auth_context auth_context, + void *cksum_data, krb5_data **out) +{ + krb5_error_code code; + krb5_int32 con_flags; + unsigned char *ptr; + struct gss_checksum_data *data = cksum_data; + krb5_data credmsg; + unsigned int junk; + krb5_data *finished = NULL; + krb5_key send_subkey; + + data->checksum_data.data = 0; + credmsg.data = 0; + /* build the checksum field */ + + if (data->ctx->gss_flags & GSS_C_DELEG_FLAG) { + /* first get KRB_CRED message, so we know its length */ + + /* clear the time check flag that was set in krb5_auth_con_init() */ + krb5_auth_con_getflags(context, auth_context, &con_flags); + krb5_auth_con_setflags(context, auth_context, + con_flags & ~KRB5_AUTH_CONTEXT_DO_TIME); + + assert(data->cred->name != NULL); + + /* + * RFC 4121 4.1.1 specifies forwarded credentials must be encrypted in + * the session key, but krb5_fwd_tgt_creds will use the send subkey if + * it's set in the auth context. Suppress the send subkey + * temporarily. + */ + krb5_auth_con_getsendsubkey_k(context, auth_context, &send_subkey); + krb5_auth_con_setsendsubkey_k(context, auth_context, NULL); + + code = krb5_fwd_tgt_creds(context, auth_context, 0, + data->cred->name->princ, data->ctx->there->princ, + data->cred->ccache, 1, + &credmsg); + + /* Turn KRB5_AUTH_CONTEXT_DO_TIME back on and reset the send subkey. */ + krb5_auth_con_setflags(context, auth_context, con_flags); + krb5_auth_con_setsendsubkey_k(context, auth_context, send_subkey); + krb5_k_free_key(context, send_subkey); + + if (code) { + /* don't fail here; just don't accept/do the delegation + request */ + data->ctx->gss_flags &= ~(GSS_C_DELEG_FLAG | + GSS_C_DELEG_POLICY_FLAG); + + data->checksum_data.length = 24; + } else { + if (credmsg.length+28 > KRB5_INT16_MAX) { + code = KRB5KRB_ERR_FIELD_TOOLONG; + goto cleanup; + } + + data->checksum_data.length = 28+credmsg.length; + } + } else { + data->checksum_data.length = 24; + } +#ifdef CFX_EXERCISE + if (data->ctx->auth_context->keyblock != NULL + && data->ctx->auth_context->keyblock->enctype == 18) { + srand(time(0) ^ getpid()); + /* Our ftp client code stupidly assumes a base64-encoded + version of the token will fit in 10K, so don't make this + too big. */ + junk = rand() & 0xff; + } else + junk = 0; +#else + junk = 0; +#endif + + assert(data->exts != NULL); + + if (data->exts->iakerb.conv) { + krb5_key key; + + code = krb5_auth_con_getsendsubkey_k(context, auth_context, &key); + if (code != 0) + goto cleanup; + + code = iakerb_make_finished(context, key, data->exts->iakerb.conv, + &finished); + if (code != 0) { + krb5_k_free_key(context, key); + goto cleanup; + } + + krb5_k_free_key(context, key); + data->checksum_data.length += 8 + finished->length; + } + + data->checksum_data.length += junk; + + /* now allocate a buffer to hold the checksum data and + (maybe) KRB_CRED msg */ + + if ((data->checksum_data.data = + (char *) xmalloc(data->checksum_data.length)) == NULL) { + code = ENOMEM; + goto cleanup; + } + + ptr = (unsigned char *)data->checksum_data.data; + + TWRITE_INT(ptr, data->md5.length, 0); + TWRITE_STR(ptr, data->md5.contents, data->md5.length); + TWRITE_INT(ptr, data->ctx->gss_flags, 0); + + /* done with this, free it */ + xfree(data->md5.contents); + + if (credmsg.data) { + TWRITE_INT16(ptr, KRB5_GSS_FOR_CREDS_OPTION, 0); + TWRITE_INT16(ptr, credmsg.length, 0); + TWRITE_STR(ptr, credmsg.data, credmsg.length); + } + if (data->exts->iakerb.conv) { + TWRITE_INT(ptr, KRB5_GSS_EXTS_IAKERB_FINISHED, 1); + TWRITE_INT(ptr, finished->length, 1); + TWRITE_STR(ptr, finished->data, finished->length); + } + if (junk) + memset(ptr, 'i', junk); + *out = &data->checksum_data; + code = 0; +cleanup: + krb5_free_data_contents(context, &credmsg); + krb5_free_data(context, finished); + return code; +} + +static krb5_error_code +make_ap_req_v1(context, ctx, cred, k_cred, ad_context, + chan_bindings, mech_type, token, exts) + krb5_context context; + krb5_gss_ctx_id_rec *ctx; + krb5_gss_cred_id_t cred; + krb5_creds *k_cred; + krb5_authdata_context ad_context; + gss_channel_bindings_t chan_bindings; + gss_OID mech_type; + gss_buffer_t token; + krb5_gss_ctx_ext_t exts; +{ + krb5_flags mk_req_flags = 0; + krb5_error_code code; + struct gss_checksum_data cksum_struct; + krb5_checksum md5; + krb5_data ap_req; + unsigned char *ptr; + unsigned char *t; + unsigned int tlen; + + k5_mutex_assert_locked(&cred->lock); + ap_req.data = 0; + + /* compute the hash of the channel bindings */ + + if ((code = kg_checksum_channel_bindings(context, chan_bindings, &md5))) + return(code); + + krb5_auth_con_set_req_cksumtype(context, ctx->auth_context, + CKSUMTYPE_KG_CB); + cksum_struct.md5 = md5; + cksum_struct.ctx = ctx; + cksum_struct.cred = cred; + cksum_struct.checksum_data.data = NULL; + cksum_struct.exts = exts; + krb5_auth_con_set_checksum_func(context, ctx->auth_context, + make_gss_checksum, &cksum_struct); + + /* call mk_req. subkey and ap_req need to be used or destroyed */ + + mk_req_flags = AP_OPTS_USE_SUBKEY; + + if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) + mk_req_flags |= AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_ETYPE_NEGOTIATION; + + krb5_auth_con_set_authdata_context(context, ctx->auth_context, ad_context); + code = krb5_mk_req_extended(context, &ctx->auth_context, mk_req_flags, + NULL, k_cred, &ap_req); + krb5_auth_con_set_authdata_context(context, ctx->auth_context, NULL); + krb5_free_data_contents(context, &cksum_struct.checksum_data); + if (code) + goto cleanup; + + /* store the interesting stuff from creds and authent */ + ctx->krb_times = k_cred->times; + ctx->krb_flags = k_cred->ticket_flags; + + /* build up the token */ + if (ctx->gss_flags & GSS_C_DCE_STYLE) { + /* + * For DCE RPC, do not encapsulate the AP-REQ in the + * typical GSS wrapping. + */ + code = data_to_gss(&ap_req, token); + if (code) + goto cleanup; + } else { + /* allocate space for the token */ + tlen = g_token_size((gss_OID) mech_type, ap_req.length); + + if ((t = (unsigned char *) gssalloc_malloc(tlen)) == NULL) { + code = ENOMEM; + goto cleanup; + } + + /* fill in the buffer */ + ptr = t; + + g_make_token_header(mech_type, ap_req.length, + &ptr, KG_TOK_CTX_AP_REQ); + + TWRITE_STR(ptr, ap_req.data, ap_req.length); + + /* pass it back */ + + token->length = tlen; + token->value = (void *) t; + } + + code = 0; + +cleanup: + if (ap_req.data) + krb5_free_data_contents(context, &ap_req); + + return (code); +} + +/* + * new_connection + * + * Do the grunt work of setting up a new context. + */ +static OM_uint32 +kg_new_connection( + OM_uint32 *minor_status, + krb5_gss_cred_id_t cred, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + krb5_context context, + krb5_gss_ctx_ext_t exts) +{ + OM_uint32 major_status; + krb5_error_code code; + krb5_creds *k_cred = NULL; + krb5_gss_ctx_id_rec *ctx, *ctx_free; + krb5_timestamp now; + gss_buffer_desc token; + krb5_keyblock *keyblock; + + k5_mutex_assert_locked(&cred->lock); + major_status = GSS_S_FAILURE; + token.length = 0; + token.value = NULL; + + /* make sure the cred is usable for init */ + + if ((cred->usage != GSS_C_INITIATE) && + (cred->usage != GSS_C_BOTH)) { + *minor_status = 0; + return(GSS_S_NO_CRED); + } + + /* complain if the input token is non-null */ + + if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) { + *minor_status = 0; + return(GSS_S_DEFECTIVE_TOKEN); + } + + /* create the ctx */ + + if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec))) + == NULL) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + + /* fill in the ctx */ + memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec)); + ctx->magic = KG_CONTEXT; + ctx_free = ctx; + if ((code = krb5_auth_con_init(context, &ctx->auth_context))) + goto cleanup; + krb5_auth_con_setflags(context, ctx->auth_context, + KRB5_AUTH_CONTEXT_DO_SEQUENCE); + + /* limit the encryption types negotiated (if requested) */ + if (cred->req_enctypes) { + if ((code = krb5_set_default_tgs_enctypes(context, + cred->req_enctypes))) { + goto cleanup; + } + } + + ctx->initiate = 1; + ctx->seed_init = 0; + ctx->seqstate = 0; + + ctx->gss_flags = req_flags & (GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG | + GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | + GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG | + GSS_C_DCE_STYLE | GSS_C_IDENTIFY_FLAG | + GSS_C_EXTENDED_ERROR_FLAG); + ctx->gss_flags |= GSS_C_TRANS_FLAG; + if (!cred->suppress_ci_flags) + ctx->gss_flags |= (GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG); + if (req_flags & GSS_C_DCE_STYLE) + ctx->gss_flags |= GSS_C_MUTUAL_FLAG; + + if ((code = krb5_timeofday(context, &now))) + goto cleanup; + + if (time_req == 0 || time_req == GSS_C_INDEFINITE) { + ctx->krb_times.endtime = 0; + } else { + ctx->krb_times.endtime = now + time_req; + } + + if ((code = kg_duplicate_name(context, cred->name, &ctx->here))) + goto cleanup; + + if ((code = kg_duplicate_name(context, (krb5_gss_name_t)target_name, + &ctx->there))) + goto cleanup; + + code = get_credentials(context, cred, ctx->there, now, + ctx->krb_times.endtime, &k_cred); + if (code) + goto cleanup; + + ctx->krb_times = k_cred->times; + + /* + * GSS_C_DELEG_POLICY_FLAG means to delegate only if the + * ok-as-delegate ticket flag is set. + */ + if ((req_flags & GSS_C_DELEG_POLICY_FLAG) + && (k_cred->ticket_flags & TKT_FLG_OK_AS_DELEGATE)) + ctx->gss_flags |= GSS_C_DELEG_FLAG | GSS_C_DELEG_POLICY_FLAG; + + if (generic_gss_copy_oid(minor_status, mech_type, &ctx->mech_used) + != GSS_S_COMPLETE) { + code = *minor_status; + goto cleanup; + } + /* + * Now try to make it static if at all possible.... + */ + ctx->mech_used = krb5_gss_convert_static_mech_oid(ctx->mech_used); + + { + /* gsskrb5 v1 */ + krb5_int32 seq_temp; + if ((code = make_ap_req_v1(context, ctx, + cred, k_cred, ctx->here->ad_context, + input_chan_bindings, + mech_type, &token, exts))) { + if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) || + (code == KG_EMPTY_CCACHE)) + major_status = GSS_S_NO_CRED; + if (code == KRB5KRB_AP_ERR_TKT_EXPIRED) + major_status = GSS_S_CREDENTIALS_EXPIRED; + goto cleanup; + } + + krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &seq_temp); + ctx->seq_send = seq_temp; + code = krb5_auth_con_getsendsubkey(context, ctx->auth_context, + &keyblock); + if (code != 0) + goto cleanup; + code = krb5_k_create_key(context, keyblock, &ctx->subkey); + krb5_free_keyblock(context, keyblock); + if (code != 0) + goto cleanup; + } + + ctx->enc = NULL; + ctx->seq = NULL; + ctx->have_acceptor_subkey = 0; + code = kg_setup_keys(context, ctx, ctx->subkey, &ctx->cksumtype); + if (code != 0) + goto cleanup; + + if (!(ctx->gss_flags & GSS_C_MUTUAL_FLAG)) { + /* There will be no AP-REP, so set up sequence state now. */ + ctx->seq_recv = ctx->seq_send; + code = g_seqstate_init(&ctx->seqstate, ctx->seq_recv, + (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0, + (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, + ctx->proto); + if (code != 0) + goto cleanup; + } + + /* compute time_rec */ + if (time_rec) { + if ((code = krb5_timeofday(context, &now))) + goto cleanup; + *time_rec = ctx->krb_times.endtime - now; + } + + /* set the other returns */ + *output_token = token; + + if (ret_flags) + *ret_flags = ctx->gss_flags; + + if (actual_mech_type) + *actual_mech_type = mech_type; + + /* return successfully */ + + *context_handle = (gss_ctx_id_t) ctx; + ctx_free = NULL; + if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) { + ctx->established = 0; + major_status = GSS_S_CONTINUE_NEEDED; + } else { + ctx->gss_flags |= GSS_C_PROT_READY_FLAG; + ctx->established = 1; + major_status = GSS_S_COMPLETE; + } + +cleanup: + krb5_free_creds(context, k_cred); + if (ctx_free) { + if (ctx_free->auth_context) + krb5_auth_con_free(context, ctx_free->auth_context); + if (ctx_free->here) + kg_release_name(context, &ctx_free->here); + if (ctx_free->there) + kg_release_name(context, &ctx_free->there); + if (ctx_free->subkey) + krb5_k_free_key(context, ctx_free->subkey); + xfree(ctx_free); + } + + *minor_status = code; + return (major_status); +} + +/* + * mutual_auth + * + * Handle the reply from the acceptor, if we're doing mutual auth. + */ +static OM_uint32 +mutual_auth( + OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + krb5_context context) +{ + OM_uint32 major_status; + unsigned char *ptr; + char *sptr; + krb5_data ap_rep; + krb5_ap_rep_enc_part *ap_rep_data; + krb5_timestamp now; + krb5_gss_ctx_id_rec *ctx; + krb5_error *krb_error; + krb5_error_code code; + krb5int_access kaccess; + + major_status = GSS_S_FAILURE; + + code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION); + if (code) + goto fail; + + ctx = (krb5_gss_ctx_id_t) *context_handle; + + /* make sure the context is non-established, and that certain + arguments are unchanged */ + + if ((ctx->established) || + ((ctx->gss_flags & GSS_C_MUTUAL_FLAG) == 0)) { + code = KG_CONTEXT_ESTABLISHED; + goto fail; + } + + if (! kg_compare_name(context, ctx->there, (krb5_gss_name_t)target_name)) { + (void)krb5_gss_delete_sec_context(minor_status, + context_handle, NULL); + code = 0; + major_status = GSS_S_BAD_NAME; + goto fail; + } + + /* verify the token and leave the AP_REP message in ap_rep */ + + if (input_token == GSS_C_NO_BUFFER) { + (void)krb5_gss_delete_sec_context(minor_status, + context_handle, NULL); + code = 0; + major_status = GSS_S_DEFECTIVE_TOKEN; + goto fail; + } + + ptr = (unsigned char *) input_token->value; + + if (ctx->gss_flags & GSS_C_DCE_STYLE) { + /* Raw AP-REP */ + ap_rep.length = input_token->length; + ap_rep.data = (char *)input_token->value; + } else if (g_verify_token_header(ctx->mech_used, + &(ap_rep.length), + &ptr, KG_TOK_CTX_AP_REP, + input_token->length, 1)) { + if (g_verify_token_header((gss_OID) ctx->mech_used, + &(ap_rep.length), + &ptr, KG_TOK_CTX_ERROR, + input_token->length, 1) == 0) { + + /* Handle a KRB_ERROR message from the server */ + + sptr = (char *) ptr; /* PC compiler bug */ + TREAD_STR(sptr, ap_rep.data, ap_rep.length); + + code = krb5_rd_error(context, &ap_rep, &krb_error); + if (code) + goto fail; + if (krb_error->error) + code = (krb5_error_code)krb_error->error + ERROR_TABLE_BASE_krb5; + else + code = 0; + krb5_free_error(context, krb_error); + goto fail; + } else { + *minor_status = 0; + return(GSS_S_DEFECTIVE_TOKEN); + } + } + + sptr = (char *) ptr; /* PC compiler bug */ + TREAD_STR(sptr, ap_rep.data, ap_rep.length); + + /* decode the ap_rep */ + if ((code = krb5_rd_rep(context, ctx->auth_context, &ap_rep, + &ap_rep_data))) { + /* + * XXX A hack for backwards compatiblity. + * To be removed in 1999 -- proven + */ + krb5_auth_con_setuseruserkey(context, ctx->auth_context, + &ctx->subkey->keyblock); + if ((krb5_rd_rep(context, ctx->auth_context, &ap_rep, + &ap_rep_data))) + goto fail; + } + + /* store away the sequence number */ + ctx->seq_recv = ap_rep_data->seq_number; + code = g_seqstate_init(&ctx->seqstate, ctx->seq_recv, + (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0, + (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, + ctx->proto); + if (code) { + krb5_free_ap_rep_enc_part(context, ap_rep_data); + goto fail; + } + + if (ap_rep_data->subkey != NULL && + (ctx->proto == 1 || (ctx->gss_flags & GSS_C_DCE_STYLE) || + ap_rep_data->subkey->enctype != ctx->subkey->keyblock.enctype)) { + /* Keep acceptor's subkey. */ + ctx->have_acceptor_subkey = 1; + code = krb5_k_create_key(context, ap_rep_data->subkey, + &ctx->acceptor_subkey); + if (code) { + krb5_free_ap_rep_enc_part(context, ap_rep_data); + goto fail; + } + code = kg_setup_keys(context, ctx, ctx->acceptor_subkey, + &ctx->acceptor_subkey_cksumtype); + if (code) { + krb5_free_ap_rep_enc_part(context, ap_rep_data); + goto fail; + } + } + /* free the ap_rep_data */ + krb5_free_ap_rep_enc_part(context, ap_rep_data); + + if (ctx->gss_flags & GSS_C_DCE_STYLE) { + krb5_data outbuf; + + code = krb5_mk_rep_dce(context, ctx->auth_context, &outbuf); + if (code) + goto fail; + + code = data_to_gss(&outbuf, output_token); + if (code) + goto fail; + } + + /* set established */ + ctx->established = 1; + + /* set returns */ + + if (time_rec) { + if ((code = krb5_timeofday(context, &now))) + goto fail; + *time_rec = ctx->krb_times.endtime - now; + } + + if (ret_flags) + *ret_flags = ctx->gss_flags; + + if (actual_mech_type) + *actual_mech_type = mech_type; + + /* success */ + + *minor_status = 0; + return GSS_S_COMPLETE; + +fail: + (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL); + + *minor_status = code; + return (major_status); +} + +OM_uint32 +krb5_gss_init_sec_context_ext( + OM_uint32 *minor_status, + gss_cred_id_t claimant_cred_handle, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + krb5_gss_ctx_ext_t exts) +{ + krb5_context context; + gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL; + krb5_gss_cred_id_t cred; + krb5_error_code kerr; + OM_uint32 major_status; + OM_uint32 tmp_min_stat; + + if (*context_handle == GSS_C_NO_CONTEXT) { + kerr = krb5_gss_init_context(&context); + if (kerr) { + *minor_status = kerr; + return GSS_S_FAILURE; + } + if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) { + save_error_info(*minor_status, context); + krb5_free_context(context); + return GSS_S_FAILURE; + } + } else { + context = ((krb5_gss_ctx_id_rec *)*context_handle)->k5_context; + } + + /* set up return values so they can be "freed" successfully */ + + major_status = GSS_S_FAILURE; /* Default major code */ + output_token->length = 0; + output_token->value = NULL; + if (actual_mech_type) + *actual_mech_type = NULL; + + /* verify the mech_type */ + + if (mech_type == GSS_C_NULL_OID || g_OID_equal(mech_type, gss_mech_krb5)) { + mech_type = (gss_OID) gss_mech_krb5; + } else if (g_OID_equal(mech_type, gss_mech_krb5_old)) { + mech_type = (gss_OID) gss_mech_krb5_old; + } else if (g_OID_equal(mech_type, gss_mech_krb5_wrong)) { + mech_type = (gss_OID) gss_mech_krb5_wrong; + } else if (g_OID_equal(mech_type, gss_mech_iakerb)) { + mech_type = (gss_OID) gss_mech_iakerb; + } else { + *minor_status = 0; + if (*context_handle == GSS_C_NO_CONTEXT) + krb5_free_context(context); + return(GSS_S_BAD_MECH); + } + + /* is this a new connection or not? */ + + /*SUPPRESS 29*/ + if (*context_handle == GSS_C_NO_CONTEXT) { + /* verify the credential, or use the default */ + /*SUPPRESS 29*/ + if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) { + major_status = kg_get_defcred(minor_status, &defcred); + if (major_status && GSS_ERROR(major_status)) { + if (*context_handle == GSS_C_NO_CONTEXT) + krb5_free_context(context); + return(major_status); + } + claimant_cred_handle = defcred; + } + + major_status = kg_cred_resolve(minor_status, context, + claimant_cred_handle, target_name); + if (GSS_ERROR(major_status)) { + save_error_info(*minor_status, context); + krb5_gss_release_cred(&tmp_min_stat, &defcred); + if (*context_handle == GSS_C_NO_CONTEXT) + krb5_free_context(context); + return(major_status); + } + cred = (krb5_gss_cred_id_t)claimant_cred_handle; + + major_status = kg_new_connection(minor_status, cred, context_handle, + target_name, mech_type, req_flags, + time_req, input_chan_bindings, + input_token, actual_mech_type, + output_token, ret_flags, time_rec, + context, exts); + k5_mutex_unlock(&cred->lock); + krb5_gss_release_cred(&tmp_min_stat, &defcred); + if (*context_handle == GSS_C_NO_CONTEXT) { + save_error_info (*minor_status, context); + krb5_free_context(context); + } else + ((krb5_gss_ctx_id_rec *) *context_handle)->k5_context = context; + } else { + /* mutual_auth doesn't care about the credentials */ + major_status = mutual_auth(minor_status, context_handle, + target_name, mech_type, req_flags, + time_req, input_chan_bindings, + input_token, actual_mech_type, + output_token, ret_flags, time_rec, + context); + /* If context_handle is now NO_CONTEXT, mutual_auth called + delete_sec_context, which would've zapped the krb5 context + too. */ + } + + return(major_status); +} + +#ifndef _WIN32 +k5_mutex_t kg_kdc_flag_mutex = K5_MUTEX_PARTIAL_INITIALIZER; +static int kdc_flag = 0; +#endif + +krb5_error_code +krb5_gss_init_context (krb5_context *ctxp) +{ + krb5_error_code err; +#ifndef _WIN32 + int is_kdc; +#endif + + err = gss_krb5int_initialize_library(); + if (err) + return err; +#ifndef _WIN32 + k5_mutex_lock(&kg_kdc_flag_mutex); + is_kdc = kdc_flag; + k5_mutex_unlock(&kg_kdc_flag_mutex); + + if (is_kdc) + return krb5int_init_context_kdc(ctxp); +#endif + + return krb5_init_context(ctxp); +} + +#ifndef _WIN32 +OM_uint32 +krb5int_gss_use_kdc_context(OM_uint32 *minor_status, + const gss_OID desired_mech, + const gss_OID desired_object, + gss_buffer_t value) +{ + OM_uint32 err; + + *minor_status = 0; + + err = gss_krb5int_initialize_library(); + if (err) + return err; + k5_mutex_lock(&kg_kdc_flag_mutex); + kdc_flag = 1; + k5_mutex_unlock(&kg_kdc_flag_mutex); + return GSS_S_COMPLETE; +} +#endif + +OM_uint32 KRB5_CALLCONV +krb5_gss_init_sec_context(minor_status, claimant_cred_handle, + context_handle, target_name, mech_type, + req_flags, time_req, input_chan_bindings, + input_token, actual_mech_type, output_token, + ret_flags, time_rec) + OM_uint32 *minor_status; + gss_cred_id_t claimant_cred_handle; + gss_ctx_id_t *context_handle; + gss_name_t target_name; + gss_OID mech_type; + OM_uint32 req_flags; + OM_uint32 time_req; + gss_channel_bindings_t input_chan_bindings; + gss_buffer_t input_token; + gss_OID *actual_mech_type; + gss_buffer_t output_token; + OM_uint32 *ret_flags; + OM_uint32 *time_rec; +{ + krb5_gss_ctx_ext_rec exts; + + memset(&exts, 0, sizeof(exts)); + + return krb5_gss_init_sec_context_ext(minor_status, + claimant_cred_handle, + context_handle, + target_name, + mech_type, + req_flags, + time_req, + input_chan_bindings, + input_token, + actual_mech_type, + output_token, + ret_flags, + time_rec, + &exts); +} diff --git a/src/lib/gssapi/krb5/inq_context.c b/src/lib/gssapi/krb5/inq_context.c new file mode 100644 index 000000000000..9024b3c7ea9c --- /dev/null +++ b/src/lib/gssapi/krb5/inq_context.c @@ -0,0 +1,312 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/* + * Copyright (c) 2006-2008, Novell, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The copyright holder's name is not used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 2006-2008, Novell, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The copyright holder's name is not used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_context(minor_status, context_handle, initiator_name, + acceptor_name, lifetime_rec, mech_type, ret_flags, + locally_initiated, opened) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_name_t *initiator_name; + gss_name_t *acceptor_name; + OM_uint32 *lifetime_rec; + gss_OID *mech_type; + OM_uint32 *ret_flags; + int *locally_initiated; + int *opened; +{ + krb5_context context; + krb5_error_code code; + krb5_gss_ctx_id_rec *ctx; + krb5_gss_name_t initiator, acceptor; + krb5_timestamp now; + krb5_deltat lifetime; + + if (initiator_name) + *initiator_name = (gss_name_t) NULL; + if (acceptor_name) + *acceptor_name = (gss_name_t) NULL; + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + context = ctx->k5_context; + + /* RFC 2743 states that a partially completed context must return + * flags, locally_initiated, and open, and *may* return mech_type. */ + if (ctx->established) { + initiator = NULL; + acceptor = NULL; + + if ((code = krb5_timeofday(context, &now))) { + *minor_status = code; + save_error_info(*minor_status, context); + return(GSS_S_FAILURE); + } + + /* Add the maximum allowable clock skew as a grace period for context + * expiration, just as we do for the ticket during authentication. */ + lifetime = ctx->krb_times.endtime - now; + if (!ctx->initiate) + lifetime += context->clockskew; + if (lifetime < 0) + lifetime = 0; + + if (initiator_name) { + code = kg_duplicate_name(context, + ctx->initiate ? ctx->here : ctx->there, + &initiator); + if (code) { + *minor_status = code; + save_error_info(*minor_status, context); + return(GSS_S_FAILURE); + } + } + + if (acceptor_name) { + code = kg_duplicate_name(context, + ctx->initiate ? ctx->there : ctx->here, + &acceptor); + if (code) { + if (initiator) + kg_release_name(context, &initiator); + *minor_status = code; + save_error_info(*minor_status, context); + return(GSS_S_FAILURE); + } + } + + if (initiator_name) + *initiator_name = (gss_name_t) initiator; + + if (acceptor_name) + *acceptor_name = (gss_name_t) acceptor; + + if (lifetime_rec) + *lifetime_rec = lifetime; + } else { + lifetime = 0; + if (initiator_name) + *initiator_name = GSS_C_NO_NAME; + + if (acceptor_name) + *acceptor_name = GSS_C_NO_NAME; + + if (lifetime_rec) + *lifetime_rec = 0; + } + + if (mech_type) + *mech_type = (gss_OID) ctx->mech_used; + + if (ret_flags) + *ret_flags = ctx->gss_flags; + + if (locally_initiated) + *locally_initiated = ctx->initiate; + + if (opened) + *opened = ctx->established; + + *minor_status = 0; + if (ctx->established) + return((lifetime == 0)?GSS_S_CONTEXT_EXPIRED:GSS_S_COMPLETE); + else + return GSS_S_COMPLETE; +} + +OM_uint32 +gss_krb5int_inq_session_key( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + krb5_gss_ctx_id_rec *ctx; + krb5_key key; + gss_buffer_desc keyvalue, keyinfo; + OM_uint32 major_status, minor; + unsigned char oid_buf[GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH + 6]; + gss_OID_desc oid; + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + key = ctx->have_acceptor_subkey ? ctx->acceptor_subkey : ctx->subkey; + + keyvalue.value = key->keyblock.contents; + keyvalue.length = key->keyblock.length; + + major_status = generic_gss_add_buffer_set_member(minor_status, &keyvalue, data_set); + if (GSS_ERROR(major_status)) + goto cleanup; + + oid.elements = oid_buf; + oid.length = sizeof(oid_buf); + + major_status = generic_gss_oid_compose(minor_status, + GSS_KRB5_SESSION_KEY_ENCTYPE_OID, + GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH, + key->keyblock.enctype, + &oid); + if (GSS_ERROR(major_status)) + goto cleanup; + + keyinfo.value = oid.elements; + keyinfo.length = oid.length; + + major_status = generic_gss_add_buffer_set_member(minor_status, &keyinfo, data_set); + if (GSS_ERROR(major_status)) + goto cleanup; + + return GSS_S_COMPLETE; + +cleanup: + if (*data_set != GSS_C_NO_BUFFER_SET) { + if ((*data_set)->count != 0) + memset((*data_set)->elements[0].value, 0, (*data_set)->elements[0].length); + gss_release_buffer_set(&minor, data_set); + } + + return major_status; +} + +OM_uint32 +gss_krb5int_extract_authz_data_from_sec_context( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + OM_uint32 major_status; + krb5_gss_ctx_id_rec *ctx; + int ad_type = 0; + size_t i; + + *data_set = GSS_C_NO_BUFFER_SET; + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + + major_status = generic_gss_oid_decompose(minor_status, + GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID, + GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH, + desired_object, + &ad_type); + if (major_status != GSS_S_COMPLETE || ad_type == 0) { + *minor_status = ENOENT; + return GSS_S_FAILURE; + } + + if (ctx->authdata != NULL) { + for (i = 0; ctx->authdata[i] != NULL; i++) { + if (ctx->authdata[i]->ad_type == ad_type) { + gss_buffer_desc ad_data; + + ad_data.length = ctx->authdata[i]->length; + ad_data.value = ctx->authdata[i]->contents; + + major_status = generic_gss_add_buffer_set_member(minor_status, + &ad_data, data_set); + if (GSS_ERROR(major_status)) + break; + } + } + } + + if (GSS_ERROR(major_status)) { + OM_uint32 tmp; + + generic_gss_release_buffer_set(&tmp, data_set); + } + + return major_status; +} + +OM_uint32 +gss_krb5int_extract_authtime_from_sec_context(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_oid, + gss_buffer_set_t *data_set) +{ + krb5_gss_ctx_id_rec *ctx; + gss_buffer_desc rep; + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + + rep.value = &ctx->krb_times.authtime; + rep.length = sizeof(ctx->krb_times.authtime); + + return generic_gss_add_buffer_set_member(minor_status, &rep, data_set); +} diff --git a/src/lib/gssapi/krb5/inq_cred.c b/src/lib/gssapi/krb5/inq_cred.c new file mode 100644 index 000000000000..4e35a056316f --- /dev/null +++ b/src/lib/gssapi/krb5/inq_cred.c @@ -0,0 +1,247 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2000, 2007 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. + */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * 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 FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret, + cred_usage, mechanisms) + OM_uint32 *minor_status; + gss_cred_id_t cred_handle; + gss_name_t *name; + OM_uint32 *lifetime_ret; + gss_cred_usage_t *cred_usage; + gss_OID_set *mechanisms; +{ + krb5_context context; + gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL; + krb5_gss_cred_id_t cred = NULL; + krb5_error_code code; + krb5_timestamp now; + krb5_deltat lifetime; + krb5_gss_name_t ret_name; + krb5_principal princ; + gss_OID_set mechs; + OM_uint32 major, tmpmin, ret; + + ret = GSS_S_FAILURE; + ret_name = NULL; + + code = krb5_gss_init_context(&context); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } + + if (name) *name = NULL; + if (mechanisms) *mechanisms = NULL; + + /* check for default credential */ + /*SUPPRESS 29*/ + if (cred_handle == GSS_C_NO_CREDENTIAL) { + major = kg_get_defcred(minor_status, &defcred); + if (GSS_ERROR(major)) { + krb5_free_context(context); + return(major); + } + cred_handle = defcred; + } + + major = kg_cred_resolve(minor_status, context, cred_handle, GSS_C_NO_NAME); + if (GSS_ERROR(major)) { + krb5_gss_release_cred(minor_status, &defcred); + krb5_free_context(context); + return(major); + } + cred = (krb5_gss_cred_id_t)cred_handle; + + if ((code = krb5_timeofday(context, &now))) { + *minor_status = code; + ret = GSS_S_FAILURE; + goto fail; + } + + if (cred->expire > 0) { + if ((lifetime = cred->expire - now) < 0) + lifetime = 0; + } + else + lifetime = GSS_C_INDEFINITE; + + if (name) { + if (cred->name) { + code = kg_duplicate_name(context, cred->name, &ret_name); + } else if ((cred->usage == GSS_C_ACCEPT || cred->usage == GSS_C_BOTH) + && cred->keytab != NULL) { + /* This is a default acceptor cred; use a name from the keytab if + * we can. */ + code = k5_kt_get_principal(context, cred->keytab, &princ); + if (code == 0) { + code = kg_init_name(context, princ, NULL, NULL, NULL, + KG_INIT_NAME_NO_COPY, &ret_name); + if (code) + krb5_free_principal(context, princ); + } else if (code == KRB5_KT_NOTFOUND) + code = 0; + } + if (code) { + *minor_status = code; + save_error_info(*minor_status, context); + ret = GSS_S_FAILURE; + goto fail; + } + } + + if (mechanisms) { + if (GSS_ERROR(ret = generic_gss_create_empty_oid_set(minor_status, + &mechs)) || + GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status, + gss_mech_krb5_old, + &mechs)) || + GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status, + gss_mech_krb5, + &mechs))) { + if (ret_name) + kg_release_name(context, &ret_name); + /* *minor_status set above */ + goto fail; + } + } + + if (name) { + if (ret_name != NULL) + *name = (gss_name_t) ret_name; + else + *name = GSS_C_NO_NAME; + } + + if (lifetime_ret) + *lifetime_ret = lifetime; + + if (cred_usage) + *cred_usage = cred->usage; + k5_mutex_unlock(&cred->lock); + + if (mechanisms) + *mechanisms = mechs; + + if (cred_handle == GSS_C_NO_CREDENTIAL) + krb5_gss_release_cred(minor_status, (gss_cred_id_t *)&cred); + + krb5_free_context(context); + *minor_status = 0; + return((lifetime == 0)?GSS_S_CREDENTIALS_EXPIRED:GSS_S_COMPLETE); +fail: + k5_mutex_unlock(&cred->lock); + krb5_gss_release_cred(&tmpmin, &defcred); + krb5_free_context(context); + return ret; +} + +/* V2 interface */ +OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_cred_by_mech(minor_status, cred_handle, + mech_type, name, initiator_lifetime, + acceptor_lifetime, cred_usage) + OM_uint32 *minor_status; + gss_cred_id_t cred_handle; + gss_OID mech_type; + gss_name_t *name; + OM_uint32 *initiator_lifetime; + OM_uint32 *acceptor_lifetime; + gss_cred_usage_t *cred_usage; +{ + krb5_gss_cred_id_t cred; + OM_uint32 lifetime; + OM_uint32 mstat; + + cred = (krb5_gss_cred_id_t) cred_handle; + mstat = krb5_gss_inquire_cred(minor_status, + cred_handle, + name, + &lifetime, + cred_usage, + (gss_OID_set *) NULL); + if (mstat == GSS_S_COMPLETE) { + if (cred && + ((cred->usage == GSS_C_INITIATE) || + (cred->usage == GSS_C_BOTH)) && + initiator_lifetime) + *initiator_lifetime = lifetime; + if (cred && + ((cred->usage == GSS_C_ACCEPT) || + (cred->usage == GSS_C_BOTH)) && + acceptor_lifetime) + *acceptor_lifetime = lifetime; + } + return(mstat); +} diff --git a/src/lib/gssapi/krb5/inq_names.c b/src/lib/gssapi/krb5/inq_names.c new file mode 100644 index 000000000000..b326adbb5f23 --- /dev/null +++ b/src/lib/gssapi/krb5/inq_names.c @@ -0,0 +1,100 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/inq_names.c - Return nametypes supported by krb5 mech */ +/* + * Copyright 1995 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 "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_names_for_mech(minor_status, mechanism, name_types) + OM_uint32 *minor_status; + gss_OID mechanism; + gss_OID_set *name_types; +{ + OM_uint32 major, minor; + + /* + * We only know how to handle our own mechanism. + */ + if ((mechanism != GSS_C_NULL_OID) && + !g_OID_equal(gss_mech_krb5, mechanism) && + !g_OID_equal(gss_mech_krb5_old, mechanism) && + !g_OID_equal(gss_mech_krb5_wrong, mechanism) && + !g_OID_equal(gss_mech_iakerb, mechanism)) { + *minor_status = 0; + return(GSS_S_BAD_MECH); + } + + /* We're okay. Create an empty OID set */ + major = generic_gss_create_empty_oid_set(minor_status, name_types); + if (major == GSS_S_COMPLETE) { + /* Now add our members. */ + if ( + ((major = generic_gss_add_oid_set_member(minor_status, + gss_nt_user_name, + name_types) + ) == GSS_S_COMPLETE) && + ((major = generic_gss_add_oid_set_member(minor_status, + gss_nt_machine_uid_name, + name_types) + ) == GSS_S_COMPLETE) && + ((major = generic_gss_add_oid_set_member(minor_status, + gss_nt_string_uid_name, + name_types) + ) == GSS_S_COMPLETE) && + ((major = generic_gss_add_oid_set_member(minor_status, + gss_nt_service_name, + name_types) + ) == GSS_S_COMPLETE) && + ((major = generic_gss_add_oid_set_member(minor_status, + gss_nt_service_name_v2, + name_types) + ) == GSS_S_COMPLETE) && + ((major = generic_gss_add_oid_set_member(minor_status, + gss_nt_exported_name, + name_types) + ) == GSS_S_COMPLETE) && + ((major = generic_gss_add_oid_set_member(minor_status, + gss_nt_krb5_name, + name_types) + ) == GSS_S_COMPLETE) && + ((major = generic_gss_add_oid_set_member(minor_status, + GSS_C_NT_COMPOSITE_EXPORT, + name_types) + ) == GSS_S_COMPLETE) + ) { + major = generic_gss_add_oid_set_member(minor_status, + gss_nt_krb5_principal, + name_types); + } + + /* + * If we choked, then release the set, but don't overwrite the minor + * status with the release call. + */ + if (major != GSS_S_COMPLETE) + (void) generic_gss_release_oid_set(&minor, name_types); + } + return(major); +} diff --git a/src/lib/gssapi/krb5/k5seal.c b/src/lib/gssapi/krb5/k5seal.c new file mode 100644 index 000000000000..4da531b58245 --- /dev/null +++ b/src/lib/gssapi/krb5/k5seal.c @@ -0,0 +1,413 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * 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 FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "gssapiP_krb5.h" + +#include <assert.h> + +static krb5_error_code +make_seal_token_v1 (krb5_context context, + krb5_key enc, + krb5_key seq, + uint64_t *seqnum, + int direction, + gss_buffer_t text, + gss_buffer_t token, + int signalg, + size_t cksum_size, + int sealalg, + int do_encrypt, + int toktype, + gss_OID oid) +{ + krb5_error_code code; + size_t sumlen; + char *data_ptr; + krb5_data plaind; + krb5_checksum md5cksum; + krb5_checksum cksum; + /* msglen contains the message length + * we are signing/encrypting. tmsglen + * contains the length of the message + * we plan to write out to the token. + * tlen is the length of the token + * including header. */ + unsigned int conflen=0, tmsglen, tlen, msglen; + unsigned char *t, *ptr; + unsigned char *plain; + unsigned char pad; + krb5_keyusage sign_usage = KG_USAGE_SIGN; + + + assert((!do_encrypt) || (toktype == KG_TOK_SEAL_MSG)); + /* create the token buffer */ + /* Do we need confounder? */ + if (do_encrypt || toktype == KG_TOK_SEAL_MSG) + conflen = kg_confounder_size(context, enc->keyblock.enctype); + else conflen = 0; + + if (toktype == KG_TOK_SEAL_MSG) { + switch (sealalg) { + case SEAL_ALG_MICROSOFT_RC4: + msglen = conflen + text->length+1; + pad = 1; + break; + default: + /* XXX knows that des block size is 8 */ + msglen = (conflen+text->length+8)&(~7); + pad = 8-(text->length%8); + } + tmsglen = msglen; + } else { + tmsglen = 0; + msglen = text->length; + pad = 0; + } + tlen = g_token_size((gss_OID) oid, 14+cksum_size+tmsglen); + + if ((t = (unsigned char *) gssalloc_malloc(tlen)) == NULL) + return(ENOMEM); + + /*** fill in the token */ + + ptr = t; + g_make_token_header(oid, 14+cksum_size+tmsglen, &ptr, toktype); + + /* 0..1 SIGN_ALG */ + store_16_le(signalg, &ptr[0]); + + /* 2..3 SEAL_ALG or Filler */ + if ((toktype == KG_TOK_SEAL_MSG) && do_encrypt) { + store_16_le(sealalg, &ptr[2]); + } else { + /* No seal */ + ptr[2] = 0xff; + ptr[3] = 0xff; + } + + /* 4..5 Filler */ + ptr[4] = 0xff; + ptr[5] = 0xff; + + /* pad the plaintext, encrypt if needed, and stick it in the token */ + + /* initialize the the cksum */ + switch (signalg) { + case SGN_ALG_DES_MAC_MD5: + case SGN_ALG_MD2_5: + md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; + break; + case SGN_ALG_HMAC_SHA1_DES3_KD: + md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3; + break; + case SGN_ALG_HMAC_MD5: + md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR; + if (toktype != KG_TOK_SEAL_MSG) + sign_usage = 15; + break; + default: + case SGN_ALG_DES_MAC: + abort (); + } + + code = krb5_c_checksum_length(context, md5cksum.checksum_type, &sumlen); + if (code) { + gssalloc_free(t); + return(code); + } + md5cksum.length = sumlen; + + + if ((plain = (unsigned char *) xmalloc(msglen ? msglen : 1)) == NULL) { + gssalloc_free(t); + return(ENOMEM); + } + + if (conflen) { + if ((code = kg_make_confounder(context, enc->keyblock.enctype, + plain))) { + xfree(plain); + gssalloc_free(t); + return(code); + } + } + + memcpy(plain+conflen, text->value, text->length); + if (pad) memset(plain+conflen+text->length, pad, pad); + + /* compute the checksum */ + + /* 8 = head of token body as specified by mech spec */ + if (! (data_ptr = xmalloc(8 + msglen))) { + xfree(plain); + gssalloc_free(t); + return(ENOMEM); + } + (void) memcpy(data_ptr, ptr-2, 8); + (void) memcpy(data_ptr+8, plain, msglen); + plaind.length = 8 + msglen; + plaind.data = data_ptr; + code = krb5_k_make_checksum(context, md5cksum.checksum_type, seq, + sign_usage, &plaind, &md5cksum); + xfree(data_ptr); + + if (code) { + xfree(plain); + gssalloc_free(t); + return(code); + } + switch(signalg) { + case SGN_ALG_DES_MAC_MD5: + case 3: + + code = kg_encrypt_inplace(context, seq, KG_USAGE_SEAL, + (g_OID_equal(oid, gss_mech_krb5_old) ? + seq->keyblock.contents : NULL), + md5cksum.contents, 16); + if (code) { + krb5_free_checksum_contents(context, &md5cksum); + xfree (plain); + gssalloc_free(t); + return code; + } + + cksum.length = cksum_size; + cksum.contents = md5cksum.contents + 16 - cksum.length; + + memcpy(ptr+14, cksum.contents, cksum.length); + break; + + case SGN_ALG_HMAC_SHA1_DES3_KD: + /* + * Using key derivation, the call to krb5_c_make_checksum + * already dealt with encrypting. + */ + if (md5cksum.length != cksum_size) + abort (); + memcpy (ptr+14, md5cksum.contents, md5cksum.length); + break; + case SGN_ALG_HMAC_MD5: + memcpy (ptr+14, md5cksum.contents, cksum_size); + break; + } + + krb5_free_checksum_contents(context, &md5cksum); + + /* create the seq_num */ + + if ((code = kg_make_seq_num(context, seq, direction?0:0xff, + (krb5_ui_4)*seqnum, ptr+14, ptr+6))) { + xfree (plain); + gssalloc_free(t); + return(code); + } + + if (do_encrypt) { + switch(sealalg) { + case SEAL_ALG_MICROSOFT_RC4: + { + unsigned char bigend_seqnum[4]; + krb5_keyblock *enc_key; + int i; + store_32_be(*seqnum, bigend_seqnum); + code = krb5_k_key_keyblock(context, enc, &enc_key); + if (code) + { + xfree(plain); + gssalloc_free(t); + return(code); + } + assert (enc_key->length == 16); + for (i = 0; i <= 15; i++) + ((char *) enc_key->contents)[i] ^=0xf0; + code = kg_arcfour_docrypt (enc_key, 0, + bigend_seqnum, 4, + plain, tmsglen, + ptr+14+cksum_size); + krb5_free_keyblock (context, enc_key); + if (code) + { + xfree(plain); + gssalloc_free(t); + return(code); + } + } + break; + default: + if ((code = kg_encrypt(context, enc, KG_USAGE_SEAL, NULL, + (krb5_pointer) plain, + (krb5_pointer) (ptr+cksum_size+14), + tmsglen))) { + xfree(plain); + gssalloc_free(t); + return(code); + } + } + }else { + if (tmsglen) + memcpy(ptr+14+cksum_size, plain, tmsglen); + } + xfree(plain); + + + /* that's it. return the token */ + + (*seqnum)++; + *seqnum &= 0xffffffffL; + + token->length = tlen; + token->value = (void *) t; + + return(0); +} + +/* if signonly is true, ignore conf_req, conf_state, + and do not encode the ENC_TYPE, MSG_LENGTH, or MSG_TEXT fields */ + +OM_uint32 +kg_seal(minor_status, context_handle, conf_req_flag, qop_req, + input_message_buffer, conf_state, output_message_buffer, toktype) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + int conf_req_flag; + gss_qop_t qop_req; + gss_buffer_t input_message_buffer; + int *conf_state; + gss_buffer_t output_message_buffer; + int toktype; +{ + krb5_gss_ctx_id_rec *ctx; + krb5_error_code code; + krb5_context context; + + output_message_buffer->length = 0; + output_message_buffer->value = NULL; + + /* Only default qop or matching established cryptosystem is allowed. + + There are NO EXTENSIONS to this set for AES and friends! The + new spec says "just use 0". The old spec plus extensions would + actually allow for certain non-zero values. Fix this to handle + them later. */ + if (qop_req != 0) { + *minor_status = (OM_uint32) G_UNKNOWN_QOP; + return GSS_S_BAD_QOP; + } + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + + if (ctx->terminated || !ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return(GSS_S_NO_CONTEXT); + } + + context = ctx->k5_context; + switch (ctx->proto) + { + case 0: + code = make_seal_token_v1(context, ctx->enc, ctx->seq, + &ctx->seq_send, ctx->initiate, + input_message_buffer, output_message_buffer, + ctx->signalg, ctx->cksum_size, ctx->sealalg, + conf_req_flag, toktype, ctx->mech_used); + break; + case 1: + code = gss_krb5int_make_seal_token_v3(context, ctx, + input_message_buffer, + output_message_buffer, + conf_req_flag, toktype); + break; + default: + code = G_UNKNOWN_QOP; /* XXX */ + break; + } + + if (code) { + *minor_status = code; + save_error_info(*minor_status, context); + return(GSS_S_FAILURE); + } + + if (conf_state) + *conf_state = conf_req_flag; + + *minor_status = 0; + return(GSS_S_COMPLETE); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_wrap(minor_status, context_handle, conf_req_flag, + qop_req, input_message_buffer, conf_state, + output_message_buffer) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + int conf_req_flag; + gss_qop_t qop_req; + gss_buffer_t input_message_buffer; + int *conf_state; + gss_buffer_t output_message_buffer; +{ + return(kg_seal(minor_status, context_handle, conf_req_flag, + qop_req, input_message_buffer, conf_state, + output_message_buffer, KG_TOK_WRAP_MSG)); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_get_mic(minor_status, context_handle, qop_req, + message_buffer, message_token) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_qop_t qop_req; + gss_buffer_t message_buffer; + gss_buffer_t message_token; +{ + return(kg_seal(minor_status, context_handle, 0, + qop_req, message_buffer, NULL, + message_token, KG_TOK_MIC_MSG)); +} diff --git a/src/lib/gssapi/krb5/k5sealiov.c b/src/lib/gssapi/krb5/k5sealiov.c new file mode 100644 index 000000000000..88caa856f86a --- /dev/null +++ b/src/lib/gssapi/krb5/k5sealiov.c @@ -0,0 +1,557 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/k5sealiov.c */ +/* + * Copyright 2008, 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" + +static krb5_error_code +make_seal_token_v1_iov(krb5_context context, + krb5_gss_ctx_id_rec *ctx, + int conf_req_flag, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype) +{ + krb5_error_code code = 0; + gss_iov_buffer_t header; + gss_iov_buffer_t padding; + gss_iov_buffer_t trailer; + krb5_checksum md5cksum; + krb5_checksum cksum; + size_t k5_headerlen = 0, k5_trailerlen = 0; + size_t data_length = 0, assoc_data_length = 0; + size_t tmsglen = 0, tlen; + unsigned char *ptr; + krb5_keyusage sign_usage = KG_USAGE_SIGN; + + md5cksum.length = cksum.length = 0; + md5cksum.contents = cksum.contents = NULL; + + header = kg_locate_header_iov(iov, iov_count, toktype); + if (header == NULL) + return EINVAL; + + padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); + if (padding == NULL && toktype == KG_TOK_WRAP_MSG && + (ctx->gss_flags & GSS_C_DCE_STYLE) == 0) + return EINVAL; + + trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + if (trailer != NULL) + trailer->buffer.length = 0; + + /* Determine confounder length */ + if (toktype == KG_TOK_WRAP_MSG || conf_req_flag) + k5_headerlen = kg_confounder_size(context, ctx->enc->keyblock.enctype); + + /* Check padding length */ + if (toktype == KG_TOK_WRAP_MSG) { + size_t k5_padlen = (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) ? 1 : 8; + size_t gss_padlen; + size_t conf_data_length; + + kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); + conf_data_length = k5_headerlen + data_length - assoc_data_length; + + if (k5_padlen == 1) + gss_padlen = 1; /* one byte to indicate one byte of padding */ + else + gss_padlen = k5_padlen - (conf_data_length % k5_padlen); + + if (ctx->gss_flags & GSS_C_DCE_STYLE) { + /* DCE will pad the actual data itself; padding buffer optional and will be zeroed */ + gss_padlen = 0; + + if (conf_data_length % k5_padlen) + code = KRB5_BAD_MSIZE; + } else if (padding->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) { + code = kg_allocate_iov(padding, gss_padlen); + } else if (padding->buffer.length < gss_padlen) { + code = KRB5_BAD_MSIZE; + } + if (code != 0) + goto cleanup; + + /* Initialize padding buffer to pad itself */ + if (padding != NULL) { + padding->buffer.length = gss_padlen; + memset(padding->buffer.value, (int)gss_padlen, gss_padlen); + } + + if (ctx->gss_flags & GSS_C_DCE_STYLE) + tmsglen = k5_headerlen; /* confounder length */ + else + tmsglen = conf_data_length + padding->buffer.length; + } + + /* Determine token size */ + tlen = g_token_size(ctx->mech_used, 14 + ctx->cksum_size + tmsglen); + + k5_headerlen += tlen - tmsglen; + + if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) + code = kg_allocate_iov(header, k5_headerlen); + else if (header->buffer.length < k5_headerlen) + code = KRB5_BAD_MSIZE; + if (code != 0) + goto cleanup; + + header->buffer.length = k5_headerlen; + + ptr = (unsigned char *)header->buffer.value; + g_make_token_header(ctx->mech_used, 14 + ctx->cksum_size + tmsglen, &ptr, toktype); + + /* 0..1 SIGN_ALG */ + store_16_le(ctx->signalg, &ptr[0]); + + /* 2..3 SEAL_ALG or Filler */ + if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) { + store_16_le(ctx->sealalg, &ptr[2]); + } else { + /* No seal */ + ptr[2] = 0xFF; + ptr[3] = 0xFF; + } + + /* 4..5 Filler */ + ptr[4] = 0xFF; + ptr[5] = 0xFF; + + /* pad the plaintext, encrypt if needed, and stick it in the token */ + + /* initialize the checksum */ + switch (ctx->signalg) { + case SGN_ALG_DES_MAC_MD5: + case SGN_ALG_MD2_5: + md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; + break; + case SGN_ALG_HMAC_SHA1_DES3_KD: + md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3; + break; + case SGN_ALG_HMAC_MD5: + md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR; + if (toktype != KG_TOK_WRAP_MSG) + sign_usage = 15; + break; + default: + case SGN_ALG_DES_MAC: + abort (); + } + + code = krb5_c_checksum_length(context, md5cksum.checksum_type, &k5_trailerlen); + if (code != 0) + goto cleanup; + md5cksum.length = k5_trailerlen; + + if (k5_headerlen != 0 && toktype == KG_TOK_WRAP_MSG) { + code = kg_make_confounder(context, ctx->enc->keyblock.enctype, + ptr + 14 + ctx->cksum_size); + if (code != 0) + goto cleanup; + } + + /* compute the checksum */ + code = kg_make_checksum_iov_v1(context, md5cksum.checksum_type, + ctx->cksum_size, ctx->seq, ctx->enc, + sign_usage, iov, iov_count, toktype, + &md5cksum); + if (code != 0) + goto cleanup; + + switch (ctx->signalg) { + case SGN_ALG_DES_MAC_MD5: + case SGN_ALG_3: + code = kg_encrypt_inplace(context, ctx->seq, KG_USAGE_SEAL, + (g_OID_equal(ctx->mech_used, + gss_mech_krb5_old) ? + ctx->seq->keyblock.contents : NULL), + md5cksum.contents, 16); + if (code != 0) + goto cleanup; + + cksum.length = ctx->cksum_size; + cksum.contents = md5cksum.contents + 16 - cksum.length; + + memcpy(ptr + 14, cksum.contents, cksum.length); + break; + case SGN_ALG_HMAC_SHA1_DES3_KD: + assert(md5cksum.length == ctx->cksum_size); + memcpy(ptr + 14, md5cksum.contents, md5cksum.length); + break; + case SGN_ALG_HMAC_MD5: + memcpy(ptr + 14, md5cksum.contents, ctx->cksum_size); + break; + } + + /* create the seq_num */ + code = kg_make_seq_num(context, ctx->seq, ctx->initiate ? 0 : 0xFF, + (OM_uint32)ctx->seq_send, ptr + 14, ptr + 6); + if (code != 0) + goto cleanup; + + if (conf_req_flag) { + if (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) { + unsigned char bigend_seqnum[4]; + krb5_keyblock *enc_key; + size_t i; + + store_32_be(ctx->seq_send, bigend_seqnum); + + code = krb5_k_key_keyblock(context, ctx->enc, &enc_key); + if (code != 0) + goto cleanup; + + assert(enc_key->length == 16); + + for (i = 0; i < enc_key->length; i++) + ((char *)enc_key->contents)[i] ^= 0xF0; + + code = kg_arcfour_docrypt_iov(context, enc_key, 0, + bigend_seqnum, 4, + iov, iov_count); + krb5_free_keyblock(context, enc_key); + } else { + code = kg_encrypt_iov(context, ctx->proto, + ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0), + 0 /*EC*/, 0 /*RRC*/, + ctx->enc, KG_USAGE_SEAL, NULL, + iov, iov_count); + } + if (code != 0) + goto cleanup; + } + + ctx->seq_send++; + ctx->seq_send &= 0xFFFFFFFFL; + + code = 0; + + if (conf_state != NULL) + *conf_state = conf_req_flag; + +cleanup: + if (code != 0) + kg_release_iov(iov, iov_count); + krb5_free_checksum_contents(context, &md5cksum); + + return code; +} + +OM_uint32 +kg_seal_iov(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype) +{ + krb5_gss_ctx_id_rec *ctx; + krb5_error_code code; + krb5_context context; + + if (qop_req != 0) { + *minor_status = (OM_uint32)G_UNKNOWN_QOP; + return GSS_S_BAD_QOP; + } + + ctx = (krb5_gss_ctx_id_rec *)context_handle; + if (ctx->terminated || !ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return GSS_S_NO_CONTEXT; + } + + if (conf_req_flag && kg_integ_only_iov(iov, iov_count)) { + /* may be more sensible to return an error here */ + conf_req_flag = FALSE; + } + + context = ctx->k5_context; + switch (ctx->proto) { + case 0: + code = make_seal_token_v1_iov(context, ctx, conf_req_flag, + conf_state, iov, iov_count, toktype); + break; + case 1: + code = gss_krb5int_make_seal_token_v3_iov(context, ctx, conf_req_flag, + conf_state, iov, iov_count, toktype); + break; + default: + code = G_UNKNOWN_QOP; + break; + } + + if (code != 0) { + *minor_status = code; + save_error_info(*minor_status, context); + return GSS_S_FAILURE; + } + + *minor_status = 0; + + return GSS_S_COMPLETE; +} + +#define INIT_IOV_DATA(_iov) do { (_iov)->buffer.value = NULL; \ + (_iov)->buffer.length = 0; } \ + while (0) + +OM_uint32 +kg_seal_iov_length(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype) +{ + krb5_gss_ctx_id_rec *ctx; + gss_iov_buffer_t header, trailer, padding; + size_t data_length, assoc_data_length; + size_t gss_headerlen, gss_padlen, gss_trailerlen; + unsigned int k5_headerlen = 0, k5_trailerlen = 0, k5_padlen = 0; + krb5_error_code code; + krb5_context context; + int dce_or_mic; + + if (qop_req != GSS_C_QOP_DEFAULT) { + *minor_status = (OM_uint32)G_UNKNOWN_QOP; + return GSS_S_BAD_QOP; + } + + ctx = (krb5_gss_ctx_id_rec *)context_handle; + if (!ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return GSS_S_NO_CONTEXT; + } + + header = kg_locate_header_iov(iov, iov_count, toktype); + if (header == NULL) { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + INIT_IOV_DATA(header); + + trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + if (trailer != NULL) { + INIT_IOV_DATA(trailer); + } + + /* MIC tokens and DCE-style wrap tokens have similar length considerations: + * no padding, and the framing surrounds the header only, not the data. */ + dce_or_mic = ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0 || + toktype == KG_TOK_MIC_MSG); + + /* For CFX, EC is used instead of padding, and is placed in header or trailer */ + padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); + if (padding == NULL) { + if (conf_req_flag && ctx->proto == 0 && !dce_or_mic) { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + } else { + INIT_IOV_DATA(padding); + } + + kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); + + if (conf_req_flag && kg_integ_only_iov(iov, iov_count)) + conf_req_flag = FALSE; + + context = ctx->k5_context; + + gss_headerlen = gss_padlen = gss_trailerlen = 0; + + if (ctx->proto == 1) { + krb5_key key; + krb5_enctype enctype; + size_t ec; + + key = (ctx->have_acceptor_subkey) ? ctx->acceptor_subkey : ctx->subkey; + enctype = key->keyblock.enctype; + + code = krb5_c_crypto_length(context, enctype, + conf_req_flag ? + KRB5_CRYPTO_TYPE_TRAILER : KRB5_CRYPTO_TYPE_CHECKSUM, + &k5_trailerlen); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + if (conf_req_flag) { + code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + } + + gss_headerlen = 16; /* Header */ + if (conf_req_flag) { + gss_headerlen += k5_headerlen; /* Kerb-Header */ + gss_trailerlen = 16 /* E(Header) */ + k5_trailerlen; /* Kerb-Trailer */ + + code = krb5_c_padding_length(context, enctype, + data_length - assoc_data_length + 16 /* E(Header) */, &k5_padlen); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + if (k5_padlen == 0 && dce_or_mic) { + /* Windows rejects AEAD tokens with non-zero EC */ + code = krb5_c_block_size(context, enctype, &ec); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + } else + ec = k5_padlen; + + gss_trailerlen += ec; + } else { + gss_trailerlen = k5_trailerlen; /* Kerb-Checksum */ + } + } else if (!dce_or_mic) { + k5_padlen = (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) ? 1 : 8; + + if (k5_padlen == 1) + gss_padlen = 1; + else + gss_padlen = k5_padlen - ((data_length - assoc_data_length) % k5_padlen); + } + + data_length += gss_padlen; + + if (ctx->proto == 0) { + /* Header | Checksum | Confounder | Data | Pad */ + size_t data_size; + + k5_headerlen = kg_confounder_size(context, ctx->enc->keyblock.enctype); + + data_size = 14 /* Header */ + ctx->cksum_size + k5_headerlen; + + if (!dce_or_mic) + data_size += data_length; + + gss_headerlen = g_token_size(ctx->mech_used, data_size); + + /* g_token_size() will include data_size as well as the overhead, so + * subtract data_length just to get the overhead (ie. token size) */ + if (!dce_or_mic) + gss_headerlen -= data_length; + } + + if (minor_status != NULL) + *minor_status = 0; + + if (trailer == NULL) + gss_headerlen += gss_trailerlen; + else + trailer->buffer.length = gss_trailerlen; + + assert(gss_padlen == 0 || padding != NULL); + + if (padding != NULL) + padding->buffer.length = gss_padlen; + + header->buffer.length = gss_headerlen; + + if (conf_state != NULL) + *conf_state = conf_req_flag; + + return GSS_S_COMPLETE; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_wrap_iov(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 major_status; + + major_status = kg_seal_iov(minor_status, context_handle, conf_req_flag, + qop_req, conf_state, + iov, iov_count, KG_TOK_WRAP_MSG); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_wrap_iov_length(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 major_status; + + major_status = kg_seal_iov_length(minor_status, context_handle, + conf_req_flag, qop_req, conf_state, iov, + iov_count, KG_TOK_WRAP_MSG); + return major_status; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_get_mic_iov(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_qop_t qop_req, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 major_status; + + major_status = kg_seal_iov(minor_status, context_handle, FALSE, + qop_req, NULL, + iov, iov_count, KG_TOK_MIC_MSG); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_get_mic_iov_length(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_qop_t qop_req, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 major_status; + + major_status = kg_seal_iov_length(minor_status, context_handle, FALSE, + qop_req, NULL, iov, iov_count, + KG_TOK_MIC_MSG); + return major_status; +} diff --git a/src/lib/gssapi/krb5/k5sealv3.c b/src/lib/gssapi/krb5/k5sealv3.c new file mode 100644 index 000000000000..1a5c14c2713b --- /dev/null +++ b/src/lib/gssapi/krb5/k5sealv3.c @@ -0,0 +1,512 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/k5sealv3.c */ +/* + * Copyright 2003,2004,2007 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. + */ + +/* draft-ietf-krb-wg-gssapi-cfx-05 */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" + +int +gss_krb5int_rotate_left (void *ptr, size_t bufsiz, size_t rc) +{ + /* Optimize for receiving. After some debugging is done, the MIT + implementation won't do any rotates on sending, and while + debugging, they'll be randomly chosen. + + Return 1 for success, 0 for failure (ENOMEM). */ + void *tbuf; + + if (bufsiz == 0) + return 1; + rc = rc % bufsiz; + if (rc == 0) + return 1; + + tbuf = malloc(rc); + if (tbuf == 0) + return 0; + memcpy(tbuf, ptr, rc); + memmove(ptr, (char *)ptr + rc, bufsiz - rc); + memcpy((char *)ptr + bufsiz - rc, tbuf, rc); + free(tbuf); + return 1; +} + +static const gss_buffer_desc empty_message = { 0, 0 }; + +krb5_error_code +gss_krb5int_make_seal_token_v3 (krb5_context context, + krb5_gss_ctx_id_rec *ctx, + const gss_buffer_desc * message, + gss_buffer_t token, + int conf_req_flag, int toktype) +{ + size_t bufsize = 16; + unsigned char *outbuf = 0; + krb5_error_code err; + int key_usage; + unsigned char acceptor_flag; + const gss_buffer_desc *message2 = message; +#ifdef CFX_EXERCISE + size_t rrc; +#endif + size_t ec; + unsigned short tok_id; + krb5_checksum sum; + krb5_key key; + krb5_cksumtype cksumtype; + + acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR; + key_usage = (toktype == KG_TOK_WRAP_MSG + ? (ctx->initiate + ? KG_USAGE_INITIATOR_SEAL + : KG_USAGE_ACCEPTOR_SEAL) + : (ctx->initiate + ? KG_USAGE_INITIATOR_SIGN + : KG_USAGE_ACCEPTOR_SIGN)); + if (ctx->have_acceptor_subkey) { + key = ctx->acceptor_subkey; + cksumtype = ctx->acceptor_subkey_cksumtype; + } else { + key = ctx->subkey; + cksumtype = ctx->cksumtype; + } + assert(key != NULL); + +#ifdef CFX_EXERCISE + { + static int initialized = 0; + if (!initialized) { + srand(time(0)); + initialized = 1; + } + } +#endif + + if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) { + krb5_data plain; + krb5_enc_data cipher; + size_t ec_max; + + /* 300: Adds some slop. */ + if (SIZE_MAX - 300 < message->length) + return ENOMEM; + ec_max = SIZE_MAX - message->length - 300; + if (ec_max > 0xffff) + ec_max = 0xffff; +#ifdef CFX_EXERCISE + /* For testing only. For performance, always set ec = 0. */ + ec = ec_max & rand(); +#else + ec = 0; +#endif + err = alloc_data(&plain, message->length + 16 + ec); + if (err) + return err; + + /* Get size of ciphertext. */ + bufsize = 16 + krb5_encrypt_size (plain.length, key->keyblock.enctype); + /* Allocate space for header plus encrypted data. */ + outbuf = gssalloc_malloc(bufsize); + if (outbuf == NULL) { + free(plain.data); + return ENOMEM; + } + + /* TOK_ID */ + store_16_be(KG2_TOK_WRAP_MSG, outbuf); + /* flags */ + outbuf[2] = (acceptor_flag + | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0) + | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); + /* filler */ + outbuf[3] = 0xff; + /* EC */ + store_16_be(ec, outbuf+4); + /* RRC */ + store_16_be(0, outbuf+6); + store_64_be(ctx->seq_send, outbuf+8); + + memcpy(plain.data, message->value, message->length); + if (ec != 0) + memset(plain.data + message->length, 'x', ec); + memcpy(plain.data + message->length + ec, outbuf, 16); + + cipher.ciphertext.data = (char *)outbuf + 16; + cipher.ciphertext.length = bufsize - 16; + cipher.enctype = key->keyblock.enctype; + err = krb5_k_encrypt(context, key, key_usage, 0, &plain, &cipher); + zap(plain.data, plain.length); + free(plain.data); + plain.data = 0; + if (err) + goto error; + + /* Now that we know we're returning a valid token.... */ + ctx->seq_send++; + +#ifdef CFX_EXERCISE + rrc = rand() & 0xffff; + if (gss_krb5int_rotate_left(outbuf+16, bufsize-16, + (bufsize-16) - (rrc % (bufsize - 16)))) + store_16_be(rrc, outbuf+6); + /* If the rotate fails, don't worry about it. */ +#endif + } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) { + krb5_data plain; + size_t cksumsize; + + /* Here, message is the application-supplied data; message2 is + what goes into the output token. They may be the same, or + message2 may be empty (for MIC). */ + + tok_id = KG2_TOK_WRAP_MSG; + + wrap_with_checksum: + err = alloc_data(&plain, message->length + 16); + if (err) + return err; + + err = krb5_c_checksum_length(context, cksumtype, &cksumsize); + if (err) + goto error; + + assert(cksumsize <= 0xffff); + + bufsize = 16 + message2->length + cksumsize; + outbuf = gssalloc_malloc(bufsize); + if (outbuf == NULL) { + free(plain.data); + plain.data = 0; + err = ENOMEM; + goto error; + } + + /* TOK_ID */ + store_16_be(tok_id, outbuf); + /* flags */ + outbuf[2] = (acceptor_flag + | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); + /* filler */ + outbuf[3] = 0xff; + if (toktype == KG_TOK_WRAP_MSG) { + /* Use 0 for checksum calculation, substitute + checksum length later. */ + /* EC */ + store_16_be(0, outbuf+4); + /* RRC */ + store_16_be(0, outbuf+6); + } else { + /* MIC and DEL store 0xFF in EC and RRC. */ + store_16_be(0xffff, outbuf+4); + store_16_be(0xffff, outbuf+6); + } + store_64_be(ctx->seq_send, outbuf+8); + + memcpy(plain.data, message->value, message->length); + memcpy(plain.data + message->length, outbuf, 16); + + /* Fill in the output token -- data contents, if any, and + space for the checksum. */ + if (message2->length) + memcpy(outbuf + 16, message2->value, message2->length); + + sum.contents = outbuf + 16 + message2->length; + sum.length = cksumsize; + + err = krb5_k_make_checksum(context, cksumtype, key, + key_usage, &plain, &sum); + zap(plain.data, plain.length); + free(plain.data); + plain.data = 0; + if (err) { + zap(outbuf,bufsize); + goto error; + } + if (sum.length != cksumsize) + abort(); + memcpy(outbuf + 16 + message2->length, sum.contents, cksumsize); + krb5_free_checksum_contents(context, &sum); + sum.contents = 0; + /* Now that we know we're actually generating the token... */ + ctx->seq_send++; + + if (toktype == KG_TOK_WRAP_MSG) { +#ifdef CFX_EXERCISE + rrc = rand() & 0xffff; + /* If the rotate fails, don't worry about it. */ + if (gss_krb5int_rotate_left(outbuf+16, bufsize-16, + (bufsize-16) - (rrc % (bufsize - 16)))) + store_16_be(rrc, outbuf+6); +#endif + /* Fix up EC field. */ + store_16_be(cksumsize, outbuf+4); + } else { + store_16_be(0xffff, outbuf+6); + } + } else if (toktype == KG_TOK_MIC_MSG) { + tok_id = KG2_TOK_MIC_MSG; + message2 = &empty_message; + goto wrap_with_checksum; + } else if (toktype == KG_TOK_DEL_CTX) { + tok_id = KG2_TOK_DEL_CTX; + message = message2 = &empty_message; + goto wrap_with_checksum; + } else + abort(); + + token->value = outbuf; + token->length = bufsize; + return 0; + +error: + gssalloc_free(outbuf); + token->value = NULL; + token->length = 0; + return err; +} + +/* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX + conf_state is only valid if SEAL. */ + +OM_uint32 +gss_krb5int_unseal_token_v3(krb5_context *contextptr, + OM_uint32 *minor_status, + krb5_gss_ctx_id_rec *ctx, + unsigned char *ptr, unsigned int bodysize, + gss_buffer_t message_buffer, + int *conf_state, gss_qop_t *qop_state, int toktype) +{ + krb5_context context = *contextptr; + krb5_data plain; + uint64_t seqnum; + size_t ec, rrc; + int key_usage; + unsigned char acceptor_flag; + krb5_checksum sum; + krb5_error_code err; + krb5_boolean valid; + krb5_key key; + krb5_cksumtype cksumtype; + + if (qop_state) + *qop_state = GSS_C_QOP_DEFAULT; + + acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0; + key_usage = (toktype == KG_TOK_WRAP_MSG + ? (!ctx->initiate + ? KG_USAGE_INITIATOR_SEAL + : KG_USAGE_ACCEPTOR_SEAL) + : (!ctx->initiate + ? KG_USAGE_INITIATOR_SIGN + : KG_USAGE_ACCEPTOR_SIGN)); + + /* Oops. I wrote this code assuming ptr would be at the start of + the token header. */ + ptr -= 2; + bodysize += 2; + + if (bodysize < 16) { + defective: + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) { + *minor_status = (OM_uint32)G_BAD_DIRECTION; + return GSS_S_BAD_SIG; + } + + /* Two things to note here. + + First, we can't really enforce the use of the acceptor's subkey, + if we're the acceptor; the initiator may have sent messages + before getting the subkey. We could probably enforce it if + we're the initiator. + + Second, if someone tweaks the code to not set the flag telling + the krb5 library to generate a new subkey in the AP-REP + message, the MIT library may include a subkey anyways -- + namely, a copy of the AP-REQ subkey, if it was provided. So + the initiator may think we wanted a subkey, and set the flag, + even though we weren't trying to set the subkey. The "other" + key, the one not asserted by the acceptor, will have the same + value in that case, though, so we can just ignore the flag. */ + if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) { + key = ctx->acceptor_subkey; + cksumtype = ctx->acceptor_subkey_cksumtype; + } else { + key = ctx->subkey; + cksumtype = ctx->cksumtype; + } + assert(key != NULL); + + if (toktype == KG_TOK_WRAP_MSG) { + if (load_16_be(ptr) != KG2_TOK_WRAP_MSG) + goto defective; + if (ptr[3] != 0xff) + goto defective; + ec = load_16_be(ptr+4); + rrc = load_16_be(ptr+6); + seqnum = load_64_be(ptr+8); + if (!gss_krb5int_rotate_left(ptr+16, bodysize-16, rrc)) { + no_mem: + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) { + /* confidentiality */ + krb5_enc_data cipher; + unsigned char *althdr; + + if (conf_state) + *conf_state = 1; + /* Do we have no decrypt_size function? + + For all current cryptosystems, the ciphertext size will + be larger than the plaintext size. */ + cipher.enctype = key->keyblock.enctype; + cipher.ciphertext.length = bodysize - 16; + cipher.ciphertext.data = (char *)ptr + 16; + plain.length = bodysize - 16; + plain.data = gssalloc_malloc(plain.length); + if (plain.data == NULL) + goto no_mem; + err = krb5_k_decrypt(context, key, key_usage, 0, + &cipher, &plain); + if (err) { + gssalloc_free(plain.data); + goto error; + } + /* Don't use bodysize here! Use the fact that + cipher.ciphertext.length has been adjusted to the + correct length. */ + althdr = (unsigned char *)plain.data + plain.length - 16; + if (load_16_be(althdr) != KG2_TOK_WRAP_MSG + || althdr[2] != ptr[2] + || althdr[3] != ptr[3] + || memcmp(althdr+8, ptr+8, 8)) { + free(plain.data); + goto defective; + } + message_buffer->value = plain.data; + message_buffer->length = plain.length - ec - 16; + if(message_buffer->length == 0) { + gssalloc_free(message_buffer->value); + message_buffer->value = NULL; + } + } else { + size_t cksumsize; + + err = krb5_c_checksum_length(context, cksumtype, &cksumsize); + if (err) + goto error; + + /* no confidentiality */ + if (conf_state) + *conf_state = 0; + if (ec + 16 < ec) + /* overflow check */ + goto defective; + if (ec + 16 > bodysize) + goto defective; + /* We have: header | msg | cksum. + We need cksum(msg | header). + Rotate the first two. */ + store_16_be(0, ptr+4); + store_16_be(0, ptr+6); + plain = make_data(ptr, bodysize - ec); + if (!gss_krb5int_rotate_left(ptr, bodysize-ec, 16)) + goto no_mem; + sum.length = ec; + if (sum.length != cksumsize) { + *minor_status = 0; + return GSS_S_BAD_SIG; + } + sum.contents = ptr+bodysize-ec; + sum.checksum_type = cksumtype; + err = krb5_k_verify_checksum(context, key, key_usage, + &plain, &sum, &valid); + if (err) + goto error; + if (!valid) { + *minor_status = 0; + return GSS_S_BAD_SIG; + } + message_buffer->length = plain.length - 16; + message_buffer->value = gssalloc_malloc(message_buffer->length); + if (message_buffer->value == NULL) + goto no_mem; + memcpy(message_buffer->value, plain.data, message_buffer->length); + } + err = g_seqstate_check(ctx->seqstate, seqnum); + *minor_status = 0; + return err; + } else if (toktype == KG_TOK_MIC_MSG) { + /* wrap token, no confidentiality */ + if (load_16_be(ptr) != KG2_TOK_MIC_MSG) + goto defective; + verify_mic_1: + if (ptr[3] != 0xff) + goto defective; + if (load_32_be(ptr+4) != 0xffffffffL) + goto defective; + seqnum = load_64_be(ptr+8); + plain.length = message_buffer->length + 16; + plain.data = malloc(plain.length); + if (plain.data == NULL) + goto no_mem; + if (message_buffer->length) + memcpy(plain.data, message_buffer->value, message_buffer->length); + memcpy(plain.data + message_buffer->length, ptr, 16); + sum.length = bodysize - 16; + sum.contents = ptr + 16; + sum.checksum_type = cksumtype; + err = krb5_k_verify_checksum(context, key, key_usage, + &plain, &sum, &valid); + free(plain.data); + plain.data = NULL; + if (err) { + error: + *minor_status = err; + save_error_info(*minor_status, context); + return GSS_S_BAD_SIG; /* XXX */ + } + if (!valid) { + *minor_status = 0; + return GSS_S_BAD_SIG; + } + err = g_seqstate_check(ctx->seqstate, seqnum); + *minor_status = 0; + return err; + } else if (toktype == KG_TOK_DEL_CTX) { + if (load_16_be(ptr) != KG2_TOK_DEL_CTX) + goto defective; + message_buffer = (gss_buffer_t)&empty_message; + goto verify_mic_1; + } else { + goto defective; + } +} diff --git a/src/lib/gssapi/krb5/k5sealv3iov.c b/src/lib/gssapi/krb5/k5sealv3iov.c new file mode 100644 index 000000000000..a73edb6a4ffd --- /dev/null +++ b/src/lib/gssapi/krb5/k5sealv3iov.c @@ -0,0 +1,468 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/k5sealv3iov.c */ +/* + * Copyright 2008 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 "gssapiP_krb5.h" + +krb5_error_code +gss_krb5int_make_seal_token_v3_iov(krb5_context context, + krb5_gss_ctx_id_rec *ctx, + int conf_req_flag, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype) +{ + krb5_error_code code = 0; + gss_iov_buffer_t header; + gss_iov_buffer_t padding; + gss_iov_buffer_t trailer; + unsigned char acceptor_flag; + unsigned short tok_id; + unsigned char *outbuf = NULL; + unsigned char *tbuf = NULL; + int key_usage; + size_t rrc = 0; + unsigned int gss_headerlen, gss_trailerlen; + krb5_key key; + krb5_cksumtype cksumtype; + size_t data_length, assoc_data_length; + + assert(ctx->proto == 1); + + acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR; + key_usage = (toktype == KG_TOK_WRAP_MSG + ? (ctx->initiate + ? KG_USAGE_INITIATOR_SEAL + : KG_USAGE_ACCEPTOR_SEAL) + : (ctx->initiate + ? KG_USAGE_INITIATOR_SIGN + : KG_USAGE_ACCEPTOR_SIGN)); + if (ctx->have_acceptor_subkey) { + key = ctx->acceptor_subkey; + cksumtype = ctx->acceptor_subkey_cksumtype; + } else { + key = ctx->subkey; + cksumtype = ctx->cksumtype; + } + assert(key != NULL); + assert(cksumtype != 0); + + kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); + + header = kg_locate_header_iov(iov, iov_count, toktype); + if (header == NULL) + return EINVAL; + + padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); + if (padding != NULL) + padding->buffer.length = 0; + + trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + + if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) { + unsigned int k5_headerlen, k5_trailerlen, k5_padlen; + size_t ec = 0; + size_t conf_data_length = data_length - assoc_data_length; + + code = krb5_c_crypto_length(context, key->keyblock.enctype, + KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen); + if (code != 0) + goto cleanup; + + code = krb5_c_padding_length(context, key->keyblock.enctype, + conf_data_length + 16 /* E(Header) */, &k5_padlen); + if (code != 0) + goto cleanup; + + if (k5_padlen == 0 && (ctx->gss_flags & GSS_C_DCE_STYLE)) { + /* Windows rejects AEAD tokens with non-zero EC */ + code = krb5_c_block_size(context, key->keyblock.enctype, &ec); + if (code != 0) + goto cleanup; + } else + ec = k5_padlen; + + code = krb5_c_crypto_length(context, key->keyblock.enctype, + KRB5_CRYPTO_TYPE_TRAILER, &k5_trailerlen); + if (code != 0) + goto cleanup; + + gss_headerlen = 16 /* Header */ + k5_headerlen; + gss_trailerlen = ec + 16 /* E(Header) */ + k5_trailerlen; + + if (trailer == NULL) { + rrc = gss_trailerlen; + /* Workaround for Windows bug where it rotates by EC + RRC */ + if (ctx->gss_flags & GSS_C_DCE_STYLE) + rrc -= ec; + gss_headerlen += gss_trailerlen; + } + + if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) { + code = kg_allocate_iov(header, (size_t) gss_headerlen); + } else if (header->buffer.length < gss_headerlen) + code = KRB5_BAD_MSIZE; + if (code != 0) + goto cleanup; + outbuf = (unsigned char *)header->buffer.value; + header->buffer.length = (size_t) gss_headerlen; + + if (trailer != NULL) { + if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) + code = kg_allocate_iov(trailer, (size_t) gss_trailerlen); + else if (trailer->buffer.length < gss_trailerlen) + code = KRB5_BAD_MSIZE; + if (code != 0) + goto cleanup; + trailer->buffer.length = (size_t) gss_trailerlen; + } + + /* TOK_ID */ + store_16_be(KG2_TOK_WRAP_MSG, outbuf); + /* flags */ + outbuf[2] = (acceptor_flag + | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0) + | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); + /* filler */ + outbuf[3] = 0xFF; + /* EC */ + store_16_be(ec, outbuf + 4); + /* RRC */ + store_16_be(0, outbuf + 6); + store_64_be(ctx->seq_send, outbuf + 8); + + /* EC | copy of header to be encrypted, located in (possibly rotated) trailer */ + if (trailer == NULL) + tbuf = (unsigned char *)header->buffer.value + 16; /* Header */ + else + tbuf = (unsigned char *)trailer->buffer.value; + + memset(tbuf, 0xFF, ec); + memcpy(tbuf + ec, header->buffer.value, 16); + + code = kg_encrypt_iov(context, ctx->proto, + ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0), + ec, rrc, key, key_usage, 0, iov, iov_count); + if (code != 0) + goto cleanup; + + /* RRC */ + store_16_be(rrc, outbuf + 6); + + ctx->seq_send++; + } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) { + tok_id = KG2_TOK_WRAP_MSG; + + wrap_with_checksum: + + gss_headerlen = 16; + + code = krb5_c_crypto_length(context, key->keyblock.enctype, + KRB5_CRYPTO_TYPE_CHECKSUM, + &gss_trailerlen); + if (code != 0) + goto cleanup; + + assert(gss_trailerlen <= 0xFFFF); + + if (trailer == NULL) { + rrc = gss_trailerlen; + gss_headerlen += gss_trailerlen; + } + + if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) + code = kg_allocate_iov(header, (size_t) gss_headerlen); + else if (header->buffer.length < gss_headerlen) + code = KRB5_BAD_MSIZE; + if (code != 0) + goto cleanup; + outbuf = (unsigned char *)header->buffer.value; + header->buffer.length = (size_t) gss_headerlen; + + if (trailer != NULL) { + if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) + code = kg_allocate_iov(trailer, (size_t) gss_trailerlen); + else if (trailer->buffer.length < gss_trailerlen) + code = KRB5_BAD_MSIZE; + if (code != 0) + goto cleanup; + trailer->buffer.length = (size_t) gss_trailerlen; + } + + /* TOK_ID */ + store_16_be(tok_id, outbuf); + /* flags */ + outbuf[2] = (acceptor_flag + | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); + /* filler */ + outbuf[3] = 0xFF; + if (toktype == KG_TOK_WRAP_MSG) { + /* Use 0 for checksum calculation, substitute + * checksum length later. + */ + /* EC */ + store_16_be(0, outbuf + 4); + /* RRC */ + store_16_be(0, outbuf + 6); + } else { + /* MIC and DEL store 0xFF in EC and RRC */ + store_16_be(0xFFFF, outbuf + 4); + store_16_be(0xFFFF, outbuf + 6); + } + store_64_be(ctx->seq_send, outbuf + 8); + + code = kg_make_checksum_iov_v3(context, cksumtype, + rrc, key, key_usage, + iov, iov_count, toktype); + if (code != 0) + goto cleanup; + + ctx->seq_send++; + + if (toktype == KG_TOK_WRAP_MSG) { + /* Fix up EC field */ + store_16_be(gss_trailerlen, outbuf + 4); + /* Fix up RRC field */ + store_16_be(rrc, outbuf + 6); + } + } else if (toktype == KG_TOK_MIC_MSG) { + tok_id = KG2_TOK_MIC_MSG; + trailer = NULL; + goto wrap_with_checksum; + } else if (toktype == KG_TOK_DEL_CTX) { + tok_id = KG2_TOK_DEL_CTX; + goto wrap_with_checksum; + } else { + abort(); + } + + code = 0; + if (conf_state != NULL) + *conf_state = conf_req_flag; + +cleanup: + if (code != 0) + kg_release_iov(iov, iov_count); + + return code; +} + +OM_uint32 +gss_krb5int_unseal_v3_iov(krb5_context context, + OM_uint32 *minor_status, + krb5_gss_ctx_id_rec *ctx, + gss_iov_buffer_desc *iov, + int iov_count, + int *conf_state, + gss_qop_t *qop_state, + int toktype) +{ + OM_uint32 code; + gss_iov_buffer_t header; + gss_iov_buffer_t padding; + gss_iov_buffer_t trailer; + unsigned char acceptor_flag; + unsigned char *ptr = NULL; + int key_usage; + size_t rrc, ec; + size_t data_length, assoc_data_length; + krb5_key key; + uint64_t seqnum; + krb5_boolean valid; + krb5_cksumtype cksumtype; + int conf_flag = 0; + + if (qop_state != NULL) + *qop_state = GSS_C_QOP_DEFAULT; + + header = kg_locate_header_iov(iov, iov_count, toktype); + assert(header != NULL); + + padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); + if (padding != NULL && padding->buffer.length != 0) + return GSS_S_DEFECTIVE_TOKEN; + + trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + + acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0; + key_usage = (toktype == KG_TOK_WRAP_MSG + ? (!ctx->initiate + ? KG_USAGE_INITIATOR_SEAL + : KG_USAGE_ACCEPTOR_SEAL) + : (!ctx->initiate + ? KG_USAGE_INITIATOR_SIGN + : KG_USAGE_ACCEPTOR_SIGN)); + + kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); + + ptr = (unsigned char *)header->buffer.value; + + if (header->buffer.length < 16) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) { + *minor_status = (OM_uint32)G_BAD_DIRECTION; + return GSS_S_BAD_SIG; + } + + if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) { + key = ctx->acceptor_subkey; + cksumtype = ctx->acceptor_subkey_cksumtype; + } else { + key = ctx->subkey; + cksumtype = ctx->cksumtype; + } + assert(key != NULL); + + + if (toktype == KG_TOK_WRAP_MSG) { + unsigned int k5_trailerlen; + + if (load_16_be(ptr) != KG2_TOK_WRAP_MSG) + goto defective; + conf_flag = ((ptr[2] & FLAG_WRAP_CONFIDENTIAL) != 0); + if (ptr[3] != 0xFF) + goto defective; + ec = load_16_be(ptr + 4); + rrc = load_16_be(ptr + 6); + seqnum = load_64_be(ptr + 8); + + code = krb5_c_crypto_length(context, key->keyblock.enctype, + conf_flag ? KRB5_CRYPTO_TYPE_TRAILER : + KRB5_CRYPTO_TYPE_CHECKSUM, + &k5_trailerlen); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + /* Deal with RRC */ + if (trailer == NULL) { + size_t desired_rrc = k5_trailerlen; + + if (conf_flag) { + desired_rrc += 16; /* E(Header) */ + + if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0) + desired_rrc += ec; + } + + /* According to MS, we only need to deal with a fixed RRC for DCE */ + if (rrc != desired_rrc) + goto defective; + } else if (rrc != 0) { + /* Should have been rotated by kg_unseal_stream_iov() */ + goto defective; + } + + if (conf_flag) { + unsigned char *althdr; + + /* Decrypt */ + code = kg_decrypt_iov(context, ctx->proto, + ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0), + ec, rrc, + key, key_usage, 0, iov, iov_count); + if (code != 0) { + *minor_status = code; + return GSS_S_BAD_SIG; + } + + /* Validate header integrity */ + if (trailer == NULL) + althdr = (unsigned char *)header->buffer.value + 16 + ec; + else + althdr = (unsigned char *)trailer->buffer.value + ec; + + if (load_16_be(althdr) != KG2_TOK_WRAP_MSG + || althdr[2] != ptr[2] + || althdr[3] != ptr[3] + || memcmp(althdr + 8, ptr + 8, 8) != 0) { + *minor_status = 0; + return GSS_S_BAD_SIG; + } + } else { + /* Verify checksum: note EC is checksum size here, not padding */ + if (ec != k5_trailerlen) + goto defective; + + /* Zero EC, RRC before computing checksum */ + store_16_be(0, ptr + 4); + store_16_be(0, ptr + 6); + + code = kg_verify_checksum_iov_v3(context, cksumtype, rrc, + key, key_usage, + iov, iov_count, toktype, &valid); + if (code != 0 || valid == FALSE) { + *minor_status = code; + return GSS_S_BAD_SIG; + } + } + + code = g_seqstate_check(ctx->seqstate, seqnum); + } else if (toktype == KG_TOK_MIC_MSG) { + if (load_16_be(ptr) != KG2_TOK_MIC_MSG) + goto defective; + + verify_mic_1: + if (ptr[3] != 0xFF) + goto defective; + seqnum = load_64_be(ptr + 8); + + /* For MIC tokens, the GSS header and checksum are in the same buffer. + * Fake up an RRC so that the checksum is expected in the header. */ + rrc = (trailer != NULL) ? 0 : header->buffer.length - 16; + code = kg_verify_checksum_iov_v3(context, cksumtype, rrc, + key, key_usage, + iov, iov_count, toktype, &valid); + if (code != 0 || valid == FALSE) { + *minor_status = code; + return GSS_S_BAD_SIG; + } + code = g_seqstate_check(ctx->seqstate, seqnum); + } else if (toktype == KG_TOK_DEL_CTX) { + if (load_16_be(ptr) != KG2_TOK_DEL_CTX) + goto defective; + goto verify_mic_1; + } else { + goto defective; + } + + *minor_status = 0; + + if (conf_state != NULL) + *conf_state = conf_flag; + + return code; + +defective: + *minor_status = 0; + + return GSS_S_DEFECTIVE_TOKEN; +} diff --git a/src/lib/gssapi/krb5/k5unseal.c b/src/lib/gssapi/krb5/k5unseal.c new file mode 100644 index 000000000000..26a2d33e7a2c --- /dev/null +++ b/src/lib/gssapi/krb5/k5unseal.c @@ -0,0 +1,588 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2001, 2007 by the Massachusetts Institute of Technology. + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * 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 FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "gssapiP_krb5.h" +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif +#include <assert.h> + +/* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX + conf_state is only valid if SEAL. */ + +static OM_uint32 +kg_unseal_v1(context, minor_status, ctx, ptr, bodysize, message_buffer, + conf_state, qop_state, toktype) + krb5_context context; + OM_uint32 *minor_status; + krb5_gss_ctx_id_rec *ctx; + unsigned char *ptr; + int bodysize; + gss_buffer_t message_buffer; + int *conf_state; + gss_qop_t *qop_state; + int toktype; +{ + krb5_error_code code; + int conflen = 0; + int signalg; + int sealalg; + int bad_pad = 0; + gss_buffer_desc token; + krb5_checksum cksum; + krb5_checksum md5cksum; + krb5_data plaind; + char *data_ptr; + unsigned char *plain; + unsigned int cksum_len = 0; + size_t plainlen; + int direction; + krb5_ui_4 seqnum; + OM_uint32 retval; + size_t sumlen; + size_t padlen; + krb5_keyusage sign_usage = KG_USAGE_SIGN; + + if (toktype == KG_TOK_SEAL_MSG) { + message_buffer->length = 0; + message_buffer->value = NULL; + } + + /* Sanity checks */ + + if (ctx->seq == NULL) { + /* ctx was established using a newer enctype, and cannot process RFC + * 1964 tokens. */ + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + if ((bodysize < 22) || (ptr[4] != 0xff) || (ptr[5] != 0xff)) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + signalg = ptr[0] + (ptr[1]<<8); + sealalg = ptr[2] + (ptr[3]<<8); + + if ((toktype != KG_TOK_SEAL_MSG) && + (sealalg != 0xffff)) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + /* in the current spec, there is only one valid seal algorithm per + key type, so a simple comparison is ok */ + + if ((toktype == KG_TOK_SEAL_MSG) && + !((sealalg == 0xffff) || + (sealalg == ctx->sealalg))) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + /* there are several mappings of seal algorithms to sign algorithms, + but few enough that we can try them all. */ + + if ((ctx->sealalg == SEAL_ALG_NONE && signalg > 1) || + (ctx->sealalg == SEAL_ALG_1 && signalg != SGN_ALG_3) || + (ctx->sealalg == SEAL_ALG_DES3KD && + signalg != SGN_ALG_HMAC_SHA1_DES3_KD)|| + (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4 && + signalg != SGN_ALG_HMAC_MD5)) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + switch (signalg) { + case SGN_ALG_DES_MAC_MD5: + case SGN_ALG_MD2_5: + case SGN_ALG_HMAC_MD5: + cksum_len = 8; + if (toktype != KG_TOK_SEAL_MSG) + sign_usage = 15; + break; + case SGN_ALG_3: + cksum_len = 16; + break; + case SGN_ALG_HMAC_SHA1_DES3_KD: + cksum_len = 20; + break; + default: + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + if ((size_t)bodysize < 14 + cksum_len) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + /* get the token parameters */ + + if ((code = kg_get_seq_num(context, ctx->seq, ptr+14, ptr+6, &direction, + &seqnum))) { + *minor_status = code; + return(GSS_S_BAD_SIG); + } + + /* decode the message, if SEAL */ + + if (toktype == KG_TOK_SEAL_MSG) { + size_t tmsglen = bodysize-(14+cksum_len); + if (sealalg != 0xffff) { + if ((plain = (unsigned char *) xmalloc(tmsglen)) == NULL) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + if (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) { + unsigned char bigend_seqnum[4]; + krb5_keyblock *enc_key; + int i; + store_32_be(seqnum, bigend_seqnum); + code = krb5_k_key_keyblock(context, ctx->enc, &enc_key); + if (code) + { + xfree(plain); + *minor_status = code; + return(GSS_S_FAILURE); + } + + assert (enc_key->length == 16); + for (i = 0; i <= 15; i++) + ((char *) enc_key->contents)[i] ^=0xf0; + code = kg_arcfour_docrypt (enc_key, 0, + &bigend_seqnum[0], 4, + ptr+14+cksum_len, tmsglen, + plain); + krb5_free_keyblock (context, enc_key); + } else { + code = kg_decrypt(context, ctx->enc, KG_USAGE_SEAL, NULL, + ptr+14+cksum_len, plain, tmsglen); + } + if (code) { + xfree(plain); + *minor_status = code; + return(GSS_S_FAILURE); + } + } else { + plain = ptr+14+cksum_len; + } + + plainlen = tmsglen; + + conflen = kg_confounder_size(context, ctx->enc->keyblock.enctype); + if (tmsglen < conflen) { + if (sealalg != 0xffff) + xfree(plain); + *minor_status = 0; + return(GSS_S_DEFECTIVE_TOKEN); + } + padlen = plain[tmsglen - 1]; + if (tmsglen - conflen < padlen) { + /* Don't error out yet, to avoid padding oracle attacks. We will + * treat this as a checksum failure later on. */ + padlen = 0; + bad_pad = 1; + } + token.length = tmsglen - conflen - padlen; + + if (token.length) { + if ((token.value = (void *) gssalloc_malloc(token.length)) == NULL) { + if (sealalg != 0xffff) + xfree(plain); + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + memcpy(token.value, plain+conflen, token.length); + } else { + token.value = NULL; + } + } else if (toktype == KG_TOK_SIGN_MSG) { + token = *message_buffer; + plain = token.value; + plainlen = token.length; + } else { + token.length = 0; + token.value = NULL; + plain = token.value; + plainlen = token.length; + } + + /* compute the checksum of the message */ + + /* initialize the the cksum */ + switch (signalg) { + case SGN_ALG_DES_MAC_MD5: + case SGN_ALG_MD2_5: + case SGN_ALG_DES_MAC: + case SGN_ALG_3: + md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; + break; + case SGN_ALG_HMAC_MD5: + md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR; + break; + case SGN_ALG_HMAC_SHA1_DES3_KD: + md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3; + break; + default: + abort (); + } + + code = krb5_c_checksum_length(context, md5cksum.checksum_type, &sumlen); + if (code) + return(code); + md5cksum.length = sumlen; + + switch (signalg) { + case SGN_ALG_DES_MAC_MD5: + case SGN_ALG_3: + /* compute the checksum of the message */ + + /* 8 = bytes of token body to be checksummed according to spec */ + + if (! (data_ptr = xmalloc(8 + plainlen))) { + if (sealalg != 0xffff) + xfree(plain); + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + + (void) memcpy(data_ptr, ptr-2, 8); + + (void) memcpy(data_ptr+8, plain, plainlen); + + plaind.length = 8 + plainlen; + plaind.data = data_ptr; + code = krb5_k_make_checksum(context, md5cksum.checksum_type, + ctx->seq, sign_usage, + &plaind, &md5cksum); + xfree(data_ptr); + + if (code) { + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = code; + return(GSS_S_FAILURE); + } + + code = kg_encrypt_inplace(context, ctx->seq, KG_USAGE_SEAL, + (g_OID_equal(ctx->mech_used, + gss_mech_krb5_old) ? + ctx->seq->keyblock.contents : NULL), + md5cksum.contents, 16); + if (code) { + krb5_free_checksum_contents(context, &md5cksum); + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = code; + return GSS_S_FAILURE; + } + + if (signalg == 0) + cksum.length = 8; + else + cksum.length = 16; + cksum.contents = md5cksum.contents + 16 - cksum.length; + + code = k5_bcmp(cksum.contents, ptr + 14, cksum.length); + break; + + case SGN_ALG_MD2_5: + if (!ctx->seed_init && + (code = kg_make_seed(context, ctx->subkey, ctx->seed))) { + krb5_free_checksum_contents(context, &md5cksum); + if (sealalg != 0xffff) + xfree(plain); + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = code; + return GSS_S_FAILURE; + } + + if (! (data_ptr = xmalloc(sizeof(ctx->seed) + 8 + plainlen))) { + krb5_free_checksum_contents(context, &md5cksum); + if (sealalg == 0) + xfree(plain); + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + (void) memcpy(data_ptr, ptr-2, 8); + (void) memcpy(data_ptr+8, ctx->seed, sizeof(ctx->seed)); + (void) memcpy(data_ptr+8+sizeof(ctx->seed), plain, plainlen); + plaind.length = 8 + sizeof(ctx->seed) + plainlen; + plaind.data = data_ptr; + krb5_free_checksum_contents(context, &md5cksum); + code = krb5_k_make_checksum(context, md5cksum.checksum_type, + ctx->seq, sign_usage, + &plaind, &md5cksum); + xfree(data_ptr); + + if (code) { + if (sealalg == 0) + xfree(plain); + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = code; + return(GSS_S_FAILURE); + } + + code = k5_bcmp(md5cksum.contents, ptr + 14, 8); + /* Falls through to defective-token?? */ + + default: + *minor_status = 0; + return(GSS_S_DEFECTIVE_TOKEN); + + case SGN_ALG_HMAC_SHA1_DES3_KD: + case SGN_ALG_HMAC_MD5: + /* compute the checksum of the message */ + + /* 8 = bytes of token body to be checksummed according to spec */ + + if (! (data_ptr = xmalloc(8 + plainlen))) { + if (sealalg != 0xffff) + xfree(plain); + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + + (void) memcpy(data_ptr, ptr-2, 8); + + (void) memcpy(data_ptr+8, plain, plainlen); + + plaind.length = 8 + plainlen; + plaind.data = data_ptr; + code = krb5_k_make_checksum(context, md5cksum.checksum_type, + ctx->seq, sign_usage, + &plaind, &md5cksum); + xfree(data_ptr); + + if (code) { + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = code; + return(GSS_S_FAILURE); + } + + code = k5_bcmp(md5cksum.contents, ptr + 14, cksum_len); + break; + } + + krb5_free_checksum_contents(context, &md5cksum); + if (sealalg != 0xffff) + xfree(plain); + + /* compare the computed checksum against the transmitted checksum */ + + if (code || bad_pad) { + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = 0; + return(GSS_S_BAD_SIG); + } + + + /* It got through unscathed. Make sure the context is unexpired. */ + + if (toktype == KG_TOK_SEAL_MSG) + *message_buffer = token; + + if (conf_state) + *conf_state = (sealalg != 0xffff); + + if (qop_state) + *qop_state = GSS_C_QOP_DEFAULT; + + /* do sequencing checks */ + + if ((ctx->initiate && direction != 0xff) || + (!ctx->initiate && direction != 0)) { + if (toktype == KG_TOK_SEAL_MSG) { + gssalloc_free(token.value); + message_buffer->value = NULL; + message_buffer->length = 0; + } + *minor_status = (OM_uint32)G_BAD_DIRECTION; + return(GSS_S_BAD_SIG); + } + + retval = g_seqstate_check(ctx->seqstate, (uint64_t)seqnum); + + /* success or ordering violation */ + + *minor_status = 0; + return(retval); +} + +/* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX + conf_state is only valid if SEAL. */ + +OM_uint32 +kg_unseal(minor_status, context_handle, input_token_buffer, + message_buffer, conf_state, qop_state, toktype) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_buffer_t input_token_buffer; + gss_buffer_t message_buffer; + int *conf_state; + gss_qop_t *qop_state; + int toktype; +{ + krb5_gss_ctx_id_rec *ctx; + unsigned char *ptr; + unsigned int bodysize; + int err; + int toktype2; + int vfyflags = 0; + OM_uint32 ret; + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + + if (ctx->terminated || !ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return(GSS_S_NO_CONTEXT); + } + + /* parse the token, leave the data in message_buffer, setting conf_state */ + + /* verify the header */ + + ptr = (unsigned char *) input_token_buffer->value; + + + err = g_verify_token_header(ctx->mech_used, + &bodysize, &ptr, -1, + input_token_buffer->length, + vfyflags); + if (err) { + *minor_status = err; + return GSS_S_DEFECTIVE_TOKEN; + } + + if (bodysize < 2) { + *minor_status = (OM_uint32)G_BAD_TOK_HEADER; + return GSS_S_DEFECTIVE_TOKEN; + } + + toktype2 = load_16_be(ptr); + + ptr += 2; + bodysize -= 2; + + switch (toktype2) { + case KG2_TOK_MIC_MSG: + case KG2_TOK_WRAP_MSG: + case KG2_TOK_DEL_CTX: + ret = gss_krb5int_unseal_token_v3(&ctx->k5_context, minor_status, ctx, + ptr, bodysize, message_buffer, + conf_state, qop_state, toktype); + break; + case KG_TOK_MIC_MSG: + case KG_TOK_WRAP_MSG: + case KG_TOK_DEL_CTX: + ret = kg_unseal_v1(ctx->k5_context, minor_status, ctx, ptr, bodysize, + message_buffer, conf_state, qop_state, + toktype); + break; + default: + *minor_status = (OM_uint32)G_BAD_TOK_HEADER; + ret = GSS_S_DEFECTIVE_TOKEN; + break; + } + + if (ret != 0) + save_error_info (*minor_status, ctx->k5_context); + + return ret; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_unwrap(minor_status, context_handle, + input_message_buffer, output_message_buffer, + conf_state, qop_state) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_buffer_t input_message_buffer; + gss_buffer_t output_message_buffer; + int *conf_state; + gss_qop_t *qop_state; +{ + OM_uint32 rstat; + + rstat = kg_unseal(minor_status, context_handle, + input_message_buffer, output_message_buffer, + conf_state, qop_state, KG_TOK_WRAP_MSG); + return(rstat); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_verify_mic(minor_status, context_handle, + message_buffer, token_buffer, + qop_state) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_buffer_t message_buffer; + gss_buffer_t token_buffer; + gss_qop_t *qop_state; +{ + OM_uint32 rstat; + + rstat = kg_unseal(minor_status, context_handle, + token_buffer, message_buffer, + NULL, qop_state, KG_TOK_MIC_MSG); + return(rstat); +} diff --git a/src/lib/gssapi/krb5/k5unsealiov.c b/src/lib/gssapi/krb5/k5unsealiov.c new file mode 100644 index 000000000000..8b6704274db8 --- /dev/null +++ b/src/lib/gssapi/krb5/k5unsealiov.c @@ -0,0 +1,675 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/k5unsealiov.c */ +/* + * Copyright 2008, 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" + +static OM_uint32 +kg_unseal_v1_iov(krb5_context context, + OM_uint32 *minor_status, + krb5_gss_ctx_id_rec *ctx, + gss_iov_buffer_desc *iov, + int iov_count, + size_t token_wrapper_len, + int *conf_state, + gss_qop_t *qop_state, + int toktype) +{ + OM_uint32 code; + gss_iov_buffer_t header; + gss_iov_buffer_t trailer; + unsigned char *ptr; + int sealalg; + int signalg; + krb5_checksum cksum; + krb5_checksum md5cksum; + size_t cksum_len = 0; + size_t conflen = 0; + int direction; + krb5_ui_4 seqnum; + OM_uint32 retval; + size_t sumlen; + krb5_keyusage sign_usage = KG_USAGE_SIGN; + + md5cksum.length = cksum.length = 0; + md5cksum.contents = cksum.contents = NULL; + + header = kg_locate_header_iov(iov, iov_count, toktype); + assert(header != NULL); + + trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + if (trailer != NULL && trailer->buffer.length != 0) { + *minor_status = (OM_uint32)KRB5_BAD_MSIZE; + return GSS_S_DEFECTIVE_TOKEN; + } + + if (ctx->seq == NULL) { + /* ctx was established using a newer enctype, and cannot process RFC + * 1964 tokens. */ + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + if (header->buffer.length < token_wrapper_len + 22) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + ptr = (unsigned char *)header->buffer.value + token_wrapper_len; + + signalg = ptr[0]; + signalg |= ptr[1] << 8; + + sealalg = ptr[2]; + sealalg |= ptr[3] << 8; + + if (ptr[4] != 0xFF || ptr[5] != 0xFF) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + if (toktype != KG_TOK_WRAP_MSG && sealalg != 0xFFFF) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + if (toktype == KG_TOK_WRAP_MSG && + !(sealalg == 0xFFFF || sealalg == ctx->sealalg)) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + if ((ctx->sealalg == SEAL_ALG_NONE && signalg > 1) || + (ctx->sealalg == SEAL_ALG_1 && signalg != SGN_ALG_3) || + (ctx->sealalg == SEAL_ALG_DES3KD && + signalg != SGN_ALG_HMAC_SHA1_DES3_KD)|| + (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4 && + signalg != SGN_ALG_HMAC_MD5)) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + switch (signalg) { + case SGN_ALG_DES_MAC_MD5: + case SGN_ALG_MD2_5: + case SGN_ALG_HMAC_MD5: + cksum_len = 8; + if (toktype != KG_TOK_WRAP_MSG) + sign_usage = 15; + break; + case SGN_ALG_3: + cksum_len = 16; + break; + case SGN_ALG_HMAC_SHA1_DES3_KD: + cksum_len = 20; + break; + default: + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + /* get the token parameters */ + code = kg_get_seq_num(context, ctx->seq, ptr + 14, ptr + 6, &direction, + &seqnum); + if (code != 0) { + *minor_status = code; + return GSS_S_BAD_SIG; + } + + /* decode the message, if SEAL */ + if (toktype == KG_TOK_WRAP_MSG) { + if (sealalg != 0xFFFF) { + if (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) { + unsigned char bigend_seqnum[4]; + krb5_keyblock *enc_key; + size_t i; + + store_32_be(seqnum, bigend_seqnum); + + code = krb5_k_key_keyblock(context, ctx->enc, &enc_key); + if (code != 0) { + retval = GSS_S_FAILURE; + goto cleanup; + } + + assert(enc_key->length == 16); + + for (i = 0; i < enc_key->length; i++) + ((char *)enc_key->contents)[i] ^= 0xF0; + + code = kg_arcfour_docrypt_iov(context, enc_key, 0, + &bigend_seqnum[0], 4, + iov, iov_count); + krb5_free_keyblock(context, enc_key); + } else { + code = kg_decrypt_iov(context, 0, + ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0), + 0 /*EC*/, 0 /*RRC*/, + ctx->enc, KG_USAGE_SEAL, NULL, + iov, iov_count); + } + if (code != 0) { + retval = GSS_S_FAILURE; + goto cleanup; + } + } + conflen = kg_confounder_size(context, ctx->enc->keyblock.enctype); + } + + if (header->buffer.length != token_wrapper_len + 14 + cksum_len + conflen) { + retval = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + + /* compute the checksum of the message */ + + /* initialize the checksum */ + + switch (signalg) { + case SGN_ALG_DES_MAC_MD5: + case SGN_ALG_MD2_5: + case SGN_ALG_DES_MAC: + case SGN_ALG_3: + md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; + break; + case SGN_ALG_HMAC_MD5: + md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR; + break; + case SGN_ALG_HMAC_SHA1_DES3_KD: + md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3; + break; + default: + abort(); + } + + code = krb5_c_checksum_length(context, md5cksum.checksum_type, &sumlen); + if (code != 0) { + retval = GSS_S_FAILURE; + goto cleanup; + } + md5cksum.length = sumlen; + + /* compute the checksum of the message */ + code = kg_make_checksum_iov_v1(context, md5cksum.checksum_type, + cksum_len, ctx->seq, ctx->enc, + sign_usage, iov, iov_count, toktype, + &md5cksum); + if (code != 0) { + retval = GSS_S_FAILURE; + goto cleanup; + } + + switch (signalg) { + case SGN_ALG_DES_MAC_MD5: + case SGN_ALG_3: + code = kg_encrypt_inplace(context, ctx->seq, KG_USAGE_SEAL, + (g_OID_equal(ctx->mech_used, + gss_mech_krb5_old) ? + ctx->seq->keyblock.contents : NULL), + md5cksum.contents, 16); + if (code != 0) { + retval = GSS_S_FAILURE; + goto cleanup; + } + + cksum.length = cksum_len; + cksum.contents = md5cksum.contents + 16 - cksum.length; + + code = k5_bcmp(cksum.contents, ptr + 14, cksum.length); + break; + case SGN_ALG_HMAC_SHA1_DES3_KD: + case SGN_ALG_HMAC_MD5: + code = k5_bcmp(md5cksum.contents, ptr + 14, cksum_len); + break; + default: + code = 0; + retval = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + break; + } + + if (code != 0) { + code = 0; + retval = GSS_S_BAD_SIG; + goto cleanup; + } + + /* + * For GSS_C_DCE_STYLE, the caller manages the padding, because the + * pad length is in the RPC PDU. The value of the padding may be + * uninitialized. For normal GSS, the last bytes of the decrypted + * data contain the pad length. kg_fixup_padding_iov() will find + * this and fixup the last data IOV appropriately. + */ + if (toktype == KG_TOK_WRAP_MSG && + (ctx->gss_flags & GSS_C_DCE_STYLE) == 0) { + retval = kg_fixup_padding_iov(&code, iov, iov_count); + if (retval != GSS_S_COMPLETE) + goto cleanup; + } + + if (conf_state != NULL) + *conf_state = (sealalg != 0xFFFF); + + if (qop_state != NULL) + *qop_state = GSS_C_QOP_DEFAULT; + + if ((ctx->initiate && direction != 0xff) || + (!ctx->initiate && direction != 0)) { + *minor_status = (OM_uint32)G_BAD_DIRECTION; + retval = GSS_S_BAD_SIG; + } + + code = 0; + retval = g_seqstate_check(ctx->seqstate, (uint64_t)seqnum); + +cleanup: + krb5_free_checksum_contents(context, &md5cksum); + + *minor_status = code; + + return retval; +} + +/* + * Caller must provide TOKEN | DATA | PADDING | TRAILER, except + * for DCE in which case it can just provide TOKEN | DATA (must + * guarantee that DATA is padded) + */ +static OM_uint32 +kg_unseal_iov_token(OM_uint32 *minor_status, + krb5_gss_ctx_id_rec *ctx, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype) +{ + krb5_error_code code; + krb5_context context = ctx->k5_context; + unsigned char *ptr; + gss_iov_buffer_t header; + gss_iov_buffer_t padding; + gss_iov_buffer_t trailer; + size_t input_length; + unsigned int bodysize; + int toktype2; + + header = kg_locate_header_iov(iov, iov_count, toktype); + if (header == NULL) { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + + padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); + trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + + ptr = (unsigned char *)header->buffer.value; + input_length = header->buffer.length; + + if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0 && + toktype == KG_TOK_WRAP_MSG) { + size_t data_length, assoc_data_length; + + kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); + + input_length += data_length - assoc_data_length; + + if (padding != NULL) + input_length += padding->buffer.length; + + if (trailer != NULL) + input_length += trailer->buffer.length; + } + + code = g_verify_token_header(ctx->mech_used, + &bodysize, &ptr, -1, + input_length, 0); + if (code != 0) { + *minor_status = code; + return GSS_S_DEFECTIVE_TOKEN; + } + + if (bodysize < 2) { + *minor_status = (OM_uint32)G_BAD_TOK_HEADER; + return GSS_S_DEFECTIVE_TOKEN; + } + + toktype2 = load_16_be(ptr); + + ptr += 2; + bodysize -= 2; + + switch (toktype2) { + case KG2_TOK_MIC_MSG: + case KG2_TOK_WRAP_MSG: + case KG2_TOK_DEL_CTX: + code = gss_krb5int_unseal_v3_iov(context, minor_status, ctx, iov, iov_count, + conf_state, qop_state, toktype); + break; + case KG_TOK_MIC_MSG: + case KG_TOK_WRAP_MSG: + case KG_TOK_DEL_CTX: + code = kg_unseal_v1_iov(context, minor_status, ctx, iov, iov_count, + (size_t)(ptr - (unsigned char *)header->buffer.value), + conf_state, qop_state, toktype); + break; + default: + *minor_status = (OM_uint32)G_BAD_TOK_HEADER; + code = GSS_S_DEFECTIVE_TOKEN; + break; + } + + if (code != 0) + save_error_info(*minor_status, context); + + return code; +} + +/* + * Split a STREAM | SIGN_DATA | DATA into + * HEADER | SIGN_DATA | DATA | PADDING | TRAILER + */ +static OM_uint32 +kg_unseal_stream_iov(OM_uint32 *minor_status, + krb5_gss_ctx_id_rec *ctx, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype) +{ + unsigned char *ptr; + unsigned int bodysize; + OM_uint32 code = 0, major_status = GSS_S_FAILURE; + krb5_context context = ctx->k5_context; + int conf_req_flag, toktype2; + int i = 0, j; + gss_iov_buffer_desc *tiov = NULL; + gss_iov_buffer_t stream, data = NULL; + gss_iov_buffer_t theader, tdata = NULL, tpadding, ttrailer; + + assert(toktype == KG_TOK_WRAP_MSG); + + if (toktype != KG_TOK_WRAP_MSG || (ctx->gss_flags & GSS_C_DCE_STYLE)) { + code = EINVAL; + goto cleanup; + } + + stream = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM); + assert(stream != NULL); + + ptr = (unsigned char *)stream->buffer.value; + + code = g_verify_token_header(ctx->mech_used, + &bodysize, &ptr, -1, + stream->buffer.length, 0); + if (code != 0) { + major_status = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + + if (bodysize < 2) { + *minor_status = (OM_uint32)G_BAD_TOK_HEADER; + return GSS_S_DEFECTIVE_TOKEN; + } + + toktype2 = load_16_be(ptr); + + ptr += 2; + bodysize -= 2; + + tiov = (gss_iov_buffer_desc *)calloc((size_t)iov_count + 2, sizeof(gss_iov_buffer_desc)); + if (tiov == NULL) { + code = ENOMEM; + goto cleanup; + } + + /* HEADER */ + theader = &tiov[i++]; + theader->type = GSS_IOV_BUFFER_TYPE_HEADER; + theader->buffer.value = stream->buffer.value; + theader->buffer.length = ptr - (unsigned char *)stream->buffer.value; + if (bodysize < 14 || + stream->buffer.length != theader->buffer.length + bodysize) { + major_status = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + theader->buffer.length += 14; + + /* n[SIGN_DATA] | DATA | m[SIGN_DATA] */ + for (j = 0; j < iov_count; j++) { + OM_uint32 type = GSS_IOV_BUFFER_TYPE(iov[j].type); + + if (type == GSS_IOV_BUFFER_TYPE_DATA) { + if (data != NULL) { + /* only a single DATA buffer can appear */ + code = EINVAL; + goto cleanup; + } + + data = &iov[j]; + tdata = &tiov[i]; + } + if (type == GSS_IOV_BUFFER_TYPE_DATA || + type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY) + tiov[i++] = iov[j]; + } + + if (data == NULL) { + /* a single DATA buffer must be present */ + code = EINVAL; + goto cleanup; + } + + /* PADDING | TRAILER */ + tpadding = &tiov[i++]; + tpadding->type = GSS_IOV_BUFFER_TYPE_PADDING; + tpadding->buffer.length = 0; + tpadding->buffer.value = NULL; + + ttrailer = &tiov[i++]; + ttrailer->type = GSS_IOV_BUFFER_TYPE_TRAILER; + + switch (toktype2) { + case KG2_TOK_MIC_MSG: + case KG2_TOK_WRAP_MSG: + case KG2_TOK_DEL_CTX: { + size_t ec, rrc; + krb5_enctype enctype; + unsigned int k5_headerlen = 0; + unsigned int k5_trailerlen = 0; + + if (ctx->have_acceptor_subkey) + enctype = ctx->acceptor_subkey->keyblock.enctype; + else + enctype = ctx->subkey->keyblock.enctype; + conf_req_flag = ((ptr[0] & FLAG_WRAP_CONFIDENTIAL) != 0); + ec = conf_req_flag ? load_16_be(ptr + 2) : 0; + rrc = load_16_be(ptr + 4); + + if (rrc != 0) { + if (!gss_krb5int_rotate_left((unsigned char *)stream->buffer.value + 16, + stream->buffer.length - 16, rrc)) { + code = ENOMEM; + goto cleanup; + } + store_16_be(0, ptr + 4); /* set RRC to zero */ + } + + if (conf_req_flag) { + code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen); + if (code != 0) + goto cleanup; + theader->buffer.length += k5_headerlen; /* length validated later */ + } + + /* no PADDING for CFX, EC is used instead */ + code = krb5_c_crypto_length(context, enctype, + conf_req_flag ? KRB5_CRYPTO_TYPE_TRAILER : KRB5_CRYPTO_TYPE_CHECKSUM, + &k5_trailerlen); + if (code != 0) + goto cleanup; + + ttrailer->buffer.length = ec + (conf_req_flag ? 16 : 0 /* E(Header) */) + k5_trailerlen; + ttrailer->buffer.value = (unsigned char *)stream->buffer.value + + stream->buffer.length - ttrailer->buffer.length; + break; + } + case KG_TOK_MIC_MSG: + case KG_TOK_WRAP_MSG: + case KG_TOK_DEL_CTX: + theader->buffer.length += ctx->cksum_size + + kg_confounder_size(context, ctx->enc->keyblock.enctype); + + /* + * we can't set the padding accurately until decryption; + * kg_fixup_padding_iov() will take care of this + */ + tpadding->buffer.length = 1; + tpadding->buffer.value = (unsigned char *)stream->buffer.value + stream->buffer.length - 1; + + /* no TRAILER for pre-CFX */ + ttrailer->buffer.length = 0; + ttrailer->buffer.value = NULL; + + break; + default: + code = (OM_uint32)G_BAD_TOK_HEADER; + major_status = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + break; + } + + /* IOV: -----------0-------------+---1---+--2--+----------------3--------------*/ + /* Old: GSS-Header | Conf | Data | Pad | */ + /* CFX: GSS-Header | Kerb-Header | Data | | EC | E(Header) | Kerb-Trailer */ + /* GSS: -------GSS-HEADER--------+-DATA--+-PAD-+----------GSS-TRAILER----------*/ + + /* validate lengths */ + if (stream->buffer.length < theader->buffer.length + + tpadding->buffer.length + + ttrailer->buffer.length) + { + code = (OM_uint32)KRB5_BAD_MSIZE; + major_status = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + + /* setup data */ + tdata->buffer.length = stream->buffer.length - ttrailer->buffer.length - + tpadding->buffer.length - theader->buffer.length; + + assert(data != NULL); + + if (data->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) { + code = kg_allocate_iov(tdata, tdata->buffer.length); + if (code != 0) + goto cleanup; + memcpy(tdata->buffer.value, + (unsigned char *)stream->buffer.value + theader->buffer.length, tdata->buffer.length); + } else + tdata->buffer.value = (unsigned char *)stream->buffer.value + theader->buffer.length; + + assert(i <= iov_count + 2); + + major_status = kg_unseal_iov_token(&code, ctx, conf_state, qop_state, + tiov, i, toktype); + if (major_status == GSS_S_COMPLETE) + *data = *tdata; + else + kg_release_iov(tdata, 1); + +cleanup: + if (tiov != NULL) + free(tiov); + + *minor_status = code; + + return major_status; +} + +OM_uint32 +kg_unseal_iov(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype) +{ + krb5_gss_ctx_id_rec *ctx; + OM_uint32 code; + + ctx = (krb5_gss_ctx_id_rec *)context_handle; + if (ctx->terminated || !ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return GSS_S_NO_CONTEXT; + } + + if (kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM) != NULL) { + code = kg_unseal_stream_iov(minor_status, ctx, conf_state, qop_state, + iov, iov_count, toktype); + } else { + code = kg_unseal_iov_token(minor_status, ctx, conf_state, qop_state, + iov, iov_count, toktype); + } + + return code; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_unwrap_iov(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 major_status; + + major_status = kg_unseal_iov(minor_status, context_handle, + conf_state, qop_state, + iov, iov_count, KG_TOK_WRAP_MSG); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_verify_mic_iov(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 major_status; + + major_status = kg_unseal_iov(minor_status, context_handle, + NULL, qop_state, + iov, iov_count, KG_TOK_MIC_MSG); + + return major_status; +} diff --git a/src/lib/gssapi/krb5/krb5_gss_glue.c b/src/lib/gssapi/krb5/krb5_gss_glue.c new file mode 100644 index 000000000000..b35a2152344e --- /dev/null +++ b/src/lib/gssapi/krb5/krb5_gss_glue.c @@ -0,0 +1,445 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/* + * Copyright (c) 2006-2008, Novell, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The copyright holder's name is not used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +#include "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +gss_krb5_get_tkt_flags(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + krb5_flags *ticket_flags) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_GET_TKT_FLAGS_OID_LENGTH, + GSS_KRB5_GET_TKT_FLAGS_OID }; + OM_uint32 major_status; + gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; + + if (ticket_flags == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + major_status = gss_inquire_sec_context_by_oid(minor_status, + context_handle, + (gss_OID)&req_oid, + &data_set); + if (major_status != GSS_S_COMPLETE) + return major_status; + + if (data_set == GSS_C_NO_BUFFER_SET || + data_set->count != 1 || + data_set->elements[0].length != sizeof(*ticket_flags)) { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + + *ticket_flags = *((krb5_flags *)data_set->elements[0].value); + + gss_release_buffer_set(minor_status, &data_set); + + *minor_status = 0; + + return GSS_S_COMPLETE; +} + +OM_uint32 KRB5_CALLCONV +gss_krb5_copy_ccache(OM_uint32 *minor_status, + gss_cred_id_t cred_handle, + krb5_ccache out_ccache) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_COPY_CCACHE_OID_LENGTH, + GSS_KRB5_COPY_CCACHE_OID }; + OM_uint32 major_status; + gss_buffer_desc req_buffer; + + if (out_ccache == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + req_buffer.value = out_ccache; + req_buffer.length = sizeof(out_ccache); + + major_status = gss_set_cred_option(minor_status, + &cred_handle, + (gss_OID)&req_oid, + &req_buffer); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +gss_krb5_import_cred(OM_uint32 *minor_status, + krb5_ccache id, + krb5_principal keytab_principal, + krb5_keytab keytab, + gss_cred_id_t *cred) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_IMPORT_CRED_OID_LENGTH, + GSS_KRB5_IMPORT_CRED_OID }; + OM_uint32 major_status; + struct krb5_gss_import_cred_req req; + gss_buffer_desc req_buffer; + + if (cred == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *cred = GSS_C_NO_CREDENTIAL; + + req.id = id; + req.keytab_principal = keytab_principal; + req.keytab = keytab; + + req_buffer.value = &req; + req_buffer.length = sizeof(req); + + major_status = gss_set_cred_option(minor_status, + cred, + (gss_OID)&req_oid, + &req_buffer); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +gss_krb5_export_lucid_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + OM_uint32 version, + void **kctx) +{ + unsigned char oid_buf[GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID_LENGTH + 6]; + gss_OID_desc req_oid; + OM_uint32 major_status, minor; + gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; + + if (kctx == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *kctx = NULL; + + req_oid.elements = oid_buf; + req_oid.length = sizeof(oid_buf); + + major_status = generic_gss_oid_compose(minor_status, + GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID, + GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID_LENGTH, + (int)version, + &req_oid); + if (GSS_ERROR(major_status)) + return major_status; + + major_status = gss_inquire_sec_context_by_oid(minor_status, + *context_handle, + &req_oid, + &data_set); + if (GSS_ERROR(major_status)) + return major_status; + + if (data_set == GSS_C_NO_BUFFER_SET || + data_set->count != 1 || + data_set->elements[0].length != sizeof(void *)) { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + + *kctx = *((void **)data_set->elements[0].value); + + /* Clean up the context state (it is an error for + * someone to attempt to use this context again) + */ + (void)gss_delete_sec_context(minor_status, context_handle, NULL); + *context_handle = GSS_C_NO_CONTEXT; + + generic_gss_release_buffer_set(&minor, &data_set); + + return GSS_S_COMPLETE; +} + +OM_uint32 KRB5_CALLCONV +gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status, + gss_cred_id_t cred, + OM_uint32 num_ktypes, + krb5_enctype *ktypes) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID_LENGTH, + GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID }; + OM_uint32 major_status; + struct krb5_gss_set_allowable_enctypes_req req; + gss_buffer_desc req_buffer; + + req.num_ktypes = num_ktypes; + req.ktypes = ktypes; + + req_buffer.length = sizeof(req); + req_buffer.value = &req; + + major_status = gss_set_cred_option(minor_status, + &cred, + (gss_OID)&req_oid, + &req_buffer); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +gss_krb5_ccache_name(OM_uint32 *minor_status, + const char *name, + const char **out_name) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_CCACHE_NAME_OID_LENGTH, + GSS_KRB5_CCACHE_NAME_OID }; + OM_uint32 major_status; + struct krb5_gss_ccache_name_req req; + gss_buffer_desc req_buffer; + + req.name = name; + req.out_name = out_name; + + req_buffer.length = sizeof(req); + req_buffer.value = &req; + + major_status = gssspi_mech_invoke(minor_status, + (gss_OID)gss_mech_krb5, + (gss_OID)&req_oid, + &req_buffer); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, void *kctx) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_FREE_LUCID_SEC_CONTEXT_OID_LENGTH, + GSS_KRB5_FREE_LUCID_SEC_CONTEXT_OID }; + OM_uint32 major_status; + gss_buffer_desc req_buffer; + + req_buffer.length = sizeof(kctx); + req_buffer.value = kctx; + + major_status = gssspi_mech_invoke(minor_status, + (gss_OID)gss_mech_krb5, + (gss_OID)&req_oid, + &req_buffer); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_register_acceptor_identity(const char *keytab) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_OID_LENGTH, + GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_OID }; + OM_uint32 major_status; + OM_uint32 minor_status; + gss_buffer_desc req_buffer; + + req_buffer.length = (keytab == NULL) ? 0 : strlen(keytab); + req_buffer.value = (char *)keytab; + + major_status = gssspi_mech_invoke(&minor_status, + (gss_OID)gss_mech_krb5, + (gss_OID)&req_oid, + &req_buffer); + + return major_status; +} + +#ifndef _WIN32 +krb5_error_code +krb5_gss_use_kdc_context(void) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_USE_KDC_CONTEXT_OID_LENGTH, + GSS_KRB5_USE_KDC_CONTEXT_OID }; + OM_uint32 major_status; + OM_uint32 minor_status; + gss_buffer_desc req_buffer; + krb5_error_code ret; + + req_buffer.length = 0; + req_buffer.value = NULL; + + major_status = gssspi_mech_invoke(&minor_status, + (gss_OID)gss_mech_krb5, + (gss_OID)&req_oid, + &req_buffer); + + if (major_status != GSS_S_COMPLETE) { + if (minor_status != 0) + ret = (krb5_error_code)minor_status; + else + ret = KRB5KRB_ERR_GENERIC; + } else + ret = 0; + + return ret; +} +#endif + +/* + * This API should go away and be replaced with an accessor + * into a gss_name_t. + */ +OM_uint32 KRB5_CALLCONV +gsskrb5_extract_authz_data_from_sec_context(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + int ad_type, + gss_buffer_t ad_data) +{ + gss_OID_desc req_oid; + unsigned char oid_buf[GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH + 6]; + OM_uint32 major_status; + gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; + + if (ad_data == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + req_oid.elements = oid_buf; + req_oid.length = sizeof(oid_buf); + + major_status = generic_gss_oid_compose(minor_status, + GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID, + GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH, + ad_type, + &req_oid); + if (GSS_ERROR(major_status)) + return major_status; + + major_status = gss_inquire_sec_context_by_oid(minor_status, + context_handle, + (gss_OID)&req_oid, + &data_set); + if (major_status != GSS_S_COMPLETE) { + return major_status; + } + + if (data_set == GSS_C_NO_BUFFER_SET || + data_set->count != 1) { + return GSS_S_FAILURE; + } + + ad_data->length = data_set->elements[0].length; + ad_data->value = data_set->elements[0].value; + + data_set->elements[0].length = 0; + data_set->elements[0].value = NULL; + + data_set->count = 0; + + gss_release_buffer_set(minor_status, &data_set); + + return GSS_S_COMPLETE; +} + +OM_uint32 KRB5_CALLCONV +gss_krb5_set_cred_rcache(OM_uint32 *minor_status, + gss_cred_id_t cred, + krb5_rcache rcache) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_SET_CRED_RCACHE_OID_LENGTH, + GSS_KRB5_SET_CRED_RCACHE_OID }; + OM_uint32 major_status; + gss_buffer_desc req_buffer; + + req_buffer.length = sizeof(rcache); + req_buffer.value = rcache; + + major_status = gss_set_cred_option(minor_status, + &cred, + (gss_OID)&req_oid, + &req_buffer); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +gsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + krb5_timestamp *authtime) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID_LENGTH, + GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID }; + OM_uint32 major_status; + gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; + + if (authtime == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + major_status = gss_inquire_sec_context_by_oid(minor_status, + context_handle, + (gss_OID)&req_oid, + &data_set); + if (major_status != GSS_S_COMPLETE) + return major_status; + + if (data_set == GSS_C_NO_BUFFER_SET || + data_set->count != 1 || + data_set->elements[0].length != sizeof(*authtime)) { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + + *authtime = *((krb5_timestamp *)data_set->elements[0].value); + + gss_release_buffer_set(minor_status, &data_set); + + *minor_status = 0; + + return GSS_S_COMPLETE; +} diff --git a/src/lib/gssapi/krb5/lucid_context.c b/src/lib/gssapi/krb5/lucid_context.c new file mode 100644 index 000000000000..a894f0e7355c --- /dev/null +++ b/src/lib/gssapi/krb5/lucid_context.c @@ -0,0 +1,292 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/lucid_context.c */ +/* + * Copyright 2004, 2008 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. + */ + +/* Externalize a "lucid" security context from a krb5_gss_ctx_id_rec + * structure. */ +#include "gssapiP_krb5.h" +#include "gssapi_krb5.h" + +/* + * Local routine prototypes + */ +static void +free_external_lucid_ctx_v1( + gss_krb5_lucid_context_v1_t *ctx); + +static void +free_lucid_key_data( + gss_krb5_lucid_key_t *key); + +static krb5_error_code +copy_keyblock_to_lucid_key( + krb5_keyblock *k5key, + gss_krb5_lucid_key_t *lkey); + +static krb5_error_code +make_external_lucid_ctx_v1( + krb5_gss_ctx_id_rec * gctx, + int version, + void **out_ptr); + + +/* + * Exported routines + */ + +OM_uint32 +gss_krb5int_export_lucid_sec_context( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + krb5_error_code kret = 0; + OM_uint32 retval; + krb5_gss_ctx_id_t ctx = (krb5_gss_ctx_id_t)context_handle; + void *lctx = NULL; + int version = 0; + gss_buffer_desc rep; + + /* Assume failure */ + retval = GSS_S_FAILURE; + *minor_status = 0; + *data_set = GSS_C_NO_BUFFER_SET; + + if (ctx->terminated || !ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return GSS_S_NO_CONTEXT; + } + + retval = generic_gss_oid_decompose(minor_status, + GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID, + GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID_LENGTH, + desired_object, + &version); + if (GSS_ERROR(retval)) + return retval; + + /* Externalize a structure of the right version */ + switch (version) { + case 1: + kret = make_external_lucid_ctx_v1((krb5_pointer)ctx, + version, &lctx); + break; + default: + kret = (OM_uint32) KG_LUCID_VERSION; + break; + } + + if (kret) + goto error_out; + + rep.value = &lctx; + rep.length = sizeof(lctx); + + retval = generic_gss_add_buffer_set_member(minor_status, &rep, data_set); + if (GSS_ERROR(retval)) + goto error_out; + +error_out: + if (*minor_status == 0) + *minor_status = (OM_uint32) kret; + return(retval); +} + +/* + * Frees the storage associated with an + * exported lucid context structure. + */ +OM_uint32 +gss_krb5int_free_lucid_sec_context( + OM_uint32 *minor_status, + const gss_OID desired_mech, + const gss_OID desired_object, + gss_buffer_t value) +{ + OM_uint32 retval; + krb5_error_code kret = 0; + int version; + void *kctx; + + /* Assume failure */ + retval = GSS_S_FAILURE; + *minor_status = 0; + + kctx = value->value; + if (!kctx) { + kret = EINVAL; + goto error_out; + } + + /* Determine version and call correct free routine */ + version = ((gss_krb5_lucid_context_version_t *)kctx)->version; + switch (version) { + case 1: + free_external_lucid_ctx_v1((gss_krb5_lucid_context_v1_t*) kctx); + break; + default: + kret = EINVAL; + break; + } + + if (kret) + goto error_out; + + /* Success! */ + *minor_status = 0; + retval = GSS_S_COMPLETE; + + return (retval); + +error_out: + if (*minor_status == 0) + *minor_status = (OM_uint32) kret; + return(retval); +} + +/* + * Local routines + */ + +static krb5_error_code +make_external_lucid_ctx_v1( + krb5_gss_ctx_id_rec * gctx, + int version, + void **out_ptr) +{ + gss_krb5_lucid_context_v1_t *lctx = NULL; + unsigned int bufsize = sizeof(gss_krb5_lucid_context_v1_t); + krb5_error_code retval; + + /* Allocate the structure */ + if ((lctx = xmalloc(bufsize)) == NULL) { + retval = ENOMEM; + goto error_out; + } + + memset(lctx, 0, bufsize); + + lctx->version = 1; + lctx->initiate = gctx->initiate ? 1 : 0; + lctx->endtime = gctx->krb_times.endtime; + lctx->send_seq = gctx->seq_send; + lctx->recv_seq = gctx->seq_recv; + lctx->protocol = gctx->proto; + /* gctx->proto == 0 ==> rfc1964-style key information + gctx->proto == 1 ==> cfx-style (draft-ietf-krb-wg-gssapi-cfx-07) keys */ + if (gctx->proto == 0) { + lctx->rfc1964_kd.sign_alg = gctx->signalg; + lctx->rfc1964_kd.seal_alg = gctx->sealalg; + /* Copy key */ + if ((retval = copy_keyblock_to_lucid_key(&gctx->seq->keyblock, + &lctx->rfc1964_kd.ctx_key))) + goto error_out; + } + else if (gctx->proto == 1) { + /* Copy keys */ + /* (subkey is always present, either a copy of the kerberos + session key or a subkey) */ + if ((retval = copy_keyblock_to_lucid_key(&gctx->subkey->keyblock, + &lctx->cfx_kd.ctx_key))) + goto error_out; + if (gctx->have_acceptor_subkey) { + if ((retval = copy_keyblock_to_lucid_key(&gctx->acceptor_subkey->keyblock, + &lctx->cfx_kd.acceptor_subkey))) + goto error_out; + lctx->cfx_kd.have_acceptor_subkey = 1; + } + } + else { + xfree(lctx); + return EINVAL; /* XXX better error code? */ + } + + /* Success! */ + *out_ptr = lctx; + return 0; + +error_out: + if (lctx) { + free_external_lucid_ctx_v1(lctx); + } + return retval; + +} + +/* Copy the contents of a krb5_keyblock to a gss_krb5_lucid_key_t structure */ +static krb5_error_code +copy_keyblock_to_lucid_key( + krb5_keyblock *k5key, + gss_krb5_lucid_key_t *lkey) +{ + if (!k5key || !k5key->contents || k5key->length == 0) + return EINVAL; + + memset(lkey, 0, sizeof(gss_krb5_lucid_key_t)); + + /* Allocate storage for the key data */ + if ((lkey->data = xmalloc(k5key->length)) == NULL) { + return ENOMEM; + } + memcpy(lkey->data, k5key->contents, k5key->length); + lkey->length = k5key->length; + lkey->type = k5key->enctype; + + return 0; +} + + +/* Free any storage associated with a gss_krb5_lucid_key_t structure */ +static void +free_lucid_key_data( + gss_krb5_lucid_key_t *key) +{ + if (key) { + if (key->data && key->length) { + zap(key->data, key->length); + xfree(key->data); + zap(key, sizeof(gss_krb5_lucid_key_t)); + } + } +} +/* Free any storage associated with a gss_krb5_lucid_context_v1 structure */ +static void +free_external_lucid_ctx_v1( + gss_krb5_lucid_context_v1_t *ctx) +{ + if (ctx) { + if (ctx->protocol == 0) { + free_lucid_key_data(&ctx->rfc1964_kd.ctx_key); + } + if (ctx->protocol == 1) { + free_lucid_key_data(&ctx->cfx_kd.ctx_key); + if (ctx->cfx_kd.have_acceptor_subkey) + free_lucid_key_data(&ctx->cfx_kd.acceptor_subkey); + } + xfree(ctx); + ctx = NULL; + } +} diff --git a/src/lib/gssapi/krb5/naming_exts.c b/src/lib/gssapi/krb5/naming_exts.c new file mode 100644 index 000000000000..6062a6dd8052 --- /dev/null +++ b/src/lib/gssapi/krb5/naming_exts.c @@ -0,0 +1,684 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/naming_exts.c */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" + +krb5_error_code +kg_init_name(krb5_context context, krb5_principal principal, + char *service, char *host, krb5_authdata_context ad_context, + krb5_flags flags, krb5_gss_name_t *ret_name) +{ + krb5_error_code code; + krb5_gss_name_t name; + + *ret_name = NULL; + + assert(principal != NULL); + + if (principal == NULL) + return EINVAL; + + name = xmalloc(sizeof(krb5_gss_name_rec)); + if (name == NULL) + return ENOMEM; + + memset(name, 0, sizeof(krb5_gss_name_rec)); + + code = k5_mutex_init(&name->lock); + if (code != 0) + goto cleanup; + + if ((flags & KG_INIT_NAME_NO_COPY) == 0) { + code = krb5_copy_principal(context, principal, &name->princ); + if (code != 0) + goto cleanup; + + if (ad_context != NULL) { + code = krb5_authdata_context_copy(context, + ad_context, + &name->ad_context); + if (code != 0) + goto cleanup; + } + + code = ENOMEM; + if (service != NULL) { + name->service = strdup(service); + if (name->service == NULL) + goto cleanup; + } + if (host != NULL) { + name->host = strdup(host); + if (name->host == NULL) + goto cleanup; + } + code = 0; + } else { + name->princ = principal; + name->service = service; + name->host = host; + name->ad_context = ad_context; + } + + *ret_name = name; + +cleanup: + if (code != 0) + kg_release_name(context, &name); + + return code; +} + +krb5_error_code +kg_release_name(krb5_context context, + krb5_gss_name_t *name) +{ + if (*name != NULL) { + krb5_free_principal(context, (*name)->princ); + free((*name)->service); + free((*name)->host); + krb5_authdata_context_free(context, (*name)->ad_context); + k5_mutex_destroy(&(*name)->lock); + free(*name); + *name = NULL; + } + + return 0; +} + +krb5_error_code +kg_duplicate_name(krb5_context context, + const krb5_gss_name_t src, + krb5_gss_name_t *dst) +{ + krb5_error_code code; + + k5_mutex_lock(&src->lock); + code = kg_init_name(context, src->princ, src->service, src->host, + src->ad_context, 0, dst); + k5_mutex_unlock(&src->lock); + return code; +} + + +krb5_boolean +kg_compare_name(krb5_context context, + krb5_gss_name_t name1, + krb5_gss_name_t name2) +{ + return krb5_principal_compare(context, name1->princ, name2->princ); +} + +/* Determine the principal to use for an acceptor name, which is different from + * name->princ for host-based names. */ +krb5_boolean +kg_acceptor_princ(krb5_context context, krb5_gss_name_t name, + krb5_principal *princ_out) +{ + krb5_error_code code; + const char *host; + char *tmp = NULL; + + *princ_out = NULL; + if (name == NULL) + return 0; + + /* If it's not a host-based name, just copy name->princ. */ + if (name->service == NULL) + return krb5_copy_principal(context, name->princ, princ_out); + + if (name->host != NULL && name->princ->length == 2) { + /* If a host was given, we have to use the canonicalized form of it (as + * given by krb5_sname_to_principal) for backward compatibility. */ + const krb5_data *d = &name->princ->data[1]; + tmp = k5memdup0(d->data, d->length, &code); + if (tmp == NULL) + return ENOMEM; + host = tmp; + } else /* No host was given; use an empty string. */ + host = ""; + + code = krb5_build_principal(context, princ_out, 0, "", name->service, host, + (char *)NULL); + if (*princ_out != NULL) + (*princ_out)->type = KRB5_NT_SRV_HST; + free(tmp); + return code; +} + +static OM_uint32 +kg_map_name_error(OM_uint32 *minor_status, krb5_error_code code) +{ + OM_uint32 major_status; + + switch (code) { + case 0: + major_status = GSS_S_COMPLETE; + break; + case ENOENT: + case EPERM: + major_status = GSS_S_UNAVAILABLE; + break; + default: + major_status = GSS_S_FAILURE; + break; + } + + *minor_status = code; + + return major_status; +} + +/* Owns data on success */ +static krb5_error_code +data_list_to_buffer_set(krb5_context context, + krb5_data *data, + gss_buffer_set_t *buffer_set) +{ + gss_buffer_set_t set = GSS_C_NO_BUFFER_SET; + OM_uint32 minor_status; + int i; + krb5_error_code code = 0; + + if (data == NULL) + goto cleanup; + + if (buffer_set == NULL) + goto cleanup; + + if (GSS_ERROR(gss_create_empty_buffer_set(&minor_status, + &set))) { + assert(minor_status != 0); + code = minor_status; + goto cleanup; + } + + for (i = 0; data[i].data != NULL; i++) + ; + + set->count = i; + set->elements = gssalloc_calloc(i, sizeof(gss_buffer_desc)); + if (set->elements == NULL) { + gss_release_buffer_set(&minor_status, &set); + code = ENOMEM; + goto cleanup; + } + + /* + * Copy last element first so data remains properly + * NULL-terminated in case of allocation failure + * in data_to_gss() on windows. + */ + for (i = set->count-1; i >= 0; i--) { + if (data_to_gss(&data[i], &set->elements[i])) { + gss_release_buffer_set(&minor_status, &set); + code = ENOMEM; + goto cleanup; + } + } +cleanup: + krb5int_free_data_list(context, data); + + if (buffer_set != NULL) + *buffer_set = set; + + return code; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_name(OM_uint32 *minor_status, + gss_name_t name, + int *name_is_MN, + gss_OID *MN_mech, + gss_buffer_set_t *attrs) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t kname; + krb5_data *kattrs = NULL; + + if (minor_status != NULL) + *minor_status = 0; + + if (attrs != NULL) + *attrs = GSS_C_NO_BUFFER_SET; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + kname = (krb5_gss_name_t)name; + + k5_mutex_lock(&kname->lock); + + if (kname->ad_context == NULL) { + code = krb5_authdata_context_init(context, &kname->ad_context); + if (code != 0) + goto cleanup; + } + + code = krb5_authdata_get_attribute_types(context, + kname->ad_context, + &kattrs); + if (code != 0) + goto cleanup; + + code = data_list_to_buffer_set(context, kattrs, attrs); + kattrs = NULL; + if (code != 0) + goto cleanup; + +cleanup: + k5_mutex_unlock(&kname->lock); + krb5int_free_data_list(context, kattrs); + + krb5_free_context(context); + + return kg_map_name_error(minor_status, code); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_get_name_attribute(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t kname; + krb5_data kattr; + krb5_boolean kauthenticated; + krb5_boolean kcomplete; + krb5_data kvalue; + krb5_data kdisplay_value; + + if (minor_status != NULL) + *minor_status = 0; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + kname = (krb5_gss_name_t)name; + k5_mutex_lock(&kname->lock); + + if (kname->ad_context == NULL) { + code = krb5_authdata_context_init(context, &kname->ad_context); + if (code != 0) { + *minor_status = code; + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + return GSS_S_UNAVAILABLE; + } + } + + kattr.data = (char *)attr->value; + kattr.length = attr->length; + + kauthenticated = FALSE; + kcomplete = FALSE; + + code = krb5_authdata_get_attribute(context, + kname->ad_context, + &kattr, + &kauthenticated, + &kcomplete, + value ? &kvalue : NULL, + display_value ? &kdisplay_value : NULL, + more); + if (code == 0) { + if (value != NULL) + code = data_to_gss(&kvalue, value); + + if (authenticated != NULL) + *authenticated = kauthenticated; + if (complete != NULL) + *complete = kcomplete; + + if (display_value != NULL) { + if (code == 0) + code = data_to_gss(&kdisplay_value, display_value); + else + free(kdisplay_value.data); + } + } + + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + + return kg_map_name_error(minor_status, code); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_set_name_attribute(OM_uint32 *minor_status, + gss_name_t name, + int complete, + gss_buffer_t attr, + gss_buffer_t value) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t kname; + krb5_data kattr; + krb5_data kvalue; + + if (minor_status != NULL) + *minor_status = 0; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + kname = (krb5_gss_name_t)name; + k5_mutex_lock(&kname->lock); + + if (kname->ad_context == NULL) { + code = krb5_authdata_context_init(context, &kname->ad_context); + if (code != 0) { + *minor_status = code; + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + return GSS_S_UNAVAILABLE; + } + } + + kattr.data = (char *)attr->value; + kattr.length = attr->length; + + kvalue.data = (char *)value->value; + kvalue.length = value->length; + + code = krb5_authdata_set_attribute(context, + kname->ad_context, + complete, + &kattr, + &kvalue); + + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + + return kg_map_name_error(minor_status, code); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_delete_name_attribute(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t attr) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t kname; + krb5_data kattr; + + if (minor_status != NULL) + *minor_status = 0; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + kname = (krb5_gss_name_t)name; + k5_mutex_lock(&kname->lock); + + if (kname->ad_context == NULL) { + code = krb5_authdata_context_init(context, &kname->ad_context); + if (code != 0) { + *minor_status = code; + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + return GSS_S_UNAVAILABLE; + } + } + + kattr.data = (char *)attr->value; + kattr.length = attr->length; + + code = krb5_authdata_delete_attribute(context, + kname->ad_context, + &kattr); + + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + + return kg_map_name_error(minor_status, code); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_map_name_to_any(OM_uint32 *minor_status, + gss_name_t name, + int authenticated, + gss_buffer_t type_id, + gss_any_t *output) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t kname; + char *kmodule; + + if (minor_status != NULL) + *minor_status = 0; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + kname = (krb5_gss_name_t)name; + k5_mutex_lock(&kname->lock); + + if (kname->ad_context == NULL) { + code = krb5_authdata_context_init(context, &kname->ad_context); + if (code != 0) { + *minor_status = code; + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + return GSS_S_UNAVAILABLE; + } + } + + kmodule = (char *)type_id->value; + if (kmodule[type_id->length] != '\0') { + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + return GSS_S_UNAVAILABLE; + } + + code = krb5_authdata_export_internal(context, + kname->ad_context, + authenticated, + kmodule, + (void **)output); + + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + + return kg_map_name_error(minor_status, code); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_release_any_name_mapping(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t type_id, + gss_any_t *input) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t kname; + char *kmodule; + + if (minor_status != NULL) + *minor_status = 0; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + kname = (krb5_gss_name_t)name; + k5_mutex_lock(&kname->lock); + + if (kname->ad_context == NULL) { + code = krb5_authdata_context_init(context, &kname->ad_context); + if (code != 0) { + *minor_status = code; + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + return GSS_S_UNAVAILABLE; + } + } + + kmodule = (char *)type_id->value; + if (kmodule[type_id->length] != '\0') { + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + return GSS_S_UNAVAILABLE; + } + + code = krb5_authdata_free_internal(context, + kname->ad_context, + kmodule, + *input); + if (code == 0) + *input = NULL; + + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + + return kg_map_name_error(minor_status, code); + +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_export_name_composite(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t exp_composite_name) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t kname; + krb5_data *attrs = NULL; + char *princstr = NULL; + unsigned char *cp; + size_t princlen; + + if (minor_status != NULL) + *minor_status = 0; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + kname = (krb5_gss_name_t)name; + k5_mutex_lock(&kname->lock); + + code = krb5_unparse_name(context, kname->princ, &princstr); + if (code != 0) + goto cleanup; + + princlen = strlen(princstr); + + if (kname->ad_context != NULL) { + code = krb5_authdata_export_attributes(context, + kname->ad_context, + AD_USAGE_MASK, + &attrs); + if (code != 0) + goto cleanup; + } + + /* 04 02 OID Name AuthData */ + + exp_composite_name->length = 10 + gss_mech_krb5->length + princlen; + exp_composite_name->length += 4; /* length of encoded attributes */ + if (attrs != NULL) + exp_composite_name->length += attrs->length; + exp_composite_name->value = malloc(exp_composite_name->length); + if (exp_composite_name->value == NULL) { + code = ENOMEM; + goto cleanup; + } + + cp = exp_composite_name->value; + + /* Note: we assume the OID will be less than 128 bytes... */ + *cp++ = 0x04; + *cp++ = 0x02; + + store_16_be(gss_mech_krb5->length + 2, cp); + cp += 2; + *cp++ = 0x06; + *cp++ = (gss_mech_krb5->length) & 0xFF; + memcpy(cp, gss_mech_krb5->elements, gss_mech_krb5->length); + cp += gss_mech_krb5->length; + + store_32_be(princlen, cp); + cp += 4; + memcpy(cp, princstr, princlen); + cp += princlen; + + store_32_be(attrs != NULL ? attrs->length : 0, cp); + cp += 4; + + if (attrs != NULL) { + memcpy(cp, attrs->data, attrs->length); + cp += attrs->length; + } + +cleanup: + krb5_free_unparsed_name(context, princstr); + krb5_free_data(context, attrs); + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + + return kg_map_name_error(minor_status, code); +} + +#if 0 +OM_uint32 +krb5_gss_display_name_ext(OM_uint32 *minor_status, + gss_name_t name, + gss_OID display_as_name_type, + gss_buffer_t display_name) +{ +} +#endif diff --git a/src/lib/gssapi/krb5/prf.c b/src/lib/gssapi/krb5/prf.c new file mode 100644 index 000000000000..e897074fc193 --- /dev/null +++ b/src/lib/gssapi/krb5/prf.c @@ -0,0 +1,139 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/prf.c */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" + +#ifndef MIN /* Usually found in <sys/param.h>. */ +#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b)) +#endif + +OM_uint32 KRB5_CALLCONV +krb5_gss_pseudo_random(OM_uint32 *minor_status, + gss_ctx_id_t context, + int prf_key, + const gss_buffer_t prf_in, + ssize_t desired_output_len, + gss_buffer_t prf_out) +{ + krb5_error_code code; + krb5_key key = NULL; + krb5_gss_ctx_id_t ctx; + int i; + OM_uint32 minor; + size_t prflen; + krb5_data t, ns; + unsigned char *p; + + prf_out->length = 0; + prf_out->value = NULL; + + t.length = 0; + t.data = NULL; + + ns.length = 0; + ns.data = NULL; + + ctx = (krb5_gss_ctx_id_t)context; + if (ctx->terminated || !ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return GSS_S_NO_CONTEXT; + } + + switch (prf_key) { + case GSS_C_PRF_KEY_FULL: + if (ctx->have_acceptor_subkey) { + key = ctx->acceptor_subkey; + break; + } + /* fallthrough */ + case GSS_C_PRF_KEY_PARTIAL: + key = ctx->subkey; + break; + default: + code = EINVAL; + goto cleanup; + } + + if (key == NULL) { + code = EINVAL; + goto cleanup; + } + + if (desired_output_len == 0) + return GSS_S_COMPLETE; + + prf_out->value = k5alloc(desired_output_len, &code); + if (prf_out->value == NULL) { + code = KG_INPUT_TOO_LONG; + goto cleanup; + } + prf_out->length = desired_output_len; + + code = krb5_c_prf_length(ctx->k5_context, + krb5_k_key_enctype(ctx->k5_context, key), + &prflen); + if (code != 0) + goto cleanup; + + ns.length = 4 + prf_in->length; + ns.data = k5alloc(ns.length, &code); + if (ns.data == NULL) { + code = KG_INPUT_TOO_LONG; + goto cleanup; + } + + t.length = prflen; + t.data = k5alloc(t.length, &code); + if (t.data == NULL) + goto cleanup; + + memcpy(ns.data + 4, prf_in->value, prf_in->length); + i = 0; + p = (unsigned char *)prf_out->value; + while (desired_output_len > 0) { + store_32_be(i, ns.data); + + code = krb5_k_prf(ctx->k5_context, key, &ns, &t); + if (code != 0) + goto cleanup; + + memcpy(p, t.data, MIN(t.length, desired_output_len)); + + p += t.length; + desired_output_len -= t.length; + i++; + } + +cleanup: + if (code != 0) + gss_release_buffer(&minor, prf_out); + krb5_free_data_contents(ctx->k5_context, &ns); + krb5_free_data_contents(ctx->k5_context, &t); + + *minor_status = (OM_uint32)code; + return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; +} diff --git a/src/lib/gssapi/krb5/process_context_token.c b/src/lib/gssapi/krb5/process_context_token.c new file mode 100644 index 000000000000..a672f48c859b --- /dev/null +++ b/src/lib/gssapi/krb5/process_context_token.c @@ -0,0 +1,66 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" + +/* + * $Id$ + */ + +OM_uint32 KRB5_CALLCONV +krb5_gss_process_context_token(minor_status, context_handle, + token_buffer) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_buffer_t token_buffer; +{ + krb5_gss_ctx_id_rec *ctx; + OM_uint32 majerr; + + ctx = (krb5_gss_ctx_id_t) context_handle; + + if (ctx->terminated || !ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return(GSS_S_NO_CONTEXT); + } + + /* We only support context deletion tokens for now, and RFC 4121 does not + * define a context deletion token. */ + if (ctx->proto) { + *minor_status = 0; + return(GSS_S_DEFECTIVE_TOKEN); + } + + /* "unseal" the token */ + + if (GSS_ERROR(majerr = kg_unseal(minor_status, context_handle, + token_buffer, + GSS_C_NO_BUFFER, NULL, NULL, + KG_TOK_DEL_CTX))) + return(majerr); + + /* Mark the context as terminated, but do not delete it (as that would + * leave the caller with a dangling context handle). */ + ctx->terminated = 1; + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/krb5/rel_cred.c b/src/lib/gssapi/krb5/rel_cred.c new file mode 100644 index 000000000000..8db745086009 --- /dev/null +++ b/src/lib/gssapi/krb5/rel_cred.c @@ -0,0 +1,101 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_release_cred(minor_status, cred_handle) + OM_uint32 *minor_status; + gss_cred_id_t *cred_handle; +{ + krb5_context context; + krb5_gss_cred_id_t cred; + krb5_error_code code1, code2, code3; + + code1 = krb5_gss_init_context(&context); + if (code1) { + *minor_status = code1; + return GSS_S_FAILURE; + } + + if (*cred_handle == GSS_C_NO_CREDENTIAL) { + *minor_status = 0; + krb5_free_context(context); + return(GSS_S_COMPLETE); + } + + cred = (krb5_gss_cred_id_t)*cred_handle; + + k5_mutex_destroy(&cred->lock); + /* ignore error destroying mutex */ + + if (cred->ccache) { + if (cred->destroy_ccache) + code1 = krb5_cc_destroy(context, cred->ccache); + else + code1 = krb5_cc_close(context, cred->ccache); + } else + code1 = 0; + + if (cred->client_keytab) + krb5_kt_close(context, cred->client_keytab); + +#ifndef LEAN_CLIENT + if (cred->keytab) + code2 = krb5_kt_close(context, cred->keytab); + else +#endif /* LEAN_CLIENT */ + code2 = 0; + + if (cred->rcache) + code3 = krb5_rc_close(context, cred->rcache); + else + code3 = 0; + if (cred->name) + kg_release_name(context, &cred->name); + + krb5_free_principal(context, cred->impersonator); + + if (cred->req_enctypes) + free(cred->req_enctypes); + + if (cred->password != NULL) + zapfree(cred->password, strlen(cred->password)); + + xfree(cred); + + *cred_handle = NULL; + + *minor_status = 0; + if (code1) + *minor_status = code1; + if (code2) + *minor_status = code2; + if (code3) + *minor_status = code3; + + if (*minor_status) + save_error_info(*minor_status, context); + krb5_free_context(context); + return(*minor_status?GSS_S_FAILURE:GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/krb5/rel_name.c b/src/lib/gssapi/krb5/rel_name.c new file mode 100644 index 000000000000..3dabe32f3342 --- /dev/null +++ b/src/lib/gssapi/krb5/rel_name.c @@ -0,0 +1,47 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_release_name(minor_status, input_name) + OM_uint32 *minor_status; + gss_name_t *input_name; +{ + krb5_context context; + krb5_error_code code; + + code = krb5_gss_init_context(&context); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } + + kg_release_name(context, (krb5_gss_name_t *)input_name); + krb5_free_context(context); + + *input_name = (gss_name_t) NULL; + + *minor_status = 0; + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/krb5/rel_oid.c b/src/lib/gssapi/krb5/rel_oid.c new file mode 100644 index 000000000000..739efe4680b7 --- /dev/null +++ b/src/lib/gssapi/krb5/rel_oid.c @@ -0,0 +1,78 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/rel_oid.c - Release an OID */ +/* + * Copyright 1995, 2007 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 "gssapiP_krb5.h" + +OM_uint32 +krb5_gss_release_oid(minor_status, oid) + OM_uint32 *minor_status; + gss_OID *oid; +{ + /* + * The V2 API says the following! + * + * gss_release_oid[()] will recognize any of the GSSAPI's own OID values, + * and will silently ignore attempts to free these OIDs; for other OIDs + * it will call the C free() routine for both the OID data and the + * descriptor. This allows applications to freely mix their own heap- + * allocated OID values with OIDs returned by GSS-API. + */ + if (krb5_gss_internal_release_oid(minor_status, oid) != GSS_S_COMPLETE) { + /* Pawn it off on the generic routine */ + return(generic_gss_release_oid(minor_status, oid)); + } + else { + *oid = GSS_C_NO_OID; + *minor_status = 0; + return(GSS_S_COMPLETE); + } +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_internal_release_oid(minor_status, oid) + OM_uint32 *minor_status; + gss_OID *oid; +{ + /* + * This function only knows how to release internal OIDs. It will + * return GSS_S_CONTINUE_NEEDED for any OIDs it does not recognize. + */ + + *minor_status = 0; + if ((*oid != gss_mech_krb5) && + (*oid != gss_mech_krb5_old) && + (*oid != gss_mech_krb5_wrong) && + (*oid != gss_mech_iakerb) && + (*oid != gss_nt_krb5_name) && + (*oid != gss_nt_krb5_principal)) { + /* We don't know about this OID */ + return(GSS_S_CONTINUE_NEEDED); + } + else { + *oid = GSS_C_NO_OID; + return(GSS_S_COMPLETE); + } +} diff --git a/src/lib/gssapi/krb5/s4u_gss_glue.c b/src/lib/gssapi/krb5/s4u_gss_glue.c new file mode 100644 index 000000000000..ff1c310bce5e --- /dev/null +++ b/src/lib/gssapi/krb5/s4u_gss_glue.c @@ -0,0 +1,308 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ +#include "k5-int.h" +#include "gssapiP_krb5.h" +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif +#include <assert.h> + +static int +kg_is_initiator_cred(krb5_gss_cred_id_t cred) +{ + return (cred->usage == GSS_C_INITIATE || cred->usage == GSS_C_BOTH) && + (cred->ccache != NULL); +} + +static OM_uint32 +kg_impersonate_name(OM_uint32 *minor_status, + const krb5_gss_cred_id_t impersonator_cred, + const krb5_gss_name_t user, + OM_uint32 time_req, + krb5_gss_cred_id_t *output_cred, + OM_uint32 *time_rec, + krb5_context context) +{ + OM_uint32 major_status; + krb5_error_code code; + krb5_creds in_creds, *out_creds = NULL; + + *output_cred = NULL; + memset(&in_creds, 0, sizeof(in_creds)); + + in_creds.client = user->princ; + in_creds.server = impersonator_cred->name->princ; + + if (impersonator_cred->req_enctypes != NULL) + in_creds.keyblock.enctype = impersonator_cred->req_enctypes[0]; + + k5_mutex_lock(&user->lock); + + if (user->ad_context != NULL) { + code = krb5_authdata_export_authdata(context, + user->ad_context, + AD_USAGE_TGS_REQ, + &in_creds.authdata); + if (code != 0) { + k5_mutex_unlock(&user->lock); + *minor_status = code; + return GSS_S_FAILURE; + } + } + + k5_mutex_unlock(&user->lock); + + code = krb5_get_credentials_for_user(context, + KRB5_GC_CANONICALIZE | KRB5_GC_NO_STORE, + impersonator_cred->ccache, + &in_creds, + NULL, &out_creds); + if (code != 0) { + krb5_free_authdata(context, in_creds.authdata); + *minor_status = code; + return GSS_S_FAILURE; + } + + major_status = kg_compose_deleg_cred(minor_status, + impersonator_cred, + out_creds, + time_req, + output_cred, + time_rec, + context); + + krb5_free_authdata(context, in_creds.authdata); + krb5_free_creds(context, out_creds); + + return major_status; +} + +/* The mechglue always passes null desired_mechs and actual_mechs. */ +OM_uint32 KRB5_CALLCONV +krb5_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + const gss_name_t desired_name, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + OM_uint32 major_status; + krb5_error_code code; + krb5_gss_cred_id_t imp_cred = (krb5_gss_cred_id_t)impersonator_cred_handle; + krb5_gss_cred_id_t cred; + krb5_context context; + + if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (desired_name == GSS_C_NO_NAME) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (output_cred_handle == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (cred_usage != GSS_C_INITIATE) { + *minor_status = (OM_uint32)G_BAD_USAGE; + return GSS_S_FAILURE; + } + + if (imp_cred->usage != GSS_C_INITIATE && imp_cred->usage != GSS_C_BOTH) { + *minor_status = 0; + return GSS_S_NO_CRED; + } + + *output_cred_handle = GSS_C_NO_CREDENTIAL; + if (time_rec != NULL) + *time_rec = 0; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + major_status = kg_cred_resolve(minor_status, context, + impersonator_cred_handle, NULL); + if (GSS_ERROR(major_status)) { + krb5_free_context(context); + return major_status; + } + + major_status = kg_impersonate_name(minor_status, + imp_cred, + (krb5_gss_name_t)desired_name, + time_req, + &cred, + time_rec, + context); + + if (!GSS_ERROR(major_status)) + *output_cred_handle = (gss_cred_id_t)cred; + + k5_mutex_unlock(&imp_cred->lock); + krb5_free_context(context); + + return major_status; + +} + +/* + * Set up cred to be an S4U2Proxy credential by copying in the impersonator's + * creds, setting a cache config variable with the impersonator principal name, + * and saving the impersonator principal name in the cred structure. + */ +static krb5_error_code +make_proxy_cred(krb5_context context, krb5_gss_cred_id_t cred, + krb5_gss_cred_id_t impersonator_cred) +{ + krb5_error_code code; + krb5_data data; + char *str; + + code = krb5_cc_copy_creds(context, impersonator_cred->ccache, + cred->ccache); + if (code) + return code; + + code = krb5_unparse_name(context, impersonator_cred->name->princ, &str); + if (code) + return code; + + data = string2data(str); + code = krb5_cc_set_config(context, cred->ccache, NULL, + KRB5_CC_CONF_PROXY_IMPERSONATOR, &data); + krb5_free_unparsed_name(context, str); + if (code) + return code; + + return krb5_copy_principal(context, impersonator_cred->name->princ, + &cred->impersonator); +} + +OM_uint32 +kg_compose_deleg_cred(OM_uint32 *minor_status, + krb5_gss_cred_id_t impersonator_cred, + krb5_creds *subject_creds, + OM_uint32 time_req, + krb5_gss_cred_id_t *output_cred, + OM_uint32 *time_rec, + krb5_context context) +{ + OM_uint32 major_status; + krb5_error_code code; + krb5_gss_cred_id_t cred = NULL; + + *output_cred = NULL; + k5_mutex_assert_locked(&impersonator_cred->lock); + + if (!kg_is_initiator_cred(impersonator_cred) || + impersonator_cred->name == NULL || + impersonator_cred->impersonator != NULL) { + code = G_BAD_USAGE; + goto cleanup; + } + + assert(impersonator_cred->name->princ != NULL); + + assert(subject_creds != NULL); + assert(subject_creds->client != NULL); + + cred = xmalloc(sizeof(*cred)); + if (cred == NULL) { + code = ENOMEM; + goto cleanup; + } + memset(cred, 0, sizeof(*cred)); + + code = k5_mutex_init(&cred->lock); + if (code != 0) + goto cleanup; + + cred->usage = GSS_C_INITIATE; + + cred->expire = subject_creds->times.endtime; + + code = kg_init_name(context, subject_creds->client, NULL, NULL, NULL, 0, + &cred->name); + if (code != 0) + goto cleanup; + + code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache); + if (code != 0) + goto cleanup; + cred->destroy_ccache = 1; + + code = krb5_cc_initialize(context, cred->ccache, subject_creds->client); + if (code != 0) + goto cleanup; + + /* + * Only return a "proxy" credential for use with constrained + * delegation if the subject credentials are forwardable. + * Submitting non-forwardable credentials to the KDC for use + * with constrained delegation will only return an error. + */ + if (subject_creds->ticket_flags & TKT_FLG_FORWARDABLE) { + code = make_proxy_cred(context, cred, impersonator_cred); + if (code != 0) + goto cleanup; + } + + code = krb5_cc_store_cred(context, cred->ccache, subject_creds); + if (code != 0) + goto cleanup; + + if (time_rec != NULL) { + krb5_timestamp now; + + code = krb5_timeofday(context, &now); + if (code != 0) + goto cleanup; + + *time_rec = cred->expire - now; + } + + major_status = GSS_S_COMPLETE; + *minor_status = 0; + *output_cred = cred; + +cleanup: + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + } + + if (GSS_ERROR(major_status) && cred != NULL) { + k5_mutex_destroy(&cred->lock); + krb5_cc_destroy(context, cred->ccache); + kg_release_name(context, &cred->name); + xfree(cred); + } + + return major_status; +} diff --git a/src/lib/gssapi/krb5/ser_sctx.c b/src/lib/gssapi/krb5/ser_sctx.c new file mode 100644 index 000000000000..79c4c710e6d4 --- /dev/null +++ b/src/lib/gssapi/krb5/ser_sctx.c @@ -0,0 +1,798 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/ser_sctx.c - [De]serialization of security context */ +/* + * Copyright 1995, 2004, 2008 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 "gssapiP_krb5.h" + +/* + * This module contains routines to [de]serialize + * krb5_gss_enc_desc and krb5_gss_ctx_id_t. + * XXX This whole serialization abstraction is unnecessary in a + * non-messaging environment, which krb5 is. Someday, this should + * all get redone without the extra level of indirection. I've done + * some of this work here, since adding new serializers is an internal + * krb5 interface, and I won't use those. There is some more + * deobfuscation (no longer anonymizing pointers, mostly) which could + * still be done. --marc + */ + +static krb5_error_code +kg_oid_externalize(kcontext, arg, buffer, lenremain) + krb5_context kcontext; + krb5_pointer arg; + krb5_octet **buffer; + size_t *lenremain; +{ + gss_OID oid = (gss_OID) arg; + krb5_error_code err; + + err = krb5_ser_pack_int32(KV5M_GSS_OID, buffer, lenremain); + if (err) + return err; + err = krb5_ser_pack_int32((krb5_int32) oid->length, + buffer, lenremain); + if (err) + return err; + err = krb5_ser_pack_bytes((krb5_octet *) oid->elements, + oid->length, buffer, lenremain); + if (err) + return err; + err = krb5_ser_pack_int32(KV5M_GSS_OID, buffer, lenremain); + return err; +} + +static krb5_error_code +kg_oid_internalize(kcontext, argp, buffer, lenremain) + krb5_context kcontext; + krb5_pointer *argp; + krb5_octet **buffer; + size_t *lenremain; +{ + gss_OID oid; + krb5_int32 ibuf; + krb5_octet *bp; + size_t remain; + + bp = *buffer; + remain = *lenremain; + + /* Read in and check our magic number */ + if (krb5_ser_unpack_int32(&ibuf, &bp, &remain)) + return (EINVAL); + + if (ibuf != KV5M_GSS_OID) + return (EINVAL); + + oid = (gss_OID) malloc(sizeof(gss_OID_desc)); + if (oid == NULL) + return ENOMEM; + if (krb5_ser_unpack_int32(&ibuf, &bp, &remain)) { + free(oid); + return EINVAL; + } + oid->length = ibuf; + oid->elements = malloc((size_t)ibuf); + if (oid->elements == 0) { + free(oid); + return ENOMEM; + } + if (krb5_ser_unpack_bytes((krb5_octet *) oid->elements, + oid->length, &bp, &remain)) { + free(oid->elements); + free(oid); + return EINVAL; + } + + /* Read in and check our trailing magic number */ + if (krb5_ser_unpack_int32(&ibuf, &bp, &remain)) { + free(oid->elements); + free(oid); + return (EINVAL); + } + + if (ibuf != KV5M_GSS_OID) { + free(oid->elements); + free(oid); + return (EINVAL); + } + + *buffer = bp; + *lenremain = remain; + *argp = (krb5_pointer) oid; + return 0; +} + +static krb5_error_code +kg_oid_size(kcontext, arg, sizep) + krb5_context kcontext; + krb5_pointer arg; + size_t *sizep; +{ + krb5_error_code kret; + gss_OID oid; + size_t required; + + kret = EINVAL; + if ((oid = (gss_OID) arg)) { + required = 2*sizeof(krb5_int32); /* For the header and trailer */ + required += sizeof(krb5_int32); + required += oid->length; + + kret = 0; + + *sizep += required; + } + + return(kret); +} + +static krb5_error_code +kg_seqstate_externalize(kcontext, arg, buffer, lenremain) + krb5_context kcontext; + g_seqnum_state arg; + krb5_octet **buffer; + size_t *lenremain; +{ + krb5_error_code err; + err = krb5_ser_pack_int32(KV5M_GSS_QUEUE, buffer, lenremain); + if (err == 0) + err = g_seqstate_externalize(arg, buffer, lenremain); + if (err == 0) + err = krb5_ser_pack_int32(KV5M_GSS_QUEUE, buffer, lenremain); + return err; +} + +static krb5_error_code +kg_seqstate_internalize(kcontext, argp, buffer, lenremain) + krb5_context kcontext; + g_seqnum_state *argp; + krb5_octet **buffer; + size_t *lenremain; +{ + krb5_int32 ibuf; + krb5_octet *bp; + size_t remain; + krb5_error_code err; + + bp = *buffer; + remain = *lenremain; + + /* Read in and check our magic number */ + if (krb5_ser_unpack_int32(&ibuf, &bp, &remain)) + return (EINVAL); + + if (ibuf != KV5M_GSS_QUEUE) + return (EINVAL); + + err = g_seqstate_internalize(argp, &bp, &remain); + if (err) + return err; + + /* Read in and check our trailing magic number */ + if (krb5_ser_unpack_int32(&ibuf, &bp, &remain)) { + g_seqstate_free(*argp); + return (EINVAL); + } + + if (ibuf != KV5M_GSS_QUEUE) { + g_seqstate_free(*argp); + return (EINVAL); + } + + *buffer = bp; + *lenremain = remain; + return 0; +} + +static krb5_error_code +kg_seqstate_size(kcontext, arg, sizep) + krb5_context kcontext; + g_seqnum_state arg; + size_t *sizep; +{ + krb5_error_code kret; + size_t required; + + kret = EINVAL; + if (arg) { + required = 2*sizeof(krb5_int32); /* For the header and trailer */ + g_seqstate_size(arg, &required); + + kret = 0; + *sizep += required; + } + return(kret); +} + +/* + * Determine the size required for this krb5_gss_ctx_id_rec. + */ +krb5_error_code +kg_ctx_size(kcontext, arg, sizep) + krb5_context kcontext; + krb5_pointer arg; + size_t *sizep; +{ + krb5_error_code kret; + krb5_gss_ctx_id_rec *ctx; + size_t required; + + /* + * krb5_gss_ctx_id_rec requires: + * krb5_int32 for KG_CONTEXT + * krb5_int32 for initiate. + * krb5_int32 for established. + * krb5_int32 for have_acceptor_subkey. + * krb5_int32 for seed_init. + * krb5_int32 for gss_flags. + * sizeof(seed) for seed + * ... for here + * ... for there + * ... for subkey + * krb5_int32 for signalg. + * krb5_int32 for cksum_size. + * krb5_int32 for sealalg. + * ... for enc + * ... for seq + * krb5_int32 for authtime. + * krb5_int32 for starttime. + * krb5_int32 for endtime. + * krb5_int32 for renew_till. + * krb5_int32 for flags. + * int64_t for seq_send. + * int64_t for seq_recv. + * ... for seqstate + * ... for auth_context + * ... for mech_used + * krb5_int32 for proto + * krb5_int32 for cksumtype + * ... for acceptor_subkey + * krb5_int32 for acceptor_key_cksumtype + * krb5_int32 for cred_rcache + * krb5_int32 for number of elements in authdata array + * ... for authdata array + * krb5_int32 for trailer. + */ + kret = EINVAL; + if ((ctx = (krb5_gss_ctx_id_rec *) arg)) { + required = 21*sizeof(krb5_int32); + required += 2*sizeof(int64_t); + required += sizeof(ctx->seed); + + kret = 0; + if (!kret && ctx->here) + kret = krb5_size_opaque(kcontext, + KV5M_PRINCIPAL, + (krb5_pointer) ctx->here->princ, + &required); + + if (!kret && ctx->there) + kret = krb5_size_opaque(kcontext, + KV5M_PRINCIPAL, + (krb5_pointer) ctx->there->princ, + &required); + + if (!kret && ctx->subkey) + kret = krb5_size_opaque(kcontext, + KV5M_KEYBLOCK, + (krb5_pointer) &ctx->subkey->keyblock, + &required); + + if (!kret && ctx->enc) + kret = krb5_size_opaque(kcontext, + KV5M_KEYBLOCK, + (krb5_pointer) &ctx->enc->keyblock, + &required); + + if (!kret && ctx->seq) + kret = krb5_size_opaque(kcontext, + KV5M_KEYBLOCK, + (krb5_pointer) &ctx->seq->keyblock, + &required); + + if (!kret) + kret = kg_oid_size(kcontext, + (krb5_pointer) ctx->mech_used, + &required); + + if (!kret && ctx->seqstate) + kret = kg_seqstate_size(kcontext, ctx->seqstate, &required); + + if (!kret) + kret = krb5_size_opaque(kcontext, + KV5M_CONTEXT, + (krb5_pointer) ctx->k5_context, + &required); + if (!kret) + kret = krb5_size_opaque(kcontext, + KV5M_AUTH_CONTEXT, + (krb5_pointer) ctx->auth_context, + &required); + if (!kret && ctx->acceptor_subkey) + kret = krb5_size_opaque(kcontext, + KV5M_KEYBLOCK, (krb5_pointer) + &ctx->acceptor_subkey->keyblock, + &required); + if (!kret && ctx->authdata) { + krb5_int32 i; + + for (i = 0; !kret && ctx->authdata[i]; i++) { + kret = krb5_size_opaque(kcontext, + KV5M_AUTHDATA, + (krb5_pointer)ctx->authdata[i], + &required); + } + } + if (!kret) { + krb5_gss_name_t initiator_name; + + initiator_name = ctx->initiate ? ctx->here : ctx->there; + + if (initiator_name && initiator_name->ad_context) { + kret = krb5_size_opaque(kcontext, + KV5M_AUTHDATA_CONTEXT, + initiator_name->ad_context, + &required); + } + } + *sizep += required; + } + return(kret); +} + +/* + * Externalize this krb5_gss_ctx_id_ret. + */ +krb5_error_code +kg_ctx_externalize(kcontext, arg, buffer, lenremain) + krb5_context kcontext; + krb5_pointer arg; + krb5_octet **buffer; + size_t *lenremain; +{ + krb5_error_code kret; + krb5_gss_ctx_id_rec *ctx; + size_t required; + krb5_octet *bp; + size_t remain; + krb5int_access kaccess; + + kret = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION); + if (kret) + return(kret); + + required = 0; + bp = *buffer; + remain = *lenremain; + kret = EINVAL; + if ((ctx = (krb5_gss_ctx_id_rec *) arg)) { + kret = ENOMEM; + if (!kg_ctx_size(kcontext, arg, &required) && + (required <= remain)) { + /* Our identifier */ + (void) krb5_ser_pack_int32(KG_CONTEXT, &bp, &remain); + + /* Now static data */ + (void) krb5_ser_pack_int32((krb5_int32) ctx->initiate, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->established, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->have_acceptor_subkey, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->seed_init, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->gss_flags, + &bp, &remain); + (void) krb5_ser_pack_bytes((krb5_octet *) ctx->seed, + sizeof(ctx->seed), + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->signalg, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->cksum_size, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->sealalg, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->krb_times.authtime, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->krb_times.starttime, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->krb_times.endtime, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->krb_times.renew_till, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->krb_flags, + &bp, &remain); + (void) (*kaccess.ser_pack_int64)((int64_t) ctx->seq_send, + &bp, &remain); + (void) (*kaccess.ser_pack_int64)((int64_t) ctx->seq_recv, + &bp, &remain); + + /* Now dynamic data */ + kret = 0; + + if (!kret && ctx->mech_used) + kret = kg_oid_externalize(kcontext, ctx->mech_used, + &bp, &remain); + + if (!kret && ctx->here) + kret = krb5_externalize_opaque(kcontext, + KV5M_PRINCIPAL, + (krb5_pointer) ctx->here->princ, + &bp, &remain); + + if (!kret && ctx->there) + kret = krb5_externalize_opaque(kcontext, + KV5M_PRINCIPAL, + (krb5_pointer) ctx->there->princ, + &bp, &remain); + + if (!kret && ctx->subkey) + kret = krb5_externalize_opaque(kcontext, + KV5M_KEYBLOCK, (krb5_pointer) + &ctx->subkey->keyblock, + &bp, &remain); + + if (!kret && ctx->enc) + kret = krb5_externalize_opaque(kcontext, + KV5M_KEYBLOCK, (krb5_pointer) + &ctx->enc->keyblock, + &bp, &remain); + + if (!kret && ctx->seq) + kret = krb5_externalize_opaque(kcontext, + KV5M_KEYBLOCK, (krb5_pointer) + &ctx->seq->keyblock, + &bp, &remain); + + if (!kret && ctx->seqstate) + kret = kg_seqstate_externalize(kcontext, + ctx->seqstate, &bp, &remain); + + if (!kret) + kret = krb5_externalize_opaque(kcontext, + KV5M_CONTEXT, + (krb5_pointer) ctx->k5_context, + &bp, &remain); + + if (!kret) + kret = krb5_externalize_opaque(kcontext, + KV5M_AUTH_CONTEXT, + (krb5_pointer) ctx->auth_context, + &bp, &remain); + + if (!kret) + kret = krb5_ser_pack_int32((krb5_int32) ctx->proto, + &bp, &remain); + if (!kret) + kret = krb5_ser_pack_int32((krb5_int32) ctx->cksumtype, + &bp, &remain); + if (!kret && ctx->acceptor_subkey) + kret = krb5_externalize_opaque(kcontext, + KV5M_KEYBLOCK, (krb5_pointer) + &ctx->acceptor_subkey->keyblock, + &bp, &remain); + if (!kret) + kret = krb5_ser_pack_int32((krb5_int32) ctx->acceptor_subkey_cksumtype, + &bp, &remain); + + if (!kret) + kret = krb5_ser_pack_int32((krb5_int32) ctx->cred_rcache, + &bp, &remain); + if (!kret) { + krb5_int32 i = 0; + + if (ctx->authdata) { + for (; ctx->authdata[i]; i++) + ; + } + /* authdata count */ + kret = krb5_ser_pack_int32(i, &bp, &remain); + if (!kret && ctx->authdata) { + /* authdata */ + for (i = 0; !kret && ctx->authdata[i]; i++) + kret = krb5_externalize_opaque(kcontext, + KV5M_AUTHDATA, + ctx->authdata[i], + &bp, + &remain); + } + } + /* authdata context */ + if (!kret) { + krb5_gss_name_t initiator_name; + + initiator_name = ctx->initiate ? ctx->here : ctx->there; + + if (initiator_name && initiator_name->ad_context) { + kret = krb5_externalize_opaque(kcontext, + KV5M_AUTHDATA_CONTEXT, + initiator_name->ad_context, + &bp, + &remain); + } + } + /* trailer */ + if (!kret) + kret = krb5_ser_pack_int32(KG_CONTEXT, &bp, &remain); + if (!kret) { + *buffer = bp; + *lenremain = remain; + } + } + } + return(kret); +} + +/* Internalize a keyblock and convert it to a key. */ +static krb5_error_code +intern_key(krb5_context ctx, krb5_key *key, krb5_octet **bp, size_t *sp) +{ + krb5_keyblock *keyblock; + krb5_error_code ret; + + ret = krb5_internalize_opaque(ctx, KV5M_KEYBLOCK, + (krb5_pointer *) &keyblock, bp, sp); + if (ret != 0) + return ret; + ret = krb5_k_create_key(ctx, keyblock, key); + krb5_free_keyblock(ctx, keyblock); + return ret; +} + +/* + * Internalize this krb5_gss_ctx_id_t. + */ +krb5_error_code +kg_ctx_internalize(kcontext, argp, buffer, lenremain) + krb5_context kcontext; + krb5_pointer *argp; + krb5_octet **buffer; + size_t *lenremain; +{ + krb5_error_code kret; + krb5_gss_ctx_id_rec *ctx; + krb5_int32 ibuf; + krb5_octet *bp; + size_t remain; + krb5int_access kaccess; + krb5_principal princ; + + kret = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION); + if (kret) + return(kret); + + bp = *buffer; + remain = *lenremain; + kret = EINVAL; + princ = NULL; + /* Read our magic number */ + if (krb5_ser_unpack_int32(&ibuf, &bp, &remain)) + ibuf = 0; + if (ibuf == KG_CONTEXT) { + kret = ENOMEM; + + /* Get a context */ + if ((remain >= (17*sizeof(krb5_int32) + + 2*sizeof(int64_t) + + sizeof(ctx->seed))) && + (ctx = (krb5_gss_ctx_id_rec *) + xmalloc(sizeof(krb5_gss_ctx_id_rec)))) { + memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec)); + + ctx->magic = ibuf; + ctx->k5_context = kcontext; + + /* Get static data */ + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->initiate = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->established = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->have_acceptor_subkey = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->seed_init = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->gss_flags = (int) ibuf; + (void) krb5_ser_unpack_bytes((krb5_octet *) ctx->seed, + sizeof(ctx->seed), + &bp, &remain); + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->signalg = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->cksum_size = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->sealalg = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->krb_times.authtime = (krb5_timestamp) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->krb_times.starttime = (krb5_timestamp) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->krb_times.endtime = (krb5_timestamp) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->krb_times.renew_till = (krb5_timestamp) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->krb_flags = (krb5_flags) ibuf; + (void) (*kaccess.ser_unpack_int64)((int64_t *)&ctx->seq_send, + &bp, &remain); + kret = (*kaccess.ser_unpack_int64)((int64_t *)&ctx->seq_recv, + &bp, &remain); + if (kret) { + free(ctx); + return kret; + } + + { + krb5_pointer tmp; + kret = kg_oid_internalize(kcontext, &tmp, &bp, + &remain); + if (kret == 0) + ctx->mech_used = tmp; + else if (kret == EINVAL) + kret = 0; + } + /* Now get substructure data */ + kret = krb5_internalize_opaque(kcontext, + KV5M_PRINCIPAL, + (krb5_pointer *) &princ, + &bp, &remain); + if (kret == 0) { + kret = kg_init_name(kcontext, princ, NULL, NULL, NULL, + KG_INIT_NAME_NO_COPY, &ctx->here); + if (kret) + krb5_free_principal(kcontext, princ); + } else if (kret == EINVAL) + kret = 0; + if (!kret) { + kret = krb5_internalize_opaque(kcontext, + KV5M_PRINCIPAL, + (krb5_pointer *) &princ, + &bp, &remain); + if (kret == 0) { + kret = kg_init_name(kcontext, princ, NULL, NULL, NULL, + KG_INIT_NAME_NO_COPY, &ctx->there); + if (kret) + krb5_free_principal(kcontext, princ); + } else if (kret == EINVAL) + kret = 0; + } + if (!kret && + (kret = intern_key(kcontext, &ctx->subkey, &bp, &remain))) { + if (kret == EINVAL) + kret = 0; + } + if (!kret && + (kret = intern_key(kcontext, &ctx->enc, &bp, &remain))) { + if (kret == EINVAL) + kret = 0; + } + if (!kret && + (kret = intern_key(kcontext, &ctx->seq, &bp, &remain))) { + if (kret == EINVAL) + kret = 0; + } + + if (!kret) { + kret = kg_seqstate_internalize(kcontext, &ctx->seqstate, + &bp, &remain); + if (kret == EINVAL) + kret = 0; + } + + if (!kret) + kret = krb5_internalize_opaque(kcontext, + KV5M_CONTEXT, + (krb5_pointer *) &ctx->k5_context, + &bp, &remain); + + if (!kret) + kret = krb5_internalize_opaque(kcontext, + KV5M_AUTH_CONTEXT, + (krb5_pointer *) &ctx->auth_context, + &bp, &remain); + + if (!kret) + kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->proto = ibuf; + if (!kret) + kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->cksumtype = ibuf; + if (!kret && + (kret = intern_key(kcontext, &ctx->acceptor_subkey, + &bp, &remain))) { + if (kret == EINVAL) + kret = 0; + } + if (!kret) + kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->acceptor_subkey_cksumtype = ibuf; + if (!kret) + kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->cred_rcache = ibuf; + /* authdata */ + if (!kret) + kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain); + if (!kret) { + krb5_int32 nadata = ibuf, i; + + if (nadata > 0) { + ctx->authdata = (krb5_authdata **)calloc((size_t)nadata + 1, + sizeof(krb5_authdata *)); + if (ctx->authdata == NULL) { + kret = ENOMEM; + } else { + for (i = 0; !kret && i < nadata; i++) + kret = krb5_internalize_opaque(kcontext, + KV5M_AUTHDATA, + (krb5_pointer *)&ctx->authdata[i], + &bp, + &remain); + } + } + } + /* authdata context */ + if (!kret) { + krb5_gss_name_t initiator_name; + + initiator_name = ctx->initiate ? ctx->here : ctx->there; + if (initiator_name == NULL) { + kret = EINVAL; + } else { + kret = krb5_internalize_opaque(kcontext, + KV5M_AUTHDATA_CONTEXT, + (krb5_pointer *)&initiator_name->ad_context, + &bp, + &remain); + if (kret == EINVAL) + kret = 0; + } + } + /* Get trailer */ + if (!kret) + kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain); + if (!kret && ibuf != KG_CONTEXT) + kret = EINVAL; + + if (!kret) { + *buffer = bp; + *lenremain = remain; + *argp = (krb5_pointer) ctx; + } else { + if (ctx->seq) + krb5_k_free_key(kcontext, ctx->seq); + if (ctx->enc) + krb5_k_free_key(kcontext, ctx->enc); + if (ctx->subkey) + krb5_k_free_key(kcontext, ctx->subkey); + if (ctx->there) + kg_release_name(kcontext, &ctx->there); + if (ctx->here) + kg_release_name(kcontext, &ctx->here); + xfree(ctx); + } + } + } + return(kret); +} diff --git a/src/lib/gssapi/krb5/set_allowable_enctypes.c b/src/lib/gssapi/krb5/set_allowable_enctypes.c new file mode 100644 index 000000000000..d9fd279ed234 --- /dev/null +++ b/src/lib/gssapi/krb5/set_allowable_enctypes.c @@ -0,0 +1,123 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/set_allowable_enctypes.c */ +/* + * Copyright 2004 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. + */ + +/* + * krb5_gss_set_allowable_enctypes() + */ + +/* + * gss_krb5_set_allowable_enctypes + * + * This function may be called by a context initiator after calling + * gss_acquire_cred(), but before calling gss_init_sec_context(), + * to restrict the set of enctypes which will be negotiated during + * context establishment to those in the provided array. + * + * 'cred_handle' must be a valid credential handle obtained via + * gss_acquire_cred(). It may not be GSS_C_NO_CREDENTIAL. + * gss_acquire_cred() may be called with GSS_C_NO_CREDENTIAL + * to get a handle to the default credential. + * + * The purpose of this function is to limit the keys that may + * be exported via gss_krb5_export_lucid_sec_context(); thus it + * should limit the enctypes of all keys that will be needed + * after the security context has been established. + * (i.e. context establishment may use a session key with a + * stronger enctype than in the provided array, however a + * subkey must be established within the enctype limits + * established by this function.) + * + */ + +#include "gssapiP_krb5.h" +#ifdef HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif +#include "gssapi_krb5.h" + +OM_uint32 +gss_krb5int_set_allowable_enctypes(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_oid, + const gss_buffer_t value) +{ + unsigned int i; + krb5_enctype * new_ktypes; + OM_uint32 major_status; + krb5_gss_cred_id_t cred; + krb5_error_code kerr = 0; + struct krb5_gss_set_allowable_enctypes_req *req; + + /* Assume a failure */ + *minor_status = 0; + major_status = GSS_S_FAILURE; + + assert(value->length == sizeof(*req)); + req = (struct krb5_gss_set_allowable_enctypes_req *)value->value; + + /* verify and valildate cred handle */ + cred = (krb5_gss_cred_id_t) *cred_handle; + + if (req->ktypes) { + for (i = 0; i < req->num_ktypes && req->ktypes[i]; i++) { + if (!krb5_c_valid_enctype(req->ktypes[i])) { + kerr = KRB5_PROG_ETYPE_NOSUPP; + goto error_out; + } + } + } else { + k5_mutex_lock(&cred->lock); + if (cred->req_enctypes) + free(cred->req_enctypes); + cred->req_enctypes = NULL; + k5_mutex_unlock(&cred->lock); + return GSS_S_COMPLETE; + } + + /* Copy the requested ktypes into the cred structure */ + if ((new_ktypes = (krb5_enctype *)malloc(sizeof(krb5_enctype) * (i + 1)))) { + memcpy(new_ktypes, req->ktypes, sizeof(krb5_enctype) * i); + new_ktypes[i] = 0; /* "null-terminate" the list */ + } + else { + kerr = ENOMEM; + goto error_out; + } + k5_mutex_lock(&cred->lock); + if (cred->req_enctypes) + free(cred->req_enctypes); + cred->req_enctypes = new_ktypes; + k5_mutex_unlock(&cred->lock); + + /* Success! */ + return GSS_S_COMPLETE; + +error_out: + *minor_status = kerr; + return(major_status); +} diff --git a/src/lib/gssapi/krb5/set_ccache.c b/src/lib/gssapi/krb5/set_ccache.c new file mode 100644 index 000000000000..8acf3ec90cde --- /dev/null +++ b/src/lib/gssapi/krb5/set_ccache.c @@ -0,0 +1,101 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/set_ccache.c */ +/* + * Copyright 1999, 2003 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. + */ + +/* + * Set ccache name used by gssapi, and optionally obtain old ccache + * name. Caller should not free returned name. + */ + +#include <string.h> +#include "gssapiP_krb5.h" + +OM_uint32 +gss_krb5int_ccache_name(OM_uint32 *minor_status, + const gss_OID desired_mech, + const gss_OID desired_object, + const gss_buffer_t value) +{ + char *old_name = NULL; + OM_uint32 err = 0; + OM_uint32 minor = 0; + char *gss_out_name; + struct krb5_gss_ccache_name_req *req; + + err = gss_krb5int_initialize_library(); + if (err) { + *minor_status = err; + return GSS_S_FAILURE; + } + + assert(value->length == sizeof(*req)); + + if (value->length != sizeof(*req)) + return GSS_S_FAILURE; + + req = (struct krb5_gss_ccache_name_req *)value->value; + + gss_out_name = k5_getspecific(K5_KEY_GSS_KRB5_SET_CCACHE_OLD_NAME); + + if (req->out_name) { + const char *tmp_name = NULL; + + if (!err) { + kg_get_ccache_name (&err, &tmp_name); + } + if (!err) { + old_name = gss_out_name; + gss_out_name = (char *)tmp_name; + } + } + /* If out_name was NULL, we keep the same gss_out_name value, and + don't free up any storage (leave old_name NULL). */ + + if (!err) + kg_set_ccache_name (&err, req->name); + + minor = k5_setspecific(K5_KEY_GSS_KRB5_SET_CCACHE_OLD_NAME, gss_out_name); + if (minor) { + /* Um. Now what? */ + if (err == 0) { + err = minor; + } + free(gss_out_name); + gss_out_name = NULL; + } + + if (!err) { + if (req->out_name) { + *(req->out_name) = gss_out_name; + } + } + + if (old_name != NULL) { + free (old_name); + } + + *minor_status = err; + return (*minor_status == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; +} diff --git a/src/lib/gssapi/krb5/store_cred.c b/src/lib/gssapi/krb5/store_cred.c new file mode 100644 index 000000000000..654d96541726 --- /dev/null +++ b/src/lib/gssapi/krb5/store_cred.c @@ -0,0 +1,234 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/store_cred.c */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" + +static int +has_unexpired_creds(krb5_gss_cred_id_t kcred, + const gss_OID desired_mech, + int default_cred, + gss_const_key_value_set_t cred_store) +{ + OM_uint32 major_status, minor; + gss_name_t cred_name; + gss_OID_set_desc desired_mechs; + gss_cred_id_t tmp_cred = GSS_C_NO_CREDENTIAL; + OM_uint32 time_rec; + + desired_mechs.count = 1; + desired_mechs.elements = (gss_OID)desired_mech; + + if (default_cred) + cred_name = GSS_C_NO_NAME; + else + cred_name = (gss_name_t)kcred->name; + + major_status = krb5_gss_acquire_cred_from(&minor, cred_name, 0, + &desired_mechs, GSS_C_INITIATE, + cred_store, &tmp_cred, NULL, + &time_rec); + + krb5_gss_release_cred(&minor, &tmp_cred); + + return (GSS_ERROR(major_status) || time_rec); +} + +static OM_uint32 +copy_initiator_creds(OM_uint32 *minor_status, + gss_cred_id_t input_cred_handle, + const gss_OID desired_mech, + OM_uint32 overwrite_cred, + OM_uint32 default_cred, + gss_const_key_value_set_t cred_store) +{ + OM_uint32 major_status; + krb5_error_code code; + krb5_gss_cred_id_t kcred = NULL; + krb5_context context = NULL; + krb5_ccache ccache = NULL; + const char *ccache_name; + + *minor_status = 0; + + if (!default_cred && cred_store == GSS_C_NO_CRED_STORE) { + *minor_status = G_STORE_NON_DEFAULT_CRED_NOSUPP; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + major_status = krb5_gss_validate_cred_1(minor_status, + input_cred_handle, + context); + if (GSS_ERROR(major_status)) + goto cleanup; + + kcred = (krb5_gss_cred_id_t)input_cred_handle; + + if (kcred->ccache == NULL) { + *minor_status = KG_CCACHE_NOMATCH; + major_status = GSS_S_DEFECTIVE_CREDENTIAL; + goto cleanup; + } + + if (!overwrite_cred && + has_unexpired_creds(kcred, desired_mech, default_cred, cred_store)) { + major_status = GSS_S_DUPLICATE_ELEMENT; + goto cleanup; + } + + major_status = kg_value_from_cred_store(cred_store, + KRB5_CS_CCACHE_URN, &ccache_name); + if (GSS_ERROR(major_status)) + goto cleanup; + + if (ccache_name != NULL) { + code = krb5_cc_resolve(context, ccache_name, &ccache); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_CRED_UNAVAIL; + goto cleanup; + } + code = krb5_cc_initialize(context, ccache, + kcred->name->princ); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_CRED_UNAVAIL; + goto cleanup; + } + } + + if (ccache == NULL) { + if (!default_cred) { + *minor_status = G_STORE_NON_DEFAULT_CRED_NOSUPP; + major_status = GSS_S_FAILURE; + goto cleanup; + } + code = krb5int_cc_default(context, &ccache); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + goto cleanup; + } + } + + code = krb5_cc_copy_creds(context, kcred->ccache, ccache); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + *minor_status = 0; + major_status = GSS_S_COMPLETE; + +cleanup: + if (kcred != NULL) + k5_mutex_unlock(&kcred->lock); + if (ccache != NULL) + krb5_cc_close(context, ccache); + krb5_free_context(context); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_store_cred(OM_uint32 *minor_status, + gss_cred_id_t input_cred_handle, + gss_cred_usage_t cred_usage, + const gss_OID desired_mech, + OM_uint32 overwrite_cred, + OM_uint32 default_cred, + gss_OID_set *elements_stored, + gss_cred_usage_t *cred_usage_stored) +{ + return krb5_gss_store_cred_into(minor_status, input_cred_handle, + cred_usage, desired_mech, + overwrite_cred, default_cred, + GSS_C_NO_CRED_STORE, + elements_stored, cred_usage_stored); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_store_cred_into(OM_uint32 *minor_status, + gss_cred_id_t input_cred_handle, + gss_cred_usage_t cred_usage, + const gss_OID desired_mech, + OM_uint32 overwrite_cred, + OM_uint32 default_cred, + gss_const_key_value_set_t cred_store, + gss_OID_set *elements_stored, + gss_cred_usage_t *cred_usage_stored) +{ + OM_uint32 major_status; + gss_cred_usage_t actual_usage; + OM_uint32 lifetime; + + if (input_cred_handle == GSS_C_NO_CREDENTIAL) + return GSS_S_NO_CRED; + + major_status = GSS_S_FAILURE; + + if (cred_usage == GSS_C_ACCEPT) { + *minor_status = G_STORE_ACCEPTOR_CRED_NOSUPP; + return GSS_S_FAILURE; + } else if (cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) { + *minor_status = G_BAD_USAGE; + return GSS_S_FAILURE; + } + + major_status = krb5_gss_inquire_cred(minor_status, input_cred_handle, + NULL, &lifetime, + &actual_usage, elements_stored); + if (GSS_ERROR(major_status)) + return major_status; + + if (lifetime == 0) + return GSS_S_CREDENTIALS_EXPIRED; + + if (actual_usage != GSS_C_INITIATE && actual_usage != GSS_C_BOTH) { + *minor_status = G_STORE_ACCEPTOR_CRED_NOSUPP; + return GSS_S_FAILURE; + } + + major_status = copy_initiator_creds(minor_status, input_cred_handle, + desired_mech, overwrite_cred, + default_cred, cred_store); + if (GSS_ERROR(major_status)) + return major_status; + + if (cred_usage_stored != NULL) + *cred_usage_stored = GSS_C_INITIATE; + + return GSS_S_COMPLETE; +} diff --git a/src/lib/gssapi/krb5/util_cksum.c b/src/lib/gssapi/krb5/util_cksum.c new file mode 100644 index 000000000000..cfd585ec7219 --- /dev/null +++ b/src/lib/gssapi/krb5/util_cksum.c @@ -0,0 +1,295 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif + +/* Checksumming the channel bindings always uses plain MD5. */ +krb5_error_code +kg_checksum_channel_bindings(context, cb, cksum) + krb5_context context; + gss_channel_bindings_t cb; + krb5_checksum *cksum; +{ + size_t len; + char *buf = 0; + char *ptr; + size_t sumlen; + krb5_data plaind; + krb5_error_code code; + void *temp; + + /* initialize the the cksum */ + code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, &sumlen); + if (code) + return(code); + + cksum->checksum_type = CKSUMTYPE_RSA_MD5; + cksum->length = sumlen; + + /* generate a buffer full of zeros if no cb specified */ + + if (cb == GSS_C_NO_CHANNEL_BINDINGS) { + if ((cksum->contents = (krb5_octet *) xmalloc(cksum->length)) == NULL) { + return(ENOMEM); + } + memset(cksum->contents, '\0', cksum->length); + return(0); + } + + /* create the buffer to checksum into */ + + len = (sizeof(krb5_int32)*5+ + cb->initiator_address.length+ + cb->acceptor_address.length+ + cb->application_data.length); + + if ((buf = (char *) xmalloc(len)) == NULL) + return(ENOMEM); + + /* helper macros. This code currently depends on a long being 32 + bits, and htonl dtrt. */ + + ptr = buf; + + TWRITE_INT(ptr, cb->initiator_addrtype, 0); + TWRITE_BUF(ptr, cb->initiator_address, 0); + TWRITE_INT(ptr, cb->acceptor_addrtype, 0); + TWRITE_BUF(ptr, cb->acceptor_address, 0); + TWRITE_BUF(ptr, cb->application_data, 0); + + /* checksum the data */ + + plaind.length = len; + plaind.data = buf; + + code = krb5_c_make_checksum(context, CKSUMTYPE_RSA_MD5, 0, 0, + &plaind, cksum); + if (code) + goto cleanup; + + if ((temp = xmalloc(cksum->length)) == NULL) { + krb5_free_checksum_contents(context, cksum); + code = ENOMEM; + goto cleanup; + } + + memcpy(temp, cksum->contents, cksum->length); + krb5_free_checksum_contents(context, cksum); + cksum->contents = (krb5_octet *)temp; + + /* success */ +cleanup: + if (buf) + xfree(buf); + return code; +} + +krb5_error_code +kg_make_checksum_iov_v1(krb5_context context, + krb5_cksumtype type, + size_t cksum_len, + krb5_key seq, + krb5_key enc, + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype, + krb5_checksum *checksum) +{ + krb5_error_code code; + gss_iov_buffer_desc *header; + krb5_crypto_iov *kiov; + int i = 0, j; + size_t conf_len = 0, token_header_len; + + header = kg_locate_header_iov(iov, iov_count, toktype); + assert(header != NULL); + + kiov = calloc(iov_count + 3, sizeof(krb5_crypto_iov)); + if (kiov == NULL) + return ENOMEM; + + /* Checksum over ( Header | Confounder | Data | Pad ) */ + if (toktype == KG_TOK_WRAP_MSG) + conf_len = kg_confounder_size(context, enc->keyblock.enctype); + + /* Checksum output */ + kiov[i].flags = KRB5_CRYPTO_TYPE_CHECKSUM; + kiov[i].data.length = checksum->length; + kiov[i].data.data = xmalloc(checksum->length); + if (kiov[i].data.data == NULL) { + xfree(kiov); + return ENOMEM; + } + i++; + + /* Header | SND_SEQ | SGN_CKSUM | Confounder */ + token_header_len = 16 + cksum_len + conf_len; + + /* Header (calculate from end because of variable length ASN.1 header) */ + kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY; + kiov[i].data.length = 8; + kiov[i].data.data = (char *)header->buffer.value + header->buffer.length - token_header_len; + i++; + + /* Confounder */ + if (toktype == KG_TOK_WRAP_MSG) { + kiov[i].flags = KRB5_CRYPTO_TYPE_DATA; + kiov[i].data.length = conf_len; + kiov[i].data.data = (char *)header->buffer.value + header->buffer.length - conf_len; + i++; + } + + for (j = 0; j < iov_count; j++) { + kiov[i].flags = kg_translate_flag_iov(iov[j].type); + kiov[i].data.length = iov[j].buffer.length; + kiov[i].data.data = (char *)iov[j].buffer.value; + i++; + } + + code = krb5_k_make_checksum_iov(context, type, seq, sign_usage, kiov, i); + if (code == 0) { + checksum->length = kiov[0].data.length; + checksum->contents = (unsigned char *)kiov[0].data.data; + } else + free(kiov[0].data.data); + + xfree(kiov); + + return code; +} + +static krb5_error_code +checksum_iov_v3(krb5_context context, + krb5_cksumtype type, + size_t rrc, + krb5_key key, + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype, + krb5_boolean verify, + krb5_boolean *valid) +{ + krb5_error_code code; + gss_iov_buffer_desc *header; + gss_iov_buffer_desc *trailer; + krb5_crypto_iov *kiov; + size_t kiov_count; + int i = 0, j; + unsigned int k5_checksumlen; + + if (verify) + *valid = FALSE; + + code = krb5_c_crypto_length(context, key->keyblock.enctype, KRB5_CRYPTO_TYPE_CHECKSUM, &k5_checksumlen); + if (code != 0) + return code; + + header = kg_locate_header_iov(iov, iov_count, toktype); + assert(header != NULL); + + trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + assert(rrc != 0 || trailer != NULL); + + if (trailer == NULL) { + if (rrc != k5_checksumlen) + return KRB5_BAD_MSIZE; + if (header->buffer.length != 16 + k5_checksumlen) + return KRB5_BAD_MSIZE; + } else if (trailer->buffer.length != k5_checksumlen) + return KRB5_BAD_MSIZE; + + kiov_count = 2 + iov_count; + kiov = (krb5_crypto_iov *)xmalloc(kiov_count * sizeof(krb5_crypto_iov)); + if (kiov == NULL) + return ENOMEM; + + /* Checksum over ( Data | Header ) */ + + /* Data */ + for (j = 0; j < iov_count; j++) { + kiov[i].flags = kg_translate_flag_iov(iov[j].type); + kiov[i].data.length = iov[j].buffer.length; + kiov[i].data.data = (char *)iov[j].buffer.value; + i++; + } + + /* Header */ + kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY; + kiov[i].data.length = 16; + kiov[i].data.data = (char *)header->buffer.value; + i++; + + /* Checksum */ + kiov[i].flags = KRB5_CRYPTO_TYPE_CHECKSUM; + if (trailer == NULL) { + kiov[i].data.length = header->buffer.length - 16; + kiov[i].data.data = (char *)header->buffer.value + 16; + } else { + kiov[i].data.length = trailer->buffer.length; + kiov[i].data.data = (char *)trailer->buffer.value; + } + i++; + + if (verify) + code = krb5_k_verify_checksum_iov(context, type, key, sign_usage, kiov, kiov_count, valid); + else + code = krb5_k_make_checksum_iov(context, type, key, sign_usage, kiov, kiov_count); + + xfree(kiov); + + return code; +} + +krb5_error_code +kg_make_checksum_iov_v3(krb5_context context, + krb5_cksumtype type, + size_t rrc, + krb5_key key, + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype) +{ + return checksum_iov_v3(context, type, rrc, key, + sign_usage, iov, iov_count, toktype, 0, NULL); +} + +krb5_error_code +kg_verify_checksum_iov_v3(krb5_context context, + krb5_cksumtype type, + size_t rrc, + krb5_key key, + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype, + krb5_boolean *valid) +{ + return checksum_iov_v3(context, type, rrc, key, + sign_usage, iov, iov_count, toktype, 1, valid); +} diff --git a/src/lib/gssapi/krb5/util_crypt.c b/src/lib/gssapi/krb5/util_crypt.c new file mode 100644 index 000000000000..0cebde12a85c --- /dev/null +++ b/src/lib/gssapi/krb5/util_crypt.c @@ -0,0 +1,782 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2001, 2008 by the Massachusetts Institute of Technology. + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * 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 FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif + +static krb5_error_code +kg_copy_keys(krb5_context context, krb5_gss_ctx_id_rec *ctx, krb5_key subkey) +{ + krb5_error_code code; + + krb5_k_free_key(context, ctx->enc); + ctx->enc = NULL; + code = krb5_k_create_key(context, &subkey->keyblock, &ctx->enc); + if (code != 0) + return code; + + krb5_k_free_key(context, ctx->seq); + ctx->seq = NULL; + code = krb5_k_create_key(context, &subkey->keyblock, &ctx->seq); + if (code != 0) + return code; + + return 0; +} + +static krb5_error_code +kg_derive_des_enc_key(krb5_context context, krb5_key subkey, krb5_key *out) +{ + krb5_error_code code; + krb5_keyblock *keyblock; + unsigned int i; + + *out = NULL; + + code = krb5_k_key_keyblock(context, subkey, &keyblock); + if (code != 0) + return code; + + for (i = 0; i < keyblock->length; i++) + keyblock->contents[i] ^= 0xF0; + + code = krb5_k_create_key(context, keyblock, out); + krb5_free_keyblock(context, keyblock); + return code; +} + +krb5_error_code +kg_setup_keys(krb5_context context, krb5_gss_ctx_id_rec *ctx, krb5_key subkey, + krb5_cksumtype *cksumtype) +{ + krb5_error_code code; + + assert(ctx != NULL); + assert(subkey != NULL); + + *cksumtype = 0; + ctx->proto = 0; + + if (ctx->enc == NULL) { + ctx->signalg = -1; + ctx->sealalg = -1; + } + + code = krb5int_c_mandatory_cksumtype(context, subkey->keyblock.enctype, + cksumtype); + if (code != 0) + return code; + + switch (subkey->keyblock.enctype) { + case ENCTYPE_DES_CBC_MD5: + case ENCTYPE_DES_CBC_MD4: + case ENCTYPE_DES_CBC_CRC: + krb5_k_free_key(context, ctx->seq); + code = krb5_k_create_key(context, &subkey->keyblock, &ctx->seq); + if (code != 0) + return code; + + krb5_k_free_key(context, ctx->enc); + code = kg_derive_des_enc_key(context, subkey, &ctx->enc); + if (code != 0) + return code; + + ctx->enc->keyblock.enctype = ENCTYPE_DES_CBC_RAW; + ctx->seq->keyblock.enctype = ENCTYPE_DES_CBC_RAW; + ctx->signalg = SGN_ALG_DES_MAC_MD5; + ctx->cksum_size = 8; + ctx->sealalg = SEAL_ALG_DES; + + break; + case ENCTYPE_DES3_CBC_SHA1: + code = kg_copy_keys(context, ctx, subkey); + if (code != 0) + return code; + + ctx->enc->keyblock.enctype = ENCTYPE_DES3_CBC_RAW; + ctx->seq->keyblock.enctype = ENCTYPE_DES3_CBC_RAW; + ctx->signalg = SGN_ALG_HMAC_SHA1_DES3_KD; + ctx->cksum_size = 20; + ctx->sealalg = SEAL_ALG_DES3KD; + break; + case ENCTYPE_ARCFOUR_HMAC: + case ENCTYPE_ARCFOUR_HMAC_EXP: + /* RFC 4121 accidentally omits RC4-HMAC-EXP as a "not-newer" enctype, + * even though RFC 4757 treats it as one. */ + code = kg_copy_keys(context, ctx, subkey); + if (code != 0) + return code; + + ctx->signalg = SGN_ALG_HMAC_MD5; + ctx->cksum_size = 8; + ctx->sealalg = SEAL_ALG_MICROSOFT_RC4; + break; + default: + ctx->proto = 1; + break; + } + + return 0; +} + +int +kg_confounder_size(krb5_context context, krb5_enctype enctype) +{ + krb5_error_code code; + size_t blocksize; + /* We special case rc4*/ + if (enctype == ENCTYPE_ARCFOUR_HMAC || enctype == ENCTYPE_ARCFOUR_HMAC_EXP) + return 8; + code = krb5_c_block_size(context, enctype, &blocksize); + if (code) + return(-1); /* XXX */ + + return(blocksize); +} + +krb5_error_code +kg_make_confounder(krb5_context context, krb5_enctype enctype, + unsigned char *buf) +{ + int confsize; + krb5_data lrandom; + + confsize = kg_confounder_size(context, enctype); + if (confsize < 0) + return KRB5_BAD_MSIZE; + + lrandom.length = confsize; + lrandom.data = (char *)buf; + + return(krb5_c_random_make_octets(context, &lrandom)); +} + +/* Set *data_out to a krb5_data structure containing iv, or to NULL if iv is + * NULL. */ +static krb5_error_code +iv_to_state(krb5_context context, krb5_key key, krb5_pointer iv, + krb5_data **data_out) +{ + krb5_error_code code; + krb5_data *data; + size_t blocksize; + + *data_out = NULL; + if (iv == NULL) + return 0; + + code = krb5_c_block_size(context, key->keyblock.enctype, &blocksize); + if (code) + return code; + + data = k5alloc(sizeof(*data), &code); + if (data == NULL) + return code; + code = alloc_data(data, blocksize); + if (code) { + free(data); + return code; + } + memcpy(data->data, iv, blocksize); + *data_out = data; + return 0; +} + +krb5_error_code +kg_encrypt(krb5_context context, krb5_key key, int usage, krb5_pointer iv, + krb5_const_pointer in, krb5_pointer out, unsigned int length) +{ + krb5_error_code code; + krb5_data *state, inputd; + krb5_enc_data outputd; + + code = iv_to_state(context, key, iv, &state); + if (code) + return code; + + inputd.length = length; + inputd.data = (char *)in; + + outputd.ciphertext.length = length; + outputd.ciphertext.data = out; + + code = krb5_k_encrypt(context, key, usage, state, &inputd, &outputd); + krb5_free_data(context, state); + return code; +} + +krb5_error_code +kg_encrypt_inplace(krb5_context context, krb5_key key, int usage, + krb5_pointer iv, krb5_pointer ptr, unsigned int length) +{ + krb5_error_code code; + krb5_crypto_iov iov; + krb5_data *state; + + code = iv_to_state(context, key, iv, &state); + if (code) + return code; + + iov.flags = KRB5_CRYPTO_TYPE_DATA; + iov.data = make_data((void *)ptr, length); + code = krb5_k_encrypt_iov(context, key, usage, state, &iov, 1); + krb5_free_data(context, state); + return code; +} + +/* length is the length of the cleartext. */ + +krb5_error_code +kg_decrypt(krb5_context context, krb5_key key, int usage, krb5_pointer iv, + krb5_const_pointer in, krb5_pointer out, unsigned int length) +{ + krb5_error_code code; + krb5_data *state, outputd; + krb5_enc_data inputd; + + code = iv_to_state(context, key, iv, &state); + if (code) + return code; + + inputd.enctype = ENCTYPE_UNKNOWN; + inputd.ciphertext.length = length; + inputd.ciphertext.data = (char *)in; + + outputd.length = length; + outputd.data = out; + + code = krb5_k_decrypt(context, key, usage, state, &inputd, &outputd); + krb5_free_data(context, state); + return code; +} + +krb5_error_code +kg_arcfour_docrypt(const krb5_keyblock *keyblock, int usage, + const unsigned char *kd_data, size_t kd_data_len, + const unsigned char *input_buf, size_t input_len, + unsigned char *output_buf) +{ + krb5_data kd = make_data((char *) kd_data, kd_data_len); + krb5_crypto_iov kiov; + + memcpy(output_buf, input_buf, input_len); + kiov.flags = KRB5_CRYPTO_TYPE_DATA; + kiov.data = make_data(output_buf, input_len); + return krb5int_arcfour_gsscrypt(keyblock, usage, &kd, &kiov, 1); +} + +/* AEAD */ +static krb5_error_code +kg_translate_iov_v1(krb5_context context, krb5_enctype enctype, + gss_iov_buffer_desc *iov, int iov_count, + krb5_crypto_iov **pkiov, size_t *pkiov_count) +{ + gss_iov_buffer_desc *header; + gss_iov_buffer_desc *trailer; + int i = 0, j; + size_t kiov_count; + krb5_crypto_iov *kiov; + size_t conf_len; + + *pkiov = NULL; + *pkiov_count = 0; + + conf_len = kg_confounder_size(context, enctype); + + header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); + assert(header != NULL); + + if (header->buffer.length < conf_len) + return KRB5_BAD_MSIZE; + + trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + assert(trailer == NULL || trailer->buffer.length == 0); + + kiov_count = 3 + iov_count; + kiov = (krb5_crypto_iov *)malloc(kiov_count * sizeof(krb5_crypto_iov)); + if (kiov == NULL) + return ENOMEM; + + /* For pre-CFX (raw enctypes) there is no krb5 header */ + kiov[i].flags = KRB5_CRYPTO_TYPE_HEADER; + kiov[i].data.length = 0; + kiov[i].data.data = NULL; + i++; + + /* For pre-CFX, the confounder is at the end of the GSS header */ + kiov[i].flags = KRB5_CRYPTO_TYPE_DATA; + kiov[i].data.length = conf_len; + kiov[i].data.data = (char *)header->buffer.value + header->buffer.length - conf_len; + i++; + + for (j = 0; j < iov_count; j++) { + kiov[i].flags = kg_translate_flag_iov(iov[j].type); + if (kiov[i].flags == KRB5_CRYPTO_TYPE_EMPTY) + continue; + + kiov[i].data.length = iov[j].buffer.length; + kiov[i].data.data = (char *)iov[j].buffer.value; + i++; + } + + kiov[i].flags = KRB5_CRYPTO_TYPE_TRAILER; + kiov[i].data.length = 0; + kiov[i].data.data = NULL; + i++; + + *pkiov = kiov; + *pkiov_count = i; + + return 0; +} + +/* + * DCE_STYLE indicates actual RRC is EC + RRC + * EC is extra rotate count for DCE_STYLE, pad length otherwise + * RRC is rotate count. + */ +static krb5_error_code +kg_translate_iov_v3(krb5_context context, int dce_style, size_t ec, size_t rrc, + krb5_enctype enctype, gss_iov_buffer_desc *iov, + int iov_count, krb5_crypto_iov **pkiov, + size_t *pkiov_count) +{ + gss_iov_buffer_t header; + gss_iov_buffer_t trailer; + int i = 0, j; + size_t kiov_count; + krb5_crypto_iov *kiov; + unsigned int k5_headerlen = 0, k5_trailerlen = 0; + size_t gss_headerlen, gss_trailerlen; + krb5_error_code code; + + *pkiov = NULL; + *pkiov_count = 0; + + header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); + assert(header != NULL); + + trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + assert(trailer == NULL || rrc == 0); + + code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_HEADER, + &k5_headerlen); + if (code != 0) + return code; + + code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_TRAILER, + &k5_trailerlen); + if (code != 0) + return code; + + /* Check header and trailer sizes */ + gss_headerlen = 16 /* GSS-Header */ + k5_headerlen; /* Kerb-Header */ + gss_trailerlen = ec + 16 /* E(GSS-Header) */ + k5_trailerlen; /* Kerb-Trailer */ + + /* If we're caller without a trailer, we must rotate by trailer length */ + if (trailer == NULL) { + size_t actual_rrc = rrc; + + if (dce_style) + actual_rrc += ec; /* compensate for Windows bug */ + + if (actual_rrc != gss_trailerlen) + return KRB5_BAD_MSIZE; + + gss_headerlen += gss_trailerlen; + gss_trailerlen = 0; + } else { + if (trailer->buffer.length != gss_trailerlen) + return KRB5_BAD_MSIZE; + } + + if (header->buffer.length != gss_headerlen) + return KRB5_BAD_MSIZE; + + kiov_count = 3 + iov_count; + kiov = (krb5_crypto_iov *)malloc(kiov_count * sizeof(krb5_crypto_iov)); + if (kiov == NULL) + return ENOMEM; + + /* + * The krb5 header is located at the end of the GSS header. + */ + kiov[i].flags = KRB5_CRYPTO_TYPE_HEADER; + kiov[i].data.length = k5_headerlen; + kiov[i].data.data = (char *)header->buffer.value + header->buffer.length - k5_headerlen; + i++; + + for (j = 0; j < iov_count; j++) { + kiov[i].flags = kg_translate_flag_iov(iov[j].type); + if (kiov[i].flags == KRB5_CRYPTO_TYPE_EMPTY) + continue; + + kiov[i].data.length = iov[j].buffer.length; + kiov[i].data.data = (char *)iov[j].buffer.value; + i++; + } + + /* + * The EC and encrypted GSS header are placed in the trailer, which may + * be rotated directly after the plaintext header if no trailer buffer + * is provided. + */ + kiov[i].flags = KRB5_CRYPTO_TYPE_DATA; + kiov[i].data.length = ec + 16; /* E(Header) */ + if (trailer == NULL) + kiov[i].data.data = (char *)header->buffer.value + 16; + else + kiov[i].data.data = (char *)trailer->buffer.value; + i++; + + /* + * The krb5 trailer is placed after the encrypted copy of the + * krb5 header (which may be in the GSS header or trailer). + */ + kiov[i].flags = KRB5_CRYPTO_TYPE_TRAILER; + kiov[i].data.length = k5_trailerlen; + kiov[i].data.data = kiov[i - 1].data.data + ec + 16; /* E(Header) */ + i++; + + *pkiov = kiov; + *pkiov_count = i; + + return 0; +} + +/* PROTO is 1 if CFX, 0 if pre-CFX */ +static krb5_error_code +kg_translate_iov(krb5_context context, int proto, int dce_style, size_t ec, + size_t rrc, krb5_enctype enctype, gss_iov_buffer_desc *iov, + int iov_count, krb5_crypto_iov **pkiov, size_t *pkiov_count) +{ + return proto ? + kg_translate_iov_v3(context, dce_style, ec, rrc, enctype, + iov, iov_count, pkiov, pkiov_count) : + kg_translate_iov_v1(context, enctype, iov, iov_count, + pkiov, pkiov_count); +} + +krb5_error_code +kg_encrypt_iov(krb5_context context, int proto, int dce_style, size_t ec, + size_t rrc, krb5_key key, int usage, krb5_pointer iv, + gss_iov_buffer_desc *iov, int iov_count) +{ + krb5_error_code code; + krb5_data *state; + size_t kiov_len; + krb5_crypto_iov *kiov; + + code = iv_to_state(context, key, iv, &state); + if (code) + return code; + + code = kg_translate_iov(context, proto, dce_style, ec, rrc, + key->keyblock.enctype, iov, iov_count, + &kiov, &kiov_len); + if (code == 0) { + code = krb5_k_encrypt_iov(context, key, usage, state, kiov, kiov_len); + free(kiov); + } + + krb5_free_data(context, state); + return code; +} + +/* length is the length of the cleartext. */ + +krb5_error_code +kg_decrypt_iov(krb5_context context, int proto, int dce_style, size_t ec, + size_t rrc, krb5_key key, int usage, krb5_pointer iv, + gss_iov_buffer_desc *iov, int iov_count) +{ + krb5_error_code code; + krb5_data *state; + size_t kiov_len; + krb5_crypto_iov *kiov; + + code = iv_to_state(context, key, iv, &state); + if (code) + return code; + + code = kg_translate_iov(context, proto, dce_style, ec, rrc, + key->keyblock.enctype, iov, iov_count, + &kiov, &kiov_len); + if (code == 0) { + code = krb5_k_decrypt_iov(context, key, usage, state, kiov, kiov_len); + free(kiov); + } + + krb5_free_data(context, state); + return code; +} + +krb5_error_code +kg_arcfour_docrypt_iov(krb5_context context, const krb5_keyblock *keyblock, + int usage, const unsigned char *kd_data, + size_t kd_data_len, gss_iov_buffer_desc *iov, + int iov_count) +{ + krb5_error_code code; + krb5_data kd = make_data((char *) kd_data, kd_data_len); + krb5_crypto_iov *kiov = NULL; + size_t kiov_len = 0; + + code = kg_translate_iov(context, 0 /* proto */, 0 /* dce_style */, + 0 /* ec */, 0 /* rrc */, keyblock->enctype, + iov, iov_count, &kiov, &kiov_len); + if (code) + return code; + code = krb5int_arcfour_gsscrypt(keyblock, usage, &kd, kiov, kiov_len); + free(kiov); + return code; +} + +krb5_cryptotype +kg_translate_flag_iov(OM_uint32 type) +{ + krb5_cryptotype ktype; + + switch (GSS_IOV_BUFFER_TYPE(type)) { + case GSS_IOV_BUFFER_TYPE_DATA: + case GSS_IOV_BUFFER_TYPE_PADDING: + ktype = KRB5_CRYPTO_TYPE_DATA; + break; + case GSS_IOV_BUFFER_TYPE_SIGN_ONLY: + ktype = KRB5_CRYPTO_TYPE_SIGN_ONLY; + break; + default: + ktype = KRB5_CRYPTO_TYPE_EMPTY; + break; + } + + return ktype; +} + +gss_iov_buffer_t +kg_locate_iov(gss_iov_buffer_desc *iov, int iov_count, OM_uint32 type) +{ + int i; + gss_iov_buffer_t p = GSS_C_NO_IOV_BUFFER; + + if (iov == GSS_C_NO_IOV_BUFFER) + return GSS_C_NO_IOV_BUFFER; + + for (i = iov_count - 1; i >= 0; i--) { + if (GSS_IOV_BUFFER_TYPE(iov[i].type) == type) { + if (p == GSS_C_NO_IOV_BUFFER) + p = &iov[i]; + else + return GSS_C_NO_IOV_BUFFER; + } + } + + return p; +} + +/* Return the IOV where the GSSAPI token header should be placed (and possibly + * the checksum as well, depending on the token type). */ +gss_iov_buffer_t +kg_locate_header_iov(gss_iov_buffer_desc *iov, int iov_count, int toktype) +{ + if (toktype == KG_TOK_MIC_MSG) + return kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_MIC_TOKEN); + else + return kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); +} + +void +kg_iov_msglen(gss_iov_buffer_desc *iov, int iov_count, size_t *data_length_p, + size_t *assoc_data_length_p) +{ + int i; + size_t data_length = 0, assoc_data_length = 0; + + assert(iov != GSS_C_NO_IOV_BUFFER); + + *data_length_p = *assoc_data_length_p = 0; + + for (i = 0; i < iov_count; i++) { + OM_uint32 type = GSS_IOV_BUFFER_TYPE(iov[i].type); + + if (type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY) + assoc_data_length += iov[i].buffer.length; + + if (type == GSS_IOV_BUFFER_TYPE_DATA || + type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY) + data_length += iov[i].buffer.length; + } + + *data_length_p = data_length; + *assoc_data_length_p = assoc_data_length; +} + +void +kg_release_iov(gss_iov_buffer_desc *iov, int iov_count) +{ + int i; + + assert(iov != GSS_C_NO_IOV_BUFFER); + + for (i = 0; i < iov_count; i++) { + if (iov[i].type & GSS_IOV_BUFFER_FLAG_ALLOCATED) { + gssalloc_free(iov[i].buffer.value); + iov[i].buffer.length = 0; + iov[i].buffer.value = NULL; + iov[i].type &= ~(GSS_IOV_BUFFER_FLAG_ALLOCATED); + } + } +} + +OM_uint32 +kg_fixup_padding_iov(OM_uint32 *minor_status, gss_iov_buffer_desc *iov, + int iov_count) +{ + gss_iov_buffer_t padding = NULL; + gss_iov_buffer_t data = NULL; + size_t padlength, relative_padlength; + unsigned char *p; + + data = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_DATA); + padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); + + if (data == NULL) { + *minor_status = 0; + return GSS_S_COMPLETE; + } + + if (padding == NULL || padding->buffer.length == 0) { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + + p = (unsigned char *)padding->buffer.value; + padlength = p[padding->buffer.length - 1]; + + if (data->buffer.length + padding->buffer.length < padlength || + padlength == 0) { + *minor_status = (OM_uint32)KRB5_BAD_MSIZE; + return GSS_S_DEFECTIVE_TOKEN; + } + + /* + * kg_unseal_stream_iov() will place one byte of padding in the + * padding buffer; its true value is unknown until after decryption. + * + * relative_padlength contains the number of bytes to compensate the + * padding and data buffers by; it will be zero if the caller manages + * the padding length. + * + * If the caller manages the padding length, then relative_padlength + * wil be zero. + * + * eg. if the buffers are structured as follows: + * + * +---DATA---+-PAD-+ + * | ABCDE444 | 4 | + * +----------+-----+ + * + * after compensation they would look like: + * + * +-DATA--+-PAD--+ + * | ABCDE | NULL | + * +-------+------+ + */ + relative_padlength = padlength - padding->buffer.length; + + assert(data->buffer.length >= relative_padlength); + + data->buffer.length -= relative_padlength; + + kg_release_iov(padding, 1); + padding->buffer.length = 0; + padding->buffer.value = NULL; + + return GSS_S_COMPLETE; +} + +krb5_boolean +kg_integ_only_iov(gss_iov_buffer_desc *iov, int iov_count) +{ + int i; + krb5_boolean has_conf_data = FALSE; + + assert(iov != GSS_C_NO_IOV_BUFFER); + + for (i = 0; i < iov_count; i++) { + if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_DATA) { + has_conf_data = TRUE; + break; + } + } + + return (has_conf_data == FALSE); +} + +krb5_error_code +kg_allocate_iov(gss_iov_buffer_t iov, size_t size) +{ + assert(iov != GSS_C_NO_IOV_BUFFER); + assert(iov->type & GSS_IOV_BUFFER_FLAG_ALLOCATE); + + iov->buffer.length = size; + iov->buffer.value = gssalloc_malloc(size); + if (iov->buffer.value == NULL) { + iov->buffer.length = 0; + return ENOMEM; + } + + iov->type |= GSS_IOV_BUFFER_FLAG_ALLOCATED; + + return 0; +} diff --git a/src/lib/gssapi/krb5/util_seed.c b/src/lib/gssapi/krb5/util_seed.c new file mode 100644 index 000000000000..6e1c9ac8aecb --- /dev/null +++ b/src/lib/gssapi/krb5/util_seed.c @@ -0,0 +1,61 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif + +static const unsigned char zeros[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; + +krb5_error_code +kg_make_seed(context, key, seed) + krb5_context context; + krb5_key key; + unsigned char *seed; +{ + krb5_error_code code; + krb5_key rkey = NULL; + krb5_keyblock *tmpkey, *kb; + unsigned int i; + + code = krb5_k_key_keyblock(context, key, &tmpkey); + if (code) + return(code); + + /* reverse the key bytes, as per spec */ + kb = &key->keyblock; + for (i=0; i<tmpkey->length; i++) + tmpkey->contents[i] = kb->contents[kb->length - 1 - i]; + + code = krb5_k_create_key(context, tmpkey, &rkey); + if (code) + goto cleanup; + + code = kg_encrypt(context, rkey, KG_USAGE_SEAL, NULL, zeros, seed, 16); + +cleanup: + krb5_free_keyblock(context, tmpkey); + krb5_k_free_key(context, rkey); + return(code); +} diff --git a/src/lib/gssapi/krb5/util_seqnum.c b/src/lib/gssapi/krb5/util_seqnum.c new file mode 100644 index 000000000000..bef631da9d1a --- /dev/null +++ b/src/lib/gssapi/krb5/util_seqnum.c @@ -0,0 +1,102 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2001, 2009 by the Massachusetts Institute of Technology. + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" +#include "k5-int.h" + +/* + * $Id$ + */ + +krb5_error_code +kg_make_seq_num(context, key, direction, seqnum, cksum, buf) + krb5_context context; + krb5_key key; + int direction; + krb5_ui_4 seqnum; + unsigned char *cksum; + unsigned char *buf; +{ + unsigned char plain[8]; + + plain[4] = direction; + plain[5] = direction; + plain[6] = direction; + plain[7] = direction; + if (key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC || + key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { + /* Yes, Microsoft used big-endian sequence number.*/ + store_32_be(seqnum, plain); + return kg_arcfour_docrypt (&key->keyblock, 0, + cksum, 8, + &plain[0], 8, + buf); + + } + + store_32_le(seqnum, plain); + return(kg_encrypt(context, key, KG_USAGE_SEQ, cksum, plain, buf, 8)); +} + +krb5_error_code kg_get_seq_num(context, key, cksum, buf, direction, seqnum) + krb5_context context; + krb5_key key; + unsigned char *cksum; + unsigned char *buf; + int *direction; + krb5_ui_4 *seqnum; +{ + krb5_error_code code; + unsigned char plain[8]; + + if (key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC || + key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { + code = kg_arcfour_docrypt (&key->keyblock, 0, + cksum, 8, + buf, 8, + plain); + } else { + code = kg_decrypt(context, key, KG_USAGE_SEQ, cksum, buf, plain, 8); + } + if (code) + return(code); + + if ((plain[4] != plain[5]) || + (plain[4] != plain[6]) || + (plain[4] != plain[7])) + return((krb5_error_code) KG_BAD_SEQ); + + *direction = plain[4]; + if (key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC || + key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { + *seqnum = (plain[3]|(plain[2]<<8) | (plain[1]<<16)| (plain[0]<<24)); + } else { + *seqnum = ((plain[0]) | + (plain[1]<<8) | + (plain[2]<<16) | + (plain[3]<<24)); + } + + return(0); +} diff --git a/src/lib/gssapi/krb5/val_cred.c b/src/lib/gssapi/krb5/val_cred.c new file mode 100644 index 000000000000..cb1cb9393acf --- /dev/null +++ b/src/lib/gssapi/krb5/val_cred.c @@ -0,0 +1,83 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1997, 2007 by 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 "gssapiP_krb5.h" + +/* Check to see whether or not a GSSAPI krb5 credential is valid. If + * it is not, return an error. */ + +OM_uint32 +krb5_gss_validate_cred_1(OM_uint32 *minor_status, gss_cred_id_t cred_handle, + krb5_context context) +{ + krb5_gss_cred_id_t cred; + krb5_error_code code; + krb5_principal princ; + + cred = (krb5_gss_cred_id_t) cred_handle; + k5_mutex_lock(&cred->lock); + + if (cred->ccache && cred->expire != 0) { + if ((code = krb5_cc_get_principal(context, cred->ccache, &princ))) { + k5_mutex_unlock(&cred->lock); + *minor_status = code; + return(GSS_S_DEFECTIVE_CREDENTIAL); + } + if (!krb5_principal_compare(context, princ, cred->name->princ)) { + k5_mutex_unlock(&cred->lock); + *minor_status = KG_CCACHE_NOMATCH; + return(GSS_S_DEFECTIVE_CREDENTIAL); + } + (void)krb5_free_principal(context, princ); + } + *minor_status = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +krb5_gss_validate_cred(minor_status, cred_handle) + OM_uint32 *minor_status; + gss_cred_id_t cred_handle; +{ + krb5_context context; + krb5_error_code code; + OM_uint32 maj; + + code = krb5_gss_init_context(&context); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } + + maj = krb5_gss_validate_cred_1(minor_status, cred_handle, context); + if (maj == 0) { + krb5_gss_cred_id_t cred = (krb5_gss_cred_id_t) cred_handle; + k5_mutex_assert_locked(&cred->lock); + k5_mutex_unlock(&cred->lock); + } + save_error_info(*minor_status, context); + krb5_free_context(context); + return maj; +} diff --git a/src/lib/gssapi/krb5/wrap_size_limit.c b/src/lib/gssapi/krb5/wrap_size_limit.c new file mode 100644 index 000000000000..7959f424ec96 --- /dev/null +++ b/src/lib/gssapi/krb5/wrap_size_limit.c @@ -0,0 +1,178 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2000 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. + */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * 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 FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "gssapiP_krb5.h" + +/* V2 interface */ +OM_uint32 KRB5_CALLCONV +krb5_gss_wrap_size_limit(minor_status, context_handle, conf_req_flag, + qop_req, req_output_size, max_input_size) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + int conf_req_flag; + gss_qop_t qop_req; + OM_uint32 req_output_size; + OM_uint32 *max_input_size; +{ + krb5_gss_ctx_id_rec *ctx; + OM_uint32 data_size, conflen; + OM_uint32 ohlen; + int overhead; + + /* only default qop is allowed */ + if (qop_req != GSS_C_QOP_DEFAULT) { + *minor_status = (OM_uint32) G_UNKNOWN_QOP; + return GSS_S_BAD_QOP; + } + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + if (ctx->terminated || !ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return(GSS_S_NO_CONTEXT); + } + + if (ctx->proto == 1) { + /* No pseudo-ASN.1 wrapper overhead, so no sequence length and + OID. */ + OM_uint32 sz = req_output_size; + + /* Token header: 16 octets. */ + if (conf_req_flag) { + krb5_key key; + krb5_enctype enctype; + + key = ctx->have_acceptor_subkey ? ctx->acceptor_subkey + : ctx->subkey; + enctype = key->keyblock.enctype; + + while (sz > 0 && krb5_encrypt_size(sz, enctype) + 16 > req_output_size) + sz--; + /* Allow for encrypted copy of header. */ + if (sz > 16) + sz -= 16; + else + sz = 0; +#ifdef CFX_EXERCISE + /* Allow for EC padding. In the MIT implementation, only + added while testing. */ + if (sz > 65535) + sz -= 65535; + else + sz = 0; +#endif + } else { + krb5_cksumtype cksumtype; + krb5_error_code err; + size_t cksumsize; + + cksumtype = ctx->have_acceptor_subkey ? ctx->acceptor_subkey_cksumtype + : ctx->cksumtype; + + err = krb5_c_checksum_length(ctx->k5_context, cksumtype, &cksumsize); + if (err) { + *minor_status = err; + return GSS_S_FAILURE; + } + + /* Allow for token header and checksum. */ + if (sz < 16 + cksumsize) + sz = 0; + else + sz -= (16 + cksumsize); + } + + *max_input_size = sz; + *minor_status = 0; + return GSS_S_COMPLETE; + } + + /* Calculate the token size and subtract that from the output size */ + overhead = 7 + ctx->mech_used->length; + data_size = req_output_size; + conflen = kg_confounder_size(ctx->k5_context, ctx->enc->keyblock.enctype); + data_size = (conflen + data_size + 8) & (~(OM_uint32)7); + ohlen = g_token_size(ctx->mech_used, + (unsigned int) (data_size + ctx->cksum_size + 14)) + - req_output_size; + + if (ohlen+overhead < req_output_size) + /* + * Cannot have trailer length that will cause us to pad over our + * length. + */ + *max_input_size = (req_output_size - ohlen - overhead) & (~(OM_uint32)7); + else + *max_input_size = 0; + + *minor_status = 0; + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/libgssapi_krb5.exports b/src/lib/gssapi/libgssapi_krb5.exports new file mode 100644 index 000000000000..9facb3f42671 --- /dev/null +++ b/src/lib/gssapi/libgssapi_krb5.exports @@ -0,0 +1,162 @@ +GSS_C_ATTR_LOCAL_LOGIN_USER +GSS_C_INQ_SSPI_SESSION_KEY +GSS_C_NT_ANONYMOUS +GSS_C_NT_COMPOSITE_EXPORT +GSS_C_NT_EXPORT_NAME +GSS_C_NT_HOSTBASED_SERVICE +GSS_C_NT_HOSTBASED_SERVICE_X +GSS_C_NT_MACHINE_UID_NAME +GSS_C_NT_STRING_UID_NAME +GSS_C_NT_USER_NAME +GSS_KRB5_NT_PRINCIPAL_NAME +GSS_KRB5_CRED_NO_CI_FLAGS_X +GSS_C_MA_MECH_CONCRETE +GSS_C_MA_MECH_PSEUDO +GSS_C_MA_MECH_COMPOSITE +GSS_C_MA_MECH_NEGO +GSS_C_MA_MECH_GLUE +GSS_C_MA_NOT_MECH +GSS_C_MA_DEPRECATED +GSS_C_MA_NOT_DFLT_MECH +GSS_C_MA_ITOK_FRAMED +GSS_C_MA_AUTH_INIT +GSS_C_MA_AUTH_TARG +GSS_C_MA_AUTH_INIT_INIT +GSS_C_MA_AUTH_TARG_INIT +GSS_C_MA_AUTH_INIT_ANON +GSS_C_MA_AUTH_TARG_ANON +GSS_C_MA_DELEG_CRED +GSS_C_MA_INTEG_PROT +GSS_C_MA_CONF_PROT +GSS_C_MA_MIC +GSS_C_MA_WRAP +GSS_C_MA_PROT_READY +GSS_C_MA_REPLAY_DET +GSS_C_MA_OOS_DET +GSS_C_MA_CBINDINGS +GSS_C_MA_PFS +GSS_C_MA_COMPRESS +GSS_C_MA_CTX_TRANS +gss_accept_sec_context +gss_acquire_cred +gss_acquire_cred_with_password +gss_acquire_cred_impersonate_name +gss_add_buffer_set_member +gss_add_cred +gss_add_cred_impersonate_name +gss_add_cred_with_password +gss_add_oid_set_member +gss_authorize_localname +gss_canonicalize_name +gss_compare_name +gss_complete_auth_token +gss_context_time +gss_create_empty_buffer_set +gss_create_empty_oid_set +gss_decapsulate_token +gss_delete_name_attribute +gss_delete_sec_context +gss_display_mech_attr +gss_display_name +gss_display_name_ext +gss_display_status +gss_duplicate_name +gss_encapsulate_token +gss_export_cred +gss_export_name +gss_export_name_composite +gss_export_sec_context +gss_get_mic +gss_get_mic_iov +gss_get_mic_iov_length +gss_get_name_attribute +gss_import_cred +gss_import_name +gss_import_sec_context +gss_indicate_mechs +gss_init_sec_context +gss_indicate_mechs_by_attrs +gss_inquire_attrs_for_mech +gss_inquire_context +gss_inquire_cred +gss_inquire_cred_by_mech +gss_inquire_cred_by_oid +gss_inquire_mech_for_saslname +gss_inquire_mechs_for_name +gss_inquire_names_for_mech +gss_inquire_saslname_for_mech +gss_inquire_sec_context_by_oid +gss_krb5_ccache_name +gss_krb5_copy_ccache +gss_krb5_export_lucid_sec_context +gss_krb5_free_lucid_sec_context +gss_krb5_get_tkt_flags +gss_krb5_import_cred +gss_krb5_set_allowable_enctypes +gss_krb5_set_cred_rcache +gss_krb5int_make_seal_token_v3 +gss_krb5int_unseal_token_v3 +gsskrb5_extract_authtime_from_sec_context +gsskrb5_extract_authz_data_from_sec_context +gss_localname +gss_map_name_to_any +gss_mech_iakerb +gss_mech_krb5 +gss_mech_krb5_old +gss_mech_krb5_wrong +gss_mech_set_krb5 +gss_mech_set_krb5_both +gss_mech_set_krb5_old +gss_nt_exported_name +gss_nt_krb5_name +gss_nt_krb5_principal +gss_nt_machine_uid_name +gss_nt_service_name +gss_nt_service_name_v2 +gss_nt_string_uid_name +gss_nt_user_name +gss_oid_equal +gss_oid_to_str +gss_pname_to_uid +gss_pseudo_random +gss_process_context_token +gss_release_any_name_mapping +gss_release_buffer_set +gss_release_buffer +gss_release_cred +gss_release_iov_buffer +gss_release_name +gss_release_oid +gss_release_oid_set +gss_seal +gss_set_name_attribute +gss_set_neg_mechs +gss_set_sec_context_option +gss_sign +gss_store_cred +gss_str_to_oid +gss_test_oid_set_member +gss_unseal +gss_unwrap +gss_unwrap_aead +gss_unwrap_iov +gss_userok +gss_verify +gss_verify_mic +gss_verify_mic_iov +gss_wrap +gss_wrap_aead +gss_wrap_iov +gss_wrap_iov_length +gss_wrap_size_limit +gss_set_cred_option +gssspi_set_cred_option +gssspi_mech_invoke +krb5_gss_dbg_client_expcreds +krb5_gss_register_acceptor_identity +krb5_gss_use_kdc_context +gss_inquire_name +gss_acquire_cred_from +gss_add_cred_from +gss_store_cred_into +gssint_g_seqstate_init diff --git a/src/lib/gssapi/mechglue/Makefile.in b/src/lib/gssapi/mechglue/Makefile.in new file mode 100644 index 000000000000..5e1ed6df6a4c --- /dev/null +++ b/src/lib/gssapi/mechglue/Makefile.in @@ -0,0 +1,224 @@ +mydir=lib$(S)gssapi$(S)mechglue +BUILDTOP=$(REL)..$(S)..$(S).. +LOCALINCLUDES = -I. -I$(srcdir) -I$(srcdir)/.. -I../generic -I$(srcdir)/../generic -I../krb5 -I$(srcdir)/../krb5 -I../spnego -I$(srcdir)/../spnego +DEFINES=-D_GSS_STATIC_LINK=1 + +##DOSBUILDTOP = ..\..\.. +##DOS##PREFIXDIR=mechglue +##DOS##OBJFILE=..\$(OUTPRE)mechglue.lst + +##DOS##DLL_EXP_TYPE=GSS + +SRCS = \ + $(srcdir)/g_accept_sec_context.c \ + $(srcdir)/g_acquire_cred.c \ + $(srcdir)/g_acquire_cred_with_pw.c \ + $(srcdir)/g_acquire_cred_imp_name.c \ + $(srcdir)/g_authorize_localname.c \ + $(srcdir)/g_buffer_set.c \ + $(srcdir)/g_canon_name.c \ + $(srcdir)/g_compare_name.c \ + $(srcdir)/g_complete_auth_token.c \ + $(srcdir)/g_context_time.c \ + $(srcdir)/g_decapsulate_token.c \ + $(srcdir)/g_delete_sec_context.c \ + $(srcdir)/g_del_name_attr.c \ + $(srcdir)/g_dsp_name.c \ + $(srcdir)/g_dsp_name_ext.c \ + $(srcdir)/g_dsp_status.c \ + $(srcdir)/g_dup_name.c \ + $(srcdir)/g_encapsulate_token.c \ + $(srcdir)/g_exp_sec_context.c \ + $(srcdir)/g_export_cred.c \ + $(srcdir)/g_export_name.c \ + $(srcdir)/g_export_name_comp.c \ + $(srcdir)/g_get_name_attr.c \ + $(srcdir)/g_glue.c \ + $(srcdir)/g_imp_cred.c \ + $(srcdir)/g_imp_name.c \ + $(srcdir)/g_imp_sec_context.c \ + $(srcdir)/g_init_sec_context.c \ + $(srcdir)/g_initialize.c \ + $(srcdir)/g_inq_context.c \ + $(srcdir)/g_inq_context_oid.c \ + $(srcdir)/g_inq_cred.c \ + $(srcdir)/g_inq_cred_oid.c \ + $(srcdir)/g_inq_name.c \ + $(srcdir)/g_inq_names.c \ + $(srcdir)/g_map_name_to_any.c \ + $(srcdir)/g_mech_invoke.c \ + $(srcdir)/g_mechattr.c \ + $(srcdir)/g_mechname.c \ + $(srcdir)/g_oid_ops.c \ + $(srcdir)/g_prf.c \ + $(srcdir)/g_process_context.c \ + $(srcdir)/g_rel_buffer.c \ + $(srcdir)/g_rel_cred.c \ + $(srcdir)/g_rel_name.c \ + $(srcdir)/g_rel_name_mapping.c \ + $(srcdir)/g_rel_oid_set.c \ + $(srcdir)/g_saslname.c \ + $(srcdir)/g_seal.c \ + $(srcdir)/g_set_context_option.c \ + $(srcdir)/g_set_cred_option.c \ + $(srcdir)/g_set_name_attr.c \ + $(srcdir)/g_set_neg_mechs.c \ + $(srcdir)/g_sign.c \ + $(srcdir)/g_store_cred.c \ + $(srcdir)/g_unseal.c \ + $(srcdir)/g_unwrap_aead.c \ + $(srcdir)/g_unwrap_iov.c \ + $(srcdir)/g_verify.c \ + $(srcdir)/g_wrap_aead.c \ + $(srcdir)/g_wrap_iov.c \ + $(srcdir)/gssd_pname_to_uid.c + +OBJS = \ + $(OUTPRE)g_accept_sec_context.$(OBJEXT) \ + $(OUTPRE)g_acquire_cred.$(OBJEXT) \ + $(OUTPRE)g_acquire_cred_with_pw.$(OBJEXT) \ + $(OUTPRE)g_acquire_cred_imp_name.$(OBJEXT) \ + $(OUTPRE)g_authorize_localname.$(OBJEXT) \ + $(OUTPRE)g_buffer_set.$(OBJEXT) \ + $(OUTPRE)g_canon_name.$(OBJEXT) \ + $(OUTPRE)g_compare_name.$(OBJEXT) \ + $(OUTPRE)g_complete_auth_token.$(OBJEXT) \ + $(OUTPRE)g_context_time.$(OBJEXT) \ + $(OUTPRE)g_decapsulate_token.$(OBJEXT) \ + $(OUTPRE)g_delete_sec_context.$(OBJEXT) \ + $(OUTPRE)g_del_name_attr.$(OBJEXT) \ + $(OUTPRE)g_dsp_name.$(OBJEXT) \ + $(OUTPRE)g_dsp_name_ext.$(OBJEXT) \ + $(OUTPRE)g_dsp_status.$(OBJEXT) \ + $(OUTPRE)g_dup_name.$(OBJEXT) \ + $(OUTPRE)g_encapsulate_token.$(OBJEXT) \ + $(OUTPRE)g_exp_sec_context.$(OBJEXT) \ + $(OUTPRE)g_export_cred.$(OBJEXT) \ + $(OUTPRE)g_export_name.$(OBJEXT) \ + $(OUTPRE)g_export_name_comp.$(OBJEXT) \ + $(OUTPRE)g_get_name_attr.$(OBJEXT) \ + $(OUTPRE)g_glue.$(OBJEXT) \ + $(OUTPRE)g_imp_cred.$(OBJEXT) \ + $(OUTPRE)g_imp_name.$(OBJEXT) \ + $(OUTPRE)g_imp_sec_context.$(OBJEXT) \ + $(OUTPRE)g_init_sec_context.$(OBJEXT) \ + $(OUTPRE)g_initialize.$(OBJEXT) \ + $(OUTPRE)g_inq_context.$(OBJEXT) \ + $(OUTPRE)g_inq_context_oid.$(OBJEXT) \ + $(OUTPRE)g_inq_cred.$(OBJEXT) \ + $(OUTPRE)g_inq_cred_oid.$(OBJEXT) \ + $(OUTPRE)g_inq_name.$(OBJEXT) \ + $(OUTPRE)g_inq_names.$(OBJEXT) \ + $(OUTPRE)g_map_name_to_any.$(OBJEXT) \ + $(OUTPRE)g_mech_invoke.$(OBJEXT) \ + $(OUTPRE)g_mechattr.$(OBJEXT) \ + $(OUTPRE)g_mechname.$(OBJEXT) \ + $(OUTPRE)g_oid_ops.$(OBJEXT) \ + $(OUTPRE)g_prf.$(OBJEXT) \ + $(OUTPRE)g_process_context.$(OBJEXT) \ + $(OUTPRE)g_rel_buffer.$(OBJEXT) \ + $(OUTPRE)g_rel_cred.$(OBJEXT) \ + $(OUTPRE)g_rel_name.$(OBJEXT) \ + $(OUTPRE)g_rel_name_mapping.$(OBJEXT) \ + $(OUTPRE)g_rel_oid_set.$(OBJEXT) \ + $(OUTPRE)g_saslname.$(OBJEXT) \ + $(OUTPRE)g_seal.$(OBJEXT) \ + $(OUTPRE)g_set_context_option.$(OBJEXT) \ + $(OUTPRE)g_set_cred_option.$(OBJEXT) \ + $(OUTPRE)g_set_name_attr.$(OBJEXT) \ + $(OUTPRE)g_set_neg_mechs.$(OBJEXT) \ + $(OUTPRE)g_sign.$(OBJEXT) \ + $(OUTPRE)g_store_cred.$(OBJEXT) \ + $(OUTPRE)g_unseal.$(OBJEXT) \ + $(OUTPRE)g_unwrap_aead.$(OBJEXT) \ + $(OUTPRE)g_unwrap_iov.$(OBJEXT) \ + $(OUTPRE)g_verify.$(OBJEXT) \ + $(OUTPRE)g_wrap_aead.$(OBJEXT) \ + $(OUTPRE)g_wrap_iov.$(OBJEXT) \ + $(OUTPRE)gssd_pname_to_uid.$(OBJEXT) + +STLIBOBJS = \ + g_accept_sec_context.o \ + g_acquire_cred.o \ + g_acquire_cred_with_pw.o \ + g_acquire_cred_imp_name.o \ + g_authorize_localname.o \ + g_buffer_set.o \ + g_canon_name.o \ + g_compare_name.o \ + g_complete_auth_token.o \ + g_context_time.o \ + g_decapsulate_token.o \ + g_delete_sec_context.o \ + g_del_name_attr.o \ + g_dsp_name.o \ + g_dsp_name_ext.o \ + g_dsp_status.o \ + g_dup_name.o \ + g_encapsulate_token.o \ + g_exp_sec_context.o \ + g_export_cred.o \ + g_export_name.o \ + g_export_name_comp.o \ + g_get_name_attr.o \ + g_glue.o \ + g_imp_cred.o \ + g_imp_name.o \ + g_imp_sec_context.o \ + g_init_sec_context.o \ + g_initialize.o \ + g_inq_context.o \ + g_inq_context_oid.o \ + g_inq_cred.o \ + g_inq_cred_oid.o \ + g_inq_name.o \ + g_inq_names.o \ + g_map_name_to_any.o \ + g_mech_invoke.o \ + g_mechattr.o \ + g_mechname.o \ + g_oid_ops.o \ + g_prf.o \ + g_process_context.o \ + g_rel_buffer.o \ + g_rel_cred.o \ + g_rel_name.o \ + g_rel_name_mapping.o \ + g_rel_oid_set.o \ + g_saslname.o \ + g_seal.o \ + g_set_context_option.o \ + g_set_cred_option.o \ + g_set_name_attr.o \ + g_set_neg_mechs.o \ + g_sign.o \ + g_store_cred.o \ + g_unseal.o \ + g_unwrap_aead.o \ + g_unwrap_iov.o \ + g_verify.o \ + g_wrap_aead.o \ + g_wrap_iov.o \ + gssd_pname_to_uid.o + +EHDRDIR= $(BUILDTOP)$(S)include$(S)gssapi +EXPORTED_HEADERS = mechglue.h + +$(OBJS): $(EXPORTED_HEADERS) + +all-unix: all-libobjs + +##DOS##LIBOBJS = $(OBJS) + +clean-unix:: clean-libobjs + +# Krb5InstallHeaders($(EXPORTED_HEADERS), $(KRB5_INCDIR)/krb5) +install: + @set -x; for f in $(EXPORTED_HEADERS) ; \ + do $(INSTALL_DATA) $(srcdir)$(S)$$f \ + $(DESTDIR)$(KRB5_INCDIR)$(S)gssapi$(S)$$f ; \ + done + +includes: + +@libobj_frag@ diff --git a/src/lib/gssapi/mechglue/deps b/src/lib/gssapi/mechglue/deps new file mode 100644 index 000000000000..26f62aa8a2ec --- /dev/null +++ b/src/lib/gssapi/mechglue/deps @@ -0,0 +1,510 @@ +# +# Generated makefile dependencies follow. +# +g_accept_sec_context.so g_accept_sec_context.po $(OUTPRE)g_accept_sec_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_accept_sec_context.c mechglue.h mglueP.h +g_acquire_cred.so g_acquire_cred.po $(OUTPRE)g_acquire_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_acquire_cred.c mechglue.h mglueP.h +g_acquire_cred_with_pw.so g_acquire_cred_with_pw.po \ + $(OUTPRE)g_acquire_cred_with_pw.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(COM_ERR_DEPS) \ + $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_ext.h \ + $(srcdir)/../generic/gssapi_generic.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \ + ../generic/gssapi_err_generic.h g_acquire_cred_with_pw.c \ + mechglue.h mglueP.h +g_acquire_cred_imp_name.so g_acquire_cred_imp_name.po \ + $(OUTPRE)g_acquire_cred_imp_name.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(COM_ERR_DEPS) \ + $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_ext.h \ + $(srcdir)/../generic/gssapi_generic.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \ + ../generic/gssapi_err_generic.h g_acquire_cred_imp_name.c \ + mechglue.h mglueP.h +g_authorize_localname.so g_authorize_localname.po $(OUTPRE)g_authorize_localname.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_authorize_localname.c mechglue.h mglueP.h +g_buffer_set.so g_buffer_set.po $(OUTPRE)g_buffer_set.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_buffer_set.c mechglue.h mglueP.h +g_canon_name.so g_canon_name.po $(OUTPRE)g_canon_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_canon_name.c mechglue.h mglueP.h +g_compare_name.so g_compare_name.po $(OUTPRE)g_compare_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_compare_name.c mechglue.h mglueP.h +g_complete_auth_token.so g_complete_auth_token.po $(OUTPRE)g_complete_auth_token.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_complete_auth_token.c mechglue.h mglueP.h +g_context_time.so g_context_time.po $(OUTPRE)g_context_time.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_context_time.c mechglue.h mglueP.h +g_decapsulate_token.so g_decapsulate_token.po $(OUTPRE)g_decapsulate_token.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_decapsulate_token.c mechglue.h mglueP.h +g_delete_sec_context.so g_delete_sec_context.po $(OUTPRE)g_delete_sec_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_delete_sec_context.c mechglue.h mglueP.h +g_del_name_attr.so g_del_name_attr.po $(OUTPRE)g_del_name_attr.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_del_name_attr.c mechglue.h mglueP.h +g_dsp_name.so g_dsp_name.po $(OUTPRE)g_dsp_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_dsp_name.c mechglue.h mglueP.h +g_dsp_name_ext.so g_dsp_name_ext.po $(OUTPRE)g_dsp_name_ext.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_dsp_name_ext.c mechglue.h mglueP.h +g_dsp_status.so g_dsp_status.po $(OUTPRE)g_dsp_status.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_dsp_status.c mechglue.h mglueP.h +g_dup_name.so g_dup_name.po $(OUTPRE)g_dup_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_dup_name.c mechglue.h mglueP.h +g_encapsulate_token.so g_encapsulate_token.po $(OUTPRE)g_encapsulate_token.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_encapsulate_token.c mechglue.h mglueP.h +g_exp_sec_context.so g_exp_sec_context.po $(OUTPRE)g_exp_sec_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_exp_sec_context.c mechglue.h mglueP.h +g_export_cred.so g_export_cred.po $(OUTPRE)g_export_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_export_cred.c mechglue.h mglueP.h +g_export_name.so g_export_name.po $(OUTPRE)g_export_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_export_name.c mechglue.h mglueP.h +g_export_name_comp.so g_export_name_comp.po $(OUTPRE)g_export_name_comp.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_export_name_comp.c mechglue.h mglueP.h +g_get_name_attr.so g_get_name_attr.po $(OUTPRE)g_get_name_attr.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_get_name_attr.c mechglue.h mglueP.h +g_glue.so g_glue.po $(OUTPRE)g_glue.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(COM_ERR_DEPS) \ + $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_ext.h \ + $(srcdir)/../generic/gssapi_generic.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \ + ../generic/gssapi_err_generic.h g_glue.c mechglue.h \ + mglueP.h +g_imp_cred.so g_imp_cred.po $(OUTPRE)g_imp_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_imp_cred.c mechglue.h mglueP.h +g_imp_name.so g_imp_name.po $(OUTPRE)g_imp_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_imp_name.c mechglue.h mglueP.h +g_imp_sec_context.so g_imp_sec_context.po $(OUTPRE)g_imp_sec_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_imp_sec_context.c mechglue.h mglueP.h +g_init_sec_context.so g_init_sec_context.po $(OUTPRE)g_init_sec_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_init_sec_context.c mechglue.h mglueP.h +g_initialize.so g_initialize.po $(OUTPRE)g_initialize.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(srcdir)/../krb5/gssapiP_krb5.h $(srcdir)/../krb5/gssapi_krb5.h \ + $(srcdir)/../spnego/gssapiP_spnego.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h ../generic/gssapi_err_generic.h \ + ../krb5/gssapi_err_krb5.h g_initialize.c mechglue.h \ + mglueP.h +g_inq_context.so g_inq_context.po $(OUTPRE)g_inq_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_inq_context.c mechglue.h mglueP.h +g_inq_context_oid.so g_inq_context_oid.po $(OUTPRE)g_inq_context_oid.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_inq_context_oid.c mechglue.h mglueP.h +g_inq_cred.so g_inq_cred.po $(OUTPRE)g_inq_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_inq_cred.c mechglue.h mglueP.h +g_inq_cred_oid.so g_inq_cred_oid.po $(OUTPRE)g_inq_cred_oid.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_inq_cred_oid.c mechglue.h mglueP.h +g_inq_name.so g_inq_name.po $(OUTPRE)g_inq_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_inq_name.c mechglue.h mglueP.h +g_inq_names.so g_inq_names.po $(OUTPRE)g_inq_names.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_inq_names.c mechglue.h mglueP.h +g_map_name_to_any.so g_map_name_to_any.po $(OUTPRE)g_map_name_to_any.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_map_name_to_any.c mechglue.h mglueP.h +g_mech_invoke.so g_mech_invoke.po $(OUTPRE)g_mech_invoke.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_mech_invoke.c mechglue.h mglueP.h +g_mechattr.so g_mechattr.po $(OUTPRE)g_mechattr.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_mechattr.c mechglue.h mglueP.h +g_mechname.so g_mechname.po $(OUTPRE)g_mechname.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_mechname.c mechglue.h mglueP.h +g_oid_ops.so g_oid_ops.po $(OUTPRE)g_oid_ops.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_oid_ops.c mechglue.h mglueP.h +g_prf.so g_prf.po $(OUTPRE)g_prf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(COM_ERR_DEPS) \ + $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_ext.h \ + $(srcdir)/../generic/gssapi_generic.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \ + ../generic/gssapi_err_generic.h g_prf.c mechglue.h \ + mglueP.h +g_process_context.so g_process_context.po $(OUTPRE)g_process_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_process_context.c mechglue.h mglueP.h +g_rel_buffer.so g_rel_buffer.po $(OUTPRE)g_rel_buffer.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_rel_buffer.c mechglue.h mglueP.h +g_rel_cred.so g_rel_cred.po $(OUTPRE)g_rel_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_rel_cred.c mechglue.h mglueP.h +g_rel_name.so g_rel_name.po $(OUTPRE)g_rel_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_rel_name.c mechglue.h mglueP.h +g_rel_name_mapping.so g_rel_name_mapping.po $(OUTPRE)g_rel_name_mapping.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_rel_name_mapping.c mechglue.h mglueP.h +g_rel_oid_set.so g_rel_oid_set.po $(OUTPRE)g_rel_oid_set.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_rel_oid_set.c mechglue.h mglueP.h +g_saslname.so g_saslname.po $(OUTPRE)g_saslname.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_saslname.c mechglue.h mglueP.h +g_seal.so g_seal.po $(OUTPRE)g_seal.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(COM_ERR_DEPS) \ + $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_ext.h \ + $(srcdir)/../generic/gssapi_generic.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \ + ../generic/gssapi_err_generic.h g_seal.c mechglue.h \ + mglueP.h +g_set_context_option.so g_set_context_option.po $(OUTPRE)g_set_context_option.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_set_context_option.c mechglue.h mglueP.h +g_set_cred_option.so g_set_cred_option.po $(OUTPRE)g_set_cred_option.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_set_cred_option.c mechglue.h mglueP.h +g_set_name_attr.so g_set_name_attr.po $(OUTPRE)g_set_name_attr.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_set_name_attr.c mechglue.h mglueP.h +g_set_neg_mechs.so g_set_neg_mechs.po $(OUTPRE)g_set_neg_mechs.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_set_neg_mechs.c mechglue.h mglueP.h +g_sign.so g_sign.po $(OUTPRE)g_sign.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(COM_ERR_DEPS) \ + $(srcdir)/../generic/gssapiP_generic.h $(srcdir)/../generic/gssapi_ext.h \ + $(srcdir)/../generic/gssapi_generic.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \ + ../generic/gssapi_err_generic.h g_sign.c mechglue.h \ + mglueP.h +g_store_cred.so g_store_cred.po $(OUTPRE)g_store_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_store_cred.c mechglue.h mglueP.h +g_unseal.so g_unseal.po $(OUTPRE)g_unseal.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_unseal.c mechglue.h mglueP.h +g_unwrap_aead.so g_unwrap_aead.po $(OUTPRE)g_unwrap_aead.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_unwrap_aead.c mechglue.h mglueP.h +g_unwrap_iov.so g_unwrap_iov.po $(OUTPRE)g_unwrap_iov.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_unwrap_iov.c mechglue.h mglueP.h +g_verify.so g_verify.po $(OUTPRE)g_verify.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_verify.c mechglue.h mglueP.h +g_wrap_aead.so g_wrap_aead.po $(OUTPRE)g_wrap_aead.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_wrap_aead.c mechglue.h mglueP.h +g_wrap_iov.so g_wrap_iov.po $(OUTPRE)g_wrap_iov.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + g_wrap_iov.c mechglue.h mglueP.h +gssd_pname_to_uid.so gssd_pname_to_uid.po $(OUTPRE)gssd_pname_to_uid.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h ../generic/gssapi_err_generic.h \ + gssd_pname_to_uid.c mechglue.h mglueP.h diff --git a/src/lib/gssapi/mechglue/g_accept_sec_context.c b/src/lib/gssapi/mechglue/g_accept_sec_context.c new file mode 100644 index 000000000000..ddaf87412e9e --- /dev/null +++ b/src/lib/gssapi/mechglue/g_accept_sec_context.c @@ -0,0 +1,399 @@ +/* #pragma ident "@(#)g_accept_sec_context.c 1.19 04/02/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_accept_sec_context + */ + +#include "mglueP.h" +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <errno.h> +#include <time.h> + +#ifndef LEAN_CLIENT +static OM_uint32 +val_acc_sec_ctx_args( + OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_cred_id_t verifier_cred_handle, + gss_buffer_t input_token_buffer, + gss_channel_bindings_t input_chan_bindings, + gss_name_t *src_name, + gss_OID *mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + gss_cred_id_t *d_cred) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (src_name != NULL) + *src_name = GSS_C_NO_NAME; + + if (mech_type != NULL) + *mech_type = GSS_C_NO_OID; + + if (output_token != GSS_C_NO_BUFFER) { + output_token->length = 0; + output_token->value = NULL; + } + + if (d_cred != NULL) + *d_cred = GSS_C_NO_CREDENTIAL; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (context_handle == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (input_token_buffer == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_READ); + + if (output_token == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + return (GSS_S_COMPLETE); +} + +/* Return true if mech should be accepted with no acceptor credential. */ +static int +allow_mech_by_default(gss_OID mech) +{ + OM_uint32 status, minor; + gss_OID_set attrs; + int reject = 0, p; + + /* Whether we accept an interposer mech depends on whether we accept the + * mech it interposes. */ + mech = gssint_get_public_oid(mech); + if (mech == GSS_C_NO_OID) + return 0; + + status = gss_inquire_attrs_for_mech(&minor, mech, &attrs, NULL); + if (status) + return 0; + + /* Check for each attribute which would cause us to exclude this mech from + * the default credential. */ + if (generic_gss_test_oid_set_member(&minor, GSS_C_MA_DEPRECATED, + attrs, &p) != GSS_S_COMPLETE || p) + reject = 1; + else if (generic_gss_test_oid_set_member(&minor, GSS_C_MA_NOT_DFLT_MECH, + attrs, &p) != GSS_S_COMPLETE || p) + reject = 1; + + (void) gss_release_oid_set(&minor, &attrs); + return !reject; +} + +OM_uint32 KRB5_CALLCONV +gss_accept_sec_context (minor_status, + context_handle, + verifier_cred_handle, + input_token_buffer, + input_chan_bindings, + src_name, + mech_type, + output_token, + ret_flags, + time_rec, + d_cred) + +OM_uint32 * minor_status; +gss_ctx_id_t * context_handle; +gss_cred_id_t verifier_cred_handle; +gss_buffer_t input_token_buffer; +gss_channel_bindings_t input_chan_bindings; +gss_name_t * src_name; +gss_OID * mech_type; +gss_buffer_t output_token; +OM_uint32 * ret_flags; +OM_uint32 * time_rec; +gss_cred_id_t * d_cred; + +{ + OM_uint32 status, temp_status, temp_minor_status; + OM_uint32 temp_ret_flags = 0; + gss_union_ctx_id_t union_ctx_id = NULL; + gss_cred_id_t input_cred_handle = GSS_C_NO_CREDENTIAL; + gss_cred_id_t tmp_d_cred = GSS_C_NO_CREDENTIAL; + gss_name_t internal_name = GSS_C_NO_NAME; + gss_name_t tmp_src_name = GSS_C_NO_NAME; + gss_OID_desc token_mech_type_desc; + gss_OID token_mech_type = &token_mech_type_desc; + gss_OID actual_mech = GSS_C_NO_OID; + gss_OID selected_mech = GSS_C_NO_OID; + gss_OID public_mech; + gss_mechanism mech = NULL; + gss_union_cred_t uc; + int i; + + status = val_acc_sec_ctx_args(minor_status, + context_handle, + verifier_cred_handle, + input_token_buffer, + input_chan_bindings, + src_name, + mech_type, + output_token, + ret_flags, + time_rec, + d_cred); + if (status != GSS_S_COMPLETE) + return (status); + + /* + * if context_handle is GSS_C_NO_CONTEXT, allocate a union context + * descriptor to hold the mech type information as well as the + * underlying mechanism context handle. Otherwise, cast the + * value of *context_handle to the union context variable. + */ + + if(*context_handle == GSS_C_NO_CONTEXT) { + + if (input_token_buffer == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_READ); + + /* Get the token mech type */ + status = gssint_get_mech_type(token_mech_type, input_token_buffer); + if (status) + return status; + + /* + * An interposer calling back into the mechglue can't pass in a special + * mech, so we have to recognize it using verifier_cred_handle. Use + * the mechanism for which we have matching creds, if available. + */ + if (verifier_cred_handle != GSS_C_NO_CREDENTIAL) { + uc = (gss_union_cred_t)verifier_cred_handle; + for (i = 0; i < uc->count; i++) { + public_mech = gssint_get_public_oid(&uc->mechs_array[i]); + if (public_mech && g_OID_equal(token_mech_type, public_mech)) { + selected_mech = &uc->mechs_array[i]; + break; + } + } + } + + if (selected_mech == GSS_C_NO_OID) { + status = gssint_select_mech_type(minor_status, token_mech_type, + &selected_mech); + if (status) + return status; + } + + } else { + union_ctx_id = (gss_union_ctx_id_t)*context_handle; + selected_mech = union_ctx_id->mech_type; + } + + /* Now create a new context if we didn't get one. */ + if (*context_handle == GSS_C_NO_CONTEXT) { + status = GSS_S_FAILURE; + union_ctx_id = (gss_union_ctx_id_t) + malloc(sizeof(gss_union_ctx_id_desc)); + if (!union_ctx_id) + return (GSS_S_FAILURE); + + union_ctx_id->loopback = union_ctx_id; + union_ctx_id->internal_ctx_id = GSS_C_NO_CONTEXT; + status = generic_gss_copy_oid(&temp_minor_status, selected_mech, + &union_ctx_id->mech_type); + if (status != GSS_S_COMPLETE) { + free(union_ctx_id); + return (status); + } + + /* set the new context handle to caller's data */ + *context_handle = (gss_ctx_id_t)union_ctx_id; + } + + /* + * get the appropriate cred handle from the union cred struct. + */ + if (verifier_cred_handle != GSS_C_NO_CREDENTIAL) { + input_cred_handle = + gssint_get_mechanism_cred((gss_union_cred_t)verifier_cred_handle, + selected_mech); + if (input_cred_handle == GSS_C_NO_CREDENTIAL) { + /* verifier credential specified but no acceptor credential found */ + status = GSS_S_NO_CRED; + goto error_out; + } + } else if (!allow_mech_by_default(selected_mech)) { + status = GSS_S_NO_CRED; + goto error_out; + } + + /* + * now select the approprate underlying mechanism routine and + * call it. + */ + + mech = gssint_get_mechanism(selected_mech); + if (mech && mech->gss_accept_sec_context) { + + status = mech->gss_accept_sec_context(minor_status, + &union_ctx_id->internal_ctx_id, + input_cred_handle, + input_token_buffer, + input_chan_bindings, + src_name ? &internal_name : NULL, + &actual_mech, + output_token, + &temp_ret_flags, + time_rec, + d_cred ? &tmp_d_cred : NULL); + + /* If there's more work to do, keep going... */ + if (status == GSS_S_CONTINUE_NEEDED) + return GSS_S_CONTINUE_NEEDED; + + /* if the call failed, return with failure */ + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + goto error_out; + } + + /* + * if src_name is non-NULL, + * convert internal_name into a union name equivalent + * First call the mechanism specific display_name() + * then call gss_import_name() to create + * the union name struct cast to src_name + */ + if (src_name != NULL) { + if (internal_name != GSS_C_NO_NAME) { + /* consumes internal_name regardless of success */ + temp_status = gssint_convert_name_to_union_name( + &temp_minor_status, mech, + internal_name, &tmp_src_name); + if (temp_status != GSS_S_COMPLETE) { + status = temp_status; + *minor_status = temp_minor_status; + map_error(minor_status, mech); + if (output_token->length) + (void) gss_release_buffer(&temp_minor_status, + output_token); + goto error_out; + } + *src_name = tmp_src_name; + } else + *src_name = GSS_C_NO_NAME; + } + +#define g_OID_prefix_equal(o1, o2) \ + (((o1)->length >= (o2)->length) && \ + (memcmp((o1)->elements, (o2)->elements, (o2)->length) == 0)) + + /* Ensure we're returning correct creds format */ + if ((temp_ret_flags & GSS_C_DELEG_FLAG) && + tmp_d_cred != GSS_C_NO_CREDENTIAL) { + public_mech = gssint_get_public_oid(selected_mech); + if (actual_mech != GSS_C_NO_OID && + public_mech != GSS_C_NO_OID && + !g_OID_prefix_equal(actual_mech, public_mech)) { + *d_cred = tmp_d_cred; /* unwrapped pseudo-mech */ + } else { + gss_union_cred_t d_u_cred = NULL; + + d_u_cred = malloc(sizeof (gss_union_cred_desc)); + if (d_u_cred == NULL) { + status = GSS_S_FAILURE; + goto error_out; + } + (void) memset(d_u_cred, 0, sizeof (gss_union_cred_desc)); + + d_u_cred->count = 1; + + status = generic_gss_copy_oid(&temp_minor_status, + selected_mech, + &d_u_cred->mechs_array); + + if (status != GSS_S_COMPLETE) { + free(d_u_cred); + goto error_out; + } + + d_u_cred->cred_array = malloc(sizeof(gss_cred_id_t)); + if (d_u_cred->cred_array != NULL) { + d_u_cred->cred_array[0] = tmp_d_cred; + } else { + free(d_u_cred); + status = GSS_S_FAILURE; + goto error_out; + } + + d_u_cred->loopback = d_u_cred; + *d_cred = (gss_cred_id_t)d_u_cred; + } + } + + if (mech_type != NULL) + *mech_type = gssint_get_public_oid(actual_mech); + if (ret_flags != NULL) + *ret_flags = temp_ret_flags; + return (status); + } else { + + status = GSS_S_BAD_MECH; + } + +error_out: + if (union_ctx_id) { + if (union_ctx_id->mech_type) { + if (union_ctx_id->mech_type->elements) + free(union_ctx_id->mech_type->elements); + free(union_ctx_id->mech_type); + } + if (union_ctx_id->internal_ctx_id && mech && + mech->gss_delete_sec_context) { + mech->gss_delete_sec_context(&temp_minor_status, + &union_ctx_id->internal_ctx_id, + GSS_C_NO_BUFFER); + } + free(union_ctx_id); + *context_handle = GSS_C_NO_CONTEXT; + } + + if (src_name) + *src_name = GSS_C_NO_NAME; + + if (tmp_src_name != GSS_C_NO_NAME) + (void) gss_release_buffer(&temp_minor_status, + (gss_buffer_t)tmp_src_name); + + return (status); +} +#endif /* LEAN_CLIENT */ diff --git a/src/lib/gssapi/mechglue/g_acquire_cred.c b/src/lib/gssapi/mechglue/g_acquire_cred.c new file mode 100644 index 000000000000..9bd500b60285 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_acquire_cred.c @@ -0,0 +1,565 @@ +/* #pragma ident "@(#)g_acquire_cred.c 1.22 04/02/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_acquire_cred + */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <errno.h> +#include <time.h> + +static OM_uint32 +val_acq_cred_args( + OM_uint32 *minor_status, + gss_name_t desired_name, + OM_uint32 time_req, + gss_OID_set desired_mechs, + int cred_usage, + gss_const_key_value_set_t cred_store, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_cred_handle != NULL) + *output_cred_handle = GSS_C_NO_CREDENTIAL; + + if (actual_mechs != NULL) + *actual_mechs = GSS_C_NULL_OID_SET; + + if (time_rec != NULL) + *time_rec = 0; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (output_cred_handle == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (cred_usage != GSS_C_ACCEPT + && cred_usage != GSS_C_INITIATE + && cred_usage != GSS_C_BOTH) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_acquire_cred(minor_status, + desired_name, + time_req, + desired_mechs, + cred_usage, + output_cred_handle, + actual_mechs, + time_rec) + +OM_uint32 * minor_status; +gss_name_t desired_name; +OM_uint32 time_req; +gss_OID_set desired_mechs; +int cred_usage; +gss_cred_id_t * output_cred_handle; +gss_OID_set * actual_mechs; +OM_uint32 * time_rec; + +{ + return gss_acquire_cred_from(minor_status, desired_name, time_req, + desired_mechs, cred_usage, NULL, + output_cred_handle, actual_mechs, time_rec); +} + +OM_uint32 KRB5_CALLCONV +gss_acquire_cred_from(minor_status, + desired_name, + time_req, + desired_mechs, + cred_usage, + cred_store, + output_cred_handle, + actual_mechs, + time_rec) + +OM_uint32 * minor_status; +gss_name_t desired_name; +OM_uint32 time_req; +gss_OID_set desired_mechs; +int cred_usage; +gss_const_key_value_set_t cred_store; +gss_cred_id_t * output_cred_handle; +gss_OID_set * actual_mechs; +OM_uint32 * time_rec; + +{ + OM_uint32 major = GSS_S_FAILURE, tmpMinor; + OM_uint32 first_major = GSS_S_COMPLETE, first_minor = 0; + OM_uint32 initTimeOut = 0, acceptTimeOut = 0, outTime = GSS_C_INDEFINITE; + gss_OID_set mechs = GSS_C_NO_OID_SET; + gss_OID_set_desc except_attrs; + gss_OID_desc attr_oids[2]; + unsigned int i; + gss_union_cred_t creds = NULL; + + major = val_acq_cred_args(minor_status, + desired_name, + time_req, + desired_mechs, + cred_usage, + cred_store, + output_cred_handle, + actual_mechs, + time_rec); + if (major != GSS_S_COMPLETE) + goto cleanup; + + /* + * if desired_mechs equals GSS_C_NULL_OID_SET, then try to + * acquire credentials for all non-deprecated mechanisms. + */ + if (desired_mechs == GSS_C_NULL_OID_SET) { + attr_oids[0] = *GSS_C_MA_DEPRECATED; + attr_oids[1] = *GSS_C_MA_NOT_DFLT_MECH; + except_attrs.count = 2; + except_attrs.elements = attr_oids; + major = gss_indicate_mechs_by_attrs(minor_status, GSS_C_NO_OID_SET, + &except_attrs, GSS_C_NO_OID_SET, + &mechs); + if (major != GSS_S_COMPLETE) + goto cleanup; + } else + mechs = desired_mechs; + + if (mechs->count == 0) { + major = GSS_S_BAD_MECH; + goto cleanup; + } + + /* allocate the output credential structure */ + creds = (gss_union_cred_t)calloc(1, sizeof (gss_union_cred_desc)); + if (creds == NULL) { + major = GSS_S_FAILURE; + *minor_status = ENOMEM; + goto cleanup; + } + + creds->count = 0; + creds->loopback = creds; + + /* for each requested mech attempt to obtain a credential */ + for (i = 0, major = GSS_S_UNAVAILABLE; i < mechs->count; i++) { + major = gss_add_cred_from(&tmpMinor, (gss_cred_id_t)creds, + desired_name, &mechs->elements[i], + cred_usage, time_req, time_req, + cred_store, NULL, NULL, + time_rec ? &initTimeOut : NULL, + time_rec ? &acceptTimeOut : NULL); + if (major == GSS_S_COMPLETE) { + /* update the credential's time */ + if (cred_usage == GSS_C_ACCEPT) { + if (outTime > acceptTimeOut) + outTime = acceptTimeOut; + } else if (cred_usage == GSS_C_INITIATE) { + if (outTime > initTimeOut) + outTime = initTimeOut; + } else { + /* + * time_rec is the lesser of the + * init/accept times + */ + if (initTimeOut > acceptTimeOut) + outTime = (outTime > acceptTimeOut) ? + acceptTimeOut : outTime; + else + outTime = (outTime > initTimeOut) ? + initTimeOut : outTime; + } + } else if (first_major == GSS_S_COMPLETE) { + first_major = major; + first_minor = tmpMinor; + } + } /* for */ + + /* If we didn't get any creds, return the error status from the first mech + * (which is often the preferred one). */ + if (creds->count < 1) { + major = first_major; + *minor_status = first_minor; + goto cleanup; + } + major = GSS_S_COMPLETE; + + /* + * fill in output parameters + * setup the actual mechs output parameter + */ + if (actual_mechs != NULL) { + major = gssint_make_public_oid_set(minor_status, creds->mechs_array, + creds->count, actual_mechs); + if (GSS_ERROR(major)) + goto cleanup; + } + + if (time_rec) + *time_rec = outTime; + + *output_cred_handle = (gss_cred_id_t)creds; + +cleanup: + if (GSS_ERROR(major)) + gss_release_cred(&tmpMinor, (gss_cred_id_t *)&creds); + if (desired_mechs == GSS_C_NO_OID_SET) + generic_gss_release_oid_set(&tmpMinor, &mechs); + + return (major); +} + +static OM_uint32 +val_add_cred_args( + OM_uint32 *minor_status, + gss_cred_id_t input_cred_handle, + gss_name_t desired_name, + gss_OID desired_mech, + gss_cred_usage_t cred_usage, + gss_const_key_value_set_t cred_store, + OM_uint32 initiator_time_req, + OM_uint32 acceptor_time_req, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *initiator_time_rec, + OM_uint32 *acceptor_time_rec) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_cred_handle != NULL) + *output_cred_handle = GSS_C_NO_CREDENTIAL; + + if (actual_mechs != NULL) + *actual_mechs = GSS_C_NO_OID_SET; + + if (acceptor_time_rec != NULL) + *acceptor_time_rec = 0; + + if (initiator_time_rec != NULL) + *initiator_time_rec = 0; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL && + output_cred_handle == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED); + + if (cred_usage != GSS_C_ACCEPT + && cred_usage != GSS_C_INITIATE + && cred_usage != GSS_C_BOTH) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + return (GSS_S_COMPLETE); +} + + +/* V2 KRB5_CALLCONV */ +OM_uint32 KRB5_CALLCONV +gss_add_cred(minor_status, input_cred_handle, + desired_name, desired_mech, cred_usage, + initiator_time_req, acceptor_time_req, + output_cred_handle, actual_mechs, + initiator_time_rec, acceptor_time_rec) + OM_uint32 *minor_status; + gss_cred_id_t input_cred_handle; + gss_name_t desired_name; + gss_OID desired_mech; + gss_cred_usage_t cred_usage; + OM_uint32 initiator_time_req; + OM_uint32 acceptor_time_req; + gss_cred_id_t *output_cred_handle; + gss_OID_set *actual_mechs; + OM_uint32 *initiator_time_rec; + OM_uint32 *acceptor_time_rec; +{ + return gss_add_cred_from(minor_status, input_cred_handle, desired_name, + desired_mech, cred_usage, initiator_time_req, + acceptor_time_req, NULL, output_cred_handle, + actual_mechs, initiator_time_rec, + acceptor_time_rec); +} + +OM_uint32 KRB5_CALLCONV +gss_add_cred_from(minor_status, input_cred_handle, + desired_name, desired_mech, + cred_usage, + initiator_time_req, acceptor_time_req, + cred_store, + output_cred_handle, actual_mechs, + initiator_time_rec, acceptor_time_rec) + OM_uint32 *minor_status; + gss_cred_id_t input_cred_handle; + gss_name_t desired_name; + gss_OID desired_mech; + gss_cred_usage_t cred_usage; + OM_uint32 initiator_time_req; + OM_uint32 acceptor_time_req; + gss_const_key_value_set_t cred_store; + gss_cred_id_t *output_cred_handle; + gss_OID_set *actual_mechs; + OM_uint32 *initiator_time_rec; + OM_uint32 *acceptor_time_rec; +{ + OM_uint32 status, temp_minor_status; + OM_uint32 time_req, time_rec = 0, *time_recp = NULL; + gss_union_name_t union_name; + gss_union_cred_t new_union_cred, union_cred; + gss_name_t internal_name = GSS_C_NO_NAME; + gss_name_t allocated_name = GSS_C_NO_NAME; + gss_mechanism mech; + gss_cred_id_t cred = NULL; + gss_OID new_mechs_array = NULL; + gss_cred_id_t * new_cred_array = NULL; + gss_OID_set target_mechs = GSS_C_NO_OID_SET; + gss_OID selected_mech = GSS_C_NO_OID; + + status = val_add_cred_args(minor_status, + input_cred_handle, + desired_name, + desired_mech, + cred_usage, + cred_store, + initiator_time_req, + acceptor_time_req, + output_cred_handle, + actual_mechs, + initiator_time_rec, + acceptor_time_rec); + if (status != GSS_S_COMPLETE) + return (status); + + status = gssint_select_mech_type(minor_status, desired_mech, + &selected_mech); + if (status != GSS_S_COMPLETE) + return (status); + + mech = gssint_get_mechanism(selected_mech); + if (!mech) + return GSS_S_BAD_MECH; + else if (!mech->gss_acquire_cred) + return (GSS_S_UNAVAILABLE); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL) { + union_cred = malloc(sizeof (gss_union_cred_desc)); + if (union_cred == NULL) + return (GSS_S_FAILURE); + + (void) memset(union_cred, 0, sizeof (gss_union_cred_desc)); + } else { + union_cred = (gss_union_cred_t)input_cred_handle; + if (gssint_get_mechanism_cred(union_cred, selected_mech) != + GSS_C_NO_CREDENTIAL) + return (GSS_S_DUPLICATE_ELEMENT); + } + + /* for default credentials we will use GSS_C_NO_NAME */ + if (input_cred_handle != GSS_C_NO_CREDENTIAL || + cred_store != GSS_C_NO_CRED_STORE) { + /* may need to create a mechanism specific name */ + if (desired_name) { + union_name = (gss_union_name_t)desired_name; + if (union_name->mech_type && + g_OID_equal(union_name->mech_type, selected_mech)) + internal_name = union_name->mech_name; + else { + if (gssint_import_internal_name(minor_status, selected_mech, + union_name, &allocated_name) != + GSS_S_COMPLETE) + return (GSS_S_BAD_NAME); + internal_name = allocated_name; + } + } + } + + + if (cred_usage == GSS_C_ACCEPT) + time_req = acceptor_time_req; + else if (cred_usage == GSS_C_INITIATE) + time_req = initiator_time_req; + else if (cred_usage == GSS_C_BOTH) + time_req = (acceptor_time_req > initiator_time_req) ? + acceptor_time_req : initiator_time_req; + else + time_req = 0; + + status = gss_create_empty_oid_set(minor_status, &target_mechs); + if (status != GSS_S_COMPLETE) + goto errout; + + status = gss_add_oid_set_member(minor_status, + gssint_get_public_oid(selected_mech), + &target_mechs); + if (status != GSS_S_COMPLETE) + goto errout; + + if (initiator_time_rec != NULL || acceptor_time_rec != NULL) + time_recp = &time_rec; + + if (mech->gss_acquire_cred_from) { + status = mech->gss_acquire_cred_from(minor_status, internal_name, + time_req, target_mechs, + cred_usage, cred_store, &cred, + NULL, time_recp); + } else if (cred_store == GSS_C_NO_CRED_STORE) { + status = mech->gss_acquire_cred(minor_status, internal_name, time_req, + target_mechs, cred_usage, &cred, NULL, + time_recp); + } else { + return GSS_S_UNAVAILABLE; + } + + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + goto errout; + } + + /* now add the new credential elements */ + new_mechs_array = (gss_OID) + malloc(sizeof (gss_OID_desc) * (union_cred->count+1)); + + new_cred_array = (gss_cred_id_t *) + malloc(sizeof (gss_cred_id_t) * (union_cred->count+1)); + + if (!new_mechs_array || !new_cred_array) { + status = GSS_S_FAILURE; + goto errout; + } + + if (acceptor_time_rec) + if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) + *acceptor_time_rec = time_rec; + if (initiator_time_rec) + if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) + *initiator_time_rec = time_rec; + + /* + * OK, expand the mechanism array and the credential array + */ + (void) memcpy(new_mechs_array, union_cred->mechs_array, + sizeof (gss_OID_desc) * union_cred->count); + (void) memcpy(new_cred_array, union_cred->cred_array, + sizeof (gss_cred_id_t) * union_cred->count); + + new_cred_array[union_cred->count] = cred; + if ((new_mechs_array[union_cred->count].elements = + malloc(selected_mech->length)) == NULL) + goto errout; + + g_OID_copy(&new_mechs_array[union_cred->count], selected_mech); + + if (actual_mechs != NULL) { + status = gssint_make_public_oid_set(minor_status, new_mechs_array, + union_cred->count + 1, + actual_mechs); + if (GSS_ERROR(status)) { + free(new_mechs_array[union_cred->count].elements); + goto errout; + } + } + + if (output_cred_handle == NULL) { + free(union_cred->mechs_array); + free(union_cred->cred_array); + new_union_cred = union_cred; + } else { + new_union_cred = malloc(sizeof (gss_union_cred_desc)); + if (new_union_cred == NULL) { + free(new_mechs_array[union_cred->count].elements); + goto errout; + } + *new_union_cred = *union_cred; + *output_cred_handle = (gss_cred_id_t)new_union_cred; + } + + new_union_cred->mechs_array = new_mechs_array; + new_union_cred->cred_array = new_cred_array; + new_union_cred->count++; + new_union_cred->loopback = new_union_cred; + + /* We're done with the internal name. Free it if we allocated it. */ + + if (allocated_name) + (void) gssint_release_internal_name(&temp_minor_status, + selected_mech, + &allocated_name); + (void) generic_gss_release_oid_set(&temp_minor_status, &target_mechs); + + return (GSS_S_COMPLETE); + +errout: + if (new_mechs_array) + free(new_mechs_array); + if (new_cred_array) + free(new_cred_array); + + if (cred != NULL && mech->gss_release_cred) + mech->gss_release_cred(&temp_minor_status, &cred); + + if (allocated_name) + (void) gssint_release_internal_name(&temp_minor_status, + selected_mech, + &allocated_name); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL && union_cred) + free(union_cred); + + (void) generic_gss_release_oid_set(&temp_minor_status, &target_mechs); + + return (status); +} diff --git a/src/lib/gssapi/mechglue/g_acquire_cred_imp_name.c b/src/lib/gssapi/mechglue/g_acquire_cred_imp_name.c new file mode 100644 index 000000000000..9eab25e4cd8e --- /dev/null +++ b/src/lib/gssapi/mechglue/g_acquire_cred_imp_name.c @@ -0,0 +1,529 @@ +/* #pragma ident "@(#)g_acquire_cred.c 1.22 04/02/23 SMI" */ + +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* Glue routine for gss_acquire_cred_impersonate_name */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <errno.h> +#include <time.h> + +static OM_uint32 +val_acq_cred_impersonate_name_args( + OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + const gss_name_t desired_name, + OM_uint32 time_req, + gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_cred_handle != NULL) + *output_cred_handle = GSS_C_NO_CREDENTIAL; + + if (actual_mechs != NULL) + *actual_mechs = GSS_C_NULL_OID_SET; + + if (time_rec != NULL) + *time_rec = 0; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED); + + if (desired_name == GSS_C_NO_NAME) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME); + + if (output_cred_handle == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (cred_usage != GSS_C_ACCEPT + && cred_usage != GSS_C_INITIATE + && cred_usage != GSS_C_BOTH) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_acquire_cred_impersonate_name(OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + const gss_name_t desired_name, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + OM_uint32 major = GSS_S_FAILURE; + OM_uint32 initTimeOut, acceptTimeOut, outTime = GSS_C_INDEFINITE; + gss_OID_set_desc default_OID_set; + gss_OID_set mechs; + gss_OID_desc default_OID; + gss_mechanism mech; + unsigned int i; + gss_union_cred_t creds; + + major = val_acq_cred_impersonate_name_args(minor_status, + impersonator_cred_handle, + desired_name, + time_req, + desired_mechs, + cred_usage, + output_cred_handle, + actual_mechs, + time_rec); + if (major != GSS_S_COMPLETE) + return (major); + + /* Initial value needed below. */ + major = GSS_S_FAILURE; + + /* + * if desired_mechs equals GSS_C_NULL_OID_SET, then pick an + * appropriate default. We use the first mechanism in the + * mechansim list as the default. This set is created with + * statics thus needs not be freed + */ + if(desired_mechs == GSS_C_NULL_OID_SET) { + mech = gssint_get_mechanism(NULL); + if (mech == NULL) + return (GSS_S_BAD_MECH); + + mechs = &default_OID_set; + default_OID_set.count = 1; + default_OID_set.elements = &default_OID; + default_OID.length = mech->mech_type.length; + default_OID.elements = mech->mech_type.elements; + } else + mechs = desired_mechs; + + if (mechs->count == 0) + return (GSS_S_BAD_MECH); + + /* allocate the output credential structure */ + creds = (gss_union_cred_t)malloc(sizeof (gss_union_cred_desc)); + if (creds == NULL) + return (GSS_S_FAILURE); + + /* initialize to 0s */ + (void) memset(creds, 0, sizeof (gss_union_cred_desc)); + creds->loopback = creds; + + /* for each requested mech attempt to obtain a credential */ + for (i = 0; i < mechs->count; i++) { + major = gss_add_cred_impersonate_name(minor_status, + (gss_cred_id_t)creds, + impersonator_cred_handle, + desired_name, + &mechs->elements[i], + cred_usage, + time_req, + time_req, NULL, + NULL, + &initTimeOut, + &acceptTimeOut); + if (major == GSS_S_COMPLETE) { + /* update the credential's time */ + if (cred_usage == GSS_C_ACCEPT) { + if (outTime > acceptTimeOut) + outTime = acceptTimeOut; + } else if (cred_usage == GSS_C_INITIATE) { + if (outTime > initTimeOut) + outTime = initTimeOut; + } else { + /* + * time_rec is the lesser of the + * init/accept times + */ + if (initTimeOut > acceptTimeOut) + outTime = (outTime > acceptTimeOut) ? + acceptTimeOut : outTime; + else + outTime = (outTime > initTimeOut) ? + initTimeOut : outTime; + } + } + } /* for */ + + /* ensure that we have at least one credential element */ + if (creds->count < 1) { + free(creds); + return (major); + } + + /* + * fill in output parameters + * setup the actual mechs output parameter + */ + if (actual_mechs != NULL) { + gss_OID_set_desc oids; + + oids.count = creds->count; + oids.elements = creds->mechs_array; + + major = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs); + if (GSS_ERROR(major)) { + (void) gss_release_cred(minor_status, + (gss_cred_id_t *)&creds); + return (major); + } + } + + if (time_rec) + *time_rec = outTime; + + + creds->loopback = creds; + *output_cred_handle = (gss_cred_id_t)creds; + return (GSS_S_COMPLETE); +} + +static OM_uint32 +val_add_cred_impersonate_name_args( + OM_uint32 *minor_status, + gss_cred_id_t input_cred_handle, + const gss_cred_id_t impersonator_cred_handle, + gss_name_t desired_name, + gss_OID desired_mech, + gss_cred_usage_t cred_usage, + OM_uint32 initiator_time_req, + OM_uint32 acceptor_time_req, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *initiator_time_rec, + OM_uint32 *acceptor_time_rec) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_cred_handle != NULL) + *output_cred_handle = GSS_C_NO_CREDENTIAL; + + if (actual_mechs != NULL) + *actual_mechs = GSS_C_NO_OID_SET; + + if (acceptor_time_rec != NULL) + *acceptor_time_rec = 0; + + if (initiator_time_rec != NULL) + *initiator_time_rec = 0; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED); + + if (desired_name == GSS_C_NO_NAME) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL && + output_cred_handle == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED); + + if (cred_usage != GSS_C_ACCEPT + && cred_usage != GSS_C_INITIATE + && cred_usage != GSS_C_BOTH) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + return (GSS_S_COMPLETE); +} + + +/* V2 KRB5_CALLCONV */ +OM_uint32 KRB5_CALLCONV +gss_add_cred_impersonate_name(OM_uint32 *minor_status, + gss_cred_id_t input_cred_handle, + const gss_cred_id_t impersonator_cred_handle, + const gss_name_t desired_name, + const gss_OID desired_mech, + gss_cred_usage_t cred_usage, + OM_uint32 initiator_time_req, + OM_uint32 acceptor_time_req, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *initiator_time_rec, + OM_uint32 *acceptor_time_rec) +{ + OM_uint32 status, temp_minor_status; + OM_uint32 time_req, time_rec; + gss_union_name_t union_name; + gss_union_cred_t new_union_cred, union_cred; + gss_cred_id_t mech_impersonator_cred; + gss_name_t internal_name = GSS_C_NO_NAME; + gss_name_t allocated_name = GSS_C_NO_NAME; + gss_mechanism mech; + gss_cred_id_t cred = NULL; + gss_OID new_mechs_array = NULL; + gss_cred_id_t * new_cred_array = NULL; + gss_OID_set target_mechs = GSS_C_NO_OID_SET; + gss_OID selected_mech = GSS_C_NO_OID; + + status = val_add_cred_impersonate_name_args(minor_status, + input_cred_handle, + impersonator_cred_handle, + desired_name, + desired_mech, + cred_usage, + initiator_time_req, + acceptor_time_req, + output_cred_handle, + actual_mechs, + initiator_time_rec, + acceptor_time_rec); + if (status != GSS_S_COMPLETE) + return (status); + + status = gssint_select_mech_type(minor_status, desired_mech, + &selected_mech); + if (status != GSS_S_COMPLETE) + return status; + + mech = gssint_get_mechanism(selected_mech); + if (!mech) + return GSS_S_BAD_MECH; + else if (!mech->gss_acquire_cred_impersonate_name) + return (GSS_S_UNAVAILABLE); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL) { + union_cred = malloc(sizeof (gss_union_cred_desc)); + if (union_cred == NULL) + return (GSS_S_FAILURE); + + (void) memset(union_cred, 0, sizeof (gss_union_cred_desc)); + + /* for default credentials we will use GSS_C_NO_NAME */ + internal_name = GSS_C_NO_NAME; + } else { + union_cred = (gss_union_cred_t)input_cred_handle; + if (gssint_get_mechanism_cred(union_cred, selected_mech) != + GSS_C_NO_CREDENTIAL) + return (GSS_S_DUPLICATE_ELEMENT); + } + + mech_impersonator_cred = + gssint_get_mechanism_cred((gss_union_cred_t)impersonator_cred_handle, + selected_mech); + if (mech_impersonator_cred == GSS_C_NO_CREDENTIAL) + return (GSS_S_NO_CRED); + + /* may need to create a mechanism specific name */ + union_name = (gss_union_name_t)desired_name; + if (union_name->mech_type && + g_OID_equal(union_name->mech_type, selected_mech)) + internal_name = union_name->mech_name; + else { + if (gssint_import_internal_name(minor_status, + selected_mech, union_name, + &allocated_name) != GSS_S_COMPLETE) + return (GSS_S_BAD_NAME); + internal_name = allocated_name; + } + + if (cred_usage == GSS_C_ACCEPT) + time_req = acceptor_time_req; + else if (cred_usage == GSS_C_INITIATE) + time_req = initiator_time_req; + else if (cred_usage == GSS_C_BOTH) + time_req = (acceptor_time_req > initiator_time_req) ? + acceptor_time_req : initiator_time_req; + else + time_req = 0; + + status = gss_create_empty_oid_set(minor_status, &target_mechs); + if (status != GSS_S_COMPLETE) + goto errout; + + status = gss_add_oid_set_member(minor_status, + gssint_get_public_oid(selected_mech), + &target_mechs); + if (status != GSS_S_COMPLETE) + goto errout; + + status = mech->gss_acquire_cred_impersonate_name(minor_status, + mech_impersonator_cred, + internal_name, + time_req, + target_mechs, + cred_usage, + &cred, + NULL, + &time_rec); + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + goto errout; + } + + /* now add the new credential elements */ + new_mechs_array = (gss_OID) + malloc(sizeof (gss_OID_desc) * (union_cred->count+1)); + + new_cred_array = (gss_cred_id_t *) + malloc(sizeof (gss_cred_id_t) * (union_cred->count+1)); + + if (!new_mechs_array || !new_cred_array) { + status = GSS_S_FAILURE; + goto errout; + } + + if (acceptor_time_rec) + if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) + *acceptor_time_rec = time_rec; + if (initiator_time_rec) + if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) + *initiator_time_rec = time_rec; + + /* + * OK, expand the mechanism array and the credential array + */ + (void) memcpy(new_mechs_array, union_cred->mechs_array, + sizeof (gss_OID_desc) * union_cred->count); + (void) memcpy(new_cred_array, union_cred->cred_array, + sizeof (gss_cred_id_t) * union_cred->count); + + new_cred_array[union_cred->count] = cred; + if ((new_mechs_array[union_cred->count].elements = + malloc(selected_mech->length)) == NULL) + goto errout; + + g_OID_copy(&new_mechs_array[union_cred->count], selected_mech); + + if (actual_mechs != NULL) { + status = gssint_make_public_oid_set(minor_status, new_mechs_array, + union_cred->count + 1, + actual_mechs); + if (GSS_ERROR(status)) { + free(new_mechs_array[union_cred->count].elements); + goto errout; + } + } + + if (output_cred_handle == NULL) { + free(union_cred->mechs_array); + free(union_cred->cred_array); + new_union_cred = union_cred; + } else { + new_union_cred = malloc(sizeof (gss_union_cred_desc)); + if (new_union_cred == NULL) { + free(new_mechs_array[union_cred->count].elements); + goto errout; + } + *new_union_cred = *union_cred; + *output_cred_handle = (gss_cred_id_t)new_union_cred; + } + + new_union_cred->mechs_array = new_mechs_array; + new_union_cred->cred_array = new_cred_array; + new_union_cred->count++; + new_union_cred->loopback = new_union_cred; + + /* We're done with the internal name. Free it if we allocated it. */ + + if (allocated_name) + (void) gssint_release_internal_name(&temp_minor_status, selected_mech, + &allocated_name); + + if (target_mechs) + (void) gss_release_oid_set(&temp_minor_status, &target_mechs); + + return (GSS_S_COMPLETE); + +errout: + if (new_mechs_array) + free(new_mechs_array); + if (new_cred_array) + free(new_cred_array); + + if (cred != NULL && mech->gss_release_cred) + mech->gss_release_cred(&temp_minor_status, &cred); + + if (allocated_name) + (void) gssint_release_internal_name(&temp_minor_status, + selected_mech, &allocated_name); + + if (target_mechs) + (void) gss_release_oid_set(&temp_minor_status, &target_mechs); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL && union_cred) + free(union_cred); + + return (status); +} diff --git a/src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c b/src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c new file mode 100644 index 000000000000..7835d59726d3 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c @@ -0,0 +1,524 @@ +/* #pragma ident "@(#)g_acquire_cred.c 1.22 04/02/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_acquire_cred_with_password + */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <errno.h> +#include <time.h> + +static OM_uint32 +val_acq_cred_pw_args( + OM_uint32 *minor_status, + const gss_name_t desired_name, + const gss_buffer_t password, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + int cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_cred_handle != NULL) + *output_cred_handle = GSS_C_NO_CREDENTIAL; + + if (actual_mechs != NULL) + *actual_mechs = GSS_C_NULL_OID_SET; + + if (time_rec != NULL) + *time_rec = 0; + + /* Validate arguments. */ + + if (desired_name == GSS_C_NO_NAME) + return (GSS_S_BAD_NAME); + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (output_cred_handle == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (cred_usage != GSS_C_ACCEPT + && cred_usage != GSS_C_INITIATE + && cred_usage != GSS_C_BOTH) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + if (password == GSS_C_NO_BUFFER || + password->length == 0 || + password->value == NULL) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_acquire_cred_with_password( + minor_status, + desired_name, + password, + time_req, + desired_mechs, + cred_usage, + output_cred_handle, + actual_mechs, + time_rec) + +OM_uint32 * minor_status; +const gss_name_t desired_name; +const gss_buffer_t password; +OM_uint32 time_req; +const gss_OID_set desired_mechs; +int cred_usage; +gss_cred_id_t * output_cred_handle; +gss_OID_set * actual_mechs; +OM_uint32 * time_rec; +{ + OM_uint32 major = GSS_S_FAILURE; + OM_uint32 initTimeOut, acceptTimeOut, outTime = GSS_C_INDEFINITE; + gss_OID_set_desc default_OID_set; + gss_OID_set mechs; + gss_OID_desc default_OID; + gss_mechanism mech; + unsigned int i; + gss_union_cred_t creds; + + major = val_acq_cred_pw_args(minor_status, + desired_name, + password, + time_req, + desired_mechs, + cred_usage, + output_cred_handle, + actual_mechs, + time_rec); + if (major != GSS_S_COMPLETE) + return (major); + + /* Initial value needed below. */ + major = GSS_S_FAILURE; + + /* + * if desired_mechs equals GSS_C_NULL_OID_SET, then pick an + * appropriate default. We use the first mechanism in the + * mechansim list as the default. This set is created with + * statics thus needs not be freed + */ + if(desired_mechs == GSS_C_NULL_OID_SET) { + mech = gssint_get_mechanism(NULL); + if (mech == NULL) + return (GSS_S_BAD_MECH); + + mechs = &default_OID_set; + default_OID_set.count = 1; + default_OID_set.elements = &default_OID; + default_OID.length = mech->mech_type.length; + default_OID.elements = mech->mech_type.elements; + } else + mechs = desired_mechs; + + if (mechs->count == 0) + return (GSS_S_BAD_MECH); + + /* allocate the output credential structure */ + creds = (gss_union_cred_t)malloc(sizeof (gss_union_cred_desc)); + if (creds == NULL) + return (GSS_S_FAILURE); + + /* initialize to 0s */ + (void) memset(creds, 0, sizeof (gss_union_cred_desc)); + creds->loopback = creds; + + /* for each requested mech attempt to obtain a credential */ + for (i = 0; i < mechs->count; i++) { + major = gss_add_cred_with_password(minor_status, (gss_cred_id_t)creds, + desired_name, + &mechs->elements[i], + password, + cred_usage, time_req, time_req, NULL, + NULL, &initTimeOut, &acceptTimeOut); + if (major == GSS_S_COMPLETE) { + /* update the credential's time */ + if (cred_usage == GSS_C_ACCEPT) { + if (outTime > acceptTimeOut) + outTime = acceptTimeOut; + } else if (cred_usage == GSS_C_INITIATE) { + if (outTime > initTimeOut) + outTime = initTimeOut; + } else { + /* + * time_rec is the lesser of the + * init/accept times + */ + if (initTimeOut > acceptTimeOut) + outTime = (outTime > acceptTimeOut) ? + acceptTimeOut : outTime; + else + outTime = (outTime > initTimeOut) ? + initTimeOut : outTime; + } + } + } /* for */ + + /* ensure that we have at least one credential element */ + if (creds->count < 1) { + free(creds); + return (major); + } + + /* + * fill in output parameters + * setup the actual mechs output parameter + */ + if (actual_mechs != NULL) { + major = gssint_make_public_oid_set(minor_status, creds->mechs_array, + creds->count, actual_mechs); + if (GSS_ERROR(major)) { + (void) gss_release_cred(minor_status, + (gss_cred_id_t *)&creds); + return (major); + } + } + + if (time_rec) + *time_rec = outTime; + + + creds->loopback = creds; + *output_cred_handle = (gss_cred_id_t)creds; + return (GSS_S_COMPLETE); +} + +static OM_uint32 +val_add_cred_pw_args( + OM_uint32 *minor_status, + gss_cred_id_t input_cred_handle, + const gss_name_t desired_name, + const gss_OID desired_mech, + const gss_buffer_t password, + gss_cred_usage_t cred_usage, + OM_uint32 initiator_time_req, + OM_uint32 acceptor_time_req, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *initiator_time_rec, + OM_uint32 *acceptor_time_rec) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_cred_handle != NULL) + *output_cred_handle = GSS_C_NO_CREDENTIAL; + + if (actual_mechs != NULL) + *actual_mechs = GSS_C_NO_OID_SET; + + if (acceptor_time_rec != NULL) + *acceptor_time_rec = 0; + + if (initiator_time_rec != NULL) + *initiator_time_rec = 0; + + /* Validate arguments. */ + + if (desired_name == GSS_C_NO_NAME) + return (GSS_S_BAD_NAME); + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL && + output_cred_handle == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED); + + if (cred_usage != GSS_C_ACCEPT + && cred_usage != GSS_C_INITIATE + && cred_usage != GSS_C_BOTH) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + if (password == GSS_C_NO_BUFFER || + password->length == 0 || + password->value == NULL) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + + return (GSS_S_COMPLETE); +} + + +/* V2 KRB5_CALLCONV */ +OM_uint32 KRB5_CALLCONV +gss_add_cred_with_password(minor_status, input_cred_handle, + desired_name, desired_mech, password, cred_usage, + initiator_time_req, acceptor_time_req, + output_cred_handle, actual_mechs, + initiator_time_rec, acceptor_time_rec) + OM_uint32 *minor_status; + const gss_cred_id_t input_cred_handle; + const gss_name_t desired_name; + const gss_OID desired_mech; + const gss_buffer_t password; + gss_cred_usage_t cred_usage; + OM_uint32 initiator_time_req; + OM_uint32 acceptor_time_req; + gss_cred_id_t *output_cred_handle; + gss_OID_set *actual_mechs; + OM_uint32 *initiator_time_rec; + OM_uint32 *acceptor_time_rec; +{ + OM_uint32 status, temp_minor_status; + OM_uint32 time_req, time_rec; + gss_union_name_t union_name; + gss_union_cred_t new_union_cred, union_cred; + gss_name_t internal_name = GSS_C_NO_NAME; + gss_name_t allocated_name = GSS_C_NO_NAME; + gss_mechanism mech; + gss_cred_id_t cred = NULL; + gss_OID new_mechs_array = NULL; + gss_cred_id_t * new_cred_array = NULL; + gss_OID_set target_mechs = GSS_C_NO_OID_SET; + gss_OID selected_mech = GSS_C_NO_OID; + + status = val_add_cred_pw_args(minor_status, + input_cred_handle, + desired_name, + desired_mech, + password, + cred_usage, + initiator_time_req, + acceptor_time_req, + output_cred_handle, + actual_mechs, + initiator_time_rec, + acceptor_time_rec); + if (status != GSS_S_COMPLETE) + return (status); + + status = gssint_select_mech_type(minor_status, desired_mech, + &selected_mech); + if (status != GSS_S_COMPLETE) + return (status); + + mech = gssint_get_mechanism(selected_mech); + if (!mech) + return GSS_S_BAD_MECH; + if (!mech->gssspi_acquire_cred_with_password) + return GSS_S_UNAVAILABLE; + + if (input_cred_handle == GSS_C_NO_CREDENTIAL) { + union_cred = malloc(sizeof (gss_union_cred_desc)); + if (union_cred == NULL) + return (GSS_S_FAILURE); + + (void) memset(union_cred, 0, sizeof (gss_union_cred_desc)); + + /* for default credentials we will use GSS_C_NO_NAME */ + internal_name = GSS_C_NO_NAME; + } else { + union_cred = (gss_union_cred_t)input_cred_handle; + if (gssint_get_mechanism_cred(union_cred, selected_mech) != + GSS_C_NO_CREDENTIAL) + return (GSS_S_DUPLICATE_ELEMENT); + } + + /* may need to create a mechanism specific name */ + union_name = (gss_union_name_t)desired_name; + if (union_name->mech_type && + g_OID_equal(union_name->mech_type, selected_mech)) + internal_name = union_name->mech_name; + else { + if (gssint_import_internal_name(minor_status, + selected_mech, union_name, + &allocated_name) != GSS_S_COMPLETE) + return (GSS_S_BAD_NAME); + internal_name = allocated_name; + } + + if (cred_usage == GSS_C_ACCEPT) + time_req = acceptor_time_req; + else if (cred_usage == GSS_C_INITIATE) + time_req = initiator_time_req; + else if (cred_usage == GSS_C_BOTH) + time_req = (acceptor_time_req > initiator_time_req) ? + acceptor_time_req : initiator_time_req; + else + time_req = 0; + + status = gss_create_empty_oid_set(minor_status, &target_mechs); + if (status != GSS_S_COMPLETE) + goto errout; + + status = gss_add_oid_set_member(minor_status, + gssint_get_public_oid(selected_mech), + &target_mechs); + if (status != GSS_S_COMPLETE) + goto errout; + + status = mech->gssspi_acquire_cred_with_password(minor_status, + internal_name, + password, + time_req, + target_mechs, + cred_usage, + &cred, + NULL, + &time_rec); + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + goto errout; + } + + /* now add the new credential elements */ + new_mechs_array = (gss_OID) + malloc(sizeof (gss_OID_desc) * (union_cred->count+1)); + + new_cred_array = (gss_cred_id_t *) + malloc(sizeof (gss_cred_id_t) * (union_cred->count+1)); + + if (!new_mechs_array || !new_cred_array) { + status = GSS_S_FAILURE; + goto errout; + } + + if (acceptor_time_rec) + if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) + *acceptor_time_rec = time_rec; + if (initiator_time_rec) + if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) + *initiator_time_rec = time_rec; + + /* + * OK, expand the mechanism array and the credential array + */ + (void) memcpy(new_mechs_array, union_cred->mechs_array, + sizeof (gss_OID_desc) * union_cred->count); + (void) memcpy(new_cred_array, union_cred->cred_array, + sizeof (gss_cred_id_t) * union_cred->count); + + new_cred_array[union_cred->count] = cred; + if ((new_mechs_array[union_cred->count].elements = + malloc(selected_mech->length)) == NULL) + goto errout; + + g_OID_copy(&new_mechs_array[union_cred->count], selected_mech); + + if (actual_mechs != NULL) { + status = gssint_make_public_oid_set(minor_status, new_mechs_array, + union_cred->count + 1, + actual_mechs); + if (GSS_ERROR(status)) { + free(new_mechs_array[union_cred->count].elements); + goto errout; + } + } + + if (output_cred_handle == NULL) { + free(union_cred->mechs_array); + free(union_cred->cred_array); + new_union_cred = union_cred; + } else { + new_union_cred = malloc(sizeof (gss_union_cred_desc)); + if (new_union_cred == NULL) { + free(new_mechs_array[union_cred->count].elements); + goto errout; + } + *new_union_cred = *union_cred; + *output_cred_handle = (gss_cred_id_t)new_union_cred; + } + + new_union_cred->mechs_array = new_mechs_array; + new_union_cred->cred_array = new_cred_array; + new_union_cred->count++; + new_union_cred->loopback = new_union_cred; + + /* We're done with the internal name. Free it if we allocated it. */ + + if (allocated_name) + (void) gssint_release_internal_name(&temp_minor_status, + selected_mech, + &allocated_name); + + if (target_mechs) + (void)gss_release_oid_set(&temp_minor_status, &target_mechs); + + return (GSS_S_COMPLETE); + +errout: + if (new_mechs_array) + free(new_mechs_array); + if (new_cred_array) + free(new_cred_array); + + if (cred != NULL && mech->gss_release_cred) + mech->gss_release_cred(&temp_minor_status, &cred); + + if (allocated_name) + (void) gssint_release_internal_name(&temp_minor_status, + selected_mech, &allocated_name); + + if (target_mechs) + (void)gss_release_oid_set(&temp_minor_status, &target_mechs); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL && union_cred) + free(union_cred); + + return (status); +} diff --git a/src/lib/gssapi/mechglue/g_authorize_localname.c b/src/lib/gssapi/mechglue/g_authorize_localname.c new file mode 100644 index 000000000000..0e4fb0722924 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_authorize_localname.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2011, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* #pragma ident "@(#)g_userok.c 1.1 04/03/25 SMI" */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <mglueP.h> +#include <gssapi/gssapi.h> + +static OM_uint32 +mech_authorize_localname(OM_uint32 *minor, + const gss_union_name_t unionName, + const gss_union_name_t unionUser) +{ + OM_uint32 major = GSS_S_UNAVAILABLE; + gss_mechanism mech; + + if (unionName->mech_type == GSS_C_NO_OID) + return (GSS_S_NAME_NOT_MN); + + mech = gssint_get_mechanism(unionName->mech_type); + if (mech == NULL) + return (GSS_S_UNAVAILABLE); + + if (mech->gssspi_authorize_localname != NULL) { + major = mech->gssspi_authorize_localname(minor, + unionName->mech_name, + unionUser->external_name, + unionUser->name_type); + if (major != GSS_S_COMPLETE) + map_error(minor, mech); + } + + return (major); +} + +/* + * Naming extensions based local login authorization. + */ +static OM_uint32 +attr_authorize_localname(OM_uint32 *minor, + const gss_name_t name, + const gss_union_name_t unionUser) +{ + OM_uint32 major = GSS_S_UNAVAILABLE; /* attribute not present */ + gss_buffer_t externalName; + int more = -1; + + if (unionUser->name_type != GSS_C_NO_OID && + !g_OID_equal(unionUser->name_type, GSS_C_NT_USER_NAME)) + return (GSS_S_BAD_NAMETYPE); + + externalName = unionUser->external_name; + assert(externalName != GSS_C_NO_BUFFER); + + while (more != 0 && major != GSS_S_COMPLETE) { + OM_uint32 tmpMajor, tmpMinor; + gss_buffer_desc value; + gss_buffer_desc display_value; + int authenticated = 0, complete = 0; + + tmpMajor = gss_get_name_attribute(minor, + name, + GSS_C_ATTR_LOCAL_LOGIN_USER, + &authenticated, + &complete, + &value, + &display_value, + &more); + if (GSS_ERROR(tmpMajor)) { + major = tmpMajor; + break; + } + + if (authenticated && + value.length == externalName->length && + memcmp(value.value, externalName->value, externalName->length) == 0) + major = GSS_S_COMPLETE; + else + major = GSS_S_UNAUTHORIZED; + + gss_release_buffer(&tmpMinor, &value); + gss_release_buffer(&tmpMinor, &display_value); + } + + return (major); +} + +/* + * Equality based local login authorization. + */ +static OM_uint32 +compare_names_authorize_localname(OM_uint32 *minor, + const gss_union_name_t unionName, + const gss_name_t user) +{ + + OM_uint32 status, tmpMinor; + gss_name_t canonName; + int match = 0; + + status = gss_canonicalize_name(minor, + user, + unionName->mech_type, + &canonName); + if (status != GSS_S_COMPLETE) + return (status); + + status = gss_compare_name(minor, + (gss_name_t)unionName, + canonName, + &match); + if (status == GSS_S_COMPLETE && match == 0) + status = GSS_S_UNAUTHORIZED; + + (void) gss_release_name(&tmpMinor, &canonName); + + return (status); +} + +OM_uint32 KRB5_CALLCONV +gss_authorize_localname(OM_uint32 *minor, + const gss_name_t name, + const gss_name_t user) + +{ + OM_uint32 major; + gss_union_name_t unionName; + gss_union_name_t unionUser; + int mechAvailable = 0; + + if (minor == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (name == GSS_C_NO_NAME || user == GSS_C_NO_NAME) + return (GSS_S_CALL_INACCESSIBLE_READ); + + *minor = 0; + + unionName = (gss_union_name_t)name; + unionUser = (gss_union_name_t)user; + + if (unionUser->mech_type != GSS_C_NO_OID) + return (GSS_S_BAD_NAME); + + /* If mech returns yes, we return yes */ + major = mech_authorize_localname(minor, unionName, unionUser); + if (major == GSS_S_COMPLETE) + return (GSS_S_COMPLETE); + else if (major != GSS_S_UNAVAILABLE) + mechAvailable = 1; + + /* If attribute exists, we evaluate attribute */ + major = attr_authorize_localname(minor, unionName, unionUser); + if (major == GSS_S_COMPLETE || major == GSS_S_UNAUTHORIZED) + return (major); + + /* If mech did not implement SPI, compare the local name */ + if (mechAvailable == 0 && + unionName->mech_type != GSS_C_NO_OID) { + major = compare_names_authorize_localname(minor, + unionName, + unionUser); + } + + return (major); +} + +int KRB5_CALLCONV +gss_userok(const gss_name_t name, + const char *user) +{ + OM_uint32 major, minor; + gss_buffer_desc userBuf; + gss_name_t userName; + + userBuf.value = (void *)user; + userBuf.length = strlen(user); + + major = gss_import_name(&minor, &userBuf, GSS_C_NT_USER_NAME, &userName); + if (GSS_ERROR(major)) + return (0); + + major = gss_authorize_localname(&minor, name, userName); + + (void) gss_release_name(&minor, &userName); + + return (major == GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/mechglue/g_buffer_set.c b/src/lib/gssapi/mechglue/g_buffer_set.c new file mode 100644 index 000000000000..9310ddb4b728 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_buffer_set.c @@ -0,0 +1,55 @@ +/* + * Copyright 2008 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 "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <errno.h> + +OM_uint32 KRB5_CALLCONV gss_create_empty_buffer_set + (OM_uint32 * minor_status, + gss_buffer_set_t *buffer_set) +{ + return generic_gss_create_empty_buffer_set(minor_status, buffer_set); +} + +OM_uint32 KRB5_CALLCONV gss_add_buffer_set_member + (OM_uint32 * minor_status, + const gss_buffer_t member_buffer, + gss_buffer_set_t *buffer_set) +{ + return generic_gss_add_buffer_set_member(minor_status, + member_buffer, + buffer_set); +} + +OM_uint32 KRB5_CALLCONV gss_release_buffer_set + (OM_uint32 * minor_status, + gss_buffer_set_t *buffer_set) +{ + return generic_gss_release_buffer_set(minor_status, buffer_set); +} diff --git a/src/lib/gssapi/mechglue/g_canon_name.c b/src/lib/gssapi/mechglue/g_canon_name.c new file mode 100644 index 000000000000..61f657f91f71 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_canon_name.c @@ -0,0 +1,176 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* #pragma ident "@(#)g_canon_name.c 1.15 04/02/23 SMI" */ + +/* + * routine gss_canonicalize_name + * + * This routine is used to produce a mechanism specific + * representation of name that has been previously + * imported with gss_import_name. The routine uses the mechanism + * specific implementation of gss_import_name to implement this + * function. + * + * We allow a NULL output_name, in which case we modify the + * input_name to include the mechanism specific name. + */ + +#include <mglueP.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <errno.h> + +static OM_uint32 +val_canon_name_args( + OM_uint32 *minor_status, + const gss_name_t input_name, + const gss_OID mech_type, + gss_name_t *output_name) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_name != NULL) + *output_name = GSS_C_NO_NAME; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (input_name == GSS_C_NO_NAME || mech_type == GSS_C_NULL_OID) + return (GSS_S_CALL_INACCESSIBLE_READ); + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_canonicalize_name(minor_status, + input_name, + mech_type, + output_name) +OM_uint32 *minor_status; +const gss_name_t input_name; +const gss_OID mech_type; +gss_name_t *output_name; +{ + gss_union_name_t in_union, out_union = NULL, dest_union = NULL; + OM_uint32 major_status = GSS_S_FAILURE, tmpmin; + gss_OID selected_mech; + + major_status = val_canon_name_args(minor_status, + input_name, + mech_type, + output_name); + if (major_status != GSS_S_COMPLETE) + return (major_status); + + major_status = gssint_select_mech_type(minor_status, mech_type, + &selected_mech); + if (major_status != GSS_S_COMPLETE) + return (major_status); + + /* Initial value needed below. */ + major_status = GSS_S_FAILURE; + + in_union = (gss_union_name_t)input_name; + /* + * If the caller wants to reuse the name, and the name has already + * been converted, then there is nothing for us to do. + */ + if (!output_name && in_union->mech_type && + g_OID_equal(in_union->mech_type, selected_mech)) + return (GSS_S_COMPLETE); + + /* ok, then we need to do something - start by creating data struct */ + if (output_name) { + out_union = + (gss_union_name_t)malloc(sizeof (gss_union_name_desc)); + if (!out_union) + goto allocation_failure; + + out_union->mech_type = 0; + out_union->mech_name = 0; + out_union->name_type = 0; + out_union->external_name = 0; + out_union->loopback = out_union; + + /* Allocate the buffer for the user specified representation */ + if (gssint_create_copy_buffer(in_union->external_name, + &out_union->external_name, 1)) + goto allocation_failure; + + if (in_union->name_type != GSS_C_NULL_OID) { + major_status = generic_gss_copy_oid(minor_status, + in_union->name_type, + &out_union->name_type); + if (major_status) { + map_errcode(minor_status); + goto allocation_failure; + } + } + + } + + /* + * might need to delete any old mechanism names if we are + * reusing the buffer. + */ + if (!output_name) { + if (in_union->mech_type) { + (void) gssint_release_internal_name(minor_status, + in_union->mech_type, + &in_union->mech_name); + (void) gss_release_oid(minor_status, + &in_union->mech_type); + in_union->mech_type = 0; + } + dest_union = in_union; + } else + dest_union = out_union; + + /* now let's create the new mech name */ + if ((major_status = generic_gss_copy_oid(minor_status, selected_mech, + &dest_union->mech_type))) { + map_errcode(minor_status); + goto allocation_failure; + } + + if ((major_status = + gssint_import_internal_name(minor_status, selected_mech, + in_union, + &dest_union->mech_name))) + goto allocation_failure; + + if (output_name) + *output_name = (gss_name_t)dest_union; + + return (GSS_S_COMPLETE); + +allocation_failure: + if (out_union) { + /* Release the partly constructed out_union. */ + gss_name_t name = (gss_name_t)out_union; + (void) gss_release_name(&tmpmin, &name); + } else if (!output_name) { + /* Release only the mech name fields in in_union. */ + if (in_union->mech_name) { + (void) gssint_release_internal_name(&tmpmin, + dest_union->mech_type, + &dest_union->mech_name); + } + if (in_union->mech_type) + (void) gss_release_oid(&tmpmin, &dest_union->mech_type); + } + + return (major_status); +} /********** gss_canonicalize_name ********/ diff --git a/src/lib/gssapi/mechglue/g_compare_name.c b/src/lib/gssapi/mechglue/g_compare_name.c new file mode 100644 index 000000000000..af2e76bbdabd --- /dev/null +++ b/src/lib/gssapi/mechglue/g_compare_name.c @@ -0,0 +1,210 @@ +/* #pragma ident "@(#)g_compare_name.c 1.16 04/02/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_compare_name + * + */ + +#include "mglueP.h" +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> + +static OM_uint32 +val_comp_name_args( + OM_uint32 *minor_status, + gss_name_t name1, + gss_name_t name2, + int *name_equal) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + /* Validate arguments. */ + + if (name1 == GSS_C_NO_NAME || name2 == GSS_C_NO_NAME) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME); + + if (name_equal == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_compare_name (minor_status, + name1, + name2, + name_equal) + +OM_uint32 * minor_status; +gss_name_t name1; +gss_name_t name2; +int * name_equal; + +{ + OM_uint32 major_status, temp_minor; + gss_union_name_t union_name1, union_name2; + gss_mechanism mech = NULL; + gss_name_t internal_name; + + major_status = val_comp_name_args(minor_status, + name1, name2, name_equal); + if (major_status != GSS_S_COMPLETE) + return (major_status); + + union_name1 = (gss_union_name_t) name1; + union_name2 = (gss_union_name_t) name2; + /* + * Try our hardest to make union_name1 be the mechanism-specific + * name. (Of course we can't if both names aren't + * mechanism-specific.) + */ + if (union_name1->mech_type == 0) { + union_name1 = (gss_union_name_t) name2; + union_name2 = (gss_union_name_t) name1; + } + /* + * If union_name1 is mechanism specific, then fetch its mechanism + * information. + */ + if (union_name1->mech_type) { + mech = gssint_get_mechanism (union_name1->mech_type); + if (!mech) + return (GSS_S_BAD_MECH); + if (!mech->gss_compare_name) + return (GSS_S_UNAVAILABLE); + } + + *name_equal = 0; /* Default to *not* equal.... */ + + /* + * First case... both names are mechanism-specific + */ + if (union_name1->mech_type && union_name2->mech_type) { + if (!g_OID_equal(union_name1->mech_type, union_name2->mech_type)) + return (GSS_S_COMPLETE); + if ((union_name1->mech_name == 0) || (union_name2->mech_name == 0)) + /* should never happen */ + return (GSS_S_BAD_NAME); + if (!mech) + return (GSS_S_BAD_MECH); + if (!mech->gss_compare_name) + return (GSS_S_UNAVAILABLE); + major_status = mech->gss_compare_name(minor_status, + union_name1->mech_name, + union_name2->mech_name, + name_equal); + if (major_status != GSS_S_COMPLETE) + map_error(minor_status, mech); + return major_status; + } + + /* + * Second case... both names are NOT mechanism specific. + * + * All we do here is make sure the two name_types are equal and then + * that the external_names are equal. Note the we do not take care + * of the case where two different external names map to the same + * internal name. We cannot determine this, since we as yet do not + * know what mechanism to use for calling the underlying + * gss_import_name(). + */ + if (!union_name1->mech_type && !union_name2->mech_type) { + /* + * Second case, first sub-case... one name has null + * name_type, the other doesn't. + * + * Not knowing a mech_type we can't import the name with + * null name_type so we can't compare. + */ + if ((union_name1->name_type == GSS_C_NULL_OID && + union_name2->name_type != GSS_C_NULL_OID) || + (union_name1->name_type != GSS_C_NULL_OID && + union_name2->name_type == GSS_C_NULL_OID)) + return (GSS_S_COMPLETE); + /* + * Second case, second sub-case... both names have + * name_types, but they are different. + */ + if ((union_name1->name_type != GSS_C_NULL_OID && + union_name2->name_type != GSS_C_NULL_OID) && + !g_OID_equal(union_name1->name_type, + union_name2->name_type)) + return (GSS_S_COMPLETE); + /* + * Second case, third sub-case... both names have equal + * name_types (and both have no mech_types) so we just + * compare the external_names. + */ + if ((union_name1->external_name->length != + union_name2->external_name->length) || + (memcmp(union_name1->external_name->value, + union_name2->external_name->value, + union_name1->external_name->length) != 0)) + return (GSS_S_COMPLETE); + *name_equal = 1; + return (GSS_S_COMPLETE); + } + + /* + * Final case... one name is mechanism specific, the other isn't. + * + * We attempt to convert the general name to the mechanism type of + * the mechanism-specific name, and then do the compare. If we + * can't import the general name, then we return that the name is + * _NOT_ equal. + */ + if (union_name2->mech_type) { + /* We make union_name1 the mechanism specific name. */ + union_name1 = (gss_union_name_t) name2; + union_name2 = (gss_union_name_t) name1; + } + major_status = gssint_import_internal_name(minor_status, + union_name1->mech_type, + union_name2, + &internal_name); + if (major_status != GSS_S_COMPLETE) + return (GSS_S_COMPLETE); /* return complete, but not equal */ + + if (!mech) + return (GSS_S_BAD_MECH); + if (!mech->gss_compare_name) + return (GSS_S_UNAVAILABLE); + major_status = mech->gss_compare_name(minor_status, + union_name1->mech_name, + internal_name, name_equal); + if (major_status != GSS_S_COMPLETE) + map_error(minor_status, mech); + gssint_release_internal_name(&temp_minor, union_name1->mech_type, + &internal_name); + return (major_status); + +} diff --git a/src/lib/gssapi/mechglue/g_complete_auth_token.c b/src/lib/gssapi/mechglue/g_complete_auth_token.c new file mode 100644 index 000000000000..91815513017f --- /dev/null +++ b/src/lib/gssapi/mechglue/g_complete_auth_token.c @@ -0,0 +1,70 @@ +/* #ident "@(#)gss_seal.c 1.10 95/08/07 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_complete_auth_token + */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <errno.h> + +OM_uint32 KRB5_CALLCONV +gss_complete_auth_token (OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + gss_buffer_t input_message_buffer) +{ + OM_uint32 status; + gss_union_ctx_id_t ctx; + gss_mechanism mech; + + if (context_handle == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + ctx = (gss_union_ctx_id_t) context_handle; + mech = gssint_get_mechanism (ctx->mech_type); + + if (mech != NULL) { + if (mech->gss_complete_auth_token != NULL) { + status = mech->gss_complete_auth_token(minor_status, + ctx->internal_ctx_id, + input_message_buffer); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } else + status = GSS_S_COMPLETE; + } else + status = GSS_S_BAD_MECH; + + return status; +} diff --git a/src/lib/gssapi/mechglue/g_context_time.c b/src/lib/gssapi/mechglue/g_context_time.c new file mode 100644 index 000000000000..2ff8d0996ef0 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_context_time.c @@ -0,0 +1,79 @@ +/* #pragma ident "@(#)g_context_time.c 1.12 98/01/22 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routines for gss_context_time + */ + +#include "mglueP.h" + +OM_uint32 KRB5_CALLCONV +gss_context_time (minor_status, + context_handle, + time_rec) + +OM_uint32 * minor_status; +gss_ctx_id_t context_handle; +OM_uint32 * time_rec; + +{ + OM_uint32 status; + gss_union_ctx_id_t ctx; + gss_mechanism mech; + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + *minor_status = 0; + + if (time_rec == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (context_handle == GSS_C_NO_CONTEXT) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT); + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + ctx = (gss_union_ctx_id_t) context_handle; + mech = gssint_get_mechanism (ctx->mech_type); + + if (mech) { + + if (mech->gss_context_time) { + status = mech->gss_context_time( + minor_status, + ctx->internal_ctx_id, + time_rec); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } else + status = GSS_S_UNAVAILABLE; + + return(status); + } + + return (GSS_S_BAD_MECH); +} diff --git a/src/lib/gssapi/mechglue/g_decapsulate_token.c b/src/lib/gssapi/mechglue/g_decapsulate_token.c new file mode 100644 index 000000000000..934d2607cc6c --- /dev/null +++ b/src/lib/gssapi/mechglue/g_decapsulate_token.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2011, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "mglueP.h" + +OM_uint32 KRB5_CALLCONV +gss_decapsulate_token(gss_const_buffer_t input_token, + gss_const_OID token_oid, + gss_buffer_t output_token) +{ + OM_uint32 minor; + unsigned int body_size = 0; + unsigned char *buf_in; + + if (input_token == GSS_C_NO_BUFFER || token_oid == GSS_C_NO_OID) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (output_token == GSS_C_NO_BUFFER) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + buf_in = input_token->value; + + minor = g_verify_token_header(token_oid, &body_size, &buf_in, + -1, input_token->length, + G_VFY_TOKEN_HDR_WRAPPER_REQUIRED); + if (minor != 0) + return GSS_S_DEFECTIVE_TOKEN; + + output_token->value = malloc(body_size); + if (output_token->value == NULL) + return GSS_S_FAILURE; + + memcpy(output_token->value, buf_in, body_size); + output_token->length = body_size; + + return GSS_S_COMPLETE; +} diff --git a/src/lib/gssapi/mechglue/g_del_name_attr.c b/src/lib/gssapi/mechglue/g_del_name_attr.c new file mode 100644 index 000000000000..e81e3315ade4 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_del_name_attr.c @@ -0,0 +1,66 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* Glue routine for gss_delete_name_attribute */ + +#include "mglueP.h" + +OM_uint32 KRB5_CALLCONV +gss_delete_name_attribute(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t attr) +{ + OM_uint32 status; + gss_union_name_t union_name; + gss_mechanism mech; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (name == GSS_C_NO_NAME) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + + *minor_status = 0; + + union_name = (gss_union_name_t)name; + + if (union_name->mech_type == GSS_C_NO_OID) + return GSS_S_UNAVAILABLE; + + mech = gssint_get_mechanism(name->mech_type); + if (mech == NULL) + return GSS_S_BAD_NAME; + + if (mech->gss_delete_name_attribute == NULL) + return GSS_S_UNAVAILABLE; + + status = (*mech->gss_delete_name_attribute)(minor_status, + union_name->mech_name, + attr); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + + return status; +} diff --git a/src/lib/gssapi/mechglue/g_delete_sec_context.c b/src/lib/gssapi/mechglue/g_delete_sec_context.c new file mode 100644 index 000000000000..4bf0dec5ce33 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_delete_sec_context.c @@ -0,0 +1,104 @@ +/* #pragma ident "@(#)g_delete_sec_context.c 1.11 97/11/09 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_delete_sec_context + */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +static OM_uint32 +val_del_sec_ctx_args( + OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_token != GSS_C_NO_BUFFER) { + output_token->length = 0; + output_token->value = NULL; + } + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (context_handle == NULL || *context_handle == GSS_C_NO_CONTEXT) + return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CONTEXT); + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_delete_sec_context (minor_status, + context_handle, + output_token) + +OM_uint32 * minor_status; +gss_ctx_id_t * context_handle; +gss_buffer_t output_token; + +{ + OM_uint32 status; + gss_union_ctx_id_t ctx; + + status = val_del_sec_ctx_args(minor_status, context_handle, output_token); + if (status != GSS_S_COMPLETE) + return (status); + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + ctx = (gss_union_ctx_id_t) *context_handle; + if (GSSINT_CHK_LOOP(ctx)) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT); + + status = gssint_delete_internal_sec_context(minor_status, + ctx->mech_type, + &ctx->internal_ctx_id, + output_token); + if (status) + return status; + + /* now free up the space for the union context structure */ + free(ctx->mech_type->elements); + free(ctx->mech_type); + free(*context_handle); + *context_handle = GSS_C_NO_CONTEXT; + + return (GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/mechglue/g_dsp_name.c b/src/lib/gssapi/mechglue/g_dsp_name.c new file mode 100644 index 000000000000..21867c814ec5 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_dsp_name.c @@ -0,0 +1,118 @@ +/* #pragma ident "@(#)g_dsp_name.c 1.13 04/02/23 SMI" */ +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_display_name() + * + */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> + +static OM_uint32 +val_dsp_name_args( + OM_uint32 *minor_status, + gss_name_t input_name, + gss_buffer_t output_name_buffer, + gss_OID *output_name_type) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_name_buffer != GSS_C_NO_BUFFER) { + output_name_buffer->length = 0; + output_name_buffer->value = NULL; + } + + if (output_name_type != NULL) + *output_name_type = GSS_C_NO_OID; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (output_name_buffer == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (input_name == GSS_C_NO_NAME) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME); + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_display_name (minor_status, + input_name, + output_name_buffer, + output_name_type) + +OM_uint32 * minor_status; +gss_name_t input_name; +gss_buffer_t output_name_buffer; +gss_OID * output_name_type; + +{ + OM_uint32 major_status; + gss_union_name_t union_name; + + major_status = val_dsp_name_args(minor_status, input_name, + output_name_buffer, output_name_type); + if (major_status != GSS_S_COMPLETE) + return (major_status); + + union_name = (gss_union_name_t) input_name; + + if (union_name->mech_type) { + /* + * OK, we have a mechanism-specific name; let's use it! + */ + return (gssint_display_internal_name(minor_status, + union_name->mech_type, + union_name->mech_name, + output_name_buffer, + output_name_type)); + } + + if ((output_name_buffer->value = + gssalloc_malloc(union_name->external_name->length + 1)) == NULL) + return (GSS_S_FAILURE); + output_name_buffer->length = union_name->external_name->length; + (void) memcpy(output_name_buffer->value, + union_name->external_name->value, + union_name->external_name->length); + ((char *)output_name_buffer->value)[output_name_buffer->length] = '\0'; + + if (output_name_type != NULL) + *output_name_type = union_name->name_type; + + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/mechglue/g_dsp_name_ext.c b/src/lib/gssapi/mechglue/g_dsp_name_ext.c new file mode 100644 index 000000000000..be08dd16c4a3 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_dsp_name_ext.c @@ -0,0 +1,133 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_display_name_ext() + * + */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> + +static OM_uint32 +val_dsp_name_ext_args( + OM_uint32 *minor_status, + gss_name_t input_name, + gss_OID display_as_name_type, + gss_buffer_t output_name_buffer) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_name_buffer != GSS_C_NO_BUFFER) { + output_name_buffer->length = 0; + output_name_buffer->value = NULL; + } + + /* Validate arguments. */ + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (output_name_buffer == GSS_C_NO_BUFFER) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (input_name == GSS_C_NO_NAME) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + + if (display_as_name_type == GSS_C_NO_OID) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAMETYPE; + + return GSS_S_COMPLETE; +} + + +OM_uint32 KRB5_CALLCONV +gss_display_name_ext (OM_uint32 *minor_status, + gss_name_t input_name, + gss_OID display_as_name_type, + gss_buffer_t output_name_buffer) +{ + OM_uint32 status; + gss_union_name_t union_name; + gss_mechanism mech; + + status = val_dsp_name_ext_args(minor_status, + input_name, + display_as_name_type, + output_name_buffer); + if (status != GSS_S_COMPLETE) + return status; + + union_name = (gss_union_name_t) input_name; + + if (union_name->mech_type) { + mech = gssint_get_mechanism(union_name->mech_type); + if (mech == NULL) + status = GSS_S_BAD_NAME; + else if (mech->gss_display_name_ext == NULL) { + if (mech->gss_display_name != NULL && + union_name->name_type != GSS_C_NO_OID && + g_OID_equal(display_as_name_type, union_name->name_type)) { + status = (*mech->gss_display_name)(minor_status, + union_name->mech_name, + output_name_buffer, + NULL); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } else + status = GSS_S_UNAVAILABLE; + } else { + status = (*mech->gss_display_name_ext)(minor_status, + union_name->mech_name, + display_as_name_type, + output_name_buffer); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } + return status; + } + + if (union_name->name_type == GSS_C_NO_OID || + !g_OID_equal(display_as_name_type, union_name->name_type)) + return GSS_S_UNAVAILABLE; + + if ((output_name_buffer->value = + malloc(union_name->external_name->length + 1)) == NULL) { + return GSS_S_FAILURE; + } + output_name_buffer->length = union_name->external_name->length; + (void) memcpy(output_name_buffer->value, + union_name->external_name->value, + union_name->external_name->length); + ((char *)output_name_buffer->value)[output_name_buffer->length] = '\0'; + + return GSS_S_COMPLETE; +} diff --git a/src/lib/gssapi/mechglue/g_dsp_status.c b/src/lib/gssapi/mechglue/g_dsp_status.c new file mode 100644 index 000000000000..70e8492636ad --- /dev/null +++ b/src/lib/gssapi/mechglue/g_dsp_status.c @@ -0,0 +1,363 @@ +/* #pragma ident "@(#)g_dsp_status.c 1.17 04/02/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine gss_display_status + * + */ + +#include "mglueP.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* local function */ +static OM_uint32 displayMajor(OM_uint32, OM_uint32 *, gss_buffer_t); + +OM_uint32 KRB5_CALLCONV +gss_display_status (minor_status, + status_value, + status_type, + req_mech_type, + message_context, + status_string) + +OM_uint32 * minor_status; +OM_uint32 status_value; +int status_type; +gss_OID req_mech_type; +OM_uint32 * message_context; +gss_buffer_t status_string; + +{ + gss_OID mech_type = (gss_OID) req_mech_type; + gss_mechanism mech; + gss_OID_desc m_oid = { 0, 0 }; + + if (minor_status != NULL) + *minor_status = 0; + + if (status_string != GSS_C_NO_BUFFER) { + status_string->length = 0; + status_string->value = NULL; + } + + if (minor_status == NULL || + message_context == NULL || + status_string == GSS_C_NO_BUFFER) + + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + /* we handle major status codes, and the mechs do the minor */ + if (status_type == GSS_C_GSS_CODE) + return (displayMajor(status_value, message_context, + status_string)); + + /* + * must be the minor status - let mechs do the work + * select the appropriate underlying mechanism routine and + * call it. + */ + + /* In this version, we only handle status codes that have been + mapped to a flat numbering space. Look up the value we got + passed. If it's not found, complain. */ + if (status_value == 0) { + status_string->value = gssalloc_strdup("Unknown error"); + if (status_string->value == NULL) { + *minor_status = ENOMEM; + map_errcode(minor_status); + return GSS_S_FAILURE; + } + status_string->length = strlen(status_string->value); + *message_context = 0; + *minor_status = 0; + return GSS_S_COMPLETE; + } + { + int err; + OM_uint32 m_status = 0, status; + + err = gssint_mecherrmap_get(status_value, &m_oid, &m_status); + if (err) { + *minor_status = err; + map_errcode(minor_status); + return GSS_S_BAD_STATUS; + } + if (m_oid.length == 0) { + /* Magic flag for com_err values. */ + status = g_display_com_err_status(minor_status, m_status, status_string); + if (status != GSS_S_COMPLETE) + map_errcode(minor_status); + return status; + } + mech_type = &m_oid; + status_value = m_status; + } + + mech = gssint_get_mechanism (mech_type); + + if (mech && mech->gss_display_status) { + OM_uint32 r; + + r = mech->gss_display_status(minor_status, + status_value, status_type, mech_type, + message_context, status_string); + /* How's this for weird? If we get an error returning the + mechanism-specific error code, we save away the + mechanism-specific error code describing the error. */ + if (r != GSS_S_COMPLETE) + map_error(minor_status, mech); + return r; + } + + if (!mech) + return (GSS_S_BAD_MECH); + + return (GSS_S_UNAVAILABLE); +} + +/* + * function to map the major error codes + * it uses case statements so that the strings could be wrapped by gettext + * msgCtxt is interpreted as: + * 0 - first call + * 1 - routine error + * >= 2 - the supplementary error code bit shifted by 1 + */ +static OM_uint32 +displayMajor(status, msgCtxt, outStr) +OM_uint32 status; +OM_uint32 *msgCtxt; +gss_buffer_t outStr; +{ + OM_uint32 oneVal, mask = 0x1, currErr; + char *errStr = NULL; + int i, haveErr = 0; + + /* take care of the success value first */ + if (status == GSS_S_COMPLETE) + errStr = _("The routine completed successfully"); + else if (*msgCtxt == 0 && (oneVal = GSS_CALLING_ERROR(status))) { + switch (oneVal) { + case GSS_S_CALL_INACCESSIBLE_READ: + errStr = _("A required input parameter could not be " + "read"); + break; + + case GSS_S_CALL_INACCESSIBLE_WRITE: + errStr = _("A required output parameter could not be " + "written"); + break; + + case GSS_S_CALL_BAD_STRUCTURE: + errStr = _("A parameter was malformed"); + break; + + default: + errStr = _("An invalid status code was supplied"); + break; + } + + /* we now need to determine new value of msgCtxt */ + if (GSS_ROUTINE_ERROR(status)) + *msgCtxt = 1; + else if ((oneVal = GSS_SUPPLEMENTARY_INFO(status)) != 0) + *msgCtxt = (OM_uint32)(oneVal << 1); + else + *msgCtxt = 0; + + } else if ((*msgCtxt == 0 || *msgCtxt == 1) && + (oneVal = GSS_ROUTINE_ERROR(status))) { + switch (oneVal) { + case GSS_S_BAD_MECH: + errStr = _("An unsupported mechanism was requested"); + break; + + case GSS_S_BAD_NAME: + errStr = _("An invalid name was supplied"); + break; + + case GSS_S_BAD_NAMETYPE: + errStr = _("A supplied name was of an unsupported " + "type"); + break; + + case GSS_S_BAD_BINDINGS: + errStr = _("Incorrect channel bindings were supplied"); + break; + + case GSS_S_BAD_SIG: /* same as GSS_S_BAD_MIC: */ + errStr = _("A token had an invalid Message Integrity " + "Check (MIC)"); + break; + + case GSS_S_NO_CRED: + errStr = _("No credentials were supplied, or the " + "credentials were unavailable or " + "inaccessible"); + break; + + case GSS_S_NO_CONTEXT: + errStr = _("No context has been established"); + break; + + case GSS_S_DEFECTIVE_TOKEN: + errStr = _("Invalid token was supplied"); + break; + + case GSS_S_DEFECTIVE_CREDENTIAL: + errStr = _("Invalid credential was supplied"); + break; + + case GSS_S_CREDENTIALS_EXPIRED: + errStr = _("The referenced credential has expired"); + break; + + case GSS_S_CONTEXT_EXPIRED: + errStr = _("The referenced context has expired"); + break; + + case GSS_S_FAILURE: + errStr = _("Unspecified GSS failure. Minor code " + "may provide more information"); + break; + + case GSS_S_BAD_QOP: + errStr = _("The quality-of-protection (QOP) " + "requested could not be provided"); + break; + + case GSS_S_UNAUTHORIZED: + errStr = _("The operation is forbidden by local " + "security policy"); + break; + + case GSS_S_UNAVAILABLE: + errStr = _("The operation or option is not " + "available or unsupported"); + break; + + case GSS_S_DUPLICATE_ELEMENT: + errStr = _("The requested credential element " + "already exists"); + break; + + case GSS_S_NAME_NOT_MN: + errStr = _("The provided name was not mechanism " + "specific (MN)"); + break; + + case GSS_S_BAD_STATUS: + default: + errStr = _("An invalid status code was supplied"); + } + + /* we must determine if the caller should call us again */ + if ((oneVal = GSS_SUPPLEMENTARY_INFO(status)) != 0) + *msgCtxt = (OM_uint32)(oneVal << 1); + else + *msgCtxt = 0; + + } else if ((*msgCtxt == 0 || *msgCtxt >= 2) && + (oneVal = GSS_SUPPLEMENTARY_INFO(status))) { + /* + * if msgCtxt is not 0, then it should encode + * the supplementary error code we should be printing + */ + if (*msgCtxt >= 2) + oneVal = (OM_uint32) (*msgCtxt) >> 1; + else + oneVal = GSS_SUPPLEMENTARY_INFO(status); + + /* we display the errors LSB first */ + for (i = 0; i < 16; i++) { + if (oneVal & mask) { + haveErr = 1; + break; + } + mask <<= 1; + } + + /* isolate the bit or if not found set to illegal value */ + if (haveErr) + currErr = oneVal & mask; + else + currErr = 1 << 17; /* illegal value */ + + switch (currErr) { + case GSS_S_CONTINUE_NEEDED: + errStr = _("The routine must be called again to " + "complete its function"); + break; + + case GSS_S_DUPLICATE_TOKEN: + errStr = _("The token was a duplicate of an earlier " + "token"); + break; + + case GSS_S_OLD_TOKEN: + errStr = _("The token's validity period has expired"); + break; + + case GSS_S_UNSEQ_TOKEN: + errStr = _("A later token has already been processed"); + break; + + case GSS_S_GAP_TOKEN: + errStr = _("An expected per-message token was not " + "received"); + break; + + default: + errStr = _("An invalid status code was supplied"); + } + + /* + * we must check if there is any other supplementary errors + * if found, then turn off current bit, and store next value + * in msgCtxt shifted by 1 bit + */ + if (!haveErr) + *msgCtxt = 0; + else if (GSS_SUPPLEMENTARY_INFO(oneVal) ^ mask) + *msgCtxt = (OM_uint32) + ((GSS_SUPPLEMENTARY_INFO(oneVal) ^ mask) << 1); + else + *msgCtxt = 0; + } + + if (errStr == NULL) + errStr = "An invalid status code was supplied"; + + /* now copy the status code and return to caller */ + outStr->length = strlen(errStr); + outStr->value = gssalloc_strdup(errStr); + if (outStr->value == NULL) { + outStr->length = 0; + return (GSS_S_FAILURE); + } + + return (GSS_S_COMPLETE); +} /* displayMajor */ diff --git a/src/lib/gssapi/mechglue/g_dup_name.c b/src/lib/gssapi/mechglue/g_dup_name.c new file mode 100644 index 000000000000..85306fcc1913 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_dup_name.c @@ -0,0 +1,144 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* #pragma ident "@(#)g_dup_name.c 1.14 04/02/23 SMI" */ + +/* + * routine gss_duplicate_name + * + * This routine does not rely on mechanism implementation of this + * name, but instead uses mechanism specific gss_import_name routine. + */ + +#include <mglueP.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <errno.h> + +static OM_uint32 +val_dup_name_args( + OM_uint32 *minor_status, + const gss_name_t src_name, + gss_name_t *dest_name) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (dest_name != NULL) + *dest_name = GSS_C_NO_NAME; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + /* if output_name is NULL, simply return */ + if (dest_name == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (src_name == GSS_C_NO_NAME) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME); + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_duplicate_name(minor_status, + src_name, + dest_name) +OM_uint32 *minor_status; +const gss_name_t src_name; +gss_name_t *dest_name; +{ + gss_union_name_t src_union, dest_union; + OM_uint32 major_status = GSS_S_FAILURE; + + major_status = val_dup_name_args(minor_status, src_name, dest_name); + if (major_status != GSS_S_COMPLETE) + return (major_status); + + src_union = (gss_union_name_t)src_name; + + /* + * First create the union name struct that will hold the external + * name and the name type. + */ + dest_union = (gss_union_name_t)malloc(sizeof (gss_union_name_desc)); + if (!dest_union) + goto allocation_failure; + + dest_union->loopback = 0; + dest_union->mech_type = 0; + dest_union->mech_name = 0; + dest_union->name_type = 0; + dest_union->external_name = 0; + + /* Now copy the external representaion */ + if (gssint_create_copy_buffer(src_union->external_name, + &dest_union->external_name, 0)) + goto allocation_failure; + + if (src_union->name_type != GSS_C_NULL_OID) { + major_status = generic_gss_copy_oid(minor_status, + src_union->name_type, + &dest_union->name_type); + if (major_status != GSS_S_COMPLETE) { + map_errcode(minor_status); + goto allocation_failure; + } + } + + /* + * See if source name is mechanim specific, if so then need to import it + */ + if (src_union->mech_type) { + major_status = generic_gss_copy_oid(minor_status, + src_union->mech_type, + &dest_union->mech_type); + if (major_status != GSS_S_COMPLETE) { + map_errcode(minor_status); + goto allocation_failure; + } + + major_status = gssint_import_internal_name(minor_status, + src_union->mech_type, + src_union, + &dest_union->mech_name); + if (major_status != GSS_S_COMPLETE) + goto allocation_failure; + } + + + dest_union->loopback = dest_union; + *dest_name = (gss_name_t)dest_union; + return (GSS_S_COMPLETE); + +allocation_failure: + if (dest_union) { + if (dest_union->external_name) { + if (dest_union->external_name->value) + free(dest_union->external_name->value); + free(dest_union->external_name); + } + if (dest_union->name_type) + (void) generic_gss_release_oid(minor_status, + &dest_union->name_type); + if (dest_union->mech_name) + (void) gssint_release_internal_name(minor_status, + dest_union->mech_type, + &dest_union->mech_name); + if (dest_union->mech_type) + (void) generic_gss_release_oid(minor_status, + &dest_union->mech_type); + free(dest_union); + } + return (major_status); +} /* gss_duplicate_name */ diff --git a/src/lib/gssapi/mechglue/g_encapsulate_token.c b/src/lib/gssapi/mechglue/g_encapsulate_token.c new file mode 100644 index 000000000000..6ce0eeb0f5d0 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_encapsulate_token.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2011, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "mglueP.h" + +OM_uint32 KRB5_CALLCONV +gss_encapsulate_token(gss_const_buffer_t input_token, + gss_const_OID token_oid, + gss_buffer_t output_token) +{ + unsigned int tokenSize; + unsigned char *buf; + + if (input_token == GSS_C_NO_BUFFER || token_oid == GSS_C_NO_OID) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (output_token == GSS_C_NO_BUFFER) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + tokenSize = g_token_size(token_oid, input_token->length); + + assert(tokenSize > 2); + tokenSize -= 2; /* TOK_ID */ + + output_token->value = malloc(tokenSize); + if (output_token->value == NULL) + return GSS_S_FAILURE; + + buf = output_token->value; + + g_make_token_header(token_oid, input_token->length, &buf, -1); + memcpy(buf, input_token->value, input_token->length); + output_token->length = tokenSize; + + return GSS_S_COMPLETE; +} diff --git a/src/lib/gssapi/mechglue/g_exp_sec_context.c b/src/lib/gssapi/mechglue/g_exp_sec_context.c new file mode 100644 index 000000000000..b63745299f64 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_exp_sec_context.c @@ -0,0 +1,144 @@ +/* #pragma ident "@(#)g_exp_sec_context.c 1.14 04/02/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_export_sec_context + */ +#ifndef LEAN_CLIENT + +#include "mglueP.h" +#include <stdio.h> +#include <errno.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> + +static OM_uint32 +val_exp_sec_ctx_args( + OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t interprocess_token) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (interprocess_token != GSS_C_NO_BUFFER) { + interprocess_token->length = 0; + interprocess_token->value = NULL; + } + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (context_handle == NULL || *context_handle == GSS_C_NO_CONTEXT) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT); + + if (interprocess_token == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_export_sec_context(minor_status, + context_handle, + interprocess_token) + +OM_uint32 * minor_status; +gss_ctx_id_t * context_handle; +gss_buffer_t interprocess_token; + +{ + OM_uint32 status; + OM_uint32 length; + gss_union_ctx_id_t ctx = NULL; + gss_mechanism mech; + gss_buffer_desc token = GSS_C_EMPTY_BUFFER; + char *buf; + + status = val_exp_sec_ctx_args(minor_status, + context_handle, interprocess_token); + if (status != GSS_S_COMPLETE) + return (status); + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + ctx = (gss_union_ctx_id_t) *context_handle; + mech = gssint_get_mechanism (ctx->mech_type); + if (!mech) + return GSS_S_BAD_MECH; + if (!mech->gss_export_sec_context) + return (GSS_S_UNAVAILABLE); + + status = mech->gss_export_sec_context(minor_status, + &ctx->internal_ctx_id, &token); + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + goto cleanup; + } + + length = token.length + 4 + ctx->mech_type->length; + interprocess_token->length = length; + interprocess_token->value = malloc(length); + if (interprocess_token->value == 0) { + *minor_status = ENOMEM; + status = GSS_S_FAILURE; + goto cleanup; + } + buf = interprocess_token->value; + length = ctx->mech_type->length; + buf[3] = (unsigned char) (length & 0xFF); + length >>= 8; + buf[2] = (unsigned char) (length & 0xFF); + length >>= 8; + buf[1] = (unsigned char) (length & 0xFF); + length >>= 8; + buf[0] = (unsigned char) (length & 0xFF); + memcpy(buf+4, ctx->mech_type->elements, (size_t) ctx->mech_type->length); + memcpy(buf+4+ctx->mech_type->length, token.value, token.length); + + status = GSS_S_COMPLETE; + +cleanup: + (void) gss_release_buffer(minor_status, &token); + if (ctx != NULL && ctx->internal_ctx_id == GSS_C_NO_CONTEXT) { + /* If the mech deleted its context, delete the union context. */ + free(ctx->mech_type->elements); + free(ctx->mech_type); + free(ctx); + *context_handle = GSS_C_NO_CONTEXT; + } + return status; +} +#endif /*LEAN_CLIENT */ diff --git a/src/lib/gssapi/mechglue/g_export_cred.c b/src/lib/gssapi/mechglue/g_export_cred.c new file mode 100644 index 000000000000..8f5fe4ad5a2b --- /dev/null +++ b/src/lib/gssapi/mechglue/g_export_cred.c @@ -0,0 +1,114 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/mechglue/g_export_cred.c - gss_export_cred definition */ +/* + * Copyright (C) 2012 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "mglueP.h" + +static OM_uint32 +val_exp_cred_args(OM_uint32 *minor_status, gss_cred_id_t cred_handle, + gss_buffer_t token) +{ + + /* Initialize outputs. */ + if (minor_status != NULL) + *minor_status = 0; + if (token != GSS_C_NO_BUFFER) { + token->length = 0; + token->value = NULL; + } + + /* Validate arguments. */ + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + if (cred_handle == GSS_C_NO_CREDENTIAL) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED; + if (token == GSS_C_NO_BUFFER) + return GSS_S_CALL_INACCESSIBLE_WRITE; + return GSS_S_COMPLETE; +} + +OM_uint32 KRB5_CALLCONV +gss_export_cred(OM_uint32 * minor_status, gss_cred_id_t cred_handle, + gss_buffer_t token) +{ + OM_uint32 status, tmpmin; + gss_union_cred_t cred; + gss_OID mech_oid; + gss_OID public_oid; + gss_mechanism mech; + gss_buffer_desc mech_token; + struct k5buf buf; + char lenbuf[4]; + int i; + + status = val_exp_cred_args(minor_status, cred_handle, token); + if (status != GSS_S_COMPLETE) + return status; + + k5_buf_init_dynamic(&buf); + + cred = (gss_union_cred_t) cred_handle; + for (i = 0; i < cred->count; i++) { + /* Get an export token for this mechanism. */ + mech_oid = &cred->mechs_array[i]; + public_oid = gssint_get_public_oid(mech_oid); + mech = gssint_get_mechanism(mech_oid); + if (mech == NULL) { + status = GSS_S_DEFECTIVE_CREDENTIAL; + goto error; + } + if (mech->gss_export_cred == NULL) { + status = GSS_S_UNAVAILABLE; + goto error; + } + status = mech->gss_export_cred(minor_status, cred->cred_array[i], + &mech_token); + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + goto error; + } + + /* Append the mech OID and token to buf. */ + store_32_be(public_oid->length, lenbuf); + k5_buf_add_len(&buf, lenbuf, 4); + k5_buf_add_len(&buf, public_oid->elements, public_oid->length); + store_32_be(mech_token.length, lenbuf); + k5_buf_add_len(&buf, lenbuf, 4); + k5_buf_add_len(&buf, mech_token.value, mech_token.length); + gss_release_buffer(&tmpmin, &mech_token); + } + + return k5buf_to_gss(minor_status, &buf, token); + +error: + k5_buf_free(&buf); + return status; +} diff --git a/src/lib/gssapi/mechglue/g_export_name.c b/src/lib/gssapi/mechglue/g_export_name.c new file mode 100644 index 000000000000..c845f8caf70f --- /dev/null +++ b/src/lib/gssapi/mechglue/g_export_name.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1996,1997, by Sun Microsystems, Inc. + * All rights reserved. + */ + +/* #pragma ident "@(#)g_export_name.c 1.11 00/07/17 SMI" */ + +/* + * glue routine gss_export_name + * + * Will either call the mechanism defined gss_export_name, or if one is + * not defined will call a generic_gss_export_name routine. + */ + +#include <mglueP.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <errno.h> + +OM_uint32 KRB5_CALLCONV +gss_export_name(minor_status, + input_name, + exported_name) +OM_uint32 * minor_status; +const gss_name_t input_name; +gss_buffer_t exported_name; +{ + gss_union_name_t union_name; + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (exported_name != GSS_C_NO_BUFFER) { + exported_name->value = NULL; + exported_name->length = 0; + } + + /* Validate arguments. */ + + if (minor_status == NULL || exported_name == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (input_name == GSS_C_NO_NAME) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME); + + union_name = (gss_union_name_t)input_name; + + /* the name must be in mechanism specific format */ + if (!union_name->mech_type) + return (GSS_S_NAME_NOT_MN); + + return gssint_export_internal_name(minor_status, union_name->mech_type, + union_name->mech_name, exported_name); +} diff --git a/src/lib/gssapi/mechglue/g_export_name_comp.c b/src/lib/gssapi/mechglue/g_export_name_comp.c new file mode 100644 index 000000000000..ab538a095d6a --- /dev/null +++ b/src/lib/gssapi/mechglue/g_export_name_comp.c @@ -0,0 +1,72 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + */ + +/* + * glue routine for gss_export_name_composite + */ + +#include "mglueP.h" + +OM_uint32 KRB5_CALLCONV +gss_export_name_composite(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t exp_composite_name) +{ + OM_uint32 status; + gss_union_name_t union_name; + gss_mechanism mech; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (name == GSS_C_NO_NAME) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + + if (exp_composite_name == GSS_C_NO_BUFFER) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor_status = 0; + + union_name = (gss_union_name_t)name; + + if (union_name->mech_type == GSS_C_NO_OID) + return GSS_S_UNAVAILABLE; + + mech = gssint_get_mechanism(name->mech_type); + if (mech == NULL) + return GSS_S_BAD_NAME; + + if (mech->gss_export_name_composite == NULL) + return GSS_S_UNAVAILABLE; + + status = (*mech->gss_export_name_composite)(minor_status, + union_name->mech_name, + exp_composite_name); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + + return status; +} diff --git a/src/lib/gssapi/mechglue/g_get_name_attr.c b/src/lib/gssapi/mechglue/g_get_name_attr.c new file mode 100644 index 000000000000..047d5d428fb0 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_get_name_attr.c @@ -0,0 +1,93 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* Glue routine for gss_get_name_attribute */ + +#include "mglueP.h" + +OM_uint32 KRB5_CALLCONV +gss_get_name_attribute(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) +{ + OM_uint32 status; + gss_union_name_t union_name; + gss_mechanism mech; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (name == GSS_C_NO_NAME) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + if (attr == GSS_C_NO_BUFFER) + return GSS_S_CALL_INACCESSIBLE_READ; + if (more == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (authenticated != NULL) + *authenticated = 0; + if (complete != NULL) + *complete = 0; + if (value != GSS_C_NO_BUFFER) { + value->value = NULL; + value->length = 0; + } + if (display_value != GSS_C_NO_BUFFER) { + display_value->value = NULL; + display_value->length = 0; + } + + *minor_status = 0; + + union_name = (gss_union_name_t)name; + + if (union_name->mech_type == GSS_C_NO_OID) + return GSS_S_UNAVAILABLE; + + mech = gssint_get_mechanism(name->mech_type); + if (mech == NULL) + return GSS_S_BAD_NAME; + + if (mech->gss_get_name_attribute == NULL) + return GSS_S_UNAVAILABLE; + + status = (*mech->gss_get_name_attribute)(minor_status, + union_name->mech_name, + attr, + authenticated, + complete, + value, + display_value, + more); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + + return status; +} diff --git a/src/lib/gssapi/mechglue/g_glue.c b/src/lib/gssapi/mechglue/g_glue.c new file mode 100644 index 000000000000..4aa3591a0d06 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_glue.c @@ -0,0 +1,752 @@ +/* #pragma ident "@(#)g_glue.c 1.25 04/02/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <errno.h> + +#define MSO_BIT (8*(sizeof (int) - 1)) /* Most significant octet bit */ + +extern gss_mechanism *gssint_mechs_array; + +/* + * This file contains the support routines for the glue layer. + */ + +/* + * get_der_length: Givin a pointer to a buffer that contains a DER encoded + * length, decode the length updating the buffer to point to the character + * after the DER encoding. The parameter bytes will point to the number of + * bytes that made up the DER encoding of the length originally pointed to + * by the buffer. Note we return -1 on error. + */ +int +gssint_get_der_length(unsigned char **buf, unsigned int buf_len, unsigned int *bytes) +{ + /* p points to the beginning of the buffer */ + unsigned char *p = *buf; + int length, new_length; + unsigned int octets; + + if (buf_len < 1) + return (-1); + + /* We should have at least one byte */ + *bytes = 1; + + /* + * If the High order bit is not set then the length is just the value + * of *p. + */ + if (*p < 128) { + *buf = p+1; /* Advance the buffer */ + return (*p); /* return the length */ + } + + /* + * if the High order bit is set, then the low order bits represent + * the number of bytes that contain the DER encoding of the length. + */ + + octets = *p++ & 0x7f; + *bytes += octets; + + /* See if the supplied buffer contains enough bytes for the length. */ + if (octets > buf_len - 1) + return (-1); + + /* + * Calculate a multibyte length. The length is encoded as an + * unsigned integer base 256. + */ + for (length = 0; octets; octets--) { + new_length = (length << 8) + *p++; + if (new_length < length) /* overflow */ + return (-1); + length = new_length; + } + + *buf = p; /* Advance the buffer */ + + return (length); +} + +/* + * der_length_size: Return the number of bytes to encode a given length. + */ +unsigned int +gssint_der_length_size(unsigned int len) +{ + int i; + + if (len < 128) + return (1); + + for (i = 0; len; i++) { + len >>= 8; + } + + return (i+1); +} + +/* + * put_der_length: Encode the supplied length into the buffer pointed to + * by buf. max_length represents the maximum length of the buffer pointed + * to by buff. We will advance buf to point to the character after the newly + * DER encoded length. We return 0 on success or -l it the length cannot + * be encoded in max_len characters. + */ +int +gssint_put_der_length(unsigned int length, unsigned char **buf, unsigned int max_len) +{ + unsigned char *s, *p; + unsigned int buf_len = 0; + int i, first; + + /* Oops */ + if (buf == 0 || max_len < 1) + return (-1); + + s = *buf; + + /* Single byte is the length */ + if (length < 128) { + *s++ = length; + *buf = s; + return (0); + } + + /* First byte contains the number of octets */ + p = s + 1; + + /* Running total of the DER encoding length */ + buf_len = 0; + + /* + * Encode MSB first. We do the encoding by setting a shift + * factor to MSO_BIT (24 for 32 bit words) and then shifting the length + * by the factor. We then encode the resulting low order byte. + * We subtract 8 from the shift factor and repeat to ecnode the next + * byte. We stop when the shift factor is zero or we've run out of + * buffer to encode into. + */ + first = 0; + for (i = MSO_BIT; i >= 0 && buf_len <= max_len; i -= 8) { + unsigned int v; + v = (length >> i) & 0xff; + if ((v) || first) { + buf_len += 1; + *p++ = v; + first = 1; + } + } + if (i >= 0) /* buffer overflow */ + return (-1); + + /* + * We go back now and set the first byte to be the length with + * the high order bit set. + */ + *s = buf_len | 0x80; + *buf = p; + + return (0); +} + + +/* + * glue routine for get_mech_type + * + */ + +OM_uint32 gssint_get_mech_type_oid(OID, token) + gss_OID OID; + gss_buffer_t token; +{ + unsigned char * buffer_ptr; + int length; + + /* + * This routine reads the prefix of "token" in order to determine + * its mechanism type. It assumes the encoding suggested in + * Appendix B of RFC 1508. This format starts out as follows : + * + * tag for APPLICATION 0, Sequence[constructed, definite length] + * length of remainder of token + * tag of OBJECT IDENTIFIER + * length of mechanism OID + * encoding of mechanism OID + * <the rest of the token> + * + * Numerically, this looks like : + * + * 0x60 + * <length> - could be multiple bytes + * 0x06 + * <length> - assume only one byte, hence OID length < 127 + * <mech OID bytes> + * + * The routine fills in the OID value and returns an error as necessary. + */ + + if (OID == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if ((token == NULL) || (token->value == NULL)) + return (GSS_S_DEFECTIVE_TOKEN); + + /* Skip past the APP/Sequnce byte and the token length */ + + buffer_ptr = (unsigned char *) token->value; + + if (*(buffer_ptr++) != 0x60) + return (GSS_S_DEFECTIVE_TOKEN); + length = *buffer_ptr++; + + /* check if token length is null */ + if (length == 0) + return (GSS_S_DEFECTIVE_TOKEN); + + if (length & 0x80) { + if ((length & 0x7f) > 4) + return (GSS_S_DEFECTIVE_TOKEN); + buffer_ptr += length & 0x7f; + } + + if (*(buffer_ptr++) != 0x06) + return (GSS_S_DEFECTIVE_TOKEN); + + OID->length = (OM_uint32) *(buffer_ptr++); + OID->elements = (void *) buffer_ptr; + return (GSS_S_COMPLETE); +} + +/* + * The following mechanisms do not always identify themselves + * per the GSS-API specification, when interoperating with MS + * peers. We include the OIDs here so we do not have to ilnk + * with the mechanism. + */ +static gss_OID_desc gss_ntlm_mechanism_oid_desc = + {10, (void *)"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"}; +static gss_OID_desc gss_spnego_mechanism_oid_desc = + {6, (void *)"\x2b\x06\x01\x05\x05\x02"}; +static gss_OID_desc gss_krb5_mechanism_oid_desc = + {9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"}; + +#define NTLMSSP_SIGNATURE "NTLMSSP" + +OM_uint32 gssint_get_mech_type(OID, token) + gss_OID OID; + gss_buffer_t token; +{ + /* Check for interoperability exceptions */ + if (token->length >= sizeof(NTLMSSP_SIGNATURE) && + memcmp(token->value, NTLMSSP_SIGNATURE, + sizeof(NTLMSSP_SIGNATURE)) == 0) { + *OID = gss_ntlm_mechanism_oid_desc; + } else if (token->length != 0 && + ((char *)token->value)[0] == 0x6E) { + /* Could be a raw AP-REQ (check for APPLICATION tag) */ + *OID = gss_krb5_mechanism_oid_desc; + } else if (token->length == 0) { + *OID = gss_spnego_mechanism_oid_desc; + } else { + return gssint_get_mech_type_oid(OID, token); + } + + return (GSS_S_COMPLETE); +} + +static OM_uint32 +import_internal_attributes(OM_uint32 *minor, + gss_mechanism dmech, + gss_union_name_t sname, + gss_name_t dname) +{ + OM_uint32 major, tmpMinor; + gss_mechanism smech; + gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET; + size_t i; + + if (sname->mech_name == GSS_C_NO_NAME) + return (GSS_S_UNAVAILABLE); + + smech = gssint_get_mechanism (sname->mech_type); + if (smech == NULL) + return (GSS_S_BAD_MECH); + + if (smech->gss_inquire_name == NULL || + smech->gss_get_name_attribute == NULL) + return (GSS_S_UNAVAILABLE); + + if (dmech->gss_set_name_attribute == NULL) + return (GSS_S_UNAVAILABLE); + + major = smech->gss_inquire_name(minor, sname->mech_name, + NULL, NULL, &attrs); + if (GSS_ERROR(major) || attrs == GSS_C_NO_BUFFER_SET) { + gss_release_buffer_set(&tmpMinor, &attrs); + return (major); + } + + for (i = 0; i < attrs->count; i++) { + int more = -1; + + while (more != 0) { + gss_buffer_desc value, display_value; + int authenticated, complete; + + major = smech->gss_get_name_attribute(minor, sname->mech_name, + &attrs->elements[i], + &authenticated, &complete, + &value, &display_value, + &more); + if (GSS_ERROR(major)) + continue; + + if (authenticated) { + dmech->gss_set_name_attribute(minor, dname, complete, + &attrs->elements[i], &value); + } + + gss_release_buffer(&tmpMinor, &value); + gss_release_buffer(&tmpMinor, &display_value); + } + } + + gss_release_buffer_set(&tmpMinor, &attrs); + + return (GSS_S_COMPLETE); +} + +/* + * Internal routines to get and release an internal mechanism name + */ + +OM_uint32 gssint_import_internal_name (minor_status, mech_type, union_name, + internal_name) +OM_uint32 *minor_status; +gss_OID mech_type; +gss_union_name_t union_name; +gss_name_t *internal_name; +{ + OM_uint32 status, tmpMinor; + gss_mechanism mech; + gss_OID public_mech; + + mech = gssint_get_mechanism (mech_type); + if (mech == NULL) + return (GSS_S_BAD_MECH); + + /* + * If we are importing a name for the same mechanism, and the + * mechanism implements gss_duplicate_name, then use that. + */ + if (union_name->mech_type != GSS_C_NO_OID && + union_name->mech_name != GSS_C_NO_NAME && + g_OID_equal(union_name->mech_type, mech_type) && + mech->gss_duplicate_name != NULL) { + status = mech->gss_duplicate_name(minor_status, + union_name->mech_name, + internal_name); + if (status != GSS_S_UNAVAILABLE) { + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + return (status); + } + } + + if (mech->gssspi_import_name_by_mech) { + public_mech = gssint_get_public_oid(mech_type); + status = mech->gssspi_import_name_by_mech(minor_status, public_mech, + union_name->external_name, + union_name->name_type, + internal_name); + } else if (mech->gss_import_name) { + status = mech->gss_import_name(minor_status, union_name->external_name, + union_name->name_type, internal_name); + } else { + return (GSS_S_UNAVAILABLE); + } + + if (status == GSS_S_COMPLETE) { + /* Attempt to round-trip attributes */ + (void) import_internal_attributes(&tmpMinor, mech, + union_name, *internal_name); + } else { + map_error(minor_status, mech); + } + + return (status); +} + +OM_uint32 gssint_export_internal_name(minor_status, mech_type, + internal_name, name_buf) + OM_uint32 *minor_status; + const gss_OID mech_type; + const gss_name_t internal_name; + gss_buffer_t name_buf; +{ + OM_uint32 status; + gss_mechanism mech; + gss_buffer_desc dispName; + gss_OID nameOid; + unsigned char *buf = NULL; + const unsigned char tokId[] = "\x04\x01"; + const unsigned int tokIdLen = 2; + const int mechOidLenLen = 2, mechOidTagLen = 1, nameLenLen = 4; + int mechOidDERLen = 0; + int mechOidLen = 0; + + mech = gssint_get_mechanism(mech_type); + if (!mech) + return (GSS_S_BAD_MECH); + + if (mech->gss_export_name) { + status = mech->gss_export_name(minor_status, + internal_name, + name_buf); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + return status; + } + + /* + * if we are here it is because the mechanism does not provide + * a gss_export_name so we will use our implementation. We + * do required that the mechanism define a gss_display_name. + */ + if (!mech->gss_display_name) + return (GSS_S_UNAVAILABLE); + + /* + * NOTE: RFC2743 (section 3.2) governs the format of the outer + * wrapper of exported names; the mechanisms' specs govern + * the format of the inner portion of the exported name + * and, for some (e.g., RFC1964, the Kerberos V mech), a + * generic default as implemented here will do. + * + * The outer wrapper of an exported MN is: 2-octet tok Id + * (0x0401) + 2-octet network-byte order mech OID length + mech + * oid (in DER format, including DER tag and DER length) + + * 4-octet network-byte order length of inner portion + inner + * portion. + * + * For the Kerberos V mechanism the inner portion of an exported + * MN is the display name string and ignores the name type OID + * altogether. And we hope this will be so for any future + * mechanisms also, so that factoring name export/import out of + * the mech and into libgss pays off. + */ + if ((status = mech->gss_display_name(minor_status, + internal_name, + &dispName, + &nameOid)) + != GSS_S_COMPLETE) { + map_error(minor_status, mech); + return (status); + } + + /* determine the size of the buffer needed */ + mechOidDERLen = gssint_der_length_size(mech_type->length); + name_buf->length = tokIdLen + mechOidLenLen + + mechOidTagLen + mechOidDERLen + + mech_type->length + + nameLenLen + dispName.length; + if ((name_buf->value = (void*)gssalloc_malloc(name_buf->length)) == + (void*)NULL) { + name_buf->length = 0; + (void) gss_release_buffer(&status, &dispName); + return (GSS_S_FAILURE); + } + + /* now create the name ..... */ + buf = (unsigned char *)name_buf->value; + (void) memset(name_buf->value, 0, name_buf->length); + (void) memcpy(buf, tokId, tokIdLen); + buf += tokIdLen; + + /* spec allows only 2 bytes for the mech oid length */ + mechOidLen = mechOidDERLen + mechOidTagLen + mech_type->length; + store_16_be(mechOidLen, buf); + buf += 2; + + /* + * DER Encoding of mech OID contains OID Tag (0x06), length and + * mech OID value + */ + *buf++ = 0x06; + if (gssint_put_der_length(mech_type->length, &buf, + (name_buf->length - tokIdLen -2)) != 0) { + name_buf->length = 0; + free(name_buf->value); + (void) gss_release_buffer(&status, &dispName); + return (GSS_S_FAILURE); + } + + (void) memcpy(buf, mech_type->elements, mech_type->length); + buf += mech_type->length; + + /* spec designates the next 4 bytes for the name length */ + store_32_be(dispName.length, buf); + buf += 4; + + /* for the final ingredient - add the name from gss_display_name */ + (void) memcpy(buf, dispName.value, dispName.length); + + /* release the buffer obtained from gss_display_name */ + (void) gss_release_buffer(minor_status, &dispName); + return (GSS_S_COMPLETE); +} /* gssint_export_internal_name */ + +OM_uint32 gssint_display_internal_name (minor_status, mech_type, internal_name, + external_name, name_type) +OM_uint32 *minor_status; +gss_OID mech_type; +gss_name_t internal_name; +gss_buffer_t external_name; +gss_OID *name_type; +{ + OM_uint32 status; + gss_mechanism mech; + + mech = gssint_get_mechanism (mech_type); + if (mech) { + if (mech->gss_display_name) { + status = mech->gss_display_name ( + minor_status, + internal_name, + external_name, + name_type); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } else + status = GSS_S_UNAVAILABLE; + + return (status); + } + + return (GSS_S_BAD_MECH); +} + +OM_uint32 gssint_release_internal_name (minor_status, mech_type, internal_name) +OM_uint32 *minor_status; +gss_OID mech_type; +gss_name_t *internal_name; +{ + OM_uint32 status; + gss_mechanism mech; + + mech = gssint_get_mechanism (mech_type); + if (mech) { + if (mech->gss_release_name) { + status = mech->gss_release_name ( + minor_status, + internal_name); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } else + status = GSS_S_UNAVAILABLE; + + return (status); + } + + return (GSS_S_BAD_MECH); +} + +OM_uint32 gssint_delete_internal_sec_context (minor_status, + mech_type, + internal_ctx, + output_token) +OM_uint32 *minor_status; +gss_OID mech_type; +gss_ctx_id_t *internal_ctx; +gss_buffer_t output_token; +{ + OM_uint32 status; + gss_mechanism mech; + + mech = gssint_get_mechanism (mech_type); + if (mech) { + if (mech->gss_delete_sec_context) + status = mech->gss_delete_sec_context (minor_status, + internal_ctx, + output_token); + else + status = GSS_S_UNAVAILABLE; + + return (status); + } + + return (GSS_S_BAD_MECH); +} + +/* + * This function converts an internal gssapi name to a union gssapi + * name. Note that internal_name should be considered "consumed" by + * this call, whether or not we return an error. + */ +OM_uint32 gssint_convert_name_to_union_name(minor_status, mech, + internal_name, external_name) + OM_uint32 *minor_status; + gss_mechanism mech; + gss_name_t internal_name; + gss_name_t *external_name; +{ + OM_uint32 major_status,tmp; + gss_union_name_t union_name; + + union_name = (gss_union_name_t) malloc (sizeof(gss_union_name_desc)); + if (!union_name) { + major_status = GSS_S_FAILURE; + *minor_status = ENOMEM; + map_errcode(minor_status); + goto allocation_failure; + } + union_name->mech_type = 0; + union_name->mech_name = internal_name; + union_name->name_type = 0; + union_name->external_name = 0; + + major_status = generic_gss_copy_oid(minor_status, &mech->mech_type, + &union_name->mech_type); + if (major_status != GSS_S_COMPLETE) { + map_errcode(minor_status); + goto allocation_failure; + } + + union_name->external_name = + (gss_buffer_t) malloc(sizeof(gss_buffer_desc)); + if (!union_name->external_name) { + major_status = GSS_S_FAILURE; + goto allocation_failure; + } + union_name->external_name->length = 0; + union_name->external_name->value = NULL; + + major_status = mech->gss_display_name(minor_status, + internal_name, + union_name->external_name, + &union_name->name_type); + if (major_status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + goto allocation_failure; + } + + union_name->loopback = union_name; + *external_name = /*(gss_name_t) CHECK */union_name; + return (GSS_S_COMPLETE); + +allocation_failure: + if (union_name) { + if (union_name->external_name) { + if (union_name->external_name->value) + free(union_name->external_name->value); + free(union_name->external_name); + } + if (union_name->name_type) + gss_release_oid(&tmp, &union_name->name_type); + if (union_name->mech_type) + gss_release_oid(&tmp, &union_name->mech_type); + free(union_name); + } + /* + * do as the top comment says - since we are now owners of + * internal_name, we must clean it up + */ + if (internal_name) + (void) gssint_release_internal_name(&tmp, &mech->mech_type, + &internal_name); + return (major_status); +} + +/* + * Glue routine for returning the mechanism-specific credential from a + * external union credential. + */ +gss_cred_id_t +gssint_get_mechanism_cred(union_cred, mech_type) + gss_union_cred_t union_cred; + gss_OID mech_type; +{ + int i; + + if (union_cred == GSS_C_NO_CREDENTIAL) + return GSS_C_NO_CREDENTIAL; + + for (i=0; i < union_cred->count; i++) { + if (g_OID_equal(mech_type, &union_cred->mechs_array[i])) + return union_cred->cred_array[i]; + } + return GSS_C_NO_CREDENTIAL; +} + +/* + * Routine to create and copy the gss_buffer_desc structure. + * Both space for the structure and the data is allocated. + */ +OM_uint32 +gssint_create_copy_buffer(srcBuf, destBuf, addNullChar) + const gss_buffer_t srcBuf; + gss_buffer_t *destBuf; + int addNullChar; +{ + gss_buffer_t aBuf; + unsigned int len; + + if (destBuf == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + *destBuf = 0; + + aBuf = (gss_buffer_t)malloc(sizeof (gss_buffer_desc)); + if (!aBuf) + return (GSS_S_FAILURE); + + if (addNullChar) + len = srcBuf->length + 1; + else + len = srcBuf->length; + + if (!(aBuf->value = (void*)gssalloc_malloc(len))) { + free(aBuf); + return (GSS_S_FAILURE); + } + + + (void) memcpy(aBuf->value, srcBuf->value, srcBuf->length); + aBuf->length = srcBuf->length; + *destBuf = aBuf; + + /* optionally add a NULL character */ + if (addNullChar) + ((char *)aBuf->value)[aBuf->length] = '\0'; + + return (GSS_S_COMPLETE); +} /* ****** gssint_create_copy_buffer ****** */ diff --git a/src/lib/gssapi/mechglue/g_imp_cred.c b/src/lib/gssapi/mechglue/g_imp_cred.c new file mode 100644 index 000000000000..77e2ff55ce57 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_imp_cred.c @@ -0,0 +1,177 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/mechglue/g_imp_cred.c - gss_import_cred definition */ +/* + * Copyright (C) 2012 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "mglueP.h" + +static OM_uint32 +val_imp_cred_args(OM_uint32 *minor_status, gss_buffer_t token, + gss_cred_id_t *cred_handle) +{ + /* Initialize outputs. */ + if (minor_status != NULL) + *minor_status = 0; + if (cred_handle != NULL) + *cred_handle = GSS_C_NO_CREDENTIAL; + + /* Validate arguments. */ + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + if (cred_handle == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + if (token == GSS_C_NO_BUFFER) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_DEFECTIVE_TOKEN; + if (GSS_EMPTY_BUFFER(token)) + return GSS_S_DEFECTIVE_TOKEN; + return GSS_S_COMPLETE; +} + +/* Populate mech_oid and mech_token with the next entry in token, using aliased + * memory from token. Advance token by the amount consumed. */ +static OM_uint32 +get_entry(OM_uint32 *minor_status, gss_buffer_t token, gss_OID mech_oid, + gss_buffer_t mech_token) +{ + OM_uint32 len; + + /* Get the mechanism OID. */ + if (token->length < 4) + return GSS_S_DEFECTIVE_TOKEN; + len = load_32_be(token->value); + if (token->length - 4 < len) + return GSS_S_DEFECTIVE_TOKEN; + mech_oid->length = len; + mech_oid->elements = (char *)token->value + 4; + token->value = (char *)token->value + 4 + len; + token->length -= 4 + len; + + /* Get the mechanism token. */ + if (token->length < 4) + return GSS_S_DEFECTIVE_TOKEN; + len = load_32_be(token->value); + if (token->length - 4 < len) + return GSS_S_DEFECTIVE_TOKEN; + mech_token->length = len; + mech_token->value = (char *)token->value + 4; + token->value = (char *)token->value + 4 + len; + token->length -= 4 + len; + + return GSS_S_COMPLETE; +} + +OM_uint32 KRB5_CALLCONV +gss_import_cred(OM_uint32 *minor_status, gss_buffer_t token, + gss_cred_id_t *cred_handle) +{ + OM_uint32 status, tmpmin, count; + gss_union_cred_t cred = NULL; + gss_mechanism mech; + gss_buffer_desc tok, mech_token; + gss_OID_desc mech_oid; + gss_OID selected_mech; + gss_cred_id_t mech_cred; + void *elemcopy; + + status = val_imp_cred_args(minor_status, token, cred_handle); + if (status != GSS_S_COMPLETE) + return status; + + /* Count the entries in token. */ + for (tok = *token, count = 0; tok.length > 0; count++) { + status = get_entry(minor_status, &tok, &mech_oid, &mech_token); + if (status != GSS_S_COMPLETE) + return status; + } + + /* Allocate a union credential. */ + cred = calloc(1, sizeof(*cred)); + if (cred == NULL) + goto oom; + cred->loopback = cred; + cred->count = 0; + cred->mechs_array = calloc(count, sizeof(*cred->mechs_array)); + if (cred->mechs_array == NULL) + goto oom; + cred->cred_array = calloc(count, sizeof(*cred->cred_array)); + if (cred->cred_array == NULL) + goto oom; + + tok = *token; + while (tok.length > 0) { + (void)get_entry(minor_status, &tok, &mech_oid, &mech_token); + + /* Import this entry's mechanism token. */ + status = gssint_select_mech_type(minor_status, &mech_oid, + &selected_mech); + if (status != GSS_S_COMPLETE) + goto error; + mech = gssint_get_mechanism(selected_mech); + if (mech == NULL || (mech->gss_import_cred == NULL && + mech->gssspi_import_cred_by_mech == NULL)) { + status = GSS_S_DEFECTIVE_TOKEN; + goto error; + } + if (mech->gssspi_import_cred_by_mech) { + status = mech->gssspi_import_cred_by_mech(minor_status, + gssint_get_public_oid(selected_mech), + &mech_token, &mech_cred); + } else { + status = mech->gss_import_cred(minor_status, &mech_token, + &mech_cred); + } + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + goto error; + } + + /* Add the resulting mechanism cred to the union cred. */ + elemcopy = malloc(selected_mech->length); + if (elemcopy == NULL) { + if (mech->gss_release_cred != NULL) + mech->gss_release_cred(&tmpmin, &mech_cred); + goto oom; + } + memcpy(elemcopy, selected_mech->elements, selected_mech->length); + cred->mechs_array[cred->count].length = selected_mech->length; + cred->mechs_array[cred->count].elements = elemcopy; + cred->cred_array[cred->count++] = mech_cred; + } + + *cred_handle = cred; + return GSS_S_COMPLETE; + +oom: + status = GSS_S_FAILURE; + *minor_status = ENOMEM; +error: + (void)gss_release_cred(&tmpmin, &cred); + return status; +} diff --git a/src/lib/gssapi/mechglue/g_imp_name.c b/src/lib/gssapi/mechglue/g_imp_name.c new file mode 100644 index 000000000000..374965bc9288 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_imp_name.c @@ -0,0 +1,387 @@ +/* #pragma ident "@(#)g_imp_name.c 1.26 04/02/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine gss_import_name + * + */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <errno.h> + +/* local function to import GSS_C_EXPORT_NAME names */ +static OM_uint32 importExportName(OM_uint32 *, gss_union_name_t, gss_OID); + +static OM_uint32 +val_imp_name_args( + OM_uint32 *minor_status, + gss_buffer_t input_name_buffer, + gss_OID input_name_type, + gss_name_t *output_name) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_name != NULL) + *output_name = GSS_C_NO_NAME; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (output_name == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (input_name_buffer == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME); + + if (input_name_type == GSS_C_NO_OID || + !g_OID_equal(input_name_type, GSS_C_NT_ANONYMOUS)) { + if (input_name_buffer->length == 0) + return (GSS_S_BAD_NAME); + + if (input_name_buffer->value == NULL) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME); + } + + return (GSS_S_COMPLETE); +} + +static gss_buffer_desc emptyNameBuffer; + +OM_uint32 KRB5_CALLCONV +gss_import_name(minor_status, + input_name_buffer, + input_name_type, + output_name) + +OM_uint32 * minor_status; +gss_buffer_t input_name_buffer; +gss_OID input_name_type; +gss_name_t * output_name; + +{ + gss_union_name_t union_name; + OM_uint32 tmp, major_status = GSS_S_FAILURE; + + if (input_name_buffer == GSS_C_NO_BUFFER) + input_name_buffer = &emptyNameBuffer; + + major_status = val_imp_name_args(minor_status, + input_name_buffer, input_name_type, + output_name); + if (major_status != GSS_S_COMPLETE) + return (major_status); + + /* + * First create the union name struct that will hold the external + * name and the name type. + */ + union_name = (gss_union_name_t) malloc (sizeof(gss_union_name_desc)); + if (!union_name) + return (GSS_S_FAILURE); + + union_name->loopback = 0; + union_name->mech_type = 0; + union_name->mech_name = 0; + union_name->name_type = 0; + union_name->external_name = 0; + + /* + * All we do here is record the external name and name_type. + * When the name is actually used, the underlying gss_import_name() + * is called for the appropriate mechanism. The exception to this + * rule is when the name of GSS_C_NT_EXPORT_NAME type. If that is + * the case, then we make it MN in this call. + */ + major_status = gssint_create_copy_buffer(input_name_buffer, + &union_name->external_name, 0); + if (major_status != GSS_S_COMPLETE) { + free(union_name); + return (major_status); + } + + if (input_name_type != GSS_C_NULL_OID) { + major_status = generic_gss_copy_oid(minor_status, + input_name_type, + &union_name->name_type); + if (major_status != GSS_S_COMPLETE) { + map_errcode(minor_status); + goto allocation_failure; + } + } + + /* + * In MIT Distribution the mechanism is determined from the nametype; + * This is not a good idea - first mechanism that supports a given + * name type is picked up; later on the caller can request a + * different mechanism. So we don't determine the mechanism here. Now + * the user level and kernel level import_name routine looks similar + * except the kernel routine makes a copy of the nametype structure. We + * do however make this an MN for names of GSS_C_NT_EXPORT_NAME type. + */ + if (input_name_type != GSS_C_NULL_OID && + (g_OID_equal(input_name_type, GSS_C_NT_EXPORT_NAME) || + g_OID_equal(input_name_type, GSS_C_NT_COMPOSITE_EXPORT))) { + major_status = importExportName(minor_status, union_name, input_name_type); + if (major_status != GSS_S_COMPLETE) + goto allocation_failure; + } + + union_name->loopback = union_name; + *output_name = (gss_name_t)union_name; + return (GSS_S_COMPLETE); + +allocation_failure: + if (union_name) { + if (union_name->external_name) { + if (union_name->external_name->value) + free(union_name->external_name->value); + free(union_name->external_name); + } + if (union_name->name_type) + generic_gss_release_oid(&tmp, &union_name->name_type); + if (union_name->mech_name) + gssint_release_internal_name(minor_status, union_name->mech_type, + &union_name->mech_name); + if (union_name->mech_type) + generic_gss_release_oid(&tmp, &union_name->mech_type); + free(union_name); + } + return (major_status); +} + +/* + * GSS export name constants + */ +static const unsigned int expNameTokIdLen = 2; +static const unsigned int mechOidLenLen = 2; +static const unsigned int nameTypeLenLen = 2; + +static OM_uint32 +importExportName(minor, unionName, inputNameType) + OM_uint32 *minor; + gss_union_name_t unionName; + gss_OID inputNameType; +{ + gss_OID_desc mechOid; + gss_buffer_desc expName; + unsigned char *buf; + gss_mechanism mech; + OM_uint32 major, mechOidLen, nameLen, curLength; + unsigned int bytes; + + expName.value = unionName->external_name->value; + expName.length = unionName->external_name->length; + + curLength = expNameTokIdLen + mechOidLenLen; + if (expName.length < curLength) + return (GSS_S_DEFECTIVE_TOKEN); + + buf = (unsigned char *)expName.value; + if (buf[0] != 0x04) + return (GSS_S_DEFECTIVE_TOKEN); + if (buf[1] != 0x01 && buf[1] != 0x02) /* allow composite names */ + return (GSS_S_DEFECTIVE_TOKEN); + + buf += expNameTokIdLen; + + /* extract the mechanism oid length */ + mechOidLen = (*buf++ << 8); + mechOidLen |= (*buf++); + curLength += mechOidLen; + if (expName.length < curLength) + return (GSS_S_DEFECTIVE_TOKEN); + /* + * The mechOid itself is encoded in DER format, OID Tag (0x06) + * length and the value of mech_OID + */ + if (*buf++ != 0x06) + return (GSS_S_DEFECTIVE_TOKEN); + + /* + * mechoid Length is encoded twice; once in 2 bytes as + * explained in RFC2743 (under mechanism independent exported + * name object format) and once using DER encoding + * + * We verify both lengths. + */ + + mechOid.length = gssint_get_der_length(&buf, + (expName.length - curLength), &bytes); + mechOid.elements = (void *)buf; + + /* + * 'bytes' is the length of the DER length, '1' is for the DER + * tag for OID + */ + if ((bytes + mechOid.length + 1) != mechOidLen) + return (GSS_S_DEFECTIVE_TOKEN); + + buf += mechOid.length; + if ((mech = gssint_get_mechanism(&mechOid)) == NULL) + return (GSS_S_BAD_MECH); + + if (mech->gssspi_import_name_by_mech == NULL && + mech->gss_import_name == NULL) + return (GSS_S_UNAVAILABLE); + + /* + * we must now determine if we should unwrap the name ourselves + * or make the mechanism do it - we should only unwrap it + * if we create it; so if mech->gss_export_name == NULL, we must + * have created it. + */ + if (mech->gss_export_name) { + if (mech->gssspi_import_name_by_mech) { + major = mech->gssspi_import_name_by_mech(minor, &mechOid, &expName, + inputNameType, + &unionName->mech_name); + } else { + major = mech->gss_import_name(minor, &expName, inputNameType, + &unionName->mech_name); + } + if (major != GSS_S_COMPLETE) + map_error(minor, mech); + else { + major = generic_gss_copy_oid(minor, &mechOid, + &unionName->mech_type); + if (major != GSS_S_COMPLETE) + map_errcode(minor); + } + return (major); + } + /* + * we must have exported the name - so we now need to reconstruct it + * and call the mechanism to create it + * + * WARNING: Older versions of gssint_export_internal_name() did + * not export names correctly, but now it does. In + * order to stay compatible with existing exported + * names we must support names exported the broken + * way. + * + * Specifically, gssint_export_internal_name() used to include + * the name type OID in the encoding of the exported MN. + * Additionally, the Kerberos V mech used to make display names + * that included a null terminator which was counted in the + * display name gss_buffer_desc. + */ + curLength += 4; /* 4 bytes for name len */ + if (expName.length < curLength) + return (GSS_S_DEFECTIVE_TOKEN); + + /* next 4 bytes in the name are the name length */ + nameLen = load_32_be(buf); + buf += 4; + + /* + * we use < here because bad code in rpcsec_gss rounds up exported + * name token lengths and pads with nulls, otherwise != would be + * appropriate + */ + curLength += nameLen; /* this is the total length */ + if (expName.length < curLength) + return (GSS_S_DEFECTIVE_TOKEN); + + /* + * We detect broken exported names here: they always start with + * a two-octet network-byte order OID length, which is always + * less than 256 bytes, so the first octet of the length is + * always '\0', which is not allowed in GSS-API display names + * (or never occurs in them anyways). Of course, the OID + * shouldn't be there, but it is. After the OID (sans DER tag + * and length) there's the name itself, though null-terminated; + * this null terminator should also not be there, but it is. + */ + if (nameLen > 0 && *buf == '\0') { + OM_uint32 nameTypeLen; + /* next two bytes are the name oid */ + if (nameLen < nameTypeLenLen) + return (GSS_S_DEFECTIVE_TOKEN); + + nameLen -= nameTypeLenLen; + + nameTypeLen = (*buf++) << 8; + nameTypeLen |= (*buf++); + + if (nameLen < nameTypeLen) + return (GSS_S_DEFECTIVE_TOKEN); + + buf += nameTypeLen; + nameLen -= nameTypeLen; + + /* + * adjust for expected null terminator that should + * really not be there + */ + if (nameLen > 0 && *(buf + nameLen - 1) == '\0') + nameLen--; + } + + /* + * Can a name be null? Let the mech decide. + * + * NOTE: We use GSS_C_NULL_OID as the name type when importing + * the unwrapped name. Presumably the exported name had, + * prior to being exported been obtained in such a way + * that it has been properly perpared ("canonicalized," in + * GSS-API terms) accroding to some name type; we cannot + * tell what that name type was now, but the name should + * need no further preparation other than the lowest + * common denominator afforded by the mech to names + * imported with GSS_C_NULL_OID. For the Kerberos V mech + * this means doing less busywork too (particularly once + * IDN is thrown in with Kerberos V extensions). + */ + expName.length = nameLen; + expName.value = nameLen ? (void *)buf : NULL; + if (mech->gssspi_import_name_by_mech) { + major = mech->gssspi_import_name_by_mech(minor, &mechOid, &expName, + GSS_C_NULL_OID, + &unionName->mech_name); + } else { + major = mech->gss_import_name(minor, &expName, + GSS_C_NULL_OID, &unionName->mech_name); + } + if (major != GSS_S_COMPLETE) { + map_error(minor, mech); + return (major); + } + + major = generic_gss_copy_oid(minor, &mechOid, &unionName->mech_type); + if (major != GSS_S_COMPLETE) { + map_errcode(minor); + } + return major; +} /* importExportName */ diff --git a/src/lib/gssapi/mechglue/g_imp_sec_context.c b/src/lib/gssapi/mechglue/g_imp_sec_context.c new file mode 100644 index 000000000000..a0e2d71221b6 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_imp_sec_context.c @@ -0,0 +1,175 @@ +/* #pragma ident "@(#)g_imp_sec_context.c 1.18 04/02/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine gss_export_sec_context + */ + +#ifndef LEAN_CLIENT + +#include "mglueP.h" +#include <stdio.h> +#include <errno.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> + +static OM_uint32 +val_imp_sec_ctx_args( + OM_uint32 *minor_status, + gss_buffer_t interprocess_token, + gss_ctx_id_t *context_handle) +{ + + /* Initialize outputs. */ + if (minor_status != NULL) + *minor_status = 0; + + if (context_handle != NULL) + *context_handle = GSS_C_NO_CONTEXT; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (context_handle == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (interprocess_token == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_DEFECTIVE_TOKEN); + + if (GSS_EMPTY_BUFFER(interprocess_token)) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_DEFECTIVE_TOKEN); + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_import_sec_context(minor_status, + interprocess_token, + context_handle) + +OM_uint32 * minor_status; +gss_buffer_t interprocess_token; +gss_ctx_id_t * context_handle; + +{ + OM_uint32 length = 0; + OM_uint32 status; + char *p; + gss_union_ctx_id_t ctx; + gss_ctx_id_t mctx; + gss_buffer_desc token; + gss_OID_desc token_mech; + gss_OID selected_mech = GSS_C_NO_OID; + gss_OID public_mech; + gss_mechanism mech; + + status = val_imp_sec_ctx_args(minor_status, + interprocess_token, context_handle); + if (status != GSS_S_COMPLETE) + return (status); + + /* Initial value needed below. */ + status = GSS_S_FAILURE; + + ctx = (gss_union_ctx_id_t) malloc(sizeof(gss_union_ctx_id_desc)); + if (!ctx) + return (GSS_S_FAILURE); + + if (interprocess_token->length >= sizeof (OM_uint32)) { + p = interprocess_token->value; + length = (OM_uint32)*p++; + length = (OM_uint32)(length << 8) + *p++; + length = (OM_uint32)(length << 8) + *p++; + length = (OM_uint32)(length << 8) + *p++; + } + + if (length == 0 || + length > (interprocess_token->length - sizeof (OM_uint32))) { + free(ctx); + return (GSS_S_CALL_BAD_STRUCTURE | GSS_S_DEFECTIVE_TOKEN); + } + + token_mech.length = length; + token_mech.elements = p; + + p += length; + + token.length = interprocess_token->length - sizeof (OM_uint32) - length; + token.value = p; + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + status = gssint_select_mech_type(minor_status, &token_mech, + &selected_mech); + if (status != GSS_S_COMPLETE) + goto error_out; + + mech = gssint_get_mechanism(selected_mech); + if (!mech) { + status = GSS_S_BAD_MECH; + goto error_out; + } + if (!mech->gssspi_import_sec_context_by_mech && + !mech->gss_import_sec_context) { + status = GSS_S_UNAVAILABLE; + goto error_out; + } + + if (generic_gss_copy_oid(minor_status, selected_mech, + &ctx->mech_type) != GSS_S_COMPLETE) { + status = GSS_S_FAILURE; + goto error_out; + } + + if (mech->gssspi_import_sec_context_by_mech) { + public_mech = gssint_get_public_oid(selected_mech); + status = mech->gssspi_import_sec_context_by_mech(minor_status, + public_mech, + &token, &mctx); + } else { + status = mech->gss_import_sec_context(minor_status, &token, &mctx); + } + if (status == GSS_S_COMPLETE) { + ctx->internal_ctx_id = mctx; + ctx->loopback = ctx; + *context_handle = (gss_ctx_id_t)ctx; + return (GSS_S_COMPLETE); + } + map_error(minor_status, mech); + free(ctx->mech_type->elements); + free(ctx->mech_type); + +error_out: + free(ctx); + return status; +} +#endif /* LEAN_CLIENT */ diff --git a/src/lib/gssapi/mechglue/g_init_sec_context.c b/src/lib/gssapi/mechglue/g_init_sec_context.c new file mode 100644 index 000000000000..9f154b8936d0 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_init_sec_context.c @@ -0,0 +1,254 @@ +/* #pragma ident "@(#)g_init_sec_context.c 1.20 03/10/24 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_init_sec_context + */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> + +static OM_uint32 +val_init_sec_ctx_args( + OM_uint32 *minor_status, + gss_cred_id_t claimant_cred_handle, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID req_mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (actual_mech_type != NULL) + *actual_mech_type = GSS_C_NO_OID; + + if (output_token != GSS_C_NO_BUFFER) { + output_token->length = 0; + output_token->value = NULL; + } + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (context_handle == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CONTEXT); + + if (target_name == NULL) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME); + + if (output_token == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_init_sec_context (minor_status, + claimant_cred_handle, + context_handle, + target_name, + req_mech_type, + req_flags, + time_req, + input_chan_bindings, + input_token, + actual_mech_type, + output_token, + ret_flags, + time_rec) + +OM_uint32 * minor_status; +gss_cred_id_t claimant_cred_handle; +gss_ctx_id_t * context_handle; +gss_name_t target_name; +gss_OID req_mech_type; +OM_uint32 req_flags; +OM_uint32 time_req; +gss_channel_bindings_t input_chan_bindings; +gss_buffer_t input_token; +gss_OID * actual_mech_type; +gss_buffer_t output_token; +OM_uint32 * ret_flags; +OM_uint32 * time_rec; + +{ + OM_uint32 status, temp_minor_status; + gss_union_name_t union_name; + gss_union_cred_t union_cred; + gss_name_t internal_name; + gss_union_ctx_id_t union_ctx_id; + gss_OID selected_mech; + gss_mechanism mech; + gss_cred_id_t input_cred_handle; + + status = val_init_sec_ctx_args(minor_status, + claimant_cred_handle, + context_handle, + target_name, + req_mech_type, + req_flags, + time_req, + input_chan_bindings, + input_token, + actual_mech_type, + output_token, + ret_flags, + time_rec); + if (status != GSS_S_COMPLETE) + return (status); + + status = gssint_select_mech_type(minor_status, req_mech_type, + &selected_mech); + if (status != GSS_S_COMPLETE) + return (status); + + union_name = (gss_union_name_t)target_name; + + /* + * obtain the gss mechanism information for the requested + * mechanism. If mech_type is NULL, set it to the resultant + * mechanism + */ + mech = gssint_get_mechanism(selected_mech); + if (mech == NULL) + return (GSS_S_BAD_MECH); + + if (mech->gss_init_sec_context == NULL) + return (GSS_S_UNAVAILABLE); + + /* + * If target_name is mechanism_specific, then it must match the + * mech_type that we're about to use. Otherwise, do an import on + * the external_name form of the target name. + */ + if (union_name->mech_type && + g_OID_equal(union_name->mech_type, selected_mech)) { + internal_name = union_name->mech_name; + } else { + if ((status = gssint_import_internal_name(minor_status, selected_mech, + union_name, + &internal_name)) != GSS_S_COMPLETE) + return (status); + } + + /* + * if context_handle is GSS_C_NO_CONTEXT, allocate a union context + * descriptor to hold the mech type information as well as the + * underlying mechanism context handle. Otherwise, cast the + * value of *context_handle to the union context variable. + */ + + if(*context_handle == GSS_C_NO_CONTEXT) { + status = GSS_S_FAILURE; + union_ctx_id = (gss_union_ctx_id_t) + malloc(sizeof(gss_union_ctx_id_desc)); + if (union_ctx_id == NULL) + goto end; + + if (generic_gss_copy_oid(&temp_minor_status, selected_mech, + &union_ctx_id->mech_type) != GSS_S_COMPLETE) { + free(union_ctx_id); + goto end; + } + + /* copy the supplied context handle */ + union_ctx_id->internal_ctx_id = GSS_C_NO_CONTEXT; + } else + union_ctx_id = (gss_union_ctx_id_t)*context_handle; + + /* + * get the appropriate cred handle from the union cred struct. + * defaults to GSS_C_NO_CREDENTIAL if there is no cred, which will + * use the default credential. + */ + union_cred = (gss_union_cred_t) claimant_cred_handle; + input_cred_handle = gssint_get_mechanism_cred(union_cred, selected_mech); + + /* + * now call the approprate underlying mechanism routine + */ + + status = mech->gss_init_sec_context( + minor_status, + input_cred_handle, + &union_ctx_id->internal_ctx_id, + internal_name, + gssint_get_public_oid(selected_mech), + req_flags, + time_req, + input_chan_bindings, + input_token, + actual_mech_type, + output_token, + ret_flags, + time_rec); + + if (status != GSS_S_COMPLETE && status != GSS_S_CONTINUE_NEEDED) { + /* + * The spec says the preferred method is to delete all context info on + * the first call to init, and on all subsequent calls make the caller + * responsible for calling gss_delete_sec_context. However, if the + * mechanism decided to delete the internal context, we should also + * delete the union context. + */ + map_error(minor_status, mech); + if (union_ctx_id->internal_ctx_id == GSS_C_NO_CONTEXT) + *context_handle = GSS_C_NO_CONTEXT; + if (*context_handle == GSS_C_NO_CONTEXT) { + free(union_ctx_id->mech_type->elements); + free(union_ctx_id->mech_type); + free(union_ctx_id); + } + } else if (*context_handle == GSS_C_NO_CONTEXT) { + union_ctx_id->loopback = union_ctx_id; + *context_handle = (gss_ctx_id_t)union_ctx_id; + } + +end: + if (union_name->mech_name == NULL || + union_name->mech_name != internal_name) { + (void) gssint_release_internal_name(&temp_minor_status, + selected_mech, &internal_name); + } + + return(status); +} diff --git a/src/lib/gssapi/mechglue/g_initialize.c b/src/lib/gssapi/mechglue/g_initialize.c new file mode 100644 index 000000000000..9197666e1072 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_initialize.c @@ -0,0 +1,1615 @@ +/* #pragma ident "@(#)g_initialize.c 1.36 05/02/02 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This function will initialize the gssapi mechglue library + */ + +#include "mglueP.h" +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#ifndef _WIN32 +#include <glob.h> +#endif + +#define M_DEFAULT "default" + +#include "k5-thread.h" +#include "k5-plugin.h" +#include "osconf.h" +#ifdef _GSS_STATIC_LINK +#include "gssapiP_krb5.h" +#include "gssapiP_spnego.h" +#endif + +#define MECH_SYM "gss_mech_initialize" +#define MECH_INTERPOSER_SYM "gss_mech_interposer" + +#ifndef MECH_CONF +#define MECH_CONF "/etc/gss/mech" +#endif +#define MECH_CONF_PATTERN MECH_CONF ".d/*.conf" + +/* Local functions */ +static void addConfigEntry(const char *oidStr, const char *oid, + const char *sharedLib, const char *kernMod, + const char *modOptions, const char *modType); +static gss_mech_info searchMechList(gss_const_OID); +static void loadConfigFile(const char *); +#if defined(_WIN32) +#ifndef MECH_KEY +#define MECH_KEY "SOFTWARE\\gss\\mech" +#endif +static time_t getRegKeyModTime(HKEY hBaseKey, const char *keyPath); +static time_t getRegConfigModTime(const char *keyPath); +static void getRegKeyValue(HKEY key, const char *keyPath, const char *valueName, void **data, DWORD *dataLen); +static void loadConfigFromRegistry(HKEY keyBase, const char *keyPath); +#endif +static void updateMechList(void); +static void initMechList(void); +static void loadInterMech(gss_mech_info aMech); +static void freeMechList(void); + +static OM_uint32 build_mechSet(void); +static void free_mechSet(void); + +/* + * list of mechanism libraries and their entry points. + * the list also maintains state of the mech libraries (loaded or not). + */ +static gss_mech_info g_mechList = NULL; +static gss_mech_info g_mechListTail = NULL; +static k5_mutex_t g_mechListLock = K5_MUTEX_PARTIAL_INITIALIZER; +static time_t g_confFileModTime = (time_t)0; +static time_t g_confLastCall = (time_t)0; + +static gss_OID_set_desc g_mechSet = { 0, NULL }; +static k5_mutex_t g_mechSetLock = K5_MUTEX_PARTIAL_INITIALIZER; + +MAKE_INIT_FUNCTION(gssint_mechglue_init); +MAKE_FINI_FUNCTION(gssint_mechglue_fini); + +int +gssint_mechglue_init(void) +{ + int err; + +#ifdef SHOW_INITFINI_FUNCS + printf("gssint_mechglue_init\n"); +#endif + + add_error_table(&et_ggss_error_table); + + err = k5_mutex_finish_init(&g_mechSetLock); + err = k5_mutex_finish_init(&g_mechListLock); + +#ifdef _GSS_STATIC_LINK + err = gss_krb5int_lib_init(); + err = gss_spnegoint_lib_init(); +#endif + + err = gssint_mecherrmap_init(); + return err; +} + +void +gssint_mechglue_fini(void) +{ + if (!INITIALIZER_RAN(gssint_mechglue_init) || PROGRAM_EXITING()) { +#ifdef SHOW_INITFINI_FUNCS + printf("gssint_mechglue_fini: skipping\n"); +#endif + return; + } + +#ifdef SHOW_INITFINI_FUNCS + printf("gssint_mechglue_fini\n"); +#endif +#ifdef _GSS_STATIC_LINK + gss_spnegoint_lib_fini(); + gss_krb5int_lib_fini(); +#endif + k5_mutex_destroy(&g_mechSetLock); + k5_mutex_destroy(&g_mechListLock); + free_mechSet(); + freeMechList(); + remove_error_table(&et_ggss_error_table); + gssint_mecherrmap_destroy(); +} + +int +gssint_mechglue_initialize_library(void) +{ + return CALL_INIT_FUNCTION(gssint_mechglue_init); +} + +/* + * function used to reclaim the memory used by a gss_OID structure. + * This routine requires direct access to the mechList. + */ +OM_uint32 KRB5_CALLCONV +gss_release_oid(minor_status, oid) +OM_uint32 *minor_status; +gss_OID *oid; +{ + OM_uint32 major; + gss_mech_info aMech; + + if (minor_status == NULL || oid == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + *minor_status = gssint_mechglue_initialize_library(); + if (*minor_status != 0) + return (GSS_S_FAILURE); + + k5_mutex_lock(&g_mechListLock); + aMech = g_mechList; + while (aMech != NULL) { + + /* + * look through the loaded mechanism libraries for + * gss_internal_release_oid until one returns success. + * gss_internal_release_oid will only return success when + * the OID was recognized as an internal mechanism OID. if no + * mechanisms recognize the OID, then call the generic version. + */ + if (aMech->mech && aMech->mech->gss_internal_release_oid) { + major = aMech->mech->gss_internal_release_oid( + minor_status, oid); + if (major == GSS_S_COMPLETE) { + k5_mutex_unlock(&g_mechListLock); + return (GSS_S_COMPLETE); + } + map_error(minor_status, aMech->mech); + } + aMech = aMech->next; + } /* while */ + k5_mutex_unlock(&g_mechListLock); + + return (generic_gss_release_oid(minor_status, oid)); +} /* gss_release_oid */ + +/* + * Wrapper around inquire_attrs_for_mech to determine whether a mechanism has + * the deprecated attribute. Must be called without g_mechSetLock since it + * will call into the mechglue. + */ +static int +is_deprecated(gss_OID element) +{ + OM_uint32 major, minor; + gss_OID_set mech_attrs = GSS_C_NO_OID_SET; + int deprecated = 0; + + major = gss_inquire_attrs_for_mech(&minor, element, &mech_attrs, NULL); + if (major == GSS_S_COMPLETE) { + gss_test_oid_set_member(&minor, (gss_OID)GSS_C_MA_DEPRECATED, + mech_attrs, &deprecated); + } + + if (mech_attrs != GSS_C_NO_OID_SET) + gss_release_oid_set(&minor, &mech_attrs); + + return deprecated; +} + +/* + * Removes mechs with the deprecated attribute from an OID set. Must be + * called without g_mechSetLock held since it calls into the mechglue. + */ +static void +prune_deprecated(gss_OID_set mech_set) +{ + OM_uint32 i, j; + + j = 0; + for (i = 0; i < mech_set->count; i++) { + if (!is_deprecated(&mech_set->elements[i])) + mech_set->elements[j++] = mech_set->elements[i]; + else + gssalloc_free(mech_set->elements[i].elements); + } + mech_set->count = j; +} + +/* + * this function will return an oid set indicating available mechanisms. + * The set returned is based on configuration file entries and + * NOT on the loaded mechanisms. This function does not check if any + * of these can actually be loaded. + * Deprecated mechanisms will not be returned. + * This routine needs direct access to the mechanism list. + * To avoid reading the configuration file each call, we will save a + * a mech oid set, and only update it once the file has changed. + */ +OM_uint32 KRB5_CALLCONV +gss_indicate_mechs(minorStatus, mechSet_out) +OM_uint32 *minorStatus; +gss_OID_set *mechSet_out; +{ + OM_uint32 status; + + /* Initialize outputs. */ + + if (minorStatus != NULL) + *minorStatus = 0; + + if (mechSet_out != NULL) + *mechSet_out = GSS_C_NO_OID_SET; + + /* Validate arguments. */ + if (minorStatus == NULL || mechSet_out == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + *minorStatus = gssint_mechglue_initialize_library(); + if (*minorStatus != 0) + return (GSS_S_FAILURE); + + if (build_mechSet()) + return GSS_S_FAILURE; + + /* + * need to lock the g_mechSet in case someone tries to update it while + * I'm copying it. + */ + k5_mutex_lock(&g_mechSetLock); + status = generic_gss_copy_oid_set(minorStatus, &g_mechSet, mechSet_out); + k5_mutex_unlock(&g_mechSetLock); + + if (*mechSet_out != GSS_C_NO_OID_SET) + prune_deprecated(*mechSet_out); + + return (status); +} /* gss_indicate_mechs */ + + +/* Call with g_mechSetLock held, or during final cleanup. */ +static void +free_mechSet(void) +{ + unsigned int i; + + if (g_mechSet.count != 0) { + for (i = 0; i < g_mechSet.count; i++) + free(g_mechSet.elements[i].elements); + free(g_mechSet.elements); + g_mechSet.elements = NULL; + g_mechSet.count = 0; + } +} + +static OM_uint32 +build_mechSet(void) +{ + gss_mech_info mList; + size_t i; + size_t count; + gss_OID curItem; + + /* + * lock the mutex since we will be updating + * the mechList structure + * we need to keep the lock while we build the mechanism list + * since we are accessing parts of the mechList which could be + * modified. + */ + k5_mutex_lock(&g_mechListLock); + + updateMechList(); + + /* + * we need to lock the mech set so that no one else will + * try to read it as we are re-creating it + */ + k5_mutex_lock(&g_mechSetLock); + + /* if the oid list already exists we must free it first */ + free_mechSet(); + + /* determine how many elements to have in the list */ + mList = g_mechList; + count = 0; + while (mList != NULL) { + count++; + mList = mList->next; + } + + /* this should always be true, but.... */ + if (count > 0) { + g_mechSet.elements = + (gss_OID) calloc(count, sizeof (gss_OID_desc)); + if (g_mechSet.elements == NULL) { + k5_mutex_unlock(&g_mechSetLock); + k5_mutex_unlock(&g_mechListLock); + return (GSS_S_FAILURE); + } + + (void) memset(g_mechSet.elements, 0, + count * sizeof (gss_OID_desc)); + + /* now copy each oid element */ + count = 0; + for (mList = g_mechList; mList != NULL; mList = mList->next) { + /* Don't expose interposer mechanisms. */ + if (mList->is_interposer) + continue; + curItem = &(g_mechSet.elements[count]); + curItem->elements = (void*) + malloc(mList->mech_type->length); + if (curItem->elements == NULL) { + /* + * this is nasty - we must delete the + * part of the array already copied + */ + for (i = 0; i < count; i++) { + free(g_mechSet.elements[i]. + elements); + } + free(g_mechSet.elements); + g_mechSet.count = 0; + g_mechSet.elements = NULL; + k5_mutex_unlock(&g_mechSetLock); + k5_mutex_unlock(&g_mechListLock); + return (GSS_S_FAILURE); + } + g_OID_copy(curItem, mList->mech_type); + count++; + } + g_mechSet.count = count; + } + +#if 0 + g_mechSetTime = fileInfo.st_mtime; +#endif + k5_mutex_unlock(&g_mechSetLock); + k5_mutex_unlock(&g_mechListLock); + + return GSS_S_COMPLETE; +} + + +/* + * this function has been added for use by modules that need to + * know what (if any) optional parameters are supplied in the + * config file (MECH_CONF). + * It will return the option string for a specified mechanism. + * caller is responsible for freeing the memory + */ +char * +gssint_get_modOptions(oid) +const gss_OID oid; +{ + gss_mech_info aMech; + char *modOptions = NULL; + + if (gssint_mechglue_initialize_library() != 0) + return (NULL); + + /* make sure we have fresh data */ + k5_mutex_lock(&g_mechListLock); + updateMechList(); + + if ((aMech = searchMechList(oid)) == NULL || + aMech->optionStr == NULL) { + k5_mutex_unlock(&g_mechListLock); + return (NULL); + } + + if (aMech->optionStr) + modOptions = strdup(aMech->optionStr); + k5_mutex_unlock(&g_mechListLock); + + return (modOptions); +} /* gssint_get_modOptions */ + +/* Return the mtime of filename or its eventual symlink target (if it is a + * symlink), whichever is larger. Return (time_t)-1 if lstat or stat fails. */ +static time_t +check_link_mtime(const char *filename, time_t *mtime_out) +{ + struct stat st1, st2; + + if (lstat(filename, &st1) != 0) + return (time_t)-1; + if (!S_ISLNK(st1.st_mode)) + return st1.st_mtime; + if (stat(filename, &st2) != 0) + return (time_t)-1; + return (st1.st_mtime > st2.st_mtime) ? st1.st_mtime : st2.st_mtime; +} + +/* Load pathname if it is newer than last. Update *highest to the maximum of + * its current value and pathname's mod time. */ +static void +load_if_changed(const char *pathname, time_t last, time_t *highest) +{ + time_t mtime; + + mtime = check_link_mtime(pathname, &mtime); + if (mtime == (time_t)-1) + return; + if (mtime > *highest) + *highest = mtime; + if (mtime > last) + loadConfigFile(pathname); +} + +#ifndef _WIN32 +/* Try to load any config files which have changed since the last call. Config + * files are MECH_CONF and any files matching MECH_CONF_PATTERN. */ +static void +loadConfigFiles() +{ + glob_t globbuf; + time_t highest = 0, now; + char **path; + + /* Don't glob and stat more than once per second. */ + if (time(&now) == (time_t)-1 || now == g_confLastCall) + return; + g_confLastCall = now; + + load_if_changed(MECH_CONF, g_confFileModTime, &highest); + + memset(&globbuf, 0, sizeof(globbuf)); + if (glob(MECH_CONF_PATTERN, 0, NULL, &globbuf) == 0) { + for (path = globbuf.gl_pathv; *path != NULL; path++) + load_if_changed(*path, g_confFileModTime, &highest); + } + globfree(&globbuf); + + g_confFileModTime = highest; +} +#endif + +/* + * determines if the mechList needs to be updated from file + * and performs the update. + * this functions must be called with a lock of g_mechListLock + */ +static void +updateMechList(void) +{ + gss_mech_info minfo; + +#if defined(_WIN32) + time_t lastConfModTime = getRegConfigModTime(MECH_KEY); + if (g_confFileModTime >= lastConfModTime) + return; + g_confFileModTime = lastConfModTime; + loadConfigFromRegistry(HKEY_CURRENT_USER, MECH_KEY); + loadConfigFromRegistry(HKEY_LOCAL_MACHINE, MECH_KEY); +#else /* _WIN32 */ + loadConfigFiles(); +#endif /* !_WIN32 */ + + /* Load any unloaded interposer mechanisms immediately, to make sure we + * interpose other mechanisms before they are used. */ + for (minfo = g_mechList; minfo != NULL; minfo = minfo->next) { + if (minfo->is_interposer && minfo->mech == NULL) + loadInterMech(minfo); + } +} /* updateMechList */ + +/* Update the mech list from system configuration if we have never done so. + * Must be invoked with the g_mechListLock mutex held. */ +static void +initMechList(void) +{ + static int lazy_init = 0; + + if (lazy_init == 0) { + updateMechList(); + lazy_init = 1; + } +} + +static void +releaseMechInfo(gss_mech_info *pCf) +{ + gss_mech_info cf; + OM_uint32 minor_status; + + if (*pCf == NULL) { + return; + } + + cf = *pCf; + + if (cf->kmodName != NULL) + free(cf->kmodName); + if (cf->uLibName != NULL) + free(cf->uLibName); + if (cf->mechNameStr != NULL) + free(cf->mechNameStr); + if (cf->optionStr != NULL) + free(cf->optionStr); + if (cf->mech_type != GSS_C_NO_OID && + cf->mech_type != &cf->mech->mech_type) + generic_gss_release_oid(&minor_status, &cf->mech_type); + if (cf->freeMech) + zapfree(cf->mech, sizeof(*cf->mech)); + if (cf->dl_handle != NULL) + krb5int_close_plugin(cf->dl_handle); + if (cf->int_mech_type != GSS_C_NO_OID) + generic_gss_release_oid(&minor_status, &cf->int_mech_type); + + memset(cf, 0, sizeof(*cf)); + free(cf); + + *pCf = NULL; +} + +#ifdef _GSS_STATIC_LINK +/* + * Register a mechanism. Called with g_mechListLock held. + */ +int +gssint_register_mechinfo(gss_mech_info template) +{ + gss_mech_info cf, new_cf; + + new_cf = calloc(1, sizeof(*new_cf)); + if (new_cf == NULL) { + return ENOMEM; + } + + new_cf->dl_handle = template->dl_handle; + /* copy mech so we can rewrite canonical mechanism OID */ + new_cf->mech = (gss_mechanism)calloc(1, sizeof(struct gss_config)); + if (new_cf->mech == NULL) { + releaseMechInfo(&new_cf); + return ENOMEM; + } + *new_cf->mech = *template->mech; + if (template->mech_type != NULL) + new_cf->mech->mech_type = *(template->mech_type); + new_cf->mech_type = &new_cf->mech->mech_type; + new_cf->priority = template->priority; + new_cf->freeMech = 1; + new_cf->next = NULL; + + if (template->kmodName != NULL) { + new_cf->kmodName = strdup(template->kmodName); + if (new_cf->kmodName == NULL) { + releaseMechInfo(&new_cf); + return ENOMEM; + } + } + if (template->uLibName != NULL) { + new_cf->uLibName = strdup(template->uLibName); + if (new_cf->uLibName == NULL) { + releaseMechInfo(&new_cf); + return ENOMEM; + } + } + if (template->mechNameStr != NULL) { + new_cf->mechNameStr = strdup(template->mechNameStr); + if (new_cf->mechNameStr == NULL) { + releaseMechInfo(&new_cf); + return ENOMEM; + } + } + if (template->optionStr != NULL) { + new_cf->optionStr = strdup(template->optionStr); + if (new_cf->optionStr == NULL) { + releaseMechInfo(&new_cf); + return ENOMEM; + } + } + if (g_mechList == NULL) { + g_mechList = new_cf; + g_mechListTail = new_cf; + return 0; + } else if (new_cf->priority < g_mechList->priority) { + new_cf->next = g_mechList; + g_mechList = new_cf; + return 0; + } + + for (cf = g_mechList; cf != NULL; cf = cf->next) { + if (cf->next == NULL || + new_cf->priority < cf->next->priority) { + new_cf->next = cf->next; + cf->next = new_cf; + if (g_mechListTail == cf) { + g_mechListTail = new_cf; + } + break; + } + } + + return 0; +} +#endif /* _GSS_STATIC_LINK */ + +#define GSS_ADD_DYNAMIC_METHOD(_dl, _mech, _symbol) \ + do { \ + struct errinfo errinfo; \ + \ + memset(&errinfo, 0, sizeof(errinfo)); \ + if (krb5int_get_plugin_func(_dl, \ + #_symbol, \ + (void (**)())&(_mech)->_symbol, \ + &errinfo) || errinfo.code) { \ + (_mech)->_symbol = NULL; \ + k5_clear_error(&errinfo); \ + } \ + } while (0) + +/* + * If _symbol is undefined in the shared object but the shared object + * is linked against the mechanism glue, it's possible for dlsym() to + * return the mechanism glue implementation. Guard against that. + */ +#define GSS_ADD_DYNAMIC_METHOD_NOLOOP(_dl, _mech, _symbol) \ + do { \ + GSS_ADD_DYNAMIC_METHOD(_dl, _mech, _symbol); \ + if ((_mech)->_symbol == _symbol) \ + (_mech)->_symbol = NULL; \ + } while (0) + +static gss_mechanism +build_dynamicMech(void *dl, const gss_OID mech_type) +{ + gss_mechanism mech; + + mech = (gss_mechanism)calloc(1, sizeof(*mech)); + if (mech == NULL) { + return NULL; + } + + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_acquire_cred); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_release_cred); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_init_sec_context); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_accept_sec_context); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_process_context_token); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_delete_sec_context); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_context_time); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_get_mic); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_verify_mic); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_wrap); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_unwrap); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_display_status); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_indicate_mechs); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_compare_name); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_display_name); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_import_name); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_release_name); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_cred); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_add_cred); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_export_sec_context); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_import_sec_context); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_cred_by_mech); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_names_for_mech); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_context); + GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_internal_release_oid); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_wrap_size_limit); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_localname); + GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_authorize_localname); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_export_name); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_duplicate_name); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_store_cred); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_sec_context_by_oid); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_cred_by_oid); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_set_sec_context_option); + GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_set_cred_option); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gssspi_mech_invoke); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_wrap_aead); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_unwrap_aead); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_wrap_iov); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_unwrap_iov); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_wrap_iov_length); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_complete_auth_token); + /* Services4User (introduced in 1.8) */ + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_acquire_cred_impersonate_name); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_add_cred_impersonate_name); + /* Naming extensions (introduced in 1.8) */ + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_display_name_ext); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_name); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_get_name_attribute); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_set_name_attribute); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_delete_name_attribute); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_export_name_composite); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_map_name_to_any); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_release_any_name_mapping); + /* RFC 4401 (introduced in 1.8) */ + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_pseudo_random); + /* RFC 4178 (introduced in 1.8; gss_get_neg_mechs not implemented) */ + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_set_neg_mechs); + /* draft-ietf-sasl-gs2 */ + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_saslname_for_mech); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_mech_for_saslname); + /* RFC 5587 */ + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_attrs_for_mech); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_acquire_cred_from); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_store_cred_into); + GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_acquire_cred_with_password); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_export_cred); + GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_import_cred); + GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_import_sec_context_by_mech); + GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_import_name_by_mech); + GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_import_cred_by_mech); + + assert(mech_type != GSS_C_NO_OID); + + mech->mech_type = *(mech_type); + + return mech; +} + +#define RESOLVE_GSSI_SYMBOL(_dl, _mech, _psym, _nsym) \ + do { \ + struct errinfo errinfo; \ + memset(&errinfo, 0, sizeof(errinfo)); \ + if (krb5int_get_plugin_func(_dl, \ + "gssi" #_nsym, \ + (void (**)())&(_mech)->_psym \ + ## _nsym, \ + &errinfo) || errinfo.code) { \ + (_mech)->_psym ## _nsym = NULL; \ + k5_clear_error(&errinfo); \ + } \ + } while (0) + +/* Build an interposer mechanism function table from dl. */ +static gss_mechanism +build_interMech(void *dl, const gss_OID mech_type) +{ + gss_mechanism mech; + + mech = calloc(1, sizeof(*mech)); + if (mech == NULL) { + return NULL; + } + + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _acquire_cred); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _release_cred); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _init_sec_context); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _accept_sec_context); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _process_context_token); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _delete_sec_context); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _context_time); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _get_mic); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _verify_mic); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _wrap); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _unwrap); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _display_status); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _indicate_mechs); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _compare_name); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _display_name); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _import_name); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _release_name); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_cred); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _add_cred); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _export_sec_context); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _import_sec_context); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_cred_by_mech); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_names_for_mech); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_context); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _internal_release_oid); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _wrap_size_limit); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _localname); + RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _authorize_localname); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _export_name); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _duplicate_name); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _store_cred); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_sec_context_by_oid); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_cred_by_oid); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _set_sec_context_option); + RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _set_cred_option); + RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _mech_invoke); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _wrap_aead); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _unwrap_aead); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _wrap_iov); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _unwrap_iov); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _wrap_iov_length); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _complete_auth_token); + /* Services4User (introduced in 1.8) */ + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _acquire_cred_impersonate_name); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _add_cred_impersonate_name); + /* Naming extensions (introduced in 1.8) */ + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _display_name_ext); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_name); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _get_name_attribute); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _set_name_attribute); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _delete_name_attribute); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _export_name_composite); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _map_name_to_any); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _release_any_name_mapping); + /* RFC 4401 (introduced in 1.8) */ + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _pseudo_random); + /* RFC 4178 (introduced in 1.8; get_neg_mechs not implemented) */ + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _set_neg_mechs); + /* draft-ietf-sasl-gs2 */ + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_saslname_for_mech); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_mech_for_saslname); + /* RFC 5587 */ + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_attrs_for_mech); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _acquire_cred_from); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _store_cred_into); + RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _acquire_cred_with_password); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _export_cred); + RESOLVE_GSSI_SYMBOL(dl, mech, gss, _import_cred); + RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _import_sec_context_by_mech); + RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _import_name_by_mech); + RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _import_cred_by_mech); + + mech->mech_type = *mech_type; + return mech; +} + +/* + * Concatenate an interposer mech OID and a real mech OID to create an + * identifier for the interposed mech. (The concatenation will not be a valid + * DER OID encoding, but the OID is only used internally.) + */ +static gss_OID +interposed_oid(gss_OID pre, gss_OID real) +{ + gss_OID o; + + o = (gss_OID)malloc(sizeof(gss_OID_desc)); + if (!o) + return NULL; + + o->length = pre->length + real->length; + o->elements = malloc(o->length); + if (!o->elements) { + free(o); + return NULL; + } + + memcpy(o->elements, pre->elements, pre->length); + memcpy((char *)o->elements + pre->length, real->elements, + real->length); + + return o; +} + +static void +loadInterMech(gss_mech_info minfo) +{ + struct plugin_file_handle *dl = NULL; + struct errinfo errinfo; + gss_OID_set (*isym)(const gss_OID); + gss_OID_set list; + gss_OID oid; + OM_uint32 min; + gss_mech_info mi; + size_t i; + + memset(&errinfo, 0, sizeof(errinfo)); + + if (krb5int_open_plugin(minfo->uLibName, &dl, &errinfo) != 0 || + errinfo.code != 0) { +#if 0 + (void) syslog(LOG_INFO, "libgss dlopen(%s): %s\n", + aMech->uLibName, dlerror()); +#endif + return; + } + + if (krb5int_get_plugin_func(dl, MECH_INTERPOSER_SYM, + (void (**)())&isym, &errinfo) != 0) + goto cleanup; + + /* Get a list of mechs to interpose. */ + list = (*isym)(minfo->mech_type); + if (!list) + goto cleanup; + minfo->mech = build_interMech(dl, minfo->mech_type); + if (minfo->mech == NULL) + goto cleanup; + minfo->freeMech = 1; + + /* Add interposer fields for each interposed mech. */ + for (i = 0; i < list->count; i++) { + /* Skip this mech if it doesn't exist or is already + * interposed. */ + oid = &list->elements[i]; + mi = searchMechList(oid); + if (mi == NULL || mi->int_mech_type != NULL) + continue; + + /* Construct a special OID to represent the interposed mech. */ + mi->int_mech_type = interposed_oid(minfo->mech_type, oid); + if (mi->int_mech_type == NULL) + continue; + + /* Save an alias to the interposer's function table. */ + mi->int_mech = minfo->mech; + } + (void)gss_release_oid_set(&min, &list); + + minfo->dl_handle = dl; + dl = NULL; + +cleanup: +#if 0 + if (aMech->mech == NULL) { + (void) syslog(LOG_INFO, "unable to initialize mechanism" + " library [%s]\n", aMech->uLibName); + } +#endif + if (dl != NULL) + krb5int_close_plugin(dl); + k5_clear_error(&errinfo); +} + +static void +freeMechList(void) +{ + gss_mech_info cf, next_cf; + + for (cf = g_mechList; cf != NULL; cf = next_cf) { + next_cf = cf->next; + releaseMechInfo(&cf); + } +} + +/* + * Determine the mechanism to use for a caller-specified mech OID. For the + * real mech OID of an interposed mech, return the interposed OID. For an + * interposed mech OID (which an interposer mech uses when re-entering the + * mechglue), return the real mech OID. The returned OID is an alias and + * should not be modified or freed. + */ +OM_uint32 +gssint_select_mech_type(OM_uint32 *minor, gss_const_OID oid, + gss_OID *selected_oid) +{ + gss_mech_info minfo; + OM_uint32 status; + + *selected_oid = GSS_C_NO_OID; + + if (gssint_mechglue_initialize_library() != 0) + return GSS_S_FAILURE; + + k5_mutex_lock(&g_mechListLock); + + /* Read conf file at least once so that interposer plugins have a + * chance of getting initialized. */ + initMechList(); + + minfo = g_mechList; + if (oid == GSS_C_NULL_OID) + oid = minfo->mech_type; + while (minfo != NULL) { + if (g_OID_equal(minfo->mech_type, oid)) { + if (minfo->int_mech_type != GSS_C_NO_OID) + *selected_oid = minfo->int_mech_type; + else + *selected_oid = minfo->mech_type; + status = GSS_S_COMPLETE; + goto done; + } else if ((minfo->int_mech_type != GSS_C_NO_OID) && + (g_OID_equal(minfo->int_mech_type, oid))) { + *selected_oid = minfo->mech_type; + status = GSS_S_COMPLETE; + goto done; + } + minfo = minfo->next; + } + status = GSS_S_BAD_MECH; + +done: + k5_mutex_unlock(&g_mechListLock); + return status; +} + +/* If oid is an interposed OID, return the corresponding real mech OID. If + * it's a real mech OID, return it unmodified. Otherwised return null. */ +gss_OID +gssint_get_public_oid(gss_const_OID oid) +{ + gss_mech_info minfo; + gss_OID public_oid = GSS_C_NO_OID; + + /* if oid is null -> then get default which is the first in the list */ + if (oid == GSS_C_NO_OID) + return GSS_C_NO_OID; + + if (gssint_mechglue_initialize_library() != 0) + return GSS_C_NO_OID; + + k5_mutex_lock(&g_mechListLock); + + for (minfo = g_mechList; minfo != NULL; minfo = minfo->next) { + if (minfo->is_interposer) + continue; + if (g_OID_equal(minfo->mech_type, oid) || + ((minfo->int_mech_type != GSS_C_NO_OID) && + (g_OID_equal(minfo->int_mech_type, oid)))) { + public_oid = minfo->mech_type; + break; + } + } + + k5_mutex_unlock(&g_mechListLock); + return public_oid; +} + +/* Translate a vector of oids (as from a union cred struct) into a set of + * public OIDs using gssint_get_public_oid. */ +OM_uint32 +gssint_make_public_oid_set(OM_uint32 *minor_status, gss_OID oids, int count, + gss_OID_set *public_set) +{ + OM_uint32 status, tmpmin; + gss_OID_set set; + gss_OID public_oid; + int i; + + *public_set = GSS_C_NO_OID_SET; + + status = generic_gss_create_empty_oid_set(minor_status, &set); + if (GSS_ERROR(status)) + return status; + + for (i = 0; i < count; i++) { + public_oid = gssint_get_public_oid(&oids[i]); + if (public_oid == GSS_C_NO_OID) + continue; + status = generic_gss_add_oid_set_member(minor_status, + public_oid, &set); + if (GSS_ERROR(status)) { + (void) generic_gss_release_oid_set(&tmpmin, &set); + return status; + } + } + + *public_set = set; + return GSS_S_COMPLETE; +} + +/* + * Register a mechanism. Called with g_mechListLock held. + */ + +/* + * given the mechanism type, return the mechanism structure + * containing the mechanism library entry points. + * will return NULL if mech type is not found + * This function will also trigger the loading of the mechanism + * module if it has not been already loaded. + */ +gss_mechanism +gssint_get_mechanism(gss_const_OID oid) +{ + gss_mech_info aMech; + gss_mechanism (*sym)(const gss_OID); + struct plugin_file_handle *dl; + struct errinfo errinfo; + + if (gssint_mechglue_initialize_library() != 0) + return (NULL); + + k5_mutex_lock(&g_mechListLock); + + /* Check if the mechanism is already loaded. */ + aMech = g_mechList; + if (oid == GSS_C_NULL_OID) + oid = aMech->mech_type; + while (aMech != NULL) { + if (g_OID_equal(aMech->mech_type, oid) && aMech->mech) { + k5_mutex_unlock(&g_mechListLock); + return aMech->mech; + } else if (aMech->int_mech_type != GSS_C_NO_OID && + g_OID_equal(aMech->int_mech_type, oid)) { + k5_mutex_unlock(&g_mechListLock); + return aMech->int_mech; + } + aMech = aMech->next; + } + + /* + * might need to re-read the configuration file before loading + * the mechanism to ensure we have the latest info. + */ + updateMechList(); + + aMech = searchMechList(oid); + + /* is the mechanism present in the list ? */ + if (aMech == NULL) { + k5_mutex_unlock(&g_mechListLock); + return ((gss_mechanism)NULL); + } + + /* has another thread loaded the mech */ + if (aMech->mech) { + k5_mutex_unlock(&g_mechListLock); + return (aMech->mech); + } + + memset(&errinfo, 0, sizeof(errinfo)); + + if (krb5int_open_plugin(aMech->uLibName, &dl, &errinfo) != 0 || + errinfo.code != 0) { +#if 0 + (void) syslog(LOG_INFO, "libgss dlopen(%s): %s\n", + aMech->uLibName, dlerror()); +#endif + k5_mutex_unlock(&g_mechListLock); + return ((gss_mechanism)NULL); + } + + if (krb5int_get_plugin_func(dl, MECH_SYM, (void (**)())&sym, + &errinfo) == 0) { + /* Call the symbol to get the mechanism table */ + aMech->mech = (*sym)(aMech->mech_type); + } else { + /* Try dynamic dispatch table */ + aMech->mech = build_dynamicMech(dl, aMech->mech_type); + aMech->freeMech = 1; + } + if (aMech->mech == NULL) { + (void) krb5int_close_plugin(dl); +#if 0 + (void) syslog(LOG_INFO, "unable to initialize mechanism" + " library [%s]\n", aMech->uLibName); +#endif + k5_mutex_unlock(&g_mechListLock); + return ((gss_mechanism)NULL); + } + + aMech->dl_handle = dl; + + k5_mutex_unlock(&g_mechListLock); + return (aMech->mech); +} /* gssint_get_mechanism */ + +/* + * this routine is used for searching the list of mechanism data. + * + * this needs to be called with g_mechListLock held. + */ +static gss_mech_info searchMechList(gss_const_OID oid) +{ + gss_mech_info aMech = g_mechList; + + /* if oid is null -> then get default which is the first in the list */ + if (oid == GSS_C_NULL_OID) + return (aMech); + + while (aMech != NULL) { + if (g_OID_equal(aMech->mech_type, oid)) + return (aMech); + aMech = aMech->next; + } + + /* none found */ + return ((gss_mech_info) NULL); +} /* searchMechList */ + +/* Return the first non-whitespace character starting from str. */ +static char * +skip_whitespace(char *str) +{ + while (isspace(*str)) + str++; + return str; +} + +/* Truncate str at the first whitespace character and return the first + * non-whitespace character after that point. */ +static char * +delimit_ws(char *str) +{ + while (*str != '\0' && !isspace(*str)) + str++; + if (*str != '\0') + *str++ = '\0'; + return skip_whitespace(str); +} + +/* Truncate str at the first occurrence of delimiter and return the first + * non-whitespace character after that point. */ +static char * +delimit(char *str, char delimiter) +{ + while (*str != '\0' && *str != delimiter) + str++; + if (*str != '\0') + *str++ = '\0'; + return skip_whitespace(str); +} + +/* + * loads the configuration file + * this is called while having a mutex lock on the mechanism list + * entries for libraries that have been loaded can't be modified + * mechNameStr and mech_type fields are not updated during updates + */ +static void +loadConfigFile(const char *fileName) +{ + char *sharedLib, *kernMod, *modOptions, *modType, *oid, *next; + char buffer[BUFSIZ], *oidStr; + FILE *confFile; + + if ((confFile = fopen(fileName, "r")) == NULL) { + return; + } + + (void) memset(buffer, 0, sizeof (buffer)); + while (fgets(buffer, BUFSIZ, confFile) != NULL) { + + /* ignore lines beginning with # */ + if (*buffer == '#') + continue; + + /* Parse out the name, oid, and shared library path. */ + oidStr = buffer; + oid = delimit_ws(oidStr); + if (*oid == '\0') + continue; + sharedLib = delimit_ws(oid); + if (*sharedLib == '\0') + continue; + next = delimit_ws(sharedLib); + + /* Parse out the kernel module name if present. */ + if (*next != '\0' && *next != '[' && *next != '<') { + kernMod = next; + next = delimit_ws(kernMod); + } else { + kernMod = NULL; + } + + /* Parse out the module options if present. */ + if (*next == '[') { + modOptions = next + 1; + next = delimit(modOptions, ']'); + } else { + modOptions = NULL; + } + + /* Parse out the module type if present. */ + if (*next == '<') { + modType = next + 1; + (void)delimit(modType, '>'); + } else { + modType = NULL; + } + + addConfigEntry(oidStr, oid, sharedLib, kernMod, modOptions, + modType); + } /* while */ + (void) fclose(confFile); +} /* loadConfigFile */ + +#if defined(_WIN32) + +static time_t +filetimeToTimet(const FILETIME *ft) +{ + ULARGE_INTEGER ull; + + ull.LowPart = ft->dwLowDateTime; + ull.HighPart = ft->dwHighDateTime; + return (time_t)(ull.QuadPart / 10000000ULL - 11644473600ULL); +} + +static time_t +getRegConfigModTime(const char *keyPath) +{ + time_t currentUserModTime = getRegKeyModTime(HKEY_CURRENT_USER, + keyPath); + time_t localMachineModTime = getRegKeyModTime(HKEY_LOCAL_MACHINE, + keyPath); + + return currentUserModTime > localMachineModTime ? currentUserModTime : + localMachineModTime; +} + +static time_t +getRegKeyModTime(HKEY hBaseKey, const char *keyPath) +{ + HKEY hConfigKey; + HRESULT rc; + int iSubKey = 0; + time_t modTime = 0, keyModTime; + FILETIME keyLastWriteTime; + char subKeyName[256]; + + if ((rc = RegOpenKeyEx(hBaseKey, keyPath, 0, KEY_ENUMERATE_SUB_KEYS, + &hConfigKey)) != ERROR_SUCCESS) { + /* TODO: log error message */ + return 0; + } + do { + int subKeyNameSize=sizeof(subKeyName)/sizeof(subKeyName[0]); + if ((rc = RegEnumKeyEx(hConfigKey, iSubKey++, subKeyName, + &subKeyNameSize, NULL, NULL, NULL, + &keyLastWriteTime)) != ERROR_SUCCESS) { + break; + } + keyModTime = filetimeToTimet(&keyLastWriteTime); + if (modTime < keyModTime) { + modTime = keyModTime; + } + } while (1); + RegCloseKey(hConfigKey); + return modTime; +} + +static void +getRegKeyValue(HKEY hKey, const char *keyPath, const char *valueName, + void **data, DWORD* dataLen) +{ + DWORD sizeRequired=*dataLen; + HRESULT hr; + /* Get data length required */ + if ((hr = RegGetValue(hKey, keyPath, valueName, RRF_RT_REG_SZ, NULL, + NULL, &sizeRequired)) != ERROR_SUCCESS) { + /* TODO: LOG registry error */ + return; + } + /* adjust data buffer size if necessary */ + if (*dataLen < sizeRequired) { + *dataLen = sizeRequired; + *data = realloc(*data, sizeRequired); + if (!*data) { + *dataLen = 0; + /* TODO: LOG OOM ERROR! */ + return; + } + } + /* get data */ + if ((hr = RegGetValue(hKey, keyPath, valueName, RRF_RT_REG_SZ, NULL, + *data, &sizeRequired)) != ERROR_SUCCESS) { + /* LOG registry error */ + return; + } +} + +static void +loadConfigFromRegistry(HKEY hBaseKey, const char *keyPath) +{ + HKEY hConfigKey; + DWORD iSubKey, nSubKeys, maxSubKeyNameLen, modTypeLen; + char *oidStr = NULL, *oid = NULL, *sharedLib = NULL, *kernMod = NULL; + char *modOptions = NULL, *modType = NULL; + DWORD oidStrLen = 0, oidLen = 0, sharedLibLen = 0, kernModLen = 0; + DWORD modOptionsLen = 0; + HRESULT rc; + + if ((rc = RegOpenKeyEx(hBaseKey, keyPath, 0, + KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE, + &hConfigKey)) != ERROR_SUCCESS) { + /* TODO: log registry error */ + return; + } + + if ((rc = RegQueryInfoKey(hConfigKey, + NULL, /* lpClass */ + NULL, /* lpcClass */ + NULL, /* lpReserved */ + &nSubKeys, + &maxSubKeyNameLen, + NULL, /* lpcMaxClassLen */ + NULL, /* lpcValues */ + NULL, /* lpcMaxValueNameLen */ + NULL, /* lpcMaxValueLen */ + NULL, /* lpcbSecurityDescriptor */ + NULL /* lpftLastWriteTime */ )) != ERROR_SUCCESS) { + goto cleanup; + } + oidStr = malloc(++maxSubKeyNameLen); + if (!oidStr) { + goto cleanup; + } + for (iSubKey=0; iSubKey<nSubKeys; iSubKey++) { + oidStrLen = maxSubKeyNameLen; + if ((rc = RegEnumKeyEx(hConfigKey, iSubKey, oidStr, &oidStrLen, + NULL, NULL, NULL, NULL)) != + ERROR_SUCCESS) { + /* TODO: log registry error */ + continue; + } + getRegKeyValue(hConfigKey, oidStr, "OID", &oid, &oidLen); + getRegKeyValue(hConfigKey, oidStr, "Shared Library", + &sharedLib, &sharedLibLen); + getRegKeyValue(hConfigKey, oidStr, "Kernel Module", &kernMod, + &kernModLen); + getRegKeyValue(hConfigKey, oidStr, "Options", &modOptions, + &modOptionsLen); + getRegKeyValue(hConfigKey, oidStr, "Type", &modType, + &modTypeLen); + addConfigEntry(oidStr, oid, sharedLib, kernMod, modOptions, + modType); + } +cleanup: + RegCloseKey(hConfigKey); + if (oidStr) { + free(oidStr); + } + if (oid) { + free(oid); + } + if (sharedLib) { + free(sharedLib); + } + if (kernMod) { + free(kernMod); + } + if (modOptions) { + free(modOptions); + } +} +#endif + +static void +addConfigEntry(const char *oidStr, const char *oid, const char *sharedLib, + const char *kernMod, const char *modOptions, + const char *modType) +{ +#if defined(_WIN32) + const char *sharedPath; +#else + char sharedPath[sizeof (MECH_LIB_PREFIX) + BUFSIZ]; +#endif + char *tmpStr; + gss_OID mechOid; + gss_mech_info aMech, tmp; + OM_uint32 minor; + gss_buffer_desc oidBuf; + + if ((!oid) || (!oidStr)) { + return; + } + /* + * check if an entry for this oid already exists + * if it does, and the library is already loaded then + * we can't modify it, so skip it + */ + oidBuf.value = (void *)oid; + oidBuf.length = strlen(oid); + if (generic_gss_str_to_oid(&minor, &oidBuf, &mechOid) + != GSS_S_COMPLETE) { +#if 0 + (void) syslog(LOG_INFO, "invalid mechanism oid" + " [%s] in configuration file", oid); +#endif + return; + } + + aMech = searchMechList(mechOid); + if (aMech && aMech->mech) { + generic_gss_release_oid(&minor, &mechOid); + return; + } + + /* + * If that's all, then this is a corrupt entry. Skip it. + */ + if (! *sharedLib) { + generic_gss_release_oid(&minor, &mechOid); + return; + } +#if defined(_WIN32) + sharedPath = sharedLib; +#else + if (sharedLib[0] == '/') + snprintf(sharedPath, sizeof(sharedPath), "%s", sharedLib); + else + snprintf(sharedPath, sizeof(sharedPath), "%s%s", + MECH_LIB_PREFIX, sharedLib); +#endif + /* + * are we creating a new mechanism entry or + * just modifying existing (non loaded) mechanism entry + */ + if (aMech) { + /* + * delete any old values and set new + * mechNameStr and mech_type are not modified + */ + if (aMech->kmodName) { + free(aMech->kmodName); + aMech->kmodName = NULL; + } + + if (aMech->optionStr) { + free(aMech->optionStr); + aMech->optionStr = NULL; + } + + if ((tmpStr = strdup(sharedPath)) != NULL) { + if (aMech->uLibName) + free(aMech->uLibName); + aMech->uLibName = tmpStr; + } + + if (kernMod) /* this is an optional parameter */ + aMech->kmodName = strdup(kernMod); + + if (modOptions) /* optional module options */ + aMech->optionStr = strdup(modOptions); + + /* the oid is already set */ + generic_gss_release_oid(&minor, &mechOid); + return; + } + + /* adding a new entry */ + aMech = calloc(1, sizeof (struct gss_mech_config)); + if (aMech == NULL) { + generic_gss_release_oid(&minor, &mechOid); + return; + } + aMech->mech_type = mechOid; + aMech->uLibName = strdup(sharedPath); + aMech->mechNameStr = strdup(oidStr); + aMech->freeMech = 0; + + /* check if any memory allocations failed - bad news */ + if (aMech->uLibName == NULL || aMech->mechNameStr == NULL) { + if (aMech->uLibName) + free(aMech->uLibName); + if (aMech->mechNameStr) + free(aMech->mechNameStr); + generic_gss_release_oid(&minor, &mechOid); + free(aMech); + return; + } + if (kernMod) /* this is an optional parameter */ + aMech->kmodName = strdup(kernMod); + + if (modOptions) + aMech->optionStr = strdup(modOptions); + + if (modType && strcmp(modType, "interposer") == 0) + aMech->is_interposer = 1; + + /* + * add the new entry to the end of the list - make sure + * that only complete entries are added because other + * threads might currently be searching the list. + */ + tmp = g_mechListTail; + g_mechListTail = aMech; + + if (tmp != NULL) + tmp->next = aMech; + + if (g_mechList == NULL) + g_mechList = aMech; +} + diff --git a/src/lib/gssapi/mechglue/g_inq_context.c b/src/lib/gssapi/mechglue/g_inq_context.c new file mode 100644 index 000000000000..6f1c71eede9d --- /dev/null +++ b/src/lib/gssapi/mechglue/g_inq_context.c @@ -0,0 +1,168 @@ +/* #pragma ident "@(#)g_inquire_context.c 1.15 04/02/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_inquire_context + */ + +#include "mglueP.h" +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +static OM_uint32 +val_inq_ctx_args( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_name_t *src_name, + gss_name_t *targ_name, + OM_uint32 *lifetime_rec, + gss_OID *mech_type, + OM_uint32 *ctx_flags, + int *locally_initiated, + int *opened) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (src_name != NULL) + *src_name = GSS_C_NO_NAME; + + if (targ_name != NULL) + *targ_name = GSS_C_NO_NAME; + + if (mech_type != NULL) + *mech_type = GSS_C_NO_OID; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (context_handle == GSS_C_NO_CONTEXT) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT); + + return (GSS_S_COMPLETE); +} + + +/* Last argument new for V2 */ +OM_uint32 KRB5_CALLCONV +gss_inquire_context( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_name_t *src_name, + gss_name_t *targ_name, + OM_uint32 *lifetime_rec, + gss_OID *mech_type, + OM_uint32 *ctx_flags, + int *locally_initiated, + int *opened) +{ + gss_union_ctx_id_t ctx; + gss_mechanism mech; + OM_uint32 status, temp_minor; + gss_OID actual_mech; + gss_name_t localTargName = NULL, localSourceName = NULL; + + status = val_inq_ctx_args(minor_status, + context_handle, + src_name, targ_name, + lifetime_rec, + mech_type, ctx_flags, + locally_initiated, opened); + if (status != GSS_S_COMPLETE) + return (status); + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + ctx = (gss_union_ctx_id_t) context_handle; + mech = gssint_get_mechanism (ctx->mech_type); + + if (!mech || !mech->gss_inquire_context || !mech->gss_display_name || + !mech->gss_release_name) { + return (GSS_S_UNAVAILABLE); + } + + status = mech->gss_inquire_context( + minor_status, + ctx->internal_ctx_id, + (src_name ? &localSourceName : NULL), + (targ_name ? &localTargName : NULL), + lifetime_rec, + &actual_mech, + ctx_flags, + locally_initiated, + opened); + + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + return status; + } + + /* need to convert names */ + + if (src_name) { + if (localSourceName) { + status = gssint_convert_name_to_union_name(minor_status, mech, + localSourceName, src_name); + + if (status != GSS_S_COMPLETE) { + if (localTargName) + mech->gss_release_name(&temp_minor, &localTargName); + return (status); + } + } else { + *src_name = GSS_C_NO_NAME; + } + } + + if (targ_name) { + if (localTargName) { + status = gssint_convert_name_to_union_name(minor_status, mech, + localTargName, targ_name); + + if (status != GSS_S_COMPLETE) { + if (src_name) + (void) gss_release_name(&temp_minor, src_name); + + return (status); + } + } + else { + *targ_name = GSS_C_NO_NAME; + } + } + + if (mech_type) + *mech_type = gssint_get_public_oid(actual_mech); + + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/mechglue/g_inq_context_oid.c b/src/lib/gssapi/mechglue/g_inq_context_oid.c new file mode 100644 index 000000000000..ebdeaaee88b2 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_inq_context_oid.c @@ -0,0 +1,68 @@ +/* + * Copyright 2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* Glue routine for gss_inquire_sec_context_by_oid */ + +#include "mglueP.h" + +OM_uint32 KRB5_CALLCONV +gss_inquire_sec_context_by_oid (OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + OM_uint32 status; + gss_union_ctx_id_t ctx; + gss_mechanism mech; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (context_handle == GSS_C_NO_CONTEXT) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT; + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + ctx = (gss_union_ctx_id_t) context_handle; + mech = gssint_get_mechanism (ctx->mech_type); + + if (mech != NULL) { + if (mech->gss_inquire_sec_context_by_oid != NULL) { + status = mech->gss_inquire_sec_context_by_oid(minor_status, + ctx->internal_ctx_id, + desired_object, + data_set); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } else + status = GSS_S_UNAVAILABLE; + + return status; + } + + return GSS_S_BAD_MECH; +} diff --git a/src/lib/gssapi/mechglue/g_inq_cred.c b/src/lib/gssapi/mechglue/g_inq_cred.c new file mode 100644 index 000000000000..911196264f1f --- /dev/null +++ b/src/lib/gssapi/mechglue/g_inq_cred.c @@ -0,0 +1,233 @@ +/* #pragma ident "@(#)g_inquire_cred.c 1.16 04/02/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_inquire_cred + */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <time.h> + +OM_uint32 KRB5_CALLCONV +gss_inquire_cred(minor_status, + cred_handle, + name, + lifetime, + cred_usage, + mechanisms) + +OM_uint32 * minor_status; +gss_cred_id_t cred_handle; +gss_name_t * name; +OM_uint32 * lifetime; +int * cred_usage; +gss_OID_set * mechanisms; + +{ + OM_uint32 status, temp_minor_status; + gss_union_cred_t union_cred; + gss_mechanism mech; + gss_cred_id_t mech_cred; + gss_name_t mech_name; + gss_OID_set mechs = NULL; + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (name != NULL) + *name = GSS_C_NO_NAME; + + if (mechanisms != NULL) + *mechanisms = GSS_C_NO_OID_SET; + + /* Validate arguments. */ + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + /* + * XXX We should iterate over all mechanisms in the credential and + * aggregate the results. This requires a union name structure containing + * multiple mechanism names, which we don't currently have. For now, + * inquire the first mechanism in the credential; this is consistent with + * our historical behavior. + */ + + /* Determine mechanism and mechanism credential. */ + if (cred_handle != GSS_C_NO_CREDENTIAL) { + union_cred = (gss_union_cred_t) cred_handle; + if (union_cred->count <= 0) + return (GSS_S_DEFECTIVE_CREDENTIAL); + mech_cred = union_cred->cred_array[0]; + mech = gssint_get_mechanism(&union_cred->mechs_array[0]); + } else { + union_cred = NULL; + mech_cred = GSS_C_NO_CREDENTIAL; + mech = gssint_get_mechanism(GSS_C_NULL_OID); + } + + /* Skip the call into the mech if the caller doesn't care about any of the + * values we would ask for. */ + if (name != NULL || lifetime != NULL || cred_usage != NULL) { + if (mech == NULL) + return (GSS_S_DEFECTIVE_CREDENTIAL); + if (!mech->gss_inquire_cred) + return (GSS_S_UNAVAILABLE); + + status = mech->gss_inquire_cred(minor_status, mech_cred, + name ? &mech_name : NULL, + lifetime, cred_usage, NULL); + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + return(status); + } + + if (name) { + /* Convert mech_name into a union_name equivalent. */ + status = gssint_convert_name_to_union_name(&temp_minor_status, + mech, mech_name, name); + if (status != GSS_S_COMPLETE) { + *minor_status = temp_minor_status; + map_error(minor_status, mech); + return (status); + } + } + } + + /* + * copy the mechanism set in union_cred into an OID set and return in + * the mechanisms parameter. + */ + + if(mechanisms != NULL) { + if (union_cred) { + status = gssint_make_public_oid_set(minor_status, + union_cred->mechs_array, + union_cred->count, &mechs); + if (GSS_ERROR(status)) + goto error; + } else { + status = gss_create_empty_oid_set(minor_status, &mechs); + if (GSS_ERROR(status)) + goto error; + + status = gss_add_oid_set_member(minor_status, + &mech->mech_type, &mechs); + if (GSS_ERROR(status)) + goto error; + } + *mechanisms = mechs; + } + + return(GSS_S_COMPLETE); + +error: + if (mechs != NULL) + (void) gss_release_oid_set(&temp_minor_status, &mechs); + + if (name && *name != NULL) + (void) gss_release_name(&temp_minor_status, name); + + return (status); +} + +OM_uint32 KRB5_CALLCONV +gss_inquire_cred_by_mech(minor_status, cred_handle, mech_type, name, + initiator_lifetime, acceptor_lifetime, cred_usage) + OM_uint32 *minor_status; + gss_cred_id_t cred_handle; + gss_OID mech_type; + gss_name_t *name; + OM_uint32 *initiator_lifetime; + OM_uint32 *acceptor_lifetime; + gss_cred_usage_t *cred_usage; +{ + gss_union_cred_t union_cred; + gss_cred_id_t mech_cred; + gss_mechanism mech; + OM_uint32 status, temp_minor_status; + gss_name_t internal_name; + gss_OID selected_mech, public_mech; + + if (minor_status != NULL) + *minor_status = 0; + + if (name != NULL) + *name = GSS_C_NO_NAME; + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + status = gssint_select_mech_type(minor_status, mech_type, &selected_mech); + if (status != GSS_S_COMPLETE) + return (status); + + mech = gssint_get_mechanism(selected_mech); + if (!mech) + return (GSS_S_BAD_MECH); + if (!mech->gss_inquire_cred_by_mech) + return (GSS_S_BAD_BINDINGS); + + union_cred = (gss_union_cred_t) cred_handle; + mech_cred = gssint_get_mechanism_cred(union_cred, selected_mech); + +#if 0 + if (mech_cred == NULL) + return (GSS_S_DEFECTIVE_CREDENTIAL); +#endif + + public_mech = gssint_get_public_oid(selected_mech); + status = mech->gss_inquire_cred_by_mech(minor_status, + mech_cred, public_mech, + name ? &internal_name : NULL, + initiator_lifetime, + acceptor_lifetime, cred_usage); + + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + return (status); + } + + if (name) { + /* + * Convert internal_name into a union_name equivalent. + */ + status = gssint_convert_name_to_union_name( + &temp_minor_status, mech, + internal_name, name); + if (status != GSS_S_COMPLETE) { + *minor_status = temp_minor_status; + map_error(minor_status, mech); + return (status); + } + } + + return (GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/mechglue/g_inq_cred_oid.c b/src/lib/gssapi/mechglue/g_inq_cred_oid.c new file mode 100644 index 000000000000..4c23dfcbd364 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_inq_cred_oid.c @@ -0,0 +1,133 @@ +/* + * Copyright 2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* Glue routine for gss_inquire_cred_by_oid */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <time.h> + +static OM_uint32 append_to_buffer_set(OM_uint32 *minor_status, + gss_buffer_set_t *dst, + const gss_buffer_set_t src) +{ + size_t i; + OM_uint32 status; + + if (src == GSS_C_NO_BUFFER_SET) + return GSS_S_COMPLETE; + + if (*dst == GSS_C_NO_BUFFER_SET) { + status = gss_create_empty_buffer_set(minor_status, dst); + if (status != GSS_S_COMPLETE) + return status; + } + + status = GSS_S_COMPLETE; + + for (i = 0; i < src->count; i++) { + status = gss_add_buffer_set_member(minor_status, + &src->elements[i], + dst); + if (status != GSS_S_COMPLETE) + break; + } + + return status; +} + +OM_uint32 KRB5_CALLCONV +gss_inquire_cred_by_oid(OM_uint32 *minor_status, + const gss_cred_id_t cred_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + gss_union_cred_t union_cred; + gss_mechanism mech; + int i; + gss_buffer_set_t union_set = GSS_C_NO_BUFFER_SET; + gss_buffer_set_t ret_set = GSS_C_NO_BUFFER_SET; + OM_uint32 status, minor; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (cred_handle == GSS_C_NO_CREDENTIAL) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED; + + *minor_status = 0; + *data_set = GSS_C_NO_BUFFER_SET; + + union_cred = (gss_union_cred_t) cred_handle; + + status = gss_create_empty_buffer_set(minor_status, &ret_set); + if (status != GSS_S_COMPLETE) { + return status; + } + + status = GSS_S_UNAVAILABLE; + + for (i = 0; i < union_cred->count; i++) { + mech = gssint_get_mechanism(&union_cred->mechs_array[i]); + if (mech == NULL) { + status = GSS_S_BAD_MECH; + break; + } + + if (mech->gss_inquire_cred_by_oid == NULL) { + status = GSS_S_UNAVAILABLE; + continue; + } + + status = (mech->gss_inquire_cred_by_oid)(minor_status, + union_cred->cred_array[i], + desired_object, + &ret_set); + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + continue; + } + + if (union_cred->count == 1) { + union_set = ret_set; + break; + } + + status = append_to_buffer_set(minor_status, &union_set, ret_set); + gss_release_buffer_set(&minor, &ret_set); + if (status != GSS_S_COMPLETE) + break; + } + + if (status != GSS_S_COMPLETE) + gss_release_buffer_set(&minor, &union_set); + + *data_set = union_set; + + return status; +} diff --git a/src/lib/gssapi/mechglue/g_inq_name.c b/src/lib/gssapi/mechglue/g_inq_name.c new file mode 100644 index 000000000000..60a3b54e7990 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_inq_name.c @@ -0,0 +1,97 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* Glue routine for gss_inquire_name */ + +#include "mglueP.h" + +OM_uint32 KRB5_CALLCONV +gss_inquire_name(OM_uint32 *minor_status, + gss_name_t name, + int *name_is_MN, + gss_OID *MN_mech, + gss_buffer_set_t *attrs) +{ + OM_uint32 status, tmp; + gss_union_name_t union_name; + gss_mechanism mech; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (name == GSS_C_NO_NAME) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + + if (MN_mech != NULL) + *MN_mech = GSS_C_NO_OID; + + if (attrs != NULL) + *attrs = GSS_C_NO_BUFFER_SET; + + *minor_status = 0; + union_name = (gss_union_name_t)name; + + if (union_name->mech_type == GSS_C_NO_OID) { + /* We don't yet support non-mechanism attributes */ + if (name_is_MN != NULL) + *name_is_MN = 0; + *minor_status = 0; + return GSS_S_COMPLETE; + } + + if (name_is_MN != NULL) + *name_is_MN = 1; + + if (MN_mech != NULL) { + status = generic_gss_copy_oid(minor_status, + union_name->mech_type, + MN_mech); + if (GSS_ERROR(status)) + return status; + } + + mech = gssint_get_mechanism(name->mech_type); + if (mech == NULL) { + gss_release_oid(&tmp, MN_mech); + return GSS_S_BAD_NAME; + } + + if (mech->gss_inquire_name == NULL) { + gss_release_oid(&tmp, MN_mech); + return GSS_S_UNAVAILABLE; + } + + status = (*mech->gss_inquire_name)(minor_status, + union_name->mech_name, + NULL, + NULL, + attrs); + if (status != GSS_S_COMPLETE) { + generic_gss_release_oid(&tmp, MN_mech); + map_error(minor_status, mech); + } + + return status; +} diff --git a/src/lib/gssapi/mechglue/g_inq_names.c b/src/lib/gssapi/mechglue/g_inq_names.c new file mode 100644 index 000000000000..d22af8bcf958 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_inq_names.c @@ -0,0 +1,168 @@ +/* #pragma ident "@(#)g_inquire_names.c 1.16 04/02/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_inquire_context + */ + +#include "mglueP.h" + +#define MAX_MECH_OID_PAIRS 32 + +/* Last argument new for V2 */ +OM_uint32 KRB5_CALLCONV +gss_inquire_names_for_mech(minor_status, mechanism, name_types) + +OM_uint32 * minor_status; +gss_OID mechanism; +gss_OID_set * name_types; + +{ + OM_uint32 status; + gss_OID selected_mech = GSS_C_NO_OID, public_mech; + gss_mechanism mech; + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (name_types != NULL) + *name_types = GSS_C_NO_OID_SET; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (name_types == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + status = gssint_select_mech_type(minor_status, mechanism, + &selected_mech); + if (status != GSS_S_COMPLETE) + return (status); + + mech = gssint_get_mechanism(selected_mech); + if (mech == NULL) + return GSS_S_BAD_MECH; + else if (mech->gss_inquire_names_for_mech == NULL) + return GSS_S_UNAVAILABLE; + public_mech = gssint_get_public_oid(selected_mech); + status = mech->gss_inquire_names_for_mech(minor_status, public_mech, + name_types); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + + return status; +} + +static OM_uint32 +val_inq_mechs4name_args( + OM_uint32 *minor_status, + const gss_name_t input_name, + gss_OID_set *mech_set) +{ + + /* Initialize outputs. */ + if (minor_status != NULL) + *minor_status = 0; + + if (mech_set != NULL) + *mech_set = GSS_C_NO_OID_SET; + + /* Validate arguments.e + */ + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (input_name == GSS_C_NO_NAME) + return (GSS_S_BAD_NAME); + + return (GSS_S_COMPLETE); +} + +static int +mech_supports_nametype(gss_OID mech_oid, gss_OID name_type) +{ + OM_uint32 status, minor; + gss_OID_set types = GSS_C_NO_OID_SET; + int present; + + status = gss_inquire_names_for_mech(&minor, mech_oid, &types); + if (status != GSS_S_COMPLETE) + return (0); + status = gss_test_oid_set_member(&minor, name_type, types, &present); + (void) gss_release_oid_set(&minor, &types); + return (status == GSS_S_COMPLETE && present); +} + +OM_uint32 KRB5_CALLCONV +gss_inquire_mechs_for_name(OM_uint32 *minor_status, + const gss_name_t input_name, gss_OID_set *mech_set) +{ + OM_uint32 status, tmpmin; + gss_OID_set all_mechs = GSS_C_NO_OID_SET; + gss_OID_set mechs = GSS_C_NO_OID_SET; + gss_OID mech_oid, name_type; + gss_buffer_desc name_buffer = GSS_C_EMPTY_BUFFER; + size_t i; + + status = val_inq_mechs4name_args(minor_status, input_name, mech_set); + if (status != GSS_S_COMPLETE) + return (status); + + status = gss_display_name(minor_status, input_name, &name_buffer, + &name_type); + if (status != GSS_S_COMPLETE) + goto cleanup; + status = gss_indicate_mechs(minor_status, &all_mechs); + if (status != GSS_S_COMPLETE) + goto cleanup; + status = gss_create_empty_oid_set(minor_status, &mechs); + if (status != GSS_S_COMPLETE) + goto cleanup; + for (i = 0; i < all_mechs->count; i++) { + mech_oid = &all_mechs->elements[i]; + if (mech_supports_nametype(mech_oid, name_type)) { + status = gss_add_oid_set_member(minor_status, mech_oid, &mechs); + if (status != GSS_S_COMPLETE) + goto cleanup; + } + } + + *mech_set = mechs; + mechs = GSS_C_NO_OID_SET; + +cleanup: + (void) gss_release_buffer(&tmpmin, &name_buffer); + (void) gss_release_oid_set(&tmpmin, &all_mechs); + (void) gss_release_oid_set(&tmpmin, &mechs); + return (status); +} diff --git a/src/lib/gssapi/mechglue/g_map_name_to_any.c b/src/lib/gssapi/mechglue/g_map_name_to_any.c new file mode 100644 index 000000000000..ebf49450f062 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_map_name_to_any.c @@ -0,0 +1,76 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* Glue routine for gss_map_name_to_any */ + +#include "mglueP.h" + +OM_uint32 KRB5_CALLCONV +gss_map_name_to_any(OM_uint32 *minor_status, + gss_name_t name, + int authenticated, + gss_buffer_t type_id, + gss_any_t *output) +{ + OM_uint32 status; + gss_union_name_t union_name; + gss_mechanism mech; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (name == GSS_C_NO_NAME) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + + if (type_id == GSS_C_NO_BUFFER) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (output == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor_status = 0; + + union_name = (gss_union_name_t)name; + + if (union_name->mech_type == GSS_C_NO_OID) + return GSS_S_UNAVAILABLE; + + mech = gssint_get_mechanism(name->mech_type); + if (mech == NULL) + return GSS_S_BAD_NAME; + + if (mech->gss_map_name_to_any == NULL) + return GSS_S_UNAVAILABLE; + + status = (*mech->gss_map_name_to_any)(minor_status, + union_name->mech_name, + authenticated, + type_id, + output); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + + return status; +} diff --git a/src/lib/gssapi/mechglue/g_mech_invoke.c b/src/lib/gssapi/mechglue/g_mech_invoke.c new file mode 100644 index 000000000000..0647cda96945 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_mech_invoke.c @@ -0,0 +1,72 @@ +/* + * Copyright 2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* Glue routine for gssspi_mech_invoke */ + +#include "mglueP.h" +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <errno.h> + +OM_uint32 KRB5_CALLCONV +gssspi_mech_invoke (OM_uint32 *minor_status, + const gss_OID desired_mech, + const gss_OID desired_object, + gss_buffer_t value) +{ + OM_uint32 status; + gss_OID selected_mech = GSS_C_NO_OID; + gss_mechanism mech; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor_status = 0; + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + status = gssint_select_mech_type(minor_status, desired_mech, + &selected_mech); + if (status != GSS_S_COMPLETE) + return status; + + mech = gssint_get_mechanism(selected_mech); + if (mech == NULL || mech->gssspi_mech_invoke == NULL) { + return GSS_S_BAD_MECH; + } + + status = mech->gssspi_mech_invoke(minor_status, + gssint_get_public_oid(selected_mech), + desired_object, + value); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + + return status; +} diff --git a/src/lib/gssapi/mechglue/g_mechattr.c b/src/lib/gssapi/mechglue/g_mechattr.c new file mode 100644 index 000000000000..e49651eb6ade --- /dev/null +++ b/src/lib/gssapi/mechglue/g_mechattr.c @@ -0,0 +1,222 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/mechglue/g_mechattr.c */ +/* + * Copyright (C) 2010 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 "mglueP.h" + +static int +testMechAttr(gss_const_OID attr, + gss_const_OID_set against) +{ + int present = 0; + OM_uint32 minor; + + if (GSS_ERROR(generic_gss_test_oid_set_member(&minor, attr, + (gss_OID_set)against, + &present))) + return 0; + + return present; +} + +/* + * Return TRUE iff all the elements of desired and none of the elements + * of except exist in available. + */ +static int +testMechAttrsOffered(gss_const_OID_set desired, + gss_const_OID_set except, + gss_const_OID_set available) +{ + size_t i; + + if (desired != GSS_C_NO_OID_SET) { + for (i = 0; i < desired->count; i++) { + if (!testMechAttr(&desired->elements[i], available)) + return 0; + } + } + + if (except != GSS_C_NO_OID_SET) { + for (i = 0; i < except->count; i++) { + if (testMechAttr(&except->elements[i], available)) + return 0; + } + } + + return 1; +} + +/* + * Return TRUE iff all the elements of critical exist in known. + */ +static int +testMechAttrsKnown(gss_const_OID_set critical, + gss_const_OID_set known) +{ + size_t i; + + if (critical != GSS_C_NO_OID_SET) { + for (i = 0; i < critical->count; i++) { + if (!testMechAttr(&critical->elements[i], known)) + return 0; + } + } + + return 1; +} + +OM_uint32 KRB5_CALLCONV +gss_indicate_mechs_by_attrs( + OM_uint32 *minor, + gss_const_OID_set desired_mech_attrs, + gss_const_OID_set except_mech_attrs, + gss_const_OID_set critical_mech_attrs, + gss_OID_set *mechs) +{ + OM_uint32 status, tmpMinor; + gss_OID_set allMechs = GSS_C_NO_OID_SET; + size_t i; + + if (minor == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor = 0; + + if (mechs == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *mechs = GSS_C_NO_OID_SET; + + status = gss_indicate_mechs(minor, &allMechs); + if (GSS_ERROR(status)) + goto cleanup; + + status = generic_gss_create_empty_oid_set(minor, mechs); + if (GSS_ERROR(status)) + goto cleanup; + + for (i = 0; i < allMechs->count; i++) { + gss_OID_set supportedAttrs = GSS_C_NO_OID_SET; + gss_OID_set knownAttrs = GSS_C_NO_OID_SET; + + status = gss_inquire_attrs_for_mech(minor, &allMechs->elements[i], + &supportedAttrs, &knownAttrs); + if (GSS_ERROR(status)) + continue; + + if (testMechAttrsOffered(desired_mech_attrs, + except_mech_attrs, supportedAttrs) && + testMechAttrsKnown(critical_mech_attrs, knownAttrs)) { + status = gss_add_oid_set_member(minor, &allMechs->elements[i], + mechs); + if (GSS_ERROR(status)) { + gss_release_oid_set(&tmpMinor, &supportedAttrs); + gss_release_oid_set(&tmpMinor, &knownAttrs); + goto cleanup; + } + } + + gss_release_oid_set(&tmpMinor, &supportedAttrs); + gss_release_oid_set(&tmpMinor, &knownAttrs); + } + + *minor = 0; + status = GSS_S_COMPLETE; + +cleanup: + gss_release_oid_set(&tmpMinor, &allMechs); + + return status; +} + +OM_uint32 KRB5_CALLCONV +gss_inquire_attrs_for_mech( + OM_uint32 *minor, + gss_const_OID mech_oid, + gss_OID_set *mech_attrs, + gss_OID_set *known_mech_attrs) +{ + OM_uint32 status, tmpMinor; + gss_OID selected_mech, public_mech; + gss_mechanism mech; + + if (minor == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor = 0; + + if (mech_attrs != NULL) + *mech_attrs = GSS_C_NO_OID_SET; + + if (known_mech_attrs != NULL) + *known_mech_attrs = GSS_C_NO_OID_SET; + + status = gssint_select_mech_type(minor, mech_oid, &selected_mech); + if (status != GSS_S_COMPLETE) + return status; + + mech = gssint_get_mechanism(selected_mech); + if (mech == NULL) + return GSS_S_BAD_MECH; + + /* If the mech does not implement RFC 5587, return success with an empty + * mech_attrs and known_mech_attrs. */ + if (mech->gss_inquire_attrs_for_mech == NULL) + return GSS_S_COMPLETE; + + public_mech = gssint_get_public_oid(selected_mech); + status = mech->gss_inquire_attrs_for_mech(minor, public_mech, mech_attrs, + known_mech_attrs); + if (GSS_ERROR(status)) { + map_error(minor, mech); + return status; + } + + if (known_mech_attrs != NULL && *known_mech_attrs == GSS_C_NO_OID_SET) { + status = generic_gss_copy_oid_set(minor, + gss_ma_known_attrs, + known_mech_attrs); + if (GSS_ERROR(status)) { + gss_release_oid_set(&tmpMinor, mech_attrs); + if (mech_attrs != NULL) + *mech_attrs = GSS_C_NO_OID_SET; + } + } + + return GSS_S_COMPLETE; +} + +OM_uint32 KRB5_CALLCONV +gss_display_mech_attr( + OM_uint32 *minor, + gss_const_OID mech_attr, + gss_buffer_t name, + gss_buffer_t short_desc, + gss_buffer_t long_desc) +{ + return generic_gss_display_mech_attr(minor, mech_attr, + name, short_desc, long_desc); +} diff --git a/src/lib/gssapi/mechglue/g_mechname.c b/src/lib/gssapi/mechglue/g_mechname.c new file mode 100644 index 000000000000..cfb0a0d2af8e --- /dev/null +++ b/src/lib/gssapi/mechglue/g_mechname.c @@ -0,0 +1,116 @@ +/* + * g_mechname.c --- registry of mechanism-specific name types + * + * This file contains a registry of mechanism-specific name types. It + * is used to determine which name types not should be lazy evaluated, + * but rather evaluated on the spot. + */ + +#include "mglueP.h" +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <errno.h> + +static gss_mech_spec_name name_list = NULL; + +/* + * generic searching helper function. + */ +static gss_mech_spec_name search_mech_spec(name_type) + gss_OID name_type; +{ + gss_mech_spec_name p; + + for (p = name_list; p; p = p->next) { + if (g_OID_equal(name_type, p->name_type)) + return p; + } + return NULL; +} + +/* + * Given a name_type, if it is specific to a mechanism, return the + * mechanism OID. Otherwise, return NULL. + */ +gss_OID gss_find_mechanism_from_name_type(name_type) + gss_OID name_type; +{ + gss_mech_spec_name p; + + p = search_mech_spec(name_type); + if (!p) + return NULL; + return p->mech; +} + +/* + * This function adds a (name_type, mechanism) pair to the + * mechanism-specific name type registry. If an entry for the + * name_type already exists, then zero out the mechanism entry. + * Otherwise, enter the pair into the registry. + */ +OM_uint32 +gss_add_mech_name_type(minor_status, name_type, mech) + OM_uint32 *minor_status; + gss_OID name_type; + gss_OID mech; +{ + OM_uint32 major_status, tmp; + gss_mech_spec_name p; + + p = search_mech_spec(name_type); + if (p) { + /* + * We found an entry for this name type; mark it as not being + * a mechanism-specific name type. + */ + if (p->mech) { + if (!g_OID_equal(mech, p->mech)) { + generic_gss_release_oid(minor_status, &p->mech); + p->mech = 0; + } + } + return GSS_S_COMPLETE; + } + p = malloc(sizeof(gss_mech_spec_name_desc)); + if (!p) { + *minor_status = ENOMEM; + map_errcode(minor_status); + goto allocation_failure; + } + p->name_type = 0; + p->mech = 0; + + major_status = generic_gss_copy_oid(minor_status, name_type, + &p->name_type); + if (major_status) { + map_errcode(minor_status); + goto allocation_failure; + } + major_status = generic_gss_copy_oid(minor_status, mech, + &p->mech); + if (major_status) { + map_errcode(minor_status); + goto allocation_failure; + } + + p->next = name_list; + p->prev = 0; + name_list = p; + + return GSS_S_COMPLETE; + +allocation_failure: + if (p) { + if (p->mech) + generic_gss_release_oid(&tmp, &p->mech); + if (p->name_type) + generic_gss_release_oid(&tmp, &p->name_type); + free(p); + } + return GSS_S_FAILURE; +} diff --git a/src/lib/gssapi/mechglue/g_oid_ops.c b/src/lib/gssapi/mechglue/g_oid_ops.c new file mode 100644 index 000000000000..1d75c63e69d7 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_oid_ops.c @@ -0,0 +1,112 @@ +/* #pragma ident "@(#)g_oid_ops.c 1.11 98/01/22 SMI" */ +/* lib/gssapi/mechglue/g_oid_ops.c - GSSAPI V2 interfaces to manipulate OIDs */ +/* + * Copyright 1995, 2007 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 "mglueP.h" + +/* + * gss_release_oid has been moved to g_initialize, becasue it requires access + * to the mechanism list. All functions requiring direct access to the + * mechanism list are now in g_initialize.c + */ + +OM_uint32 KRB5_CALLCONV +gss_create_empty_oid_set(minor_status, oid_set) + OM_uint32 *minor_status; + gss_OID_set *oid_set; +{ + OM_uint32 status; + status = generic_gss_create_empty_oid_set(minor_status, oid_set); + if (status != GSS_S_COMPLETE) + map_errcode(minor_status); + return status; +} + +OM_uint32 KRB5_CALLCONV +gss_add_oid_set_member(minor_status, member_oid, oid_set) + OM_uint32 *minor_status; + gss_OID member_oid; + gss_OID_set *oid_set; +{ + OM_uint32 status; + status = generic_gss_add_oid_set_member(minor_status, member_oid, oid_set); + if (status != GSS_S_COMPLETE) + map_errcode(minor_status); + return status; +} + +OM_uint32 KRB5_CALLCONV +gss_test_oid_set_member(minor_status, member, set, present) + OM_uint32 *minor_status; + gss_OID member; + gss_OID_set set; + int *present; +{ + return generic_gss_test_oid_set_member(minor_status, member, set, present); +} + +OM_uint32 KRB5_CALLCONV +gss_oid_to_str(minor_status, oid, oid_str) + OM_uint32 *minor_status; + gss_OID oid; + gss_buffer_t oid_str; +{ + OM_uint32 status = generic_gss_oid_to_str(minor_status, oid, oid_str); + if (status != GSS_S_COMPLETE) + map_errcode(minor_status); + return status; +} + +OM_uint32 KRB5_CALLCONV +gss_str_to_oid(minor_status, oid_str, oid) + OM_uint32 *minor_status; + gss_buffer_t oid_str; + gss_OID *oid; +{ + OM_uint32 status = generic_gss_str_to_oid(minor_status, oid_str, oid); + if (status != GSS_S_COMPLETE) + map_errcode(minor_status); + return status; +} + +OM_uint32 +gssint_copy_oid_set( + OM_uint32 *minor_status, + const gss_OID_set_desc * const oidset, + gss_OID_set *new_oidset) +{ + return generic_gss_copy_oid_set(minor_status, oidset, new_oidset); +} + +int KRB5_CALLCONV +gss_oid_equal( + gss_const_OID first_oid, + gss_const_OID second_oid) +{ + /* GSS_C_NO_OID doesn't match itself, per draft-josefsson-gss-capsulate. */ + if (first_oid == GSS_C_NO_OID || second_oid == GSS_C_NO_OID) + return 0; + return g_OID_equal(first_oid, second_oid); +} diff --git a/src/lib/gssapi/mechglue/g_prf.c b/src/lib/gssapi/mechglue/g_prf.c new file mode 100644 index 000000000000..fcca3e44c4c0 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_prf.c @@ -0,0 +1,81 @@ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* Glue routine for gss_pseudo_random */ + +#include "mglueP.h" + +OM_uint32 KRB5_CALLCONV +gss_pseudo_random (OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int prf_key, + const gss_buffer_t prf_in, + ssize_t desired_output_len, + gss_buffer_t prf_out) +{ + OM_uint32 status; + gss_union_ctx_id_t ctx; + gss_mechanism mech; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (context_handle == GSS_C_NO_CONTEXT) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT; + + if (prf_in == GSS_C_NO_BUFFER) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT; + + if (prf_out == GSS_C_NO_BUFFER) + return GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CONTEXT; + + prf_out->length = 0; + prf_out->value = NULL; + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + ctx = (gss_union_ctx_id_t) context_handle; + mech = gssint_get_mechanism (ctx->mech_type); + + if (mech != NULL) { + if (mech->gss_pseudo_random != NULL) { + status = mech->gss_pseudo_random(minor_status, + ctx->internal_ctx_id, + prf_key, + prf_in, + desired_output_len, + prf_out); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } else + status = GSS_S_UNAVAILABLE; + + return status; + } + + return GSS_S_BAD_MECH; +} diff --git a/src/lib/gssapi/mechglue/g_process_context.c b/src/lib/gssapi/mechglue/g_process_context.c new file mode 100644 index 000000000000..bc260aeb10b2 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_process_context.c @@ -0,0 +1,82 @@ +/* #pragma ident "@(#)g_process_context.c 1.12 98/01/22 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine gss_process_context + */ + +#include "mglueP.h" + +OM_uint32 KRB5_CALLCONV +gss_process_context_token (minor_status, + context_handle, + token_buffer) + +OM_uint32 * minor_status; +gss_ctx_id_t context_handle; +gss_buffer_t token_buffer; + +{ + OM_uint32 status; + gss_union_ctx_id_t ctx; + gss_mechanism mech; + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + *minor_status = 0; + + if (context_handle == GSS_C_NO_CONTEXT) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT); + + if (token_buffer == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_READ); + + if (GSS_EMPTY_BUFFER(token_buffer)) + return (GSS_S_CALL_INACCESSIBLE_READ); + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + ctx = (gss_union_ctx_id_t) context_handle; + mech = gssint_get_mechanism (ctx->mech_type); + + if (mech) { + + if (mech->gss_process_context_token) { + status = mech->gss_process_context_token( + minor_status, + ctx->internal_ctx_id, + token_buffer); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } else + status = GSS_S_UNAVAILABLE; + + return(status); + } + + return (GSS_S_BAD_MECH); +} diff --git a/src/lib/gssapi/mechglue/g_rel_buffer.c b/src/lib/gssapi/mechglue/g_rel_buffer.c new file mode 100644 index 000000000000..8c3328acc5a6 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_rel_buffer.c @@ -0,0 +1,58 @@ +/* #ident "@(#)g_rel_buffer.c 1.2 96/02/06 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_release_buffer + */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +OM_uint32 KRB5_CALLCONV +gss_release_buffer (minor_status, + buffer) + +OM_uint32 * minor_status; +gss_buffer_t buffer; +{ + if (minor_status) + *minor_status = 0; + + /* if buffer is NULL, return */ + + if(buffer == GSS_C_NO_BUFFER) + return(GSS_S_COMPLETE); + + if ((buffer->length) && + (buffer->value)) { + gssalloc_free(buffer->value); + buffer->length = 0; + buffer->value = NULL; + } + + return (GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/mechglue/g_rel_cred.c b/src/lib/gssapi/mechglue/g_rel_cred.c new file mode 100644 index 000000000000..ccdee05a5625 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_rel_cred.c @@ -0,0 +1,99 @@ +/* #pragma ident "@(#)g_rel_cred.c 1.14 04/02/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* Glue routine for gss_release_cred */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +OM_uint32 KRB5_CALLCONV +gss_release_cred(minor_status, + cred_handle) + +OM_uint32 * minor_status; +gss_cred_id_t * cred_handle; + +{ + OM_uint32 status, temp_status; + int j; + gss_union_cred_t union_cred; + gss_mechanism mech; + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + *minor_status = 0; + + if (cred_handle == NULL) + return (GSS_S_NO_CRED | GSS_S_CALL_INACCESSIBLE_READ); + + /* + * Loop through the union_cred struct, selecting the approprate + * underlying mechanism routine and calling it. At the end, + * release all of the storage taken by the union_cred struct. + */ + + union_cred = (gss_union_cred_t) *cred_handle; + if (union_cred == (gss_union_cred_t)GSS_C_NO_CREDENTIAL) + return (GSS_S_COMPLETE); + + if (GSSINT_CHK_LOOP(union_cred)) + return (GSS_S_NO_CRED | GSS_S_CALL_INACCESSIBLE_READ); + *cred_handle = NULL; + + status = GSS_S_COMPLETE; + + for(j=0; j < union_cred->count; j++) { + + mech = gssint_get_mechanism (&union_cred->mechs_array[j]); + + if (union_cred->mechs_array[j].elements) + free(union_cred->mechs_array[j].elements); + if (mech) { + if (mech->gss_release_cred) { + temp_status = mech->gss_release_cred + ( + minor_status, + &union_cred->cred_array[j]); + + if (temp_status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + status = GSS_S_NO_CRED; + } + + } else + status = GSS_S_UNAVAILABLE; + } else + status = GSS_S_DEFECTIVE_CREDENTIAL; + } + + free(union_cred->cred_array); + free(union_cred->mechs_array); + free(union_cred); + + return(status); +} diff --git a/src/lib/gssapi/mechglue/g_rel_name.c b/src/lib/gssapi/mechglue/g_rel_name.c new file mode 100644 index 000000000000..e00869238323 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_rel_name.c @@ -0,0 +1,86 @@ +/* #pragma ident "@(#)g_rel_name.c 1.11 04/02/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_release_name + */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> + +OM_uint32 KRB5_CALLCONV +gss_release_name (minor_status, + input_name) + +OM_uint32 * minor_status; +gss_name_t * input_name; + +{ + gss_union_name_t union_name; + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + *minor_status = 0; + + /* if input_name is NULL, return error */ + if (input_name == NULL) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME); + + if (*input_name == GSS_C_NO_NAME) + return GSS_S_COMPLETE; + + /* + * free up the space for the external_name and then + * free the union_name descriptor + */ + + union_name = (gss_union_name_t) *input_name; + if (GSSINT_CHK_LOOP(union_name)) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME); + *input_name = 0; + *minor_status = 0; + + if (union_name->name_type != GSS_C_NO_OID) + gss_release_oid(minor_status, &union_name->name_type); + + if (union_name->external_name != GSS_C_NO_BUFFER) { + if (union_name->external_name->value != NULL) + gssalloc_free(union_name->external_name->value); + free(union_name->external_name); + } + + if (union_name->mech_type) { + gssint_release_internal_name(minor_status, union_name->mech_type, + &union_name->mech_name); + gss_release_oid(minor_status, &union_name->mech_type); + } + + free(union_name); + + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/mechglue/g_rel_name_mapping.c b/src/lib/gssapi/mechglue/g_rel_name_mapping.c new file mode 100644 index 000000000000..f09136afee74 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_rel_name_mapping.c @@ -0,0 +1,74 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* Glue routine for gss_release_any_name_mapping */ + +#include "mglueP.h" + +OM_uint32 KRB5_CALLCONV +gss_release_any_name_mapping(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t type_id, + gss_any_t *input) +{ + OM_uint32 status; + gss_union_name_t union_name; + gss_mechanism mech; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (name == GSS_C_NO_NAME) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + + if (type_id == GSS_C_NO_BUFFER) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (input == NULL) + return GSS_S_CALL_INACCESSIBLE_READ; + + *minor_status = 0; + + union_name = (gss_union_name_t)name; + + if (union_name->mech_type == GSS_C_NO_OID) + return GSS_S_UNAVAILABLE; + + mech = gssint_get_mechanism(name->mech_type); + if (mech == NULL) + return GSS_S_BAD_NAME; + + if (mech->gss_release_any_name_mapping == NULL) + return GSS_S_UNAVAILABLE; + + status = (*mech->gss_release_any_name_mapping)(minor_status, + union_name->mech_name, + type_id, + input); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + + return status; +} diff --git a/src/lib/gssapi/mechglue/g_rel_oid_set.c b/src/lib/gssapi/mechglue/g_rel_oid_set.c new file mode 100644 index 000000000000..fa008d6bb9bb --- /dev/null +++ b/src/lib/gssapi/mechglue/g_rel_oid_set.c @@ -0,0 +1,43 @@ +/* #pragma ident "@(#)g_rel_oid_set.c 1.12 97/11/11 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_release_oid_set + */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +OM_uint32 KRB5_CALLCONV +gss_release_oid_set (minor_status, + set) + +OM_uint32 * minor_status; +gss_OID_set * set; +{ + return generic_gss_release_oid_set(minor_status, set); +} diff --git a/src/lib/gssapi/mechglue/g_saslname.c b/src/lib/gssapi/mechglue/g_saslname.c new file mode 100644 index 000000000000..48060c367172 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_saslname.c @@ -0,0 +1,220 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/mechglue/g_saslname.c */ +/* + * Copyright (C) 2010 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 "mglueP.h" +#include <krb5/krb5.h> + +static char basis_32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + +#define OID_SASL_NAME_LENGTH (sizeof("GS2-XXXXXXXXXXX") - 1) + +static OM_uint32 +oidToSaslName(OM_uint32 *minor, const gss_OID mech, + char sasl_name[OID_SASL_NAME_LENGTH + 1]) +{ + unsigned char derBuf[2]; + krb5_crypto_iov iov[3]; + unsigned char cksumBuf[20], *q = cksumBuf; + char *p = sasl_name; + + if (mech->length > 127) { + *minor = ERANGE; + return GSS_S_BAD_MECH; + } + + derBuf[0] = 0x06; + derBuf[1] = (unsigned char)mech->length; + + iov[0].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY; + iov[0].data.length = 2; + iov[0].data.data = (char *)derBuf; + iov[1].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY; + iov[1].data.length = mech->length; + iov[1].data.data = (char *)mech->elements; + iov[2].flags = KRB5_CRYPTO_TYPE_CHECKSUM; + iov[2].data.length = sizeof(cksumBuf); + iov[2].data.data = (char *)cksumBuf; + + *minor = krb5_k_make_checksum_iov(NULL, CKSUMTYPE_NIST_SHA, + NULL, 0, iov, 3); + if (*minor != 0) + return GSS_S_FAILURE; + + memcpy(p, "GS2-", 4); + p += 4; + + *p++ = basis_32[q[0] >> 3]; + *p++ = basis_32[((q[0] & 7) << 2) | (q[1] >> 6)]; + *p++ = basis_32[(q[1] & 0x3f) >> 1]; + *p++ = basis_32[((q[1] & 1) << 4) | (q[2] >> 4)]; + *p++ = basis_32[((q[2] & 0xf) << 1) | (q[3] >> 7)]; + *p++ = basis_32[(q[3] & 0x7f) >> 2]; + *p++ = basis_32[((q[3] & 3) << 3) | (q[4] >> 5)]; + *p++ = basis_32[(q[4] & 0x1f)]; + *p++ = basis_32[q[5] >> 3]; + *p++ = basis_32[((q[5] & 7) << 2) | (q[6] >> 6)]; + *p++ = basis_32[(q[6] & 0x3f) >> 1]; + + *p++ = '\0'; + + *minor = 0; + return GSS_S_COMPLETE; +} + +static OM_uint32 +oidToSaslNameAlloc(OM_uint32 *minor, const gss_OID mech, + gss_buffer_t sasl_name) +{ + OM_uint32 status, tmpMinor; + + sasl_name->value = malloc(OID_SASL_NAME_LENGTH + 1); + if (sasl_name->value == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + sasl_name->length = OID_SASL_NAME_LENGTH; + + status = oidToSaslName(minor, mech, (char *)sasl_name->value); + if (GSS_ERROR(status)) { + gss_release_buffer(&tmpMinor, sasl_name); + return status; + } + + return GSS_S_COMPLETE; +} + +OM_uint32 KRB5_CALLCONV gss_inquire_saslname_for_mech( + OM_uint32 *minor_status, + const gss_OID desired_mech, + gss_buffer_t sasl_mech_name, + gss_buffer_t mech_name, + gss_buffer_t mech_description) +{ + OM_uint32 status; + gss_OID selected_mech, public_mech; + gss_mechanism mech; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor_status = 0; + + if (sasl_mech_name != GSS_C_NO_BUFFER) { + sasl_mech_name->length = 0; + sasl_mech_name->value = NULL; + } + + if (mech_name != GSS_C_NO_BUFFER) { + mech_name->length = 0; + mech_name->value = NULL; + } + + if (mech_description != GSS_C_NO_BUFFER) { + mech_description->length = 0; + mech_description->value = NULL; + } + + status = gssint_select_mech_type(minor_status, desired_mech, + &selected_mech); + if (status != GSS_S_COMPLETE) + return status; + + mech = gssint_get_mechanism(desired_mech); + if (mech == NULL) { + return GSS_S_BAD_MECH; + } else if (mech->gss_inquire_saslname_for_mech == NULL) { + status = GSS_S_UNAVAILABLE; + } else { + public_mech = gssint_get_public_oid(selected_mech); + status = mech->gss_inquire_saslname_for_mech(minor_status, public_mech, + sasl_mech_name, mech_name, + mech_description); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } + + if (status == GSS_S_UNAVAILABLE) { + if (sasl_mech_name != GSS_C_NO_BUFFER) + status = oidToSaslNameAlloc(minor_status, desired_mech, + sasl_mech_name); + else + status = GSS_S_COMPLETE; + } + + return status; +} + +/* We cannot interpose this function as mech_type is an output parameter. */ +OM_uint32 KRB5_CALLCONV gss_inquire_mech_for_saslname( + OM_uint32 *minor_status, + const gss_buffer_t sasl_mech_name, + gss_OID *mech_type) +{ + OM_uint32 status, tmpMinor; + gss_OID_set mechSet = GSS_C_NO_OID_SET; + size_t i; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor_status = 0; + + if (mech_type != NULL) + *mech_type = GSS_C_NO_OID; + + status = gss_indicate_mechs(minor_status, &mechSet); + if (status != GSS_S_COMPLETE) + return status; + + for (i = 0, status = GSS_S_BAD_MECH; i < mechSet->count; i++) { + gss_mechanism mech; + char mappedName[OID_SASL_NAME_LENGTH + 1]; + + mech = gssint_get_mechanism(&mechSet->elements[i]); + if (mech != NULL && mech->gss_inquire_mech_for_saslname != NULL) { + status = mech->gss_inquire_mech_for_saslname(minor_status, + sasl_mech_name, + mech_type); + if (status == GSS_S_COMPLETE) + break; + } + if (status == GSS_S_BAD_MECH && + sasl_mech_name->length == OID_SASL_NAME_LENGTH && + oidToSaslName(&tmpMinor, &mechSet->elements[i], + mappedName) == GSS_S_COMPLETE && + memcmp(sasl_mech_name->value, mappedName, + OID_SASL_NAME_LENGTH) == 0) { + if (mech_type != NULL) + *mech_type = &mech->mech_type; + status = GSS_S_COMPLETE; + break; + } + } + + gss_release_oid_set(&tmpMinor, &mechSet); + + return status; +} diff --git a/src/lib/gssapi/mechglue/g_seal.c b/src/lib/gssapi/mechglue/g_seal.c new file mode 100644 index 000000000000..f17241c90891 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_seal.c @@ -0,0 +1,249 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_wrap + */ + +#include "mglueP.h" + +static OM_uint32 +val_wrap_args(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + gss_buffer_t input_message_buffer, + int *conf_state, + gss_buffer_t output_message_buffer) +{ + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_message_buffer != GSS_C_NO_BUFFER) { + output_message_buffer->length = 0; + output_message_buffer->value = NULL; + } + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (context_handle == GSS_C_NO_CONTEXT) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT); + + if (input_message_buffer == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_READ); + + if (output_message_buffer == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + return (GSS_S_COMPLETE); +} + +OM_uint32 KRB5_CALLCONV +gss_wrap( OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + gss_buffer_t input_message_buffer, + int *conf_state, + gss_buffer_t output_message_buffer) +{ + + /* EXPORT DELETE START */ + + OM_uint32 status; + gss_union_ctx_id_t ctx; + gss_mechanism mech; + + status = val_wrap_args(minor_status, context_handle, + conf_req_flag, qop_req, + input_message_buffer, conf_state, + output_message_buffer); + if (status != GSS_S_COMPLETE) + return (status); + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + ctx = (gss_union_ctx_id_t) context_handle; + mech = gssint_get_mechanism (ctx->mech_type); + + if (mech) { + if (mech->gss_wrap) { + status = mech->gss_wrap(minor_status, + ctx->internal_ctx_id, + conf_req_flag, + qop_req, + input_message_buffer, + conf_state, + output_message_buffer); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } else if (mech->gss_wrap_aead || + (mech->gss_wrap_iov && mech->gss_wrap_iov_length)) { + status = gssint_wrap_aead(mech, + minor_status, + ctx, + conf_req_flag, + (gss_qop_t)qop_req, + GSS_C_NO_BUFFER, + input_message_buffer, + conf_state, + output_message_buffer); + } else + status = GSS_S_UNAVAILABLE; + + return(status); + } + /* EXPORT DELETE END */ + + return (GSS_S_BAD_MECH); +} + +OM_uint32 KRB5_CALLCONV +gss_seal(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + int qop_req, + gss_buffer_t input_message_buffer, + int *conf_state, + gss_buffer_t output_message_buffer) +{ + + return gss_wrap(minor_status, context_handle, + conf_req_flag, (gss_qop_t) qop_req, + input_message_buffer, conf_state, + output_message_buffer); +} + +/* + * It is only possible to implement gss_wrap_size_limit() on top + * of gss_wrap_iov_length() for mechanisms that do not use any + * padding and have fixed length headers/trailers. + */ +static OM_uint32 +gssint_wrap_size_limit_iov_shim(gss_mechanism mech, + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + OM_uint32 req_output_size, + OM_uint32 *max_input_size) +{ + gss_iov_buffer_desc iov[4]; + OM_uint32 status; + OM_uint32 ohlen; + + 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_DATA; + iov[1].buffer.length = req_output_size; + iov[1].buffer.value = NULL; + + iov[2].type = GSS_IOV_BUFFER_TYPE_PADDING; + iov[2].buffer.value = NULL; + iov[2].buffer.length = 0; + + iov[3].type = GSS_IOV_BUFFER_TYPE_TRAILER; + iov[3].buffer.value = NULL; + iov[3].buffer.length = 0; + + assert(mech->gss_wrap_iov_length); + + status = mech->gss_wrap_iov_length(minor_status, context_handle, + conf_req_flag, qop_req, + NULL, iov, + sizeof(iov)/sizeof(iov[0])); + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + return status; + } + + ohlen = iov[0].buffer.length + iov[3].buffer.length; + + if (iov[2].buffer.length == 0 && ohlen < req_output_size) + *max_input_size = req_output_size - ohlen; + else + *max_input_size = 0; + + return GSS_S_COMPLETE; +} + +/* + * New for V2 + */ +OM_uint32 KRB5_CALLCONV +gss_wrap_size_limit(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, OM_uint32 req_output_size, OM_uint32 *max_input_size) +{ + gss_union_ctx_id_t ctx; + gss_mechanism mech; + OM_uint32 major_status; + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + *minor_status = 0; + + if (context_handle == GSS_C_NO_CONTEXT) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT); + + if (max_input_size == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + ctx = (gss_union_ctx_id_t) context_handle; + mech = gssint_get_mechanism (ctx->mech_type); + + if (!mech) + return (GSS_S_BAD_MECH); + + if (mech->gss_wrap_size_limit) + major_status = mech->gss_wrap_size_limit(minor_status, + ctx->internal_ctx_id, + conf_req_flag, qop_req, + req_output_size, max_input_size); + else if (mech->gss_wrap_iov_length) + major_status = gssint_wrap_size_limit_iov_shim(mech, minor_status, + ctx->internal_ctx_id, + conf_req_flag, qop_req, + req_output_size, max_input_size); + else + major_status = GSS_S_UNAVAILABLE; + if (major_status != GSS_S_COMPLETE) + map_error(minor_status, mech); + return major_status; +} diff --git a/src/lib/gssapi/mechglue/g_set_context_option.c b/src/lib/gssapi/mechglue/g_set_context_option.c new file mode 100644 index 000000000000..87db240df3db --- /dev/null +++ b/src/lib/gssapi/mechglue/g_set_context_option.c @@ -0,0 +1,107 @@ +/* + * Copyright 2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* Glue routine for gss_set_sec_context_option */ + +#include "mglueP.h" +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <errno.h> + +OM_uint32 KRB5_CALLCONV +gss_set_sec_context_option (OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + const gss_OID desired_object, + const gss_buffer_t value) +{ + OM_uint32 status, minor; + gss_union_ctx_id_t ctx; + gss_mechanism mech; + gss_ctx_id_t internal_ctx = GSS_C_NO_CONTEXT; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (context_handle == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor_status = 0; + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + ctx = (gss_union_ctx_id_t) *context_handle; + if (ctx == NULL) { + mech = gssint_get_mechanism (GSS_C_NO_OID); + } else { + mech = gssint_get_mechanism (ctx->mech_type); + } + + if (mech == NULL) + return GSS_S_BAD_MECH; + if (mech->gss_set_sec_context_option == NULL) + return GSS_S_UNAVAILABLE; + + status = mech->gss_set_sec_context_option(minor_status, + ctx ? &ctx->internal_ctx_id : + &internal_ctx, + desired_object, + value); + if (status == GSS_S_COMPLETE) { + if (ctx == NULL && internal_ctx != GSS_C_NO_CONTEXT) { + /* Allocate a union context handle to wrap new context */ + ctx = (gss_union_ctx_id_t)malloc(sizeof(*ctx)); + if (ctx == NULL) { + *minor_status = ENOMEM; + gssint_delete_internal_sec_context(&minor, + &mech->mech_type, + &internal_ctx, + GSS_C_NO_BUFFER); + return GSS_S_FAILURE; + } + + status = generic_gss_copy_oid(minor_status, + &mech->mech_type, + &ctx->mech_type); + if (status != GSS_S_COMPLETE) { + gssint_delete_internal_sec_context(&minor, + ctx->mech_type, + &internal_ctx, + GSS_C_NO_BUFFER); + free(ctx); + return status; + } + + ctx->internal_ctx_id = internal_ctx; + *context_handle = (gss_ctx_id_t)ctx; + } + } else + map_error(minor_status, mech); + + return status; +} diff --git a/src/lib/gssapi/mechglue/g_set_cred_option.c b/src/lib/gssapi/mechglue/g_set_cred_option.c new file mode 100644 index 000000000000..90e5756e5199 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_set_cred_option.c @@ -0,0 +1,198 @@ +/* + * Copyright 2008-2010 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* Glue routine for gssspi_set_cred_option */ + +#include "mglueP.h" +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#include <time.h> + +static OM_uint32 +alloc_union_cred(OM_uint32 *minor_status, + gss_mechanism mech, + gss_cred_id_t mech_cred, + gss_union_cred_t *pcred) +{ + OM_uint32 status; + OM_uint32 temp_minor_status; + gss_union_cred_t cred = NULL; + + *pcred = NULL; + + status = GSS_S_FAILURE; + + cred = calloc(1, sizeof(*cred)); + if (cred == NULL) { + *minor_status = ENOMEM; + goto cleanup; + } + + cred->loopback = cred; + cred->count = 1; + + cred->cred_array = calloc(cred->count, sizeof(gss_cred_id_t)); + if (cred->cred_array == NULL) { + *minor_status = ENOMEM; + goto cleanup; + } + cred->cred_array[0] = mech_cred; + + status = generic_gss_copy_oid(minor_status, + &mech->mech_type, + &cred->mechs_array); + if (status != GSS_S_COMPLETE) + goto cleanup; + + status = GSS_S_COMPLETE; + *pcred = cred; + +cleanup: + if (status != GSS_S_COMPLETE) + gss_release_cred(&temp_minor_status, (gss_cred_id_t *)&cred); + + return status; +} + +/* + * This differs from gssspi_set_cred_option() as shipped in 1.7, in that + * it can return a cred handle. To denote this change we have changed the + * name of the function from gssspi_set_cred_option() to gss_set_cred_option(). + * However, the dlsym() entry point is still gssspi_set_cred_option(). This + * fixes a separate issue, namely that a dynamically loaded mechanism could + * not itself call set_cred_option() without calling its own implementation + * instead of the mechanism glue's. (This is useful where a mechanism wishes + * to export a mechanism-specific API that is a wrapper around this function.) + */ +OM_uint32 KRB5_CALLCONV +gss_set_cred_option(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_object, + const gss_buffer_t value) +{ + gss_union_cred_t union_cred; + gss_mechanism mech; + int i; + OM_uint32 status; + OM_uint32 mech_status; + OM_uint32 mech_minor_status; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (cred_handle == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor_status = 0; + + status = GSS_S_UNAVAILABLE; + + if (*cred_handle == GSS_C_NO_CREDENTIAL) { + gss_cred_id_t mech_cred = GSS_C_NO_CREDENTIAL; + + /* + * We need to give a mechanism the opportunity to allocate a + * credentials handle. Unfortunately this does mean that only + * the default mechanism can allocate a credentials handle. + */ + mech = gssint_get_mechanism(NULL); + if (mech == NULL) + return GSS_S_BAD_MECH; + + if (mech->gssspi_set_cred_option == NULL) + return GSS_S_UNAVAILABLE; + + status = mech->gssspi_set_cred_option(minor_status, + &mech_cred, + desired_object, + value); + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + return status; + } + + if (mech_cred != GSS_C_NO_CREDENTIAL) { + status = alloc_union_cred(minor_status, + mech, + mech_cred, + &union_cred); + if (status != GSS_S_COMPLETE) + return status; + *cred_handle = (gss_cred_id_t)union_cred; + } + } else { + union_cred = (gss_union_cred_t)*cred_handle; + + for (i = 0; i < union_cred->count; i++) { + mech = gssint_get_mechanism(&union_cred->mechs_array[i]); + if (mech == NULL) { + status = GSS_S_BAD_MECH; + break; + } + + if (mech->gssspi_set_cred_option == NULL) + continue; + + mech_status = mech->gssspi_set_cred_option(&mech_minor_status, + &union_cred->cred_array[i], + desired_object, + value); + if (mech_status == GSS_S_UNAVAILABLE) + continue; + else { + status = mech_status; + *minor_status = mech_minor_status; + } + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + break; + } + } + } + + return status; +} + +/* + * Provide this for backward ABI compatibility, but remove it from the + * header. + */ +OM_uint32 KRB5_CALLCONV +gssspi_set_cred_option(OM_uint32 *minor_status, + gss_cred_id_t cred, + const gss_OID desired_object, + const gss_buffer_t value); + +OM_uint32 KRB5_CALLCONV +gssspi_set_cred_option(OM_uint32 *minor_status, + gss_cred_id_t cred, + const gss_OID desired_object, + const gss_buffer_t value) +{ + return gss_set_cred_option(minor_status, &cred, + desired_object, value); +} diff --git a/src/lib/gssapi/mechglue/g_set_name_attr.c b/src/lib/gssapi/mechglue/g_set_name_attr.c new file mode 100644 index 000000000000..a479762a7609 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_set_name_attr.c @@ -0,0 +1,70 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* Glue routine for gss_set_name_attribute */ + +#include "mglueP.h" + +OM_uint32 KRB5_CALLCONV +gss_set_name_attribute(OM_uint32 *minor_status, + gss_name_t name, + int complete, + gss_buffer_t attr, + gss_buffer_t value) +{ + OM_uint32 status; + gss_union_name_t union_name; + gss_mechanism mech; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (name == GSS_C_NO_NAME) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + + *minor_status = 0; + + union_name = (gss_union_name_t)name; + + if (union_name->mech_type == GSS_C_NO_OID) + return GSS_S_UNAVAILABLE; + + mech = gssint_get_mechanism(name->mech_type); + if (mech == NULL) + return GSS_S_BAD_NAME; + + if (mech->gss_set_name_attribute == NULL) + return GSS_S_UNAVAILABLE; + + status = (*mech->gss_set_name_attribute)(minor_status, + union_name->mech_name, + complete, + attr, + value); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + + return status; +} diff --git a/src/lib/gssapi/mechglue/g_set_neg_mechs.c b/src/lib/gssapi/mechglue/g_set_neg_mechs.c new file mode 100644 index 000000000000..69cac7037334 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_set_neg_mechs.c @@ -0,0 +1,73 @@ +/* lib/gssapi/mechglue/g_set_neg_mechs.c - Glue for gss_set_neg_mechs */ +/* + * Copyright (C) 2010 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 "mglueP.h" + +OM_uint32 KRB5_CALLCONV +gss_set_neg_mechs(OM_uint32 *minor_status, + gss_cred_id_t cred_handle, + const gss_OID_set mech_set) +{ + gss_union_cred_t union_cred; + gss_mechanism mech; + int i, avail; + OM_uint32 status; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (cred_handle == GSS_C_NO_CREDENTIAL) + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED; + + *minor_status = 0; + + union_cred = (gss_union_cred_t) cred_handle; + + avail = 0; + status = GSS_S_COMPLETE; + for (i = 0; i < union_cred->count; i++) { + mech = gssint_get_mechanism(&union_cred->mechs_array[i]); + if (mech == NULL) { + status = GSS_S_BAD_MECH; + break; + } + + if (mech->gss_set_neg_mechs == NULL) + continue; + + avail = 1; + status = (mech->gss_set_neg_mechs)(minor_status, + union_cred->cred_array[i], + mech_set); + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + break; + } + } + + if (status == GSS_S_COMPLETE && !avail) + return GSS_S_UNAVAILABLE; + return status; +} diff --git a/src/lib/gssapi/mechglue/g_sign.c b/src/lib/gssapi/mechglue/g_sign.c new file mode 100644 index 000000000000..86d641aa2e28 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_sign.c @@ -0,0 +1,134 @@ +/* #pragma ident "@(#)g_sign.c 1.14 98/04/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine gss_get_mic + */ + +#include "mglueP.h" + +static OM_uint32 +val_get_mic_args( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_qop_t qop_req, + gss_buffer_t message_buffer, + gss_buffer_t msg_token) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (msg_token != GSS_C_NO_BUFFER) { + msg_token->value = NULL; + msg_token->length = 0; + } + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (context_handle == GSS_C_NO_CONTEXT) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT); + + if (message_buffer == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_READ); + + if (msg_token == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_get_mic (minor_status, + context_handle, + qop_req, + message_buffer, + msg_token) + +OM_uint32 * minor_status; +gss_ctx_id_t context_handle; +gss_qop_t qop_req; +gss_buffer_t message_buffer; +gss_buffer_t msg_token; + +{ + OM_uint32 status; + gss_union_ctx_id_t ctx; + gss_mechanism mech; + + status = val_get_mic_args(minor_status, context_handle, + qop_req, message_buffer, msg_token); + if (status != GSS_S_COMPLETE) + return (status); + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + ctx = (gss_union_ctx_id_t) context_handle; + mech = gssint_get_mechanism (ctx->mech_type); + + if (mech) { + if (mech->gss_get_mic) { + status = mech->gss_get_mic( + minor_status, + ctx->internal_ctx_id, + qop_req, + message_buffer, + msg_token); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } else + status = GSS_S_UNAVAILABLE; + + return(status); + } + + return (GSS_S_BAD_MECH); +} + +OM_uint32 KRB5_CALLCONV +gss_sign (minor_status, + context_handle, + qop_req, + message_buffer, + msg_token) + +OM_uint32 * minor_status; +gss_ctx_id_t context_handle; +int qop_req; +gss_buffer_t message_buffer; +gss_buffer_t msg_token; + +{ + return (gss_get_mic(minor_status, context_handle, (gss_qop_t) qop_req, + message_buffer, msg_token)); +} diff --git a/src/lib/gssapi/mechglue/g_store_cred.c b/src/lib/gssapi/mechglue/g_store_cred.c new file mode 100644 index 000000000000..c2b6ddf3c0d0 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_store_cred.c @@ -0,0 +1,266 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* #pragma ident "@(#)g_store_cred.c 1.2 04/04/05 SMI" */ + +/* + * glue routine for gss_store_cred + */ + +#include <mglueP.h> + +static OM_uint32 +store_cred_fallback( + OM_uint32 *minor_status, + gss_mechanism mech, + gss_cred_id_t mech_cred, + gss_cred_usage_t cred_usage, + gss_OID desired_mech, + OM_uint32 overwrite_cred, + OM_uint32 default_cred, + gss_const_key_value_set_t cred_store, + gss_OID_set *elements_stored, + gss_cred_usage_t *cred_usage_stored) +{ + gss_OID public_mech = gssint_get_public_oid(desired_mech); + + if (mech->gss_store_cred_into != NULL) { + return mech->gss_store_cred_into(minor_status, mech_cred, + cred_usage, public_mech, + overwrite_cred, default_cred, + cred_store, elements_stored, + cred_usage_stored); + } else if (cred_store == GSS_C_NO_CRED_STORE) { + return mech->gss_store_cred(minor_status, mech_cred, + cred_usage, public_mech, + overwrite_cred, default_cred, + elements_stored, + cred_usage_stored); + } else { + return GSS_S_UNAVAILABLE; + } +} + +static OM_uint32 +val_store_cred_args( + OM_uint32 *minor_status, + const gss_cred_id_t input_cred_handle, + gss_cred_usage_t cred_usage, + const gss_OID desired_mech, + OM_uint32 overwrite_cred, + OM_uint32 default_cred, + gss_const_key_value_set_t cred_store, + gss_OID_set *elements_stored, + gss_cred_usage_t *cred_usage_stored) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (elements_stored != NULL) + *elements_stored = GSS_C_NULL_OID_SET; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED); + + if (cred_usage != GSS_C_ACCEPT + && cred_usage != GSS_C_INITIATE + && cred_usage != GSS_C_BOTH) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + if (cred_store != NULL && cred_store->count == 0) { + *minor_status = EINVAL; + map_errcode(minor_status); + return GSS_S_FAILURE; + } + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_store_cred(minor_status, + input_cred_handle, + cred_usage, + desired_mech, + overwrite_cred, + default_cred, + elements_stored, + cred_usage_stored) + +OM_uint32 *minor_status; +gss_cred_id_t input_cred_handle; +gss_cred_usage_t cred_usage; +const gss_OID desired_mech; +OM_uint32 overwrite_cred; +OM_uint32 default_cred; +gss_OID_set *elements_stored; +gss_cred_usage_t *cred_usage_stored; + +{ + return gss_store_cred_into(minor_status, input_cred_handle, cred_usage, + desired_mech, overwrite_cred, default_cred, + GSS_C_NO_CRED_STORE, elements_stored, + cred_usage_stored); +} + +OM_uint32 KRB5_CALLCONV +gss_store_cred_into(minor_status, + input_cred_handle, + cred_usage, + desired_mech, + overwrite_cred, + default_cred, + cred_store, + elements_stored, + cred_usage_stored) + +OM_uint32 *minor_status; +gss_cred_id_t input_cred_handle; +gss_cred_usage_t cred_usage; +gss_OID desired_mech; +OM_uint32 overwrite_cred; +OM_uint32 default_cred; +gss_const_key_value_set_t cred_store; +gss_OID_set *elements_stored; +gss_cred_usage_t *cred_usage_stored; + +{ + OM_uint32 major_status = GSS_S_FAILURE; + gss_union_cred_t union_cred; + gss_cred_id_t mech_cred; + gss_mechanism mech; + gss_OID dmech; + gss_OID selected_mech; + int i; + + major_status = val_store_cred_args(minor_status, + input_cred_handle, + cred_usage, + desired_mech, + overwrite_cred, + default_cred, + cred_store, + elements_stored, + cred_usage_stored); + if (major_status != GSS_S_COMPLETE) + return (major_status); + + /* Initial value needed below. */ + major_status = GSS_S_FAILURE; + + if (cred_usage_stored != NULL) + *cred_usage_stored = GSS_C_BOTH; /* there's no GSS_C_NEITHER */ + + union_cred = (gss_union_cred_t)input_cred_handle; + + /* desired_mech != GSS_C_NULL_OID -> store one element */ + if (desired_mech != GSS_C_NULL_OID) { + major_status = gssint_select_mech_type(minor_status, + desired_mech, + &selected_mech); + if (major_status != GSS_S_COMPLETE) + return (major_status); + + mech = gssint_get_mechanism(selected_mech); + if (mech == NULL) + return (GSS_S_BAD_MECH); + + if (mech->gss_store_cred_into == NULL && + cred_store != GSS_C_NO_CRED_STORE) + return (major_status); + + if (mech->gss_store_cred == NULL && + mech->gss_store_cred_into == NULL) + return (major_status); + + mech_cred = gssint_get_mechanism_cred(union_cred, selected_mech); + if (mech_cred == GSS_C_NO_CREDENTIAL) + return (GSS_S_NO_CRED); + + major_status = store_cred_fallback(minor_status, mech, + mech_cred, cred_usage, + selected_mech, + overwrite_cred, + default_cred, cred_store, + elements_stored, + cred_usage_stored); + if (major_status != GSS_S_COMPLETE) + map_error(minor_status, mech); + return major_status; + } + + /* desired_mech == GSS_C_NULL_OID -> store all elements */ + + *minor_status = 0; + + for (i = 0; i < union_cred->count; i++) { + /* Get mech and cred element */ + dmech = &union_cred->mechs_array[i]; + mech = gssint_get_mechanism(dmech); + if (mech == NULL) + continue; + + if (mech->gss_store_cred_into == NULL && + cred_store != GSS_C_NO_CRED_STORE) + continue; + + if (mech->gss_store_cred == NULL && + mech->gss_store_cred_into == NULL) + continue; + + mech_cred = gssint_get_mechanism_cred(union_cred, dmech); + if (mech_cred == GSS_C_NO_CREDENTIAL) + continue; /* can't happen, but safe to ignore */ + + major_status = store_cred_fallback(minor_status, mech, + mech_cred, cred_usage, + dmech, overwrite_cred, + default_cred, cred_store, + NULL, cred_usage_stored); + if (major_status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + continue; + } + + /* Succeeded for at least one mech */ + + if (elements_stored == NULL) + continue; + + if (*elements_stored == GSS_C_NULL_OID_SET) { + major_status = gss_create_empty_oid_set(minor_status, + elements_stored); + + if (GSS_ERROR(major_status)) + return (major_status); + } + + major_status = gss_add_oid_set_member(minor_status, dmech, + elements_stored); + + /* The caller should clean up elements_stored */ + if (GSS_ERROR(major_status)) + return (major_status); + } + + /* + * Success with some mechs may mask failure with others, but + * that's what elements_stored is for. + */ + return (major_status); +} diff --git a/src/lib/gssapi/mechglue/g_unseal.c b/src/lib/gssapi/mechglue/g_unseal.c new file mode 100644 index 000000000000..3e8053c6e9af --- /dev/null +++ b/src/lib/gssapi/mechglue/g_unseal.c @@ -0,0 +1,130 @@ +/* #pragma ident "@(#)g_unseal.c 1.13 98/01/22 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine gss_unwrap + */ + +#include "mglueP.h" + +OM_uint32 KRB5_CALLCONV +gss_unwrap (minor_status, + context_handle, + input_message_buffer, + output_message_buffer, + conf_state, + qop_state) + +OM_uint32 * minor_status; +gss_ctx_id_t context_handle; +gss_buffer_t input_message_buffer; +gss_buffer_t output_message_buffer; +int * conf_state; +gss_qop_t * qop_state; + +{ +/* EXPORT DELETE START */ + OM_uint32 status; + gss_union_ctx_id_t ctx; + gss_mechanism mech; + + if (minor_status != NULL) + *minor_status = 0; + + if (output_message_buffer != GSS_C_NO_BUFFER) { + output_message_buffer->length = 0; + output_message_buffer->value = NULL; + } + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (context_handle == GSS_C_NO_CONTEXT) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT); + + if (input_message_buffer == GSS_C_NO_BUFFER || + GSS_EMPTY_BUFFER(input_message_buffer)) + + return (GSS_S_CALL_INACCESSIBLE_READ); + + if (output_message_buffer == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + ctx = (gss_union_ctx_id_t) context_handle; + mech = gssint_get_mechanism (ctx->mech_type); + + if (mech) { + if (mech->gss_unwrap) { + status = mech->gss_unwrap(minor_status, + ctx->internal_ctx_id, + input_message_buffer, + output_message_buffer, + conf_state, + qop_state); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } else if (mech->gss_unwrap_aead || mech->gss_unwrap_iov) { + status = gssint_unwrap_aead(mech, + minor_status, + ctx, + input_message_buffer, + GSS_C_NO_BUFFER, + output_message_buffer, + conf_state, + (gss_qop_t *)qop_state); + } else + status = GSS_S_UNAVAILABLE; + + return(status); + } + +/* EXPORT DELETE END */ + + return (GSS_S_BAD_MECH); +} + +OM_uint32 KRB5_CALLCONV +gss_unseal (minor_status, + context_handle, + input_message_buffer, + output_message_buffer, + conf_state, + qop_state) + +OM_uint32 * minor_status; +gss_ctx_id_t context_handle; +gss_buffer_t input_message_buffer; +gss_buffer_t output_message_buffer; +int * conf_state; +int * qop_state; + +{ + return (gss_unwrap(minor_status, context_handle, + input_message_buffer, + output_message_buffer, conf_state, (gss_qop_t *) qop_state)); +} diff --git a/src/lib/gssapi/mechglue/g_unwrap_aead.c b/src/lib/gssapi/mechglue/g_unwrap_aead.c new file mode 100644 index 000000000000..e78bff2d3289 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_unwrap_aead.c @@ -0,0 +1,197 @@ +/* #pragma ident "@(#)g_seal.c 1.19 98/04/21 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_unwrap_aead + */ + +#include "mglueP.h" + +static OM_uint32 +val_unwrap_aead_args( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_buffer_t input_message_buffer, + gss_buffer_t input_assoc_buffer, + gss_buffer_t output_payload_buffer, + int *conf_state, + gss_qop_t *qop_state) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (context_handle == GSS_C_NO_CONTEXT) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT); + + if (input_message_buffer == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_READ); + + if (output_payload_buffer == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + return (GSS_S_COMPLETE); +} + +static OM_uint32 +gssint_unwrap_aead_iov_shim(gss_mechanism mech, + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_buffer_t input_message_buffer, + gss_buffer_t input_assoc_buffer, + gss_buffer_t output_payload_buffer, + int *conf_state, + gss_qop_t *qop_state) +{ + OM_uint32 status; + gss_iov_buffer_desc iov[3]; + int i = 0; + + iov[i].type = GSS_IOV_BUFFER_TYPE_STREAM; + iov[i].buffer = *input_message_buffer; + i++; + + if (input_assoc_buffer != NULL) { + iov[i].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; + iov[i].buffer = *input_assoc_buffer; + i++; + } + + iov[i].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE; + iov[i].buffer.value = NULL; + iov[i].buffer.length = 0; + i++; + + assert(mech->gss_unwrap_iov); + + status = mech->gss_unwrap_iov(minor_status, context_handle, conf_state, + qop_state, iov, i); + if (status == GSS_S_COMPLETE) { + *output_payload_buffer = iov[i - 1].buffer; + } else { + OM_uint32 minor; + + map_error(minor_status, mech); + + if (iov[i - 1].type & GSS_IOV_BUFFER_FLAG_ALLOCATED) { + gss_release_buffer(&minor, &iov[i - 1].buffer); + iov[i - 1].type &= ~(GSS_IOV_BUFFER_FLAG_ALLOCATED); + } + } + + return status; +} + +OM_uint32 +gssint_unwrap_aead (gss_mechanism mech, + OM_uint32 *minor_status, + gss_union_ctx_id_t ctx, + gss_buffer_t input_message_buffer, + gss_buffer_t input_assoc_buffer, + gss_buffer_t output_payload_buffer, + int *conf_state, + gss_qop_t *qop_state) +{ + OM_uint32 status; + + assert(mech != NULL); + assert(ctx != NULL); + + /* EXPORT DELETE START */ + + if (mech->gss_unwrap_aead) { + status = mech->gss_unwrap_aead(minor_status, + ctx->internal_ctx_id, + input_message_buffer, + input_assoc_buffer, + output_payload_buffer, + conf_state, + qop_state); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } else if (mech->gss_unwrap_iov) { + status = gssint_unwrap_aead_iov_shim(mech, + minor_status, + ctx->internal_ctx_id, + input_message_buffer, + input_assoc_buffer, + output_payload_buffer, + conf_state, + qop_state); + } else + status = GSS_S_UNAVAILABLE; + /* EXPORT DELETE END */ + + return (status); +} + +OM_uint32 KRB5_CALLCONV +gss_unwrap_aead (minor_status, + context_handle, + input_message_buffer, + input_assoc_buffer, + output_payload_buffer, + conf_state, + qop_state) +OM_uint32 * minor_status; +gss_ctx_id_t context_handle; +gss_buffer_t input_message_buffer; +gss_buffer_t input_assoc_buffer; +gss_buffer_t output_payload_buffer; +int *conf_state; +gss_qop_t *qop_state; +{ + + OM_uint32 status; + gss_union_ctx_id_t ctx; + gss_mechanism mech; + + status = val_unwrap_aead_args(minor_status, context_handle, + input_message_buffer, input_assoc_buffer, + output_payload_buffer, + conf_state, qop_state); + if (status != GSS_S_COMPLETE) + return (status); + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + ctx = (gss_union_ctx_id_t) context_handle; + mech = gssint_get_mechanism (ctx->mech_type); + + if (!mech) + return (GSS_S_BAD_MECH); + + return gssint_unwrap_aead(mech, minor_status, ctx, + input_message_buffer, input_assoc_buffer, + output_payload_buffer, conf_state, qop_state); +} diff --git a/src/lib/gssapi/mechglue/g_unwrap_iov.c b/src/lib/gssapi/mechglue/g_unwrap_iov.c new file mode 100644 index 000000000000..c0dd314b1be8 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_unwrap_iov.c @@ -0,0 +1,141 @@ +/* #pragma ident "@(#)g_seal.c 1.19 98/04/21 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_unwrap_iov + */ + +#include "mglueP.h" + +static OM_uint32 +val_unwrap_iov_args( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (context_handle == GSS_C_NO_CONTEXT) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT); + + if (iov == GSS_C_NO_IOV_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_READ); + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_unwrap_iov (minor_status, + context_handle, + conf_state, + qop_state, + iov, + iov_count) +OM_uint32 * minor_status; +gss_ctx_id_t context_handle; +int * conf_state; +gss_qop_t *qop_state; +gss_iov_buffer_desc * iov; +int iov_count; +{ + /* EXPORT DELETE START */ + + OM_uint32 status; + gss_union_ctx_id_t ctx; + gss_mechanism mech; + + status = val_unwrap_iov_args(minor_status, context_handle, + conf_state, qop_state, iov, iov_count); + if (status != GSS_S_COMPLETE) + return (status); + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + ctx = (gss_union_ctx_id_t) context_handle; + mech = gssint_get_mechanism (ctx->mech_type); + + if (mech) { + if (mech->gss_unwrap_iov) { + status = mech->gss_unwrap_iov( + minor_status, + ctx->internal_ctx_id, + conf_state, + qop_state, + iov, + iov_count); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } else + status = GSS_S_UNAVAILABLE; + + return(status); + } + /* EXPORT DELETE END */ + + return (GSS_S_BAD_MECH); +} + +OM_uint32 KRB5_CALLCONV +gss_verify_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_qop_t *qop_state, gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 status; + gss_union_ctx_id_t ctx; + gss_mechanism mech; + + status = val_unwrap_iov_args(minor_status, context_handle, NULL, + qop_state, iov, iov_count); + if (status != GSS_S_COMPLETE) + return status; + + /* Select the approprate underlying mechanism routine and call it. */ + ctx = (gss_union_ctx_id_t)context_handle; + mech = gssint_get_mechanism(ctx->mech_type); + if (mech == NULL) + return GSS_S_BAD_MECH; + if (mech->gss_verify_mic_iov == NULL) + return GSS_S_UNAVAILABLE; + status = mech->gss_verify_mic_iov(minor_status, ctx->internal_ctx_id, + qop_state, iov, iov_count); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + return status; +} diff --git a/src/lib/gssapi/mechglue/g_verify.c b/src/lib/gssapi/mechglue/g_verify.c new file mode 100644 index 000000000000..1578ae111092 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_verify.c @@ -0,0 +1,106 @@ +/* #pragma ident "@(#)g_verify.c 1.13 98/04/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_verify_mic + */ + +#include "mglueP.h" + +OM_uint32 KRB5_CALLCONV +gss_verify_mic (minor_status, + context_handle, + message_buffer, + token_buffer, + qop_state) + +OM_uint32 * minor_status; +gss_ctx_id_t context_handle; +gss_buffer_t message_buffer; +gss_buffer_t token_buffer; +gss_qop_t * qop_state; + +{ + OM_uint32 status; + gss_union_ctx_id_t ctx; + gss_mechanism mech; + + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + *minor_status = 0; + + if (context_handle == GSS_C_NO_CONTEXT) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT); + + if ((message_buffer == GSS_C_NO_BUFFER) || + GSS_EMPTY_BUFFER(token_buffer)) + + return (GSS_S_CALL_INACCESSIBLE_READ); + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + ctx = (gss_union_ctx_id_t) context_handle; + mech = gssint_get_mechanism (ctx->mech_type); + + if (mech) { + if (mech->gss_verify_mic) { + status = mech->gss_verify_mic( + minor_status, + ctx->internal_ctx_id, + message_buffer, + token_buffer, + qop_state); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } else + status = GSS_S_UNAVAILABLE; + + return(status); + } + + return (GSS_S_BAD_MECH); +} + +OM_uint32 KRB5_CALLCONV +gss_verify (minor_status, + context_handle, + message_buffer, + token_buffer, + qop_state) + +OM_uint32 * minor_status; +gss_ctx_id_t context_handle; +gss_buffer_t message_buffer; +gss_buffer_t token_buffer; +int * qop_state; + +{ + return (gss_verify_mic(minor_status, context_handle, + message_buffer, token_buffer, + (gss_qop_t *) qop_state)); +} diff --git a/src/lib/gssapi/mechglue/g_wrap_aead.c b/src/lib/gssapi/mechglue/g_wrap_aead.c new file mode 100644 index 000000000000..96cdf3ce6ab8 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_wrap_aead.c @@ -0,0 +1,267 @@ +/* #pragma ident "@(#)g_seal.c 1.19 98/04/21 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_wrap_aead + */ + +#include "mglueP.h" + +static OM_uint32 +val_wrap_aead_args( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + gss_buffer_t input_assoc_buffer, + gss_buffer_t input_payload_buffer, + int *conf_state, + gss_buffer_t output_message_buffer) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (context_handle == GSS_C_NO_CONTEXT) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT); + + if (input_payload_buffer == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_READ); + + if (output_message_buffer == GSS_C_NO_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + return (GSS_S_COMPLETE); +} + +static OM_uint32 +gssint_wrap_aead_iov_shim(gss_mechanism mech, + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + gss_buffer_t input_assoc_buffer, + gss_buffer_t input_payload_buffer, + int *conf_state, + gss_buffer_t output_message_buffer) +{ + gss_iov_buffer_desc iov[5]; + OM_uint32 status; + size_t offset; + int i = 0, iov_count; + + /* HEADER | SIGN_ONLY_DATA | DATA | PADDING | TRAILER */ + + iov[i].type = GSS_IOV_BUFFER_TYPE_HEADER; + iov[i].buffer.value = NULL; + iov[i].buffer.length = 0; + i++; + + if (input_assoc_buffer != GSS_C_NO_BUFFER) { + iov[i].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; + iov[i].buffer = *input_assoc_buffer; + i++; + } + + iov[i].type = GSS_IOV_BUFFER_TYPE_DATA; + iov[i].buffer = *input_payload_buffer; + i++; + + iov[i].type = GSS_IOV_BUFFER_TYPE_PADDING; + iov[i].buffer.value = NULL; + iov[i].buffer.length = 0; + i++; + + iov[i].type = GSS_IOV_BUFFER_TYPE_TRAILER; + iov[i].buffer.value = NULL; + iov[i].buffer.length = 0; + i++; + + iov_count = i; + + assert(mech->gss_wrap_iov_length); + + status = mech->gss_wrap_iov_length(minor_status, context_handle, + conf_req_flag, qop_req, + NULL, iov, iov_count); + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + return status; + } + + /* Format output token (does not include associated data) */ + for (i = 0, output_message_buffer->length = 0; i < iov_count; i++) { + if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_SIGN_ONLY) + continue; + + output_message_buffer->length += iov[i].buffer.length; + } + + output_message_buffer->value = gssalloc_malloc(output_message_buffer->length); + if (output_message_buffer->value == NULL) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + + i = 0, offset = 0; + + /* HEADER */ + iov[i].buffer.value = (unsigned char *)output_message_buffer->value + offset; + offset += iov[i].buffer.length; + i++; + + /* SIGN_ONLY_DATA */ + if (input_assoc_buffer != GSS_C_NO_BUFFER) + i++; + + /* DATA */ + iov[i].buffer.value = (unsigned char *)output_message_buffer->value + offset; + offset += iov[i].buffer.length; + + memcpy(iov[i].buffer.value, input_payload_buffer->value, iov[i].buffer.length); + i++; + + /* PADDING */ + iov[i].buffer.value = (unsigned char *)output_message_buffer->value + offset; + offset += iov[i].buffer.length; + i++; + + /* TRAILER */ + iov[i].buffer.value = (unsigned char *)output_message_buffer->value + offset; + offset += iov[i].buffer.length; + i++; + + assert(offset == output_message_buffer->length); + + assert(mech->gss_wrap_iov); + + status = mech->gss_wrap_iov(minor_status, context_handle, + conf_req_flag, qop_req, + conf_state, iov, iov_count); + if (status != GSS_S_COMPLETE) { + OM_uint32 minor; + + map_error(minor_status, mech); + gss_release_buffer(&minor, output_message_buffer); + } + + return status; +} + +OM_uint32 +gssint_wrap_aead (gss_mechanism mech, + OM_uint32 *minor_status, + gss_union_ctx_id_t ctx, + int conf_req_flag, + gss_qop_t qop_req, + gss_buffer_t input_assoc_buffer, + gss_buffer_t input_payload_buffer, + int *conf_state, + gss_buffer_t output_message_buffer) +{ + /* EXPORT DELETE START */ + OM_uint32 status; + + assert(ctx != NULL); + assert(mech != NULL); + + if (mech->gss_wrap_aead) { + status = mech->gss_wrap_aead(minor_status, + ctx->internal_ctx_id, + conf_req_flag, + qop_req, + input_assoc_buffer, + input_payload_buffer, + conf_state, + output_message_buffer); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } else if (mech->gss_wrap_iov && mech->gss_wrap_iov_length) { + status = gssint_wrap_aead_iov_shim(mech, + minor_status, + ctx->internal_ctx_id, + conf_req_flag, + qop_req, + input_assoc_buffer, + input_payload_buffer, + conf_state, + output_message_buffer); + } else + status = GSS_S_UNAVAILABLE; + + /* EXPORT DELETE END */ + + return status; +} + +OM_uint32 KRB5_CALLCONV +gss_wrap_aead (minor_status, + context_handle, + conf_req_flag, + qop_req, + input_assoc_buffer, + input_payload_buffer, + conf_state, + output_message_buffer) +OM_uint32 * minor_status; +gss_ctx_id_t context_handle; +int conf_req_flag; +gss_qop_t qop_req; +gss_buffer_t input_assoc_buffer; +gss_buffer_t input_payload_buffer; +int * conf_state; +gss_buffer_t output_message_buffer; +{ + OM_uint32 status; + gss_mechanism mech; + gss_union_ctx_id_t ctx; + + status = val_wrap_aead_args(minor_status, context_handle, + conf_req_flag, qop_req, + input_assoc_buffer, input_payload_buffer, + conf_state, output_message_buffer); + if (status != GSS_S_COMPLETE) + return (status); + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + ctx = (gss_union_ctx_id_t)context_handle; + mech = gssint_get_mechanism (ctx->mech_type); + if (!mech) + return (GSS_S_BAD_MECH); + + return gssint_wrap_aead(mech, minor_status, ctx, + conf_req_flag, qop_req, + input_assoc_buffer, input_payload_buffer, + conf_state, output_message_buffer); +} diff --git a/src/lib/gssapi/mechglue/g_wrap_iov.c b/src/lib/gssapi/mechglue/g_wrap_iov.c new file mode 100644 index 000000000000..40cd98fc91cd --- /dev/null +++ b/src/lib/gssapi/mechglue/g_wrap_iov.c @@ -0,0 +1,261 @@ +/* #pragma ident "@(#)g_seal.c 1.19 98/04/21 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_wrap_iov + */ + +#include "mglueP.h" + +static OM_uint32 +val_wrap_iov_args( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (context_handle == GSS_C_NO_CONTEXT) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT); + + if (iov == GSS_C_NO_IOV_BUFFER) + return (GSS_S_CALL_INACCESSIBLE_READ); + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_wrap_iov (minor_status, + context_handle, + conf_req_flag, + qop_req, + conf_state, + iov, + iov_count) +OM_uint32 * minor_status; +gss_ctx_id_t context_handle; +int conf_req_flag; +gss_qop_t qop_req; +int * conf_state; +gss_iov_buffer_desc * iov; +int iov_count; +{ + /* EXPORT DELETE START */ + + OM_uint32 status; + gss_union_ctx_id_t ctx; + gss_mechanism mech; + + status = val_wrap_iov_args(minor_status, context_handle, + conf_req_flag, qop_req, + conf_state, iov, iov_count); + if (status != GSS_S_COMPLETE) + return (status); + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + ctx = (gss_union_ctx_id_t) context_handle; + mech = gssint_get_mechanism (ctx->mech_type); + + if (mech) { + if (mech->gss_wrap_iov) { + status = mech->gss_wrap_iov( + minor_status, + ctx->internal_ctx_id, + conf_req_flag, + qop_req, + conf_state, + iov, + iov_count); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } else + status = GSS_S_UNAVAILABLE; + + return(status); + } + /* EXPORT DELETE END */ + + return (GSS_S_BAD_MECH); +} + +OM_uint32 KRB5_CALLCONV +gss_wrap_iov_length (minor_status, + context_handle, + conf_req_flag, + qop_req, + conf_state, + iov, + iov_count) +OM_uint32 * minor_status; +gss_ctx_id_t context_handle; +int conf_req_flag; +gss_qop_t qop_req; +int * conf_state; +gss_iov_buffer_desc * iov; +int iov_count; +{ + /* EXPORT DELETE START */ + + OM_uint32 status; + gss_union_ctx_id_t ctx; + gss_mechanism mech; + + status = val_wrap_iov_args(minor_status, context_handle, + conf_req_flag, qop_req, + conf_state, iov, iov_count); + if (status != GSS_S_COMPLETE) + return (status); + + /* + * select the approprate underlying mechanism routine and + * call it. + */ + + ctx = (gss_union_ctx_id_t) context_handle; + mech = gssint_get_mechanism (ctx->mech_type); + + if (mech) { + if (mech->gss_wrap_iov_length) { + status = mech->gss_wrap_iov_length( + minor_status, + ctx->internal_ctx_id, + conf_req_flag, + qop_req, + conf_state, + iov, + iov_count); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } else + status = GSS_S_UNAVAILABLE; + + return(status); + } + /* EXPORT DELETE END */ + + return (GSS_S_BAD_MECH); +} + +OM_uint32 KRB5_CALLCONV +gss_get_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_qop_t qop_req, gss_iov_buffer_desc *iov, int iov_count) +{ + OM_uint32 status; + gss_union_ctx_id_t ctx; + gss_mechanism mech; + + status = val_wrap_iov_args(minor_status, context_handle, 0, qop_req, NULL, + iov, iov_count); + if (status != GSS_S_COMPLETE) + return status; + + /* Select the approprate underlying mechanism routine and call it. */ + ctx = (gss_union_ctx_id_t)context_handle; + mech = gssint_get_mechanism(ctx->mech_type); + if (mech == NULL) + return GSS_S_BAD_MECH; + if (mech->gss_get_mic_iov == NULL) + return GSS_S_UNAVAILABLE; + status = mech->gss_get_mic_iov(minor_status, ctx->internal_ctx_id, qop_req, + iov, iov_count); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + return status; +} + +OM_uint32 KRB5_CALLCONV +gss_get_mic_iov_length(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_qop_t qop_req, gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 status; + gss_union_ctx_id_t ctx; + gss_mechanism mech; + + status = val_wrap_iov_args(minor_status, context_handle, 0, qop_req, NULL, + iov, iov_count); + if (status != GSS_S_COMPLETE) + return status; + + /* Select the approprate underlying mechanism routine and call it. */ + ctx = (gss_union_ctx_id_t)context_handle; + mech = gssint_get_mechanism(ctx->mech_type); + if (mech == NULL) + return GSS_S_BAD_MECH; + if (mech->gss_get_mic_iov_length == NULL) + return GSS_S_UNAVAILABLE; + status = mech->gss_get_mic_iov_length(minor_status, ctx->internal_ctx_id, + qop_req, iov, iov_count); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + return status; +} + +OM_uint32 KRB5_CALLCONV +gss_release_iov_buffer (minor_status, + iov, + iov_count) +OM_uint32 * minor_status; +gss_iov_buffer_desc * iov; +int iov_count; +{ + OM_uint32 status = GSS_S_COMPLETE; + int i; + + if (minor_status) + *minor_status = 0; + + if (iov == GSS_C_NO_IOV_BUFFER) + return GSS_S_COMPLETE; + + for (i = 0; i < iov_count; i++) { + if (iov[i].type & GSS_IOV_BUFFER_FLAG_ALLOCATED) { + status = gss_release_buffer(minor_status, &iov[i].buffer); + if (status != GSS_S_COMPLETE) + break; + + iov[i].type &= ~(GSS_IOV_BUFFER_FLAG_ALLOCATED); + } + } + + return status; +} diff --git a/src/lib/gssapi/mechglue/gssd_pname_to_uid.c b/src/lib/gssapi/mechglue/gssd_pname_to_uid.c new file mode 100644 index 000000000000..4caa751657b7 --- /dev/null +++ b/src/lib/gssapi/mechglue/gssd_pname_to_uid.c @@ -0,0 +1,226 @@ +/* #pragma ident "@(#)gssd_pname_to_uid.c 1.18 04/02/23 SMI" */ +/* + * Copyright (c) 2011, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routines that test the mech id either passed in to + * gss_init_sec_contex() or gss_accept_sec_context() or within the glue + * routine supported version of the security context and then call + * the appropriate underlying mechanism library procedure. + * + */ + +#include "mglueP.h" + +#ifndef NO_PASSWORD +#include <pwd.h> +#endif + +static OM_uint32 +attr_localname(OM_uint32 *minor, + const gss_mechanism mech, + const gss_name_t mech_name, + gss_buffer_t localname) +{ + OM_uint32 major = GSS_S_UNAVAILABLE; + OM_uint32 tmpMinor; + int more = -1; + gss_buffer_desc value; + gss_buffer_desc display_value; + int authenticated = 0, complete = 0; + + value.value = NULL; + display_value.value = NULL; + if (mech->gss_get_name_attribute == NULL) + return GSS_S_UNAVAILABLE; + + major = mech->gss_get_name_attribute(minor, + mech_name, + GSS_C_ATTR_LOCAL_LOGIN_USER, + &authenticated, + &complete, + &value, + &display_value, + &more); + if (GSS_ERROR(major)) { + map_error(minor, mech); + goto cleanup; + } + + if (!authenticated) + major = GSS_S_UNAVAILABLE; + else { + localname->value = value.value; + localname->length = value.length; + value.value = NULL; + } + +cleanup: + if (display_value.value) + gss_release_buffer(&tmpMinor, &display_value); + if (value.value) + gss_release_buffer(&tmpMinor, &value); + return major; +} + +OM_uint32 KRB5_CALLCONV +gss_localname(OM_uint32 *minor, + const gss_name_t pname, + gss_const_OID mech_type, + gss_buffer_t localname) +{ + OM_uint32 major, tmpMinor; + gss_mechanism mech; + gss_union_name_t unionName; + gss_name_t mechName = GSS_C_NO_NAME, mechNameP; + gss_OID selected_mech = GSS_C_NO_OID, public_mech; + + if (localname != GSS_C_NO_BUFFER) { + localname->length = 0; + localname->value = NULL; + } + + if (minor == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor = 0; + + if (pname == GSS_C_NO_NAME) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (localname == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + unionName = (gss_union_name_t)pname; + + if (mech_type != GSS_C_NO_OID) { + major = gssint_select_mech_type(minor, mech_type, &selected_mech); + if (major != GSS_S_COMPLETE) + return major; + mech = gssint_get_mechanism(selected_mech); + } else + mech = gssint_get_mechanism(unionName->mech_type); + + if (mech == NULL) + return GSS_S_BAD_MECH; + + /* may need to create a mechanism specific name */ + if (unionName->mech_type == GSS_C_NO_OID || + (unionName->mech_type != GSS_C_NO_OID && + !g_OID_equal(unionName->mech_type, &mech->mech_type))) { + major = gssint_import_internal_name(minor, &mech->mech_type, + unionName, &mechName); + if (GSS_ERROR(major)) + return major; + + mechNameP = mechName; + } else + mechNameP = unionName->mech_name; + + major = GSS_S_UNAVAILABLE; + + if (mech->gss_localname != NULL) { + public_mech = gssint_get_public_oid(selected_mech); + major = mech->gss_localname(minor, mechNameP, public_mech, localname); + if (GSS_ERROR(major)) + map_error(minor, mech); + } + + if (GSS_ERROR(major)) + major = attr_localname(minor, mech, mechNameP, localname); + + if (mechName != GSS_C_NO_NAME) + gssint_release_internal_name(&tmpMinor, &mech->mech_type, &mechName); + + return major; +} + +#ifndef _WIN32 +OM_uint32 KRB5_CALLCONV +gss_pname_to_uid(OM_uint32 *minor, + const gss_name_t name, + const gss_OID mech_type, + uid_t *uidOut) +{ + OM_uint32 major = GSS_S_UNAVAILABLE, tmpminor; +#ifndef NO_PASSWORD + gss_buffer_desc localname; + char pwbuf[BUFSIZ]; + char *localuser = NULL; + struct passwd *pwd = NULL; + struct passwd pw; + int code = 0; + + localname.value = NULL; + major = gss_localname(minor, name, mech_type, &localname); + if (!GSS_ERROR(major) && localname.value) { + localuser = malloc(localname.length + 1); + if (localuser == NULL) + code = ENOMEM; + if (code == 0) { + memcpy(localuser, localname.value, localname.length); + localuser[localname.length] = '\0'; + code = k5_getpwnam_r(localuser, &pw, pwbuf, sizeof(pwbuf), &pwd); + } + if ((code == 0) && pwd) + *uidOut = pwd->pw_uid; + else + major = GSS_S_FAILURE; + } + free(localuser); + if (localname.value) + gss_release_buffer(&tmpminor, &localname); +#endif /*NO_PASSWORD*/ + return major; +} +#endif /*_WIN32*/ diff --git a/src/lib/gssapi/mechglue/mechglue.h b/src/lib/gssapi/mechglue/mechglue.h new file mode 100644 index 000000000000..85983694a258 --- /dev/null +++ b/src/lib/gssapi/mechglue/mechglue.h @@ -0,0 +1,42 @@ +/* #ident "@(#)mechglue.h 1.13 95/08/07 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This header contains the mechglue definitions. + */ + +#ifndef _GSS_MECHGLUE_H +#define _GSS_MECHGLUE_H + +#include <gssapi/gssapi.h> + +/********************************************************/ +/* GSSAPI Extension functions -- these functions aren't */ +/* in the GSSAPI, but they are provided in this library */ + +#include <gssapi/gssapi_ext.h> + +void KRB5_CALLCONV gss_initialize(void); + +#endif /* _GSS_MECHGLUE_H */ diff --git a/src/lib/gssapi/mechglue/mglueP.h b/src/lib/gssapi/mechglue/mglueP.h new file mode 100644 index 000000000000..2b5145e079fe --- /dev/null +++ b/src/lib/gssapi/mechglue/mglueP.h @@ -0,0 +1,854 @@ +/* lib/gssapi/mechglue/mglueP.h */ + +/* + * Copyright (c) 1995, by Sun Microsystems, Inc. + * All rights reserved. + */ + +/* This header contains the private mechglue definitions. */ + +#ifndef _GSS_MECHGLUEP_H +#define _GSS_MECHGLUEP_H + +#include "autoconf.h" +#include "mechglue.h" +#include "gssapiP_generic.h" + +#define g_OID_copy(o1, o2) \ +do { \ + memcpy((o1)->elements, (o2)->elements, (o2)->length); \ + (o1)->length = (o2)->length; \ +} while (0) + +/* + * Array of context IDs typed by mechanism OID + */ +typedef struct gss_union_ctx_id_struct { + struct gss_union_ctx_id_struct *loopback; + gss_OID mech_type; + gss_ctx_id_t internal_ctx_id; +} gss_union_ctx_id_desc, *gss_union_ctx_id_t; + +/* + * Generic GSSAPI names. A name can either be a generic name, or a + * mechanism specific name.... + */ +typedef struct gss_name_struct { + struct gss_name_struct *loopback; + gss_OID name_type; + gss_buffer_t external_name; + /* + * These last two fields are only filled in for mechanism + * names. + */ + gss_OID mech_type; + gss_name_t mech_name; +} gss_union_name_desc, *gss_union_name_t; + +/* + * Structure for holding list of mechanism-specific name types + */ +typedef struct gss_mech_spec_name_t { + gss_OID name_type; + gss_OID mech; + struct gss_mech_spec_name_t *next, *prev; +} gss_mech_spec_name_desc, *gss_mech_spec_name; + +/* + * Set of Credentials typed on mechanism OID + */ +typedef struct gss_cred_id_struct { + struct gss_cred_id_struct *loopback; + int count; + gss_OID mechs_array; + gss_cred_id_t *cred_array; +} gss_union_cred_desc, *gss_union_cred_t; + +/* + * Rudimentary pointer validation macro to check whether the + * "loopback" field of an opaque struct points back to itself. This + * field also catches some programming errors where an opaque pointer + * is passed to a function expecting the address of the opaque + * pointer. + */ +#define GSSINT_CHK_LOOP(p) (!((p) != NULL && (p)->loopback == (p))) + +/********************************************************/ +/* The Mechanism Dispatch Table -- a mechanism needs to */ +/* define one of these and provide a function to return */ +/* it to initialize the GSSAPI library */ +int gssint_mechglue_initialize_library(void); + +OM_uint32 gssint_get_mech_type_oid(gss_OID OID, gss_buffer_t token); + +/* + * This is the definition of the mechs_array struct, which is used to + * define the mechs array table. This table is used to indirectly + * access mechanism specific versions of the gssapi routines through + * the routines in the glue module (gssd_mech_glue.c) + * + * This contants all of the functions defined in gssapi.h except for + * gss_release_buffer() and gss_release_oid_set(), which I am + * assuming, for now, to be equal across mechanisms. + */ + +typedef struct gss_config { + gss_OID_desc mech_type; + void * context; + OM_uint32 (KRB5_CALLCONV *gss_acquire_cred) + ( + OM_uint32*, /* minor_status */ + gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + gss_OID_set, /* desired_mechs */ + int, /* cred_usage */ + gss_cred_id_t*, /* output_cred_handle */ + gss_OID_set*, /* actual_mechs */ + OM_uint32* /* time_rec */ + ); + OM_uint32 (KRB5_CALLCONV *gss_release_cred) + ( + OM_uint32*, /* minor_status */ + gss_cred_id_t* /* cred_handle */ + ); + OM_uint32 (KRB5_CALLCONV *gss_init_sec_context) + ( + OM_uint32*, /* minor_status */ + gss_cred_id_t, /* claimant_cred_handle */ + gss_ctx_id_t*, /* context_handle */ + gss_name_t, /* target_name */ + gss_OID, /* mech_type */ + OM_uint32, /* req_flags */ + OM_uint32, /* time_req */ + gss_channel_bindings_t, /* input_chan_bindings */ + gss_buffer_t, /* input_token */ + gss_OID*, /* actual_mech_type */ + gss_buffer_t, /* output_token */ + OM_uint32*, /* ret_flags */ + OM_uint32* /* time_rec */ + ); + OM_uint32 (KRB5_CALLCONV *gss_accept_sec_context) + ( + OM_uint32*, /* minor_status */ + gss_ctx_id_t*, /* context_handle */ + gss_cred_id_t, /* verifier_cred_handle */ + gss_buffer_t, /* input_token_buffer */ + gss_channel_bindings_t, /* input_chan_bindings */ + gss_name_t*, /* src_name */ + gss_OID*, /* mech_type */ + gss_buffer_t, /* output_token */ + OM_uint32*, /* ret_flags */ + OM_uint32*, /* time_rec */ + gss_cred_id_t* /* delegated_cred_handle */ + ); + OM_uint32 (KRB5_CALLCONV *gss_process_context_token) + ( + OM_uint32*, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_buffer_t /* token_buffer */ + ); + OM_uint32 (KRB5_CALLCONV *gss_delete_sec_context) + ( + OM_uint32*, /* minor_status */ + gss_ctx_id_t*, /* context_handle */ + gss_buffer_t /* output_token */ + ); + OM_uint32 (KRB5_CALLCONV *gss_context_time) + ( + OM_uint32*, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + OM_uint32* /* time_rec */ + ); + OM_uint32 (KRB5_CALLCONV *gss_get_mic) + ( + OM_uint32*, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_qop_t, /* qop_req */ + gss_buffer_t, /* message_buffer */ + gss_buffer_t /* message_token */ + ); + OM_uint32 (KRB5_CALLCONV *gss_verify_mic) + ( + OM_uint32*, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_buffer_t, /* message_buffer */ + gss_buffer_t, /* token_buffer */ + gss_qop_t* /* qop_state */ + ); + OM_uint32 (KRB5_CALLCONV *gss_wrap) + ( + OM_uint32*, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* conf_req_flag */ + gss_qop_t, /* qop_req */ + gss_buffer_t, /* input_message_buffer */ + int*, /* conf_state */ + gss_buffer_t /* output_message_buffer */ + ); + OM_uint32 (KRB5_CALLCONV *gss_unwrap) + ( + OM_uint32*, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_buffer_t, /* input_message_buffer */ + gss_buffer_t, /* output_message_buffer */ + int*, /* conf_state */ + gss_qop_t* /* qop_state */ + ); + OM_uint32 (KRB5_CALLCONV *gss_display_status) + ( + OM_uint32*, /* minor_status */ + OM_uint32, /* status_value */ + int, /* status_type */ + gss_OID, /* mech_type */ + OM_uint32*, /* message_context */ + gss_buffer_t /* status_string */ + ); + OM_uint32 (KRB5_CALLCONV *gss_indicate_mechs) + ( + OM_uint32*, /* minor_status */ + gss_OID_set* /* mech_set */ + ); + OM_uint32 (KRB5_CALLCONV *gss_compare_name) + ( + OM_uint32*, /* minor_status */ + gss_name_t, /* name1 */ + gss_name_t, /* name2 */ + int* /* name_equal */ + ); + OM_uint32 (KRB5_CALLCONV *gss_display_name) + ( + OM_uint32*, /* minor_status */ + gss_name_t, /* input_name */ + gss_buffer_t, /* output_name_buffer */ + gss_OID* /* output_name_type */ + ); + OM_uint32 (KRB5_CALLCONV *gss_import_name) + ( + OM_uint32*, /* minor_status */ + gss_buffer_t, /* input_name_buffer */ + gss_OID, /* input_name_type */ + gss_name_t* /* output_name */ + ); + OM_uint32 (KRB5_CALLCONV *gss_release_name) + ( + OM_uint32*, /* minor_status */ + gss_name_t* /* input_name */ + ); + OM_uint32 (KRB5_CALLCONV *gss_inquire_cred) + ( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* cred_handle */ + gss_name_t *, /* name */ + OM_uint32 *, /* lifetime */ + int *, /* cred_usage */ + gss_OID_set * /* mechanisms */ + ); + OM_uint32 (KRB5_CALLCONV *gss_add_cred) + ( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* input_cred_handle */ + gss_name_t, /* desired_name */ + gss_OID, /* desired_mech */ + gss_cred_usage_t, /* cred_usage */ + OM_uint32, /* initiator_time_req */ + OM_uint32, /* acceptor_time_req */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *, /* initiator_time_rec */ + OM_uint32 * /* acceptor_time_rec */ + ); + OM_uint32 (KRB5_CALLCONV *gss_export_sec_context) + ( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t *, /* context_handle */ + gss_buffer_t /* interprocess_token */ + ); + OM_uint32 (KRB5_CALLCONV *gss_import_sec_context) + ( + OM_uint32 *, /* minor_status */ + gss_buffer_t, /* interprocess_token */ + gss_ctx_id_t * /* context_handle */ + ); + OM_uint32 (KRB5_CALLCONV *gss_inquire_cred_by_mech) + ( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* cred_handle */ + gss_OID, /* mech_type */ + gss_name_t *, /* name */ + OM_uint32 *, /* initiator_lifetime */ + OM_uint32 *, /* acceptor_lifetime */ + gss_cred_usage_t * /* cred_usage */ + ); + OM_uint32 (KRB5_CALLCONV *gss_inquire_names_for_mech) + ( + OM_uint32 *, /* minor_status */ + gss_OID, /* mechanism */ + gss_OID_set * /* name_types */ + ); + OM_uint32 (KRB5_CALLCONV *gss_inquire_context) + ( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_name_t *, /* src_name */ + gss_name_t *, /* targ_name */ + OM_uint32 *, /* lifetime_rec */ + gss_OID *, /* mech_type */ + OM_uint32 *, /* ctx_flags */ + int *, /* locally_initiated */ + int * /* open */ + ); + OM_uint32 (KRB5_CALLCONV *gss_internal_release_oid) + ( + OM_uint32 *, /* minor_status */ + gss_OID * /* OID */ + ); + OM_uint32 (KRB5_CALLCONV *gss_wrap_size_limit) + ( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* conf_req_flag */ + gss_qop_t, /* qop_req */ + OM_uint32, /* req_output_size */ + OM_uint32 * /* max_input_size */ + ); + OM_uint32 (KRB5_CALLCONV *gss_localname) + ( + OM_uint32 *, /* minor */ + const gss_name_t, /* name */ + gss_const_OID, /* mech_type */ + gss_buffer_t /* localname */ + ); + OM_uint32 (KRB5_CALLCONV *gssspi_authorize_localname) + ( + OM_uint32 *, /* minor_status */ + const gss_name_t, /* pname */ + gss_const_buffer_t, /* local user */ + gss_const_OID /* local nametype */ + /* */); + OM_uint32 (KRB5_CALLCONV *gss_export_name) + ( + OM_uint32 *, /* minor_status */ + const gss_name_t, /* input_name */ + gss_buffer_t /* exported_name */ + /* */); + OM_uint32 (KRB5_CALLCONV *gss_duplicate_name) + ( + OM_uint32*, /* minor_status */ + const gss_name_t, /* input_name */ + gss_name_t * /* output_name */ + /* */); + OM_uint32 (KRB5_CALLCONV *gss_store_cred) + ( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* input_cred */ + gss_cred_usage_t, /* cred_usage */ + const gss_OID, /* desired_mech */ + OM_uint32, /* overwrite_cred */ + OM_uint32, /* default_cred */ + gss_OID_set *, /* elements_stored */ + gss_cred_usage_t * /* cred_usage_stored */ + /* */); + + + /* GGF extensions */ + + OM_uint32 (KRB5_CALLCONV *gss_inquire_sec_context_by_oid) + ( + OM_uint32 *, /* minor_status */ + const gss_ctx_id_t, /* context_handle */ + const gss_OID, /* OID */ + gss_buffer_set_t * /* data_set */ + ); + OM_uint32 (KRB5_CALLCONV *gss_inquire_cred_by_oid) + ( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* cred_handle */ + const gss_OID, /* OID */ + gss_buffer_set_t * /* data_set */ + ); + OM_uint32 (KRB5_CALLCONV *gss_set_sec_context_option) + ( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t *, /* context_handle */ + const gss_OID, /* OID */ + const gss_buffer_t /* value */ + ); + OM_uint32 (KRB5_CALLCONV *gssspi_set_cred_option) + ( + OM_uint32 *, /* minor_status */ + gss_cred_id_t *, /* cred_handle */ + const gss_OID, /* OID */ + const gss_buffer_t /* value */ + ); + OM_uint32 (KRB5_CALLCONV *gssspi_mech_invoke) + ( + OM_uint32*, /* minor_status */ + const gss_OID, /* mech OID */ + const gss_OID, /* OID */ + gss_buffer_t /* value */ + ); + + /* AEAD extensions */ + OM_uint32 (KRB5_CALLCONV *gss_wrap_aead) + ( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* conf_req_flag */ + gss_qop_t, /* qop_req */ + gss_buffer_t, /* input_assoc_buffer */ + gss_buffer_t, /* input_payload_buffer */ + int *, /* conf_state */ + gss_buffer_t /* output_message_buffer */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_unwrap_aead) + ( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_buffer_t, /* input_message_buffer */ + gss_buffer_t, /* input_assoc_buffer */ + gss_buffer_t, /* output_payload_buffer */ + int *, /* conf_state */ + gss_qop_t * /* qop_state */ + /* */); + + /* SSPI extensions */ + OM_uint32 (KRB5_CALLCONV *gss_wrap_iov) + ( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* conf_req_flag */ + gss_qop_t, /* qop_req */ + int *, /* conf_state */ + gss_iov_buffer_desc *, /* iov */ + int /* iov_count */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_unwrap_iov) + ( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int *, /* conf_state */ + gss_qop_t *, /* qop_state */ + gss_iov_buffer_desc *, /* iov */ + int /* iov_count */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_wrap_iov_length) + ( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* conf_req_flag*/ + gss_qop_t, /* qop_req */ + int *, /* conf_state */ + gss_iov_buffer_desc *, /* iov */ + int /* iov_count */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_complete_auth_token) + ( + OM_uint32*, /* minor_status */ + const gss_ctx_id_t, /* context_handle */ + gss_buffer_t /* input_message_buffer */ + ); + + /* New for 1.8 */ + + OM_uint32 (KRB5_CALLCONV *gss_acquire_cred_impersonate_name) + ( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 * /* time_rec */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_add_cred_impersonate_name) + ( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* input_cred_handle */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_name_t, /* desired_name */ + const gss_OID, /* desired_mech */ + gss_cred_usage_t, /* cred_usage */ + OM_uint32, /* initiator_time_req */ + OM_uint32, /* acceptor_time_req */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *, /* initiator_time_rec */ + OM_uint32 * /* acceptor_time_rec */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_display_name_ext) + ( + OM_uint32 *, /* minor_status */ + gss_name_t, /* name */ + gss_OID, /* display_as_name_type */ + gss_buffer_t /* display_name */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_inquire_name) + ( + OM_uint32 *, /* minor_status */ + gss_name_t, /* name */ + int *, /* name_is_MN */ + gss_OID *, /* MN_mech */ + gss_buffer_set_t * /* attrs */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_get_name_attribute) + ( + OM_uint32 *, /* minor_status */ + gss_name_t, /* name */ + gss_buffer_t, /* attr */ + int *, /* authenticated */ + int *, /* complete */ + gss_buffer_t, /* value */ + gss_buffer_t, /* display_value */ + int * /* more */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_set_name_attribute) + ( + OM_uint32 *, /* minor_status */ + gss_name_t, /* name */ + int, /* complete */ + gss_buffer_t, /* attr */ + gss_buffer_t /* value */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_delete_name_attribute) + ( + OM_uint32 *, /* minor_status */ + gss_name_t, /* name */ + gss_buffer_t /* attr */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_export_name_composite) + ( + OM_uint32 *, /* minor_status */ + gss_name_t, /* name */ + gss_buffer_t /* exp_composite_name */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_map_name_to_any) + ( + OM_uint32 *, /* minor_status */ + gss_name_t, /* name */ + int, /* authenticated */ + gss_buffer_t, /* type_id */ + gss_any_t * /* output */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_release_any_name_mapping) + ( + OM_uint32 *, /* minor_status */ + gss_name_t, /* name */ + gss_buffer_t, /* type_id */ + gss_any_t * /* input */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_pseudo_random) + ( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context */ + int, /* prf_key */ + const gss_buffer_t, /* prf_in */ + ssize_t, /* desired_output_len */ + gss_buffer_t /* prf_out */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_set_neg_mechs) + ( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* cred_handle */ + const gss_OID_set /* mech_set */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_inquire_saslname_for_mech) + ( + OM_uint32 *, /* minor_status */ + const gss_OID, /* desired_mech */ + gss_buffer_t, /* sasl_mech_name */ + gss_buffer_t, /* mech_name */ + gss_buffer_t /* mech_description */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_inquire_mech_for_saslname) + ( + OM_uint32 *, /* minor_status */ + const gss_buffer_t, /* sasl_mech_name */ + gss_OID * /* mech_type */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_inquire_attrs_for_mech) + ( + OM_uint32 *, /* minor_status */ + gss_const_OID, /* mech */ + gss_OID_set *, /* mech_attrs */ + gss_OID_set * /* known_mech_attrs */ + /* */); + + /* Credential store extensions */ + + OM_uint32 (KRB5_CALLCONV *gss_acquire_cred_from) + ( + OM_uint32 *, /* minor_status */ + gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_const_key_value_set_t, /* cred_store */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 * /* time_rec */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_store_cred_into) + ( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* input_cred_handle */ + gss_cred_usage_t, /* input_usage */ + gss_OID, /* desired_mech */ + OM_uint32, /* overwrite_cred */ + OM_uint32, /* default_cred */ + gss_const_key_value_set_t, /* cred_store */ + gss_OID_set *, /* elements_stored */ + gss_cred_usage_t * /* cred_usage_stored */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gssspi_acquire_cred_with_password) + ( + OM_uint32 *, /* minor_status */ + const gss_name_t, /* desired_name */ + const gss_buffer_t, /* password */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + int, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 * /* time_rec */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_export_cred) + ( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* cred_handle */ + gss_buffer_t /* token */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gss_import_cred) + ( + OM_uint32 *, /* minor_status */ + gss_buffer_t, /* token */ + gss_cred_id_t * /* cred_handle */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gssspi_import_sec_context_by_mech) + ( + OM_uint32 *, /* minor_status */ + gss_OID, /* desired_mech */ + gss_buffer_t, /* interprocess_token */ + gss_ctx_id_t * /* context_handle */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gssspi_import_name_by_mech) + ( + OM_uint32 *, /* minor_status */ + gss_OID, /* mech_type */ + gss_buffer_t, /* input_name_buffer */ + gss_OID, /* input_name_type */ + gss_name_t* /* output_name */ + /* */); + + OM_uint32 (KRB5_CALLCONV *gssspi_import_cred_by_mech) + ( + OM_uint32 *, /* minor_status */ + gss_OID, /* mech_type */ + gss_buffer_t, /* token */ + gss_cred_id_t * /* cred_handle */ + /* */); + + /* get_mic_iov extensions, added in 1.12 */ + + OM_uint32 (KRB5_CALLCONV *gss_get_mic_iov) + ( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_qop_t, /* qop_req */ + gss_iov_buffer_desc *, /* iov */ + int /* iov_count */ + ); + + OM_uint32 (KRB5_CALLCONV *gss_verify_mic_iov) + ( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_qop_t *, /* qop_state */ + gss_iov_buffer_desc *, /* iov */ + int /* iov_count */ + ); + + OM_uint32 (KRB5_CALLCONV *gss_get_mic_iov_length) + ( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_qop_t, /* qop_req */ + gss_iov_buffer_desc *, /* iov */ + int /* iov_count */ + ); + +} *gss_mechanism; + +/* + * In the user space we use a wrapper structure to encompass the + * mechanism entry points. The wrapper contain the mechanism + * entry points and other data which is only relevant to the gss-api + * layer. In the kernel we use only the gss_config strucutre because + * the kernal does not cantain any of the extra gss-api specific data. + */ +typedef struct gss_mech_config { + char *kmodName; /* kernel module name */ + char *uLibName; /* user library name */ + char *mechNameStr; /* mechanism string name */ + char *optionStr; /* optional mech parameters */ + void *dl_handle; /* RTLD object handle for the mech */ + gss_OID mech_type; /* mechanism oid */ + gss_mechanism mech; /* mechanism initialization struct */ + int priority; /* mechanism preference order */ + int freeMech; /* free mech table */ + int is_interposer; /* interposer mechanism flag */ + gss_OID int_mech_type; /* points to the interposer OID */ + gss_mechanism int_mech; /* points to the interposer mech */ + struct gss_mech_config *next; /* next element in the list */ +} *gss_mech_info; + +/********************************************************/ +/* Internal mechglue routines */ + +#if 0 +int gssint_mechglue_init(void); +void gssint_mechglue_fini(void); +#endif + +OM_uint32 gssint_select_mech_type(OM_uint32 *minor, gss_const_OID in_oid, + gss_OID *selected_oid); +gss_OID gssint_get_public_oid(gss_const_OID internal_oid); +OM_uint32 gssint_make_public_oid_set(OM_uint32 *minor_status, gss_OID oids, + int count, gss_OID_set *public_set); +gss_mechanism gssint_get_mechanism (gss_const_OID); +OM_uint32 gssint_get_mech_type (gss_OID, gss_buffer_t); +char *gssint_get_kmodName(const gss_OID); +char *gssint_get_modOptions(const gss_OID); +OM_uint32 gssint_import_internal_name (OM_uint32 *, gss_OID, gss_union_name_t, + gss_name_t *); +OM_uint32 gssint_export_internal_name(OM_uint32 *, const gss_OID, + const gss_name_t, gss_buffer_t); +OM_uint32 gssint_display_internal_name (OM_uint32 *, gss_OID, gss_name_t, + gss_buffer_t, gss_OID *); +OM_uint32 gssint_release_internal_name (OM_uint32 *, gss_OID, gss_name_t *); +OM_uint32 gssint_delete_internal_sec_context (OM_uint32 *, gss_OID, + gss_ctx_id_t *, gss_buffer_t); +#ifdef _GSS_STATIC_LINK +int gssint_register_mechinfo(gss_mech_info template); +#endif + +OM_uint32 gssint_convert_name_to_union_name + (OM_uint32 *, /* minor_status */ + gss_mechanism, /* mech */ + gss_name_t, /* internal_name */ + gss_name_t * /* external_name */ + ); +gss_cred_id_t gssint_get_mechanism_cred + (gss_union_cred_t, /* union_cred */ + gss_OID /* mech_type */ + ); + +OM_uint32 gssint_create_copy_buffer( + const gss_buffer_t, /* src buffer */ + gss_buffer_t *, /* destination buffer */ + int /* NULL terminate buffer ? */ +); + +OM_uint32 gssint_copy_oid_set( + OM_uint32 *, /* minor_status */ + const gss_OID_set_desc * const, /* oid set */ + gss_OID_set * /* new oid set */ +); + +gss_OID gss_find_mechanism_from_name_type (gss_OID); /* name_type */ + +OM_uint32 gss_add_mech_name_type + (OM_uint32 *, /* minor_status */ + gss_OID, /* name_type */ + gss_OID /* mech */ + ); + +/* + * Sun extensions to GSS-API v2 + */ + +int +gssint_get_der_length( + unsigned char **, /* buf */ + unsigned int, /* buf_len */ + unsigned int * /* bytes */ +); + +unsigned int +gssint_der_length_size(unsigned int /* len */); + +int +gssint_put_der_length( + unsigned int, /* length */ + unsigned char **, /* buf */ + unsigned int /* max_len */ +); + +OM_uint32 +gssint_wrap_aead (gss_mechanism, /* mech */ + OM_uint32 *, /* minor_status */ + gss_union_ctx_id_t, /* ctx */ + int, /* conf_req_flag */ + gss_qop_t, /* qop_req_flag */ + gss_buffer_t, /* input_assoc_buffer */ + gss_buffer_t, /* input_payload_buffer */ + int *, /* conf_state */ + gss_buffer_t); /* output_message_buffer */ +OM_uint32 +gssint_unwrap_aead (gss_mechanism, /* mech */ + OM_uint32 *, /* minor_status */ + gss_union_ctx_id_t, /* ctx */ + gss_buffer_t, /* input_message_buffer */ + gss_buffer_t, /* input_assoc_buffer */ + gss_buffer_t, /* output_payload_buffer */ + int *, /* conf_state */ + gss_qop_t *); /* qop_state */ + + +/* Use this to map an error code that was returned from a mech + operation; the mech will be asked to produce the associated error + messages. + + Remember that if the minor status code cannot be returned to the + caller (e.g., if it's stuffed in an automatic variable and then + ignored), then we don't care about producing a mapping. */ +#define map_error(MINORP, MECH) \ + (*(MINORP) = gssint_mecherrmap_map(*(MINORP), &(MECH)->mech_type)) +#define map_error_oid(MINORP, MECHOID) \ + (*(MINORP) = gssint_mecherrmap_map(*(MINORP), (MECHOID))) + +/* Use this to map an errno value or com_err error code being + generated within the mechglue code (e.g., by calling generic oid + ops). Any errno or com_err values produced by mech operations + should be processed with map_error. This means they'll be stored + separately even if the mech uses com_err, because we can't assume + that it will use com_err. */ +#define map_errcode(MINORP) \ + (*(MINORP) = gssint_mecherrmap_map_errcode(*(MINORP))) + +#endif /* _GSS_MECHGLUEP_H */ diff --git a/src/lib/gssapi/spnego/Makefile.in b/src/lib/gssapi/spnego/Makefile.in new file mode 100644 index 000000000000..c21ea230ca2a --- /dev/null +++ b/src/lib/gssapi/spnego/Makefile.in @@ -0,0 +1,24 @@ +mydir=lib$(S)gssapi$(S)spnego +BUILDTOP=$(REL)..$(S)..$(S).. +LOCALINCLUDES = -I. -I$(srcdir) -I$(srcdir)/.. -I../generic -I$(srcdir)/../generic -I../mechglue -I$(srcdir)/../mechglue +DEFINES=-D_GSS_STATIC_LINK=1 + +##DOS##BUILDTOP = ..\..\.. +##DOS##PREFIXDIR=spnego +##DOS##OBJFILE = ..\$(OUTPRE)spnego.lst + +##DOS##DLL_EXP_TYPE=GSS + +SRCS = $(srcdir)/spnego_mech.c + +OBJS = $(OUTPRE)spnego_mech.$(OBJEXT) + +STLIBOBJS = spnego_mech.o + +all-unix: all-libobjs + +##DOS##LIBOBJS = $(OBJS) + +clean-unix:: clean-libobjs + +@libobj_frag@ diff --git a/src/lib/gssapi/spnego/deps b/src/lib/gssapi/spnego/deps new file mode 100644 index 000000000000..feb409e732d5 --- /dev/null +++ b/src/lib/gssapi/spnego/deps @@ -0,0 +1,18 @@ +# +# Generated makefile dependencies follow. +# +spnego_mech.so spnego_mech.po $(OUTPRE)spnego_mech.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(srcdir)/../mechglue/mechglue.h $(srcdir)/../mechglue/mglueP.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_spnego.h spnego_mech.c diff --git a/src/lib/gssapi/spnego/gssapiP_spnego.h b/src/lib/gssapi/spnego/gssapiP_spnego.h new file mode 100644 index 000000000000..84c63fc17d2a --- /dev/null +++ b/src/lib/gssapi/spnego/gssapiP_spnego.h @@ -0,0 +1,664 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _GSSAPIP_SPNEGO_H_ +#define _GSSAPIP_SPNEGO_H_ + +/* #pragma ident "@(#)gssapiP_spnego.h 1.3 03/09/18 SMI" */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gssapi/gssapi.h> + +#define SEC_CONTEXT_TOKEN 1 +#define SPNEGO_SIZE_OF_INT 4 + +#define ACCEPT_COMPLETE 0 +#define ACCEPT_INCOMPLETE 1 +#define REJECT 2 +#define REQUEST_MIC 3 +#define ACCEPT_DEFECTIVE_TOKEN 0xffffffffUL + +/* + * constants for der encoding/decoding routines. + */ + +#define MECH_OID 0x06 +#define OCTET_STRING 0x04 +#define CONTEXT 0xa0 +#define SEQUENCE 0x30 +#define SEQUENCE_OF 0x30 +#define BIT_STRING 0x03 +#define BIT_STRING_LENGTH 0x02 +#define BIT_STRING_PADDING 0x01 +#define ENUMERATED 0x0a +#define ENUMERATION_LENGTH 1 +#define HEADER_ID 0x60 +#define GENERAL_STRING 0x1b + +/* + * SPNEGO specific error codes (minor status codes) + */ +#define ERR_SPNEGO_NO_MECHS_AVAILABLE 0x20000001 +#define ERR_SPNEGO_NO_CREDS_ACQUIRED 0x20000002 +#define ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR 0x20000003 +#define ERR_SPNEGO_NEGOTIATION_FAILED 0x20000004 +#define ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR 0x20000005 + +/* + * send_token_flag is used to indicate in later steps what type + * of token, if any should be sent or processed. + * NO_TOKEN_SEND = no token should be sent + * INIT_TOKEN_SEND = initial token will be sent + * CONT_TOKEN_SEND = continuing tokens to be sent + * CHECK_MIC = no token to be sent, but have a MIC to check. + * ERROR_TOKEN_SEND = error token from peer needs to be sent. + */ + +typedef enum {NO_TOKEN_SEND, INIT_TOKEN_SEND, CONT_TOKEN_SEND, + CHECK_MIC, ERROR_TOKEN_SEND} send_token_flag; + +/* + * The Mech OID: + * { iso(1) org(3) dod(6) internet(1) security(5) + * mechanism(5) spnego(2) } + */ + +#define SPNEGO_OID_LENGTH 6 +#define SPNEGO_OID "\053\006\001\005\005\002" + +typedef void *spnego_token_t; + +/* spnego name structure for internal representation. */ +typedef struct { + gss_OID type; + gss_buffer_t buffer; + gss_OID mech_type; + gss_name_t mech_name; +} spnego_name_desc, *spnego_name_t; + +/* Structure for credential */ +typedef struct { + gss_cred_id_t mcred; /* mechglue union of obtainable creds */ + gss_OID_set neg_mechs; /* app-specified list of allowable mechs */ + int no_ask_integ; /* do not request integ from mechs */ +} spnego_gss_cred_id_rec, *spnego_gss_cred_id_t; + +/* Structure for context handle */ +typedef struct { + OM_uint32 magic_num; + gss_buffer_desc DER_mechTypes; + gss_OID_set mech_set; + gss_OID internal_mech; /* alias into mech_set->elements */ + gss_ctx_id_t ctx_handle; + int mic_reqd; + int mic_sent; + int mic_rcvd; + int firstpass; + int mech_complete; + int nego_done; + int initiate; + int opened; + OM_uint32 ctx_flags; + gss_name_t internal_name; + gss_OID actual_mech; +} spnego_gss_ctx_id_rec, *spnego_gss_ctx_id_t; + +/* + * The magic number must be less than a standard pagesize + * to avoid a possible collision with a real address. + */ +#define SPNEGO_MAGIC_ID 0x00000fed + +/* SPNEGO oid declarations */ +extern const gss_OID_desc * const gss_mech_spnego; +extern const gss_OID_set_desc * const gss_mech_set_spnego; + +#if defined(DEBUG) && defined(HAVE_SYSLOG_H) +#include <syslog.h> +#define dsyslog(a) syslog(LOG_DEBUG, a) +#else +#define dsyslog(a) +#define SPNEGO_STATIC +#endif /* DEBUG */ + +/* + * declarations of internal name mechanism functions + */ + +OM_uint32 KRB5_CALLCONV spnego_gss_acquire_cred +( + OM_uint32 *, /* minor_status */ + gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 * /* time_rec */ +); + +OM_uint32 KRB5_CALLCONV spnego_gss_release_cred +( + OM_uint32 *, /* minor_status */ + /* CSTYLED */ + gss_cred_id_t * /* cred_handle */ +); + +OM_uint32 KRB5_CALLCONV spnego_gss_init_sec_context +( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* claimant_cred_handle */ + gss_ctx_id_t *, /* context_handle */ + gss_name_t, /* target_name */ + gss_OID, /* mech_type */ + OM_uint32, /* req_flags */ + OM_uint32, /* time_req */ + gss_channel_bindings_t, /* input_chan_bindings */ + gss_buffer_t, /* input_token */ + gss_OID *, /* actual_mech_type */ + gss_buffer_t, /* output_token */ + OM_uint32 *, /* ret_flags */ + OM_uint32 * /* time_rec */ +); + +#ifndef LEAN_CLIENT +OM_uint32 KRB5_CALLCONV spnego_gss_accept_sec_context +( + OM_uint32 *, /* minor_status */ + gss_ctx_id_t *, /* context_handle */ + gss_cred_id_t, /* verifier_cred_handle */ + gss_buffer_t, /* input_token_buffer */ + gss_channel_bindings_t, /* input_chan_bindings */ + gss_name_t *, /* src_name */ + gss_OID *, /* mech_type */ + gss_buffer_t, /* output_token */ + OM_uint32 *, /* ret_flags */ + OM_uint32 *, /* time_rec */ + /* CSTYLED */ + gss_cred_id_t * /* delegated_cred_handle */ +); +#endif /* LEAN_CLIENT */ + +OM_uint32 KRB5_CALLCONV spnego_gss_compare_name +( + OM_uint32 *, /* minor_status */ + const gss_name_t, /* name1 */ + const gss_name_t, /* name2 */ + int * /* name_equal */ +); + +OM_uint32 KRB5_CALLCONV spnego_gss_display_name +( + OM_uint32 *, /* minor_status */ + gss_name_t, /* input_name */ + gss_buffer_t, /* output_name_buffer */ + gss_OID * /* output_name_type */ +); + +OM_uint32 KRB5_CALLCONV spnego_gss_display_status +( + OM_uint32 *, /* minor_status */ + OM_uint32, /* status_value */ + int, /* status_type */ + gss_OID, /* mech_type */ + OM_uint32 *, /* message_context */ + gss_buffer_t /* status_string */ +); + +OM_uint32 KRB5_CALLCONV spnego_gss_import_name +( + OM_uint32 *, /* minor_status */ + gss_buffer_t, /* input_name_buffer */ + gss_OID, /* input_name_type */ + /* CSTYLED */ + gss_name_t * /* output_name */ +); + +OM_uint32 KRB5_CALLCONV spnego_gss_release_name +( + OM_uint32 *, /* minor_status */ + /* CSTYLED */ + gss_name_t * /* input_name */ +); + +OM_uint32 KRB5_CALLCONV spnego_gss_duplicate_name +( + OM_uint32 *, /* minor_status */ + /* CSTYLED */ + const gss_name_t, /* input_name */ + gss_name_t * /* output_name */ +); + +OM_uint32 KRB5_CALLCONV spnego_gss_inquire_cred +( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* cred_handle */ + gss_name_t *, /* name */ + OM_uint32 *, /* lifetime */ + int *, /* cred_usage */ + gss_OID_set * /* mechanisms */ +); + +OM_uint32 KRB5_CALLCONV spnego_gss_inquire_names_for_mech +( + OM_uint32 *, /* minor_status */ + gss_OID, /* mechanism */ + gss_OID_set * /* name_types */ +); + +OM_uint32 KRB5_CALLCONV spnego_gss_unwrap +( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_buffer_t input_message_buffer, + gss_buffer_t output_message_buffer, + int *conf_state, + gss_qop_t *qop_state +); + +OM_uint32 KRB5_CALLCONV spnego_gss_wrap +( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + gss_buffer_t input_message_buffer, + int *conf_state, + gss_buffer_t output_message_buffer +); + +OM_uint32 KRB5_CALLCONV spnego_gss_process_context_token +( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t token_buffer +); + +OM_uint32 KRB5_CALLCONV spnego_gss_delete_sec_context +( + OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token +); + +OM_uint32 KRB5_CALLCONV spnego_gss_context_time +( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + OM_uint32 *time_rec +); +#ifndef LEAN_CLIENT +OM_uint32 KRB5_CALLCONV spnego_gss_export_sec_context +( + OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t interprocess_token +); + +OM_uint32 KRB5_CALLCONV spnego_gss_import_sec_context +( + OM_uint32 *minor_status, + const gss_buffer_t interprocess_token, + gss_ctx_id_t *context_handle +); +#endif /* LEAN_CLIENT */ + +OM_uint32 KRB5_CALLCONV spnego_gss_inquire_context +( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + gss_name_t *src_name, + gss_name_t *targ_name, + OM_uint32 *lifetime_rec, + gss_OID *mech_type, + OM_uint32 *ctx_flags, + int *locally_initiated, + int *opened +); + +OM_uint32 KRB5_CALLCONV spnego_gss_wrap_size_limit +( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + OM_uint32 req_output_size, + OM_uint32 *max_input_size +); + +OM_uint32 KRB5_CALLCONV spnego_gss_get_mic +( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + gss_qop_t qop_req, + const gss_buffer_t message_buffer, + gss_buffer_t message_token +); + +OM_uint32 KRB5_CALLCONV spnego_gss_verify_mic +( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t msg_buffer, + const gss_buffer_t token_buffer, + gss_qop_t *qop_state +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_inquire_sec_context_by_oid +( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_inquire_cred_by_oid +( + OM_uint32 *minor_status, + const gss_cred_id_t cred_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_set_cred_option +( + OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_object, + const gss_buffer_t value +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_set_sec_context_option +( + OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + const gss_OID desired_object, + const gss_buffer_t value +); + +#ifdef _GSS_STATIC_LINK +int gss_spnegoint_lib_init(void); +void gss_spnegoint_lib_fini(void); +#else +gss_mechanism KRB5_CALLCONV gss_mech_initialize(void); +#endif /* _GSS_STATIC_LINK */ + +OM_uint32 KRB5_CALLCONV spnego_gss_wrap_aead +( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + gss_buffer_t input_assoc_buffer, + gss_buffer_t input_payload_buffer, + int *conf_state, + gss_buffer_t output_message_buffer +); + +OM_uint32 KRB5_CALLCONV spnego_gss_unwrap_aead +( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_buffer_t input_message_buffer, + gss_buffer_t input_assoc_buffer, + gss_buffer_t output_payload_buffer, + int *conf_state, + gss_qop_t *qop_state +); + +OM_uint32 KRB5_CALLCONV spnego_gss_wrap_iov +( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count +); + +OM_uint32 KRB5_CALLCONV spnego_gss_unwrap_iov +( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count +); + +OM_uint32 KRB5_CALLCONV spnego_gss_wrap_iov_length +( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_complete_auth_token +( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + gss_buffer_t input_message_buffer +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_acquire_cred_impersonate_name( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *); /* time_rec */ + +OM_uint32 KRB5_CALLCONV +spnego_gss_acquire_cred_with_password( + OM_uint32 *minor_status, + const gss_name_t desired_name, + const gss_buffer_t password, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec); + +OM_uint32 KRB5_CALLCONV +spnego_gss_display_name_ext +( + OM_uint32 *minor_status, + gss_name_t name, + gss_OID display_as_name_type, + gss_buffer_t display_name +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_inquire_name +( + OM_uint32 *minor_status, + gss_name_t name, + int *name_is_MN, + gss_OID *MN_mech, + gss_buffer_set_t *attrs +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_get_name_attribute +( + OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_set_name_attribute +( + OM_uint32 *minor_status, + gss_name_t name, + int complete, + gss_buffer_t attr, + gss_buffer_t value +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_delete_name_attribute +( + OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t attr +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_export_name_composite +( + OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t exp_composite_name +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_map_name_to_any +( + OM_uint32 *minor_status, + gss_name_t name, + int authenticated, + gss_buffer_t type_id, + gss_any_t *output +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_release_any_name_mapping +( + OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t type_id, + gss_any_t *input +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_pseudo_random +( + OM_uint32 *minor_status, + gss_ctx_id_t context, + int prf_key, + const gss_buffer_t prf_in, + ssize_t desired_output_len, + gss_buffer_t prf_out +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_set_neg_mechs +( + OM_uint32 *minor_status, + gss_cred_id_t cred_handle, + const gss_OID_set mech_list +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_inquire_mech_for_saslname +( + OM_uint32 *minor_status, + const gss_buffer_t sasl_mech_name, + gss_OID *mech_type +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_inquire_saslname_for_mech +( + OM_uint32 *minor_status, + const gss_OID desired_mech, + gss_buffer_t sasl_mech_name, + gss_buffer_t mech_name, + gss_buffer_t mech_description +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_inquire_attrs_for_mech +( + OM_uint32 *minor_status, + gss_const_OID mech, + gss_OID_set *mech_attrs, + gss_OID_set *known_mech_attrs +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_acquire_cred_from +( + OM_uint32 *minor_status, + const gss_name_t desired_name, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_const_key_value_set_t cred_store, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_export_cred( + OM_uint32 *minor_status, + gss_cred_id_t cred_handle, + gss_buffer_t token +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_import_cred( + OM_uint32 *minor_status, + gss_buffer_t token, + gss_cred_id_t *cred_handle +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_get_mic_iov( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_qop_t qop_req, + gss_iov_buffer_desc *iov, + int iov_count +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_verify_mic_iov( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count +); + +OM_uint32 KRB5_CALLCONV +spnego_gss_get_mic_iov_length( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_qop_t qop_req, + gss_iov_buffer_desc *iov, + int iov_count +); + +#ifdef __cplusplus +} +#endif + +#endif /* _GSSAPIP_SPNEGO_H_ */ diff --git a/src/lib/gssapi/spnego/mech_spnego.exports b/src/lib/gssapi/spnego/mech_spnego.exports new file mode 100644 index 000000000000..9d570e5c058e --- /dev/null +++ b/src/lib/gssapi/spnego/mech_spnego.exports @@ -0,0 +1 @@ +gss_mech_initialize diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c new file mode 100644 index 000000000000..9d6027ce8058 --- /dev/null +++ b/src/lib/gssapi/spnego/spnego_mech.c @@ -0,0 +1,4355 @@ +/* + * Copyright (C) 2006,2008 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. + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * A module that implements the spnego security mechanism. + * It is used to negotiate the security mechanism between + * peers using the GSS-API. SPNEGO is specified in RFC 4178. + * + */ +/* + * Copyright (c) 2006-2008, Novell, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The copyright holder's name is not used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* #pragma ident "@(#)spnego_mech.c 1.7 04/09/28 SMI" */ + +#include <k5-int.h> +#include <krb5.h> +#include <mglueP.h> +#include "gssapiP_spnego.h" +#include <gssapi_err_generic.h> + + +#undef g_token_size +#undef g_verify_token_header +#undef g_make_token_header + +#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED) +typedef const gss_OID_desc *gss_OID_const; + +/* der routines defined in libgss */ +extern unsigned int gssint_der_length_size(unsigned int); +extern int gssint_get_der_length(unsigned char **, unsigned int, + unsigned int*); +extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int); + + +/* private routines for spnego_mechanism */ +static spnego_token_t make_spnego_token(const char *); +static gss_buffer_desc make_err_msg(const char *); +static int g_token_size(gss_OID_const, unsigned int); +static int g_make_token_header(gss_OID_const, unsigned int, + unsigned char **, unsigned int); +static int g_verify_token_header(gss_OID_const, unsigned int *, + unsigned char **, + int, unsigned int); +static int g_verify_neg_token_init(unsigned char **, unsigned int); +static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t); +static gss_buffer_t get_input_token(unsigned char **, unsigned int); +static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int); +static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *); +static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t, + gss_const_key_value_set_t, + gss_cred_id_t *, gss_OID_set *, + OM_uint32 *); +static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t, + gss_cred_usage_t, gss_OID_set *); +static void release_spnego_ctx(spnego_gss_ctx_id_t *); +static spnego_gss_ctx_id_t create_spnego_ctx(int); +static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf); +static int put_input_token(unsigned char **, gss_buffer_t, unsigned int); +static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int); +static int put_negResult(unsigned char **, OM_uint32, unsigned int); + +static OM_uint32 +process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t, + gss_buffer_t *, OM_uint32 *, send_token_flag *); +static OM_uint32 +handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t, + gss_buffer_t *, OM_uint32 *, send_token_flag *); + +static OM_uint32 +init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, send_token_flag *, + spnego_gss_ctx_id_t *); +static OM_uint32 +init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID, + gss_buffer_t *, gss_buffer_t *, + OM_uint32 *, send_token_flag *); +static OM_uint32 +init_ctx_cont(OM_uint32 *, spnego_gss_ctx_id_t, gss_buffer_t, + gss_buffer_t *, gss_buffer_t *, + OM_uint32 *, send_token_flag *); +static OM_uint32 +init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, + gss_OID, gss_buffer_t *, gss_buffer_t *, + OM_uint32 *, send_token_flag *); +static OM_uint32 +init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t, + gss_name_t, OM_uint32, OM_uint32, gss_buffer_t, + gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *, + OM_uint32 *, send_token_flag *); + +static OM_uint32 +acc_ctx_new(OM_uint32 *, gss_buffer_t, spnego_gss_cred_id_t, gss_buffer_t *, + gss_buffer_t *, OM_uint32 *, send_token_flag *, + spnego_gss_ctx_id_t *); +static OM_uint32 +acc_ctx_cont(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t, gss_buffer_t *, + gss_buffer_t *, OM_uint32 *, send_token_flag *); +static OM_uint32 +acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID, + OM_uint32 *, send_token_flag *); +static OM_uint32 +acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t, + gss_buffer_t, gss_OID *, gss_buffer_t, + OM_uint32 *, OM_uint32 *, gss_cred_id_t *, + OM_uint32 *, send_token_flag *); + +static gss_OID +negotiate_mech(gss_OID_set, gss_OID_set, OM_uint32 *); +static int +g_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *); + +static int +make_spnego_tokenInit_msg(spnego_gss_ctx_id_t, + int, + gss_buffer_t, + OM_uint32, gss_buffer_t, send_token_flag, + gss_buffer_t); +static int +make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t, + gss_buffer_t, send_token_flag, + gss_buffer_t); + +static OM_uint32 +get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t, + gss_OID_set *, OM_uint32 *, gss_buffer_t *, + gss_buffer_t *); +static OM_uint32 +get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int, + OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *); + +static int +is_kerb_mech(gss_OID oid); + +/* SPNEGO oid structure */ +static const gss_OID_desc spnego_oids[] = { + {SPNEGO_OID_LENGTH, SPNEGO_OID}, +}; + +const gss_OID_desc * const gss_mech_spnego = spnego_oids+0; +static const gss_OID_set_desc spnego_oidsets[] = { + {1, (gss_OID) spnego_oids+0}, +}; +const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0; + +static int make_NegHints(OM_uint32 *, gss_buffer_t *); +static int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int); +static OM_uint32 +acc_ctx_hints(OM_uint32 *, spnego_gss_cred_id_t, gss_buffer_t *, OM_uint32 *, + send_token_flag *, spnego_gss_ctx_id_t *); + +/* + * The Mech OID for SPNEGO: + * { iso(1) org(3) dod(6) internet(1) security(5) + * mechanism(5) spnego(2) } + */ +static struct gss_config spnego_mechanism = +{ + {SPNEGO_OID_LENGTH, SPNEGO_OID}, + NULL, + spnego_gss_acquire_cred, + spnego_gss_release_cred, + spnego_gss_init_sec_context, +#ifndef LEAN_CLIENT + spnego_gss_accept_sec_context, +#else + NULL, +#endif /* LEAN_CLIENT */ + NULL, /* gss_process_context_token */ + spnego_gss_delete_sec_context, /* gss_delete_sec_context */ + spnego_gss_context_time, /* gss_context_time */ + spnego_gss_get_mic, /* gss_get_mic */ + spnego_gss_verify_mic, /* gss_verify_mic */ + spnego_gss_wrap, /* gss_wrap */ + spnego_gss_unwrap, /* gss_unwrap */ + spnego_gss_display_status, + NULL, /* gss_indicate_mechs */ + spnego_gss_compare_name, + spnego_gss_display_name, + spnego_gss_import_name, + spnego_gss_release_name, + spnego_gss_inquire_cred, /* gss_inquire_cred */ + NULL, /* gss_add_cred */ +#ifndef LEAN_CLIENT + spnego_gss_export_sec_context, /* gss_export_sec_context */ + spnego_gss_import_sec_context, /* gss_import_sec_context */ +#else + NULL, /* gss_export_sec_context */ + NULL, /* gss_import_sec_context */ +#endif /* LEAN_CLIENT */ + NULL, /* gss_inquire_cred_by_mech */ + spnego_gss_inquire_names_for_mech, + spnego_gss_inquire_context, /* gss_inquire_context */ + NULL, /* gss_internal_release_oid */ + spnego_gss_wrap_size_limit, /* gss_wrap_size_limit */ + NULL, /* gssd_pname_to_uid */ + NULL, /* gss_userok */ + NULL, /* gss_export_name */ + spnego_gss_duplicate_name, /* gss_duplicate_name */ + NULL, /* gss_store_cred */ + spnego_gss_inquire_sec_context_by_oid, /* gss_inquire_sec_context_by_oid */ + spnego_gss_inquire_cred_by_oid, /* gss_inquire_cred_by_oid */ + spnego_gss_set_sec_context_option, /* gss_set_sec_context_option */ + spnego_gss_set_cred_option, /* gssspi_set_cred_option */ + NULL, /* gssspi_mech_invoke */ + spnego_gss_wrap_aead, + spnego_gss_unwrap_aead, + spnego_gss_wrap_iov, + spnego_gss_unwrap_iov, + spnego_gss_wrap_iov_length, + spnego_gss_complete_auth_token, + spnego_gss_acquire_cred_impersonate_name, + NULL, /* gss_add_cred_impersonate_name */ + spnego_gss_display_name_ext, + spnego_gss_inquire_name, + spnego_gss_get_name_attribute, + spnego_gss_set_name_attribute, + spnego_gss_delete_name_attribute, + spnego_gss_export_name_composite, + spnego_gss_map_name_to_any, + spnego_gss_release_any_name_mapping, + spnego_gss_pseudo_random, + spnego_gss_set_neg_mechs, + spnego_gss_inquire_saslname_for_mech, + spnego_gss_inquire_mech_for_saslname, + spnego_gss_inquire_attrs_for_mech, + spnego_gss_acquire_cred_from, + NULL, /* gss_store_cred_into */ + spnego_gss_acquire_cred_with_password, + spnego_gss_export_cred, + spnego_gss_import_cred, + NULL, /* gssspi_import_sec_context_by_mech */ + NULL, /* gssspi_import_name_by_mech */ + NULL, /* gssspi_import_cred_by_mech */ + spnego_gss_get_mic_iov, + spnego_gss_verify_mic_iov, + spnego_gss_get_mic_iov_length +}; + +#ifdef _GSS_STATIC_LINK +#include "mglueP.h" + +static int gss_spnegomechglue_init(void) +{ + struct gss_mech_config mech_spnego; + + memset(&mech_spnego, 0, sizeof(mech_spnego)); + mech_spnego.mech = &spnego_mechanism; + mech_spnego.mechNameStr = "spnego"; + mech_spnego.mech_type = GSS_C_NO_OID; + + return gssint_register_mechinfo(&mech_spnego); +} +#else +gss_mechanism KRB5_CALLCONV +gss_mech_initialize(void) +{ + return (&spnego_mechanism); +} + +MAKE_INIT_FUNCTION(gss_krb5int_lib_init); +MAKE_FINI_FUNCTION(gss_krb5int_lib_fini); +int gss_krb5int_lib_init(void); +#endif /* _GSS_STATIC_LINK */ + +int gss_spnegoint_lib_init(void) +{ + int err; + + err = k5_key_register(K5_KEY_GSS_SPNEGO_STATUS, NULL); + if (err) + return err; + +#ifdef _GSS_STATIC_LINK + return gss_spnegomechglue_init(); +#else + return 0; +#endif +} + +void gss_spnegoint_lib_fini(void) +{ +} + +static OM_uint32 +create_spnego_cred(OM_uint32 *minor_status, gss_cred_id_t mcred, + spnego_gss_cred_id_t *cred_out) +{ + spnego_gss_cred_id_t spcred; + + *cred_out = NULL; + spcred = calloc(1, sizeof(spnego_gss_cred_id_rec)); + if (spcred == NULL) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + spcred->mcred = mcred; + *cred_out = spcred; + return GSS_S_COMPLETE; +} + +/*ARGSUSED*/ +OM_uint32 KRB5_CALLCONV +spnego_gss_acquire_cred(OM_uint32 *minor_status, + gss_name_t desired_name, + OM_uint32 time_req, + gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + return spnego_gss_acquire_cred_from(minor_status, desired_name, time_req, + desired_mechs, cred_usage, NULL, + output_cred_handle, actual_mechs, + time_rec); +} + +/*ARGSUSED*/ +OM_uint32 KRB5_CALLCONV +spnego_gss_acquire_cred_from(OM_uint32 *minor_status, + const gss_name_t desired_name, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_const_key_value_set_t cred_store, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + OM_uint32 status, tmpmin; + gss_OID_set amechs; + gss_cred_id_t mcred = NULL; + spnego_gss_cred_id_t spcred = NULL; + dsyslog("Entering spnego_gss_acquire_cred\n"); + + if (actual_mechs) + *actual_mechs = NULL; + + if (time_rec) + *time_rec = 0; + + /* We will obtain a mechglue credential and wrap it in a + * spnego_gss_cred_id_rec structure. Allocate the wrapper. */ + status = create_spnego_cred(minor_status, mcred, &spcred); + if (status != GSS_S_COMPLETE) + return (status); + + /* + * Always use get_available_mechs to collect a list of + * mechs for which creds are available. + */ + status = get_available_mechs(minor_status, desired_name, + cred_usage, cred_store, &mcred, + &amechs, time_rec); + + if (actual_mechs && amechs != GSS_C_NULL_OID_SET) { + (void) gssint_copy_oid_set(&tmpmin, amechs, actual_mechs); + } + (void) gss_release_oid_set(&tmpmin, &amechs); + + if (status == GSS_S_COMPLETE) { + spcred->mcred = mcred; + *output_cred_handle = (gss_cred_id_t)spcred; + } else { + free(spcred); + *output_cred_handle = GSS_C_NO_CREDENTIAL; + } + + dsyslog("Leaving spnego_gss_acquire_cred\n"); + return (status); +} + +/*ARGSUSED*/ +OM_uint32 KRB5_CALLCONV +spnego_gss_release_cred(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle) +{ + spnego_gss_cred_id_t spcred = NULL; + + dsyslog("Entering spnego_gss_release_cred\n"); + + if (minor_status == NULL || cred_handle == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + *minor_status = 0; + + if (*cred_handle == GSS_C_NO_CREDENTIAL) + return (GSS_S_COMPLETE); + + spcred = (spnego_gss_cred_id_t)*cred_handle; + *cred_handle = GSS_C_NO_CREDENTIAL; + gss_release_oid_set(minor_status, &spcred->neg_mechs); + gss_release_cred(minor_status, &spcred->mcred); + free(spcred); + + dsyslog("Leaving spnego_gss_release_cred\n"); + return (GSS_S_COMPLETE); +} + +static spnego_gss_ctx_id_t +create_spnego_ctx(int initiate) +{ + spnego_gss_ctx_id_t spnego_ctx = NULL; + spnego_ctx = (spnego_gss_ctx_id_t) + malloc(sizeof (spnego_gss_ctx_id_rec)); + + if (spnego_ctx == NULL) { + return (NULL); + } + + spnego_ctx->magic_num = SPNEGO_MAGIC_ID; + spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT; + spnego_ctx->mech_set = NULL; + spnego_ctx->internal_mech = NULL; + spnego_ctx->DER_mechTypes.length = 0; + spnego_ctx->DER_mechTypes.value = NULL; + spnego_ctx->mic_reqd = 0; + spnego_ctx->mic_sent = 0; + spnego_ctx->mic_rcvd = 0; + spnego_ctx->mech_complete = 0; + spnego_ctx->nego_done = 0; + spnego_ctx->opened = 0; + spnego_ctx->initiate = initiate; + spnego_ctx->internal_name = GSS_C_NO_NAME; + spnego_ctx->actual_mech = GSS_C_NO_OID; + + return (spnego_ctx); +} + +/* iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) samba(7165) + * gssntlmssp(655) controls(1) spnego_req_mechlistMIC(2) */ +static const gss_OID_desc spnego_req_mechlistMIC_oid = + { 11, "\x2B\x06\x01\x04\x01\xB7\x7D\x85\x0F\x01\x02" }; + +/* + * Return nonzero if the mechanism has reason to believe that a mechlistMIC + * exchange will be required. Microsoft servers erroneously require SPNEGO + * mechlistMIC if they see an internal MIC within an NTLMSSP Authenticate + * message, even if NTLMSSP was the preferred mechanism. + */ +static int +mech_requires_mechlistMIC(spnego_gss_ctx_id_t sc) +{ + OM_uint32 major, minor; + gss_ctx_id_t ctx = sc->ctx_handle; + gss_OID oid = (gss_OID)&spnego_req_mechlistMIC_oid; + gss_buffer_set_t bufs; + int result; + + major = gss_inquire_sec_context_by_oid(&minor, ctx, oid, &bufs); + if (major != GSS_S_COMPLETE) + return 0; + + /* Report true if the mech returns a single buffer containing a single + * byte with value 1. */ + result = (bufs != NULL && bufs->count == 1 && + bufs->elements[0].length == 1 && + memcmp(bufs->elements[0].value, "\1", 1) == 0); + (void) gss_release_buffer_set(&minor, &bufs); + return result; +} + +/* iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) Microsoft(311) + * security(2) mechanisms(2) NTLM(10) */ +static const gss_OID_desc gss_mech_ntlmssp_oid = + { 10, "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a" }; + +/* iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) samba(7165) + * gssntlmssp(655) controls(1) ntlmssp_reset_crypto(3) */ +static const gss_OID_desc ntlmssp_reset_crypto_oid = + { 11, "\x2B\x06\x01\x04\x01\xB7\x7D\x85\x0F\x01\x03" }; + +/* + * MS-SPNG section 3.3.5.1 warns that the NTLM mechanism requires special + * handling of the crypto state to interop with Windows. If the mechanism for + * sc is SPNEGO, invoke a mechanism-specific operation on the context to reset + * the RC4 state after producing or verifying a MIC. Ignore a result of + * GSS_S_UNAVAILABLE for compatibility with older versions of the mechanism + * that do not support this functionality. + */ +static OM_uint32 +ntlmssp_reset_crypto_state(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc, + OM_uint32 verify) +{ + OM_uint32 major, minor; + gss_buffer_desc value; + + if (!g_OID_equal(sc->internal_mech, &gss_mech_ntlmssp_oid)) + return GSS_S_COMPLETE; + + value.length = sizeof(verify); + value.value = &verify; + major = gss_set_sec_context_option(&minor, &sc->ctx_handle, + (gss_OID)&ntlmssp_reset_crypto_oid, + &value); + if (major == GSS_S_UNAVAILABLE) + return GSS_S_COMPLETE; + *minor_status = minor; + return major; +} + +/* + * Both initiator and acceptor call here to verify and/or create mechListMIC, + * and to consistency-check the MIC state. handle_mic is invoked only if the + * negotiated mech has completed and supports MICs. + */ +static OM_uint32 +handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in, + int send_mechtok, spnego_gss_ctx_id_t sc, + gss_buffer_t *mic_out, + OM_uint32 *negState, send_token_flag *tokflag) +{ + OM_uint32 ret; + + ret = GSS_S_FAILURE; + *mic_out = GSS_C_NO_BUFFER; + if (mic_in != GSS_C_NO_BUFFER) { + if (sc->mic_rcvd) { + /* Reject MIC if we've already received a MIC. */ + *negState = REJECT; + *tokflag = ERROR_TOKEN_SEND; + return GSS_S_DEFECTIVE_TOKEN; + } + } else if (sc->mic_reqd && !send_mechtok) { + /* + * If the peer sends the final mechanism token, it + * must send the MIC with that token if the + * negotiation requires MICs. + */ + *negState = REJECT; + *tokflag = ERROR_TOKEN_SEND; + return GSS_S_DEFECTIVE_TOKEN; + } + ret = process_mic(minor_status, mic_in, sc, mic_out, + negState, tokflag); + if (ret != GSS_S_COMPLETE) { + return ret; + } + if (sc->mic_reqd) { + assert(sc->mic_sent || sc->mic_rcvd); + } + if (sc->mic_sent && sc->mic_rcvd) { + ret = GSS_S_COMPLETE; + *negState = ACCEPT_COMPLETE; + if (*mic_out == GSS_C_NO_BUFFER) { + /* + * We sent a MIC on the previous pass; we + * shouldn't be sending a mechanism token. + */ + assert(!send_mechtok); + *tokflag = NO_TOKEN_SEND; + } else { + *tokflag = CONT_TOKEN_SEND; + } + } else if (sc->mic_reqd) { + *negState = ACCEPT_INCOMPLETE; + ret = GSS_S_CONTINUE_NEEDED; + } else if (*negState == ACCEPT_COMPLETE) { + ret = GSS_S_COMPLETE; + } else { + ret = GSS_S_CONTINUE_NEEDED; + } + return ret; +} + +/* + * Perform the actual verification and/or generation of mechListMIC. + */ +static OM_uint32 +process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in, + spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out, + OM_uint32 *negState, send_token_flag *tokflag) +{ + OM_uint32 ret, tmpmin; + gss_qop_t qop_state; + gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER; + + ret = GSS_S_FAILURE; + if (mic_in != GSS_C_NO_BUFFER) { + ret = gss_verify_mic(minor_status, sc->ctx_handle, + &sc->DER_mechTypes, + mic_in, &qop_state); + if (ret == GSS_S_COMPLETE) + ret = ntlmssp_reset_crypto_state(minor_status, sc, 1); + if (ret != GSS_S_COMPLETE) { + *negState = REJECT; + *tokflag = ERROR_TOKEN_SEND; + return ret; + } + /* If we got a MIC, we must send a MIC. */ + sc->mic_reqd = 1; + sc->mic_rcvd = 1; + } + if (sc->mic_reqd && !sc->mic_sent) { + ret = gss_get_mic(minor_status, sc->ctx_handle, + GSS_C_QOP_DEFAULT, + &sc->DER_mechTypes, + &tmpmic); + if (ret == GSS_S_COMPLETE) + ret = ntlmssp_reset_crypto_state(minor_status, sc, 0); + if (ret != GSS_S_COMPLETE) { + gss_release_buffer(&tmpmin, &tmpmic); + *tokflag = NO_TOKEN_SEND; + return ret; + } + *mic_out = malloc(sizeof(gss_buffer_desc)); + if (*mic_out == GSS_C_NO_BUFFER) { + gss_release_buffer(&tmpmin, &tmpmic); + *tokflag = NO_TOKEN_SEND; + return GSS_S_FAILURE; + } + **mic_out = tmpmic; + sc->mic_sent = 1; + } + return GSS_S_COMPLETE; +} + +/* Create a new SPNEGO context handle for the initial call to + * spnego_gss_init_sec_context(). */ +static OM_uint32 +init_ctx_new(OM_uint32 *minor_status, + spnego_gss_cred_id_t spcred, + send_token_flag *tokflag, + spnego_gss_ctx_id_t *sc_out) +{ + OM_uint32 ret; + spnego_gss_ctx_id_t sc = NULL; + + *sc_out = NULL; + + sc = create_spnego_ctx(1); + if (sc == NULL) + return GSS_S_FAILURE; + + /* determine negotiation mech set */ + ret = get_negotiable_mechs(minor_status, spcred, GSS_C_INITIATE, + &sc->mech_set); + if (ret != GSS_S_COMPLETE) + goto cleanup; + + /* Set an initial internal mech to make the first context token. */ + sc->internal_mech = &sc->mech_set->elements[0]; + + if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0) { + ret = GSS_S_FAILURE; + goto cleanup; + } + + sc->ctx_handle = GSS_C_NO_CONTEXT; + *sc_out = sc; + sc = NULL; + *tokflag = INIT_TOKEN_SEND; + ret = GSS_S_CONTINUE_NEEDED; + +cleanup: + release_spnego_ctx(&sc); + return ret; +} + +/* + * Called by second and later calls to spnego_gss_init_sec_context() + * to decode reply and update state. + */ +static OM_uint32 +init_ctx_cont(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc, + gss_buffer_t buf, gss_buffer_t *responseToken, + gss_buffer_t *mechListMIC, OM_uint32 *negState, + send_token_flag *tokflag) +{ + OM_uint32 ret, tmpmin, acc_negState; + unsigned char *ptr; + gss_OID supportedMech = GSS_C_NO_OID; + + *negState = REJECT; + *tokflag = ERROR_TOKEN_SEND; + + ptr = buf->value; + ret = get_negTokenResp(minor_status, ptr, buf->length, + &acc_negState, &supportedMech, + responseToken, mechListMIC); + if (ret != GSS_S_COMPLETE) + goto cleanup; + if (acc_negState == REJECT) { + *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED; + map_errcode(minor_status); + *tokflag = NO_TOKEN_SEND; + ret = GSS_S_FAILURE; + goto cleanup; + } + /* + * nego_done is false for the first call to init_ctx_cont() + */ + if (!sc->nego_done) { + ret = init_ctx_nego(minor_status, sc, + acc_negState, + supportedMech, responseToken, + mechListMIC, + negState, tokflag); + } else if ((!sc->mech_complete && *responseToken == GSS_C_NO_BUFFER) || + (sc->mech_complete && *responseToken != GSS_C_NO_BUFFER)) { + /* Missing or spurious token from acceptor. */ + ret = GSS_S_DEFECTIVE_TOKEN; + } else if (!sc->mech_complete || + (sc->mic_reqd && + (sc->ctx_flags & GSS_C_INTEG_FLAG))) { + /* Not obviously done; we may decide we're done later in + * init_ctx_call_init or handle_mic. */ + *negState = ACCEPT_INCOMPLETE; + *tokflag = CONT_TOKEN_SEND; + ret = GSS_S_CONTINUE_NEEDED; + } else { + /* mech finished on last pass and no MIC required, so done. */ + *negState = ACCEPT_COMPLETE; + *tokflag = NO_TOKEN_SEND; + ret = GSS_S_COMPLETE; + } +cleanup: + if (supportedMech != GSS_C_NO_OID) + generic_gss_release_oid(&tmpmin, &supportedMech); + return ret; +} + +/* + * Consistency checking and mechanism negotiation handling for second + * call of spnego_gss_init_sec_context(). Call init_ctx_reselect() to + * update internal state if acceptor has counter-proposed. + */ +static OM_uint32 +init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc, + OM_uint32 acc_negState, gss_OID supportedMech, + gss_buffer_t *responseToken, gss_buffer_t *mechListMIC, + OM_uint32 *negState, send_token_flag *tokflag) +{ + OM_uint32 ret; + + *negState = REJECT; + *tokflag = ERROR_TOKEN_SEND; + ret = GSS_S_DEFECTIVE_TOKEN; + + /* + * According to RFC 4178, both supportedMech and negState must be + * present in the first acceptor token. However, some Java + * implementations include only a responseToken in the first + * NegTokenResp. In this case we can use sc->internal_mech as the + * negotiated mechanism. (We do not currently look at acc_negState + * when continuing with the optimistic mechanism.) + */ + if (supportedMech == GSS_C_NO_OID) + supportedMech = sc->internal_mech; + + /* + * If the mechanism we sent is not the mechanism returned from + * the server, we need to handle the server's counter + * proposal. There is a bug in SAMBA servers that always send + * the old Kerberos mech OID, even though we sent the new one. + * So we will treat all the Kerberos mech OIDS as the same. + */ + if (!(is_kerb_mech(supportedMech) && + is_kerb_mech(sc->internal_mech)) && + !g_OID_equal(supportedMech, sc->internal_mech)) { + ret = init_ctx_reselect(minor_status, sc, + acc_negState, supportedMech, + responseToken, mechListMIC, + negState, tokflag); + + } else if (*responseToken == GSS_C_NO_BUFFER) { + if (sc->mech_complete) { + /* + * Mech completed on first call to its + * init_sec_context(). Acceptor sends no mech + * token. + */ + *negState = ACCEPT_COMPLETE; + *tokflag = NO_TOKEN_SEND; + ret = GSS_S_COMPLETE; + } else { + /* + * Reject missing mech token when optimistic + * mech selected. + */ + *minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR; + map_errcode(minor_status); + ret = GSS_S_DEFECTIVE_TOKEN; + } + } else if ((*responseToken)->length == 0 && sc->mech_complete) { + /* Handle old IIS servers returning empty token instead of + * null tokens in the non-mutual auth case. */ + *negState = ACCEPT_COMPLETE; + *tokflag = NO_TOKEN_SEND; + ret = GSS_S_COMPLETE; + } else if (sc->mech_complete) { + /* Reject spurious mech token. */ + ret = GSS_S_DEFECTIVE_TOKEN; + } else { + *negState = ACCEPT_INCOMPLETE; + *tokflag = CONT_TOKEN_SEND; + ret = GSS_S_CONTINUE_NEEDED; + } + sc->nego_done = 1; + return ret; +} + +/* + * Handle acceptor's counter-proposal of an alternative mechanism. + */ +static OM_uint32 +init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc, + OM_uint32 acc_negState, gss_OID supportedMech, + gss_buffer_t *responseToken, gss_buffer_t *mechListMIC, + OM_uint32 *negState, send_token_flag *tokflag) +{ + OM_uint32 tmpmin; + size_t i; + + gss_delete_sec_context(&tmpmin, &sc->ctx_handle, + GSS_C_NO_BUFFER); + + /* Find supportedMech in sc->mech_set. */ + for (i = 0; i < sc->mech_set->count; i++) { + if (g_OID_equal(supportedMech, &sc->mech_set->elements[i])) + break; + } + if (i == sc->mech_set->count) + return GSS_S_DEFECTIVE_TOKEN; + sc->internal_mech = &sc->mech_set->elements[i]; + + /* + * A server conforming to RFC4178 MUST set REQUEST_MIC here, but + * Windows Server 2003 and earlier implement (roughly) RFC2478 instead, + * and send ACCEPT_INCOMPLETE. Tolerate that only if we are falling + * back to NTLMSSP. + */ + if (acc_negState == ACCEPT_INCOMPLETE) { + if (!g_OID_equal(supportedMech, &gss_mech_ntlmssp_oid)) + return GSS_S_DEFECTIVE_TOKEN; + } else if (acc_negState != REQUEST_MIC) { + return GSS_S_DEFECTIVE_TOKEN; + } + + sc->mech_complete = 0; + sc->mic_reqd = (acc_negState == REQUEST_MIC); + *negState = acc_negState; + *tokflag = CONT_TOKEN_SEND; + return GSS_S_CONTINUE_NEEDED; +} + +/* + * Wrap call to mechanism gss_init_sec_context() and update state + * accordingly. + */ +static OM_uint32 +init_ctx_call_init(OM_uint32 *minor_status, + spnego_gss_ctx_id_t sc, + spnego_gss_cred_id_t spcred, + gss_name_t target_name, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_buffer_t mechtok_in, + gss_OID *actual_mech, + gss_buffer_t mechtok_out, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + OM_uint32 *negState, + send_token_flag *send_token) +{ + OM_uint32 ret, tmpret, tmpmin, mech_req_flags; + gss_cred_id_t mcred; + + mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred; + + mech_req_flags = req_flags; + if (spcred == NULL || !spcred->no_ask_integ) + mech_req_flags |= GSS_C_INTEG_FLAG; + + ret = gss_init_sec_context(minor_status, + mcred, + &sc->ctx_handle, + target_name, + sc->internal_mech, + mech_req_flags, + time_req, + GSS_C_NO_CHANNEL_BINDINGS, + mechtok_in, + &sc->actual_mech, + mechtok_out, + &sc->ctx_flags, + time_rec); + if (ret == GSS_S_COMPLETE) { + sc->mech_complete = 1; + if (ret_flags != NULL) + *ret_flags = sc->ctx_flags; + /* + * Microsoft SPNEGO implementations expect an even number of + * token exchanges. So if we're sending a final token, ask for + * a zero-length token back from the server. Also ask for a + * token back if this is the first token or if a MIC exchange + * is required. + */ + if (*send_token == CONT_TOKEN_SEND && + mechtok_out->length == 0 && + (!sc->mic_reqd || + !(sc->ctx_flags & GSS_C_INTEG_FLAG))) { + /* The exchange is complete. */ + *negState = ACCEPT_COMPLETE; + ret = GSS_S_COMPLETE; + *send_token = NO_TOKEN_SEND; + } else { + /* Ask for one more hop. */ + *negState = ACCEPT_INCOMPLETE; + ret = GSS_S_CONTINUE_NEEDED; + } + return ret; + } + + if (ret == GSS_S_CONTINUE_NEEDED) + return ret; + + if (*send_token != INIT_TOKEN_SEND) { + *send_token = ERROR_TOKEN_SEND; + *negState = REJECT; + return ret; + } + + /* + * Since this is the first token, we can fall back to later mechanisms + * in the list. Since the mechanism list is expected to be short, we + * can do this with recursion. If all mechanisms produce errors, the + * caller should get the error from the first mech in the list. + */ + gssalloc_free(sc->mech_set->elements->elements); + memmove(sc->mech_set->elements, sc->mech_set->elements + 1, + --sc->mech_set->count * sizeof(*sc->mech_set->elements)); + if (sc->mech_set->count == 0) + goto fail; + gss_release_buffer(&tmpmin, &sc->DER_mechTypes); + if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0) + goto fail; + tmpret = init_ctx_call_init(&tmpmin, sc, spcred, target_name, + req_flags, time_req, mechtok_in, + actual_mech, mechtok_out, ret_flags, + time_rec, negState, send_token); + if (HARD_ERROR(tmpret)) + goto fail; + *minor_status = tmpmin; + return tmpret; + +fail: + /* Don't output token on error from first call. */ + *send_token = NO_TOKEN_SEND; + *negState = REJECT; + return ret; +} + +/*ARGSUSED*/ +OM_uint32 KRB5_CALLCONV +spnego_gss_init_sec_context( + OM_uint32 *minor_status, + gss_cred_id_t claimant_cred_handle, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec) +{ + send_token_flag send_token = NO_TOKEN_SEND; + OM_uint32 tmpmin, ret, negState; + gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out; + gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER; + spnego_gss_cred_id_t spcred = NULL; + spnego_gss_ctx_id_t spnego_ctx = NULL; + + dsyslog("Entering init_sec_context\n"); + + mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER; + negState = REJECT; + + /* + * This function works in three steps: + * + * 1. Perform mechanism negotiation. + * 2. Invoke the negotiated or optimistic mech's gss_init_sec_context + * function and examine the results. + * 3. Process or generate MICs if necessary. + * + * The three steps share responsibility for determining when the + * exchange is complete. If the selected mech completed in a previous + * call and no MIC exchange is expected, then step 1 will decide. If + * the selected mech completes in this call and no MIC exchange is + * expected, then step 2 will decide. If a MIC exchange is expected, + * then step 3 will decide. If an error occurs in any step, the + * exchange will be aborted, possibly with an error token. + * + * negState determines the state of the negotiation, and is + * communicated to the acceptor if a continuing token is sent. + * send_token is used to indicate what type of token, if any, should be + * generated. + */ + + /* Validate arguments. */ + if (minor_status != NULL) + *minor_status = 0; + if (output_token != GSS_C_NO_BUFFER) { + output_token->length = 0; + output_token->value = NULL; + } + if (minor_status == NULL || + output_token == GSS_C_NO_BUFFER || + context_handle == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (actual_mech != NULL) + *actual_mech = GSS_C_NO_OID; + + /* Step 1: perform mechanism negotiation. */ + spcred = (spnego_gss_cred_id_t)claimant_cred_handle; + spnego_ctx = (spnego_gss_ctx_id_t)*context_handle; + if (spnego_ctx == NULL) { + ret = init_ctx_new(minor_status, spcred, &send_token, + &spnego_ctx); + if (ret != GSS_S_CONTINUE_NEEDED) { + goto cleanup; + } + *context_handle = (gss_ctx_id_t)spnego_ctx; + } else { + ret = init_ctx_cont(minor_status, spnego_ctx, + input_token, &mechtok_in, + &mechListMIC_in, &negState, &send_token); + if (HARD_ERROR(ret)) { + goto cleanup; + } + } + + /* Step 2: invoke the selected or optimistic mechanism's + * gss_init_sec_context function, if it didn't complete previously. */ + if (!spnego_ctx->mech_complete) { + ret = init_ctx_call_init( + minor_status, spnego_ctx, spcred, + target_name, req_flags, + time_req, mechtok_in, + actual_mech, &mechtok_out, + ret_flags, time_rec, + &negState, &send_token); + + /* Give the mechanism a chance to force a mechlistMIC. */ + if (!HARD_ERROR(ret) && mech_requires_mechlistMIC(spnego_ctx)) + spnego_ctx->mic_reqd = 1; + } + + /* Step 3: process or generate the MIC, if the negotiated mech is + * complete and supports MICs. */ + if (!HARD_ERROR(ret) && spnego_ctx->mech_complete && + (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) { + + ret = handle_mic(minor_status, + mechListMIC_in, + (mechtok_out.length != 0), + spnego_ctx, &mechListMIC_out, + &negState, &send_token); + } +cleanup: + if (send_token == INIT_TOKEN_SEND) { + if (make_spnego_tokenInit_msg(spnego_ctx, + 0, + mechListMIC_out, + req_flags, + &mechtok_out, send_token, + output_token) < 0) { + ret = GSS_S_FAILURE; + } + } else if (send_token != NO_TOKEN_SEND) { + if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID, + &mechtok_out, mechListMIC_out, + send_token, + output_token) < 0) { + ret = GSS_S_FAILURE; + } + } + gss_release_buffer(&tmpmin, &mechtok_out); + if (ret == GSS_S_COMPLETE) { + spnego_ctx->opened = 1; + if (actual_mech != NULL) + *actual_mech = spnego_ctx->actual_mech; + if (ret_flags != NULL) + *ret_flags = spnego_ctx->ctx_flags; + } else if (ret != GSS_S_CONTINUE_NEEDED) { + if (spnego_ctx != NULL) { + gss_delete_sec_context(&tmpmin, + &spnego_ctx->ctx_handle, + GSS_C_NO_BUFFER); + release_spnego_ctx(&spnego_ctx); + } + *context_handle = GSS_C_NO_CONTEXT; + } + if (mechtok_in != GSS_C_NO_BUFFER) { + gss_release_buffer(&tmpmin, mechtok_in); + free(mechtok_in); + } + if (mechListMIC_in != GSS_C_NO_BUFFER) { + gss_release_buffer(&tmpmin, mechListMIC_in); + free(mechListMIC_in); + } + if (mechListMIC_out != GSS_C_NO_BUFFER) { + gss_release_buffer(&tmpmin, mechListMIC_out); + free(mechListMIC_out); + } + return ret; +} /* init_sec_context */ + +/* We don't want to import KRB5 headers here */ +static const gss_OID_desc gss_mech_krb5_oid = + { 9, "\052\206\110\206\367\022\001\002\002" }; +static const gss_OID_desc gss_mech_krb5_wrong_oid = + { 9, "\052\206\110\202\367\022\001\002\002" }; + +/* + * verify that the input token length is not 0. If it is, just return. + * If the token length is greater than 0, der encode as a sequence + * and place in buf_out, advancing buf_out. + */ + +static int +put_neg_hints(unsigned char **buf_out, gss_buffer_t input_token, + unsigned int buflen) +{ + int ret; + + /* if token length is 0, we do not want to send */ + if (input_token->length == 0) + return (0); + + if (input_token->length > buflen) + return (-1); + + *(*buf_out)++ = SEQUENCE; + if ((ret = gssint_put_der_length(input_token->length, buf_out, + input_token->length))) + return (ret); + TWRITE_STR(*buf_out, input_token->value, input_token->length); + return (0); +} + +/* + * NegHints ::= SEQUENCE { + * hintName [0] GeneralString OPTIONAL, + * hintAddress [1] OCTET STRING OPTIONAL + * } + */ + +#define HOST_PREFIX "host@" +#define HOST_PREFIX_LEN (sizeof(HOST_PREFIX) - 1) + +/* Encode the dummy hintname string (as specified in [MS-SPNG]) into a + * DER-encoded [0] tagged GeneralString, and place the result in *outbuf. */ +static int +make_NegHints(OM_uint32 *minor_status, gss_buffer_t *outbuf) +{ + OM_uint32 major_status; + unsigned int tlen = 0; + unsigned int hintNameSize = 0; + unsigned char *ptr; + unsigned char *t; + const char *hintname = "not_defined_in_RFC4178@please_ignore"; + const size_t hintname_len = strlen(hintname); + + *outbuf = GSS_C_NO_BUFFER; + major_status = GSS_S_FAILURE; + + /* Length of DER encoded GeneralString */ + tlen = 1 + gssint_der_length_size(hintname_len) + hintname_len; + hintNameSize = tlen; + + /* Length of DER encoded hintName */ + tlen += 1 + gssint_der_length_size(hintNameSize); + + t = gssalloc_malloc(tlen); + if (t == NULL) { + *minor_status = ENOMEM; + goto errout; + } + + ptr = t; + + *ptr++ = CONTEXT | 0x00; /* hintName identifier */ + if (gssint_put_der_length(hintNameSize, + &ptr, tlen - (int)(ptr-t))) + goto errout; + + *ptr++ = GENERAL_STRING; + if (gssint_put_der_length(hintname_len, &ptr, tlen - (int)(ptr-t))) + goto errout; + + memcpy(ptr, hintname, hintname_len); + ptr += hintname_len; + + *outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc)); + if (*outbuf == NULL) { + *minor_status = ENOMEM; + goto errout; + } + (*outbuf)->value = (void *)t; + (*outbuf)->length = ptr - t; + + t = NULL; /* don't free */ + + *minor_status = 0; + major_status = GSS_S_COMPLETE; + +errout: + if (t != NULL) { + free(t); + } + + return (major_status); +} + +/* + * Create a new SPNEGO context handle for the initial call to + * spnego_gss_accept_sec_context() when the request is empty. For empty + * requests, we implement the Microsoft NegHints extension to SPNEGO for + * compatibility with some versions of Samba. See: + * http://msdn.microsoft.com/en-us/library/cc247039(PROT.10).aspx + */ +static OM_uint32 +acc_ctx_hints(OM_uint32 *minor_status, + spnego_gss_cred_id_t spcred, + gss_buffer_t *mechListMIC, + OM_uint32 *negState, + send_token_flag *return_token, + spnego_gss_ctx_id_t *sc_out) +{ + OM_uint32 tmpmin, ret; + gss_OID_set supported_mechSet; + spnego_gss_ctx_id_t sc = NULL; + + *mechListMIC = GSS_C_NO_BUFFER; + supported_mechSet = GSS_C_NO_OID_SET; + *return_token = NO_TOKEN_SEND; + *negState = REJECT; + *minor_status = 0; + *sc_out = NULL; + + ret = get_negotiable_mechs(minor_status, spcred, GSS_C_ACCEPT, + &supported_mechSet); + if (ret != GSS_S_COMPLETE) + goto cleanup; + + ret = make_NegHints(minor_status, mechListMIC); + if (ret != GSS_S_COMPLETE) + goto cleanup; + + sc = create_spnego_ctx(0); + if (sc == NULL) { + ret = GSS_S_FAILURE; + goto cleanup; + } + if (put_mech_set(supported_mechSet, &sc->DER_mechTypes) < 0) { + ret = GSS_S_FAILURE; + goto cleanup; + } + sc->internal_mech = GSS_C_NO_OID; + + *negState = ACCEPT_INCOMPLETE; + *return_token = INIT_TOKEN_SEND; + sc->firstpass = 1; + *sc_out = sc; + sc = NULL; + ret = GSS_S_COMPLETE; + +cleanup: + release_spnego_ctx(&sc); + gss_release_oid_set(&tmpmin, &supported_mechSet); + + return ret; +} + +/* + * Create a new SPNEGO context handle for the initial call to + * spnego_gss_accept_sec_context(). Set negState to REJECT if the token is + * defective, else ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether + * the initiator's preferred mechanism is supported. + */ +static OM_uint32 +acc_ctx_new(OM_uint32 *minor_status, + gss_buffer_t buf, + spnego_gss_cred_id_t spcred, + gss_buffer_t *mechToken, + gss_buffer_t *mechListMIC, + OM_uint32 *negState, + send_token_flag *return_token, + spnego_gss_ctx_id_t *sc_out) +{ + OM_uint32 tmpmin, ret, req_flags; + gss_OID_set supported_mechSet, mechTypes; + gss_buffer_desc der_mechTypes; + gss_OID mech_wanted; + spnego_gss_ctx_id_t sc = NULL; + + ret = GSS_S_DEFECTIVE_TOKEN; + der_mechTypes.length = 0; + der_mechTypes.value = NULL; + *mechToken = *mechListMIC = GSS_C_NO_BUFFER; + supported_mechSet = mechTypes = GSS_C_NO_OID_SET; + *return_token = ERROR_TOKEN_SEND; + *negState = REJECT; + *minor_status = 0; + + ret = get_negTokenInit(minor_status, buf, &der_mechTypes, + &mechTypes, &req_flags, + mechToken, mechListMIC); + if (ret != GSS_S_COMPLETE) { + goto cleanup; + } + ret = get_negotiable_mechs(minor_status, spcred, GSS_C_ACCEPT, + &supported_mechSet); + if (ret != GSS_S_COMPLETE) { + *return_token = NO_TOKEN_SEND; + goto cleanup; + } + /* + * Select the best match between the list of mechs + * that the initiator requested and the list that + * the acceptor will support. + */ + mech_wanted = negotiate_mech(supported_mechSet, mechTypes, negState); + if (*negState == REJECT) { + ret = GSS_S_BAD_MECH; + goto cleanup; + } + sc = create_spnego_ctx(0); + if (sc == NULL) { + ret = GSS_S_FAILURE; + *return_token = NO_TOKEN_SEND; + goto cleanup; + } + sc->mech_set = mechTypes; + mechTypes = GSS_C_NO_OID_SET; + sc->internal_mech = mech_wanted; + sc->DER_mechTypes = der_mechTypes; + der_mechTypes.length = 0; + der_mechTypes.value = NULL; + + if (*negState == REQUEST_MIC) + sc->mic_reqd = 1; + + *return_token = INIT_TOKEN_SEND; + sc->firstpass = 1; + *sc_out = sc; + ret = GSS_S_COMPLETE; +cleanup: + gss_release_oid_set(&tmpmin, &mechTypes); + gss_release_oid_set(&tmpmin, &supported_mechSet); + if (der_mechTypes.length != 0) + gss_release_buffer(&tmpmin, &der_mechTypes); + + return ret; +} + +static OM_uint32 +acc_ctx_cont(OM_uint32 *minstat, + gss_buffer_t buf, + spnego_gss_ctx_id_t sc, + gss_buffer_t *responseToken, + gss_buffer_t *mechListMIC, + OM_uint32 *negState, + send_token_flag *return_token) +{ + OM_uint32 ret, tmpmin; + gss_OID supportedMech; + unsigned int len; + unsigned char *ptr, *bufstart; + + ret = GSS_S_DEFECTIVE_TOKEN; + *negState = REJECT; + *minstat = 0; + supportedMech = GSS_C_NO_OID; + *return_token = ERROR_TOKEN_SEND; + *responseToken = *mechListMIC = GSS_C_NO_BUFFER; + + ptr = bufstart = buf->value; +#define REMAIN (buf->length - (ptr - bufstart)) + if (REMAIN == 0 || REMAIN > INT_MAX) + return GSS_S_DEFECTIVE_TOKEN; + + /* + * Attempt to work with old Sun SPNEGO. + */ + if (*ptr == HEADER_ID) { + ret = g_verify_token_header(gss_mech_spnego, + &len, &ptr, 0, REMAIN); + if (ret) { + *minstat = ret; + return GSS_S_DEFECTIVE_TOKEN; + } + } + if (*ptr != (CONTEXT | 0x01)) { + return GSS_S_DEFECTIVE_TOKEN; + } + ret = get_negTokenResp(minstat, ptr, REMAIN, + negState, &supportedMech, + responseToken, mechListMIC); + if (ret != GSS_S_COMPLETE) + goto cleanup; + + if (*responseToken == GSS_C_NO_BUFFER && + *mechListMIC == GSS_C_NO_BUFFER) { + + ret = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + if (supportedMech != GSS_C_NO_OID) { + ret = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + sc->firstpass = 0; + *negState = ACCEPT_INCOMPLETE; + *return_token = CONT_TOKEN_SEND; +cleanup: + if (supportedMech != GSS_C_NO_OID) { + generic_gss_release_oid(&tmpmin, &supportedMech); + } + return ret; +#undef REMAIN +} + +/* + * Verify that mech OID is either exactly the same as the negotiated + * mech OID, or is a mech OID supported by the negotiated mech. MS + * implementations can list a most preferred mech using an incorrect + * krb5 OID while emitting a krb5 initiator mech token having the + * correct krb5 mech OID. + */ +static OM_uint32 +acc_ctx_vfy_oid(OM_uint32 *minor_status, + spnego_gss_ctx_id_t sc, gss_OID mechoid, + OM_uint32 *negState, send_token_flag *tokflag) +{ + OM_uint32 ret, tmpmin; + gss_mechanism mech = NULL; + gss_OID_set mech_set = GSS_C_NO_OID_SET; + int present = 0; + + if (g_OID_equal(sc->internal_mech, mechoid)) + return GSS_S_COMPLETE; + + mech = gssint_get_mechanism(sc->internal_mech); + if (mech == NULL || mech->gss_indicate_mechs == NULL) { + *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED; + map_errcode(minor_status); + *negState = REJECT; + *tokflag = ERROR_TOKEN_SEND; + return GSS_S_BAD_MECH; + } + ret = mech->gss_indicate_mechs(minor_status, &mech_set); + if (ret != GSS_S_COMPLETE) { + *tokflag = NO_TOKEN_SEND; + map_error(minor_status, mech); + goto cleanup; + } + ret = gss_test_oid_set_member(minor_status, mechoid, + mech_set, &present); + if (ret != GSS_S_COMPLETE) + goto cleanup; + if (!present) { + *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED; + map_errcode(minor_status); + *negState = REJECT; + *tokflag = ERROR_TOKEN_SEND; + ret = GSS_S_BAD_MECH; + } +cleanup: + gss_release_oid_set(&tmpmin, &mech_set); + return ret; +} +#ifndef LEAN_CLIENT +/* + * Wrap call to gss_accept_sec_context() and update state + * accordingly. + */ +static OM_uint32 +acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc, + spnego_gss_cred_id_t spcred, gss_buffer_t mechtok_in, + gss_OID *mech_type, gss_buffer_t mechtok_out, + OM_uint32 *ret_flags, OM_uint32 *time_rec, + gss_cred_id_t *delegated_cred_handle, + OM_uint32 *negState, send_token_flag *tokflag) +{ + OM_uint32 ret; + gss_OID_desc mechoid; + gss_cred_id_t mcred; + + if (sc->ctx_handle == GSS_C_NO_CONTEXT) { + /* + * mechoid is an alias; don't free it. + */ + ret = gssint_get_mech_type(&mechoid, mechtok_in); + if (ret != GSS_S_COMPLETE) { + *tokflag = NO_TOKEN_SEND; + return ret; + } + ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid, + negState, tokflag); + if (ret != GSS_S_COMPLETE) + return ret; + } + + mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred; + ret = gss_accept_sec_context(minor_status, + &sc->ctx_handle, + mcred, + mechtok_in, + GSS_C_NO_CHANNEL_BINDINGS, + &sc->internal_name, + mech_type, + mechtok_out, + &sc->ctx_flags, + time_rec, + delegated_cred_handle); + if (ret == GSS_S_COMPLETE) { +#ifdef MS_BUG_TEST + /* + * Force MIC to be not required even if we previously + * requested a MIC. + */ + char *envstr = getenv("MS_FORCE_NO_MIC"); + + if (envstr != NULL && strcmp(envstr, "1") == 0 && + !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) && + sc->mic_reqd) { + + sc->mic_reqd = 0; + } +#endif + sc->mech_complete = 1; + if (ret_flags != NULL) + *ret_flags = sc->ctx_flags; + + if (!sc->mic_reqd || + !(sc->ctx_flags & GSS_C_INTEG_FLAG)) { + /* No MIC exchange required, so we're done. */ + *negState = ACCEPT_COMPLETE; + ret = GSS_S_COMPLETE; + } else { + /* handle_mic will decide if we're done. */ + ret = GSS_S_CONTINUE_NEEDED; + } + } else if (ret != GSS_S_CONTINUE_NEEDED) { + *negState = REJECT; + *tokflag = ERROR_TOKEN_SEND; + } + return ret; +} + +/*ARGSUSED*/ +OM_uint32 KRB5_CALLCONV +spnego_gss_accept_sec_context( + OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_cred_id_t verifier_cred_handle, + gss_buffer_t input_token, + gss_channel_bindings_t input_chan_bindings, + gss_name_t *src_name, + gss_OID *mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + gss_cred_id_t *delegated_cred_handle) +{ + OM_uint32 ret, tmpmin, negState; + send_token_flag return_token; + gss_buffer_t mechtok_in, mic_in, mic_out; + gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER; + spnego_gss_ctx_id_t sc = NULL; + spnego_gss_cred_id_t spcred = NULL; + int sendTokenInit = 0, tmpret; + + mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER; + + /* + * This function works in three steps: + * + * 1. Perform mechanism negotiation. + * 2. Invoke the negotiated mech's gss_accept_sec_context function + * and examine the results. + * 3. Process or generate MICs if necessary. + * + * Step one determines whether the negotiation requires a MIC exchange, + * while steps two and three share responsibility for determining when + * the exchange is complete. If the selected mech completes in this + * call and no MIC exchange is expected, then step 2 will decide. If a + * MIC exchange is expected, then step 3 will decide. If an error + * occurs in any step, the exchange will be aborted, possibly with an + * error token. + * + * negState determines the state of the negotiation, and is + * communicated to the acceptor if a continuing token is sent. + * return_token is used to indicate what type of token, if any, should + * be generated. + */ + + /* Validate arguments. */ + if (minor_status != NULL) + *minor_status = 0; + if (output_token != GSS_C_NO_BUFFER) { + output_token->length = 0; + output_token->value = NULL; + } + if (src_name != NULL) + *src_name = GSS_C_NO_NAME; + if (mech_type != NULL) + *mech_type = GSS_C_NO_OID; + if (time_rec != NULL) + *time_rec = 0; + if (ret_flags != NULL) + *ret_flags = 0; + if (delegated_cred_handle != NULL) + *delegated_cred_handle = GSS_C_NO_CREDENTIAL; + + if (minor_status == NULL || + output_token == GSS_C_NO_BUFFER || + context_handle == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (input_token == GSS_C_NO_BUFFER) + return GSS_S_CALL_INACCESSIBLE_READ; + + /* Step 1: Perform mechanism negotiation. */ + sc = (spnego_gss_ctx_id_t)*context_handle; + spcred = (spnego_gss_cred_id_t)verifier_cred_handle; + if (sc == NULL && input_token->length == 0) { + /* Process a request for NegHints. */ + ret = acc_ctx_hints(minor_status, spcred, &mic_out, &negState, + &return_token, &sc); + if (ret != GSS_S_COMPLETE) + goto cleanup; + *context_handle = (gss_ctx_id_t)sc; + sendTokenInit = 1; + ret = GSS_S_CONTINUE_NEEDED; + } else if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) { + if (sc != NULL) { + /* Discard the context from the NegHints request. */ + release_spnego_ctx(&sc); + *context_handle = GSS_C_NO_CONTEXT; + } + /* Process an initial token; can set negState to + * REQUEST_MIC. */ + ret = acc_ctx_new(minor_status, input_token, spcred, + &mechtok_in, &mic_in, &negState, + &return_token, &sc); + if (ret != GSS_S_COMPLETE) + goto cleanup; + *context_handle = (gss_ctx_id_t)sc; + ret = GSS_S_CONTINUE_NEEDED; + } else { + /* Process a response token. Can set negState to + * ACCEPT_INCOMPLETE. */ + ret = acc_ctx_cont(minor_status, input_token, sc, &mechtok_in, + &mic_in, &negState, &return_token); + if (ret != GSS_S_COMPLETE) + goto cleanup; + ret = GSS_S_CONTINUE_NEEDED; + } + + /* Step 2: invoke the negotiated mechanism's gss_accept_sec_context + * function. */ + /* + * Handle mechtok_in and mic_in only if they are + * present in input_token. If neither is present, whether + * this is an error depends on whether this is the first + * round-trip. RET is set to a default value according to + * whether it is the first round-trip. + */ + if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) { + ret = acc_ctx_call_acc(minor_status, sc, spcred, + mechtok_in, mech_type, &mechtok_out, + ret_flags, time_rec, + delegated_cred_handle, + &negState, &return_token); + } + + /* Step 3: process or generate the MIC, if the negotiated mech is + * complete and supports MICs. */ + if (!HARD_ERROR(ret) && sc->mech_complete && + (sc->ctx_flags & GSS_C_INTEG_FLAG)) { + + ret = handle_mic(minor_status, mic_in, + (mechtok_out.length != 0), + sc, &mic_out, + &negState, &return_token); + } +cleanup: + if (return_token == INIT_TOKEN_SEND && sendTokenInit) { + assert(sc != NULL); + tmpret = make_spnego_tokenInit_msg(sc, 1, mic_out, 0, + GSS_C_NO_BUFFER, + return_token, output_token); + if (tmpret < 0) + ret = GSS_S_FAILURE; + } else if (return_token != NO_TOKEN_SEND && + return_token != CHECK_MIC) { + tmpret = make_spnego_tokenTarg_msg(negState, + sc ? sc->internal_mech : + GSS_C_NO_OID, + &mechtok_out, mic_out, + return_token, + output_token); + if (tmpret < 0) + ret = GSS_S_FAILURE; + } + if (ret == GSS_S_COMPLETE) { + sc->opened = 1; + if (sc->internal_name != GSS_C_NO_NAME && + src_name != NULL) { + *src_name = sc->internal_name; + sc->internal_name = GSS_C_NO_NAME; + } + } else if (ret != GSS_S_CONTINUE_NEEDED) { + if (sc != NULL) { + gss_delete_sec_context(&tmpmin, &sc->ctx_handle, + GSS_C_NO_BUFFER); + release_spnego_ctx(&sc); + } + *context_handle = GSS_C_NO_CONTEXT; + } + gss_release_buffer(&tmpmin, &mechtok_out); + if (mechtok_in != GSS_C_NO_BUFFER) { + gss_release_buffer(&tmpmin, mechtok_in); + free(mechtok_in); + } + if (mic_in != GSS_C_NO_BUFFER) { + gss_release_buffer(&tmpmin, mic_in); + free(mic_in); + } + if (mic_out != GSS_C_NO_BUFFER) { + gss_release_buffer(&tmpmin, mic_out); + free(mic_out); + } + return ret; +} +#endif /* LEAN_CLIENT */ + +/*ARGSUSED*/ +OM_uint32 KRB5_CALLCONV +spnego_gss_display_status( + OM_uint32 *minor_status, + OM_uint32 status_value, + int status_type, + gss_OID mech_type, + OM_uint32 *message_context, + gss_buffer_t status_string) +{ + OM_uint32 maj = GSS_S_COMPLETE; + int ret; + + dsyslog("Entering display_status\n"); + + *message_context = 0; + switch (status_value) { + case ERR_SPNEGO_NO_MECHS_AVAILABLE: + /* CSTYLED */ + *status_string = make_err_msg(_("SPNEGO cannot find " + "mechanisms to negotiate")); + break; + case ERR_SPNEGO_NO_CREDS_ACQUIRED: + /* CSTYLED */ + *status_string = make_err_msg(_("SPNEGO failed to acquire " + "creds")); + break; + case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR: + /* CSTYLED */ + *status_string = make_err_msg(_("SPNEGO acceptor did not " + "select a mechanism")); + break; + case ERR_SPNEGO_NEGOTIATION_FAILED: + /* CSTYLED */ + *status_string = make_err_msg(_("SPNEGO failed to negotiate a " + "mechanism")); + break; + case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR: + /* CSTYLED */ + *status_string = make_err_msg(_("SPNEGO acceptor did not " + "return a valid token")); + break; + default: + /* Not one of our minor codes; might be from a mech. Call back + * to gss_display_status, but first check for recursion. */ + if (k5_getspecific(K5_KEY_GSS_SPNEGO_STATUS) != NULL) { + /* Perhaps we returned a com_err code like ENOMEM. */ + const char *err = error_message(status_value); + *status_string = make_err_msg(err); + break; + } + /* Set a non-null pointer value; doesn't matter which one. */ + ret = k5_setspecific(K5_KEY_GSS_SPNEGO_STATUS, &ret); + if (ret != 0) { + *minor_status = ret; + maj = GSS_S_FAILURE; + break; + } + maj = gss_display_status(minor_status, status_value, + status_type, mech_type, + message_context, status_string); + /* This is unlikely to fail; not much we can do if it does. */ + (void)k5_setspecific(K5_KEY_GSS_SPNEGO_STATUS, NULL); + break; + } + + dsyslog("Leaving display_status\n"); + return maj; +} + + +/*ARGSUSED*/ +OM_uint32 KRB5_CALLCONV +spnego_gss_import_name( + OM_uint32 *minor_status, + gss_buffer_t input_name_buffer, + gss_OID input_name_type, + gss_name_t *output_name) +{ + OM_uint32 status; + + dsyslog("Entering import_name\n"); + + status = gss_import_name(minor_status, input_name_buffer, + input_name_type, output_name); + + dsyslog("Leaving import_name\n"); + return (status); +} + +/*ARGSUSED*/ +OM_uint32 KRB5_CALLCONV +spnego_gss_release_name( + OM_uint32 *minor_status, + gss_name_t *input_name) +{ + OM_uint32 status; + + dsyslog("Entering release_name\n"); + + status = gss_release_name(minor_status, input_name); + + dsyslog("Leaving release_name\n"); + return (status); +} + +/*ARGSUSED*/ +OM_uint32 KRB5_CALLCONV +spnego_gss_duplicate_name( + OM_uint32 *minor_status, + const gss_name_t input_name, + gss_name_t *output_name) +{ + OM_uint32 status; + + dsyslog("Entering duplicate_name\n"); + + status = gss_duplicate_name(minor_status, input_name, output_name); + + dsyslog("Leaving duplicate_name\n"); + return (status); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_inquire_cred( + OM_uint32 *minor_status, + gss_cred_id_t cred_handle, + gss_name_t *name, + OM_uint32 *lifetime, + int *cred_usage, + gss_OID_set *mechanisms) +{ + OM_uint32 status; + spnego_gss_cred_id_t spcred = NULL; + gss_cred_id_t creds = GSS_C_NO_CREDENTIAL; + OM_uint32 tmp_minor_status; + OM_uint32 initiator_lifetime, acceptor_lifetime; + + dsyslog("Entering inquire_cred\n"); + + /* + * To avoid infinite recursion, if GSS_C_NO_CREDENTIAL is + * supplied we call gss_inquire_cred_by_mech() on the + * first non-SPNEGO mechanism. + */ + spcred = (spnego_gss_cred_id_t)cred_handle; + if (spcred == NULL) { + status = get_available_mechs(minor_status, + GSS_C_NO_NAME, + GSS_C_BOTH, + GSS_C_NO_CRED_STORE, + &creds, + mechanisms, NULL); + if (status != GSS_S_COMPLETE) { + dsyslog("Leaving inquire_cred\n"); + return (status); + } + + if ((*mechanisms)->count == 0) { + gss_release_cred(&tmp_minor_status, &creds); + gss_release_oid_set(&tmp_minor_status, mechanisms); + dsyslog("Leaving inquire_cred\n"); + return (GSS_S_DEFECTIVE_CREDENTIAL); + } + + assert((*mechanisms)->elements != NULL); + + status = gss_inquire_cred_by_mech(minor_status, + creds, + &(*mechanisms)->elements[0], + name, + &initiator_lifetime, + &acceptor_lifetime, + cred_usage); + if (status != GSS_S_COMPLETE) { + gss_release_cred(&tmp_minor_status, &creds); + dsyslog("Leaving inquire_cred\n"); + return (status); + } + + if (lifetime != NULL) + *lifetime = (*cred_usage == GSS_C_ACCEPT) ? + acceptor_lifetime : initiator_lifetime; + + gss_release_cred(&tmp_minor_status, &creds); + } else { + status = gss_inquire_cred(minor_status, spcred->mcred, + name, lifetime, + cred_usage, mechanisms); + } + + dsyslog("Leaving inquire_cred\n"); + + return (status); +} + +/*ARGSUSED*/ +OM_uint32 KRB5_CALLCONV +spnego_gss_compare_name( + OM_uint32 *minor_status, + const gss_name_t name1, + const gss_name_t name2, + int *name_equal) +{ + OM_uint32 status = GSS_S_COMPLETE; + dsyslog("Entering compare_name\n"); + + status = gss_compare_name(minor_status, name1, name2, name_equal); + + dsyslog("Leaving compare_name\n"); + return (status); +} + +/*ARGSUSED*/ +/*ARGSUSED*/ +OM_uint32 KRB5_CALLCONV +spnego_gss_display_name( + OM_uint32 *minor_status, + gss_name_t input_name, + gss_buffer_t output_name_buffer, + gss_OID *output_name_type) +{ + OM_uint32 status = GSS_S_COMPLETE; + dsyslog("Entering display_name\n"); + + status = gss_display_name(minor_status, input_name, + output_name_buffer, output_name_type); + + dsyslog("Leaving display_name\n"); + return (status); +} + + +/*ARGSUSED*/ +OM_uint32 KRB5_CALLCONV +spnego_gss_inquire_names_for_mech( + OM_uint32 *minor_status, + gss_OID mechanism, + gss_OID_set *name_types) +{ + OM_uint32 major, minor; + + dsyslog("Entering inquire_names_for_mech\n"); + /* + * We only know how to handle our own mechanism. + */ + if ((mechanism != GSS_C_NULL_OID) && + !g_OID_equal(gss_mech_spnego, mechanism)) { + *minor_status = 0; + return (GSS_S_FAILURE); + } + + major = gss_create_empty_oid_set(minor_status, name_types); + if (major == GSS_S_COMPLETE) { + /* Now add our members. */ + if (((major = gss_add_oid_set_member(minor_status, + (gss_OID) GSS_C_NT_USER_NAME, + name_types)) == GSS_S_COMPLETE) && + ((major = gss_add_oid_set_member(minor_status, + (gss_OID) GSS_C_NT_MACHINE_UID_NAME, + name_types)) == GSS_S_COMPLETE) && + ((major = gss_add_oid_set_member(minor_status, + (gss_OID) GSS_C_NT_STRING_UID_NAME, + name_types)) == GSS_S_COMPLETE)) { + major = gss_add_oid_set_member(minor_status, + (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, + name_types); + } + + if (major != GSS_S_COMPLETE) + (void) gss_release_oid_set(&minor, name_types); + } + + dsyslog("Leaving inquire_names_for_mech\n"); + return (major); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_unwrap( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_buffer_t input_message_buffer, + gss_buffer_t output_message_buffer, + int *conf_state, + gss_qop_t *qop_state) +{ + OM_uint32 ret; + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle; + + if (sc->ctx_handle == GSS_C_NO_CONTEXT) + return (GSS_S_NO_CONTEXT); + + ret = gss_unwrap(minor_status, + sc->ctx_handle, + input_message_buffer, + output_message_buffer, + conf_state, + qop_state); + + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_wrap( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + gss_buffer_t input_message_buffer, + int *conf_state, + gss_buffer_t output_message_buffer) +{ + OM_uint32 ret; + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle; + + if (sc->ctx_handle == GSS_C_NO_CONTEXT) + return (GSS_S_NO_CONTEXT); + + ret = gss_wrap(minor_status, + sc->ctx_handle, + conf_req_flag, + qop_req, + input_message_buffer, + conf_state, + output_message_buffer); + + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_process_context_token( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t token_buffer) +{ + OM_uint32 ret; + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle; + + /* SPNEGO doesn't have its own context tokens. */ + if (!sc->opened) + return (GSS_S_DEFECTIVE_TOKEN); + + ret = gss_process_context_token(minor_status, + sc->ctx_handle, + token_buffer); + + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_delete_sec_context( + OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token) +{ + OM_uint32 ret = GSS_S_COMPLETE; + spnego_gss_ctx_id_t *ctx = + (spnego_gss_ctx_id_t *)context_handle; + + *minor_status = 0; + + if (context_handle == NULL) + return (GSS_S_FAILURE); + + if (*ctx == NULL) + return (GSS_S_COMPLETE); + + (void) gss_delete_sec_context(minor_status, &(*ctx)->ctx_handle, + output_token); + (void) release_spnego_ctx(ctx); + + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_context_time( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + OM_uint32 *time_rec) +{ + OM_uint32 ret; + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle; + + if (sc->ctx_handle == GSS_C_NO_CONTEXT) + return (GSS_S_NO_CONTEXT); + + ret = gss_context_time(minor_status, + sc->ctx_handle, + time_rec); + return (ret); +} +#ifndef LEAN_CLIENT +OM_uint32 KRB5_CALLCONV +spnego_gss_export_sec_context( + OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t interprocess_token) +{ + OM_uint32 ret; + spnego_gss_ctx_id_t sc = *(spnego_gss_ctx_id_t *)context_handle; + + /* We don't currently support exporting partially established + * contexts. */ + if (!sc->opened) + return GSS_S_UNAVAILABLE; + + ret = gss_export_sec_context(minor_status, + &sc->ctx_handle, + interprocess_token); + if (sc->ctx_handle == GSS_C_NO_CONTEXT) { + release_spnego_ctx(&sc); + *context_handle = GSS_C_NO_CONTEXT; + } + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_import_sec_context( + OM_uint32 *minor_status, + const gss_buffer_t interprocess_token, + gss_ctx_id_t *context_handle) +{ + OM_uint32 ret, tmpmin; + gss_ctx_id_t mctx; + spnego_gss_ctx_id_t sc; + int initiate, opened; + + ret = gss_import_sec_context(minor_status, interprocess_token, &mctx); + if (ret != GSS_S_COMPLETE) + return ret; + + ret = gss_inquire_context(&tmpmin, mctx, NULL, NULL, NULL, NULL, NULL, + &initiate, &opened); + if (ret != GSS_S_COMPLETE || !opened) { + /* We don't currently support importing partially established + * contexts. */ + (void) gss_delete_sec_context(&tmpmin, &mctx, GSS_C_NO_BUFFER); + return GSS_S_FAILURE; + } + + sc = create_spnego_ctx(initiate); + if (sc == NULL) { + (void) gss_delete_sec_context(&tmpmin, &mctx, GSS_C_NO_BUFFER); + return GSS_S_FAILURE; + } + sc->ctx_handle = mctx; + sc->opened = 1; + *context_handle = (gss_ctx_id_t)sc; + return GSS_S_COMPLETE; +} +#endif /* LEAN_CLIENT */ + +OM_uint32 KRB5_CALLCONV +spnego_gss_inquire_context( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + gss_name_t *src_name, + gss_name_t *targ_name, + OM_uint32 *lifetime_rec, + gss_OID *mech_type, + OM_uint32 *ctx_flags, + int *locally_initiated, + int *opened) +{ + OM_uint32 ret = GSS_S_COMPLETE; + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle; + + if (src_name != NULL) + *src_name = GSS_C_NO_NAME; + if (targ_name != NULL) + *targ_name = GSS_C_NO_NAME; + if (lifetime_rec != NULL) + *lifetime_rec = 0; + if (mech_type != NULL) + *mech_type = (gss_OID)gss_mech_spnego; + if (ctx_flags != NULL) + *ctx_flags = 0; + if (locally_initiated != NULL) + *locally_initiated = sc->initiate; + if (opened != NULL) + *opened = sc->opened; + + if (sc->ctx_handle != GSS_C_NO_CONTEXT) { + ret = gss_inquire_context(minor_status, sc->ctx_handle, + src_name, targ_name, lifetime_rec, + mech_type, ctx_flags, NULL, NULL); + } + + if (!sc->opened) { + /* + * We are still doing SPNEGO negotiation, so report SPNEGO as + * the OID. After negotiation is complete we will report the + * underlying mechanism OID. + */ + if (mech_type != NULL) + *mech_type = (gss_OID)gss_mech_spnego; + + /* + * Remove flags we don't support with partially-established + * contexts. (Change this to keep GSS_C_TRANS_FLAG if we add + * support for exporting partial SPNEGO contexts.) + */ + if (ctx_flags != NULL) { + *ctx_flags &= ~GSS_C_PROT_READY_FLAG; + *ctx_flags &= ~GSS_C_TRANS_FLAG; + } + } + + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_wrap_size_limit( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + OM_uint32 req_output_size, + OM_uint32 *max_input_size) +{ + OM_uint32 ret; + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle; + + if (sc->ctx_handle == GSS_C_NO_CONTEXT) + return (GSS_S_NO_CONTEXT); + + ret = gss_wrap_size_limit(minor_status, + sc->ctx_handle, + conf_req_flag, + qop_req, + req_output_size, + max_input_size); + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_get_mic( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + gss_qop_t qop_req, + const gss_buffer_t message_buffer, + gss_buffer_t message_token) +{ + OM_uint32 ret; + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle; + + if (sc->ctx_handle == GSS_C_NO_CONTEXT) + return (GSS_S_NO_CONTEXT); + + ret = gss_get_mic(minor_status, + sc->ctx_handle, + qop_req, + message_buffer, + message_token); + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_verify_mic( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t msg_buffer, + const gss_buffer_t token_buffer, + gss_qop_t *qop_state) +{ + OM_uint32 ret; + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle; + + if (sc->ctx_handle == GSS_C_NO_CONTEXT) + return (GSS_S_NO_CONTEXT); + + ret = gss_verify_mic(minor_status, + sc->ctx_handle, + msg_buffer, + token_buffer, + qop_state); + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_inquire_sec_context_by_oid( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + OM_uint32 ret; + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle; + + /* There are no SPNEGO-specific OIDs for this function. */ + if (sc->ctx_handle == GSS_C_NO_CONTEXT) + return (GSS_S_UNAVAILABLE); + + ret = gss_inquire_sec_context_by_oid(minor_status, + sc->ctx_handle, + desired_object, + data_set); + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_inquire_cred_by_oid( + OM_uint32 *minor_status, + const gss_cred_id_t cred_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + OM_uint32 ret; + spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle; + gss_cred_id_t mcred; + mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred; + ret = gss_inquire_cred_by_oid(minor_status, + mcred, + desired_object, + data_set); + return (ret); +} + +/* This is the same OID as KRB5_NO_CI_FLAGS_X_OID. */ +#define NO_CI_FLAGS_X_OID_LENGTH 6 +#define NO_CI_FLAGS_X_OID "\x2a\x85\x70\x2b\x0d\x1d" +static const gss_OID_desc no_ci_flags_oid[] = { + {NO_CI_FLAGS_X_OID_LENGTH, NO_CI_FLAGS_X_OID}, +}; + +OM_uint32 KRB5_CALLCONV +spnego_gss_set_cred_option( + OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_object, + const gss_buffer_t value) +{ + OM_uint32 ret; + OM_uint32 tmp_minor_status; + spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)*cred_handle; + gss_cred_id_t mcred; + + mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred; + ret = gss_set_cred_option(minor_status, + &mcred, + desired_object, + value); + if (ret == GSS_S_COMPLETE && spcred == NULL) { + /* + * If the mechanism allocated a new credential handle, then + * we need to wrap it up in an SPNEGO credential handle. + */ + + ret = create_spnego_cred(minor_status, mcred, &spcred); + if (ret != GSS_S_COMPLETE) { + gss_release_cred(&tmp_minor_status, &mcred); + return (ret); + } + *cred_handle = (gss_cred_id_t)spcred; + } + + if (ret != GSS_S_COMPLETE) + return (ret); + + /* Recognize KRB5_NO_CI_FLAGS_X_OID and avoid asking for integrity. */ + if (g_OID_equal(desired_object, no_ci_flags_oid)) + spcred->no_ask_integ = 1; + + return (GSS_S_COMPLETE); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_set_sec_context_option( + OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + const gss_OID desired_object, + const gss_buffer_t value) +{ + OM_uint32 ret; + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)*context_handle; + + /* There are no SPNEGO-specific OIDs for this function, and we cannot + * construct an empty SPNEGO context with it. */ + if (sc == NULL || sc->ctx_handle == GSS_C_NO_CONTEXT) + return (GSS_S_UNAVAILABLE); + + ret = gss_set_sec_context_option(minor_status, + &sc->ctx_handle, + desired_object, + value); + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_wrap_aead(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + gss_buffer_t input_assoc_buffer, + gss_buffer_t input_payload_buffer, + int *conf_state, + gss_buffer_t output_message_buffer) +{ + OM_uint32 ret; + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle; + + if (sc->ctx_handle == GSS_C_NO_CONTEXT) + return (GSS_S_NO_CONTEXT); + + ret = gss_wrap_aead(minor_status, + sc->ctx_handle, + conf_req_flag, + qop_req, + input_assoc_buffer, + input_payload_buffer, + conf_state, + output_message_buffer); + + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_unwrap_aead(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_buffer_t input_message_buffer, + gss_buffer_t input_assoc_buffer, + gss_buffer_t output_payload_buffer, + int *conf_state, + gss_qop_t *qop_state) +{ + OM_uint32 ret; + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle; + + if (sc->ctx_handle == GSS_C_NO_CONTEXT) + return (GSS_S_NO_CONTEXT); + + ret = gss_unwrap_aead(minor_status, + sc->ctx_handle, + input_message_buffer, + input_assoc_buffer, + output_payload_buffer, + conf_state, + qop_state); + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_wrap_iov(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 ret; + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle; + + if (sc->ctx_handle == GSS_C_NO_CONTEXT) + return (GSS_S_NO_CONTEXT); + + ret = gss_wrap_iov(minor_status, + sc->ctx_handle, + conf_req_flag, + qop_req, + conf_state, + iov, + iov_count); + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_unwrap_iov(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 ret; + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle; + + if (sc->ctx_handle == GSS_C_NO_CONTEXT) + return (GSS_S_NO_CONTEXT); + + ret = gss_unwrap_iov(minor_status, + sc->ctx_handle, + conf_state, + qop_state, + iov, + iov_count); + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_wrap_iov_length(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 ret; + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle; + + if (sc->ctx_handle == GSS_C_NO_CONTEXT) + return (GSS_S_NO_CONTEXT); + + ret = gss_wrap_iov_length(minor_status, + sc->ctx_handle, + conf_req_flag, + qop_req, + conf_state, + iov, + iov_count); + return (ret); +} + + +OM_uint32 KRB5_CALLCONV +spnego_gss_complete_auth_token( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + gss_buffer_t input_message_buffer) +{ + OM_uint32 ret; + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle; + + if (sc->ctx_handle == GSS_C_NO_CONTEXT) + return (GSS_S_UNAVAILABLE); + + ret = gss_complete_auth_token(minor_status, + sc->ctx_handle, + input_message_buffer); + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + const gss_name_t desired_name, + OM_uint32 time_req, + gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + OM_uint32 status, tmpmin; + gss_OID_set amechs = GSS_C_NULL_OID_SET; + spnego_gss_cred_id_t imp_spcred = NULL, out_spcred = NULL; + gss_cred_id_t imp_mcred, out_mcred = GSS_C_NO_CREDENTIAL; + + dsyslog("Entering spnego_gss_acquire_cred_impersonate_name\n"); + + if (actual_mechs) + *actual_mechs = NULL; + + if (time_rec) + *time_rec = 0; + + imp_spcred = (spnego_gss_cred_id_t)impersonator_cred_handle; + imp_mcred = imp_spcred ? imp_spcred->mcred : GSS_C_NO_CREDENTIAL; + status = gss_inquire_cred(minor_status, imp_mcred, NULL, NULL, + NULL, &amechs); + if (status != GSS_S_COMPLETE) + return status; + + status = gss_acquire_cred_impersonate_name(minor_status, imp_mcred, + desired_name, time_req, + amechs, cred_usage, + &out_mcred, actual_mechs, + time_rec); + if (status != GSS_S_COMPLETE) + goto cleanup; + + status = create_spnego_cred(minor_status, out_mcred, &out_spcred); + if (status != GSS_S_COMPLETE) + goto cleanup; + + out_mcred = GSS_C_NO_CREDENTIAL; + *output_cred_handle = (gss_cred_id_t)out_spcred; + +cleanup: + (void) gss_release_oid_set(&tmpmin, &amechs); + (void) gss_release_cred(&tmpmin, &out_mcred); + + dsyslog("Leaving spnego_gss_acquire_cred_impersonate_name\n"); + return (status); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_acquire_cred_with_password(OM_uint32 *minor_status, + const gss_name_t desired_name, + const gss_buffer_t password, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + OM_uint32 status, tmpmin; + gss_OID_set amechs = GSS_C_NULL_OID_SET; + gss_cred_id_t mcred = NULL; + spnego_gss_cred_id_t spcred = NULL; + + dsyslog("Entering spnego_gss_acquire_cred_with_password\n"); + + if (actual_mechs) + *actual_mechs = NULL; + + if (time_rec) + *time_rec = 0; + + status = get_available_mechs(minor_status, desired_name, + cred_usage, GSS_C_NO_CRED_STORE, + NULL, &amechs, NULL); + if (status != GSS_S_COMPLETE) + goto cleanup; + + status = gss_acquire_cred_with_password(minor_status, desired_name, + password, time_req, amechs, + cred_usage, &mcred, + actual_mechs, time_rec); + if (status != GSS_S_COMPLETE) + goto cleanup; + + status = create_spnego_cred(minor_status, mcred, &spcred); + if (status != GSS_S_COMPLETE) + goto cleanup; + + mcred = GSS_C_NO_CREDENTIAL; + *output_cred_handle = (gss_cred_id_t)spcred; + +cleanup: + + (void) gss_release_oid_set(&tmpmin, &amechs); + (void) gss_release_cred(&tmpmin, &mcred); + + dsyslog("Leaving spnego_gss_acquire_cred_with_password\n"); + return (status); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_display_name_ext(OM_uint32 *minor_status, + gss_name_t name, + gss_OID display_as_name_type, + gss_buffer_t display_name) +{ + OM_uint32 ret; + ret = gss_display_name_ext(minor_status, + name, + display_as_name_type, + display_name); + return (ret); +} + + +OM_uint32 KRB5_CALLCONV +spnego_gss_inquire_name(OM_uint32 *minor_status, + gss_name_t name, + int *name_is_MN, + gss_OID *MN_mech, + gss_buffer_set_t *attrs) +{ + OM_uint32 ret; + ret = gss_inquire_name(minor_status, + name, + name_is_MN, + MN_mech, + attrs); + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_get_name_attribute(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) +{ + OM_uint32 ret; + ret = gss_get_name_attribute(minor_status, + name, + attr, + authenticated, + complete, + value, + display_value, + more); + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_set_name_attribute(OM_uint32 *minor_status, + gss_name_t name, + int complete, + gss_buffer_t attr, + gss_buffer_t value) +{ + OM_uint32 ret; + ret = gss_set_name_attribute(minor_status, + name, + complete, + attr, + value); + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_delete_name_attribute(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t attr) +{ + OM_uint32 ret; + ret = gss_delete_name_attribute(minor_status, + name, + attr); + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_export_name_composite(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t exp_composite_name) +{ + OM_uint32 ret; + ret = gss_export_name_composite(minor_status, + name, + exp_composite_name); + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_map_name_to_any(OM_uint32 *minor_status, + gss_name_t name, + int authenticated, + gss_buffer_t type_id, + gss_any_t *output) +{ + OM_uint32 ret; + ret = gss_map_name_to_any(minor_status, + name, + authenticated, + type_id, + output); + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_release_any_name_mapping(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t type_id, + gss_any_t *input) +{ + OM_uint32 ret; + ret = gss_release_any_name_mapping(minor_status, + name, + type_id, + input); + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_pseudo_random(OM_uint32 *minor_status, + gss_ctx_id_t context, + int prf_key, + const gss_buffer_t prf_in, + ssize_t desired_output_len, + gss_buffer_t prf_out) +{ + OM_uint32 ret; + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context; + + if (sc->ctx_handle == GSS_C_NO_CONTEXT) + return (GSS_S_NO_CONTEXT); + + ret = gss_pseudo_random(minor_status, + sc->ctx_handle, + prf_key, + prf_in, + desired_output_len, + prf_out); + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_set_neg_mechs(OM_uint32 *minor_status, + gss_cred_id_t cred_handle, + const gss_OID_set mech_list) +{ + OM_uint32 ret; + spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle; + + /* Store mech_list in spcred for use in negotiation logic. */ + gss_release_oid_set(minor_status, &spcred->neg_mechs); + ret = generic_gss_copy_oid_set(minor_status, mech_list, + &spcred->neg_mechs); + return (ret); +} + +#define SPNEGO_SASL_NAME "SPNEGO" +#define SPNEGO_SASL_NAME_LEN (sizeof(SPNEGO_SASL_NAME) - 1) + +OM_uint32 KRB5_CALLCONV +spnego_gss_inquire_mech_for_saslname(OM_uint32 *minor_status, + const gss_buffer_t sasl_mech_name, + gss_OID *mech_type) +{ + if (sasl_mech_name->length == SPNEGO_SASL_NAME_LEN && + memcmp(sasl_mech_name->value, SPNEGO_SASL_NAME, + SPNEGO_SASL_NAME_LEN) == 0) { + if (mech_type != NULL) + *mech_type = (gss_OID)gss_mech_spnego; + return (GSS_S_COMPLETE); + } + + return (GSS_S_BAD_MECH); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_inquire_saslname_for_mech(OM_uint32 *minor_status, + const gss_OID desired_mech, + gss_buffer_t sasl_mech_name, + gss_buffer_t mech_name, + gss_buffer_t mech_description) +{ + *minor_status = 0; + + if (!g_OID_equal(desired_mech, gss_mech_spnego)) + return (GSS_S_BAD_MECH); + + if (!g_make_string_buffer(SPNEGO_SASL_NAME, sasl_mech_name) || + !g_make_string_buffer("spnego", mech_name) || + !g_make_string_buffer("Simple and Protected GSS-API " + "Negotiation Mechanism", mech_description)) + goto fail; + + return (GSS_S_COMPLETE); + +fail: + *minor_status = ENOMEM; + return (GSS_S_FAILURE); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_inquire_attrs_for_mech(OM_uint32 *minor_status, + gss_const_OID mech, + gss_OID_set *mech_attrs, + gss_OID_set *known_mech_attrs) +{ + OM_uint32 major, tmpMinor; + + /* known_mech_attrs is handled by mechglue */ + *minor_status = 0; + + if (mech_attrs == NULL) + return (GSS_S_COMPLETE); + + major = gss_create_empty_oid_set(minor_status, mech_attrs); + if (GSS_ERROR(major)) + goto cleanup; + +#define MA_SUPPORTED(ma) do { \ + major = gss_add_oid_set_member(minor_status, \ + (gss_OID)ma, mech_attrs); \ + if (GSS_ERROR(major)) \ + goto cleanup; \ + } while (0) + + MA_SUPPORTED(GSS_C_MA_MECH_NEGO); + MA_SUPPORTED(GSS_C_MA_ITOK_FRAMED); + +cleanup: + if (GSS_ERROR(major)) + gss_release_oid_set(&tmpMinor, mech_attrs); + + return (major); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_export_cred(OM_uint32 *minor_status, + gss_cred_id_t cred_handle, + gss_buffer_t token) +{ + spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle; + + return (gss_export_cred(minor_status, spcred->mcred, token)); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_import_cred(OM_uint32 *minor_status, + gss_buffer_t token, + gss_cred_id_t *cred_handle) +{ + OM_uint32 ret; + spnego_gss_cred_id_t spcred; + gss_cred_id_t mcred; + + ret = gss_import_cred(minor_status, token, &mcred); + if (GSS_ERROR(ret)) + return (ret); + + ret = create_spnego_cred(minor_status, mcred, &spcred); + if (GSS_ERROR(ret)) + return (ret); + + *cred_handle = (gss_cred_id_t)spcred; + return (ret); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_get_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_qop_t qop_req, gss_iov_buffer_desc *iov, + int iov_count) +{ + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle; + + if (sc->ctx_handle == GSS_C_NO_CONTEXT) + return (GSS_S_NO_CONTEXT); + + return gss_get_mic_iov(minor_status, sc->ctx_handle, qop_req, iov, + iov_count); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_verify_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_qop_t *qop_state, gss_iov_buffer_desc *iov, + int iov_count) +{ + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle; + + if (sc->ctx_handle == GSS_C_NO_CONTEXT) + return (GSS_S_NO_CONTEXT); + + return gss_verify_mic_iov(minor_status, sc->ctx_handle, qop_state, iov, + iov_count); +} + +OM_uint32 KRB5_CALLCONV +spnego_gss_get_mic_iov_length(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, gss_qop_t qop_req, + gss_iov_buffer_desc *iov, int iov_count) +{ + spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle; + + if (sc->ctx_handle == GSS_C_NO_CONTEXT) + return (GSS_S_NO_CONTEXT); + + return gss_get_mic_iov_length(minor_status, sc->ctx_handle, qop_req, iov, + iov_count); +} + +/* + * We will release everything but the ctx_handle so that it + * can be passed back to init/accept context. This routine should + * not be called until after the ctx_handle memory is assigned to + * the supplied context handle from init/accept context. + */ +static void +release_spnego_ctx(spnego_gss_ctx_id_t *ctx) +{ + spnego_gss_ctx_id_t context; + OM_uint32 minor_stat; + context = *ctx; + + if (context != NULL) { + (void) gss_release_buffer(&minor_stat, + &context->DER_mechTypes); + + (void) gss_release_oid_set(&minor_stat, &context->mech_set); + + (void) gss_release_name(&minor_stat, &context->internal_name); + + free(context); + *ctx = NULL; + } +} + +/* + * Can't use gss_indicate_mechs by itself to get available mechs for + * SPNEGO because it will also return the SPNEGO mech and we do not + * want to consider SPNEGO as an available security mech for + * negotiation. For this reason, get_available_mechs will return + * all available, non-deprecated mechs except SPNEGO. + * + * If a ptr to a creds list is given, this function will attempt + * to acquire creds for the creds given and trim the list of + * returned mechanisms to only those for which creds are valid. + * + */ +static OM_uint32 +get_available_mechs(OM_uint32 *minor_status, + gss_name_t name, gss_cred_usage_t usage, + gss_const_key_value_set_t cred_store, + gss_cred_id_t *creds, gss_OID_set *rmechs, OM_uint32 *time_rec) +{ + unsigned int i; + int found = 0; + OM_uint32 major_status = GSS_S_COMPLETE, tmpmin; + gss_OID_set mechs, goodmechs; + gss_OID_set_desc except_attrs; + gss_OID_desc attr_oids[2]; + + attr_oids[0] = *GSS_C_MA_DEPRECATED; + attr_oids[1] = *GSS_C_MA_NOT_DFLT_MECH; + except_attrs.count = 2; + except_attrs.elements = attr_oids; + major_status = gss_indicate_mechs_by_attrs(minor_status, + GSS_C_NO_OID_SET, + &except_attrs, + GSS_C_NO_OID_SET, &mechs); + + if (major_status != GSS_S_COMPLETE) { + return (major_status); + } + + major_status = gss_create_empty_oid_set(minor_status, rmechs); + + if (major_status != GSS_S_COMPLETE) { + (void) gss_release_oid_set(minor_status, &mechs); + return (major_status); + } + + for (i = 0; i < mechs->count && major_status == GSS_S_COMPLETE; i++) { + if ((mechs->elements[i].length + != spnego_mechanism.mech_type.length) || + memcmp(mechs->elements[i].elements, + spnego_mechanism.mech_type.elements, + spnego_mechanism.mech_type.length)) { + + major_status = gss_add_oid_set_member(minor_status, + &mechs->elements[i], + rmechs); + if (major_status == GSS_S_COMPLETE) + found++; + } + } + + /* + * If the caller wanted a list of creds returned, + * trim the list of mechanisms down to only those + * for which the creds are valid. + */ + if (found > 0 && major_status == GSS_S_COMPLETE && creds != NULL) { + major_status = gss_acquire_cred_from(minor_status, name, + GSS_C_INDEFINITE, + *rmechs, usage, + cred_store, creds, + &goodmechs, time_rec); + + /* + * Drop the old list in favor of the new + * "trimmed" list. + */ + (void) gss_release_oid_set(&tmpmin, rmechs); + if (major_status == GSS_S_COMPLETE) { + (void) gssint_copy_oid_set(&tmpmin, + goodmechs, rmechs); + (void) gss_release_oid_set(&tmpmin, &goodmechs); + } + } + + (void) gss_release_oid_set(&tmpmin, &mechs); + if (found == 0 || major_status != GSS_S_COMPLETE) { + *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE; + map_errcode(minor_status); + if (major_status == GSS_S_COMPLETE) + major_status = GSS_S_FAILURE; + } + + return (major_status); +} + +/* + * Return a list of mechanisms we are willing to negotiate for a credential, + * taking into account the mech set provided with gss_set_neg_mechs if it + * exists. + */ +static OM_uint32 +get_negotiable_mechs(OM_uint32 *minor_status, spnego_gss_cred_id_t spcred, + gss_cred_usage_t usage, gss_OID_set *rmechs) +{ + OM_uint32 ret, tmpmin; + gss_cred_id_t creds = GSS_C_NO_CREDENTIAL, *credptr; + gss_OID_set cred_mechs = GSS_C_NULL_OID_SET; + gss_OID_set intersect_mechs = GSS_C_NULL_OID_SET; + unsigned int i; + int present; + + if (spcred == NULL) { + /* + * The default credentials were supplied. Return a list of all + * available mechs except SPNEGO. When initiating, trim this + * list to mechs we can acquire credentials for. + */ + credptr = (usage == GSS_C_INITIATE) ? &creds : NULL; + ret = get_available_mechs(minor_status, GSS_C_NO_NAME, usage, + GSS_C_NO_CRED_STORE, credptr, + rmechs, NULL); + gss_release_cred(&tmpmin, &creds); + return (ret); + } + + /* Get the list of mechs in the mechglue cred. */ + ret = gss_inquire_cred(minor_status, spcred->mcred, NULL, NULL, NULL, + &cred_mechs); + if (ret != GSS_S_COMPLETE) + return (ret); + + if (spcred->neg_mechs == GSS_C_NULL_OID_SET) { + /* gss_set_neg_mechs was never called; return cred_mechs. */ + *rmechs = cred_mechs; + *minor_status = 0; + return (GSS_S_COMPLETE); + } + + /* Compute the intersection of cred_mechs and spcred->neg_mechs, + * preserving the order in spcred->neg_mechs. */ + ret = gss_create_empty_oid_set(minor_status, &intersect_mechs); + if (ret != GSS_S_COMPLETE) { + gss_release_oid_set(&tmpmin, &cred_mechs); + return (ret); + } + + for (i = 0; i < spcred->neg_mechs->count; i++) { + gss_test_oid_set_member(&tmpmin, + &spcred->neg_mechs->elements[i], + cred_mechs, &present); + if (!present) + continue; + ret = gss_add_oid_set_member(minor_status, + &spcred->neg_mechs->elements[i], + &intersect_mechs); + if (ret != GSS_S_COMPLETE) + break; + } + + gss_release_oid_set(&tmpmin, &cred_mechs); + if (intersect_mechs->count == 0 || ret != GSS_S_COMPLETE) { + gss_release_oid_set(&tmpmin, &intersect_mechs); + *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE; + map_errcode(minor_status); + return (GSS_S_FAILURE); + } + + *rmechs = intersect_mechs; + *minor_status = 0; + return (GSS_S_COMPLETE); +} + +/* following are token creation and reading routines */ + +/* + * If buff_in is not pointing to a MECH_OID, then return NULL and do not + * advance the buffer, otherwise, decode the mech_oid from the buffer and + * place in gss_OID. + */ +static gss_OID +get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length) +{ + OM_uint32 status; + gss_OID_desc toid; + gss_OID mech_out = NULL; + unsigned char *start, *end; + + if (length < 1 || **buff_in != MECH_OID) + return (NULL); + + start = *buff_in; + end = start + length; + + (*buff_in)++; + toid.length = *(*buff_in)++; + + if ((*buff_in + toid.length) > end) + return (NULL); + + toid.elements = *buff_in; + *buff_in += toid.length; + + status = generic_gss_copy_oid(minor_status, &toid, &mech_out); + + if (status != GSS_S_COMPLETE) { + map_errcode(minor_status); + mech_out = NULL; + } + + return (mech_out); +} + +/* + * der encode the given mechanism oid into buf_out, advancing the + * buffer pointer. + */ + +static int +put_mech_oid(unsigned char **buf_out, gss_OID_const mech, unsigned int buflen) +{ + if (buflen < mech->length + 2) + return (-1); + *(*buf_out)++ = MECH_OID; + *(*buf_out)++ = (unsigned char) mech->length; + memcpy(*buf_out, mech->elements, mech->length); + *buf_out += mech->length; + return (0); +} + +/* + * verify that buff_in points to an octet string, if it does not, + * return NULL and don't advance the pointer. If it is an octet string + * decode buff_in into a gss_buffer_t and return it, advancing the + * buffer pointer. + */ +static gss_buffer_t +get_input_token(unsigned char **buff_in, unsigned int buff_length) +{ + gss_buffer_t input_token; + unsigned int len; + + if (g_get_tag_and_length(buff_in, OCTET_STRING, buff_length, &len) < 0) + return (NULL); + + input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc)); + if (input_token == NULL) + return (NULL); + + input_token->length = len; + if (input_token->length > 0) { + input_token->value = gssalloc_malloc(input_token->length); + if (input_token->value == NULL) { + free(input_token); + return (NULL); + } + + memcpy(input_token->value, *buff_in, input_token->length); + } else { + input_token->value = NULL; + } + *buff_in += input_token->length; + return (input_token); +} + +/* + * verify that the input token length is not 0. If it is, just return. + * If the token length is greater than 0, der encode as an octet string + * and place in buf_out, advancing buf_out. + */ + +static int +put_input_token(unsigned char **buf_out, gss_buffer_t input_token, + unsigned int buflen) +{ + int ret; + + /* if token length is 0, we do not want to send */ + if (input_token->length == 0) + return (0); + + if (input_token->length > buflen) + return (-1); + + *(*buf_out)++ = OCTET_STRING; + if ((ret = gssint_put_der_length(input_token->length, buf_out, + input_token->length))) + return (ret); + TWRITE_STR(*buf_out, input_token->value, input_token->length); + return (0); +} + +/* + * verify that buff_in points to a sequence of der encoding. The mech + * set is the only sequence of encoded object in the token, so if it is + * a sequence of encoding, decode the mechset into a gss_OID_set and + * return it, advancing the buffer pointer. + */ +static gss_OID_set +get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in, + unsigned int buff_length) +{ + gss_OID_set returned_mechSet; + OM_uint32 major_status; + int length; + unsigned int bytes; + OM_uint32 set_length; + unsigned char *start; + int i; + + if (**buff_in != SEQUENCE_OF) + return (NULL); + + start = *buff_in; + (*buff_in)++; + + length = gssint_get_der_length(buff_in, buff_length, &bytes); + if (length < 0 || buff_length - bytes < (unsigned int)length) + return NULL; + + major_status = gss_create_empty_oid_set(minor_status, + &returned_mechSet); + if (major_status != GSS_S_COMPLETE) + return (NULL); + + for (set_length = 0, i = 0; set_length < (unsigned int)length; i++) { + gss_OID_desc *temp = get_mech_oid(minor_status, buff_in, + buff_length - (*buff_in - start)); + if (temp == NULL) + break; + + major_status = gss_add_oid_set_member(minor_status, + temp, &returned_mechSet); + if (major_status == GSS_S_COMPLETE) { + set_length += returned_mechSet->elements[i].length +2; + if (generic_gss_release_oid(minor_status, &temp)) + map_errcode(minor_status); + } + } + + return (returned_mechSet); +} + +/* + * Encode mechSet into buf. + */ +static int +put_mech_set(gss_OID_set mechSet, gss_buffer_t buf) +{ + unsigned char *ptr; + unsigned int i; + unsigned int tlen, ilen; + + tlen = ilen = 0; + for (i = 0; i < mechSet->count; i++) { + /* + * 0x06 [DER LEN] [OID] + */ + ilen += 1 + + gssint_der_length_size(mechSet->elements[i].length) + + mechSet->elements[i].length; + } + /* + * 0x30 [DER LEN] + */ + tlen = 1 + gssint_der_length_size(ilen) + ilen; + ptr = gssalloc_malloc(tlen); + if (ptr == NULL) + return -1; + + buf->value = ptr; + buf->length = tlen; +#define REMAIN (buf->length - ((unsigned char *)buf->value - ptr)) + + *ptr++ = SEQUENCE_OF; + if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0) + return -1; + for (i = 0; i < mechSet->count; i++) { + if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) { + return -1; + } + } + return 0; +#undef REMAIN +} + +/* + * Verify that buff_in is pointing to a BIT_STRING with the correct + * length and padding for the req_flags. If it is, decode req_flags + * and return them, otherwise, return NULL. + */ +static OM_uint32 +get_req_flags(unsigned char **buff_in, OM_uint32 bodysize, + OM_uint32 *req_flags) +{ + unsigned int len; + + if (**buff_in != (CONTEXT | 0x01)) + return (0); + + if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01), + bodysize, &len) < 0) + return GSS_S_DEFECTIVE_TOKEN; + + if (*(*buff_in)++ != BIT_STRING) + return GSS_S_DEFECTIVE_TOKEN; + + if (*(*buff_in)++ != BIT_STRING_LENGTH) + return GSS_S_DEFECTIVE_TOKEN; + + if (*(*buff_in)++ != BIT_STRING_PADDING) + return GSS_S_DEFECTIVE_TOKEN; + + *req_flags = (OM_uint32) (*(*buff_in)++ >> 1); + return (0); +} + +static OM_uint32 +get_negTokenInit(OM_uint32 *minor_status, + gss_buffer_t buf, + gss_buffer_t der_mechSet, + gss_OID_set *mechSet, + OM_uint32 *req_flags, + gss_buffer_t *mechtok, + gss_buffer_t *mechListMIC) +{ + OM_uint32 err; + unsigned char *ptr, *bufstart; + unsigned int len; + gss_buffer_desc tmpbuf; + + *minor_status = 0; + der_mechSet->length = 0; + der_mechSet->value = NULL; + *mechSet = GSS_C_NO_OID_SET; + *req_flags = 0; + *mechtok = *mechListMIC = GSS_C_NO_BUFFER; + + ptr = bufstart = buf->value; + if ((buf->length - (ptr - bufstart)) > INT_MAX) + return GSS_S_FAILURE; +#define REMAIN (buf->length - (ptr - bufstart)) + + err = g_verify_token_header(gss_mech_spnego, + &len, &ptr, 0, REMAIN); + if (err) { + *minor_status = err; + map_errcode(minor_status); + return GSS_S_FAILURE; + } + *minor_status = g_verify_neg_token_init(&ptr, REMAIN); + if (*minor_status) { + map_errcode(minor_status); + return GSS_S_FAILURE; + } + + /* alias into input_token */ + tmpbuf.value = ptr; + tmpbuf.length = REMAIN; + *mechSet = get_mech_set(minor_status, &ptr, REMAIN); + if (*mechSet == NULL) + return GSS_S_FAILURE; + + tmpbuf.length = ptr - (unsigned char *)tmpbuf.value; + der_mechSet->value = gssalloc_malloc(tmpbuf.length); + if (der_mechSet->value == NULL) + return GSS_S_FAILURE; + memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length); + der_mechSet->length = tmpbuf.length; + + err = get_req_flags(&ptr, REMAIN, req_flags); + if (err != GSS_S_COMPLETE) { + return err; + } + if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02), + REMAIN, &len) >= 0) { + *mechtok = get_input_token(&ptr, len); + if (*mechtok == GSS_C_NO_BUFFER) { + return GSS_S_FAILURE; + } + } + if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03), + REMAIN, &len) >= 0) { + *mechListMIC = get_input_token(&ptr, len); + if (*mechListMIC == GSS_C_NO_BUFFER) { + return GSS_S_FAILURE; + } + } + return GSS_S_COMPLETE; +#undef REMAIN +} + +static OM_uint32 +get_negTokenResp(OM_uint32 *minor_status, + unsigned char *buf, unsigned int buflen, + OM_uint32 *negState, + gss_OID *supportedMech, + gss_buffer_t *responseToken, + gss_buffer_t *mechListMIC) +{ + unsigned char *ptr, *bufstart; + unsigned int len; + int tmplen; + unsigned int tag, bytes; + + *negState = ACCEPT_DEFECTIVE_TOKEN; + *supportedMech = GSS_C_NO_OID; + *responseToken = *mechListMIC = GSS_C_NO_BUFFER; + ptr = bufstart = buf; +#define REMAIN (buflen - (ptr - bufstart)) + + if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0) + return GSS_S_DEFECTIVE_TOKEN; + if (*ptr++ == SEQUENCE) { + tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes); + if (tmplen < 0 || REMAIN < (unsigned int)tmplen) + return GSS_S_DEFECTIVE_TOKEN; + } + if (REMAIN < 1) + tag = 0; + else + tag = *ptr++; + + if (tag == CONTEXT) { + tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes); + if (tmplen < 0 || REMAIN < (unsigned int)tmplen) + return GSS_S_DEFECTIVE_TOKEN; + + if (g_get_tag_and_length(&ptr, ENUMERATED, + REMAIN, &len) < 0) + return GSS_S_DEFECTIVE_TOKEN; + + if (len != ENUMERATION_LENGTH) + return GSS_S_DEFECTIVE_TOKEN; + + if (REMAIN < 1) + return GSS_S_DEFECTIVE_TOKEN; + *negState = *ptr++; + + if (REMAIN < 1) + tag = 0; + else + tag = *ptr++; + } + if (tag == (CONTEXT | 0x01)) { + tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes); + if (tmplen < 0 || REMAIN < (unsigned int)tmplen) + return GSS_S_DEFECTIVE_TOKEN; + + *supportedMech = get_mech_oid(minor_status, &ptr, REMAIN); + if (*supportedMech == GSS_C_NO_OID) + return GSS_S_DEFECTIVE_TOKEN; + + if (REMAIN < 1) + tag = 0; + else + tag = *ptr++; + } + if (tag == (CONTEXT | 0x02)) { + tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes); + if (tmplen < 0 || REMAIN < (unsigned int)tmplen) + return GSS_S_DEFECTIVE_TOKEN; + + *responseToken = get_input_token(&ptr, REMAIN); + if (*responseToken == GSS_C_NO_BUFFER) + return GSS_S_DEFECTIVE_TOKEN; + + if (REMAIN < 1) + tag = 0; + else + tag = *ptr++; + } + if (tag == (CONTEXT | 0x03)) { + tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes); + if (tmplen < 0 || REMAIN < (unsigned int)tmplen) + return GSS_S_DEFECTIVE_TOKEN; + + *mechListMIC = get_input_token(&ptr, REMAIN); + if (*mechListMIC == GSS_C_NO_BUFFER) + return GSS_S_DEFECTIVE_TOKEN; + + /* Handle Windows 2000 duplicate response token */ + if (*responseToken && + ((*responseToken)->length == (*mechListMIC)->length) && + !memcmp((*responseToken)->value, (*mechListMIC)->value, + (*responseToken)->length)) { + OM_uint32 tmpmin; + + gss_release_buffer(&tmpmin, *mechListMIC); + free(*mechListMIC); + *mechListMIC = NULL; + } + } + return GSS_S_COMPLETE; +#undef REMAIN +} + +/* + * der encode the passed negResults as an ENUMERATED type and + * place it in buf_out, advancing the buffer. + */ + +static int +put_negResult(unsigned char **buf_out, OM_uint32 negResult, + unsigned int buflen) +{ + if (buflen < 3) + return (-1); + *(*buf_out)++ = ENUMERATED; + *(*buf_out)++ = ENUMERATION_LENGTH; + *(*buf_out)++ = (unsigned char) negResult; + return (0); +} + +/* + * This routine compares the recieved mechset to the mechset that + * this server can support. It looks sequentially through the mechset + * and the first one that matches what the server can support is + * chosen as the negotiated mechanism. If one is found, negResult + * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if + * it's not the first mech, otherwise we return NULL and negResult + * is set to REJECT. The returned pointer is an alias into + * received->elements and should not be freed. + * + * NOTE: There is currently no way to specify a preference order of + * mechanisms supported by the acceptor. + */ +static gss_OID +negotiate_mech(gss_OID_set supported, gss_OID_set received, + OM_uint32 *negResult) +{ + size_t i, j; + + for (i = 0; i < received->count; i++) { + gss_OID mech_oid = &received->elements[i]; + + /* Accept wrong mechanism OID from MS clients */ + if (g_OID_equal(mech_oid, &gss_mech_krb5_wrong_oid)) + mech_oid = (gss_OID)&gss_mech_krb5_oid; + + for (j = 0; j < supported->count; j++) { + if (g_OID_equal(mech_oid, &supported->elements[j])) { + *negResult = (i == 0) ? ACCEPT_INCOMPLETE : + REQUEST_MIC; + return &received->elements[i]; + } + } + } + *negResult = REJECT; + return (NULL); +} + +/* + * the next two routines make a token buffer suitable for + * spnego_gss_display_status. These currently take the string + * in name and place it in the token. Eventually, if + * spnego_gss_display_status returns valid error messages, + * these routines will be changes to return the error string. + */ +static spnego_token_t +make_spnego_token(const char *name) +{ + return (spnego_token_t)strdup(name); +} + +static gss_buffer_desc +make_err_msg(const char *name) +{ + gss_buffer_desc buffer; + + if (name == NULL) { + buffer.length = 0; + buffer.value = NULL; + } else { + buffer.length = strlen(name)+1; + buffer.value = make_spnego_token(name); + } + + return (buffer); +} + +/* + * Create the client side spnego token passed back to gss_init_sec_context + * and eventually up to the application program and over to the server. + * + * Use DER rules, definite length method per RFC 2478 + */ +static int +make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx, + int negHintsCompat, + gss_buffer_t mechListMIC, OM_uint32 req_flags, + gss_buffer_t data, send_token_flag sendtoken, + gss_buffer_t outbuf) +{ + int ret = 0; + unsigned int tlen, dataLen = 0; + unsigned int negTokenInitSize = 0; + unsigned int negTokenInitSeqSize = 0; + unsigned int negTokenInitContSize = 0; + unsigned int rspTokenSize = 0; + unsigned int mechListTokenSize = 0; + unsigned int micTokenSize = 0; + unsigned char *t; + unsigned char *ptr; + + if (outbuf == GSS_C_NO_BUFFER) + return (-1); + + outbuf->length = 0; + outbuf->value = NULL; + + /* calculate the data length */ + + /* + * 0xa0 [DER LEN] [mechTypes] + */ + mechListTokenSize = 1 + + gssint_der_length_size(spnego_ctx->DER_mechTypes.length) + + spnego_ctx->DER_mechTypes.length; + dataLen += mechListTokenSize; + + /* + * If a token from gss_init_sec_context exists, + * add the length of the token + the ASN.1 overhead + */ + if (data != NULL) { + /* + * Encoded in final output as: + * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA] + * -----s--------|--------s2---------- + */ + rspTokenSize = 1 + + gssint_der_length_size(data->length) + + data->length; + dataLen += 1 + gssint_der_length_size(rspTokenSize) + + rspTokenSize; + } + + if (mechListMIC) { + /* + * Encoded in final output as: + * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA] + * --s-- -----tlen------------ + */ + micTokenSize = 1 + + gssint_der_length_size(mechListMIC->length) + + mechListMIC->length; + dataLen += 1 + + gssint_der_length_size(micTokenSize) + + micTokenSize; + } + + /* + * Add size of DER encoding + * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ] + * 0x30 [DER_LEN] [data] + * + */ + negTokenInitContSize = dataLen; + negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen; + dataLen = negTokenInitSeqSize; + + /* + * negTokenInitSize indicates the bytes needed to + * hold the ASN.1 encoding of the entire NegTokenInit + * SEQUENCE. + * 0xa0 [DER_LEN] + data + * + */ + negTokenInitSize = 1 + + gssint_der_length_size(negTokenInitSeqSize) + + negTokenInitSeqSize; + + tlen = g_token_size(gss_mech_spnego, negTokenInitSize); + + t = (unsigned char *) gssalloc_malloc(tlen); + + if (t == NULL) { + return (-1); + } + + ptr = t; + + /* create the message */ + if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize, + &ptr, tlen))) + goto errout; + + *ptr++ = CONTEXT; /* NegotiationToken identifier */ + if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen))) + goto errout; + + *ptr++ = SEQUENCE; + if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr, + tlen - (int)(ptr-t)))) + goto errout; + + *ptr++ = CONTEXT | 0x00; /* MechTypeList identifier */ + if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length, + &ptr, tlen - (int)(ptr-t)))) + goto errout; + + /* We already encoded the MechSetList */ + (void) memcpy(ptr, spnego_ctx->DER_mechTypes.value, + spnego_ctx->DER_mechTypes.length); + + ptr += spnego_ctx->DER_mechTypes.length; + + if (data != NULL) { + *ptr++ = CONTEXT | 0x02; + if ((ret = gssint_put_der_length(rspTokenSize, + &ptr, tlen - (int)(ptr - t)))) + goto errout; + + if ((ret = put_input_token(&ptr, data, + tlen - (int)(ptr - t)))) + goto errout; + } + + if (mechListMIC != GSS_C_NO_BUFFER) { + *ptr++ = CONTEXT | 0x03; + if ((ret = gssint_put_der_length(micTokenSize, + &ptr, tlen - (int)(ptr - t)))) + goto errout; + + if (negHintsCompat) { + ret = put_neg_hints(&ptr, mechListMIC, + tlen - (int)(ptr - t)); + if (ret) + goto errout; + } else if ((ret = put_input_token(&ptr, mechListMIC, + tlen - (int)(ptr - t)))) + goto errout; + } + +errout: + if (ret != 0) { + if (t) + free(t); + t = NULL; + tlen = 0; + } + outbuf->length = tlen; + outbuf->value = (void *) t; + + return (ret); +} + +/* + * create the server side spnego token passed back to + * gss_accept_sec_context and eventually up to the application program + * and over to the client. + */ +static int +make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted, + gss_buffer_t data, gss_buffer_t mechListMIC, + send_token_flag sendtoken, + gss_buffer_t outbuf) +{ + unsigned int tlen = 0; + unsigned int ret = 0; + unsigned int NegTokenTargSize = 0; + unsigned int NegTokenSize = 0; + unsigned int rspTokenSize = 0; + unsigned int micTokenSize = 0; + unsigned int dataLen = 0; + unsigned char *t; + unsigned char *ptr; + + if (outbuf == GSS_C_NO_BUFFER) + return (GSS_S_DEFECTIVE_TOKEN); + if (sendtoken == INIT_TOKEN_SEND && mech_wanted == GSS_C_NO_OID) + return (GSS_S_DEFECTIVE_TOKEN); + + outbuf->length = 0; + outbuf->value = NULL; + + /* + * ASN.1 encoding of the negResult + * ENUMERATED type is 3 bytes + * ENUMERATED TAG, Length, Value, + * Plus 2 bytes for the CONTEXT id and length. + */ + dataLen = 5; + + /* + * calculate data length + * + * If this is the initial token, include length of + * mech_type and the negotiation result fields. + */ + if (sendtoken == INIT_TOKEN_SEND) { + int mechlistTokenSize; + /* + * 1 byte for the CONTEXT ID(0xa0), + * 1 byte for the OID ID(0x06) + * 1 byte for OID Length field + * Plus the rest... (OID Length, OID value) + */ + mechlistTokenSize = 3 + mech_wanted->length + + gssint_der_length_size(mech_wanted->length); + + dataLen += mechlistTokenSize; + } + if (data != NULL && data->length > 0) { + /* Length of the inner token */ + rspTokenSize = 1 + gssint_der_length_size(data->length) + + data->length; + + dataLen += rspTokenSize; + + /* Length of the outer token */ + dataLen += 1 + gssint_der_length_size(rspTokenSize); + } + if (mechListMIC != NULL) { + + /* Length of the inner token */ + micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) + + mechListMIC->length; + + dataLen += micTokenSize; + + /* Length of the outer token */ + dataLen += 1 + gssint_der_length_size(micTokenSize); + } + /* + * Add size of DER encoded: + * NegTokenTarg [ SEQUENCE ] of + * NegResult[0] ENUMERATED { + * accept_completed(0), + * accept_incomplete(1), + * reject(2) } + * supportedMech [1] MechType OPTIONAL, + * responseToken [2] OCTET STRING OPTIONAL, + * mechListMIC [3] OCTET STRING OPTIONAL + * + * size = data->length + MechListMic + SupportedMech len + + * Result Length + ASN.1 overhead + */ + NegTokenTargSize = dataLen; + dataLen += 1 + gssint_der_length_size(NegTokenTargSize); + + /* + * NegotiationToken [ CHOICE ]{ + * negTokenInit [0] NegTokenInit, + * negTokenTarg [1] NegTokenTarg } + */ + NegTokenSize = dataLen; + dataLen += 1 + gssint_der_length_size(NegTokenSize); + + tlen = dataLen; + t = (unsigned char *) gssalloc_malloc(tlen); + + if (t == NULL) { + ret = GSS_S_DEFECTIVE_TOKEN; + goto errout; + } + + ptr = t; + + /* + * Indicate that we are sending CHOICE 1 + * (NegTokenTarg) + */ + *ptr++ = CONTEXT | 0x01; + if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) { + ret = GSS_S_DEFECTIVE_TOKEN; + goto errout; + } + *ptr++ = SEQUENCE; + if (gssint_put_der_length(NegTokenTargSize, &ptr, + tlen - (int)(ptr-t)) < 0) { + ret = GSS_S_DEFECTIVE_TOKEN; + goto errout; + } + + /* + * First field of the NegTokenTarg SEQUENCE + * is the ENUMERATED NegResult. + */ + *ptr++ = CONTEXT; + if (gssint_put_der_length(3, &ptr, + tlen - (int)(ptr-t)) < 0) { + ret = GSS_S_DEFECTIVE_TOKEN; + goto errout; + } + if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) { + ret = GSS_S_DEFECTIVE_TOKEN; + goto errout; + } + if (sendtoken == INIT_TOKEN_SEND) { + /* + * Next, is the Supported MechType + */ + *ptr++ = CONTEXT | 0x01; + if (gssint_put_der_length(mech_wanted->length + 2, + &ptr, + tlen - (int)(ptr - t)) < 0) { + ret = GSS_S_DEFECTIVE_TOKEN; + goto errout; + } + if (put_mech_oid(&ptr, mech_wanted, + tlen - (int)(ptr - t)) < 0) { + ret = GSS_S_DEFECTIVE_TOKEN; + goto errout; + } + } + if (data != NULL && data->length > 0) { + *ptr++ = CONTEXT | 0x02; + if (gssint_put_der_length(rspTokenSize, &ptr, + tlen - (int)(ptr - t)) < 0) { + ret = GSS_S_DEFECTIVE_TOKEN; + goto errout; + } + if (put_input_token(&ptr, data, + tlen - (int)(ptr - t)) < 0) { + ret = GSS_S_DEFECTIVE_TOKEN; + goto errout; + } + } + if (mechListMIC != NULL) { + *ptr++ = CONTEXT | 0x03; + if (gssint_put_der_length(micTokenSize, &ptr, + tlen - (int)(ptr - t)) < 0) { + ret = GSS_S_DEFECTIVE_TOKEN; + goto errout; + } + if (put_input_token(&ptr, mechListMIC, + tlen - (int)(ptr - t)) < 0) { + ret = GSS_S_DEFECTIVE_TOKEN; + goto errout; + } + } + ret = GSS_S_COMPLETE; +errout: + if (ret != GSS_S_COMPLETE) { + if (t) + free(t); + } else { + outbuf->length = ptr - t; + outbuf->value = (void *) t; + } + + return (ret); +} + +/* determine size of token */ +static int +g_token_size(gss_OID_const mech, unsigned int body_size) +{ + int hdrsize; + + /* + * Initialize the header size to the + * MECH_OID byte + the bytes needed to indicate the + * length of the OID + the OID itself. + * + * 0x06 [MECHLENFIELD] MECHDATA + */ + hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length; + + /* + * Now add the bytes needed for the initial header + * token bytes: + * 0x60 + [DER_LEN] + HDRSIZE + */ + hdrsize += 1 + gssint_der_length_size(body_size + hdrsize); + + return (hdrsize + body_size); +} + +/* + * generate token header. + * + * Use DER Definite Length method per RFC2478 + * Use of indefinite length encoding will not be compatible + * with Microsoft or others that actually follow the spec. + */ +static int +g_make_token_header(gss_OID_const mech, + unsigned int body_size, + unsigned char **buf, + unsigned int totallen) +{ + int ret = 0; + unsigned int hdrsize; + unsigned char *p = *buf; + + hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length; + + *(*buf)++ = HEADER_ID; + if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen))) + return (ret); + + *(*buf)++ = MECH_OID; + if ((ret = gssint_put_der_length(mech->length, buf, + totallen - (int)(p - *buf)))) + return (ret); + TWRITE_STR(*buf, mech->elements, mech->length); + return (0); +} + +/* + * NOTE: This checks that the length returned by + * gssint_get_der_length() is not greater than the number of octets + * remaining, even though gssint_get_der_length() already checks, in + * theory. + */ +static int +g_get_tag_and_length(unsigned char **buf, int tag, + unsigned int buflen, unsigned int *outlen) +{ + unsigned char *ptr = *buf; + int ret = -1; /* pessimists, assume failure ! */ + unsigned int encoded_len; + int tmplen = 0; + + *outlen = 0; + if (buflen > 1 && *ptr == tag) { + ptr++; + tmplen = gssint_get_der_length(&ptr, buflen - 1, + &encoded_len); + if (tmplen < 0) { + ret = -1; + } else if ((unsigned int)tmplen > buflen - (ptr - *buf)) { + ret = -1; + } else + ret = 0; + } + *outlen = tmplen; + *buf = ptr; + return (ret); +} + +static int +g_verify_neg_token_init(unsigned char **buf_in, unsigned int cur_size) +{ + unsigned char *buf = *buf_in; + unsigned char *endptr = buf + cur_size; + int seqsize; + int ret = 0; + unsigned int bytes; + + /* + * Verify this is a NegotiationToken type token + * - check for a0(context specific identifier) + * - get length and verify that enoughd ata exists + */ + if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &bytes) < 0) + return (G_BAD_TOK_HEADER); + + cur_size = bytes; /* should indicate bytes remaining */ + + /* + * Verify the next piece, it should identify this as + * a strucure of type NegTokenInit. + */ + if (*buf++ == SEQUENCE) { + if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0) + return (G_BAD_TOK_HEADER); + /* + * Make sure we have the entire buffer as described + */ + if (seqsize > endptr - buf) + return (G_BAD_TOK_HEADER); + } else { + return (G_BAD_TOK_HEADER); + } + + cur_size = seqsize; /* should indicate bytes remaining */ + + /* + * Verify that the first blob is a sequence of mechTypes + */ + if (*buf++ == CONTEXT) { + if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0) + return (G_BAD_TOK_HEADER); + /* + * Make sure we have the entire buffer as described + */ + if (seqsize > endptr - buf) + return (G_BAD_TOK_HEADER); + } else { + return (G_BAD_TOK_HEADER); + } + + /* + * At this point, *buf should be at the beginning of the + * DER encoded list of mech types that are to be negotiated. + */ + *buf_in = buf; + + return (ret); + +} + +/* verify token header. */ +static int +g_verify_token_header(gss_OID_const mech, + unsigned int *body_size, + unsigned char **buf_in, + int tok_type, + unsigned int toksize) +{ + unsigned char *buf = *buf_in; + int seqsize; + gss_OID_desc toid; + int ret = 0; + unsigned int bytes; + + if (toksize-- < 1) + return (G_BAD_TOK_HEADER); + + if (*buf++ != HEADER_ID) + return (G_BAD_TOK_HEADER); + + if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0) + return (G_BAD_TOK_HEADER); + + if ((seqsize + bytes) != toksize) + return (G_BAD_TOK_HEADER); + + if (toksize-- < 1) + return (G_BAD_TOK_HEADER); + + + if (*buf++ != MECH_OID) + return (G_BAD_TOK_HEADER); + + if (toksize-- < 1) + return (G_BAD_TOK_HEADER); + + toid.length = *buf++; + + if (toksize < toid.length) + return (G_BAD_TOK_HEADER); + else + toksize -= toid.length; + + toid.elements = buf; + buf += toid.length; + + if (!g_OID_equal(&toid, mech)) + ret = G_WRONG_MECH; + + /* + * G_WRONG_MECH is not returned immediately because it's more important + * to return G_BAD_TOK_HEADER if the token header is in fact bad + */ + if (toksize < 2) + return (G_BAD_TOK_HEADER); + else + toksize -= 2; + + if (!ret) { + *buf_in = buf; + *body_size = toksize; + } + + return (ret); +} + +/* + * Return non-zero if the oid is one of the kerberos mech oids, + * otherwise return zero. + * + * N.B. There are 3 oids that represent the kerberos mech: + * RFC-specified GSS_MECH_KRB5_OID, + * Old pre-RFC GSS_MECH_KRB5_OLD_OID, + * Incorrect MS GSS_MECH_KRB5_WRONG_OID + */ + +static int +is_kerb_mech(gss_OID oid) +{ + int answer = 0; + OM_uint32 minor; + extern const gss_OID_set_desc * const gss_mech_set_krb5_both; + + (void) gss_test_oid_set_member(&minor, + oid, (gss_OID_set)gss_mech_set_krb5_both, &answer); + + return (answer); +} |
