summaryrefslogtreecommitdiffstats
path: root/utils/gssd
diff options
context:
space:
mode:
Diffstat (limited to 'utils/gssd')
-rw-r--r--utils/gssd/.gitignore3
-rw-r--r--utils/gssd/Makefile.am130
-rw-r--r--utils/gssd/Makefile.in1356
-rw-r--r--utils/gssd/context.c59
-rw-r--r--utils/gssd/context.h49
-rw-r--r--utils/gssd/context_heimdal.c275
-rw-r--r--utils/gssd/context_lucid.c327
-rw-r--r--utils/gssd/context_mit.c280
-rw-r--r--utils/gssd/err_util.c86
-rw-r--r--utils/gssd/err_util.h39
-rw-r--r--utils/gssd/gss_names.c141
-rw-r--r--utils/gssd/gss_names.h36
-rw-r--r--utils/gssd/gss_oids.c40
-rw-r--r--utils/gssd/gss_oids.h44
-rw-r--r--utils/gssd/gss_util.c347
-rw-r--r--utils/gssd/gss_util.h56
-rw-r--r--utils/gssd/gssd.c1320
-rw-r--r--utils/gssd/gssd.h124
-rw-r--r--utils/gssd/gssd.man408
-rw-r--r--utils/gssd/gssd_proc.c1075
-rw-r--r--utils/gssd/krb5_util.c1649
-rw-r--r--utils/gssd/krb5_util.h47
-rw-r--r--utils/gssd/svcgssd.c348
-rw-r--r--utils/gssd/svcgssd.h42
-rw-r--r--utils/gssd/svcgssd.man88
-rw-r--r--utils/gssd/svcgssd_krb5.c237
-rw-r--r--utils/gssd/svcgssd_krb5.h37
-rw-r--r--utils/gssd/svcgssd_mech2file.c74
-rw-r--r--utils/gssd/svcgssd_proc.c444
-rw-r--r--utils/gssd/write_bytes.h159
30 files changed, 9320 insertions, 0 deletions
diff --git a/utils/gssd/.gitignore b/utils/gssd/.gitignore
new file mode 100644
index 0000000..aba7b13
--- /dev/null
+++ b/utils/gssd/.gitignore
@@ -0,0 +1,3 @@
+gss_clnt_send_err
+gssd
+svcgssd
diff --git a/utils/gssd/Makefile.am b/utils/gssd/Makefile.am
new file mode 100644
index 0000000..21d3bb8
--- /dev/null
+++ b/utils/gssd/Makefile.am
@@ -0,0 +1,130 @@
+## Process this file with automake to produce Makefile.in
+
+man8_MANS = gssd.man
+if CONFIG_SVCGSS
+man8_MANS += svcgssd.man
+endif
+
+AM_CPPFLAGS += -I ../../support/nfsidmap
+
+RPCPREFIX = rpc.
+KPREFIX = @kprefix@
+sbin_PREFIXED = gssd
+if CONFIG_SVCGSS
+sbin_PREFIXED += svcgssd
+endif
+
+sbin_PROGRAMS = $(sbin_PREFIXED)
+
+EXTRA_DIST = \
+ $(man8_MANS)
+
+COMMON_SRCS = \
+ context.c \
+ context_mit.c \
+ context_heimdal.c \
+ context_lucid.c \
+ gss_util.c \
+ gss_oids.c \
+ gss_names.c \
+ err_util.c \
+ \
+ context.h \
+ err_util.h \
+ gss_oids.h \
+ gss_names.h \
+ gss_util.h
+
+gssd_SOURCES = \
+ $(COMMON_SRCS) \
+ gssd.c \
+ gssd_proc.c \
+ krb5_util.c \
+ \
+ gssd.h \
+ krb5_util.h \
+ write_bytes.h
+
+gssd_LDADD = \
+ ../../support/nfs/libnfs.la \
+ $(LIBEVENT) \
+ $(RPCSECGSS_LIBS) \
+ $(KRBLIBS) \
+ $(GSSAPI_LIBS) \
+ $(LIBTIRPC) \
+ $(LIBPTHREAD)
+
+gssd_LDFLAGS = \
+ $(KRBLDFLAGS)
+
+gssd_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(CFLAGS) \
+ $(RPCSECGSS_CFLAGS) \
+ $(KRBCFLAGS) \
+ $(GSSAPI_CFLAGS)
+
+svcgssd_SOURCES = \
+ $(COMMON_SRCS) \
+ svcgssd.c \
+ svcgssd_mech2file.c \
+ svcgssd_proc.c \
+ svcgssd_krb5.c \
+ \
+ svcgssd_krb5.h \
+ svcgssd.h
+
+svcgssd_LDADD = \
+ ../../support/nfs/libnfs.la \
+ ../../support/nfsidmap/libnfsidmap.la \
+ $(LIBEVENT) \
+ $(RPCSECGSS_LIBS) \
+ $(KRBLIBS) $(GSSAPI_LIBS) $(LIBTIRPC)
+
+svcgssd_LDFLAGS = $(KRBLDFLAGS)
+
+svcgssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) \
+ $(RPCSECGSS_CFLAGS) $(KRBCFLAGS) $(GSSAPI_CFLAGS)
+
+MAINTAINERCLEANFILES = Makefile.in
+
+#######################################################################
+# The following allows the current practice of having
+# daemons renamed during the install to include RPCPREFIX
+# and the KPREFIX
+# This could all be done much easier with program_transform_name
+# ( program_transform_name = s/^/$(RPCPREFIX)$(KPREFIX)/ )
+# but that also renames the man pages, which the current
+# practice does not do.
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PREFIXED); do \
+ mv -f $$p$(EXEEXT) $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PREFIXED); do \
+ rm -f $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+
+
+# XXX This makes some assumptions about what automake does.
+# XXX But there is no install-man-hook or install-man-local.
+install-man: install-man8 install-man-links
+uninstall-man: uninstall-man8 uninstall-man-links
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ $(LN_S) $$inst $(RPCPREFIX)$$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ done)
+
diff --git a/utils/gssd/Makefile.in b/utils/gssd/Makefile.in
new file mode 100644
index 0000000..75d69c3
--- /dev/null
+++ b/utils/gssd/Makefile.in
@@ -0,0 +1,1356 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@CONFIG_SVCGSS_TRUE@am__append_1 = svcgssd.man
+@CONFIG_SVCGSS_TRUE@am__append_2 = svcgssd
+sbin_PROGRAMS = $(am__EXEEXT_2)
+subdir = utils/gssd
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/aclocal/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/aclocal/bsdsignals.m4 \
+ $(top_srcdir)/aclocal/getrandom.m4 \
+ $(top_srcdir)/aclocal/ipv6.m4 \
+ $(top_srcdir)/aclocal/kerberos5.m4 \
+ $(top_srcdir)/aclocal/keyutils.m4 \
+ $(top_srcdir)/aclocal/libblkid.m4 \
+ $(top_srcdir)/aclocal/libcap.m4 \
+ $(top_srcdir)/aclocal/libevent.m4 \
+ $(top_srcdir)/aclocal/libpthread.m4 \
+ $(top_srcdir)/aclocal/libsqlite3.m4 \
+ $(top_srcdir)/aclocal/libtirpc.m4 \
+ $(top_srcdir)/aclocal/libxml2.m4 \
+ $(top_srcdir)/aclocal/nfs-utils.m4 \
+ $(top_srcdir)/aclocal/rpcsec_vers.m4 \
+ $(top_srcdir)/aclocal/tcp-wrappers.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/support/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+@CONFIG_SVCGSS_TRUE@am__EXEEXT_1 = svcgssd$(EXEEXT)
+am__EXEEXT_2 = gssd$(EXEEXT) $(am__EXEEXT_1)
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am__objects_1 = gssd-context.$(OBJEXT) gssd-context_mit.$(OBJEXT) \
+ gssd-context_heimdal.$(OBJEXT) gssd-context_lucid.$(OBJEXT) \
+ gssd-gss_util.$(OBJEXT) gssd-gss_oids.$(OBJEXT) \
+ gssd-gss_names.$(OBJEXT) gssd-err_util.$(OBJEXT)
+am_gssd_OBJECTS = $(am__objects_1) gssd-gssd.$(OBJEXT) \
+ gssd-gssd_proc.$(OBJEXT) gssd-krb5_util.$(OBJEXT)
+gssd_OBJECTS = $(am_gssd_OBJECTS)
+am__DEPENDENCIES_1 =
+gssd_DEPENDENCIES = ../../support/nfs/libnfs.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+gssd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(gssd_CFLAGS) $(CFLAGS) \
+ $(gssd_LDFLAGS) $(LDFLAGS) -o $@
+am__objects_2 = svcgssd-context.$(OBJEXT) \
+ svcgssd-context_mit.$(OBJEXT) \
+ svcgssd-context_heimdal.$(OBJEXT) \
+ svcgssd-context_lucid.$(OBJEXT) svcgssd-gss_util.$(OBJEXT) \
+ svcgssd-gss_oids.$(OBJEXT) svcgssd-gss_names.$(OBJEXT) \
+ svcgssd-err_util.$(OBJEXT)
+am_svcgssd_OBJECTS = $(am__objects_2) svcgssd-svcgssd.$(OBJEXT) \
+ svcgssd-svcgssd_mech2file.$(OBJEXT) \
+ svcgssd-svcgssd_proc.$(OBJEXT) svcgssd-svcgssd_krb5.$(OBJEXT)
+svcgssd_OBJECTS = $(am_svcgssd_OBJECTS)
+svcgssd_DEPENDENCIES = ../../support/nfs/libnfs.la \
+ ../../support/nfsidmap/libnfsidmap.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+svcgssd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(svcgssd_CFLAGS) \
+ $(CFLAGS) $(svcgssd_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/support/include
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gssd-context.Po \
+ ./$(DEPDIR)/gssd-context_heimdal.Po \
+ ./$(DEPDIR)/gssd-context_lucid.Po \
+ ./$(DEPDIR)/gssd-context_mit.Po ./$(DEPDIR)/gssd-err_util.Po \
+ ./$(DEPDIR)/gssd-gss_names.Po ./$(DEPDIR)/gssd-gss_oids.Po \
+ ./$(DEPDIR)/gssd-gss_util.Po ./$(DEPDIR)/gssd-gssd.Po \
+ ./$(DEPDIR)/gssd-gssd_proc.Po ./$(DEPDIR)/gssd-krb5_util.Po \
+ ./$(DEPDIR)/svcgssd-context.Po \
+ ./$(DEPDIR)/svcgssd-context_heimdal.Po \
+ ./$(DEPDIR)/svcgssd-context_lucid.Po \
+ ./$(DEPDIR)/svcgssd-context_mit.Po \
+ ./$(DEPDIR)/svcgssd-err_util.Po \
+ ./$(DEPDIR)/svcgssd-gss_names.Po \
+ ./$(DEPDIR)/svcgssd-gss_oids.Po \
+ ./$(DEPDIR)/svcgssd-gss_util.Po ./$(DEPDIR)/svcgssd-svcgssd.Po \
+ ./$(DEPDIR)/svcgssd-svcgssd_krb5.Po \
+ ./$(DEPDIR)/svcgssd-svcgssd_mech2file.Po \
+ ./$(DEPDIR)/svcgssd-svcgssd_proc.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(gssd_SOURCES) $(svcgssd_SOURCES)
+DIST_SOURCES = $(gssd_SOURCES) $(svcgssd_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man8_MANS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_CPPFLAGS = @AM_CPPFLAGS@ -I ../../support/nfsidmap
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CXXFLAGS_FOR_BUILD = @CXXFLAGS_FOR_BUILD@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+GSSD = @GSSD@
+GSSGLUE_CFLAGS = @GSSGLUE_CFLAGS@
+GSSGLUE_LIBS = @GSSGLUE_LIBS@
+GSSKRB_CFLAGS = @GSSKRB_CFLAGS@
+GSSKRB_LIBS = @GSSKRB_LIBS@
+HAVE_GETRANDOM = @HAVE_GETRANDOM@
+HAVE_LIBWRAP = @HAVE_LIBWRAP@
+HAVE_TCP_WRAPPER = @HAVE_TCP_WRAPPER@
+IDMAPD = @IDMAPD@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+K5VERS = @K5VERS@
+KRBCFLAGS = @KRBCFLAGS@
+KRBDIR = @KRBDIR@
+KRBLDFLAGS = @KRBLDFLAGS@
+KRBLIBS = @KRBLIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBLKID = @LIBBLKID@
+LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
+LIBCRYPT = @LIBCRYPT@
+LIBEVENT = @LIBEVENT@
+LIBKEYUTILS = @LIBKEYUTILS@
+LIBMOUNT = @LIBMOUNT@
+LIBMOUNT_CFLAGS = @LIBMOUNT_CFLAGS@
+LIBMOUNT_LIBS = @LIBMOUNT_LIBS@
+LIBNSL = @LIBNSL@
+LIBOBJS = @LIBOBJS@
+LIBPTHREAD = @LIBPTHREAD@
+LIBS = @LIBS@
+LIBSOCKET = @LIBSOCKET@
+LIBSQLITE = @LIBSQLITE@
+LIBTIRPC = @LIBTIRPC@
+LIBTOOL = @LIBTOOL@
+LIBWRAP = @LIBWRAP@
+LIBXML2 = @LIBXML2@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_PLUGINS = @PATH_PLUGINS@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+RANLIB = @RANLIB@
+RELEASE = @RELEASE@
+RPCGEN_PATH = @RPCGEN_PATH@
+RPCSECGSS_CFLAGS = @RPCSECGSS_CFLAGS@
+RPCSECGSS_LIBS = @RPCSECGSS_LIBS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SVCGSSD = @SVCGSSD@
+TIRPC_CFLAGS = @TIRPC_CFLAGS@
+TIRPC_LIBS = @TIRPC_LIBS@
+VERSION = @VERSION@
+XML2_CFLAGS = @XML2_CFLAGS@
+XML2_LIBS = @XML2_LIBS@
+_rpc_pipefsmount = @_rpc_pipefsmount@
+_statedir = @_statedir@
+_sysconfdir = @_sysconfdir@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+enable_gss = @enable_gss@
+enable_ipv6 = @enable_ipv6@
+enable_mountconfig = @enable_mountconfig@
+enable_nfsv4 = @enable_nfsv4@
+enable_nfsv41 = @enable_nfsv41@
+enable_svcgss = @enable_svcgss@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+kprefix = @kprefix@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+mountfile = @mountfile@
+nfsconfig = @nfsconfig@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+rpc_pipefsmount = @rpc_pipefsmount@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+startstatd = @startstatd@
+statdpath = @statdpath@
+statduser = @statduser@
+statedir = @statedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+unitdir = @unitdir@
+man8_MANS = gssd.man $(am__append_1)
+RPCPREFIX = rpc.
+KPREFIX = @kprefix@
+sbin_PREFIXED = gssd $(am__append_2)
+EXTRA_DIST = \
+ $(man8_MANS)
+
+COMMON_SRCS = \
+ context.c \
+ context_mit.c \
+ context_heimdal.c \
+ context_lucid.c \
+ gss_util.c \
+ gss_oids.c \
+ gss_names.c \
+ err_util.c \
+ \
+ context.h \
+ err_util.h \
+ gss_oids.h \
+ gss_names.h \
+ gss_util.h
+
+gssd_SOURCES = \
+ $(COMMON_SRCS) \
+ gssd.c \
+ gssd_proc.c \
+ krb5_util.c \
+ \
+ gssd.h \
+ krb5_util.h \
+ write_bytes.h
+
+gssd_LDADD = \
+ ../../support/nfs/libnfs.la \
+ $(LIBEVENT) \
+ $(RPCSECGSS_LIBS) \
+ $(KRBLIBS) \
+ $(GSSAPI_LIBS) \
+ $(LIBTIRPC) \
+ $(LIBPTHREAD)
+
+gssd_LDFLAGS = \
+ $(KRBLDFLAGS)
+
+gssd_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(CFLAGS) \
+ $(RPCSECGSS_CFLAGS) \
+ $(KRBCFLAGS) \
+ $(GSSAPI_CFLAGS)
+
+svcgssd_SOURCES = \
+ $(COMMON_SRCS) \
+ svcgssd.c \
+ svcgssd_mech2file.c \
+ svcgssd_proc.c \
+ svcgssd_krb5.c \
+ \
+ svcgssd_krb5.h \
+ svcgssd.h
+
+svcgssd_LDADD = \
+ ../../support/nfs/libnfs.la \
+ ../../support/nfsidmap/libnfsidmap.la \
+ $(LIBEVENT) \
+ $(RPCSECGSS_LIBS) \
+ $(KRBLIBS) $(GSSAPI_LIBS) $(LIBTIRPC)
+
+svcgssd_LDFLAGS = $(KRBLDFLAGS)
+svcgssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) \
+ $(RPCSECGSS_CFLAGS) $(KRBCFLAGS) $(GSSAPI_CFLAGS)
+
+MAINTAINERCLEANFILES = Makefile.in
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu utils/gssd/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/gssd/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+gssd$(EXEEXT): $(gssd_OBJECTS) $(gssd_DEPENDENCIES) $(EXTRA_gssd_DEPENDENCIES)
+ @rm -f gssd$(EXEEXT)
+ $(AM_V_CCLD)$(gssd_LINK) $(gssd_OBJECTS) $(gssd_LDADD) $(LIBS)
+
+svcgssd$(EXEEXT): $(svcgssd_OBJECTS) $(svcgssd_DEPENDENCIES) $(EXTRA_svcgssd_DEPENDENCIES)
+ @rm -f svcgssd$(EXEEXT)
+ $(AM_V_CCLD)$(svcgssd_LINK) $(svcgssd_OBJECTS) $(svcgssd_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-context.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-context_heimdal.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-context_lucid.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-context_mit.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-err_util.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-gss_names.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-gss_oids.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-gss_util.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-gssd.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-gssd_proc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-krb5_util.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-context.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-context_heimdal.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-context_lucid.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-context_mit.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-err_util.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-gss_names.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-gss_oids.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-gss_util.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-svcgssd.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-svcgssd_krb5.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-svcgssd_mech2file.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-svcgssd_proc.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+gssd-context.o: context.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-context.o -MD -MP -MF $(DEPDIR)/gssd-context.Tpo -c -o gssd-context.o `test -f 'context.c' || echo '$(srcdir)/'`context.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-context.Tpo $(DEPDIR)/gssd-context.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context.c' object='gssd-context.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-context.o `test -f 'context.c' || echo '$(srcdir)/'`context.c
+
+gssd-context.obj: context.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-context.obj -MD -MP -MF $(DEPDIR)/gssd-context.Tpo -c -o gssd-context.obj `if test -f 'context.c'; then $(CYGPATH_W) 'context.c'; else $(CYGPATH_W) '$(srcdir)/context.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-context.Tpo $(DEPDIR)/gssd-context.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context.c' object='gssd-context.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-context.obj `if test -f 'context.c'; then $(CYGPATH_W) 'context.c'; else $(CYGPATH_W) '$(srcdir)/context.c'; fi`
+
+gssd-context_mit.o: context_mit.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-context_mit.o -MD -MP -MF $(DEPDIR)/gssd-context_mit.Tpo -c -o gssd-context_mit.o `test -f 'context_mit.c' || echo '$(srcdir)/'`context_mit.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-context_mit.Tpo $(DEPDIR)/gssd-context_mit.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_mit.c' object='gssd-context_mit.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-context_mit.o `test -f 'context_mit.c' || echo '$(srcdir)/'`context_mit.c
+
+gssd-context_mit.obj: context_mit.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-context_mit.obj -MD -MP -MF $(DEPDIR)/gssd-context_mit.Tpo -c -o gssd-context_mit.obj `if test -f 'context_mit.c'; then $(CYGPATH_W) 'context_mit.c'; else $(CYGPATH_W) '$(srcdir)/context_mit.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-context_mit.Tpo $(DEPDIR)/gssd-context_mit.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_mit.c' object='gssd-context_mit.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-context_mit.obj `if test -f 'context_mit.c'; then $(CYGPATH_W) 'context_mit.c'; else $(CYGPATH_W) '$(srcdir)/context_mit.c'; fi`
+
+gssd-context_heimdal.o: context_heimdal.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-context_heimdal.o -MD -MP -MF $(DEPDIR)/gssd-context_heimdal.Tpo -c -o gssd-context_heimdal.o `test -f 'context_heimdal.c' || echo '$(srcdir)/'`context_heimdal.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-context_heimdal.Tpo $(DEPDIR)/gssd-context_heimdal.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_heimdal.c' object='gssd-context_heimdal.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-context_heimdal.o `test -f 'context_heimdal.c' || echo '$(srcdir)/'`context_heimdal.c
+
+gssd-context_heimdal.obj: context_heimdal.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-context_heimdal.obj -MD -MP -MF $(DEPDIR)/gssd-context_heimdal.Tpo -c -o gssd-context_heimdal.obj `if test -f 'context_heimdal.c'; then $(CYGPATH_W) 'context_heimdal.c'; else $(CYGPATH_W) '$(srcdir)/context_heimdal.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-context_heimdal.Tpo $(DEPDIR)/gssd-context_heimdal.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_heimdal.c' object='gssd-context_heimdal.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-context_heimdal.obj `if test -f 'context_heimdal.c'; then $(CYGPATH_W) 'context_heimdal.c'; else $(CYGPATH_W) '$(srcdir)/context_heimdal.c'; fi`
+
+gssd-context_lucid.o: context_lucid.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-context_lucid.o -MD -MP -MF $(DEPDIR)/gssd-context_lucid.Tpo -c -o gssd-context_lucid.o `test -f 'context_lucid.c' || echo '$(srcdir)/'`context_lucid.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-context_lucid.Tpo $(DEPDIR)/gssd-context_lucid.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_lucid.c' object='gssd-context_lucid.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-context_lucid.o `test -f 'context_lucid.c' || echo '$(srcdir)/'`context_lucid.c
+
+gssd-context_lucid.obj: context_lucid.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-context_lucid.obj -MD -MP -MF $(DEPDIR)/gssd-context_lucid.Tpo -c -o gssd-context_lucid.obj `if test -f 'context_lucid.c'; then $(CYGPATH_W) 'context_lucid.c'; else $(CYGPATH_W) '$(srcdir)/context_lucid.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-context_lucid.Tpo $(DEPDIR)/gssd-context_lucid.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_lucid.c' object='gssd-context_lucid.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-context_lucid.obj `if test -f 'context_lucid.c'; then $(CYGPATH_W) 'context_lucid.c'; else $(CYGPATH_W) '$(srcdir)/context_lucid.c'; fi`
+
+gssd-gss_util.o: gss_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gss_util.o -MD -MP -MF $(DEPDIR)/gssd-gss_util.Tpo -c -o gssd-gss_util.o `test -f 'gss_util.c' || echo '$(srcdir)/'`gss_util.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gss_util.Tpo $(DEPDIR)/gssd-gss_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_util.c' object='gssd-gss_util.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gss_util.o `test -f 'gss_util.c' || echo '$(srcdir)/'`gss_util.c
+
+gssd-gss_util.obj: gss_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gss_util.obj -MD -MP -MF $(DEPDIR)/gssd-gss_util.Tpo -c -o gssd-gss_util.obj `if test -f 'gss_util.c'; then $(CYGPATH_W) 'gss_util.c'; else $(CYGPATH_W) '$(srcdir)/gss_util.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gss_util.Tpo $(DEPDIR)/gssd-gss_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_util.c' object='gssd-gss_util.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gss_util.obj `if test -f 'gss_util.c'; then $(CYGPATH_W) 'gss_util.c'; else $(CYGPATH_W) '$(srcdir)/gss_util.c'; fi`
+
+gssd-gss_oids.o: gss_oids.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gss_oids.o -MD -MP -MF $(DEPDIR)/gssd-gss_oids.Tpo -c -o gssd-gss_oids.o `test -f 'gss_oids.c' || echo '$(srcdir)/'`gss_oids.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gss_oids.Tpo $(DEPDIR)/gssd-gss_oids.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_oids.c' object='gssd-gss_oids.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gss_oids.o `test -f 'gss_oids.c' || echo '$(srcdir)/'`gss_oids.c
+
+gssd-gss_oids.obj: gss_oids.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gss_oids.obj -MD -MP -MF $(DEPDIR)/gssd-gss_oids.Tpo -c -o gssd-gss_oids.obj `if test -f 'gss_oids.c'; then $(CYGPATH_W) 'gss_oids.c'; else $(CYGPATH_W) '$(srcdir)/gss_oids.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gss_oids.Tpo $(DEPDIR)/gssd-gss_oids.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_oids.c' object='gssd-gss_oids.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gss_oids.obj `if test -f 'gss_oids.c'; then $(CYGPATH_W) 'gss_oids.c'; else $(CYGPATH_W) '$(srcdir)/gss_oids.c'; fi`
+
+gssd-gss_names.o: gss_names.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gss_names.o -MD -MP -MF $(DEPDIR)/gssd-gss_names.Tpo -c -o gssd-gss_names.o `test -f 'gss_names.c' || echo '$(srcdir)/'`gss_names.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gss_names.Tpo $(DEPDIR)/gssd-gss_names.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_names.c' object='gssd-gss_names.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gss_names.o `test -f 'gss_names.c' || echo '$(srcdir)/'`gss_names.c
+
+gssd-gss_names.obj: gss_names.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gss_names.obj -MD -MP -MF $(DEPDIR)/gssd-gss_names.Tpo -c -o gssd-gss_names.obj `if test -f 'gss_names.c'; then $(CYGPATH_W) 'gss_names.c'; else $(CYGPATH_W) '$(srcdir)/gss_names.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gss_names.Tpo $(DEPDIR)/gssd-gss_names.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_names.c' object='gssd-gss_names.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gss_names.obj `if test -f 'gss_names.c'; then $(CYGPATH_W) 'gss_names.c'; else $(CYGPATH_W) '$(srcdir)/gss_names.c'; fi`
+
+gssd-err_util.o: err_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-err_util.o -MD -MP -MF $(DEPDIR)/gssd-err_util.Tpo -c -o gssd-err_util.o `test -f 'err_util.c' || echo '$(srcdir)/'`err_util.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-err_util.Tpo $(DEPDIR)/gssd-err_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='err_util.c' object='gssd-err_util.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-err_util.o `test -f 'err_util.c' || echo '$(srcdir)/'`err_util.c
+
+gssd-err_util.obj: err_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-err_util.obj -MD -MP -MF $(DEPDIR)/gssd-err_util.Tpo -c -o gssd-err_util.obj `if test -f 'err_util.c'; then $(CYGPATH_W) 'err_util.c'; else $(CYGPATH_W) '$(srcdir)/err_util.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-err_util.Tpo $(DEPDIR)/gssd-err_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='err_util.c' object='gssd-err_util.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-err_util.obj `if test -f 'err_util.c'; then $(CYGPATH_W) 'err_util.c'; else $(CYGPATH_W) '$(srcdir)/err_util.c'; fi`
+
+gssd-gssd.o: gssd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gssd.o -MD -MP -MF $(DEPDIR)/gssd-gssd.Tpo -c -o gssd-gssd.o `test -f 'gssd.c' || echo '$(srcdir)/'`gssd.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gssd.Tpo $(DEPDIR)/gssd-gssd.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gssd.c' object='gssd-gssd.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gssd.o `test -f 'gssd.c' || echo '$(srcdir)/'`gssd.c
+
+gssd-gssd.obj: gssd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gssd.obj -MD -MP -MF $(DEPDIR)/gssd-gssd.Tpo -c -o gssd-gssd.obj `if test -f 'gssd.c'; then $(CYGPATH_W) 'gssd.c'; else $(CYGPATH_W) '$(srcdir)/gssd.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gssd.Tpo $(DEPDIR)/gssd-gssd.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gssd.c' object='gssd-gssd.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gssd.obj `if test -f 'gssd.c'; then $(CYGPATH_W) 'gssd.c'; else $(CYGPATH_W) '$(srcdir)/gssd.c'; fi`
+
+gssd-gssd_proc.o: gssd_proc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gssd_proc.o -MD -MP -MF $(DEPDIR)/gssd-gssd_proc.Tpo -c -o gssd-gssd_proc.o `test -f 'gssd_proc.c' || echo '$(srcdir)/'`gssd_proc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gssd_proc.Tpo $(DEPDIR)/gssd-gssd_proc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gssd_proc.c' object='gssd-gssd_proc.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gssd_proc.o `test -f 'gssd_proc.c' || echo '$(srcdir)/'`gssd_proc.c
+
+gssd-gssd_proc.obj: gssd_proc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gssd_proc.obj -MD -MP -MF $(DEPDIR)/gssd-gssd_proc.Tpo -c -o gssd-gssd_proc.obj `if test -f 'gssd_proc.c'; then $(CYGPATH_W) 'gssd_proc.c'; else $(CYGPATH_W) '$(srcdir)/gssd_proc.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gssd_proc.Tpo $(DEPDIR)/gssd-gssd_proc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gssd_proc.c' object='gssd-gssd_proc.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gssd_proc.obj `if test -f 'gssd_proc.c'; then $(CYGPATH_W) 'gssd_proc.c'; else $(CYGPATH_W) '$(srcdir)/gssd_proc.c'; fi`
+
+gssd-krb5_util.o: krb5_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-krb5_util.o -MD -MP -MF $(DEPDIR)/gssd-krb5_util.Tpo -c -o gssd-krb5_util.o `test -f 'krb5_util.c' || echo '$(srcdir)/'`krb5_util.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-krb5_util.Tpo $(DEPDIR)/gssd-krb5_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='krb5_util.c' object='gssd-krb5_util.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-krb5_util.o `test -f 'krb5_util.c' || echo '$(srcdir)/'`krb5_util.c
+
+gssd-krb5_util.obj: krb5_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-krb5_util.obj -MD -MP -MF $(DEPDIR)/gssd-krb5_util.Tpo -c -o gssd-krb5_util.obj `if test -f 'krb5_util.c'; then $(CYGPATH_W) 'krb5_util.c'; else $(CYGPATH_W) '$(srcdir)/krb5_util.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-krb5_util.Tpo $(DEPDIR)/gssd-krb5_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='krb5_util.c' object='gssd-krb5_util.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-krb5_util.obj `if test -f 'krb5_util.c'; then $(CYGPATH_W) 'krb5_util.c'; else $(CYGPATH_W) '$(srcdir)/krb5_util.c'; fi`
+
+svcgssd-context.o: context.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-context.o -MD -MP -MF $(DEPDIR)/svcgssd-context.Tpo -c -o svcgssd-context.o `test -f 'context.c' || echo '$(srcdir)/'`context.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-context.Tpo $(DEPDIR)/svcgssd-context.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context.c' object='svcgssd-context.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-context.o `test -f 'context.c' || echo '$(srcdir)/'`context.c
+
+svcgssd-context.obj: context.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-context.obj -MD -MP -MF $(DEPDIR)/svcgssd-context.Tpo -c -o svcgssd-context.obj `if test -f 'context.c'; then $(CYGPATH_W) 'context.c'; else $(CYGPATH_W) '$(srcdir)/context.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-context.Tpo $(DEPDIR)/svcgssd-context.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context.c' object='svcgssd-context.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-context.obj `if test -f 'context.c'; then $(CYGPATH_W) 'context.c'; else $(CYGPATH_W) '$(srcdir)/context.c'; fi`
+
+svcgssd-context_mit.o: context_mit.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-context_mit.o -MD -MP -MF $(DEPDIR)/svcgssd-context_mit.Tpo -c -o svcgssd-context_mit.o `test -f 'context_mit.c' || echo '$(srcdir)/'`context_mit.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-context_mit.Tpo $(DEPDIR)/svcgssd-context_mit.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_mit.c' object='svcgssd-context_mit.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-context_mit.o `test -f 'context_mit.c' || echo '$(srcdir)/'`context_mit.c
+
+svcgssd-context_mit.obj: context_mit.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-context_mit.obj -MD -MP -MF $(DEPDIR)/svcgssd-context_mit.Tpo -c -o svcgssd-context_mit.obj `if test -f 'context_mit.c'; then $(CYGPATH_W) 'context_mit.c'; else $(CYGPATH_W) '$(srcdir)/context_mit.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-context_mit.Tpo $(DEPDIR)/svcgssd-context_mit.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_mit.c' object='svcgssd-context_mit.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-context_mit.obj `if test -f 'context_mit.c'; then $(CYGPATH_W) 'context_mit.c'; else $(CYGPATH_W) '$(srcdir)/context_mit.c'; fi`
+
+svcgssd-context_heimdal.o: context_heimdal.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-context_heimdal.o -MD -MP -MF $(DEPDIR)/svcgssd-context_heimdal.Tpo -c -o svcgssd-context_heimdal.o `test -f 'context_heimdal.c' || echo '$(srcdir)/'`context_heimdal.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-context_heimdal.Tpo $(DEPDIR)/svcgssd-context_heimdal.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_heimdal.c' object='svcgssd-context_heimdal.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-context_heimdal.o `test -f 'context_heimdal.c' || echo '$(srcdir)/'`context_heimdal.c
+
+svcgssd-context_heimdal.obj: context_heimdal.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-context_heimdal.obj -MD -MP -MF $(DEPDIR)/svcgssd-context_heimdal.Tpo -c -o svcgssd-context_heimdal.obj `if test -f 'context_heimdal.c'; then $(CYGPATH_W) 'context_heimdal.c'; else $(CYGPATH_W) '$(srcdir)/context_heimdal.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-context_heimdal.Tpo $(DEPDIR)/svcgssd-context_heimdal.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_heimdal.c' object='svcgssd-context_heimdal.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-context_heimdal.obj `if test -f 'context_heimdal.c'; then $(CYGPATH_W) 'context_heimdal.c'; else $(CYGPATH_W) '$(srcdir)/context_heimdal.c'; fi`
+
+svcgssd-context_lucid.o: context_lucid.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-context_lucid.o -MD -MP -MF $(DEPDIR)/svcgssd-context_lucid.Tpo -c -o svcgssd-context_lucid.o `test -f 'context_lucid.c' || echo '$(srcdir)/'`context_lucid.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-context_lucid.Tpo $(DEPDIR)/svcgssd-context_lucid.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_lucid.c' object='svcgssd-context_lucid.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-context_lucid.o `test -f 'context_lucid.c' || echo '$(srcdir)/'`context_lucid.c
+
+svcgssd-context_lucid.obj: context_lucid.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-context_lucid.obj -MD -MP -MF $(DEPDIR)/svcgssd-context_lucid.Tpo -c -o svcgssd-context_lucid.obj `if test -f 'context_lucid.c'; then $(CYGPATH_W) 'context_lucid.c'; else $(CYGPATH_W) '$(srcdir)/context_lucid.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-context_lucid.Tpo $(DEPDIR)/svcgssd-context_lucid.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_lucid.c' object='svcgssd-context_lucid.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-context_lucid.obj `if test -f 'context_lucid.c'; then $(CYGPATH_W) 'context_lucid.c'; else $(CYGPATH_W) '$(srcdir)/context_lucid.c'; fi`
+
+svcgssd-gss_util.o: gss_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-gss_util.o -MD -MP -MF $(DEPDIR)/svcgssd-gss_util.Tpo -c -o svcgssd-gss_util.o `test -f 'gss_util.c' || echo '$(srcdir)/'`gss_util.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-gss_util.Tpo $(DEPDIR)/svcgssd-gss_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_util.c' object='svcgssd-gss_util.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-gss_util.o `test -f 'gss_util.c' || echo '$(srcdir)/'`gss_util.c
+
+svcgssd-gss_util.obj: gss_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-gss_util.obj -MD -MP -MF $(DEPDIR)/svcgssd-gss_util.Tpo -c -o svcgssd-gss_util.obj `if test -f 'gss_util.c'; then $(CYGPATH_W) 'gss_util.c'; else $(CYGPATH_W) '$(srcdir)/gss_util.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-gss_util.Tpo $(DEPDIR)/svcgssd-gss_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_util.c' object='svcgssd-gss_util.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-gss_util.obj `if test -f 'gss_util.c'; then $(CYGPATH_W) 'gss_util.c'; else $(CYGPATH_W) '$(srcdir)/gss_util.c'; fi`
+
+svcgssd-gss_oids.o: gss_oids.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-gss_oids.o -MD -MP -MF $(DEPDIR)/svcgssd-gss_oids.Tpo -c -o svcgssd-gss_oids.o `test -f 'gss_oids.c' || echo '$(srcdir)/'`gss_oids.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-gss_oids.Tpo $(DEPDIR)/svcgssd-gss_oids.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_oids.c' object='svcgssd-gss_oids.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-gss_oids.o `test -f 'gss_oids.c' || echo '$(srcdir)/'`gss_oids.c
+
+svcgssd-gss_oids.obj: gss_oids.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-gss_oids.obj -MD -MP -MF $(DEPDIR)/svcgssd-gss_oids.Tpo -c -o svcgssd-gss_oids.obj `if test -f 'gss_oids.c'; then $(CYGPATH_W) 'gss_oids.c'; else $(CYGPATH_W) '$(srcdir)/gss_oids.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-gss_oids.Tpo $(DEPDIR)/svcgssd-gss_oids.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_oids.c' object='svcgssd-gss_oids.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-gss_oids.obj `if test -f 'gss_oids.c'; then $(CYGPATH_W) 'gss_oids.c'; else $(CYGPATH_W) '$(srcdir)/gss_oids.c'; fi`
+
+svcgssd-gss_names.o: gss_names.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-gss_names.o -MD -MP -MF $(DEPDIR)/svcgssd-gss_names.Tpo -c -o svcgssd-gss_names.o `test -f 'gss_names.c' || echo '$(srcdir)/'`gss_names.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-gss_names.Tpo $(DEPDIR)/svcgssd-gss_names.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_names.c' object='svcgssd-gss_names.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-gss_names.o `test -f 'gss_names.c' || echo '$(srcdir)/'`gss_names.c
+
+svcgssd-gss_names.obj: gss_names.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-gss_names.obj -MD -MP -MF $(DEPDIR)/svcgssd-gss_names.Tpo -c -o svcgssd-gss_names.obj `if test -f 'gss_names.c'; then $(CYGPATH_W) 'gss_names.c'; else $(CYGPATH_W) '$(srcdir)/gss_names.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-gss_names.Tpo $(DEPDIR)/svcgssd-gss_names.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_names.c' object='svcgssd-gss_names.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-gss_names.obj `if test -f 'gss_names.c'; then $(CYGPATH_W) 'gss_names.c'; else $(CYGPATH_W) '$(srcdir)/gss_names.c'; fi`
+
+svcgssd-err_util.o: err_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-err_util.o -MD -MP -MF $(DEPDIR)/svcgssd-err_util.Tpo -c -o svcgssd-err_util.o `test -f 'err_util.c' || echo '$(srcdir)/'`err_util.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-err_util.Tpo $(DEPDIR)/svcgssd-err_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='err_util.c' object='svcgssd-err_util.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-err_util.o `test -f 'err_util.c' || echo '$(srcdir)/'`err_util.c
+
+svcgssd-err_util.obj: err_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-err_util.obj -MD -MP -MF $(DEPDIR)/svcgssd-err_util.Tpo -c -o svcgssd-err_util.obj `if test -f 'err_util.c'; then $(CYGPATH_W) 'err_util.c'; else $(CYGPATH_W) '$(srcdir)/err_util.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-err_util.Tpo $(DEPDIR)/svcgssd-err_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='err_util.c' object='svcgssd-err_util.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-err_util.obj `if test -f 'err_util.c'; then $(CYGPATH_W) 'err_util.c'; else $(CYGPATH_W) '$(srcdir)/err_util.c'; fi`
+
+svcgssd-svcgssd.o: svcgssd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-svcgssd.o -MD -MP -MF $(DEPDIR)/svcgssd-svcgssd.Tpo -c -o svcgssd-svcgssd.o `test -f 'svcgssd.c' || echo '$(srcdir)/'`svcgssd.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-svcgssd.Tpo $(DEPDIR)/svcgssd-svcgssd.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svcgssd.c' object='svcgssd-svcgssd.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-svcgssd.o `test -f 'svcgssd.c' || echo '$(srcdir)/'`svcgssd.c
+
+svcgssd-svcgssd.obj: svcgssd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-svcgssd.obj -MD -MP -MF $(DEPDIR)/svcgssd-svcgssd.Tpo -c -o svcgssd-svcgssd.obj `if test -f 'svcgssd.c'; then $(CYGPATH_W) 'svcgssd.c'; else $(CYGPATH_W) '$(srcdir)/svcgssd.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-svcgssd.Tpo $(DEPDIR)/svcgssd-svcgssd.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svcgssd.c' object='svcgssd-svcgssd.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-svcgssd.obj `if test -f 'svcgssd.c'; then $(CYGPATH_W) 'svcgssd.c'; else $(CYGPATH_W) '$(srcdir)/svcgssd.c'; fi`
+
+svcgssd-svcgssd_mech2file.o: svcgssd_mech2file.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-svcgssd_mech2file.o -MD -MP -MF $(DEPDIR)/svcgssd-svcgssd_mech2file.Tpo -c -o svcgssd-svcgssd_mech2file.o `test -f 'svcgssd_mech2file.c' || echo '$(srcdir)/'`svcgssd_mech2file.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-svcgssd_mech2file.Tpo $(DEPDIR)/svcgssd-svcgssd_mech2file.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svcgssd_mech2file.c' object='svcgssd-svcgssd_mech2file.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-svcgssd_mech2file.o `test -f 'svcgssd_mech2file.c' || echo '$(srcdir)/'`svcgssd_mech2file.c
+
+svcgssd-svcgssd_mech2file.obj: svcgssd_mech2file.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-svcgssd_mech2file.obj -MD -MP -MF $(DEPDIR)/svcgssd-svcgssd_mech2file.Tpo -c -o svcgssd-svcgssd_mech2file.obj `if test -f 'svcgssd_mech2file.c'; then $(CYGPATH_W) 'svcgssd_mech2file.c'; else $(CYGPATH_W) '$(srcdir)/svcgssd_mech2file.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-svcgssd_mech2file.Tpo $(DEPDIR)/svcgssd-svcgssd_mech2file.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svcgssd_mech2file.c' object='svcgssd-svcgssd_mech2file.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-svcgssd_mech2file.obj `if test -f 'svcgssd_mech2file.c'; then $(CYGPATH_W) 'svcgssd_mech2file.c'; else $(CYGPATH_W) '$(srcdir)/svcgssd_mech2file.c'; fi`
+
+svcgssd-svcgssd_proc.o: svcgssd_proc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-svcgssd_proc.o -MD -MP -MF $(DEPDIR)/svcgssd-svcgssd_proc.Tpo -c -o svcgssd-svcgssd_proc.o `test -f 'svcgssd_proc.c' || echo '$(srcdir)/'`svcgssd_proc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-svcgssd_proc.Tpo $(DEPDIR)/svcgssd-svcgssd_proc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svcgssd_proc.c' object='svcgssd-svcgssd_proc.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-svcgssd_proc.o `test -f 'svcgssd_proc.c' || echo '$(srcdir)/'`svcgssd_proc.c
+
+svcgssd-svcgssd_proc.obj: svcgssd_proc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-svcgssd_proc.obj -MD -MP -MF $(DEPDIR)/svcgssd-svcgssd_proc.Tpo -c -o svcgssd-svcgssd_proc.obj `if test -f 'svcgssd_proc.c'; then $(CYGPATH_W) 'svcgssd_proc.c'; else $(CYGPATH_W) '$(srcdir)/svcgssd_proc.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-svcgssd_proc.Tpo $(DEPDIR)/svcgssd-svcgssd_proc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svcgssd_proc.c' object='svcgssd-svcgssd_proc.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-svcgssd_proc.obj `if test -f 'svcgssd_proc.c'; then $(CYGPATH_W) 'svcgssd_proc.c'; else $(CYGPATH_W) '$(srcdir)/svcgssd_proc.c'; fi`
+
+svcgssd-svcgssd_krb5.o: svcgssd_krb5.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-svcgssd_krb5.o -MD -MP -MF $(DEPDIR)/svcgssd-svcgssd_krb5.Tpo -c -o svcgssd-svcgssd_krb5.o `test -f 'svcgssd_krb5.c' || echo '$(srcdir)/'`svcgssd_krb5.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-svcgssd_krb5.Tpo $(DEPDIR)/svcgssd-svcgssd_krb5.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svcgssd_krb5.c' object='svcgssd-svcgssd_krb5.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-svcgssd_krb5.o `test -f 'svcgssd_krb5.c' || echo '$(srcdir)/'`svcgssd_krb5.c
+
+svcgssd-svcgssd_krb5.obj: svcgssd_krb5.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-svcgssd_krb5.obj -MD -MP -MF $(DEPDIR)/svcgssd-svcgssd_krb5.Tpo -c -o svcgssd-svcgssd_krb5.obj `if test -f 'svcgssd_krb5.c'; then $(CYGPATH_W) 'svcgssd_krb5.c'; else $(CYGPATH_W) '$(srcdir)/svcgssd_krb5.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-svcgssd_krb5.Tpo $(DEPDIR)/svcgssd-svcgssd_krb5.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svcgssd_krb5.c' object='svcgssd-svcgssd_krb5.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-svcgssd_krb5.obj `if test -f 'svcgssd_krb5.c'; then $(CYGPATH_W) 'svcgssd_krb5.c'; else $(CYGPATH_W) '$(srcdir)/svcgssd_krb5.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man8_MANS)'; \
+ list2=''; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/gssd-context.Po
+ -rm -f ./$(DEPDIR)/gssd-context_heimdal.Po
+ -rm -f ./$(DEPDIR)/gssd-context_lucid.Po
+ -rm -f ./$(DEPDIR)/gssd-context_mit.Po
+ -rm -f ./$(DEPDIR)/gssd-err_util.Po
+ -rm -f ./$(DEPDIR)/gssd-gss_names.Po
+ -rm -f ./$(DEPDIR)/gssd-gss_oids.Po
+ -rm -f ./$(DEPDIR)/gssd-gss_util.Po
+ -rm -f ./$(DEPDIR)/gssd-gssd.Po
+ -rm -f ./$(DEPDIR)/gssd-gssd_proc.Po
+ -rm -f ./$(DEPDIR)/gssd-krb5_util.Po
+ -rm -f ./$(DEPDIR)/svcgssd-context.Po
+ -rm -f ./$(DEPDIR)/svcgssd-context_heimdal.Po
+ -rm -f ./$(DEPDIR)/svcgssd-context_lucid.Po
+ -rm -f ./$(DEPDIR)/svcgssd-context_mit.Po
+ -rm -f ./$(DEPDIR)/svcgssd-err_util.Po
+ -rm -f ./$(DEPDIR)/svcgssd-gss_names.Po
+ -rm -f ./$(DEPDIR)/svcgssd-gss_oids.Po
+ -rm -f ./$(DEPDIR)/svcgssd-gss_util.Po
+ -rm -f ./$(DEPDIR)/svcgssd-svcgssd.Po
+ -rm -f ./$(DEPDIR)/svcgssd-svcgssd_krb5.Po
+ -rm -f ./$(DEPDIR)/svcgssd-svcgssd_mech2file.Po
+ -rm -f ./$(DEPDIR)/svcgssd-svcgssd_proc.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/gssd-context.Po
+ -rm -f ./$(DEPDIR)/gssd-context_heimdal.Po
+ -rm -f ./$(DEPDIR)/gssd-context_lucid.Po
+ -rm -f ./$(DEPDIR)/gssd-context_mit.Po
+ -rm -f ./$(DEPDIR)/gssd-err_util.Po
+ -rm -f ./$(DEPDIR)/gssd-gss_names.Po
+ -rm -f ./$(DEPDIR)/gssd-gss_oids.Po
+ -rm -f ./$(DEPDIR)/gssd-gss_util.Po
+ -rm -f ./$(DEPDIR)/gssd-gssd.Po
+ -rm -f ./$(DEPDIR)/gssd-gssd_proc.Po
+ -rm -f ./$(DEPDIR)/gssd-krb5_util.Po
+ -rm -f ./$(DEPDIR)/svcgssd-context.Po
+ -rm -f ./$(DEPDIR)/svcgssd-context_heimdal.Po
+ -rm -f ./$(DEPDIR)/svcgssd-context_lucid.Po
+ -rm -f ./$(DEPDIR)/svcgssd-context_mit.Po
+ -rm -f ./$(DEPDIR)/svcgssd-err_util.Po
+ -rm -f ./$(DEPDIR)/svcgssd-gss_names.Po
+ -rm -f ./$(DEPDIR)/svcgssd-gss_oids.Po
+ -rm -f ./$(DEPDIR)/svcgssd-gss_util.Po
+ -rm -f ./$(DEPDIR)/svcgssd-svcgssd.Po
+ -rm -f ./$(DEPDIR)/svcgssd-svcgssd_krb5.Po
+ -rm -f ./$(DEPDIR)/svcgssd-svcgssd_mech2file.Po
+ -rm -f ./$(DEPDIR)/svcgssd-svcgssd_proc.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-man uninstall-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) uninstall-hook
+.MAKE: install-am install-exec-am install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS cscopelist-am \
+ ctags ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-exec-hook install-html install-html-am \
+ install-info install-info-am install-man install-man8 \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-sbinPROGRAMS install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-hook \
+ uninstall-man uninstall-man8 uninstall-sbinPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+#######################################################################
+# The following allows the current practice of having
+# daemons renamed during the install to include RPCPREFIX
+# and the KPREFIX
+# This could all be done much easier with program_transform_name
+# ( program_transform_name = s/^/$(RPCPREFIX)$(KPREFIX)/ )
+# but that also renames the man pages, which the current
+# practice does not do.
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PREFIXED); do \
+ mv -f $$p$(EXEEXT) $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PREFIXED); do \
+ rm -f $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+
+# XXX This makes some assumptions about what automake does.
+# XXX But there is no install-man-hook or install-man-local.
+install-man: install-man8 install-man-links
+uninstall-man: uninstall-man8 uninstall-man-links
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ $(LN_S) $$inst $(RPCPREFIX)$$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ done)
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/utils/gssd/context.c b/utils/gssd/context.c
new file mode 100644
index 0000000..7757a77
--- /dev/null
+++ b/utils/gssd/context.c
@@ -0,0 +1,59 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <gssapi/gssapi.h>
+#include <rpc/rpc.h>
+#include <rpc/auth_gss.h>
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "err_util.h"
+#include "context.h"
+
+int
+serialize_context_for_kernel(gss_ctx_id_t *ctx,
+ gss_buffer_desc *buf,
+ gss_OID mech,
+ int32_t *endtime)
+{
+ if (g_OID_equal(&krb5oid, mech))
+ return serialize_krb5_ctx(ctx, buf, endtime);
+ else {
+ printerr(0, "ERROR: attempting to serialize context with "
+ "unknown/unsupported mechanism oid\n");
+ return -1;
+ }
+}
diff --git a/utils/gssd/context.h b/utils/gssd/context.h
new file mode 100644
index 0000000..3b55c8e
--- /dev/null
+++ b/utils/gssd/context.h
@@ -0,0 +1,49 @@
+/*
+ Copyright (c) 2004,2008 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _CONTEXT_H_
+#define _CONTEXT_H_
+
+#include <rpc/rpc.h>
+
+/* Hopefully big enough to hold any serialized context */
+#define MAX_CTX_LEN 4096
+
+/* New context format flag values */
+#define KRB5_CTX_FLAG_INITIATOR 0x00000001
+#define KRB5_CTX_FLAG_CFX 0x00000002
+#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004
+
+int serialize_context_for_kernel(gss_ctx_id_t *ctx, gss_buffer_desc *buf,
+ gss_OID mech, int32_t *endtime);
+int serialize_krb5_ctx(gss_ctx_id_t *ctx, gss_buffer_desc *buf,
+ int32_t *endtime);
+
+#endif /* _CONTEXT_H_ */
diff --git a/utils/gssd/context_heimdal.c b/utils/gssd/context_heimdal.c
new file mode 100644
index 0000000..d07103b
--- /dev/null
+++ b/utils/gssd/context_heimdal.c
@@ -0,0 +1,275 @@
+/*
+ Copyright (c) 2004-2006 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifndef HAVE_LUCID_CONTEXT_SUPPORT
+#ifdef HAVE_HEIMDAL
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include <krb5.h>
+#include <gssapi.h> /* Must use the heimdal copy! */
+#ifdef HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+#include "err_util.h"
+#include "gss_oids.h"
+#include "write_bytes.h"
+
+int write_heimdal_keyblock(char **p, char *end, krb5_keyblock *key)
+{
+ gss_buffer_desc tmp;
+ int code = -1;
+
+ if (WRITE_BYTES(p, end, key->keytype)) goto out_err;
+ tmp.length = key->keyvalue.length;
+ tmp.value = key->keyvalue.data;
+ if (write_buffer(p, end, &tmp)) goto out_err;
+ code = 0;
+ out_err:
+ return(code);
+}
+
+int write_heimdal_enc_key(char **p, char *end, gss_ctx_id_t ctx)
+{
+ krb5_keyblock enc_key, *key;
+ krb5_context context;
+ krb5_error_code ret;
+ int i;
+ char *skd, *dkd, *k5err = NULL;
+ int code = -1;
+
+ if ((ret = krb5_init_context(&context))) {
+ k5err = gssd_k5_err_msg(NULL, ret);
+ printerr(0, "ERROR: initializing krb5_context: %s\n", k5err);
+ goto out_err;
+ }
+
+ if ((ret = krb5_auth_con_getlocalsubkey(context,
+ ctx->auth_context, &key))){
+ k5err = gssd_k5_err_msg(context, ret);
+ printerr(0, "ERROR: getting auth_context key: %s\n", k5err);
+ goto out_err_free_context;
+ }
+
+ memset(&enc_key, 0, sizeof(enc_key));
+ enc_key.keytype = key->keytype;
+ /* XXX current kernel code only handles des-cbc-raw (4) */
+ if (enc_key.keytype != 4) {
+ printerr(1, "WARN: write_heimdal_enc_key: "
+ "overriding heimdal keytype (%d => %d)\n",
+ enc_key.keytype, 4);
+ enc_key.keytype = 4;
+ }
+ enc_key.keyvalue.length = key->keyvalue.length;
+ if ((enc_key.keyvalue.data =
+ calloc(1, enc_key.keyvalue.length)) == NULL) {
+ k5err = gssd_k5_err_msg(context, ENOMEM);
+ printerr(0, "ERROR: allocating memory for enc key: %s\n",
+ k5err);
+ goto out_err_free_key;
+ }
+ skd = (char *) key->keyvalue.data;
+ dkd = (char *) enc_key.keyvalue.data;
+ for (i = 0; i < enc_key.keyvalue.length; i++)
+ dkd[i] = skd[i] ^ 0xf0;
+ if (write_heimdal_keyblock(p, end, &enc_key)) {
+ goto out_err_free_enckey;
+ }
+
+ code = 0;
+
+ out_err_free_enckey:
+ krb5_free_keyblock_contents(context, &enc_key);
+ out_err_free_key:
+ krb5_free_keyblock(context, key);
+ out_err_free_context:
+ krb5_free_context(context);
+ out_err:
+ free(k5err);
+ printerr(2, "write_heimdal_enc_key: %s\n", code ? "FAILED" : "SUCCESS");
+ return(code);
+}
+
+int write_heimdal_seq_key(char **p, char *end, gss_ctx_id_t ctx)
+{
+ krb5_keyblock *key;
+ krb5_context context;
+ krb5_error_code ret;
+ char *k5err = NULL;
+ int code = -1;
+
+ if ((ret = krb5_init_context(&context))) {
+ k5err = gssd_k5_err_msg(NULL, ret);
+ printerr(0, "ERROR: initializing krb5_context: %s\n", k5err);
+ goto out_err;
+ }
+
+ if ((ret = krb5_auth_con_getlocalsubkey(context,
+ ctx->auth_context, &key))){
+ k5err = gssd_k5_err_msg(context, ret);
+ printerr(0, "ERROR: getting auth_context key: %s\n", k5err);
+ goto out_err_free_context;
+ }
+
+ /* XXX current kernel code only handles des-cbc-raw (4) */
+ if (key->keytype != 4) {
+ printerr(1, "WARN: write_heimdal_seq_key: "
+ "overriding heimdal keytype (%d => %d)\n",
+ key->keytype, 4);
+ key->keytype = 4;
+ }
+
+ if (write_heimdal_keyblock(p, end, key)) {
+ goto out_err_free_key;
+ }
+
+ code = 0;
+
+ out_err_free_key:
+ krb5_free_keyblock(context, key);
+ out_err_free_context:
+ krb5_free_context(context);
+ out_err:
+ free(k5err);
+ printerr(2, "write_heimdal_seq_key: %s\n", code ? "FAILED" : "SUCCESS");
+ return(code);
+}
+
+/*
+ * The following is the kernel structure that we are filling in:
+ *
+ * struct krb5_ctx {
+ * int initiate;
+ * int seed_init;
+ * unsigned char seed[16];
+ * int signalg;
+ * int sealalg;
+ * struct crypto_tfm *enc;
+ * struct crypto_tfm *seq;
+ * s32 endtime;
+ * u32 seq_send;
+ * struct xdr_netobj mech_used;
+ * };
+ *
+ * However, note that we do not send the data fields in the
+ * order they appear in the structure. The order they are
+ * sent down in is:
+ *
+ * initiate
+ * seed_init
+ * seed
+ * signalg
+ * sealalg
+ * endtime
+ * seq_send
+ * mech_used
+ * enc key
+ * seq key
+ *
+ */
+
+int
+serialize_krb5_ctx(gss_ctx_id_t *_ctx, gss_buffer_desc *buf, int32_t *endtime)
+{
+ gss_ctx_id_t ctx = *_ctx;
+ char *p, *end;
+ static int constant_one = 1;
+ static int constant_zero = 0;
+ unsigned char fakeseed[16];
+ uint32_t algorithm;
+
+ if (!(buf->value = calloc(1, MAX_CTX_LEN)))
+ goto out_err;
+ p = buf->value;
+ end = buf->value + MAX_CTX_LEN;
+
+
+ /* initiate: 1 => initiating 0 => accepting */
+ if (ctx->more_flags & LOCAL) {
+ if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
+ }
+ else {
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+ }
+
+ /* seed_init: not used by kernel code */
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+
+ /* seed: not used by kernel code */
+ memset(&fakeseed, 0, sizeof(fakeseed));
+ if (write_bytes(&p, end, &fakeseed, 16)) goto out_err;
+
+ /* signalg */
+ algorithm = 0; /* SGN_ALG_DES_MAC_MD5 XXX */
+ if (WRITE_BYTES(&p, end, algorithm)) goto out_err;
+
+ /* sealalg */
+ algorithm = 0; /* SEAL_ALG_DES XXX */
+ if (WRITE_BYTES(&p, end, algorithm)) goto out_err;
+
+ /* endtime */
+ if (WRITE_BYTES(&p, end, ctx->lifetime)) goto out_err;
+
+ if (endtime)
+ *endtime = ctx->lifetime;
+
+ /* seq_send */
+ if (WRITE_BYTES(&p, end, ctx->auth_context->local_seqnumber))
+ goto out_err;
+ /* mech_used */
+ if (write_buffer(&p, end, (gss_buffer_desc*)&krb5oid)) goto out_err;
+
+ /* enc: derive the encryption key and copy it into buffer */
+ if (write_heimdal_enc_key(&p, end, ctx)) goto out_err;
+
+ /* seq: get the sequence number key and copy it into buffer */
+ if (write_heimdal_seq_key(&p, end, ctx)) goto out_err;
+
+ buf->length = p - (char *)buf->value;
+ printerr(4, "serialize_krb5_ctx: returning buffer "
+ "with %d bytes\n", buf->length);
+
+ return 0;
+out_err:
+ printerr(0, "ERROR: failed exporting Heimdal krb5 ctx to kernel\n");
+ if (buf->value) free(buf->value);
+ buf->length = 0;
+ return -1;
+}
+
+#endif /* HAVE_HEIMDAL */
+#endif /* HAVE_LUCID_CONTEXT_SUPPORT */
diff --git a/utils/gssd/context_lucid.c b/utils/gssd/context_lucid.c
new file mode 100644
index 0000000..5d77c21
--- /dev/null
+++ b/utils/gssd/context_lucid.c
@@ -0,0 +1,327 @@
+/*
+ * COPYRIGHT (c) 2006
+ * The Regents of the University of Michigan
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifdef HAVE_LUCID_CONTEXT_SUPPORT
+
+/*
+ * Newer versions of MIT and Heimdal have lucid context support.
+ * We can use common code if it is supported.
+ */
+
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+
+#include <gssapi/gssapi_krb5.h>
+
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "err_util.h"
+#include "context.h"
+
+#ifndef OM_uint64
+typedef uint64_t OM_uint64;
+#endif
+
+static int
+write_lucid_keyblock(char **p, char *end, gss_krb5_lucid_key_t *key)
+{
+ gss_buffer_desc tmp;
+
+ if (WRITE_BYTES(p, end, key->type)) return -1;
+ tmp.length = key->length;
+ tmp.value = key->data;
+ if (write_buffer(p, end, &tmp)) return -1;
+ return 0;
+}
+
+static int
+prepare_krb5_rfc1964_buffer(gss_krb5_lucid_context_v1_t *lctx,
+ gss_buffer_desc *buf, int32_t *endtime)
+{
+#define FAKESEED_SIZE 16
+ char *p, *end;
+ static int constant_zero = 0;
+ unsigned char fakeseed[FAKESEED_SIZE];
+ uint32_t word_send_seq;
+ gss_krb5_lucid_key_t enc_key;
+ uint32_t i;
+ char *skd, *dkd;
+ gss_buffer_desc fakeoid;
+ int err;
+
+ /*
+ * The new Kerberos interface to get the gss context
+ * does not include the seed or seed_init fields
+ * because we never really use them. But for now,
+ * send down a fake buffer so we can use the same
+ * interface to the kernel.
+ */
+ memset(&enc_key, 0, sizeof(enc_key));
+ memset(&fakeoid, 0, sizeof(fakeoid));
+ memset(fakeseed, 0, FAKESEED_SIZE);
+
+ if (!(buf->value = calloc(1, MAX_CTX_LEN)))
+ goto out_err;
+ p = buf->value;
+ end = buf->value + MAX_CTX_LEN;
+
+ if (WRITE_BYTES(&p, end, lctx->initiate)) goto out_err;
+
+ /* seed_init and seed not used by kernel anyway */
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+ if (write_bytes(&p, end, &fakeseed, FAKESEED_SIZE)) goto out_err;
+
+ if (WRITE_BYTES(&p, end, lctx->rfc1964_kd.sign_alg)) goto out_err;
+ if (WRITE_BYTES(&p, end, lctx->rfc1964_kd.seal_alg)) goto out_err;
+ if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err;
+ if (endtime)
+ *endtime = lctx->endtime;
+ word_send_seq = lctx->send_seq; /* XXX send_seq is 64-bit */
+ if (WRITE_BYTES(&p, end, word_send_seq)) goto out_err;
+ if (write_oid(&p, end, &krb5oid)) goto out_err;
+
+#ifdef HAVE_HEIMDAL
+ /*
+ * The kernel gss code expects des-cbc-raw for all flavors of des.
+ * The keytype from MIT has this type, but Heimdal does not.
+ * Force the Heimdal keytype to 4 (des-cbc-raw).
+ * Note that the rfc1964 version only supports DES enctypes.
+ */
+ if (lctx->rfc1964_kd.ctx_key.type != 4) {
+ printerr(2, "%s: overriding heimdal keytype (%d => %d)\n",
+ __FUNCTION__, lctx->rfc1964_kd.ctx_key.type, 4);
+ lctx->rfc1964_kd.ctx_key.type = 4;
+ }
+#endif
+ printerr(2, "%s: serializing keys with enctype %d and length %d\n",
+ __FUNCTION__, lctx->rfc1964_kd.ctx_key.type,
+ lctx->rfc1964_kd.ctx_key.length);
+
+ /* derive the encryption key and copy it into buffer */
+ enc_key.type = lctx->rfc1964_kd.ctx_key.type;
+ enc_key.length = lctx->rfc1964_kd.ctx_key.length;
+ if ((enc_key.data = calloc(1, enc_key.length)) == NULL)
+ goto out_err;
+ skd = (char *) lctx->rfc1964_kd.ctx_key.data;
+ dkd = (char *) enc_key.data;
+ for (i = 0; i < enc_key.length; i++)
+ dkd[i] = skd[i] ^ 0xf0;
+ err = write_lucid_keyblock(&p, end, &enc_key);
+ free(enc_key.data);
+ if (err)
+ goto out_err;
+
+ if (write_lucid_keyblock(&p, end, &lctx->rfc1964_kd.ctx_key))
+ goto out_err;
+
+ buf->length = p - (char *)buf->value;
+ return 0;
+out_err:
+ printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
+ if (buf->value) free(buf->value);
+ buf->length = 0;
+ return -1;
+}
+
+/* Flags for version 2 context flags */
+#define KRB5_CTX_FLAG_INITIATOR 0x00000001
+#define KRB5_CTX_FLAG_CFX 0x00000002
+#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004
+
+/*
+ * Prepare a new-style buffer, as defined in rfc4121 (a.k.a. cfx),
+ * to send to the kernel for newer encryption types -- or for DES3.
+ *
+ * The new format is:
+ *
+ * u32 flags;
+ * #define KRB5_CTX_FLAG_INITIATOR 0x00000001
+ * #define KRB5_CTX_FLAG_CFX 0x00000002
+ * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004
+ * s32 endtime;
+ * u64 seq_send;
+ * u32 enctype; ( encrption type of key )
+ * raw key; ( raw key bytes (kernel will derive))
+ *
+ */
+static int
+prepare_krb5_rfc4121_buffer(gss_krb5_lucid_context_v1_t *lctx,
+ gss_buffer_desc *buf, int32_t *endtime)
+{
+ char *p, *end;
+ uint32_t v2_flags = 0;
+ uint32_t enctype;
+ uint32_t keysize;
+
+ if (!(buf->value = calloc(1, MAX_CTX_LEN)))
+ goto out_err;
+ p = buf->value;
+ end = buf->value + MAX_CTX_LEN;
+
+ /* Version 2 */
+ if (lctx->initiate)
+ v2_flags |= KRB5_CTX_FLAG_INITIATOR;
+ if (lctx->protocol != 0)
+ v2_flags |= KRB5_CTX_FLAG_CFX;
+ if (lctx->protocol != 0 && lctx->cfx_kd.have_acceptor_subkey == 1)
+ v2_flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY;
+
+ if (WRITE_BYTES(&p, end, v2_flags)) goto out_err;
+ if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err;
+ if (endtime)
+ *endtime = lctx->endtime;
+ if (WRITE_BYTES(&p, end, lctx->send_seq)) goto out_err;
+
+ /* Protocol 0 here implies DES3 or RC4 */
+ printerr(4, "%s: protocol %d\n", __FUNCTION__, lctx->protocol);
+ if (lctx->protocol == 0) {
+ enctype = lctx->rfc1964_kd.ctx_key.type;
+ keysize = lctx->rfc1964_kd.ctx_key.length;
+ } else {
+ if (lctx->cfx_kd.have_acceptor_subkey) {
+ enctype = lctx->cfx_kd.acceptor_subkey.type;
+ keysize = lctx->cfx_kd.acceptor_subkey.length;
+ } else {
+ enctype = lctx->cfx_kd.ctx_key.type;
+ keysize = lctx->cfx_kd.ctx_key.length;
+ }
+ }
+ printerr(4, "%s: serializing key with enctype %d and size %d\n",
+ __FUNCTION__, enctype, keysize);
+
+ if (WRITE_BYTES(&p, end, enctype)) goto out_err;
+
+ if (lctx->protocol == 0) {
+ if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data,
+ lctx->rfc1964_kd.ctx_key.length))
+ goto out_err;
+ } else {
+ if (lctx->cfx_kd.have_acceptor_subkey) {
+ if (write_bytes(&p, end,
+ lctx->cfx_kd.acceptor_subkey.data,
+ lctx->cfx_kd.acceptor_subkey.length))
+ goto out_err;
+ } else {
+ if (write_bytes(&p, end, lctx->cfx_kd.ctx_key.data,
+ lctx->cfx_kd.ctx_key.length))
+ goto out_err;
+ }
+ }
+
+ buf->length = p - (char *)buf->value;
+ return 0;
+
+out_err:
+ printerr(0, "ERROR: %s: failed serializing krb5 context for kernel\n",
+ __FUNCTION__);
+ if (buf->value) {
+ free(buf->value);
+ buf->value = NULL;
+ }
+ buf->length = 0;
+ return -1;
+}
+
+
+int
+serialize_krb5_ctx(gss_ctx_id_t *ctx, gss_buffer_desc *buf, int32_t *endtime)
+{
+ OM_uint32 maj_stat, min_stat;
+ void *return_ctx = 0;
+ OM_uint32 vers;
+ gss_krb5_lucid_context_v1_t *lctx = 0;
+ int retcode = 0;
+
+ printerr(4, "DEBUG: %s: lucid version!\n", __FUNCTION__);
+ maj_stat = gss_export_lucid_sec_context(&min_stat, ctx,
+ 1, &return_ctx);
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_export_lucid_sec_context",
+ maj_stat, min_stat, &krb5oid);
+ goto out_err;
+ }
+
+ /* Check the version returned, we only support v1 right now */
+ vers = ((gss_krb5_lucid_context_version_t *)return_ctx)->version;
+ switch (vers) {
+ case 1:
+ lctx = (gss_krb5_lucid_context_v1_t *) return_ctx;
+ break;
+ default:
+ printerr(0, "ERROR: unsupported lucid sec context version %d\n",
+ vers);
+ goto out_err;
+ break;
+ }
+
+ /*
+ * Now lctx points to a lucid context that we can send down to kernel
+ *
+ * Note: we send down different information to the kernel depending
+ * on the protocol version and the enctyption type.
+ * For protocol version 0 with all enctypes besides DES3, we use
+ * the original format. For protocol version != 0 or DES3, we
+ * send down the new style information.
+ */
+
+ if (lctx->protocol == 0 && lctx->rfc1964_kd.ctx_key.type <= 4)
+ retcode = prepare_krb5_rfc1964_buffer(lctx, buf, endtime);
+ else
+ retcode = prepare_krb5_rfc4121_buffer(lctx, buf, endtime);
+
+ maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, return_ctx);
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_free_lucid_sec_context",
+ maj_stat, min_stat, &krb5oid);
+ printerr(0, "WARN: failed to free lucid sec context\n");
+ }
+
+ if (retcode) {
+ printerr(1, "%s: prepare_krb5_*_buffer failed (retcode = %d)\n",
+ __FUNCTION__, retcode);
+ goto out_err;
+ }
+
+ return 0;
+
+out_err:
+ printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
+ return -1;
+}
+
+
+
+#endif /* HAVE_LUCID_CONTEXT_SUPPORT */
diff --git a/utils/gssd/context_mit.c b/utils/gssd/context_mit.c
new file mode 100644
index 0000000..fad6756
--- /dev/null
+++ b/utils/gssd/context_mit.c
@@ -0,0 +1,280 @@
+/*
+ Copyright (c) 2004-2006 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifndef HAVE_LUCID_CONTEXT_SUPPORT
+#ifdef HAVE_KRB5
+
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include <gssapi/gssapi.h>
+#include <rpc/rpc.h>
+#include <rpc/auth_gss.h>
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "err_util.h"
+#include "context.h"
+
+#include <krb5.h>
+
+#if (KRB5_VERSION > 131)
+/* XXX argggg, there's gotta be a better way than just duplicating this
+ * whole struct. Unfortunately, this is in a "private" header file,
+ * so this is our best choice at this point :-/
+ */
+
+typedef struct _krb5_gss_ctx_id_rec {
+ unsigned int initiate : 1; /* nonzero if initiating, zero if accepting */
+ unsigned int established : 1;
+ unsigned int big_endian : 1;
+ unsigned int have_acceptor_subkey : 1;
+ unsigned int seed_init : 1; /* XXX tested but never actually set */
+#ifdef CFX_EXERCISE
+ unsigned int testing_unknown_tokid : 1; /* for testing only */
+#endif
+ OM_uint32 gss_flags;
+ unsigned char seed[16];
+ krb5_principal here;
+ krb5_principal there;
+ krb5_keyblock *subkey;
+ int signalg;
+ size_t cksum_size;
+ int sealalg;
+ krb5_keyblock *enc;
+ krb5_keyblock *seq;
+ krb5_timestamp endtime;
+ 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; /* gssint_uint64 */
+ uint64_t seq_recv; /* gssint_uint64 */
+ void *seqstate;
+ krb5_auth_context auth_context;
+ gss_OID_desc *mech_used; /* gss_OID_desc */
+ /* Protocol spec revision
+ 0 => RFC 1964 with 3DES and RC4 enhancements
+ 1 => draft-ietf-krb-wg-gssapi-cfx-01
+ No others defined so far. */
+ int proto;
+ krb5_cksumtype cksumtype; /* for "main" subkey */
+ krb5_keyblock *acceptor_subkey; /* CFX only */
+ krb5_cksumtype acceptor_subkey_cksumtype;
+#ifdef CFX_EXERCISE
+ gss_buffer_desc init_token;
+#endif
+} krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t;
+
+#else /* KRB5_VERSION > 131 */
+
+typedef struct _krb5_gss_ctx_id_rec {
+ int initiate;
+ u_int32_t gss_flags;
+ int seed_init;
+ unsigned char seed[16];
+ krb5_principal here;
+ krb5_principal there;
+ krb5_keyblock *subkey;
+ int signalg;
+ int cksum_size;
+ int sealalg;
+ krb5_keyblock *enc;
+ krb5_keyblock *seq;
+ krb5_timestamp endtime;
+ krb5_flags krb_flags;
+ krb5_ui_4 seq_send;
+ krb5_ui_4 seq_recv;
+ void *seqstate;
+ int established;
+ int big_endian;
+ krb5_auth_context auth_context;
+ gss_OID_desc *mech_used;
+ int nctypes;
+ krb5_cksumtype *ctypes;
+} krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t;
+
+#endif /* KRB5_VERSION */
+
+
+static int
+write_keyblock(char **p, char *end, struct _krb5_keyblock *arg)
+{
+ gss_buffer_desc tmp;
+
+ if (WRITE_BYTES(p, end, arg->enctype)) return -1;
+ tmp.length = arg->length;
+ tmp.value = arg->contents;
+ if (write_buffer(p, end, &tmp)) return -1;
+ return 0;
+}
+
+/*
+ * We really shouldn't know about glue-layer context structure, but
+ * we need to get at the real krb5 context pointer. This should be
+ * removed as soon as we say there is no support for MIT Kerberos
+ * prior to 1.4 -- which gives us "legal" access to the context info.
+ */
+typedef struct gss_union_ctx_id_t {
+ gss_OID mech_type;
+ gss_ctx_id_t internal_ctx_id;
+} gss_union_ctx_id_desc, *gss_union_ctx_id_t;
+
+int
+serialize_krb5_ctx(gss_ctx_id_t *ctx, gss_buffer_desc *buf, int32_t *endtime)
+{
+ krb5_gss_ctx_id_t kctx = ((gss_union_ctx_id_t)(*ctx))->internal_ctx_id;
+ char *p, *end;
+ static int constant_zero = 0;
+ static int constant_one = 1;
+ static int constant_two = 2;
+ uint32_t word_seq_send;
+ u_int64_t seq_send_64bit;
+ uint32_t v2_flags = 0;
+
+ if (!(buf->value = calloc(1, MAX_CTX_LEN)))
+ goto out_err;
+ p = buf->value;
+ end = buf->value + MAX_CTX_LEN;
+
+ switch (kctx->enc->enctype) {
+ case ENCTYPE_DES_CBC_CRC:
+ case ENCTYPE_DES_CBC_MD4:
+ case ENCTYPE_DES_CBC_MD5:
+ case ENCTYPE_DES_CBC_RAW:
+ /* Old format of context to the kernel */
+ if (kctx->initiate) {
+ if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
+ }
+ else {
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+ }
+ if (kctx->seed_init) {
+ if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
+ }
+ else {
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+ }
+ if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed)))
+ goto out_err;
+ if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err;
+ if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err;
+ if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err;
+ if (endtime)
+ *endtime = kctx->endtime;
+ word_seq_send = kctx->seq_send;
+ if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err;
+ if (write_oid(&p, end, kctx->mech_used)) goto out_err;
+
+ printerr(2, "serialize_krb5_ctx: serializing keys with "
+ "enctype %d and length %d\n",
+ kctx->enc->enctype, kctx->enc->length);
+
+ if (write_keyblock(&p, end, kctx->enc)) goto out_err;
+ if (write_keyblock(&p, end, kctx->seq)) goto out_err;
+ break;
+ case ENCTYPE_DES3_CBC_RAW:
+ case ENCTYPE_DES3_CBC_SHA1:
+ case ENCTYPE_ARCFOUR_HMAC:
+ case ENCTYPE_ARCFOUR_HMAC_EXP:
+ case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
+ case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
+ /* New format of context to the kernel */
+ /* u32 flags;
+ * #define KRB5_CTX_FLAG_INITIATOR 0x00000001
+ * #define KRB5_CTX_FLAG_CFX 0x00000002
+ * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004
+ * s32 endtime;
+ * u64 seq_send;
+ * u32 enctype;
+ * rawkey data
+ */
+
+ if (kctx->initiate)
+ v2_flags |= KRB5_CTX_FLAG_INITIATOR;
+ if (kctx->proto == 1)
+ v2_flags |= KRB5_CTX_FLAG_CFX;
+ if (kctx->have_acceptor_subkey)
+ v2_flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY;
+ if (WRITE_BYTES(&p, end, v2_flags)) goto out_err;
+ if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err;
+
+ seq_send_64bit = kctx->seq_send;
+ if (WRITE_BYTES(&p, end, seq_send_64bit)) goto out_err;
+
+ if (kctx->have_acceptor_subkey) {
+ if (WRITE_BYTES(&p, end, kctx->acceptor_subkey->enctype))
+ goto out_err;
+ printerr(2, "serialize_krb5_ctx: serializing subkey "
+ "with enctype %d and size %d\n",
+ kctx->acceptor_subkey->enctype,
+ kctx->acceptor_subkey->length);
+
+ if (write_bytes(&p, end,
+ kctx->acceptor_subkey->contents,
+ kctx->acceptor_subkey->length))
+ goto out_err;
+ } else {
+ if (WRITE_BYTES(&p, end, kctx->enc->enctype))
+ goto out_err;
+ printerr(2, "serialize_krb5_ctx: serializing key "
+ "with enctype %d and size %d\n",
+ kctx->enc->enctype, kctx->enc->length);
+
+ if (write_bytes(&p, end, kctx->enc->contents,
+ kctx->enc->length))
+ goto out_err;
+ }
+ break;
+ default:
+ printerr(0, "ERROR: serialize_krb5_ctx: unsupported encryption "
+ "algorithm %d\n", kctx->enc->enctype);
+ goto out_err;
+ }
+
+ buf->length = p - (char *)buf->value;
+ return 0;
+
+out_err:
+ printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
+ if (buf->value) {
+ free(buf->value);
+ }
+ buf->value = NULL;
+ buf->length = 0;
+ return -1;
+}
+
+#endif /* HAVE_KRB5 */
+#endif /* HAVE_LUCID_CONTEXT_SUPPORT */
diff --git a/utils/gssd/err_util.c b/utils/gssd/err_util.c
new file mode 100644
index 0000000..27abd23
--- /dev/null
+++ b/utils/gssd/err_util.c
@@ -0,0 +1,86 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include "xlog.h"
+#include "err_util.h"
+
+static int verbosity = 0;
+static int fg = 0;
+
+void initerr(char *progname, int set_verbosity, int set_fg)
+{
+ verbosity = set_verbosity;
+ fg = set_fg;
+ if (!fg)
+ xlog_open(progname);
+}
+
+
+void printerr(int priority, char *format, ...)
+{
+ va_list args;
+
+ /* Don't bother formatting a message we're never going to print! */
+ if (priority > verbosity)
+ return;
+
+ va_start(args, format);
+ if (fg)
+ vfprintf(stderr, format, args);
+ else
+ xlog_backend(L_ERROR, format, args);
+ va_end(args);
+}
+
+int get_verbosity(void)
+{
+ return verbosity;
+}
+
+char *
+sec2time(int value)
+{
+ static char buf[BUFSIZ];
+ int hr, min, sec;
+
+ hr = (value / 3600);
+ min = (value - (3600*hr))/60;
+ sec = (value - (3600*hr) - (min*60));
+ sprintf(buf, "%dh:%dm:%ds", hr, min, sec);
+ return(buf);
+}
+
diff --git a/utils/gssd/err_util.h b/utils/gssd/err_util.h
new file mode 100644
index 0000000..6fa9d3d
--- /dev/null
+++ b/utils/gssd/err_util.h
@@ -0,0 +1,39 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _ERR_UTIL_H_
+#define _ERR_UTIL_H_
+
+void initerr(char *progname, int verbosity, int fg);
+void printerr(int priority, char *format, ...);
+int get_verbosity(void);
+char * sec2time(int);
+
+#endif /* _ERR_UTIL_H_ */
diff --git a/utils/gssd/gss_names.c b/utils/gssd/gss_names.c
new file mode 100644
index 0000000..982b96f
--- /dev/null
+++ b/utils/gssd/gss_names.c
@@ -0,0 +1,141 @@
+/*
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+
+#include <pwd.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <nfsidmap.h>
+#include <nfslib.h>
+#include <time.h>
+
+#include "svcgssd.h"
+#include "gss_util.h"
+#include "gss_names.h"
+#include "err_util.h"
+#include "context.h"
+#include "misc.h"
+#include "gss_oids.h"
+#include "svcgssd_krb5.h"
+
+static int
+get_krb5_hostbased_name(gss_buffer_desc *name, char **hostbased_name)
+{
+ char *p, *sname = NULL;
+ if (strchr(name->value, '@') && strchr(name->value, '/')) {
+ if ((sname = calloc(name->length, 1)) == NULL) {
+ printerr(0, "ERROR: get_krb5_hostbased_name failed "
+ "to allocate %d bytes\n", name->length);
+ return -1;
+ }
+ /* read in name and instance and replace '/' with '@' */
+ sscanf(name->value, "%[^@]", sname);
+ p = strrchr(sname, '/');
+ if (p == NULL) { /* The '@' preceeded the '/' */
+ free(sname);
+ return -1;
+ }
+ *p = '@';
+ }
+ *hostbased_name = sname;
+ return 0;
+}
+
+int
+get_hostbased_client_name(gss_name_t client_name, gss_OID mech,
+ char **hostbased_name)
+{
+ u_int32_t maj_stat, min_stat;
+ gss_buffer_desc name;
+ gss_OID name_type = GSS_C_NO_OID;
+ char *cname;
+ int res = -1;
+
+ *hostbased_name = NULL; /* preset in case we fail */
+
+ /* Get the client's gss authenticated name */
+ maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("get_hostbased_client_name: gss_display_name",
+ maj_stat, min_stat, mech);
+ goto out_err;
+ }
+ if (name.length >= 0xffff) { /* don't overflow */
+ printerr(0, "ERROR: get_hostbased_client_name: "
+ "received gss_name is too long (%d bytes)\n",
+ name.length);
+ goto out_rel_buf;
+ }
+
+ /* For Kerberos, transform the NT_KRB5_PRINCIPAL name to
+ * an NT_HOSTBASED_SERVICE name */
+ if (g_OID_equal(&krb5oid, mech)) {
+ if (get_krb5_hostbased_name(&name, &cname) != 0)
+ goto out_rel_buf;
+ *hostbased_name = cname;
+ } else {
+ printerr(1, "WARNING: unknown/unsupport mech OID\n");
+ goto out_rel_buf;
+ }
+
+ res = 0;
+out_rel_buf:
+ gss_release_buffer(&min_stat, &name);
+out_err:
+ return res;
+}
+
+void
+get_hostbased_client_buffer(gss_name_t client_name, gss_OID mech,
+ gss_buffer_t buf)
+{
+ char *hname;
+
+ if (!get_hostbased_client_name(client_name, mech, &hname)) {
+ buf->length = strlen(hname) + 1;
+ buf->value = hname;
+ } else {
+ buf->length = 0;
+ buf->value = NULL;
+ }
+}
diff --git a/utils/gssd/gss_names.h b/utils/gssd/gss_names.h
new file mode 100644
index 0000000..ce182f7
--- /dev/null
+++ b/utils/gssd/gss_names.h
@@ -0,0 +1,36 @@
+/*
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS 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.
+*/
+
+extern int get_hostbased_client_name(gss_name_t client_name, gss_OID mech,
+ char **hostbased_name);
+extern void get_hostbased_client_buffer(gss_name_t client_name,
+ gss_OID mech, gss_buffer_t buf);
diff --git a/utils/gssd/gss_oids.c b/utils/gssd/gss_oids.c
new file mode 100644
index 0000000..4362de2
--- /dev/null
+++ b/utils/gssd/gss_oids.c
@@ -0,0 +1,40 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/types.h>
+#include <gssapi/gssapi.h>
+
+/* from kerberos source, gssapi_krb5.c */
+gss_OID_desc krb5oid =
+ {9, "\052\206\110\206\367\022\001\002\002"};
diff --git a/utils/gssd/gss_oids.h b/utils/gssd/gss_oids.h
new file mode 100644
index 0000000..fde8532
--- /dev/null
+++ b/utils/gssd/gss_oids.h
@@ -0,0 +1,44 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _GSS_OIDS_H_
+#define _GSS_OIDS_H_
+
+#include <sys/types.h>
+
+extern gss_OID_desc krb5oid;
+
+#ifndef g_OID_equal
+#define g_OID_equal(o1,o2) \
+ (((o1)->length == (o2)->length) && \
+ (memcmp((o1)->elements,(o2)->elements,(unsigned int) (o1)->length) == 0))
+#endif
+
+#endif /* _GSS_OIDS_H_ */
diff --git a/utils/gssd/gss_util.c b/utils/gssd/gss_util.c
new file mode 100644
index 0000000..a4b2777
--- /dev/null
+++ b/utils/gssd/gss_util.c
@@ -0,0 +1,347 @@
+/*
+ * Adapted in part from MIT Kerberos 5-1.2.1 slave/kprop.c and from
+ * http://docs.sun.com/?p=/doc/816-1331/6m7oo9sms&a=view
+ *
+ * Copyright (c) 2002 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ * J. Bruce Fields <bfields@umich.edu>
+ * Marius Aamodt Eriksen <marius@umich.edu>
+ */
+
+/*
+ * slave/kprop.c
+ *
+ * Copyright 1990,1991 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 1994 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/file.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/param.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <gssapi/gssapi.h>
+#if defined(HAVE_KRB5) && !defined(GSS_C_NT_HOSTBASED_SERVICE)
+#include <gssapi/gssapi_generic.h>
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+#endif
+#include "gss_util.h"
+#include "err_util.h"
+#include "gssd.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#ifdef HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+
+/* Global gssd_credentials handle */
+gss_cred_id_t gssd_creds;
+
+gss_OID g_mechOid = GSS_C_NULL_OID;
+
+#if 0
+static void
+display_status_1(char *m, u_int32_t code, int type, const gss_OID mech)
+{
+ u_int32_t maj_stat, min_stat;
+ gss_buffer_desc msg = GSS_C_EMPTY_BUFFER;
+ u_int32_t msg_ctx = 0;
+ char *typestr;
+
+ switch (type) {
+ case GSS_C_GSS_CODE:
+ typestr = "GSS";
+ break;
+ case GSS_C_MECH_CODE:
+ typestr = "mechanism";
+ break;
+ default:
+ return;
+ /* NOTREACHED */
+ }
+
+ for (;;) {
+ maj_stat = gss_display_status(&min_stat, code,
+ type, mech, &msg_ctx, &msg);
+ if (maj_stat != GSS_S_COMPLETE) {
+ printerr(0, "ERROR: in call to "
+ "gss_display_status called from %s\n", m);
+ break;
+ } else {
+ printerr(0, "ERROR: GSS-API: (%s) error in %s(): %s\n",
+ typestr, m, (char *)msg.value);
+ }
+
+ if (msg.length != 0)
+ (void) gss_release_buffer(&min_stat, &msg);
+
+ if (msg_ctx == 0)
+ break;
+ }
+}
+#endif
+static char *
+gss_display_error(OM_uint32 status)
+{
+ char *error = NULL;
+
+ switch(status) {
+ case GSS_S_COMPLETE:
+ error = "GSS_S_COMPLETE";
+ break;
+ case GSS_S_CALL_INACCESSIBLE_READ:
+ error = "GSS_S_CALL_INACCESSIBLE_READ";
+ break;
+ case GSS_S_CALL_INACCESSIBLE_WRITE:
+ error = "GSS_S_CALL_INACCESSIBLE_WRITE";
+ break;
+ case GSS_S_CALL_BAD_STRUCTURE:
+ error = "GSS_S_CALL_BAD_STRUCTURE";
+ break;
+ case GSS_S_BAD_MECH:
+ error = "GSS_S_BAD_MECH";
+ break;
+ case GSS_S_BAD_NAME:
+ error = "GSS_S_BAD_NAME";
+ break;
+ case GSS_S_BAD_NAMETYPE:
+ error = "GSS_S_BAD_NAMETYPE";
+ break;
+ case GSS_S_BAD_BINDINGS:
+ error = "GSS_S_BAD_BINDINGS";
+ break;
+ case GSS_S_BAD_STATUS:
+ error = "GSS_S_BAD_STATUS";
+ break;
+ case GSS_S_BAD_SIG:
+ error = "GSS_S_BAD_SIG";
+ break;
+ case GSS_S_NO_CRED:
+ error = "GSS_S_NO_CRED";
+ break;
+ case GSS_S_NO_CONTEXT:
+ error = "GSS_S_NO_CONTEXT";
+ break;
+ case GSS_S_DEFECTIVE_TOKEN:
+ error = "GSS_S_DEFECTIVE_TOKEN";
+ break;
+ case GSS_S_DEFECTIVE_CREDENTIAL:
+ error = "GSS_S_DEFECTIVE_CREDENTIAL";
+ break;
+ case GSS_S_CREDENTIALS_EXPIRED:
+ error = "GSS_S_CREDENTIALS_EXPIRED";
+ break;
+ case GSS_S_CONTEXT_EXPIRED:
+ error = "GSS_S_CONTEXT_EXPIRED";
+ break;
+ case GSS_S_FAILURE:
+ error = "GSS_S_FAILURE";
+ break;
+ case GSS_S_BAD_QOP:
+ error = "GSS_S_BAD_QOP";
+ break;
+ case GSS_S_UNAUTHORIZED:
+ error = "GSS_S_UNAUTHORIZED";
+ break;
+ case GSS_S_UNAVAILABLE:
+ error = "GSS_S_UNAVAILABLE";
+ break;
+ case GSS_S_DUPLICATE_ELEMENT:
+ error = "GSS_S_DUPLICATE_ELEMENT";
+ break;
+ case GSS_S_NAME_NOT_MN:
+ error = "GSS_S_NAME_NOT_MN";
+ break;
+ default:
+ error = "Not defined";
+ }
+ return error;
+}
+
+static void
+display_status_2(char *m, u_int32_t major, u_int32_t minor, const gss_OID mech)
+{
+ u_int32_t maj_stat1, min_stat1;
+ u_int32_t maj_stat2, min_stat2;
+ gss_buffer_desc maj_gss_buf = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc min_gss_buf = GSS_C_EMPTY_BUFFER;
+ char maj_buf[30], min_buf[30];
+ char *maj, *min;
+ u_int32_t msg_ctx = 0;
+ int msg_verbosity = 0;
+
+ /* Get major status message */
+ maj_stat1 = gss_display_status(&min_stat1, major,
+ GSS_C_GSS_CODE, mech, &msg_ctx, &maj_gss_buf);
+
+ if (maj_stat1 != GSS_S_COMPLETE) {
+ snprintf(maj_buf, sizeof(maj_buf), "(0x%08x)", major);
+ maj = &maj_buf[0];
+ } else {
+ maj = maj_gss_buf.value;
+ }
+
+ /* Get minor status message */
+ maj_stat2 = gss_display_status(&min_stat2, minor,
+ GSS_C_MECH_CODE, mech, &msg_ctx, &min_gss_buf);
+
+ if (maj_stat2 != GSS_S_COMPLETE) {
+ snprintf(min_buf, sizeof(min_buf), "(0x%08x)", minor);
+ min = &min_buf[0];
+ } else {
+ min = min_gss_buf.value;
+ }
+
+ if (major == GSS_S_CREDENTIALS_EXPIRED)
+ msg_verbosity = 1;
+
+ printerr(msg_verbosity, "ERROR: GSS-API: error in %s(): %s (%s) - %s\n",
+ m, gss_display_error(major), maj, min);
+
+ if (maj_gss_buf.length != 0)
+ (void) gss_release_buffer(&min_stat1, &maj_gss_buf);
+ if (min_gss_buf.length != 0)
+ (void) gss_release_buffer(&min_stat2, &min_gss_buf);
+}
+
+void
+pgsserr(char *msg, u_int32_t maj_stat, u_int32_t min_stat, const gss_OID mech)
+{
+ display_status_2(msg, maj_stat, min_stat, mech);
+}
+
+int
+gssd_acquire_cred(char *server_name, const gss_OID oid)
+{
+ gss_buffer_desc name;
+ gss_name_t target_name;
+ u_int32_t maj_stat, min_stat;
+ u_int32_t ignore_maj_stat, ignore_min_stat;
+ gss_buffer_desc pbuf;
+
+ /* If server_name is NULL, get cred for GSS_C_NO_NAME */
+ if (server_name == NULL) {
+ target_name = GSS_C_NO_NAME;
+ } else {
+ name.value = (void *)server_name;
+ name.length = strlen(server_name);
+
+ maj_stat = gss_import_name(&min_stat, &name,
+ oid,
+ &target_name);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_import_name", maj_stat, min_stat, g_mechOid);
+ return (FALSE);
+ }
+ }
+
+ maj_stat = gss_acquire_cred(&min_stat, target_name, GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET, GSS_C_ACCEPT,
+ &gssd_creds, NULL, NULL);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_acquire_cred", maj_stat, min_stat, g_mechOid);
+ ignore_maj_stat = gss_display_name(&ignore_min_stat,
+ target_name, &pbuf, NULL);
+ if (ignore_maj_stat == GSS_S_COMPLETE) {
+ printerr(1, "Unable to obtain credentials for '%.*s'\n",
+ pbuf.length, pbuf.value);
+ ignore_maj_stat = gss_release_buffer(&ignore_min_stat,
+ &pbuf);
+ }
+ }
+
+ ignore_maj_stat = gss_release_name(&ignore_min_stat, &target_name);
+
+ return (maj_stat == GSS_S_COMPLETE);
+}
+
+int gssd_check_mechs(void)
+{
+ u_int32_t maj_stat, min_stat;
+ gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
+ int retval = -1;
+
+ maj_stat = gss_indicate_mechs(&min_stat, &supported_mechs);
+ if (maj_stat != GSS_S_COMPLETE) {
+ printerr(0, "Unable to obtain list of supported mechanisms. "
+ "Check that gss library is properly configured.\n");
+ goto out;
+ }
+ if (supported_mechs == GSS_C_NO_OID_SET ||
+ supported_mechs->count == 0) {
+ printerr(0, "Unable to obtain list of supported mechanisms. "
+ "Check that gss library is properly configured.\n");
+ goto out;
+ }
+ maj_stat = gss_release_oid_set(&min_stat, &supported_mechs);
+ retval = 0;
+out:
+ return retval;
+}
+
+void
+gssd_cleanup(void)
+{
+ u_int32_t min_stat;
+ gss_release_cred(&min_stat, &gssd_creds);
+}
diff --git a/utils/gssd/gss_util.h b/utils/gssd/gss_util.h
new file mode 100644
index 0000000..4da64e3
--- /dev/null
+++ b/utils/gssd/gss_util.h
@@ -0,0 +1,56 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _GSS_UTIL_H_
+#define _GSS_UTIL_H_
+
+#include <stdlib.h>
+#include <rpc/rpc.h>
+#include "write_bytes.h"
+
+extern gss_cred_id_t gssd_creds;
+
+int gssd_acquire_cred(char *server_name, const gss_OID oid);
+void pgsserr(char *msg, u_int32_t maj_stat, u_int32_t min_stat,
+ const gss_OID mech);
+int gssd_check_mechs(void);
+void gssd_cleanup(void);
+
+#ifndef HAVE_LIBGSSGLUE
+#include <gssapi/gssapi_krb5.h>
+#define gss_free_lucid_sec_context(min, ctx, ret) \
+ gss_krb5_free_lucid_sec_context(min, ret)
+
+#define gss_export_lucid_sec_context gss_krb5_export_lucid_sec_context
+#define gss_set_allowable_enctypes(min, cred, oid, num, types) \
+ gss_krb5_set_allowable_enctypes(min, cred, num, types)
+#endif
+
+#endif /* _GSS_UTIL_H_ */
diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
new file mode 100644
index 0000000..ca9b326
--- /dev/null
+++ b/utils/gssd/gssd.c
@@ -0,0 +1,1320 @@
+/*
+ gssd.c
+
+ Copyright (c) 2000, 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
+ Copyright (c) 2002 Andy Adamson <andros@UMICH.EDU>.
+ Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
+ All rights reserved, all wrongs reversed.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/inotify.h>
+#include <rpc/rpc.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <memory.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <netdb.h>
+#include <event2/event.h>
+
+#include "gssd.h"
+#include "err_util.h"
+#include "gss_util.h"
+#include "krb5_util.h"
+#include "nfslib.h"
+#include "conffile.h"
+
+static char *pipefs_path = GSSD_PIPEFS_DIR;
+static DIR *pipefs_dir;
+static int pipefs_fd;
+static int inotify_fd;
+struct event *inotify_ev;
+
+char *keytabfile = GSSD_DEFAULT_KEYTAB_FILE;
+char **ccachesearch;
+int use_memcache = 0;
+int root_uses_machine_creds = 1;
+unsigned int context_timeout = 0;
+unsigned int rpc_timeout = 5;
+char *preferred_realm = NULL;
+char *ccachedir = NULL;
+/* set $HOME to "/" by default */
+static bool set_home = true;
+/* Avoid DNS reverse lookups on server names */
+static bool avoid_dns = true;
+static bool use_gssproxy = false;
+pthread_mutex_t clp_lock = PTHREAD_MUTEX_INITIALIZER;
+static bool signal_received = false;
+static struct event_base *evbase = NULL;
+
+int upcall_timeout = DEF_UPCALL_TIMEOUT;
+static bool cancel_timed_out_upcalls = false;
+
+TAILQ_HEAD(topdir_list_head, topdir) topdir_list;
+
+/*
+ * active_thread_list:
+ *
+ * used to track upcalls for timeout purposes.
+ *
+ * protected by the active_thread_list_lock mutex.
+ *
+ * upcall_thread_info structures are added to the tail of the list
+ * by start_upcall_thread(), so entries closer to the head of the list
+ * will be closer to hitting the upcall timeout.
+ *
+ * upcall_thread_info structures are removed from the list upon a
+ * sucessful join of the upcall thread by the watchdog thread (via
+ * scan_active_thread_list().
+ */
+TAILQ_HEAD(active_thread_list_head, upcall_thread_info) active_thread_list;
+pthread_mutex_t active_thread_list_lock = PTHREAD_MUTEX_INITIALIZER;
+
+struct topdir {
+ TAILQ_ENTRY(topdir) list;
+ TAILQ_HEAD(clnt_list_head, clnt_info) clnt_list;
+ int wd;
+ char name[];
+};
+
+/*
+ * topdir_list:
+ * linked list of struct topdir with basic data about a topdir.
+ *
+ * clnt_list:
+ * linked list of struct clnt_info with basic data about a clntXXX dir,
+ * one per topdir.
+ *
+ * Directory structure: created by the kernel
+ * {rpc_pipefs}/{topdir}/clntXX : one per rpc_clnt struct in the kernel
+ * {rpc_pipefs}/{topdir}/clntXX/krb5 : read uid for which kernel wants
+ * a context, write the resulting context
+ * {rpc_pipefs}/{topdir}/clntXX/info : stores info such as server name
+ * {rpc_pipefs}/{topdir}/clntXX/gssd : pipe for all gss mechanisms using
+ * a text-based string of parameters
+ *
+ * Algorithm:
+ * Poll all {rpc_pipefs}/{topdir}/clntXX/YYYY files. When data is ready,
+ * read and process; performs rpcsec_gss context initialization protocol to
+ * get a cred for that user. Writes result to corresponding krb5 file
+ * in a form the kernel code will understand.
+ * In addition, we make sure we are notified whenever anything is
+ * created or destroyed in {rpc_pipefs} or in any of the clntXX directories,
+ * and rescan the whole {rpc_pipefs} when this happens.
+ */
+
+/*
+ * convert a presentation address string to a sockaddr_storage struct. Returns
+ * true on success or false on failure.
+ *
+ * Note that we do not populate the sin6_scope_id field here for IPv6 addrs.
+ * gssd nececessarily relies on hostname resolution and DNS AAAA records
+ * do not generally contain scope-id's. This means that GSSAPI auth really
+ * can't work with IPv6 link-local addresses.
+ *
+ * We *could* consider changing this if we did something like adopt the
+ * Microsoft "standard" of using the ipv6-literal.net domainname, but it's
+ * not really feasible at present.
+ */
+static bool
+gssd_addrstr_to_sockaddr(struct sockaddr *sa, const char *node, const char *port)
+{
+ int rc;
+ struct addrinfo *res;
+ struct addrinfo hints = { .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV };
+
+#ifndef IPV6_SUPPORTED
+ hints.ai_family = AF_INET;
+#endif /* IPV6_SUPPORTED */
+
+ rc = getaddrinfo(node, port, &hints, &res);
+ if (rc) {
+ printerr(0, "ERROR: unable to convert %s|%s to sockaddr: %s\n",
+ node, port,
+ rc == EAI_SYSTEM ? strerror(errno) : gai_strerror(rc));
+ return false;
+ }
+
+#ifdef IPV6_SUPPORTED
+ /*
+ * getnameinfo ignores the scopeid. If the address turns out to have
+ * a non-zero scopeid, we can't use it -- the resolved host might be
+ * completely different from the one intended.
+ */
+ if (res->ai_addr->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)res->ai_addr;
+ if (sin6->sin6_scope_id) {
+ printerr(0, "ERROR: address %s has non-zero "
+ "sin6_scope_id!\n", node);
+ nfs_freeaddrinfo(res);
+ return false;
+ }
+ }
+#endif /* IPV6_SUPPORTED */
+
+ memcpy(sa, res->ai_addr, res->ai_addrlen);
+ nfs_freeaddrinfo(res);
+ return true;
+}
+
+/*
+ * convert a sockaddr to a hostname
+ */
+static char *
+gssd_get_servername(const char *name, const struct sockaddr *sa, const char *addr)
+{
+ socklen_t addrlen;
+ int err;
+ char hbuf[NI_MAXHOST];
+ unsigned char buf[sizeof(struct in6_addr)];
+
+ while (avoid_dns) {
+ /*
+ * Determine if this is a server name, or an IP address.
+ * If it is an IP address, do the DNS lookup otherwise
+ * skip the DNS lookup.
+ */
+ if (strchr(name, '.') == NULL)
+ break; /* local name */
+ else if (inet_pton(AF_INET, name, buf) == 1)
+ break; /* IPv4 address */
+ else if (inet_pton(AF_INET6, name, buf) == 1)
+ break; /* IPv6 addrss */
+
+ return strdup(name);
+ }
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ addrlen = sizeof(struct sockaddr_in);
+ break;
+#ifdef IPV6_SUPPORTED
+ case AF_INET6:
+ addrlen = sizeof(struct sockaddr_in6);
+ break;
+#endif /* IPV6_SUPPORTED */
+ default:
+ printerr(0, "ERROR: unrecognized addr family %d\n",
+ sa->sa_family);
+ return NULL;
+ }
+
+ err = getnameinfo(sa, addrlen, hbuf, sizeof(hbuf), NULL, 0,
+ NI_NAMEREQD);
+ if (err) {
+ printerr(0, "ERROR: unable to resolve %s to hostname: %s\n",
+ addr, err == EAI_SYSTEM ? strerror(errno) :
+ gai_strerror(err));
+ return NULL;
+ }
+
+ return strdup(hbuf);
+}
+
+static void
+gssd_read_service_info(int dirfd, struct clnt_info *clp)
+{
+ int fd;
+ FILE *info = NULL;
+ int numfields;
+ char *server = NULL;
+ char *service = NULL;
+ int program;
+ int version;
+ char *address = NULL;
+ char *protoname = NULL;
+ char *port = NULL;
+ char *servername = NULL;
+
+ fd = openat(dirfd, "info", O_RDONLY);
+ if (fd < 0) {
+ printerr(0, "ERROR: can't open %s/info: %s\n",
+ clp->relpath, strerror(errno));
+ goto fail;
+ }
+
+ info = fdopen(fd, "r");
+ if (!info) {
+ printerr(0, "ERROR: can't fdopen %s/info: %s\n",
+ clp->relpath, strerror(errno));
+ close(fd);
+ goto fail;
+ }
+
+ /*
+ * Some history:
+ *
+ * The first three lines were added with rpc_pipefs in 2003-01-13.
+ * (commit af2f003391786fb632889c02142c941b212ba4ff)
+ *
+ * The 'protocol' line was added in 2003-06-11.
+ * (commit 9bd741ae48785d0c0e75cf906ff66f893d600c2d)
+ *
+ * The 'port' line was added in 2007-09-26.
+ * (commit bf19aacecbeebccb2c3d150a8bd9416b7dba81fe)
+ */
+ numfields = fscanf(info,
+ "RPC server: %ms\n"
+ "service: %ms (%d) version %d\n"
+ "address: %ms\n"
+ "protocol: %ms\n"
+ "port: %ms\n",
+ &server,
+ &service, &program, &version,
+ &address,
+ &protoname,
+ &port);
+
+
+ switch (numfields) {
+ case 5:
+ protoname = strdup("tcp");
+ if (!protoname)
+ goto fail;
+ /* fall through */
+ case 6:
+ /* fall through */
+ case 7:
+ break;
+ default:
+ goto fail;
+ }
+
+ /*
+ * The user space RPC library has no support for
+ * RPC-over-RDMA at this time, so change 'rdma'
+ * to 'tcp', and '20049' to '2049'.
+ */
+ if (strcmp(protoname, "rdma") == 0) {
+ free(protoname);
+ protoname = strdup("tcp");
+ if (!protoname)
+ goto fail;
+ free(port);
+ port = strdup("2049");
+ if (!port)
+ goto fail;
+ }
+
+ if (!gssd_addrstr_to_sockaddr((struct sockaddr *)&clp->addr,
+ address, port ? port : ""))
+ goto fail;
+
+ servername = gssd_get_servername(server, (struct sockaddr *)&clp->addr, address);
+ if (!servername)
+ goto fail;
+
+ if (asprintf(&clp->servicename, "%s@%s", service, servername) < 0)
+ goto fail;
+
+ clp->servername = servername;
+ clp->prog = program;
+ clp->vers = version;
+ clp->protocol = protoname;
+
+ goto out;
+
+fail:
+ printerr(0, "ERROR: failed to parse %s/info\n", clp->relpath);
+ clp->upcall_address = strdup(address);
+ clp->upcall_port = strdup(port);
+ clp->upcall_program = program;
+ clp->upcall_vers = version;
+ clp->upcall_protoname = strdup(protoname);
+ clp->upcall_service = strdup(service);
+ free(servername);
+ free(protoname);
+ clp->servicename = NULL;
+ clp->servername = NULL;
+ clp->prog = 0;
+ clp->vers = 0;
+ clp->protocol = NULL;
+out:
+ if (info)
+ fclose(info);
+
+ free(server);
+ free(service);
+ free(address);
+ free(port);
+}
+
+/* Actually frees clp and fields that might be used from other
+ * threads if was last reference.
+ */
+void
+gssd_free_client(struct clnt_info *clp)
+{
+ int refcnt;
+
+ pthread_mutex_lock(&clp_lock);
+ refcnt = --clp->refcount;
+ pthread_mutex_unlock(&clp_lock);
+ if (refcnt > 0)
+ return;
+
+ printerr(4, "freeing client %s\n", clp->relpath);
+
+ if (clp->krb5_fd >= 0)
+ close(clp->krb5_fd);
+
+ if (clp->gssd_fd >= 0)
+ close(clp->gssd_fd);
+
+ free(clp->relpath);
+ free(clp->servicename);
+ free(clp->servername);
+ free(clp->protocol);
+ if (!clp->servername) {
+ if (clp->upcall_address)
+ free(clp->upcall_address);
+ if (clp->upcall_port)
+ free(clp->upcall_port);
+ if (clp->upcall_protoname)
+ free(clp->upcall_protoname);
+ if (clp->upcall_service)
+ free(clp->upcall_service);
+ }
+ free(clp);
+}
+
+/* Called when removing from clnt_list to tear down event handling.
+ * Will then free clp if was last reference.
+ */
+static void
+gssd_destroy_client(struct clnt_info *clp)
+{
+ printerr(4, "destroying client %s\n", clp->relpath);
+
+ if (clp->krb5_ev) {
+ event_del(clp->krb5_ev);
+ event_free(clp->krb5_ev);
+ clp->krb5_ev = NULL;
+ }
+
+ if (clp->gssd_ev) {
+ event_del(clp->gssd_ev);
+ event_free(clp->gssd_ev);
+ clp->gssd_ev = NULL;
+ }
+
+ inotify_rm_watch(inotify_fd, clp->wd);
+ gssd_free_client(clp);
+}
+
+static void gssd_scan(void);
+
+/* For each upcall read the upcall info into the buffer, then create a
+ * thread in a detached state so that resources are released back into
+ * the system without the need for a join.
+ */
+static void
+gssd_clnt_gssd_cb(int UNUSED(fd), short UNUSED(which), void *data)
+{
+ struct clnt_info *clp = data;
+
+ /* if there was a failure to translate IP to name for this server,
+ * try again
+ */
+ if (!clp->servername) {
+ if (!gssd_addrstr_to_sockaddr((struct sockaddr *)&clp->addr,
+ clp->upcall_address, clp->upcall_port ?
+ clp->upcall_port : "")) {
+ goto do_upcall;
+ }
+ clp->servername = gssd_get_servername(clp->upcall_address,
+ (struct sockaddr *)&clp->addr, clp->upcall_address);
+ if (!clp->servername)
+ goto do_upcall;
+
+ if (asprintf(&clp->servicename, "%s@%s", clp->upcall_service,
+ clp->servername) < 0) {
+ free(clp->servername);
+ clp->servername = NULL;
+ goto do_upcall;
+ }
+ clp->prog = clp->upcall_program;
+ clp->vers = clp->upcall_vers;
+ clp->protocol = strdup(clp->upcall_protoname);
+ }
+do_upcall:
+ handle_gssd_upcall(clp);
+}
+
+static void
+gssd_clnt_krb5_cb(int UNUSED(fd), short UNUSED(which), void *data)
+{
+ struct clnt_info *clp = data;
+
+ handle_krb5_upcall(clp);
+}
+
+/*
+ * scan_active_thread_list:
+ *
+ * Walks the active_thread_list, trying to join as many upcall threads as
+ * possible. For threads that have terminated, the corresponding
+ * upcall_thread_info will be removed from the list and freed. Threads that
+ * are still busy and have exceeded the upcall_timeout will cause an error to
+ * be logged and may be canceled (depending on the value of
+ * cancel_timed_out_upcalls).
+ *
+ * Returns the number of seconds that the watchdog thread should wait before
+ * calling scan_active_thread_list() again.
+ */
+static int
+scan_active_thread_list(void)
+{
+ struct upcall_thread_info *info;
+ struct timespec now;
+ unsigned int sleeptime;
+ bool sleeptime_set = false;
+ int err;
+ void *tret, *saveprev;
+
+ sleeptime = upcall_timeout;
+ pthread_mutex_lock(&active_thread_list_lock);
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ TAILQ_FOREACH(info, &active_thread_list, list) {
+ err = pthread_tryjoin_np(info->tid, &tret);
+ switch (err) {
+ case 0:
+ /*
+ * The upcall thread has either completed successfully, or
+ * has been canceled _and_ has acted on the cancellation request
+ * (i.e. has hit a cancellation point). We can now remove the
+ * upcall_thread_info from the list and free it.
+ */
+ if (tret == PTHREAD_CANCELED)
+ printerr(2, "watchdog: thread id 0x%lx cancelled successfully\n",
+ info->tid);
+ saveprev = info->list.tqe_prev;
+ TAILQ_REMOVE(&active_thread_list, info, list);
+ free(info);
+ info = saveprev;
+ break;
+ case EBUSY:
+ /*
+ * The upcall thread is still running. If the timeout has expired
+ * then we either cancel the thread, log an error, and do an error
+ * downcall to the kernel (cancel_timed_out_upcalls=true) or simply
+ * log an error (cancel_timed_out_upcalls=false). In either case,
+ * the error is logged only once.
+ */
+ if (now.tv_sec >= info->timeout.tv_sec) {
+ if (cancel_timed_out_upcalls && !(info->flags & UPCALL_THREAD_CANCELED)) {
+ printerr(0, "watchdog: thread id 0x%lx timed out\n",
+ info->tid);
+ pthread_cancel(info->tid);
+ info->flags |= (UPCALL_THREAD_CANCELED|UPCALL_THREAD_WARNED);
+ do_error_downcall(info->fd, info->uid, -ETIMEDOUT);
+ } else {
+ if (!(info->flags & UPCALL_THREAD_WARNED)) {
+ printerr(0, "watchdog: thread id 0x%lx running for %ld seconds\n",
+ info->tid,
+ now.tv_sec - info->timeout.tv_sec + upcall_timeout);
+ info->flags |= UPCALL_THREAD_WARNED;
+ }
+ }
+ } else if (!sleeptime_set) {
+ /*
+ * The upcall thread is still running, but the timeout has not yet
+ * expired. Calculate the time remaining until the timeout will
+ * expire. This is the amount of time the watchdog thread will
+ * wait before running again. We only need to do this for the busy
+ * thread closest to the head of the list - entries appearing later
+ * in the list will time out later.
+ */
+ sleeptime = info->timeout.tv_sec - now.tv_sec;
+ sleeptime_set = true;
+ }
+ break;
+ default:
+ /* EDEADLK, EINVAL, and ESRCH... none of which should happen! */
+ printerr(0, "watchdog: attempt to join thread id 0x%lx returned %d (%s)!\n",
+ info->tid, err, strerror(err));
+ break;
+ }
+ }
+ pthread_mutex_unlock(&active_thread_list_lock);
+
+ return sleeptime;
+}
+
+static void *
+watchdog_thread_fn(void *UNUSED(arg))
+{
+ unsigned int sleeptime;
+
+ for (;;) {
+ sleeptime = scan_active_thread_list();
+ printerr(4, "watchdog: sleeping %u secs\n", sleeptime);
+ sleep(sleeptime);
+ }
+ return (void *)0;
+}
+
+static int
+start_watchdog_thread(void)
+{
+ pthread_attr_t attr;
+ pthread_t th;
+ int ret;
+
+ ret = pthread_attr_init(&attr);
+ if (ret != 0) {
+ printerr(0, "ERROR: failed to init pthread attr: ret %d: %s\n",
+ ret, strerror(errno));
+ return ret;
+ }
+ ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ret != 0) {
+ printerr(0, "ERROR: failed to create pthread attr: ret %d: %s\n",
+ ret, strerror(errno));
+ return ret;
+ }
+ ret = pthread_create(&th, &attr, watchdog_thread_fn, NULL);
+ if (ret != 0) {
+ printerr(0, "ERROR: pthread_create failed: ret %d: %s\n",
+ ret, strerror(errno));
+ }
+ return ret;
+}
+
+static struct clnt_info *
+gssd_get_clnt(struct topdir *tdi, const char *name)
+{
+ struct clnt_info *clp;
+
+ TAILQ_FOREACH(clp, &tdi->clnt_list, list)
+ if (!strcmp(clp->name, name))
+ return clp;
+
+ printerr(4, "creating client %s/%s\n", tdi->name, name);
+
+ clp = calloc(1, sizeof(struct clnt_info));
+ if (!clp) {
+ printerr(0, "ERROR: can't malloc clnt_info: %s\n",
+ strerror(errno));
+ return NULL;
+ }
+
+ if (asprintf(&clp->relpath, "%s/%s", tdi->name, name) < 0) {
+ clp->relpath = NULL;
+ goto out;
+ }
+
+ clp->wd = inotify_add_watch(inotify_fd, clp->relpath, IN_CREATE | IN_DELETE);
+ if (clp->wd < 0) {
+ if (errno != ENOENT)
+ printerr(0, "ERROR: %s: inotify_add_watch failed for %s: %s\n",
+ __FUNCTION__, clp->relpath, strerror(errno));
+ goto out;
+ }
+
+ clp->name = clp->relpath + strlen(tdi->name) + 1;
+ clp->krb5_fd = -1;
+ clp->gssd_fd = -1;
+ clp->refcount = 1;
+
+ TAILQ_INSERT_HEAD(&tdi->clnt_list, clp, list);
+ return clp;
+
+out:
+ free(clp->relpath);
+ free(clp);
+ return NULL;
+}
+
+static int
+gssd_scan_clnt(struct clnt_info *clp)
+{
+ int clntfd;
+
+ printerr(4, "scanning client %s\n", clp->relpath);
+
+ clntfd = openat(pipefs_fd, clp->relpath, O_RDONLY);
+ if (clntfd < 0) {
+ if (errno != ENOENT)
+ printerr(0, "ERROR: %s: can't openat %s: %s\n",
+ __FUNCTION__, clp->relpath, strerror(errno));
+ return -1;
+ }
+
+ if (clp->gssd_fd == -1)
+ clp->gssd_fd = openat(clntfd, "gssd", O_RDWR | O_NONBLOCK);
+
+ if (clp->gssd_fd == -1 && clp->krb5_fd == -1)
+ clp->krb5_fd = openat(clntfd, "krb5", O_RDWR | O_NONBLOCK);
+
+ if (!clp->gssd_ev && clp->gssd_fd >= 0) {
+ clp->gssd_ev = event_new(evbase, clp->gssd_fd, EV_READ | EV_PERSIST,
+ gssd_clnt_gssd_cb, clp);
+ if (!clp->gssd_ev) {
+ printerr(0, "ERROR: %s: can't create gssd event for %s: %s\n",
+ __FUNCTION__, clp->relpath, strerror(errno));
+ close(clp->gssd_fd);
+ clp->gssd_fd = -1;
+ } else {
+ event_add(clp->gssd_ev, NULL);
+ }
+ }
+
+ if (!clp->krb5_ev && clp->krb5_fd >= 0) {
+ clp->krb5_ev = event_new(evbase, clp->krb5_fd, EV_READ | EV_PERSIST,
+ gssd_clnt_krb5_cb, clp);
+ if (!clp->krb5_ev) {
+ printerr(0, "ERROR: %s: can't create krb5 event for %s: %s\n",
+ __FUNCTION__, clp->relpath, strerror(errno));
+ close(clp->krb5_fd);
+ clp->krb5_fd = -1;
+ } else {
+ event_add(clp->krb5_ev, NULL);
+ }
+ }
+
+ if (clp->krb5_fd == -1 && clp->gssd_fd == -1)
+ /* not fatal, files might appear later */
+ goto out;
+
+ if (clp->prog == 0)
+ gssd_read_service_info(clntfd, clp);
+
+out:
+ close(clntfd);
+ clp->scanned = true;
+ return 0;
+}
+
+static int
+gssd_create_clnt(struct topdir *tdi, const char *name)
+{
+ struct clnt_info *clp;
+
+ clp = gssd_get_clnt(tdi, name);
+ if (!clp)
+ return -1;
+
+ return gssd_scan_clnt(clp);
+}
+
+static struct topdir *
+gssd_get_topdir(const char *name)
+{
+ struct topdir *tdi;
+
+ TAILQ_FOREACH(tdi, &topdir_list, list)
+ if (!strcmp(tdi->name, name))
+ return tdi;
+
+ tdi = malloc(sizeof(*tdi) + strlen(name) + 1);
+ if (!tdi) {
+ printerr(0, "ERROR: Couldn't allocate struct topdir\n");
+ return NULL;
+ }
+
+ tdi->wd = inotify_add_watch(inotify_fd, name, IN_CREATE);
+ if (tdi->wd < 0) {
+ printerr(0, "ERROR: %s: inotify_add_watch failed for top dir %s: %s\n",
+ __FUNCTION__, tdi->name, strerror(errno));
+ free(tdi);
+ return NULL;
+ }
+
+ strcpy(tdi->name, name);
+ TAILQ_INIT(&tdi->clnt_list);
+
+ TAILQ_INSERT_HEAD(&topdir_list, tdi, list);
+ return tdi;
+}
+
+static void
+gssd_scan_topdir(const char *name)
+{
+ struct topdir *tdi;
+ int dfd;
+ DIR *dir;
+ struct clnt_info *clp;
+ struct dirent *d;
+
+ tdi = gssd_get_topdir(name);
+ if (!tdi)
+ return;
+
+ dfd = openat(pipefs_fd, tdi->name, O_RDONLY);
+ if (dfd < 0) {
+ if (errno != ENOENT)
+ printerr(0, "ERROR: %s: can't openat %s: %s\n",
+ __FUNCTION__, tdi->name, strerror(errno));
+ return;
+ }
+
+ dir = fdopendir(dfd);
+ if (!dir) {
+ printerr(0, "ERROR: can't fdopendir %s: %s\n",
+ tdi->name, strerror(errno));
+ return;
+ }
+
+ TAILQ_FOREACH(clp, &tdi->clnt_list, list)
+ clp->scanned = false;
+
+ while ((d = readdir(dir))) {
+ if (d->d_type != DT_DIR)
+ continue;
+
+ if (strncmp(d->d_name, "clnt", strlen("clnt")))
+ continue;
+
+ gssd_create_clnt(tdi, d->d_name);
+ }
+
+ closedir(dir);
+
+ TAILQ_FOREACH(clp, &tdi->clnt_list, list) {
+ void *saveprev;
+
+ if (clp->scanned)
+ continue;
+
+ printerr(3, "orphaned client %s\n", clp->relpath);
+ saveprev = clp->list.tqe_prev;
+ TAILQ_REMOVE(&tdi->clnt_list, clp, list);
+ gssd_destroy_client(clp);
+ clp = saveprev;
+ }
+}
+
+static void
+gssd_scan(void)
+{
+ struct dirent *d;
+
+ printerr(4, "doing a full rescan\n");
+ rewinddir(pipefs_dir);
+
+ while ((d = readdir(pipefs_dir))) {
+ if (d->d_type != DT_DIR)
+ continue;
+
+ if (d->d_name[0] == '.')
+ continue;
+
+ gssd_scan_topdir(d->d_name);
+ }
+
+ if (TAILQ_EMPTY(&topdir_list)) {
+ printerr(0, "ERROR: the rpc_pipefs directory is empty!\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void
+gssd_scan_cb(int UNUSED(fd), short UNUSED(which), void *UNUSED(data))
+{
+ gssd_scan();
+}
+
+static bool
+gssd_inotify_topdir(struct topdir *tdi, const struct inotify_event *ev)
+{
+ printerr(5, "inotify event for topdir (%s) - "
+ "ev->wd (%d) ev->name (%s) ev->mask (0x%08x)\n",
+ tdi->name, ev->wd, ev->len > 0 ? ev->name : "<?>", ev->mask);
+
+ if (ev->mask & IN_IGNORED) {
+ printerr(0, "ERROR: topdir disappeared!\n");
+ return false;
+ }
+
+ if (ev->len == 0)
+ return false;
+
+ if (ev->mask & IN_CREATE) {
+ if (!(ev->mask & IN_ISDIR))
+ return true;
+
+ if (strncmp(ev->name, "clnt", strlen("clnt")))
+ return true;
+
+ if (gssd_create_clnt(tdi, ev->name))
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
+static bool
+gssd_inotify_clnt(struct topdir *tdi, struct clnt_info *clp, const struct inotify_event *ev)
+{
+ printerr(5, "inotify event for clntdir (%s) - "
+ "ev->wd (%d) ev->name (%s) ev->mask (0x%08x)\n",
+ clp->relpath, ev->wd, ev->len > 0 ? ev->name : "<?>", ev->mask);
+
+ if (ev->mask & IN_IGNORED) {
+ TAILQ_REMOVE(&tdi->clnt_list, clp, list);
+ gssd_destroy_client(clp);
+ return true;
+ }
+
+ if (ev->len == 0)
+ return false;
+
+ if (ev->mask & IN_CREATE) {
+ if (!strcmp(ev->name, "gssd") ||
+ !strcmp(ev->name, "krb5") ||
+ !strcmp(ev->name, "info"))
+ if (gssd_scan_clnt(clp))
+ return false;
+
+ return true;
+
+ } else if (ev->mask & IN_DELETE) {
+ if (!strcmp(ev->name, "gssd") && clp->gssd_fd >= 0) {
+ close(clp->gssd_fd);
+ event_del(clp->gssd_ev);
+ event_free(clp->gssd_ev);
+ clp->gssd_ev = NULL;
+ clp->gssd_fd = -1;
+
+ } else if (!strcmp(ev->name, "krb5") && clp->krb5_fd >= 0) {
+ close(clp->krb5_fd);
+ event_del(clp->krb5_ev);
+ event_free(clp->krb5_ev);
+ clp->krb5_ev = NULL;
+ clp->krb5_fd = -1;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+static void
+gssd_inotify_cb(int ifd, short UNUSED(which), void *UNUSED(data))
+{
+ bool rescan = false;
+ struct topdir *tdi;
+ struct clnt_info *clp;
+
+ while (true) {
+ char buf[4096] __attribute__ ((aligned(__alignof__(struct inotify_event))));
+ const struct inotify_event *ev;
+ ssize_t len;
+ char *ptr;
+
+ len = read(ifd, buf, sizeof(buf));
+ if (len == -1 && errno == EINTR)
+ continue;
+
+ if (len <= 0)
+ break;
+
+ for (ptr = buf; ptr < buf + len;
+ ptr += sizeof(struct inotify_event) + ev->len) {
+ ev = (const struct inotify_event *)ptr;
+
+ if (ev->mask & IN_Q_OVERFLOW) {
+ printerr(0, "ERROR: inotify queue overflow\n");
+ rescan = true;
+ break;
+ }
+
+ TAILQ_FOREACH(tdi, &topdir_list, list) {
+ if (tdi->wd == ev->wd) {
+ if (!gssd_inotify_topdir(tdi, ev))
+ rescan = true;
+ goto found;
+ }
+
+ TAILQ_FOREACH(clp, &tdi->clnt_list, list) {
+ if (clp->wd == ev->wd) {
+ if (!gssd_inotify_clnt(tdi, clp, ev))
+ rescan = true;
+ goto found;
+ }
+ }
+ }
+
+found:
+ if (!tdi) {
+ printerr(5, "inotify event for unknown wd!!! - "
+ "ev->wd (%d) ev->name (%s) ev->mask (0x%08x)\n",
+ ev->wd, ev->len > 0 ? ev->name : "<?>", ev->mask);
+ rescan = true;
+ }
+ }
+ }
+
+ if (rescan)
+ gssd_scan();
+}
+
+static void
+sig_die(int signal)
+{
+ if (signal_received) {
+ gssd_destroy_krb5_principals(root_uses_machine_creds);
+ printerr(1, "forced exiting on signal %d\n", signal);
+ exit(0);
+ }
+
+ signal_received = true;
+ printerr(1, "exiting on signal %d\n", signal);
+ event_base_loopexit(evbase, NULL);
+}
+
+static void
+usage(char *progname)
+{
+ fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm] [-D] [-H] [-U upcall timeout] [-C]\n",
+ progname);
+ exit(1);
+}
+
+inline static void
+read_gss_conf(void)
+{
+ char *s;
+
+ conf_init_file(NFS_CONFFILE);
+ use_memcache = conf_get_bool("gssd", "use-memcache", use_memcache);
+ root_uses_machine_creds = conf_get_bool("gssd", "use-machine-creds",
+ root_uses_machine_creds);
+ avoid_dns = conf_get_bool("gssd", "avoid-dns", avoid_dns);
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+ limit_to_legacy_enctypes = conf_get_bool("gssd", "limit-to-legacy-enctypes",
+ limit_to_legacy_enctypes);
+#endif
+ context_timeout = conf_get_num("gssd", "context-timeout", context_timeout);
+ rpc_timeout = conf_get_num("gssd", "rpc-timeout", rpc_timeout);
+ upcall_timeout = conf_get_num("gssd", "upcall-timeout", upcall_timeout);
+ cancel_timed_out_upcalls = conf_get_bool("gssd", "cancel-timed-out-upcalls",
+ cancel_timed_out_upcalls);
+ s = conf_get_str("gssd", "pipefs-directory");
+ if (!s)
+ s = conf_get_str("general", "pipefs-directory");
+ else
+ printerr(0, "WARNING: Specifying pipefs-directory in the [gssd] "
+ "section of %s is deprecated. Use the [general] "
+ "section instead.", NFS_CONFFILE);
+ if (s)
+ pipefs_path = s;
+ s = conf_get_str("gssd", "keytab-file");
+ if (s)
+ keytabfile = s;
+ s = conf_get_str("gssd", "cred-cache-directory");
+ if (s)
+ ccachedir = strdup(s);
+ s = conf_get_str("gssd", "preferred-realm");
+ if (s)
+ preferred_realm = s;
+
+ use_gssproxy = conf_get_bool("gssd", "use-gss-proxy", use_gssproxy);
+ set_home = conf_get_bool("gssd", "set-home", set_home);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int fg = 0;
+ int verbosity = 0;
+ int rpc_verbosity = 0;
+ int opt;
+ int i;
+ int rc;
+ extern char *optarg;
+ char *progname;
+ struct event *sighup_ev;
+
+ read_gss_conf();
+
+ verbosity = conf_get_num("gssd", "verbosity", verbosity);
+ rpc_verbosity = conf_get_num("gssd", "rpc-verbosity", rpc_verbosity);
+
+ while ((opt = getopt(argc, argv, "HDfvrlmnMp:k:d:t:T:R:U:C")) != -1) {
+ switch (opt) {
+ case 'f':
+ fg = 1;
+ break;
+ case 'm':
+ /* Accept but ignore this. Now the default. */
+ break;
+ case 'M':
+ use_memcache = 1;
+ break;
+ case 'n':
+ root_uses_machine_creds = 0;
+ break;
+ case 'v':
+ verbosity++;
+ break;
+ case 'r':
+ rpc_verbosity++;
+ break;
+ case 'p':
+ pipefs_path = optarg;
+ break;
+ case 'k':
+ keytabfile = optarg;
+ break;
+ case 'd':
+ free(ccachedir);
+ ccachedir = strdup(optarg);
+ break;
+ case 't':
+ context_timeout = atoi(optarg);
+ break;
+ case 'T':
+ rpc_timeout = atoi(optarg);
+ break;
+ case 'R':
+ preferred_realm = strdup(optarg);
+ break;
+ case 'l':
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+ limit_to_legacy_enctypes = 1;
+#else
+ errx(1, "Encryption type limits not supported by Kerberos libraries.");
+#endif
+ break;
+ case 'D':
+ avoid_dns = false;
+ break;
+ case 'H':
+ set_home = false;
+ break;
+ case 'U':
+ upcall_timeout = atoi(optarg);
+ break;
+ case 'C':
+ cancel_timed_out_upcalls = true;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ /*
+ * Some krb5 routines try to scrape info out of files in the user's
+ * home directory. This can easily deadlock when that homedir is on a
+ * kerberized NFS mount. By setting $HOME to "/" by default, we prevent
+ * this behavior in routines that use $HOME in preference to the results
+ * of getpw*.
+ *
+ * Some users do not use Kerberized home dirs and need $HOME to remain
+ * unchanged. Those users can leave $HOME unchanged by setting set_home
+ * to false.
+ */
+ if (set_home) {
+ if (setenv("HOME", "/", 1)) {
+ printerr(0, "gssd: Unable to set $HOME: %s\n", strerror(errno));
+ exit(1);
+ }
+ }
+
+ if (use_gssproxy) {
+ if (setenv("GSS_USE_PROXY", "yes", 1) < 0) {
+ printerr(0, "gssd: Unable to set $GSS_USE_PROXY: %s\n",
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (ccachedir) {
+ char *ptr;
+
+ for (ptr = ccachedir, i = 2; *ptr; ptr++)
+ if (*ptr == ':')
+ i++;
+
+ ccachesearch = malloc(i * sizeof(char *));
+ if (!ccachesearch) {
+ printerr(0, "malloc failure\n");
+ exit(EXIT_FAILURE);
+ }
+
+ i = 0;
+ ccachesearch[i++] = strtok(ccachedir, ":");
+ while(ccachesearch[i - 1])
+ ccachesearch[i++] = strtok(NULL, ":");
+
+ } else {
+ ccachesearch = malloc(3 * sizeof(char *));
+ if (!ccachesearch) {
+ printerr(0, "malloc failure\n");
+ exit(EXIT_FAILURE);
+ }
+
+ ccachesearch[0] = GSSD_DEFAULT_CRED_DIR;
+ ccachesearch[1] = GSSD_USER_CRED_DIR;
+ ccachesearch[2] = NULL;
+ }
+
+ if (preferred_realm == NULL)
+ gssd_k5_get_default_realm(&preferred_realm);
+
+ if ((progname = strrchr(argv[0], '/')))
+ progname++;
+ else
+ progname = argv[0];
+
+ if (upcall_timeout > MAX_UPCALL_TIMEOUT)
+ upcall_timeout = MAX_UPCALL_TIMEOUT;
+ else if (upcall_timeout < MIN_UPCALL_TIMEOUT)
+ upcall_timeout = MIN_UPCALL_TIMEOUT;
+
+ initerr(progname, verbosity, fg);
+#ifdef HAVE_LIBTIRPC_SET_DEBUG
+ /*
+ * Only set the libtirpc debug level if explicitly requested via -r.
+ */
+ if (rpc_verbosity > 0)
+ libtirpc_set_debug(progname, rpc_verbosity, fg);
+#else
+ if (rpc_verbosity > 0)
+ printerr(0, "Warning: libtirpc does not "
+ "support setting debug levels\n");
+#endif
+
+ daemon_init(fg);
+
+ if (gssd_check_mechs() != 0)
+ errx(1, "Problem with gssapi library");
+
+ evbase = event_base_new();
+ if (!evbase) {
+ printerr(0, "ERROR: failed to create event base: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ pipefs_dir = opendir(pipefs_path);
+ if (!pipefs_dir) {
+ printerr(0, "ERROR: opendir(%s) failed: %s\n", pipefs_path, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ pipefs_fd = dirfd(pipefs_dir);
+ if (fchdir(pipefs_fd)) {
+ printerr(0, "ERROR: fchdir(%s) failed: %s\n", pipefs_path, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ inotify_fd = inotify_init1(IN_NONBLOCK);
+ if (inotify_fd == -1) {
+ printerr(0, "ERROR: inotify_init1 failed: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ signal(SIGINT, sig_die);
+ signal(SIGTERM, sig_die);
+ sighup_ev = evsignal_new(evbase, SIGHUP, gssd_scan_cb, NULL);
+ if (!sighup_ev) {
+ printerr(0, "ERROR: failed to create SIGHUP event: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ evsignal_add(sighup_ev, NULL);
+ inotify_ev = event_new(evbase, inotify_fd, EV_READ | EV_PERSIST,
+ gssd_inotify_cb, NULL);
+ if (!inotify_ev) {
+ printerr(0, "ERROR: failed to create inotify event: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ event_add(inotify_ev, NULL);
+
+ TAILQ_INIT(&active_thread_list);
+
+ rc = start_watchdog_thread();
+ if (rc != 0) {
+ printerr(0, "ERROR: failed to start watchdog thread: %d\n", rc);
+ exit(EXIT_FAILURE);
+ }
+
+ TAILQ_INIT(&topdir_list);
+ gssd_scan();
+ daemon_ready();
+
+ rc = event_base_dispatch(evbase);
+
+ printerr(0, "event_dispatch() returned %i!\n", rc);
+
+ gssd_destroy_krb5_principals(root_uses_machine_creds);
+
+ while (!TAILQ_EMPTY(&topdir_list)) {
+ struct topdir *tdi = TAILQ_FIRST(&topdir_list);
+ TAILQ_REMOVE(&topdir_list, tdi, list);
+ while (!TAILQ_EMPTY(&tdi->clnt_list)) {
+ struct clnt_info *clp = TAILQ_FIRST(&tdi->clnt_list);
+ TAILQ_REMOVE(&tdi->clnt_list, clp, list);
+ gssd_destroy_client(clp);
+ }
+ free(tdi);
+ }
+
+ event_free(inotify_ev);
+ event_free(sighup_ev);
+ event_base_free(evbase);
+
+ close(inotify_fd);
+ close(pipefs_fd);
+ closedir(pipefs_dir);
+
+ free(preferred_realm);
+ free(ccachesearch);
+ free(ccachedir);
+
+ return rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h
new file mode 100644
index 0000000..4e070ed
--- /dev/null
+++ b/utils/gssd/gssd.h
@@ -0,0 +1,124 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _RPC_GSSD_H_
+#define _RPC_GSSD_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <gssapi/gssapi.h>
+#include <event.h>
+#include <stdbool.h>
+#include <pthread.h>
+
+#ifndef GSSD_PIPEFS_DIR
+#define GSSD_PIPEFS_DIR NFS_STATEDIR "/rpc_pipefs"
+#endif
+#define DNOTIFY_SIGNAL (SIGRTMIN + 3)
+
+#define GSSD_DEFAULT_CRED_DIR "/tmp"
+#define GSSD_USER_CRED_DIR "/run/user/%U"
+#define GSSD_DEFAULT_CRED_PREFIX "krb5cc"
+#define GSSD_DEFAULT_MACHINE_CRED_SUFFIX "machine"
+#define GSSD_DEFAULT_KEYTAB_FILE "/etc/krb5.keytab"
+#define GSSD_SERVICE_NAME "nfs"
+#define RPC_CHAN_BUF_SIZE 32768
+
+/* timeouts are in seconds */
+#define MIN_UPCALL_TIMEOUT 5
+#define DEF_UPCALL_TIMEOUT 30
+#define MAX_UPCALL_TIMEOUT 600
+
+/*
+ * The gss mechanisms that we can handle
+ */
+enum {AUTHTYPE_KRB5, AUTHTYPE_LIPKEY};
+
+extern char *keytabfile;
+extern char **ccachesearch;
+extern int use_memcache;
+extern int root_uses_machine_creds;
+extern unsigned int context_timeout;
+extern unsigned int rpc_timeout;
+extern char *preferred_realm;
+
+struct clnt_info {
+ TAILQ_ENTRY(clnt_info) list;
+ int refcount;
+ int wd;
+ bool scanned;
+ char *name;
+ char *relpath;
+ char *servicename;
+ char *servername;
+ int prog;
+ int vers;
+ char *protocol;
+ int krb5_fd;
+ struct event *krb5_ev;
+ int gssd_fd;
+ struct event *gssd_ev;
+ struct sockaddr_storage addr;
+ char *upcall_address;
+ char *upcall_port;
+ int upcall_program;
+ int upcall_vers;
+ char *upcall_protoname;
+ char *upcall_service;
+};
+
+struct clnt_upcall_info {
+ struct clnt_info *clp;
+ uid_t uid;
+ int fd;
+ char *srchost;
+ char *target;
+ char *service;
+};
+
+struct upcall_thread_info {
+ TAILQ_ENTRY(upcall_thread_info) list;
+ pthread_t tid;
+ struct timespec timeout;
+ uid_t uid;
+ int fd;
+ unsigned short flags;
+#define UPCALL_THREAD_CANCELED 0x0001
+#define UPCALL_THREAD_WARNED 0x0002
+};
+
+void handle_krb5_upcall(struct clnt_info *clp);
+void handle_gssd_upcall(struct clnt_info *clp);
+void free_upcall_info(struct clnt_upcall_info *info);
+void gssd_free_client(struct clnt_info *clp);
+int do_error_downcall(int k5_fd, uid_t uid, int err);
+
+
+#endif /* _RPC_GSSD_H_ */
diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man
new file mode 100644
index 0000000..2a5384d
--- /dev/null
+++ b/utils/gssd/gssd.man
@@ -0,0 +1,408 @@
+.\"
+.\" rpc.gssd(8)
+.\"
+.\" Copyright (C) 2003 J. Bruce Fields <bfields@umich.edu>
+.\"
+.TH rpc.gssd 8 "20 Feb 2013"
+.SH NAME
+rpc.gssd \- RPCSEC_GSS daemon
+.SH SYNOPSIS
+.B rpc.gssd
+.RB [ \-DfMnlvrHC ]
+.RB [ \-k
+.IR keytab ]
+.RB [ \-p
+.IR pipefsdir ]
+.RB [ \-d
+.IR ccachedir ]
+.RB [ \-t
+.IR timeout ]
+.RB [ \-T
+.IR timeout ]
+.RB [ \-U
+.IR timeout ]
+.RB [ \-R
+.IR realm ]
+.SH INTRODUCTION
+The RPCSEC_GSS protocol, defined in RFC 5403, is used to provide
+strong security for RPC-based protocols such as NFS.
+.P
+Before exchanging RPC requests using RPCSEC_GSS, an RPC client must
+establish a GSS
+.IR "security context" .
+A security context is shared state on each
+end of a network transport that enables GSS-API security services.
+.P
+Security contexts are established using
+.IR "security credentials" .
+A credential grants temporary access to a secure network service,
+much as a railway ticket grants temporary access to use a rail service.
+.P
+A user typically obtains a credential by providing a password to the
+.BR kinit (1)
+command, or via a PAM library at login time.
+A credential acquired with a
+.I user principal
+is known as a
+.I user credential
+(see
+.BR kerberos (1)
+for more on principals).
+.P
+Certain operations require a credential that
+represents no particular user
+or
+represents the host itself.
+This kind of credential is called a
+.IR "machine credential" .
+.P
+A host establishes its machine credential using a
+.I "service principal"
+whose encrypted password is stored in a local file known as a
+.IR keytab .
+A machine credential remains effective
+without user intervention
+as long as the host can renew it.
+.P
+Once obtained, credentials are typically stored in local temporary files
+with well-known pathnames.
+.SH DESCRIPTION
+To establish GSS security contexts using these credential files,
+the Linux kernel RPC client depends on a userspace daemon called
+.BR rpc.gssd .
+The
+.B rpc.gssd
+daemon uses the rpc_pipefs filesystem to communicate with the kernel.
+.SS User Credentials
+When a user authenticates using a command such as
+.BR kinit (1),
+the resulting credential is stored in a file with a well-known name
+constructed using the user's UID.
+.P
+To interact with an NFS server
+on behalf of a particular Kerberos-authenticated user,
+the Linux kernel RPC client requests that
+.B rpc.gssd
+initialize a security context with the credential
+in that user's credential file.
+.P
+Typically, credential files are placed in
+.IR /tmp .
+However,
+.B rpc.gssd
+can search for credential files in more than one directory.
+See the description of the
+.B -d
+option for details.
+.SS Machine Credentials
+.B rpc.gssd
+searches the default keytab,
+.IR /etc/krb5.keytab ,
+in the following order for a principal and password to use
+when establishing the machine credential.
+For the search, rpc.gssd replaces <hostname> and <REALM> with the local
+system's hostname and Kerberos realm.
+.sp
+ <HOSTNAME>$@<REALM>
+.br
+ root/<hostname>@<REALM>
+.br
+ nfs/<hostname>@<REALM>
+.br
+ host/<hostname>@<REALM>
+.br
+ root/<anyname>@<REALM>
+.br
+ nfs/<anyname>@<REALM>
+.br
+ host/<anyname>@<REALM>
+.sp
+rpc.gssd selects one of the <anyname> entries if it does not find
+a service principal matching the local hostname,
+e.g. if DHCP assigns the local hostname dynamically.
+The <anyname> facility enables the use of the same keytab on multiple systems.
+However, using the same service principal to establish a machine credential
+on multiple hosts can create unwanted security exposures
+and is therefore not recommended.
+.P
+Note that <HOSTNAME>$@<REALM> is a user principal
+that enables Kerberized NFS when the local system is joined
+to an Active Directory domain using Samba.
+The keytab provides the password for this principal.
+.P
+You can specify a different keytab by using the
+.B -k
+option if
+.I /etc/krb5.keytab
+does not exist or does not provide one of these principals.
+.SS Credentials for UID 0
+UID 0 is a special case.
+By default
+.B rpc.gssd
+uses the system's machine credentials for UID 0 accesses
+that require GSS authentication.
+This limits the privileges of the root user
+when accessing network resources that require authentication.
+.P
+Specify the
+.B -n
+option when starting
+.B rpc.gssd
+if you'd like to force the root user to obtain a user credential
+rather than use the local system's machine credential.
+.P
+When
+.B -n
+is specified,
+the kernel continues to request a GSS context established
+with a machine credential for NFSv4 operations,
+such as SETCLIENTID or RENEW, that manage state.
+If
+.B rpc.gssd
+cannot obtain a machine credential (say, the local system has
+no keytab), NFSv4 operations that require machine credentials will fail.
+.SS Encryption types
+A realm administrator can choose to add keys encoded in a number of different
+encryption types to the local system's keytab.
+For instance, a host/ principal might have keys for the
+.BR aes256-cts-hmac-sha1-96 ,
+.BR aes128-cts-hmac-sha1-96 ,
+.BR des3-cbc-sha1 ", and"
+.BR arcfour-hmac " encryption types."
+This permits
+.B rpc.gssd
+to choose an appropriate encryption type that the target NFS server
+supports.
+.P
+These encryption types are stronger than legacy single-DES encryption types.
+To interoperate in environments where servers support
+only weak encryption types,
+you can restrict your client to use only single-DES encryption types
+by specifying the
+.B -l
+option when starting
+.BR rpc.gssd .
+.SH OPTIONS
+.TP
+.B \-D
+The server name passed to GSSAPI for authentication is normally the
+name exactly as requested. e.g. for NFS
+it is the server name in the "servername:/path" mount request. Only if this
+servername appears to be an IP address (IPv4 or IPv6) or an
+unqualified name (no dots) will a reverse DNS lookup
+will be performed to get the canoncial server name.
+
+If
+.B \-D
+is present, a reverse DNS lookup will
+.I always
+be used, even if the server name looks like a canonical name. So it
+is needed if partially qualified, or non canonical names are regularly
+used.
+
+Using
+.B \-D
+can introduce a security vulnerability, so it is recommended that
+.B \-D
+not be used, and that canonical names always be used when requesting
+services.
+.TP
+.B -f
+Runs
+.B rpc.gssd
+in the foreground and sends output to stderr (as opposed to syslogd)
+.TP
+.B -n
+When specified, UID 0 is forced to obtain user credentials
+which are used instead of the local system's machine credentials.
+.TP
+.BI "-k " keytab
+Tells
+.B rpc.gssd
+to use the keys found in
+.I keytab
+to obtain machine credentials.
+The default value is
+.IR /etc/krb5.keytab .
+.TP
+.B -l
+When specified, restricts
+.B rpc.gssd
+to sessions to weak encryption types such as
+.BR des-cbc-crc .
+This option is available only when the local system's Kerberos library
+supports settable encryption types.
+.TP
+.BI "-p " path
+Tells
+.B rpc.gssd
+where to look for the rpc_pipefs filesystem. The default value is
+.IR /var/lib/nfs/rpc_pipefs .
+.TP
+.BI "-d " search-path
+This option specifies a colon separated list of directories that
+.B rpc.gssd
+searches for credential files. The default value is
+.IR /tmp:/run/user/%U .
+The literal sequence "%U" can be specified to substitue the UID
+of the user for whom credentials are being searched.
+.TP
+.B -M
+By default, machine credentials are stored in files in the first
+directory in the credential directory search path (see the
+.B -d
+option). When
+.B -M
+is set,
+.B rpc.gssd
+stores machine credentials in memory instead.
+.TP
+.B -v
+Increases the verbosity of the output (can be specified multiple times).
+.TP
+.B -r
+If the RPCSEC_GSS library supports setting debug level,
+increases the verbosity of the output (can be specified multiple times).
+.TP
+.BI "-R " realm
+Kerberos tickets from this
+.I realm
+will be preferred when scanning available credentials cache files to be
+used to create a context. By default, the default realm, as configured
+in the Kerberos configuration file, is preferred.
+.TP
+.BI "-t " timeout
+Timeout, in seconds, for kernel GSS contexts. This option allows you to force
+new kernel contexts to be negotiated after
+.I timeout
+seconds, which allows changing Kerberos tickets and identities frequently.
+The default is no explicit timeout, which means the kernel context will live
+the lifetime of the Kerberos service ticket used in its creation.
+.TP
+.BI "-T " timeout
+Timeout, in seconds, to create an RPC connection with a server while
+establishing an authenticated gss context for a user.
+The default timeout is set to 5 seconds.
+If you get messages like "WARNING: can't create tcp rpc_clnt to server
+%servername% for user with uid %uid%: RPC: Remote system error -
+Connection timed out", you should consider an increase of this timeout.
+.TP
+.BI "-U " timeout
+Timeout, in seconds, for upcall threads. Threads executing longer than
+.I timeout
+seconds will cause an error message to be logged. The default
+.I timeout
+is 30 seconds. The minimum is 5 seconds. The maximum is 600 seconds.
+.TP
+.B -C
+In addition to logging an error message for threads that have timed out,
+the thread will be canceled and an error of -ETIMEDOUT will be reported
+to the kernel.
+.TP
+.B -H
+Avoids setting $HOME to "/". This allows rpc.gssd to read per user k5identity
+files versus trying to read /.k5identity for each user.
+
+If
+.B \-H
+is not set, rpc.gssd will use the first match found in
+/var/kerberos/krb5/user/$EUID/client.keytab and will not use a principal based on
+host and/or service parameters listed in $HOME/.k5identity.
+.SH CONFIGURATION FILE
+Many of the options that can be set on the command line can also be
+controlled through values set in the
+.B [gssd]
+section of the
+.I /etc/nfs.conf
+configuration file. Values recognized include:
+.TP
+.B verbosity
+Value which is equivalent to the number of
+.BR -v .
+.TP
+.B rpc-verbosity
+Value which is equivalent to the number of
+.BR -r .
+.TP
+.B use-memcache
+A Boolean flag equivalent to
+.BR -M .
+.TP
+.B use-machine-creds
+A Boolean flag. Setting to
+.B false
+is equivalent to giving the
+.B -n
+flag.
+.TP
+.B avoid-dns
+Setting to
+.B false
+is equivalent to providing the
+.B -D
+flag.
+.TP
+.B limit-to-legacy-enctypes
+Equivalent to
+.BR -l .
+.TP
+.B context-timeout
+Equivalent to
+.BR -t .
+.TP
+.B rpc-timeout
+Equivalent to
+.BR -T .
+.TP
+.B keytab-file
+Equivalent to
+.BR -k .
+.TP
+.BR cred-cache-directory
+Equivalent to
+.BR -d .
+.TP
+.B preferred-realm
+Equivalent to
+.BR -R .
+.TP
+.B upcall-timeout
+Equivalent to
+.BR -U .
+.TP
+.B cancel-timed-out-upcalls
+Setting to
+.B true
+is equivalent to providing the
+.B -C
+flag.
+.TP
+.B set-home
+Setting to
+.B false
+is equivalent to providing the
+.B -H
+flag.
+.P
+In addtion, the following value is recognized from the
+.B [general]
+section:
+.TP
+.B pipefs-directory
+Equivalent to
+.BR -p .
+
+.SH SEE ALSO
+.BR rpc.svcgssd (8),
+.BR kerberos (1),
+.BR kinit (1),
+.BR krb5.conf (5)
+.SH AUTHORS
+.br
+Dug Song <dugsong@umich.edu>
+.br
+Andy Adamson <andros@umich.edu>
+.br
+Marius Aamodt Eriksen <marius@umich.edu>
+.br
+J. Bruce Fields <bfields@umich.edu>
diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
new file mode 100644
index 0000000..a96647d
--- /dev/null
+++ b/utils/gssd/gssd_proc.c
@@ -0,0 +1,1075 @@
+/*
+ gssd_proc.c
+
+ Copyright (c) 2000-2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
+ Copyright (c) 2001 Andy Adamson <andros@UMICH.EDU>.
+ Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
+ Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
+ Copyright (c) 2004 Kevin Coffman <kwc@umich.edu>
+ Copyright (c) 2014 David H?rdeman <david@hardeman.nu>
+ All rights reserved, all wrongs reversed.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <sys/param.h>
+#include <rpc/rpc.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/fsuid.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <dirent.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <gssapi/gssapi.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <syscall.h>
+
+#include "gssd.h"
+#include "err_util.h"
+#include "gss_util.h"
+#include "krb5_util.h"
+#include "context.h"
+#include "nfsrpc.h"
+#include "nfslib.h"
+#include "gss_names.h"
+
+extern pthread_mutex_t clp_lock;
+extern pthread_mutex_t active_thread_list_lock;
+extern int upcall_timeout;
+extern TAILQ_HEAD(active_thread_list_head, upcall_thread_info) active_thread_list;
+
+/* Encryption types supported by the kernel rpcsec_gss code */
+int num_krb5_enctypes = 0;
+krb5_enctype *krb5_enctypes = NULL;
+
+/* Args for the cleanup_handler() */
+struct cleanup_args {
+ OM_uint32 *min_stat;
+ gss_buffer_t acceptor;
+ gss_buffer_t token;
+ struct authgss_private_data *pd;
+ AUTH **auth;
+ CLIENT **rpc_clnt;
+};
+
+/*
+ * Parse the supported encryption type information
+ */
+static int
+parse_enctypes(char *enctypes)
+{
+ int n = 0;
+ char *curr, *comma;
+ int i;
+ static char *cached_types;
+
+ if (cached_types && strcmp(cached_types, enctypes) == 0)
+ return 0;
+ free(cached_types);
+
+ if (krb5_enctypes != NULL) {
+ free(krb5_enctypes);
+ krb5_enctypes = NULL;
+ num_krb5_enctypes = 0;
+ }
+
+ /* count the number of commas */
+ for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) {
+ comma = strchr(curr, ',');
+ if (comma != NULL)
+ n++;
+ else
+ break;
+ }
+ /* If no more commas and we're not at the end, there's one more value */
+ if (*curr != '\0')
+ n++;
+
+ /* Empty string, return an error */
+ if (n == 0)
+ return ENOENT;
+
+ /* Allocate space for enctypes array */
+ if ((krb5_enctypes = (int *) calloc(n, sizeof(int))) == NULL) {
+ return ENOMEM;
+ }
+
+ /* Now parse each value into the array */
+ for (curr = enctypes, i = 0; curr && *curr != '\0'; curr = ++comma) {
+ krb5_enctypes[i++] = atoi(curr);
+ comma = strchr(curr, ',');
+ if (comma == NULL)
+ break;
+ }
+
+ num_krb5_enctypes = n;
+ if ((cached_types = malloc(strlen(enctypes)+1)))
+ strcpy(cached_types, enctypes);
+
+ return 0;
+}
+
+static void
+do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
+ gss_buffer_desc *context_token, OM_uint32 lifetime_rec,
+ gss_buffer_desc *acceptor)
+{
+ char *buf = NULL, *p = NULL, *end = NULL;
+ unsigned int timeout = context_timeout;
+ unsigned int buf_size = 0;
+ pthread_t tid = pthread_self();
+
+ if (get_verbosity() > 1)
+ printerr(2, "do_downcall(0x%lx): lifetime_rec=%s acceptor=%.*s\n",
+ tid, sec2time(lifetime_rec), acceptor->length, acceptor->value);
+ buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
+ sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
+ sizeof(context_token->length) + context_token->length +
+ sizeof(acceptor->length) + acceptor->length;
+ p = buf = malloc(buf_size);
+ if (!buf)
+ goto out_err;
+
+ end = buf + buf_size;
+
+ /* context_timeout set by -t option overrides context lifetime */
+ if (timeout == 0)
+ timeout = lifetime_rec;
+ if (WRITE_BYTES(&p, end, uid)) goto out_err;
+ if (WRITE_BYTES(&p, end, timeout)) goto out_err;
+ if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
+ if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
+ if (write_buffer(&p, end, context_token)) goto out_err;
+ if (write_buffer(&p, end, acceptor)) goto out_err;
+
+ if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
+ free(buf);
+ return;
+out_err:
+ free(buf);
+ printerr(1, "do_downcall(0x%lx): Failed to write downcall!\n", tid);
+ return;
+}
+
+int
+do_error_downcall(int k5_fd, uid_t uid, int err)
+{
+ char buf[1024];
+ char *p = buf, *end = buf + 1024;
+ unsigned int timeout = 0;
+ int zero = 0;
+ pthread_t tid = pthread_self();
+
+ printerr(2, "do_error_downcall(0x%lx): uid %d err %d\n", tid, uid, err);
+
+ if (WRITE_BYTES(&p, end, uid)) goto out_err;
+ if (WRITE_BYTES(&p, end, timeout)) goto out_err;
+ /* use seq_win = 0 to indicate an error: */
+ if (WRITE_BYTES(&p, end, zero)) goto out_err;
+ if (WRITE_BYTES(&p, end, err)) goto out_err;
+
+ if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
+ return 0;
+out_err:
+ printerr(1, "Failed to write error downcall!\n");
+ return -1;
+}
+
+/*
+ * If the port isn't already set, do an rpcbind query to the remote server
+ * using the program and version and get the port.
+ *
+ * Newer kernels send the value of the port= mount option in the "info"
+ * file for the upcall or '0' for NFSv2/3. For NFSv4 it sends the value
+ * of the port= option or '2049'. The port field in a new sockaddr should
+ * reflect the value that was sent by the kernel.
+ */
+static int
+populate_port(struct sockaddr *sa, const socklen_t salen,
+ const rpcprog_t program, const rpcvers_t version,
+ const unsigned short protocol)
+{
+ struct sockaddr_in *s4 = (struct sockaddr_in *) sa;
+#ifdef IPV6_SUPPORTED
+ struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa;
+#endif /* IPV6_SUPPORTED */
+ unsigned short port;
+
+ /*
+ * Newer kernels send the port in the upcall. If we already have
+ * the port, there's no need to look it up.
+ */
+ switch (sa->sa_family) {
+ case AF_INET:
+ if (s4->sin_port != 0) {
+ printerr(4, "DEBUG: port already set to %d\n",
+ ntohs(s4->sin_port));
+ return 1;
+ }
+ break;
+#ifdef IPV6_SUPPORTED
+ case AF_INET6:
+ if (s6->sin6_port != 0) {
+ printerr(4, "DEBUG: port already set to %d\n",
+ ntohs(s6->sin6_port));
+ return 1;
+ }
+ break;
+#endif /* IPV6_SUPPORTED */
+ default:
+ printerr(0, "ERROR: unsupported address family %d\n",
+ sa->sa_family);
+ return 0;
+ }
+
+ /*
+ * Newer kernels that send the port in the upcall set the value to
+ * 2049 for NFSv4 mounts when one isn't specified. The check below is
+ * only for kernels that don't send the port in the upcall. For those
+ * we either have to do an rpcbind query or set it to the standard
+ * port. Doing a query could be problematic (firewalls, etc), so take
+ * the latter approach.
+ */
+ if (program == 100003 && version == 4) {
+ port = 2049;
+ goto set_port;
+ }
+
+ port = nfs_getport(sa, salen, program, version, protocol);
+ if (!port) {
+ printerr(0, "ERROR: unable to obtain port for prog %ld "
+ "vers %ld\n", program, version);
+ return 0;
+ }
+
+set_port:
+ printerr(2, "DEBUG: setting port to %hu for prog %lu vers %lu\n", port,
+ program, version);
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ s4->sin_port = htons(port);
+ break;
+#ifdef IPV6_SUPPORTED
+ case AF_INET6:
+ s6->sin6_port = htons(port);
+ break;
+#endif /* IPV6_SUPPORTED */
+ }
+
+ return 1;
+}
+
+/*
+ * Create an RPC connection and establish an authenticated
+ * gss context with a server.
+ */
+static int
+create_auth_rpc_client(struct clnt_info *clp,
+ char *tgtname,
+ CLIENT **clnt_return,
+ AUTH **auth_return,
+ uid_t uid,
+ int authtype,
+ gss_cred_id_t cred)
+{
+ CLIENT *rpc_clnt = NULL;
+ struct rpc_gss_sec sec;
+ AUTH *auth = NULL;
+ int retval = -1;
+ OM_uint32 min_stat;
+ char rpc_errmsg[1024];
+ int protocol;
+ struct timeval timeout;
+ struct sockaddr *addr = (struct sockaddr *) &clp->addr;
+ socklen_t salen;
+ pthread_t tid = pthread_self();
+
+ sec.qop = GSS_C_QOP_DEFAULT;
+ sec.svc = RPCSEC_GSS_SVC_NONE;
+ sec.cred = cred;
+ sec.req_flags = 0;
+ if (authtype == AUTHTYPE_KRB5) {
+ sec.mech = (gss_OID)&krb5oid;
+ sec.req_flags = GSS_C_MUTUAL_FLAG;
+ }
+ else {
+ printerr(0, "ERROR: Invalid authentication type (%d) "
+ "in create_auth_rpc_client\n", authtype);
+ goto out_fail;
+ }
+
+
+ if (authtype == AUTHTYPE_KRB5) {
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+ /*
+ * Do this before creating rpc connection since we won't need
+ * rpc connection if it fails!
+ */
+ if (limit_krb5_enctypes(&sec)) {
+ printerr(1, "WARNING: Failed while limiting krb5 "
+ "encryption types for user with uid %d\n",
+ uid);
+ goto out_fail;
+ }
+#endif
+ }
+
+ /* create an rpc connection to the nfs server */
+
+ printerr(3, "create_auth_rpc_client(0x%lx): creating %s client for server %s\n",
+ tid, clp->protocol, clp->servername);
+
+ protocol = IPPROTO_TCP;
+ if ((strcmp(clp->protocol, "udp")) == 0)
+ protocol = IPPROTO_UDP;
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ salen = sizeof(struct sockaddr_in);
+ break;
+#ifdef IPV6_SUPPORTED
+ case AF_INET6:
+ salen = sizeof(struct sockaddr_in6);
+ break;
+#endif /* IPV6_SUPPORTED */
+ default:
+ printerr(1, "ERROR: Unknown address family %d\n",
+ addr->sa_family);
+ goto out_fail;
+ }
+
+ if (!populate_port(addr, salen, clp->prog, clp->vers, protocol))
+ goto out_fail;
+
+ /* set the timeout according to the requested valued */
+ timeout.tv_sec = (long) rpc_timeout;
+ timeout.tv_usec = (long) 0;
+
+ rpc_clnt = nfs_get_rpcclient(addr, salen, protocol, clp->prog,
+ clp->vers, &timeout);
+ if (!rpc_clnt) {
+ snprintf(rpc_errmsg, sizeof(rpc_errmsg),
+ "WARNING: can't create %s rpc_clnt to server %s for "
+ "user with uid %d",
+ protocol == IPPROTO_TCP ? "tcp" : "udp",
+ clp->servername, uid);
+ printerr(0, "%s\n",
+ clnt_spcreateerror(rpc_errmsg));
+ goto out_fail;
+ }
+ if (!tgtname)
+ tgtname = clp->servicename;
+
+ printerr(3, "create_auth_rpc_client(0x%lx): creating context with server %s\n",
+ tid, tgtname);
+ auth = authgss_create_default(rpc_clnt, tgtname, &sec);
+ if (!auth) {
+ if (sec.minor_status == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
+ printerr(2, "WARNING: server=%s failed context "
+ "creation with KRB5_AP_ERR_BAD_INTEGRITY\n",
+ clp->servername);
+ if (cred == GSS_C_NO_CREDENTIAL)
+ retval = gssd_refresh_krb5_machine_credential(clp->servername,
+ "*", NULL, 1);
+ else
+ retval = gssd_k5_remove_bad_service_cred(clp->servername);
+ if (!retval) {
+ auth = authgss_create_default(rpc_clnt, tgtname,
+ &sec);
+ if (auth)
+ goto success;
+ }
+ }
+ /* Our caller should print appropriate message */
+ printerr(2, "WARNING: Failed to create krb5 context for "
+ "user with uid %d for server %s\n",
+ uid, tgtname);
+ goto out_fail;
+ }
+success:
+ /* Success !!! */
+ rpc_clnt->cl_auth = auth;
+ *clnt_return = rpc_clnt;
+ *auth_return = auth;
+ retval = 0;
+
+ out:
+ if (sec.cred != GSS_C_NO_CREDENTIAL)
+ gss_release_cred(&min_stat, &sec.cred);
+ return retval;
+
+ out_fail:
+ /* Only destroy here if failure. Otherwise, caller is responsible */
+ if (rpc_clnt) clnt_destroy(rpc_clnt);
+
+ goto out;
+}
+
+/*
+ * Create the context as the user (not as root).
+ *
+ * Note that we change the *real* uid here, as changing the effective uid is
+ * not sufficient. This is due to an unfortunate historical error in the MIT
+ * krb5 libs, where they used %{uid} in the default_ccache_name. Changing that
+ * now might break some applications so we're sort of stuck with it.
+ *
+ * Unfortunately, doing this leaves the forked child vulnerable to signals and
+ * renicing, but this is the best we can do. In the event that a child is
+ * signalled before downcalling, the kernel will just eventually time out the
+ * upcall attempt.
+ */
+static int
+change_identity(uid_t uid)
+{
+ struct passwd *pw;
+ int res;
+
+ /* drop list of supplimentary groups first */
+#ifdef __NR_setgroups32
+ if (syscall(SYS_setgroups32, 0, 0) != 0) {
+#else
+ if (syscall(SYS_setgroups, 0, 0) != 0) {
+#endif
+ printerr(0, "WARNING: unable to drop supplimentary groups!");
+ return errno;
+ }
+
+ /* try to get pwent for user */
+ pw = getpwuid(uid);
+ if (!pw) {
+ /* if that doesn't work, try to get one for "nobody" */
+ errno = 0;
+ pw = getpwnam("nobody");
+ if (!pw) {
+ printerr(0, "WARNING: unable to determine gid for uid %u\n", uid);
+ return errno ? errno : ENOENT;
+ }
+ }
+
+ /* Switch the UIDs and GIDs. */
+ /* For the threaded version we have to set uid,gid per thread instead
+ * of per process. glibc setresuid() when called from a thread, it'll
+ * send a signal to all other threads to synchronize the uid in all
+ * other threads. To bypass this, we have to call syscall() directly.
+ */
+#ifdef __NR_setresgid32
+ res = syscall(SYS_setresgid32, pw->pw_gid, pw->pw_gid, pw->pw_gid);
+#else
+ res = syscall(SYS_setresgid, pw->pw_gid, pw->pw_gid, pw->pw_gid);
+#endif
+ if (res != 0) {
+ printerr(0, "WARNING: failed to set gid to %u!\n", pw->pw_gid);
+ return errno;
+ }
+
+#ifdef __NR_setresuid32
+ res = syscall(SYS_setresuid32, uid, uid, uid);
+#else
+ res = syscall(SYS_setresuid, uid, uid, uid);
+#endif
+ if (res != 0) {
+ printerr(0, "WARNING: Failed to setuid for user with uid %u\n", uid);
+ return errno;
+ }
+
+ return 0;
+}
+
+static AUTH *
+krb5_not_machine_creds(struct clnt_info *clp, uid_t uid, char *tgtname,
+ int *downcall_err, int *chg_err, CLIENT **rpc_clnt)
+{
+ AUTH *auth = NULL;
+ gss_cred_id_t gss_cred;
+ char **dname;
+ int err, resp = -1;
+ pthread_t tid = pthread_self();
+
+ printerr(2, "krb5_not_machine_creds(0x%lx): uid %d tgtname %s\n",
+ tid, uid, tgtname);
+
+ *chg_err = change_identity(uid);
+ if (*chg_err) {
+ printerr(0, "WARNING: failed to change identity: %s",
+ strerror(*chg_err));
+ goto out;
+ }
+
+ /** Tell krb5 gss which credentials cache to use.
+ * Try first to acquire credentials directly via GSSAPI
+ */
+ err = gssd_acquire_user_cred(&gss_cred);
+ if (err == 0)
+ resp = create_auth_rpc_client(clp, tgtname, rpc_clnt,
+ &auth, uid,
+ AUTHTYPE_KRB5, gss_cred);
+
+ /** if create_auth_rplc_client fails try the traditional
+ * method of trolling for credentials
+ */
+ for (dname = ccachesearch; resp != 0 && *dname != NULL; dname++) {
+ err = gssd_setup_krb5_user_gss_ccache(uid, clp->servername,
+ *dname);
+ if (err == -EKEYEXPIRED)
+ *downcall_err = -EKEYEXPIRED;
+ else if (err == 0)
+ resp = create_auth_rpc_client(clp, tgtname, rpc_clnt,
+ &auth, uid,AUTHTYPE_KRB5,
+ GSS_C_NO_CREDENTIAL);
+ }
+
+out:
+ return auth;
+}
+
+static AUTH *
+krb5_use_machine_creds(struct clnt_info *clp, uid_t uid,
+ char *srchost, char *tgtname, char *service,
+ CLIENT **rpc_clnt)
+{
+ AUTH *auth = NULL;
+ char **credlist = NULL;
+ char **ccname;
+ int nocache = 0;
+ int success = 0;
+ pthread_t tid = pthread_self();
+
+ printerr(2, "krb5_use_machine_creds(0x%lx): uid %d tgtname %s\n",
+ tid, uid, tgtname);
+
+ do {
+ gssd_refresh_krb5_machine_credential(clp->servername,
+ service, srchost, 0);
+ /*
+ * Get a list of credential cache names and try each
+ * of them until one works or we've tried them all
+ */
+ if (gssd_get_krb5_machine_cred_list(&credlist)) {
+ printerr(0, "ERROR: No credentials found "
+ "for connection to server %s\n",
+ clp->servername);
+ goto out;
+ }
+ for (ccname = credlist; ccname && *ccname; ccname++) {
+ u_int min_stat;
+
+ if (gss_krb5_ccache_name(&min_stat, *ccname, NULL) !=
+ GSS_S_COMPLETE) {
+ printerr(1, "WARNING: gss_krb5_ccache_name "
+ "with name '%s' failed (%s)\n",
+ *ccname, error_message(min_stat));
+ continue;
+ }
+ if ((create_auth_rpc_client(clp, tgtname, rpc_clnt,
+ &auth, uid,
+ AUTHTYPE_KRB5,
+ GSS_C_NO_CREDENTIAL)) == 0) {
+ /* Success! */
+ success++;
+ break;
+ }
+ printerr(2, "WARNING: Failed to create machine krb5 "
+ "context with cred cache %s for server %s\n",
+ *ccname, clp->servername);
+ }
+ gssd_free_krb5_machine_cred_list(credlist);
+ if (!success) {
+ if(nocache == 0) {
+ nocache++;
+ printerr(2, "WARNING: Machine cache prematurely "
+ "expired or corrupted trying to "
+ "recreate cache for server %s\n",
+ clp->servername);
+ } else {
+ printerr(1, "ERROR: Failed to create machine "
+ "krb5 context with any credentials "
+ "cache for server %s\n",
+ clp->servername);
+ goto out;
+ }
+ }
+ } while(!success);
+
+out:
+ return auth;
+}
+
+/*
+ * cleanup_handler:
+ *
+ * Free any resources allocated by process_krb5_upcall().
+ *
+ * Runs upon normal termination of process_krb5_upcall as well as if the
+ * thread is canceled.
+ */
+static void
+cleanup_handler(void *arg)
+{
+ struct cleanup_args *args = (struct cleanup_args *)arg;
+
+ gss_release_buffer(args->min_stat, args->acceptor);
+ if (args->token->value)
+ free(args->token->value);
+#ifdef HAVE_AUTHGSS_FREE_PRIVATE_DATA
+ if (args->pd->pd_ctx_hndl.length != 0 || args->pd->pd_ctx != 0)
+ authgss_free_private_data(args->pd);
+#endif
+ if (*args->auth)
+ AUTH_DESTROY(*args->auth);
+ if (*args->rpc_clnt)
+ clnt_destroy(*args->rpc_clnt);
+}
+
+/*
+ * process_krb5_upcall:
+ *
+ * this code uses the userland rpcsec gss library to create a krb5
+ * context on behalf of the kernel
+ *
+ * This is the meat of the upcall thread. Note that cancelability is disabled
+ * and enabled at various points to ensure that any resources reserved by the
+ * lower level libraries are released safely.
+ */
+static void
+process_krb5_upcall(struct clnt_upcall_info *info)
+{
+ struct clnt_info *clp = info->clp;
+ uid_t uid = info->uid;
+ int fd = info->fd;
+ char *srchost = info->srchost;
+ char *tgtname = info->target;
+ char *service = info->service;
+ CLIENT *rpc_clnt = NULL;
+ AUTH *auth = NULL;
+ struct authgss_private_data pd;
+ gss_buffer_desc token;
+ int err, downcall_err;
+ OM_uint32 maj_stat, min_stat, lifetime_rec;
+ gss_name_t gacceptor = GSS_C_NO_NAME;
+ gss_OID mech;
+ gss_buffer_desc acceptor = {0};
+ struct cleanup_args cleanup_args = {&min_stat, &acceptor, &token, &pd, &auth, &rpc_clnt};
+
+ token.length = 0;
+ token.value = NULL;
+ memset(&pd, 0, sizeof(struct authgss_private_data));
+
+ pthread_cleanup_push(cleanup_handler, &cleanup_args);
+ /*
+ * If "service" is specified, then the kernel is indicating that
+ * we must use machine credentials for this request. (Regardless
+ * of the uid value or the setting of root_uses_machine_creds.)
+ * If the service value is "*", then any service name can be used.
+ * Otherwise, it specifies the service name that should be used.
+ * (For now, the values of service will only be "*" or "nfs".)
+ *
+ * Restricting gssd to use "nfs" service name is needed for when
+ * the NFS server is doing a callback to the NFS client. In this
+ * case, the NFS server has to authenticate itself as "nfs" --
+ * even if there are other service keys such as "host" or "root"
+ * in the keytab.
+ *
+ * Another case when the kernel may specify the service attribute
+ * is when gssd is being asked to create the context for a
+ * SETCLIENT_ID operation. In this case, machine credentials
+ * must be used for the authentication. However, the service name
+ * used for this case is not important.
+ *
+ */
+ downcall_err = -EACCES;
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 &&
+ service == NULL)) {
+
+ auth = krb5_not_machine_creds(clp, uid, tgtname, &downcall_err,
+ &err, &rpc_clnt);
+ if (err)
+ goto out_return_error;
+ }
+ if (auth == NULL) {
+ if (uid == 0 && (root_uses_machine_creds == 1 ||
+ service != NULL)) {
+ auth = krb5_use_machine_creds(clp, uid, srchost, tgtname,
+ service, &rpc_clnt);
+ if (auth == NULL)
+ goto out_return_error;
+ } else {
+ /* krb5_not_machine_creds logs the error */
+ goto out_return_error;
+ }
+ }
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_testcancel();
+
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ if (!authgss_get_private_data(auth, &pd)) {
+ printerr(1, "WARNING: Failed to obtain authentication "
+ "data for user with uid %d for server %s\n",
+ uid, clp->servername);
+ goto out_return_error;
+ }
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_testcancel();
+
+ /* Grab the context lifetime and acceptor name out of the ctx. */
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ maj_stat = gss_inquire_context(&min_stat, pd.pd_ctx, NULL, &gacceptor,
+ &lifetime_rec, &mech, NULL, NULL, NULL);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ printerr(1, "WARNING: Failed to inquire context "
+ "maj_stat (0x%x)\n", maj_stat);
+ lifetime_rec = 0;
+ } else {
+ get_hostbased_client_buffer(gacceptor, mech, &acceptor);
+ gss_release_name(&min_stat, &gacceptor);
+ }
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_testcancel();
+
+ /*
+ * The serialization can mean turning pd.pd_ctx into a lucid context. If
+ * that happens then the pd.pd_ctx will be unusable, so we must never
+ * try to use it after this point.
+ */
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ if (serialize_context_for_kernel(&pd.pd_ctx, &token, &krb5oid, NULL)) {
+ printerr(1, "WARNING: Failed to serialize krb5 context for "
+ "user with uid %d for server %s\n",
+ uid, clp->servername);
+ goto out_return_error;
+ }
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_testcancel();
+
+ do_downcall(fd, uid, &pd, &token, lifetime_rec, &acceptor);
+
+out:
+ pthread_cleanup_pop(1);
+
+ return;
+
+out_return_error:
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_testcancel();
+
+ do_error_downcall(fd, uid, downcall_err);
+ goto out;
+}
+
+static struct clnt_upcall_info *
+alloc_upcall_info(struct clnt_info *clp, uid_t uid, int fd, char *srchost,
+ char *target, char *service)
+{
+ struct clnt_upcall_info *info;
+
+ info = malloc(sizeof(struct clnt_upcall_info));
+ if (info == NULL)
+ return NULL;
+
+ memset(info, 0, sizeof(*info));
+ pthread_mutex_lock(&clp_lock);
+ clp->refcount++;
+ pthread_mutex_unlock(&clp_lock);
+ info->clp = clp;
+ info->uid = uid;
+ info->fd = fd;
+ if (srchost) {
+ info->srchost = strdup(srchost);
+ if (info->srchost == NULL)
+ goto out_info;
+ }
+ if (target) {
+ info->target = strdup(target);
+ if (info->target == NULL)
+ goto out_srchost;
+ }
+ if (service) {
+ info->service = strdup(service);
+ if (info->service == NULL)
+ goto out_target;
+ }
+
+out:
+ return info;
+
+out_target:
+ if (info->target)
+ free(info->target);
+out_srchost:
+ if (info->srchost)
+ free(info->srchost);
+out_info:
+ free(info);
+ info = NULL;
+ goto out;
+}
+
+void free_upcall_info(struct clnt_upcall_info *info)
+{
+ gssd_free_client(info->clp);
+ if (info->service)
+ free(info->service);
+ if (info->target)
+ free(info->target);
+ if (info->srchost)
+ free(info->srchost);
+ free(info);
+}
+
+static void
+cleanup_clnt_upcall_info(void *arg)
+{
+ struct clnt_upcall_info *info = (struct clnt_upcall_info *)arg;
+
+ free_upcall_info(info);
+}
+
+static void
+gssd_work_thread_fn(struct clnt_upcall_info *info)
+{
+ pthread_cleanup_push(cleanup_clnt_upcall_info, info);
+ process_krb5_upcall(info);
+ pthread_cleanup_pop(1);
+}
+
+static struct upcall_thread_info *
+alloc_upcall_thread_info(void)
+{
+ struct upcall_thread_info *info;
+
+ info = malloc(sizeof(struct upcall_thread_info));
+ if (info == NULL)
+ return NULL;
+ memset(info, 0, sizeof(*info));
+ return info;
+}
+
+static int
+start_upcall_thread(void (*func)(struct clnt_upcall_info *), struct clnt_upcall_info *info)
+{
+ pthread_attr_t attr;
+ pthread_t th;
+ struct upcall_thread_info *tinfo;
+ int ret;
+ pthread_t tid = pthread_self();
+
+ tinfo = alloc_upcall_thread_info();
+ if (!tinfo)
+ return -ENOMEM;
+ tinfo->fd = info->fd;
+ tinfo->uid = info->uid;
+
+ ret = pthread_attr_init(&attr);
+ if (ret != 0) {
+ printerr(0, "ERROR: failed to init pthread attr: ret %d: %s\n",
+ ret, strerror(errno));
+ free(tinfo);
+ return ret;
+ }
+
+ ret = pthread_create(&th, &attr, (void *)func, (void *)info);
+ if (ret != 0) {
+ printerr(0, "ERROR: pthread_create failed: ret %d: %s\n",
+ ret, strerror(errno));
+ free(tinfo);
+ return ret;
+ }
+ printerr(2, "start_upcall_thread(0x%lx): created thread id 0x%lx\n",
+ tid, th);
+
+ tinfo->tid = th;
+ pthread_mutex_lock(&active_thread_list_lock);
+ clock_gettime(CLOCK_MONOTONIC, &tinfo->timeout);
+ tinfo->timeout.tv_sec += upcall_timeout;
+ TAILQ_INSERT_TAIL(&active_thread_list, tinfo, list);
+ pthread_mutex_unlock(&active_thread_list_lock);
+
+ return ret;
+}
+
+void
+handle_krb5_upcall(struct clnt_info *clp)
+{
+ uid_t uid;
+ struct clnt_upcall_info *info;
+ int err;
+
+ if (read(clp->krb5_fd, &uid, sizeof(uid)) < (ssize_t)sizeof(uid)) {
+ printerr(0, "WARNING: failed reading uid from krb5 "
+ "upcall pipe: %s\n", strerror(errno));
+ return;
+ }
+ printerr(2, "\n%s: uid %d (%s)\n", __func__, uid, clp->relpath);
+
+ info = alloc_upcall_info(clp, uid, clp->krb5_fd, NULL, NULL, NULL);
+ if (info == NULL) {
+ printerr(0, "%s: failed to allocate clnt_upcall_info\n", __func__);
+ do_error_downcall(clp->krb5_fd, uid, -EACCES);
+ return;
+ }
+ err = start_upcall_thread(gssd_work_thread_fn, info);
+ if (err != 0) {
+ do_error_downcall(clp->krb5_fd, uid, -EACCES);
+ free_upcall_info(info);
+ }
+}
+
+void
+handle_gssd_upcall(struct clnt_info *clp)
+{
+ uid_t uid;
+ char lbuf[RPC_CHAN_BUF_SIZE];
+ int lbuflen = 0;
+ char *p;
+ char *mech = NULL;
+ char *uidstr = NULL;
+ char *target = NULL;
+ char *service = NULL;
+ char *srchost = NULL;
+ char *enctypes = NULL;
+ pthread_t tid = pthread_self();
+ struct clnt_upcall_info *info;
+ int err;
+
+ lbuflen = read(clp->gssd_fd, lbuf, sizeof(lbuf));
+ if (lbuflen <= 0 || lbuf[lbuflen-1] != '\n') {
+ printerr(0, "WARNING: handle_gssd_upcall: "
+ "failed reading request\n");
+ return;
+ }
+ lbuf[lbuflen-1] = 0;
+
+ printerr(2, "\n%s(0x%lx): '%s' (%s)\n", __func__, tid,
+ lbuf, clp->relpath);
+
+ for (p = strtok(lbuf, " "); p; p = strtok(NULL, " ")) {
+ if (!strncmp(p, "mech=", strlen("mech=")))
+ mech = p + strlen("mech=");
+ else if (!strncmp(p, "uid=", strlen("uid=")))
+ uidstr = p + strlen("uid=");
+ else if (!strncmp(p, "enctypes=", strlen("enctypes=")))
+ enctypes = p + strlen("enctypes=");
+ else if (!strncmp(p, "target=", strlen("target=")))
+ target = p + strlen("target=");
+ else if (!strncmp(p, "service=", strlen("service=")))
+ service = p + strlen("service=");
+ else if (!strncmp(p, "srchost=", strlen("srchost=")))
+ srchost = p + strlen("srchost=");
+ }
+
+ if (!mech || strlen(mech) < 1) {
+ printerr(0, "WARNING: handle_gssd_upcall: "
+ "failed to find gss mechanism name "
+ "in upcall string '%s'\n", lbuf);
+ return;
+ }
+
+ if (uidstr) {
+ uid = (uid_t)strtol(uidstr, &p, 10);
+ if (p == uidstr || *p != '\0')
+ uidstr = NULL;
+ }
+
+ if (!uidstr) {
+ printerr(0, "WARNING: handle_gssd_upcall: "
+ "failed to find uid "
+ "in upcall string '%s'\n", lbuf);
+ return;
+ }
+
+ if (enctypes && parse_enctypes(enctypes) != 0) {
+ printerr(0, "WARNING: handle_gssd_upcall: "
+ "parsing encryption types failed: errno %d\n", errno);
+ return;
+ }
+
+ if (target && strlen(target) < 1) {
+ printerr(0, "WARNING: handle_gssd_upcall: "
+ "failed to parse target name "
+ "in upcall string '%s'\n", lbuf);
+ return;
+ }
+
+ /*
+ * The presence of attribute "service=" indicates that machine
+ * credentials should be used for this request. If the value
+ * is "*", then any machine credentials available can be used.
+ * If the value is anything else, then machine credentials for
+ * the specified service name (always "nfs" for now) should be
+ * used.
+ */
+ if (service && strlen(service) < 1) {
+ printerr(0, "WARNING: handle_gssd_upcall: "
+ "failed to parse service type "
+ "in upcall string '%s'\n", lbuf);
+ return;
+ }
+
+ if (strcmp(mech, "krb5") == 0 && clp->servername) {
+ info = alloc_upcall_info(clp, uid, clp->gssd_fd, srchost, target, service);
+ if (info == NULL) {
+ printerr(0, "%s: failed to allocate clnt_upcall_info\n", __func__);
+ do_error_downcall(clp->gssd_fd, uid, -EACCES);
+ return;
+ }
+ err = start_upcall_thread(gssd_work_thread_fn, info);
+ if (err != 0) {
+ do_error_downcall(clp->gssd_fd, uid, -EACCES);
+ free_upcall_info(info);
+ }
+ } else {
+ if (clp->servername)
+ printerr(0, "WARNING: handle_gssd_upcall: "
+ "received unknown gss mech '%s'\n", mech);
+ do_error_downcall(clp->gssd_fd, uid, -EACCES);
+ }
+}
diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
new file mode 100644
index 0000000..6f66ef4
--- /dev/null
+++ b/utils/gssd/krb5_util.c
@@ -0,0 +1,1649 @@
+/*
+ * Adapted in part from MIT Kerberos 5-1.2.1 slave/kprop.c and from
+ * http://docs.sun.com/?p=/doc/816-1331/6m7oo9sms&a=view
+ *
+ * Copyright (c) 2002-2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ * J. Bruce Fields <bfields@umich.edu>
+ * Marius Aamodt Eriksen <marius@umich.edu>
+ * Kevin Coffman <kwc@umich.edu>
+ */
+
+/*
+ * slave/kprop.c
+ *
+ * Copyright 1990,1991 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 1994 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.
+ */
+/*
+ krb5_util.c
+
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <sys/param.h>
+#include <rpc/rpc.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <dirent.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+#include <time.h>
+#include <gssapi/gssapi.h>
+#ifdef USE_PRIVATE_KRB5_FUNCTIONS
+#include <gssapi/gssapi_krb5.h>
+#endif
+#include <krb5.h>
+#include <rpc/auth_gss.h>
+
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "nfslib.h"
+#include "gssd.h"
+#include "err_util.h"
+#include "gss_util.h"
+#include "krb5_util.h"
+
+/*
+ * List of principals from our keytab that we
+ * will try to use to obtain credentials
+ * (known as a principal list entry (ple))
+ */
+struct gssd_k5_kt_princ {
+ struct gssd_k5_kt_princ *next;
+ // Only protect against deletion, not modification
+ int refcount;
+ // Only set during creation in new_ple()
+ krb5_principal princ;
+ char *realm;
+ // Modified during usage by gssd_get_single_krb5_cred()
+ char *ccname;
+ krb5_timestamp endtime;
+};
+
+
+/* Global list of principals/cache file names for machine credentials */
+static struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL;
+/* This mutex protects list modification & ple->ccname */
+static pthread_mutex_t ple_lock = PTHREAD_MUTEX_INITIALIZER;
+
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+int limit_to_legacy_enctypes = 0;
+#endif
+
+/*==========================*/
+/*=== Internal routines ===*/
+/*==========================*/
+
+static int select_krb5_ccache(const struct dirent *d);
+static int gssd_find_existing_krb5_ccache(uid_t uid, char *dirname,
+ const char **cctype, struct dirent **d);
+static int gssd_get_single_krb5_cred(krb5_context context,
+ krb5_keytab kt, struct gssd_k5_kt_princ *ple, int force_renew);
+static int query_krb5_ccache(const char* cred_cache, char **ret_princname,
+ char **ret_realm);
+
+static void release_ple_locked(krb5_context context,
+ struct gssd_k5_kt_princ *ple)
+{
+ if (--ple->refcount)
+ return;
+
+ printerr(3, "freeing cached principal (ccname=%s, realm=%s)\n",
+ ple->ccname, ple->realm);
+ krb5_free_principal(context, ple->princ);
+ free(ple->ccname);
+ free(ple->realm);
+ free(ple);
+}
+
+static void release_ple(krb5_context context, struct gssd_k5_kt_princ *ple)
+{
+ pthread_mutex_lock(&ple_lock);
+ release_ple_locked(context, ple);
+ pthread_mutex_unlock(&ple_lock);
+}
+
+
+/*
+ * Called from the scandir function to weed out potential krb5
+ * credentials cache files
+ *
+ * Returns:
+ * 0 => don't select this one
+ * 1 => select this one
+ */
+static int
+select_krb5_ccache(const struct dirent *d)
+{
+ /*
+ * Note: We used to check d->d_type for DT_REG here,
+ * but apparenlty reiser4 always has DT_UNKNOWN.
+ * Check for IS_REG after stat() call instead.
+ */
+ if (strstr(d->d_name, GSSD_DEFAULT_CRED_PREFIX))
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * Look in directory "dirname" for files that look like they
+ * are Kerberos Credential Cache files for a given UID.
+ *
+ * Returns 0 if a valid-looking entry is found. "*cctype" is
+ * set to the name of the cache type. A pointer to the dirent
+ * is planted in "*d". Caller must free "*d" with free(3).
+ *
+ * Otherwise, a negative errno is returned.
+ */
+static int
+gssd_find_existing_krb5_ccache(uid_t uid, char *dirname,
+ const char **cctype, struct dirent **d)
+{
+ struct dirent **namelist;
+ int n;
+ int i;
+ int found = 0;
+ struct dirent *best_match_dir = NULL;
+ struct stat best_match_stat, tmp_stat;
+ /* dirname + cctype + d_name + NULL */
+ char buf[PATH_MAX+5+256+1];
+ char *princname = NULL;
+ char *realm = NULL;
+ int score, best_match_score = 0, err = -EACCES;
+
+ memset(&best_match_stat, 0, sizeof(best_match_stat));
+ *cctype = NULL;
+ *d = NULL;
+ n = scandir(dirname, &namelist, select_krb5_ccache, 0);
+ if (n < 0) {
+ printerr(1, "Error doing scandir on directory '%s': %s\n",
+ dirname, strerror(errno));
+ }
+ else if (n > 0) {
+ for (i = 0; i < n; i++) {
+ snprintf(buf, sizeof(buf),
+ "%s/%s", dirname, namelist[i]->d_name);
+ printerr(3, "CC '%s' being considered, "
+ "with preferred realm '%s'\n",
+ buf, preferred_realm ?
+ preferred_realm : "<none selected>");
+ if (lstat(buf, &tmp_stat)) {
+ printerr(0, "Error doing stat on '%s'\n", buf);
+ free(namelist[i]);
+ continue;
+ }
+ /* Only pick caches owned by the user (uid) */
+ if (tmp_stat.st_uid != uid) {
+ printerr(3, "CC '%s' owned by %u, not %u\n",
+ buf, tmp_stat.st_uid, uid);
+ free(namelist[i]);
+ continue;
+ }
+ if (!S_ISREG(tmp_stat.st_mode) &&
+ !S_ISDIR(tmp_stat.st_mode)) {
+ printerr(3, "CC '%s' is not a regular "
+ "file or directory\n", buf);
+ free(namelist[i]);
+ continue;
+ }
+ if (uid == 0 && !root_uses_machine_creds &&
+ strstr(namelist[i]->d_name, "machine_")) {
+ printerr(3, "CC '%s' not available to root\n", buf);
+ free(namelist[i]);
+ continue;
+ }
+ if (S_ISDIR(tmp_stat.st_mode)) {
+ *cctype = "DIR";
+ } else
+ if (S_ISREG(tmp_stat.st_mode)) {
+ *cctype = "FILE";
+ } else {
+ continue;
+ }
+ snprintf(buf, sizeof(buf), "%s:%s/%s", *cctype,
+ dirname, namelist[i]->d_name);
+ if (!query_krb5_ccache(buf, &princname, &realm)) {
+ printerr(3, "CC '%s' is expired or corrupt\n",
+ buf);
+ free(namelist[i]);
+ err = -EKEYEXPIRED;
+ continue;
+ }
+
+ score = 0;
+ if (preferred_realm &&
+ strcmp(realm, preferred_realm) == 0)
+ score++;
+
+ printerr(3, "CC '%s'(%s@%s) passed all checks and"
+ " has mtime of %u\n",
+ buf, princname, realm,
+ tmp_stat.st_mtime);
+ /*
+ * if more than one match is found, return the most
+ * recent (the one with the latest mtime), and
+ * don't free the dirent
+ */
+ if (!found) {
+ best_match_dir = namelist[i];
+ best_match_stat = tmp_stat;
+ best_match_score = score;
+ found++;
+ }
+ else {
+ /*
+ * If current score is higher than best match
+ * score, we use the current match. Otherwise,
+ * if the current match has an mtime later
+ * than the one we are looking at, then use
+ * the current match. Otherwise, we still
+ * have the best match.
+ */
+ if (best_match_score < score ||
+ (best_match_score == score &&
+ tmp_stat.st_mtime >
+ best_match_stat.st_mtime)) {
+ free(best_match_dir);
+ best_match_dir = namelist[i];
+ best_match_stat = tmp_stat;
+ best_match_score = score;
+ }
+ else {
+ free(namelist[i]);
+ }
+ printerr(3, "CC '%s:%s/%s' is our "
+ "current best match "
+ "with mtime of %u\n",
+ cctype, dirname,
+ best_match_dir->d_name,
+ best_match_stat.st_mtime);
+ }
+ free(princname);
+ free(realm);
+ }
+ free(namelist);
+ }
+ if (found) {
+ *d = best_match_dir;
+ return 0;
+ }
+
+ return err;
+}
+
+/* check if the ticket cache exists, if not set nocache=1 so that new
+ * tgt is gotten
+ */
+static int
+gssd_check_if_cc_exists(struct gssd_k5_kt_princ *ple)
+{
+ int fd;
+ char cc_name[BUFSIZ];
+
+ snprintf(cc_name, sizeof(cc_name), "%s/%s%s_%s",
+ ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX,
+ GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm);
+ fd = open(cc_name, O_RDONLY);
+ if (fd < 0)
+ return 1;
+ close(fd);
+ return 0;
+}
+
+/*
+ * Obtain credentials via a key in the keytab given
+ * a keytab handle and a gssd_k5_kt_princ structure.
+ * Checks to see if current credentials are expired,
+ * if not, uses the keytab to obtain new credentials.
+ *
+ * Returns:
+ * 0 => success (or credentials have not expired)
+ * nonzero => error
+ */
+static int
+gssd_get_single_krb5_cred(krb5_context context,
+ krb5_keytab kt,
+ struct gssd_k5_kt_princ *ple,
+ int force_renew)
+{
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
+ krb5_get_init_creds_opt *init_opts = NULL;
+#else
+ krb5_get_init_creds_opt options;
+#endif
+ krb5_get_init_creds_opt *opts;
+ krb5_creds my_creds;
+ krb5_ccache ccache = NULL;
+ char kt_name[BUFSIZ];
+ char cc_name[BUFSIZ];
+ int code;
+ time_t now = time(0);
+ char *cache_type;
+ char *pname = NULL;
+ char *k5err = NULL;
+ int nocache = 0;
+ pthread_t tid = pthread_self();
+
+ memset(&my_creds, 0, sizeof(my_creds));
+
+ if (!use_memcache)
+ nocache = gssd_check_if_cc_exists(ple);
+ /*
+ * Workaround for clock skew among NFS server, NFS client and KDC
+ * 300 because clock skew must be within 300sec for kerberos
+ */
+ now += 300;
+ pthread_mutex_lock(&ple_lock);
+ if (ple->ccname && ple->endtime > now && !nocache && !force_renew) {
+ printerr(3, "%s(0x%lx): Credentials in CC '%s' are good until %s",
+ __func__, tid, ple->ccname, ctime((time_t *)&ple->endtime));
+ code = 0;
+ pthread_mutex_unlock(&ple_lock);
+ goto out;
+ }
+ pthread_mutex_unlock(&ple_lock);
+
+ if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) {
+ printerr(0, "ERROR: Unable to get keytab name in "
+ "gssd_get_single_krb5_cred\n");
+ goto out;
+ }
+
+ if ((krb5_unparse_name(context, ple->princ, &pname)))
+ pname = NULL;
+
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
+ code = krb5_get_init_creds_opt_alloc(context, &init_opts);
+ if (code) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "ERROR: %s allocating gic options\n", k5err);
+ goto out;
+ }
+ if (krb5_get_init_creds_opt_set_addressless(context, init_opts, 1))
+ printerr(1, "WARNING: Unable to set option for addressless "
+ "tickets. May have problems behind a NAT.\n");
+#ifdef TEST_SHORT_LIFETIME
+ /* set a short lifetime (for debugging only!) */
+ printerr(1, "WARNING: Using (debug) short machine cred lifetime!\n");
+ krb5_get_init_creds_opt_set_tkt_life(init_opts, 5*60);
+#endif
+ opts = init_opts;
+
+#else /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS */
+
+ krb5_get_init_creds_opt_init(&options);
+ krb5_get_init_creds_opt_set_address_list(&options, NULL);
+#ifdef TEST_SHORT_LIFETIME
+ /* set a short lifetime (for debugging only!) */
+ printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n");
+ krb5_get_init_creds_opt_set_tkt_life(&options, 5*60);
+#endif
+ opts = &options;
+#endif
+
+ if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
+ kt, 0, NULL, opts))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(1, "WARNING: %s while getting initial ticket for "
+ "principal '%s' using keytab '%s'\n", k5err,
+ pname ? pname : "<unparsable>", kt_name);
+ goto out;
+ }
+
+ /*
+ * Initialize cache file which we're going to be using
+ */
+
+ pthread_mutex_lock(&ple_lock);
+ if (use_memcache)
+ cache_type = "MEMORY";
+ else
+ cache_type = "FILE";
+ snprintf(cc_name, sizeof(cc_name), "%s:%s/%s%s_%s",
+ cache_type,
+ ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX,
+ GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm);
+ ple->endtime = my_creds.times.endtime;
+ if (ple->ccname == NULL || strcmp(ple->ccname, cc_name) != 0) {
+ free(ple->ccname);
+ ple->ccname = strdup(cc_name);
+ if (ple->ccname == NULL) {
+ printerr(0, "ERROR: no storage to duplicate credentials "
+ "cache name '%s'\n", cc_name);
+ code = ENOMEM;
+ pthread_mutex_unlock(&ple_lock);
+ goto out;
+ }
+ }
+ pthread_mutex_unlock(&ple_lock);
+ if ((code = krb5_cc_resolve(context, cc_name, &ccache))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "ERROR: %s while opening credential cache '%s'\n",
+ k5err, cc_name);
+ goto out;
+ }
+ if ((code = krb5_cc_initialize(context, ccache, ple->princ))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "ERROR: %s while initializing credential "
+ "cache '%s'\n", k5err, cc_name);
+ goto out;
+ }
+ if ((code = krb5_cc_store_cred(context, ccache, &my_creds))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "ERROR: %s while storing credentials in '%s'\n",
+ k5err, cc_name);
+ goto out;
+ }
+
+ code = 0;
+ printerr(2, "%s(0x%lx): principal '%s' ccache:'%s'\n",
+ __func__, tid, pname, cc_name);
+ out:
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
+ if (init_opts)
+ krb5_get_init_creds_opt_free(context, init_opts);
+#endif
+ if (pname)
+ k5_free_unparsed_name(context, pname);
+ if (ccache)
+ krb5_cc_close(context, ccache);
+ krb5_free_cred_contents(context, &my_creds);
+ free(k5err);
+ return (code);
+}
+
+/*
+ * Given a principal, find a matching ple structure
+ * Called with mutex held
+ */
+static struct gssd_k5_kt_princ *
+find_ple_by_princ(krb5_context context, krb5_principal princ)
+{
+ struct gssd_k5_kt_princ *ple;
+
+ for (ple = gssd_k5_kt_princ_list; ple != NULL; ple = ple->next) {
+ if (krb5_principal_compare(context, ple->princ, princ))
+ return ple;
+ }
+ /* no match found */
+ return NULL;
+}
+
+/*
+ * Create, initialize, and add a new ple structure to the global list
+ * Called with mutex held
+ */
+static struct gssd_k5_kt_princ *
+new_ple(krb5_context context, krb5_principal princ)
+{
+ struct gssd_k5_kt_princ *ple = NULL, *p;
+ krb5_error_code code;
+ char *default_realm;
+ int is_default_realm = 0;
+
+ ple = malloc(sizeof(struct gssd_k5_kt_princ));
+ if (ple == NULL)
+ goto outerr;
+ memset(ple, 0, sizeof(*ple));
+
+#ifdef HAVE_KRB5
+ ple->realm = strndup(princ->realm.data,
+ princ->realm.length);
+#else
+ ple->realm = strdup(princ->realm);
+#endif
+ if (ple->realm == NULL)
+ goto outerr;
+ code = krb5_copy_principal(context, princ, &ple->princ);
+ if (code)
+ goto outerr;
+
+ /*
+ * Add new entry onto the list (if this is the default
+ * realm, always add to the front of the list)
+ */
+
+ code = krb5_get_default_realm(context, &default_realm);
+ if (code == 0) {
+ if (strcmp(ple->realm, default_realm) == 0)
+ is_default_realm = 1;
+ k5_free_default_realm(context, default_realm);
+ }
+
+ if (is_default_realm) {
+ ple->next = gssd_k5_kt_princ_list;
+ gssd_k5_kt_princ_list = ple;
+ } else {
+ p = gssd_k5_kt_princ_list;
+ while (p != NULL && p->next != NULL)
+ p = p->next;
+ if (p == NULL)
+ gssd_k5_kt_princ_list = ple;
+ else
+ p->next = ple;
+ }
+
+ ple->refcount = 1;
+ return ple;
+outerr:
+ if (ple) {
+ if (ple->realm)
+ free(ple->realm);
+ free(ple);
+ }
+ return NULL;
+}
+
+/*
+ * Given a principal, find an existing ple structure, or create one
+ */
+static struct gssd_k5_kt_princ *
+get_ple_by_princ(krb5_context context, krb5_principal princ)
+{
+ struct gssd_k5_kt_princ *ple;
+
+ pthread_mutex_lock(&ple_lock);
+ ple = find_ple_by_princ(context, princ);
+ if (ple == NULL) {
+ ple = new_ple(context, princ);
+ }
+ if (ple != NULL) {
+ ple->refcount++;
+ }
+ pthread_mutex_unlock(&ple_lock);
+
+ return ple;
+}
+
+/*
+ * Given a (possibly unqualified) hostname,
+ * return the fully qualified (lower-case!) hostname
+ */
+static int
+get_full_hostname(const char *inhost, char *outhost, int outhostlen)
+{
+ struct addrinfo *addrs = NULL;
+ struct addrinfo hints;
+ int retval;
+ char *c;
+ pthread_t tid = pthread_self();
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_flags = AI_CANONNAME;
+
+ /* Get full target hostname */
+ retval = getaddrinfo(inhost, NULL, &hints, &addrs);
+ if (retval) {
+ printerr(1, "%s(0x%lx): getaddrinfo(%s) failed: %s\n",
+ __func__, tid, inhost, gai_strerror(retval));
+ goto out;
+ }
+ strncpy(outhost, addrs->ai_canonname, outhostlen);
+ nfs_freeaddrinfo(addrs);
+ for (c = outhost; *c != '\0'; c++)
+ *c = tolower(*c);
+
+ if (get_verbosity() && strcmp(inhost, outhost))
+ printerr(1, "%s(0x%0lx): inhost '%s' different than outhost '%s'\n",
+ __func__, tid, inhost, outhost);
+
+ retval = 0;
+out:
+ return retval;
+}
+
+/*
+ * If principal matches the given realm and service name,
+ * and has *any* instance (hostname), return 1.
+ * Otherwise return 0, indicating no match.
+ */
+#ifdef HAVE_KRB5
+static int
+realm_and_service_match(krb5_principal p, const char *realm, const char *service)
+{
+ /* Must have two components */
+ if (p->length != 2)
+ return 0;
+
+ if ((strlen(realm) == p->realm.length)
+ && (strncmp(realm, p->realm.data, p->realm.length) == 0)
+ && (strlen(service) == p->data[0].length)
+ && (strncmp(service, p->data[0].data, p->data[0].length) == 0))
+ return 1;
+
+ return 0;
+}
+#else
+static int
+realm_and_service_match(krb5_context context, krb5_principal p,
+ const char *realm, const char *service)
+{
+ const char *name, *inst;
+
+ if (p->name.name_string.len != 2)
+ return 0;
+
+ name = krb5_principal_get_comp_string(context, p, 0);
+ inst = krb5_principal_get_comp_string(context, p, 1);
+ if (name == NULL || inst == NULL)
+ return 0;
+ if ((strcmp(realm, p->realm) == 0)
+ && (strcmp(service, name) == 0))
+ return 1;
+
+ return 0;
+}
+#endif
+
+/*
+ * Search the given keytab file looking for an entry with the given
+ * service name and realm, ignoring hostname (instance).
+ *
+ * Returns:
+ * 0 => No error
+ * non-zero => An error occurred
+ *
+ * If a keytab entry is found, "found" is set to one, and the keytab
+ * entry is returned in "kte". Otherwise, "found" is zero, and the
+ * value of "kte" is unpredictable.
+ */
+static int
+gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
+ const char *realm, const char *service,
+ int *found, krb5_keytab_entry *kte)
+{
+ krb5_kt_cursor cursor;
+ krb5_error_code code;
+ struct gssd_k5_kt_princ *ple;
+ int retval = -1, status;
+ char kt_name[BUFSIZ];
+ char *pname;
+ char *k5err = NULL;
+
+ if (found == NULL) {
+ retval = EINVAL;
+ goto out;
+ }
+ *found = 0;
+
+ /*
+ * Look through each entry in the keytab file and determine
+ * if we might want to use it as machine credentials. If so,
+ * save info in the global principal list (gssd_k5_kt_princ_list).
+ */
+ if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "ERROR: %s attempting to get keytab name\n", k5err);
+ retval = code;
+ goto out;
+ }
+ if ((code = krb5_kt_start_seq_get(context, kt, &cursor))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "ERROR: %s while beginning keytab scan "
+ "for keytab '%s'\n", k5err, kt_name);
+ retval = code;
+ goto out;
+ }
+
+ printerr(4, "Scanning keytab for %s/*@%s\n", service, realm);
+ while ((code = krb5_kt_next_entry(context, kt, kte, &cursor)) == 0) {
+ if ((code = krb5_unparse_name(context, kte->principal,
+ &pname))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "WARNING: Skipping keytab entry because "
+ "we failed to unparse principal name: %s\n",
+ k5err);
+ k5_free_kt_entry(context, kte);
+ free(k5err);
+ k5err = NULL;
+ continue;
+ }
+ printerr(4, "Processing keytab entry for principal '%s'\n",
+ pname);
+ /* Use the first matching keytab entry found */
+#ifdef HAVE_KRB5
+ status = realm_and_service_match(kte->principal, realm, service);
+#else
+ status = realm_and_service_match(context, kte->principal, realm, service);
+#endif
+ if (status) {
+ printerr(4, "We WILL use this entry (%s)\n", pname);
+ ple = get_ple_by_princ(context, kte->principal);
+ /*
+ * Return, don't free, keytab entry if
+ * we were successful!
+ */
+ if (ple == NULL) {
+ retval = ENOMEM;
+ k5_free_kt_entry(context, kte);
+ } else {
+ release_ple(context, ple);
+ ple = NULL;
+ retval = 0;
+ *found = 1;
+ }
+ k5_free_unparsed_name(context, pname);
+ break;
+ }
+ else {
+ printerr(4, "We will NOT use this entry (%s)\n",
+ pname);
+ }
+ k5_free_unparsed_name(context, pname);
+ k5_free_kt_entry(context, kte);
+ }
+
+ if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "WARNING: %s while ending keytab scan for "
+ "keytab '%s'\n", k5err, kt_name);
+ }
+
+ /* Only clear the retval if has not been set */
+ if (retval < 0)
+ retval = 0;
+ out:
+ free(k5err);
+ return retval;
+}
+
+/*
+ * Find a keytab entry to use for a given target realm.
+ * Tries to find the most appropriate keytab to use given the
+ * name of the host we are trying to connect with.
+ *
+ * Note: the tgtname contains a hostname in the realm that we
+ * are authenticating to. It may, or may not be the same as
+ * the server hostname.
+ */
+static int
+find_keytab_entry(krb5_context context, krb5_keytab kt,
+ const char *srchost, const char *tgtname,
+ krb5_keytab_entry *kte, const char **svcnames)
+{
+ krb5_error_code code;
+ char **realmnames = NULL;
+ char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST];
+ char myhostad[NI_MAXHOST+1];
+ int i, j, k, retval;
+ char *default_realm = NULL;
+ char *realm;
+ char *k5err = NULL;
+ int tried_all = 0, tried_default = 0, tried_upper = 0;
+ krb5_principal princ;
+ const char *notsetstr = "not set";
+ char *adhostoverride = NULL;
+ pthread_t tid = pthread_self();
+
+
+ /* Get full target hostname */
+ retval = get_full_hostname(tgtname, targethostname,
+ sizeof(targethostname));
+ if (retval)
+ goto out;
+
+ /* Get full local hostname */
+ if (srchost) {
+ strcpy(myhostname, srchost);
+ strcpy(myhostad, myhostname);
+ } else {
+ /* Borrow myhostad for gethostname(), we need it later anyways */
+ if (gethostname(myhostad, sizeof(myhostad)-1) == -1) {
+ retval = errno;
+ k5err = gssd_k5_err_msg(context, retval);
+ printerr(1, "%s while getting local hostname\n", k5err);
+ goto out;
+ }
+ retval = get_full_hostname(myhostad, myhostname, sizeof(myhostname));
+ if (retval) {
+ /* Don't use myhostname */
+ myhostname[0] = 0;
+ }
+ }
+
+ /* Compute the active directory machine name HOST$ */
+ krb5_appdefault_string(context, "nfs", NULL, "ad_principal_name",
+ notsetstr, &adhostoverride);
+ if (adhostoverride && strcmp(adhostoverride, notsetstr) != 0) {
+ printerr(1,
+ "AD host string overridden with \"%s\" from appdefaults\n",
+ adhostoverride);
+ /* No overflow: Windows cannot handle strings longer than 19 chars */
+ strcpy(myhostad, adhostoverride);
+ } else {
+ /* In this case, it's been pre-filled above */
+ for (i = 0; myhostad[i] != 0; ++i) {
+ if (myhostad[i] == '.') break;
+ }
+ myhostad[i] = '$';
+ myhostad[i+1] = 0;
+ }
+ if (adhostoverride)
+ krb5_free_string(context, adhostoverride);
+
+ code = krb5_get_default_realm(context, &default_realm);
+ if (code) {
+ retval = code;
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(1, "%s while getting default realm name\n", k5err);
+ goto out;
+ }
+
+ /*
+ * Get the realm name(s) for the target hostname.
+ * In reality, this function currently only returns a
+ * single realm, but we code with the assumption that
+ * someday it may actually return a list.
+ */
+ code = krb5_get_host_realm(context, targethostname, &realmnames);
+ if (code) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "ERROR: %s while getting realm(s) for host '%s'\n",
+ k5err, targethostname);
+ retval = code;
+ goto out;
+ }
+
+ /*
+ * Make sure the preferred_realm, which may have been explicitly set
+ * on the command line, is tried first. If nothing is found go on with
+ * the host and local default realm (if that hasn't already been tried).
+ */
+ i = 0;
+ realm = realmnames[i];
+
+ if (preferred_realm && strcmp (realm, preferred_realm) != 0) {
+ realm = preferred_realm;
+ /* resetting the realmnames index */
+ i = -1;
+ }
+
+ while (1) {
+ if (realm == NULL) {
+ tried_all = 1;
+ if (!tried_default)
+ realm = default_realm;
+ }
+ if (tried_all && tried_default)
+ break;
+ if (strcmp(realm, default_realm) == 0)
+ tried_default = 1;
+ for (j = 0; svcnames[j] != NULL; j++) {
+ char spn[NI_MAXHOST+2];
+
+ /*
+ * The special svcname "$" means 'try the active
+ * directory machine account'
+ */
+ if (strcmp(svcnames[j],"$") == 0) {
+ snprintf(spn, sizeof(spn), "%s@%s", myhostad, realm);
+ code = krb5_build_principal_ext(context, &princ,
+ strlen(realm),
+ realm,
+ strlen(myhostad),
+ myhostad,
+ NULL);
+ } else {
+ if (!myhostname[0])
+ continue;
+ snprintf(spn, sizeof(spn), "%s/%s@%s",
+ svcnames[j], myhostname, realm);
+ code = krb5_build_principal_ext(context, &princ,
+ strlen(realm),
+ realm,
+ strlen(svcnames[j]),
+ svcnames[j],
+ strlen(myhostname),
+ myhostname,
+ NULL);
+ }
+
+ if (code) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(1, "%s while building principal for '%s'\n",
+ k5err, spn);
+ free(k5err);
+ k5err = NULL;
+ continue;
+ }
+ code = krb5_kt_get_entry(context, kt, princ, 0, 0, kte);
+ krb5_free_principal(context, princ);
+ if (code) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(3, "%s while getting keytab entry for '%s'\n",
+ k5err, spn);
+ free(k5err);
+ k5err = NULL;
+ /*
+ * We tried the active directory machine account
+ * with the hostname part as-is and failed...
+ * convert it to uppercase and try again before
+ * moving on to the svcname
+ */
+ if (strcmp(svcnames[j],"$") == 0 && !tried_upper) {
+ for (k = 0; myhostad[k] != '$'; ++k) {
+ myhostad[k] = toupper(myhostad[k]);
+ }
+ j--;
+ tried_upper = 1;
+ }
+ } else {
+ printerr(2, "find_keytab_entry(0x%lx): Success getting keytab entry for '%s'\n",tid, spn);
+ retval = 0;
+ goto out;
+ }
+ retval = code;
+ }
+ /*
+ * Nothing found with our hostname instance, now look for
+ * names with any instance (they must have an instance)
+ */
+ for (j = 0; svcnames[j] != NULL; j++) {
+ int found = 0;
+ if (strcmp(svcnames[j],"$") == 0)
+ continue;
+ code = gssd_search_krb5_keytab(context, kt, realm,
+ svcnames[j], &found, kte);
+ if (!code && found) {
+ printerr(3, "Success getting keytab entry for "
+ "%s/*@%s\n", svcnames[j], realm);
+ retval = 0;
+ goto out;
+ }
+ }
+ if (!tried_all) {
+ i++;
+ realm = realmnames[i];
+ }
+ }
+out:
+ if (default_realm)
+ k5_free_default_realm(context, default_realm);
+ if (realmnames)
+ krb5_free_host_realm(context, realmnames);
+ free(k5err);
+ return retval;
+}
+
+
+static inline int data_is_equal(krb5_data d1, krb5_data d2)
+{
+ return (d1.length == d2.length
+ && memcmp(d1.data, d2.data, d1.length) == 0);
+}
+
+static int
+check_for_tgt(krb5_context context, krb5_ccache ccache,
+ krb5_principal principal)
+{
+ krb5_error_code ret;
+ krb5_creds creds;
+ krb5_cc_cursor cur;
+ int found = 0;
+
+ ret = krb5_cc_start_seq_get(context, ccache, &cur);
+ if (ret)
+ return 0;
+
+ while (!found &&
+ (ret = krb5_cc_next_cred(context, ccache, &cur, &creds)) == 0) {
+ if (creds.server->length == 2 &&
+ data_is_equal(creds.server->realm,
+ principal->realm) &&
+ creds.server->data[0].length == 6 &&
+ memcmp(creds.server->data[0].data,
+ "krbtgt", 6) == 0 &&
+ data_is_equal(creds.server->data[1],
+ principal->realm) &&
+ creds.times.endtime > time(NULL))
+ found = 1;
+ krb5_free_cred_contents(context, &creds);
+ }
+ krb5_cc_end_seq_get(context, ccache, &cur);
+
+ return found;
+}
+
+static int
+query_krb5_ccache(const char* cred_cache, char **ret_princname,
+ char **ret_realm)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_ccache ccache;
+ krb5_principal principal;
+ int found = 0;
+ char *str = NULL;
+ char *princstring;
+
+ *ret_princname = *ret_realm = NULL;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ return 0;
+
+ if(!cred_cache || krb5_cc_resolve(context, cred_cache, &ccache))
+ goto err_cache;
+
+ if (krb5_cc_set_flags(context, ccache, 0))
+ goto err_princ;
+
+ ret = krb5_cc_get_principal(context, ccache, &principal);
+ if (ret)
+ goto err_princ;
+
+ found = check_for_tgt(context, ccache, principal);
+ if (found) {
+ ret = krb5_unparse_name(context, principal, &princstring);
+ if (ret == 0) {
+ if ((str = strchr(princstring, '@')) != NULL) {
+ *str = '\0';
+ *ret_princname = strdup(princstring);
+ *ret_realm = strdup(str+1);
+ if (!*ret_princname || !*ret_realm) {
+ free(*ret_princname);
+ free(*ret_realm);
+ *ret_princname = NULL;
+ *ret_realm = NULL;
+ }
+ }
+ k5_free_unparsed_name(context, princstring);
+ }
+ }
+ krb5_free_principal(context, principal);
+err_princ:
+ krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
+ krb5_cc_close(context, ccache);
+err_cache:
+ krb5_free_context(context);
+ return (*ret_princname && *ret_realm);
+}
+
+/*
+ * Obtain (or refresh if necessary) Kerberos machine credentials
+ * If a ple is passed in, it's reference will be released
+ */
+static int
+gssd_refresh_krb5_machine_credential_internal(char *hostname,
+ struct gssd_k5_kt_princ *ple,
+ char *service, char *srchost,
+ int force_renew)
+{
+ krb5_error_code code = 0;
+ krb5_context context;
+ krb5_keytab kt = NULL;;
+ int retval = 0;
+ char *k5err = NULL;
+ const char *svcnames[] = { "$", "root", "nfs", "host", NULL };
+
+ /*
+ * If a specific service name was specified, use it.
+ * Otherwise, use the default list.
+ */
+ if (service != NULL && strcmp(service, "*") != 0) {
+ svcnames[0] = service;
+ svcnames[1] = NULL;
+ }
+ if (hostname == NULL && ple == NULL) {
+ printerr(0, "ERROR: %s: Invalid args\n", __func__);
+ return EINVAL;
+ }
+ code = krb5_init_context(&context);
+ if (code) {
+ k5err = gssd_k5_err_msg(NULL, code);
+ printerr(0, "ERROR: %s: %s while initializing krb5 context\n",
+ __func__, k5err);
+ retval = code;
+ goto out;
+ }
+
+ if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n",
+ __func__, k5err, keytabfile);
+ goto out_free_context;
+ }
+
+ if (ple == NULL) {
+ krb5_keytab_entry kte;
+
+ code = find_keytab_entry(context, kt, srchost, hostname,
+ &kte, svcnames);
+ if (code) {
+ printerr(0, "ERROR: %s: no usable keytab entry found "
+ "in keytab %s for connection with host %s\n",
+ __FUNCTION__, keytabfile, hostname);
+ retval = code;
+ goto out_free_kt;
+ }
+
+ ple = get_ple_by_princ(context, kte.principal);
+ k5_free_kt_entry(context, &kte);
+ if (ple == NULL) {
+ char *pname;
+ if ((krb5_unparse_name(context, kte.principal, &pname))) {
+ pname = NULL;
+ }
+ printerr(0, "ERROR: %s: Could not locate or create "
+ "ple struct for principal %s for connection "
+ "with host %s\n",
+ __FUNCTION__, pname ? pname : "<unparsable>",
+ hostname);
+ if (pname) k5_free_unparsed_name(context, pname);
+ goto out_free_kt;
+ }
+ }
+ retval = gssd_get_single_krb5_cred(context, kt, ple, force_renew);
+out_free_kt:
+ krb5_kt_close(context, kt);
+out_free_context:
+ if (ple)
+ release_ple(context, ple);
+ krb5_free_context(context);
+out:
+ free(k5err);
+ return retval;
+}
+
+/*==========================*/
+/*=== External routines ===*/
+/*==========================*/
+
+/*
+ * Attempt to find the best match for a credentials cache file
+ * given only a UID. We really need more information, but we
+ * do the best we can.
+ *
+ * Returns 0 if a ccache was found, or a negative errno otherwise.
+ */
+int
+gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirpattern)
+{
+ /* dirname + cctype + d_name + NULL */
+ char buf[PATH_MAX+5+256+1], dirname[PATH_MAX];
+ const char *cctype;
+ struct dirent *d;
+ int err, i, j;
+ u_int maj_stat, min_stat;
+
+ printerr(3, "looking for client creds with uid %u for "
+ "server %s in %s\n", uid, servername, dirpattern);
+
+ for (i = 0, j = 0; dirpattern[i] != '\0'; i++) {
+ switch (dirpattern[i]) {
+ case '%':
+ switch (dirpattern[i + 1]) {
+ case '%':
+ dirname[j++] = dirpattern[i];
+ i++;
+ break;
+ case 'U':
+ j += sprintf(dirname + j, "%lu",
+ (unsigned long) uid);
+ i++;
+ break;
+ }
+ break;
+ default:
+ dirname[j++] = dirpattern[i];
+ break;
+ }
+ }
+ dirname[j] = '\0';
+
+ err = gssd_find_existing_krb5_ccache(uid, dirname, &cctype, &d);
+ if (err)
+ return err;
+
+ snprintf(buf, sizeof(buf), "%s:%s/%s", cctype, dirname, d->d_name);
+ free(d);
+
+ printerr(2, "using %s as credentials cache for client with "
+ "uid %u for server %s\n", buf, uid, servername);
+
+ printerr(3, "using gss_krb5_ccache_name to select krb5 ccache %s\n",
+ buf);
+ maj_stat = gss_krb5_ccache_name(&min_stat, buf, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ printerr(0, "ERROR: unable to get user cred cache '%s' "
+ "failed (%s)\n", buf, error_message(min_stat));
+ return maj_stat;
+ }
+ return 0;
+}
+
+/*
+ * Return an array of pointers to names of credential cache files
+ * which can be used to try to create gss contexts with a server.
+ *
+ * Returns:
+ * 0 => list is attached
+ * nonzero => error
+ */
+int
+gssd_get_krb5_machine_cred_list(char ***list)
+{
+ char **l;
+ int listinc = 10;
+ int listsize = listinc;
+ int i = 0;
+ int retval;
+ struct gssd_k5_kt_princ *ple;
+
+ /* Assume failure */
+ retval = -1;
+ *list = (char **) NULL;
+
+ if ((l = (char **) malloc(listsize * sizeof(char *))) == NULL) {
+ retval = ENOMEM;
+ goto out;
+ }
+
+ pthread_mutex_lock(&ple_lock);
+ for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+ if (!ple->ccname)
+ continue;
+
+ /* Take advantage of the fact we only remove the ple
+ * from the list during shutdown. If it's modified
+ * concurrently at worst we'll just miss a new entry
+ * before the current ple
+ *
+ * gssd_refresh_krb5_machine_credential_internal() will
+ * release the ple refcount
+ */
+ ple->refcount++;
+ pthread_mutex_unlock(&ple_lock);
+ /* Make sure cred is up-to-date before returning it */
+ retval = gssd_refresh_krb5_machine_credential_internal(NULL, ple,
+ NULL, NULL, 0);
+ pthread_mutex_lock(&ple_lock);
+ if (gssd_k5_kt_princ_list == NULL) {
+ /* Looks like we did shutdown... abort */
+ l[i] = NULL;
+ gssd_free_krb5_machine_cred_list(l);
+ retval = ENOMEM;
+ goto out_lock;
+ }
+ if (retval)
+ continue;
+ if (i + 1 > listsize) {
+ char **tmplist;
+ listsize += listinc;
+ tmplist = (char **)
+ realloc(l, listsize * sizeof(char *));
+ if (tmplist == NULL) {
+ gssd_free_krb5_machine_cred_list(l);
+ retval = ENOMEM;
+ goto out_lock;
+ }
+ l = tmplist;
+ }
+ if ((l[i++] = strdup(ple->ccname)) == NULL) {
+ gssd_free_krb5_machine_cred_list(l);
+ retval = ENOMEM;
+ goto out_lock;
+ }
+ }
+ if (i > 0) {
+ l[i] = NULL;
+ *list = l;
+ retval = 0;
+ } else
+ free((void *)l);
+out_lock:
+ pthread_mutex_unlock(&ple_lock);
+ out:
+ return retval;
+}
+
+/*
+ * Frees the list of names returned in get_krb5_machine_cred_list()
+ */
+void
+gssd_free_krb5_machine_cred_list(char **list)
+{
+ char **n;
+
+ if (list == NULL)
+ return;
+ for (n = list; n && *n; n++) {
+ free(*n);
+ }
+ free(list);
+}
+
+/*
+ * Called upon exit. Destroys machine credentials.
+ */
+void
+gssd_destroy_krb5_principals(int destroy_machine_creds)
+{
+ krb5_context context;
+ krb5_error_code code = 0;
+ krb5_ccache ccache;
+ struct gssd_k5_kt_princ *ple;
+ char *k5err = NULL;
+
+ code = krb5_init_context(&context);
+ if (code) {
+ k5err = gssd_k5_err_msg(NULL, code);
+ printerr(0, "ERROR: %s while initializing krb5\n", k5err);
+ free(k5err);
+ return;
+ }
+
+ pthread_mutex_lock(&ple_lock);
+ while (gssd_k5_kt_princ_list) {
+ ple = gssd_k5_kt_princ_list;
+ gssd_k5_kt_princ_list = ple->next;
+
+ if (destroy_machine_creds && ple->ccname) {
+ if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "WARNING: %s while resolving credential "
+ "cache '%s' for destruction\n", k5err,
+ ple->ccname);
+ free(k5err);
+ k5err = NULL;
+ }
+
+ if (!code && (code = krb5_cc_destroy(context, ccache))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "WARNING: %s while destroying credential "
+ "cache '%s'\n", k5err, ple->ccname);
+ free(k5err);
+ k5err = NULL;
+ }
+ }
+
+ release_ple_locked(context, ple);
+ }
+ pthread_mutex_unlock(&ple_lock);
+ krb5_free_context(context);
+}
+
+/*
+ * Obtain (or refresh if necessary) Kerberos machine credentials
+ */
+int
+gssd_refresh_krb5_machine_credential(char *hostname,
+ char *service, char *srchost,
+ int force_renew)
+{
+ return gssd_refresh_krb5_machine_credential_internal(hostname, NULL,
+ service, srchost,
+ force_renew);
+}
+
+/*
+ * A common routine for getting the Kerberos error message
+ */
+char *
+gssd_k5_err_msg(krb5_context context, krb5_error_code code)
+{
+ const char *origmsg;
+ char *msg = NULL;
+
+#if HAVE_KRB5_GET_ERROR_MESSAGE
+ if (context != NULL) {
+ origmsg = krb5_get_error_message(context, code);
+ msg = strdup(origmsg);
+ krb5_free_error_message(context, origmsg);
+ }
+#endif
+ if (msg != NULL)
+ return msg;
+#if HAVE_KRB5
+ return strdup(error_message(code));
+#else
+ if (context != NULL)
+ return strdup(krb5_get_err_text(context, code));
+ else
+ return strdup(error_message(code));
+#endif
+}
+
+/*
+ * Return default Kerberos realm
+ */
+void
+gssd_k5_get_default_realm(char **def_realm)
+{
+ krb5_context context;
+
+ if (krb5_init_context(&context))
+ return;
+
+ krb5_get_default_realm(context, def_realm);
+
+ krb5_free_context(context);
+}
+
+static int
+gssd_acquire_krb5_cred(gss_cred_id_t *gss_cred)
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_OID_set_desc desired_mechs = { 1, &krb5oid };
+
+ maj_stat = gss_acquire_cred(&min_stat, GSS_C_NO_NAME, GSS_C_INDEFINITE,
+ &desired_mechs, GSS_C_INITIATE,
+ gss_cred, NULL, NULL);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ if (get_verbosity() > 0)
+ pgsserr("gss_acquire_cred",
+ maj_stat, min_stat, &krb5oid);
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+gssd_acquire_user_cred(gss_cred_id_t *gss_cred)
+{
+ OM_uint32 maj_stat, min_stat;
+ int ret;
+
+ ret = gssd_acquire_krb5_cred(gss_cred);
+ if (ret)
+ return ret;
+
+ /* force validation of cred to check for expiry */
+ maj_stat = gss_inquire_cred(&min_stat, *gss_cred,
+ NULL, NULL, NULL, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ if (get_verbosity() > 0)
+ pgsserr("gss_inquire_cred",
+ maj_stat, min_stat, &krb5oid);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+/* Removed a service ticket for nfs/<name> from the ticket cache
+ */
+int
+gssd_k5_remove_bad_service_cred(char *name)
+{
+ krb5_creds in_creds, out_creds;
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_ccache cache;
+ krb5_principal principal;
+ int retflags = KRB5_TC_MATCH_SRV_NAMEONLY;
+ char srvname[1024];
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ goto out_cred;
+ ret = krb5_cc_default(context, &cache);
+ if (ret)
+ goto out_free_context;
+ ret = krb5_cc_get_principal(context, cache, &principal);
+ if (ret)
+ goto out_close_cache;
+ memset(&in_creds, 0, sizeof(in_creds));
+ in_creds.client = principal;
+ sprintf(srvname, "nfs/%s", name);
+ ret = krb5_parse_name(context, srvname, &in_creds.server);
+ if (ret)
+ goto out_free_principal;
+ ret = krb5_cc_retrieve_cred(context, cache, retflags, &in_creds, &out_creds);
+ if (ret)
+ goto out_free_principal;
+ ret = krb5_cc_remove_cred(context, cache, 0, &out_creds);
+out_free_principal:
+ krb5_free_principal(context, principal);
+out_close_cache:
+ krb5_cc_close(context, cache);
+out_free_context:
+ krb5_free_context(context);
+out_cred:
+ return ret;
+}
+
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+/*
+ * this routine obtains a credentials handle via gss_acquire_cred()
+ * then calls gss_krb5_set_allowable_enctypes() to limit the encryption
+ * types negotiated.
+ *
+ * XXX Should call some function to determine the enctypes supported
+ * by the kernel. (Only need to do that once!)
+ *
+ * Returns:
+ * 0 => all went well
+ * -1 => there was an error
+ */
+
+int
+limit_krb5_enctypes(struct rpc_gss_sec *sec)
+{
+ u_int maj_stat, min_stat;
+ krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC,
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_DES_CBC_MD4 };
+ int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]);
+ extern int num_krb5_enctypes;
+ extern krb5_enctype *krb5_enctypes;
+ int err = -1;
+
+ if (sec->cred == GSS_C_NO_CREDENTIAL) {
+ err = gssd_acquire_krb5_cred(&sec->cred);
+ if (err)
+ return -1;
+ }
+
+ /*
+ * If we failed for any reason to produce global
+ * list of supported enctypes, use local default here.
+ */
+ if (krb5_enctypes == NULL || limit_to_legacy_enctypes)
+ maj_stat = gss_set_allowable_enctypes(&min_stat, sec->cred,
+ &krb5oid, num_enctypes, enctypes);
+ else
+ maj_stat = gss_set_allowable_enctypes(&min_stat, sec->cred,
+ &krb5oid, num_krb5_enctypes, krb5_enctypes);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_set_allowable_enctypes",
+ maj_stat, min_stat, &krb5oid);
+ return -1;
+ }
+
+ return 0;
+}
+#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */
diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h
new file mode 100644
index 0000000..7ef8701
--- /dev/null
+++ b/utils/gssd/krb5_util.h
@@ -0,0 +1,47 @@
+#ifndef KRB5_UTIL_H
+#define KRB5_UTIL_H
+
+#include <krb5.h>
+
+#ifdef HAVE_LIBTIRPC
+#include <rpc/auth_gss.h>
+#else
+#include "gss_oids.h"
+#endif
+
+
+int gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername,
+ char *dirname);
+int gssd_get_krb5_machine_cred_list(char ***list);
+void gssd_free_krb5_machine_cred_list(char **list);
+void gssd_destroy_krb5_principals(int destroy_machine_creds);
+int gssd_refresh_krb5_machine_credential(char *hostname,
+ char *service, char *srchost,
+ int force_renew);
+char *gssd_k5_err_msg(krb5_context context, krb5_error_code code);
+void gssd_k5_get_default_realm(char **def_realm);
+
+int gssd_acquire_user_cred(gss_cred_id_t *gss_cred);
+int gssd_k5_remove_bad_service_cred(char *srvname);
+
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+extern int limit_to_legacy_enctypes;
+int limit_krb5_enctypes(struct rpc_gss_sec *sec);
+#endif
+
+/*
+ * Hide away some of the MIT vs. Heimdal differences
+ * here with macros...
+ */
+
+#ifdef HAVE_KRB5
+#define k5_free_unparsed_name(ctx, name) krb5_free_unparsed_name((ctx), (name))
+#define k5_free_default_realm(ctx, realm) krb5_free_default_realm((ctx), (realm))
+#define k5_free_kt_entry(ctx, kte) krb5_free_keytab_entry_contents((ctx),(kte))
+#else /* Heimdal */
+#define k5_free_unparsed_name(ctx, name) free(name)
+#define k5_free_default_realm(ctx, realm) free(realm)
+#define k5_free_kt_entry(ctx, kte) krb5_kt_free_entry((ctx),(kte))
+#endif
+
+#endif /* KRB5_UTIL_H */
diff --git a/utils/gssd/svcgssd.c b/utils/gssd/svcgssd.c
new file mode 100644
index 0000000..ce78d8f
--- /dev/null
+++ b/utils/gssd/svcgssd.c
@@ -0,0 +1,348 @@
+/*
+ gssd.c
+
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
+ Copyright (c) 2002 Andy Adamson <andros@UMICH.EDU>.
+ Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
+ Copyright (c) 2002 J. Bruce Fields <bfields@UMICH.EDU>.
+ All rights reserved, all wrongs reversed.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <rpc/rpc.h>
+#include <fcntl.h>
+#include <errno.h>
+
+
+#include <unistd.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <nfsidmap.h>
+#include <event2/event.h>
+
+#include "nfslib.h"
+#include "svcgssd.h"
+#include "gss_util.h"
+#include "err_util.h"
+#include "conffile.h"
+#include "misc.h"
+#include "svcgssd_krb5.h"
+
+static bool signal_received = false;
+static struct event_base *evbase = NULL;
+static int nullrpc_fd = -1;
+static struct event *nullrpc_event = NULL;
+static struct event *wait_event = NULL;
+
+#define NULLRPC_FILE "/proc/net/rpc/auth.rpcsec.init/channel"
+
+static void
+sig_die(int signal)
+{
+ if (signal_received) {
+ /* destroy krb5 machine creds */
+ printerr(1, "forced exiting on signal %d\n", signal);
+ exit(0);
+ }
+ signal_received = true;
+ printerr(1, "exiting on signal %d\n", signal);
+ event_base_loopexit(evbase, NULL);
+}
+
+static void
+sig_hup(int signal)
+{
+ /* don't exit on SIGHUP */
+ printerr(1, "Received SIGHUP(%d)... Ignoring.\n", signal);
+ return;
+}
+
+static void
+usage(char *progname)
+{
+ fprintf(stderr, "usage: %s [-n] [-f] [-v] [-r] [-i] [-p principal]\n",
+ progname);
+ exit(1);
+}
+
+static void
+svcgssd_nullrpc_cb(int fd, short UNUSED(which), void *UNUSED(data))
+{
+ char lbuf[RPC_CHAN_BUF_SIZE];
+ int lbuflen = 0;
+
+ printerr(1, "reading null request\n");
+
+ lbuflen = read(fd, lbuf, sizeof(lbuf));
+ if (lbuflen <= 0 || lbuf[lbuflen-1] != '\n') {
+ printerr(0, "WARNING: handle_nullreq: failed reading request\n");
+ return;
+ }
+ lbuf[lbuflen-1] = 0;
+
+ handle_nullreq(lbuf);
+}
+
+static void
+svcgssd_nullrpc_close(void)
+{
+ if (nullrpc_event) {
+ printerr(2, "closing nullrpc channel %s\n", NULLRPC_FILE);
+ event_free(nullrpc_event);
+ nullrpc_event = NULL;
+ }
+ if (nullrpc_fd != -1) {
+ close(nullrpc_fd);
+ nullrpc_fd = -1;
+ }
+}
+
+static void
+svcgssd_nullrpc_open(void)
+{
+ nullrpc_fd = open(NULLRPC_FILE, O_RDWR);
+ if (nullrpc_fd < 0) {
+ printerr(0, "failed to open %s: %s\n",
+ NULLRPC_FILE, strerror(errno));
+ return;
+ }
+ nullrpc_event = event_new(evbase, nullrpc_fd, EV_READ | EV_PERSIST,
+ svcgssd_nullrpc_cb, NULL);
+ if (!nullrpc_event) {
+ printerr(0, "failed to create event for %s: %s\n",
+ NULLRPC_FILE, strerror(errno));
+ close(nullrpc_fd);
+ nullrpc_fd = -1;
+ return;
+ }
+ event_add(nullrpc_event, NULL);
+ printerr(2, "opened nullrpc channel %s\n", NULLRPC_FILE);
+}
+
+static void
+svcgssd_wait_cb(int UNUSED(fd), short UNUSED(which), void *UNUSED(data))
+{
+ static int times = 0;
+ int rc;
+
+ rc = access(NULLRPC_FILE, R_OK | W_OK);
+ if (rc != 0) {
+ struct timeval t = {times < 10 ? 1 : 10, 0};
+ times++;
+ if (times % 30 == 0)
+ printerr(2, "still waiting for nullrpc channel: %s\n",
+ NULLRPC_FILE);
+ evtimer_add(wait_event, &t);
+ return;
+ }
+
+ svcgssd_nullrpc_open();
+ event_free(wait_event);
+ wait_event = NULL;
+}
+
+
+
+int
+main(int argc, char *argv[])
+{
+ int get_creds = 1;
+ int fg = 0;
+ int verbosity = 0;
+ int rpc_verbosity = 0;
+ int idmap_verbosity = 0;
+ int opt, status;
+ extern char *optarg;
+ char *progname;
+ char *principal = NULL;
+ char *s;
+ int rc;
+
+ conf_init_file(NFS_CONFFILE);
+
+ s = conf_get_str("svcgssd", "principal");
+ if (!s)
+ ;
+ else if (strcmp(s, "system")== 0)
+ get_creds = 0;
+ else
+ principal = s;
+
+ verbosity = conf_get_num("svcgssd", "Verbosity", verbosity);
+ rpc_verbosity = conf_get_num("svcgssd", "RPC-Verbosity", rpc_verbosity);
+ idmap_verbosity = conf_get_num("svcgssd", "IDMAP-Verbosity", idmap_verbosity);
+
+ while ((opt = getopt(argc, argv, "fivrnp:")) != -1) {
+ switch (opt) {
+ case 'f':
+ fg = 1;
+ break;
+ case 'i':
+ idmap_verbosity++;
+ break;
+ case 'n':
+ get_creds = 0;
+ break;
+ case 'v':
+ verbosity++;
+ break;
+ case 'r':
+ rpc_verbosity++;
+ break;
+ case 'p':
+ principal = optarg;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if ((progname = strrchr(argv[0], '/')))
+ progname++;
+ else
+ progname = argv[0];
+
+ initerr(progname, verbosity, fg);
+#ifdef HAVE_AUTHGSS_SET_DEBUG_LEVEL
+ if (verbosity && rpc_verbosity == 0)
+ rpc_verbosity = verbosity;
+ authgss_set_debug_level(rpc_verbosity);
+#elif HAVE_LIBTIRPC_SET_DEBUG
+ /*
+ * Only set the libtirpc debug level if explicitly requested via -r...
+ * svcgssd is chatty enough as it is.
+ */
+ if (rpc_verbosity > 0)
+ libtirpc_set_debug(progname, rpc_verbosity, fg);
+#else
+ if (rpc_verbosity > 0)
+ printerr(0, "Warning: rpcsec_gss library does not "
+ "support setting debug level\n");
+#endif
+#ifdef HAVE_NFS4_SET_DEBUG
+ if (verbosity && idmap_verbosity == 0)
+ idmap_verbosity = verbosity;
+ nfs4_set_debug(idmap_verbosity, NULL);
+#else
+ if (idmap_verbosity > 0)
+ printerr(0, "Warning: your nfsidmap library does not "
+ "support setting debug level\n");
+#endif
+
+ if (gssd_check_mechs() != 0) {
+ printerr(0, "ERROR: Problem with gssapi library\n");
+ exit(1);
+ }
+
+ daemon_init(fg);
+
+ evbase = event_base_new();
+ if (!evbase) {
+ printerr(0, "ERROR: failed to create event base: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ signal(SIGINT, sig_die);
+ signal(SIGTERM, sig_die);
+ signal(SIGHUP, sig_hup);
+
+ if (get_creds) {
+ if (principal)
+ status = gssd_acquire_cred(principal,
+ ((const gss_OID)GSS_C_NT_USER_NAME));
+ else
+ status = gssd_acquire_cred(GSSD_SERVICE_NAME,
+ (const gss_OID)GSS_C_NT_HOSTBASED_SERVICE);
+ if (status == FALSE) {
+ printerr(0, "unable to obtain root (machine) credentials\n");
+ printerr(0, "do you have a keytab entry for %s in"
+ "/etc/krb5.keytab?\n",
+ principal ? principal : "nfs/<your.host>@<YOUR.REALM>");
+ exit(1);
+ }
+ } else {
+ status = gssd_acquire_cred(NULL,
+ (const gss_OID)GSS_C_NT_HOSTBASED_SERVICE);
+ if (status == FALSE) {
+ printerr(0, "unable to obtain nameless credentials\n");
+ exit(1);
+ }
+ }
+
+ svcgssd_nullrpc_open();
+ if (!nullrpc_event) {
+ struct timeval t = {1, 0};
+
+ printerr(2, "waiting for nullrpc channel to appear\n");
+ wait_event = evtimer_new(evbase, svcgssd_wait_cb, NULL);
+ if (!wait_event) {
+ printerr(0, "ERROR: failed to create wait event: %s\n",
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ evtimer_add(wait_event, &t);
+ }
+
+ daemon_ready();
+
+ /* We don't need the config anymore */
+ conf_cleanup();
+
+ nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
+
+ rc = event_base_dispatch(evbase);
+ if (rc < 0)
+ printerr(0, "event_base_dispatch() returned %i!\n", rc);
+
+ svcgssd_nullrpc_close();
+ if (wait_event)
+ event_free(wait_event);
+
+ event_base_free(evbase);
+
+ nfs4_term_name_mapping();
+ svcgssd_free_enctypes();
+ gssd_cleanup();
+
+ return EXIT_SUCCESS;
+}
diff --git a/utils/gssd/svcgssd.h b/utils/gssd/svcgssd.h
new file mode 100644
index 0000000..e229b98
--- /dev/null
+++ b/utils/gssd/svcgssd.h
@@ -0,0 +1,42 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _RPC_SVCGSSD_H_
+#define _RPC_SVCGSSD_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <gssapi/gssapi.h>
+
+void handle_nullreq(char *cp);
+
+#define GSSD_SERVICE_NAME "nfs"
+
+#endif /* _RPC_SVCGSSD_H_ */
diff --git a/utils/gssd/svcgssd.man b/utils/gssd/svcgssd.man
new file mode 100644
index 0000000..8771c03
--- /dev/null
+++ b/utils/gssd/svcgssd.man
@@ -0,0 +1,88 @@
+.\"
+.\" rpc.svcgssd(8)
+.\"
+.\" Copyright (C) 2003 J. Bruce Fields <bfields@umich.edu>
+.TH rpc.svcgssd 8 "12 Jan 2007"
+.SH NAME
+rpc.svcgssd \- server-side rpcsec_gss daemon
+.SH SYNOPSIS
+.B "rpc.svcgssd [-n] [-v] [-r] [-i] [-f] [-p principal]"
+.SH DESCRIPTION
+The rpcsec_gss protocol gives a means of using the gss-api generic security
+api to provide security for protocols using rpc (in particular, nfs). Before
+exchanging any rpc requests using rpcsec_gss, the rpc client must first
+establish a security context with the rpc server. The linux kernel's
+implementation of rpcsec_gss depends on the userspace daemon
+.B rpc.svcgssd
+to handle context establishment on the rpc server. The
+daemon uses files in the proc filesystem to communicate with
+the kernel.
+
+.SH OPTIONS
+.TP
+.B -f
+Runs
+.B rpc.svcgssd
+in the foreground and sends output to stderr (as opposed to syslogd)
+.TP
+.B -v
+Increases the verbosity of the output (can be specified multiple times).
+.TP
+.B -r
+If the rpcsec_gss library supports setting debug level,
+increases the verbosity of the output (can be specified multiple times).
+.TP
+.B -i
+If the nfsidmap library supports setting debug level,
+increases the verbosity of the output (can be specified multiple times).
+.TP
+.B -p
+Use \fIprincipal\fR instead of the default
+.RI nfs/ FQDN @ REALM .
+.TP
+.B -n
+Use the system default credentials
+.RI (host/ FQDN @ REALM )
+rather than the default
+.RI nfs/ FQDN @ REALM .
+.SH CONFIGURATION FILE
+Some of the options that can be set on the command line can also be
+controlled through values set in the
+.B [svcgssd]
+section of the
+.I /etc/nfs.conf
+configuration file. Values recognized include:
+.TP
+.B principal
+If set to
+.B system
+this is equivalent to the
+.B -n
+option. If set to any other value, that is used like the
+.B -p
+option.
+.TP
+.B verbosity
+Value which is equivalent to the number of
+.BR -v .
+.TP
+.B rpc-verbosity
+Value which is equivalent to the number of
+.BR -r .
+.TP
+.B idmap-verbosity
+Value which is equivalent to the number of
+.BR -i .
+
+
+.SH SEE ALSO
+.BR rpc.gssd(8),
+.SH AUTHORS
+.br
+Dug Song <dugsong@umich.edu>
+.br
+Andy Adamson <andros@umich.edu>
+.br
+Marius Aamodt Eriksen <marius@umich.edu>
+.br
+J. Bruce Fields <bfields@umich.edu>
diff --git a/utils/gssd/svcgssd_krb5.c b/utils/gssd/svcgssd_krb5.c
new file mode 100644
index 0000000..2503c38
--- /dev/null
+++ b/utils/gssd/svcgssd_krb5.c
@@ -0,0 +1,237 @@
+/*
+ * COPYRIGHT (c) 2011
+ * The Regents of the University of Michigan
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <gssapi/gssapi.h>
+#include <krb5.h>
+
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "err_util.h"
+#include "svcgssd_krb5.h"
+#include "version.h"
+
+#define MYBUFLEN 1024
+
+char *supported_enctypes_filename = "/proc/fs/nfsd/supported_krb5_enctypes";
+int parsed_num_enctypes = 0;
+krb5_enctype *parsed_enctypes = NULL;
+char *cached_enctypes = NULL;
+
+/*==========================*/
+/*=== Internal routines ===*/
+/*==========================*/
+
+/*
+ * Parse the supported encryption type information
+ */
+static int
+parse_enctypes(char *enctypes)
+{
+ int n = 0;
+ char *curr, *comma;
+ int i;
+
+ /* Don't parse the same string over and over... */
+ if (cached_enctypes && strcmp(cached_enctypes, enctypes) == 0)
+ return 0;
+
+ /* Free any existing cached_enctypes */
+ svcgssd_free_enctypes();
+
+ /* count the number of commas */
+ for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) {
+ comma = strchr(curr, ',');
+ if (comma != NULL)
+ n++;
+ else
+ break;
+ }
+
+ /* If no more commas and we're not at the end, there's one more value */
+ if (*curr != '\0')
+ n++;
+
+ /* Empty string, return an error */
+ if (n == 0)
+ return ENOENT;
+
+ /* Skip pass any non digits */
+ while (*enctypes && isdigit(*enctypes) == 0)
+ enctypes++;
+ if (*enctypes == '\0')
+ return EINVAL;
+
+ /* Allocate space for enctypes array */
+ if ((parsed_enctypes = (int *) calloc(n, sizeof(int))) == NULL) {
+ return ENOMEM;
+ }
+
+ /* Now parse each value into the array */
+ for (curr = enctypes, i = 0; curr && *curr != '\0'; curr = ++comma) {
+ parsed_enctypes[i++] = atoi(curr);
+ comma = strchr(curr, ',');
+ if (comma == NULL)
+ break;
+ }
+
+ parsed_num_enctypes = n;
+ if ((cached_enctypes = malloc(strlen(enctypes)+1)))
+ strcpy(cached_enctypes, enctypes);
+
+ return 0;
+}
+
+static void
+get_kernel_supported_enctypes(void)
+{
+ FILE *s_e;
+ int ret;
+ char buffer[MYBUFLEN + 1];
+
+ memset(buffer, '\0', sizeof(buffer));
+
+ s_e = fopen(supported_enctypes_filename, "r");
+ if (s_e == NULL)
+ goto out_clean_parsed;
+
+ ret = fread(buffer, 1, MYBUFLEN, s_e);
+ if (ret < 0) {
+ fclose(s_e);
+ goto out_clean_parsed;
+ }
+ fclose(s_e);
+ if (parse_enctypes(buffer)) {
+ goto out_clean_parsed;
+ }
+out:
+ return;
+
+out_clean_parsed:
+ if (parsed_enctypes != NULL) {
+ free(parsed_enctypes);
+ parsed_num_enctypes = 0;
+ }
+ goto out;
+}
+
+/*==========================*/
+/*=== External routines ===*/
+/*==========================*/
+
+void
+svcgssd_free_enctypes(void)
+{
+ free(cached_enctypes);
+ cached_enctypes = NULL;
+
+ if (parsed_enctypes != NULL) {
+ free(parsed_enctypes);
+ parsed_enctypes = NULL;
+ parsed_num_enctypes = 0;
+ }
+}
+
+/*
+ * Get encryption types supported by the kernel, and then
+ * call gss_krb5_set_allowable_enctypes() to limit the
+ * encryption types negotiated.
+ *
+ * Returns:
+ * 0 => all went well
+ * -1 => there was an error
+ */
+
+int
+svcgssd_limit_krb5_enctypes(void)
+{
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+ u_int maj_stat, min_stat;
+ krb5_enctype old_kernel_enctypes[] = {
+ ENCTYPE_DES_CBC_CRC,
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_DES_CBC_MD4 };
+ krb5_enctype new_kernel_enctypes[] = {
+ ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+ ENCTYPE_DES3_CBC_SHA1,
+ ENCTYPE_ARCFOUR_HMAC,
+ ENCTYPE_DES_CBC_CRC,
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_DES_CBC_MD4 };
+ krb5_enctype *default_enctypes, *enctypes;
+ int default_num_enctypes, num_enctypes;
+
+
+ if (linux_version_code() < MAKE_VERSION(2, 6, 35)) {
+ default_enctypes = old_kernel_enctypes;
+ default_num_enctypes =
+ sizeof(old_kernel_enctypes) / sizeof(old_kernel_enctypes[0]);
+ } else {
+ default_enctypes = new_kernel_enctypes;
+ default_num_enctypes =
+ sizeof(new_kernel_enctypes) / sizeof(new_kernel_enctypes[0]);
+ }
+
+ get_kernel_supported_enctypes();
+
+ if (parsed_enctypes != NULL) {
+ enctypes = parsed_enctypes;
+ num_enctypes = parsed_num_enctypes;
+ printerr(2, "%s: Calling gss_set_allowable_enctypes with %d "
+ "enctypes from the kernel\n", __func__, num_enctypes);
+ } else {
+ enctypes = default_enctypes;
+ num_enctypes = default_num_enctypes;
+ printerr(2, "%s: Calling gss_set_allowable_enctypes with %d "
+ "enctypes from defaults\n", __func__, num_enctypes);
+ }
+
+ maj_stat = gss_set_allowable_enctypes(&min_stat, gssd_creds,
+ &krb5oid, num_enctypes, enctypes);
+ if (maj_stat != GSS_S_COMPLETE) {
+ printerr(1, "WARNING: gss_set_allowable_enctypes failed\n");
+ pgsserr("svcgssd_limit_krb5_enctypes: gss_set_allowable_enctypes",
+ maj_stat, min_stat, &krb5oid);
+ return -1;
+ }
+#endif
+ return 0;
+}
diff --git a/utils/gssd/svcgssd_krb5.h b/utils/gssd/svcgssd_krb5.h
new file mode 100644
index 0000000..78a90e9
--- /dev/null
+++ b/utils/gssd/svcgssd_krb5.h
@@ -0,0 +1,37 @@
+/*
+ * COPYRIGHT (c) 2011
+ * The Regents of the University of Michigan
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+#ifndef SVCGSSD_KRB5_H
+#define SVCGSSD_KRB5_H
+
+int svcgssd_limit_krb5_enctypes(void);
+void svcgssd_free_enctypes(void);
+
+#endif /* SVCGSSD_KRB5_H */
diff --git a/utils/gssd/svcgssd_mech2file.c b/utils/gssd/svcgssd_mech2file.c
new file mode 100644
index 0000000..c26b435
--- /dev/null
+++ b/utils/gssd/svcgssd_mech2file.c
@@ -0,0 +1,74 @@
+/*
+ linux_downcall.c
+
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2004 Andy Adamson <andros@UMICH.EDU>.
+ All rights reserved, all wrongs reversed.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <gssapi/gssapi.h>
+#include <string.h>
+
+char * mech2file(gss_OID mech);
+
+#define g_OID_equal(o1,o2) \
+ (((o1)->length == (o2)->length) && \
+ (memcmp((o1)->elements,(o2)->elements,(int) (o1)->length) == 0))
+
+struct mech2file {
+ gss_OID_desc mech;
+ char filename[8];
+};
+
+struct mech2file m2f[] = {
+ {{9, "\052\206\110\206\367\022\001\002\002"}, "krb5"},
+ {{0,0},""},
+};
+
+/*
+ * Find the Linux svcgssd downcall file name given the mechanism
+ */
+char *
+mech2file(gss_OID mech)
+{
+ struct mech2file *m2fp = m2f;
+
+ while(m2fp->mech.length != 0) {
+ if (g_OID_equal(mech,&m2fp->mech))
+ return(m2fp->filename);
+ m2fp++;
+ }
+ return NULL;
+}
diff --git a/utils/gssd/svcgssd_proc.c b/utils/gssd/svcgssd_proc.c
new file mode 100644
index 0000000..b403143
--- /dev/null
+++ b/utils/gssd/svcgssd_proc.c
@@ -0,0 +1,444 @@
+/*
+ svc_in_gssd_proc.c
+
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+
+#include <pwd.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <nfsidmap.h>
+#include <nfslib.h>
+#include <time.h>
+
+#include "svcgssd.h"
+#include "gss_util.h"
+#include "err_util.h"
+#include "context.h"
+#include "misc.h"
+#include "gss_oids.h"
+#include "svcgssd_krb5.h"
+#include "gss_names.h"
+
+extern char * mech2file(gss_OID mech);
+#define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.rpcsec.context/channel"
+#define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.rpcsec.init/channel"
+
+#define TOKEN_BUF_SIZE 8192
+
+struct svc_cred {
+ uid_t cr_uid;
+ gid_t cr_gid;
+ int cr_ngroups;
+ gid_t cr_groups[NGROUPS];
+};
+
+static int
+do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
+ gss_OID mech, gss_buffer_desc *context_token,
+ int32_t endtime, char *client_name)
+{
+ char buf[RPC_CHAN_BUF_SIZE], *bp;
+ int i, f, err, blen;
+ char *fname = NULL;
+
+ printerr(1, "doing downcall\n");
+ if ((fname = mech2file(mech)) == NULL)
+ goto out_err;
+
+ f = open(SVCGSSD_CONTEXT_CHANNEL, O_WRONLY);
+ if (f < 0) {
+ printerr(0, "WARNING: unable to open downcall channel "
+ "%s: %s\n",
+ SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
+ goto out_err;
+ }
+ bp = buf, blen = sizeof(buf);
+ qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
+ /* XXX are types OK for the rest of this? */
+ /* For context cache, use the actual context endtime */
+ qword_addint(&bp, &blen, endtime);
+ qword_addint(&bp, &blen, cred->cr_uid);
+ qword_addint(&bp, &blen, cred->cr_gid);
+ qword_addint(&bp, &blen, cred->cr_ngroups);
+ printerr(2, "mech: %s, hndl len: %d, ctx len %d, timeout: %d (%d from now), "
+ "clnt: %s, uid: %d, gid: %d, num aux grps: %d:\n",
+ fname, out_handle->length, context_token->length,
+ endtime, endtime - time(0),
+ client_name ? client_name : "<null>",
+ cred->cr_uid, cred->cr_gid, cred->cr_ngroups);
+ for (i=0; i < cred->cr_ngroups; i++) {
+ qword_addint(&bp, &blen, cred->cr_groups[i]);
+ printerr(2, " (%4d) %d\n", i+1, cred->cr_groups[i]);
+ }
+ qword_add(&bp, &blen, fname);
+ qword_addhex(&bp, &blen, context_token->value, context_token->length);
+ if (client_name)
+ qword_add(&bp, &blen, client_name);
+ qword_addeol(&bp, &blen);
+ err = 0;
+ if (blen <= 0 || write(f, buf, bp - buf) != bp - buf) {
+ printerr(1, "WARNING: error writing to downcall channel "
+ "%s: %s\n", SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
+ err = -1;
+ }
+ close(f);
+ return err;
+out_err:
+ printerr(1, "WARNING: downcall failed\n");
+ return -1;
+}
+
+struct gss_verifier {
+ u_int32_t flav;
+ gss_buffer_desc body;
+};
+
+#define RPCSEC_GSS_SEQ_WIN 5
+
+static int
+send_response(gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
+ u_int32_t maj_stat, u_int32_t min_stat,
+ gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
+{
+ char buf[2 * TOKEN_BUF_SIZE];
+ char *bp = buf;
+ int blen = sizeof(buf);
+ /* XXXARG: */
+ int g;
+
+ printerr(1, "sending null reply\n");
+
+ qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
+ qword_addhex(&bp, &blen, in_token->value, in_token->length);
+ /* For init cache, only needed for a short time */
+ qword_addint(&bp, &blen, time(0) + 60);
+ qword_adduint(&bp, &blen, maj_stat);
+ qword_adduint(&bp, &blen, min_stat);
+ qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
+ qword_addhex(&bp, &blen, out_token->value, out_token->length);
+ qword_addeol(&bp, &blen);
+ if (blen <= 0) {
+ printerr(0, "WARNING: send_respsonse: message too long\n");
+ return -1;
+ }
+ g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
+ if (g == -1) {
+ printerr(0, "WARNING: open %s failed: %s\n",
+ SVCGSSD_INIT_CHANNEL, strerror(errno));
+ return -1;
+ }
+ *bp = '\0';
+ printerr(3, "writing message: %s", buf);
+ if (write(g, buf, bp - buf) == -1) {
+ printerr(0, "WARNING: failed to write message\n");
+ close(g);
+ return -1;
+ }
+ close(g);
+ return 0;
+}
+
+#define rpc_auth_ok 0
+#define rpc_autherr_badcred 1
+#define rpc_autherr_rejectedcred 2
+#define rpc_autherr_badverf 3
+#define rpc_autherr_rejectedverf 4
+#define rpc_autherr_tooweak 5
+#define rpcsec_gsserr_credproblem 13
+#define rpcsec_gsserr_ctxproblem 14
+
+static void
+add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
+{
+ int ret;
+ static gid_t *groups = NULL;
+
+ cred->cr_ngroups = NGROUPS;
+ ret = nfs4_gss_princ_to_grouplist(secname, name,
+ cred->cr_groups, &cred->cr_ngroups);
+ if (ret < 0) {
+ groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
+ ret = nfs4_gss_princ_to_grouplist(secname, name,
+ groups, &cred->cr_ngroups);
+ if (ret < 0)
+ cred->cr_ngroups = 0;
+ else {
+ if (cred->cr_ngroups > NGROUPS)
+ cred->cr_ngroups = NGROUPS;
+ memcpy(cred->cr_groups, groups,
+ cred->cr_ngroups*sizeof(gid_t));
+ }
+ }
+}
+
+static int
+get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
+{
+ u_int32_t maj_stat, min_stat;
+ gss_buffer_desc name;
+ char *sname;
+ int res = -1;
+ uid_t uid, gid;
+ gss_OID name_type = GSS_C_NO_OID;
+ char *secname;
+
+ maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("get_ids: gss_display_name",
+ maj_stat, min_stat, mech);
+ goto out;
+ }
+ if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
+ !(sname = calloc(name.length + 1, 1))) {
+ printerr(0, "WARNING: get_ids: error allocating %d bytes "
+ "for sname\n", name.length + 1);
+ gss_release_buffer(&min_stat, &name);
+ goto out;
+ }
+ memcpy(sname, name.value, name.length);
+ printerr(1, "sname = %s\n", sname);
+ gss_release_buffer(&min_stat, &name);
+
+ res = -EINVAL;
+ if ((secname = mech2file(mech)) == NULL) {
+ printerr(0, "WARNING: get_ids: error mapping mech to "
+ "file for name '%s'\n", sname);
+ goto out_free;
+ }
+
+ res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
+ if (res < 0) {
+ /*
+ * -ENOENT means there was no mapping, any other error
+ * value means there was an error trying to do the
+ * mapping.
+ * If there was no mapping, we send down the value -1
+ * to indicate that the anonuid/anongid for the export
+ * should be used.
+ */
+ if (res == -ENOENT) {
+ cred->cr_uid = -1;
+ cred->cr_gid = -1;
+ cred->cr_ngroups = 0;
+ res = 0;
+ goto out_free;
+ }
+ printerr(1, "WARNING: get_ids: failed to map name '%s' "
+ "to uid/gid: %s\n", sname, strerror(-res));
+ goto out_free;
+ }
+ cred->cr_uid = uid;
+ cred->cr_gid = gid;
+ add_supplementary_groups(secname, sname, cred);
+ res = 0;
+out_free:
+ free(sname);
+out:
+ return res;
+}
+
+#ifdef DEBUG
+void
+print_hexl(const char *description, unsigned char *cp, int length)
+{
+ int i, j, jm;
+ unsigned char c;
+
+ printf("%s (length %d)\n", description, length);
+
+ for (i = 0; i < length; i += 0x10) {
+ printf(" %04x: ", (u_int)i);
+ jm = length - i;
+ jm = jm > 16 ? 16 : jm;
+
+ for (j = 0; j < jm; j++) {
+ if ((j % 2) == 1)
+ printf("%02x ", (u_int)cp[i+j]);
+ else
+ printf("%02x", (u_int)cp[i+j]);
+ }
+ for (; j < 16; j++) {
+ if ((j % 2) == 1)
+ printf(" ");
+ else
+ printf(" ");
+ }
+ printf(" ");
+
+ for (j = 0; j < jm; j++) {
+ c = cp[i+j];
+ c = isprint(c) ? c : '.';
+ printf("%c", c);
+ }
+ printf("\n");
+ }
+}
+#endif
+
+void
+handle_nullreq(char *cp) {
+ /* XXX initialize to a random integer to reduce chances of unnecessary
+ * invalidation of existing ctx's on restarting svcgssd. */
+ static u_int32_t handle_seq = 0;
+ char in_tok_buf[TOKEN_BUF_SIZE];
+ char in_handle_buf[15];
+ char out_handle_buf[15];
+ gss_buffer_desc in_tok = {.value = in_tok_buf},
+ out_tok = {.value = NULL},
+ in_handle = {.value = in_handle_buf},
+ out_handle = {.value = out_handle_buf},
+ ctx_token = {.value = NULL},
+ ignore_out_tok = {.value = NULL},
+ /* XXX isn't there a define for this?: */
+ null_token = {.value = NULL};
+ u_int32_t ret_flags;
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_name_t client_name = NULL;
+ gss_OID mech = GSS_C_NO_OID;
+ u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0;
+ u_int32_t ignore_min_stat;
+ struct svc_cred cred;
+ int32_t ctx_endtime;
+ char *hostbased_name = NULL;
+
+ printerr(1, "handling null request\n");
+
+ in_handle.length = (size_t) qword_get(&cp, in_handle.value,
+ sizeof(in_handle_buf));
+#ifdef DEBUG
+ print_hexl("in_handle", in_handle.value, in_handle.length);
+#endif
+
+ in_tok.length = (size_t) qword_get(&cp, in_tok.value,
+ sizeof(in_tok_buf));
+#ifdef DEBUG
+ print_hexl("in_tok", in_tok.value, in_tok.length);
+#endif
+
+ if (in_handle.length != 0) { /* CONTINUE_INIT case */
+ if (in_handle.length != sizeof(ctx)) {
+ printerr(0, "WARNING: handle_nullreq: "
+ "input handle has unexpected length %d\n",
+ in_handle.length);
+ goto out_err;
+ }
+ /* in_handle is the context id stored in the out_handle
+ * for the GSS_S_CONTINUE_NEEDED case below. */
+ memcpy(&ctx, in_handle.value, in_handle.length);
+ }
+
+ if (svcgssd_limit_krb5_enctypes()) {
+ goto out_err;
+ }
+
+ maj_stat = gss_accept_sec_context(&min_stat, &ctx, gssd_creds,
+ &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
+ &mech, &out_tok, &ret_flags, NULL, NULL);
+
+ if (maj_stat == GSS_S_CONTINUE_NEEDED) {
+ printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
+
+ /* Save the context handle for future calls */
+ out_handle.length = sizeof(ctx);
+ memcpy(out_handle.value, &ctx, sizeof(ctx));
+ goto continue_needed;
+ }
+ else if (maj_stat != GSS_S_COMPLETE) {
+ printerr(1, "WARNING: gss_accept_sec_context failed\n");
+ pgsserr("handle_nullreq: gss_accept_sec_context",
+ maj_stat, min_stat, mech);
+ goto out_err;
+ }
+ if (get_ids(client_name, mech, &cred)) {
+ /* get_ids() prints error msg */
+ maj_stat = GSS_S_BAD_NAME; /* XXX ? */
+ goto out_err;
+ }
+ if (get_hostbased_client_name(client_name, mech, &hostbased_name)) {
+ /* get_hostbased_client_name() prints error msg */
+ maj_stat = GSS_S_BAD_NAME; /* XXX ? */
+ goto out_err;
+ }
+
+ /* Context complete. Pass handle_seq in out_handle to use
+ * for context lookup in the kernel. */
+ handle_seq++;
+ out_handle.length = sizeof(handle_seq);
+ memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
+
+ /* kernel needs ctx to calculate verifier on null response, so
+ * must give it context before doing null call: */
+ if (serialize_context_for_kernel(&ctx, &ctx_token, mech, &ctx_endtime)) {
+ printerr(0, "WARNING: handle_nullreq: "
+ "serialize_context_for_kernel failed\n");
+ maj_stat = GSS_S_FAILURE;
+ goto out_err;
+ }
+ /* We no longer need the gss context */
+ gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
+
+ do_svc_downcall(&out_handle, &cred, mech, &ctx_token, ctx_endtime,
+ hostbased_name);
+continue_needed:
+ send_response(&in_handle, &in_tok, maj_stat, min_stat,
+ &out_handle, &out_tok);
+out:
+ if (ctx_token.value != NULL)
+ free(ctx_token.value);
+ if (out_tok.value != NULL)
+ gss_release_buffer(&ignore_min_stat, &out_tok);
+ if (client_name)
+ gss_release_name(&ignore_min_stat, &client_name);
+ free(hostbased_name);
+ printerr(1, "finished handling null request\n");
+ return;
+
+out_err:
+ if (ctx != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
+ send_response(&in_handle, &in_tok, maj_stat, min_stat,
+ &null_token, &null_token);
+ goto out;
+}
diff --git a/utils/gssd/write_bytes.h b/utils/gssd/write_bytes.h
new file mode 100644
index 0000000..b3f342b
--- /dev/null
+++ b/utils/gssd/write_bytes.h
@@ -0,0 +1,159 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University 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 ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _WRITE_BYTES_H_
+#define _WRITE_BYTES_H_
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h> /* for ntohl */
+
+inline static int
+write_bytes(char **ptr, const char *end, const void *arg, int arg_len)
+{
+ char *p = *ptr, *arg_end;
+
+ arg_end = p + arg_len;
+ if (arg_end > end || arg_end < p)
+ return -1;
+ memcpy(p, arg, arg_len);
+ *ptr = arg_end;
+ return 0;
+}
+
+#define WRITE_BYTES(p, end, arg) write_bytes(p, end, &arg, sizeof(arg))
+
+inline static int
+write_buffer(char **p, char *end, gss_buffer_desc *arg)
+{
+ int len = (int)arg->length; /* make an int out of size_t */
+ if (WRITE_BYTES(p, end, len))
+ return -1;
+ if (*p + len > end)
+ return -1;
+ memcpy(*p, arg->value, len);
+ *p += len;
+ return 0;
+}
+
+inline static int
+write_oid(char **p, char *end, gss_OID_desc *arg)
+{
+ int len = (int)arg->length; /* make an int out of size_t */
+ if (WRITE_BYTES(p, end, len))
+ return -1;
+ if (*p + arg->length > end)
+ return -1;
+ memcpy(*p, arg->elements, len);
+ *p += len;
+ return 0;
+}
+
+static inline int
+get_bytes(char **ptr, const char *end, void *res, int len)
+{
+ char *p, *q;
+ p = *ptr;
+ q = p + len;
+ if (q > end || q < p)
+ return -1;
+ memcpy(res, p, len);
+ *ptr = q;
+ return 0;
+}
+
+static inline int
+get_buffer(char **ptr, const char *end, gss_buffer_desc *res)
+{
+ char *p, *q;
+ p = *ptr;
+ int len;
+ if (get_bytes(&p, end, &len, sizeof(len)))
+ return -1;
+ res->length = len; /* promote to size_t if necessary */
+ q = p + res->length;
+ if (q > end || q < p)
+ return -1;
+ if (!(res->value = malloc(res->length)))
+ return -1;
+ memcpy(res->value, p, res->length);
+ *ptr = q;
+ return 0;
+}
+
+static inline int
+xdr_get_u32(u_int32_t **ptr, const u_int32_t *end, u_int32_t *res)
+{
+ if (get_bytes((char **)ptr, (char *)end, res, sizeof(res)))
+ return -1;
+ *res = ntohl(*res);
+ return 0;
+}
+
+static inline int
+xdr_get_buffer(u_int32_t **ptr, const u_int32_t *end, gss_buffer_desc *res)
+{
+ u_int32_t *p, *q;
+ u_int32_t len;
+ p = *ptr;
+ if (xdr_get_u32(&p, end, &len))
+ return -1;
+ res->length = len;
+ q = p + ((res->length + 3) >> 2);
+ if (q > end || q < p)
+ return -1;
+ if (!(res->value = malloc(res->length)))
+ return -1;
+ memcpy(res->value, p, res->length);
+ *ptr = q;
+ return 0;
+}
+
+static inline int
+xdr_write_u32(u_int32_t **ptr, const u_int32_t *end, u_int32_t arg)
+{
+ u_int32_t tmp;
+
+ tmp = htonl(arg);
+ return WRITE_BYTES((char **)ptr, (char *)end, tmp);
+}
+
+static inline int
+xdr_write_buffer(u_int32_t **ptr, const u_int32_t *end, gss_buffer_desc *arg)
+{
+ int len = arg->length;
+ if (xdr_write_u32(ptr, end, len))
+ return -1;
+ return write_bytes((char **)ptr, (char *)end, arg->value,
+ (arg->length + 3) & ~3);
+}
+
+#endif /* _WRITE_BYTES_H_ */