summaryrefslogtreecommitdiffstats
path: root/support/nfsidmap
diff options
context:
space:
mode:
Diffstat (limited to 'support/nfsidmap')
-rw-r--r--support/nfsidmap/AUTHORS1
-rw-r--r--support/nfsidmap/COPYING30
-rw-r--r--support/nfsidmap/Makefile.am75
-rw-r--r--support/nfsidmap/Makefile.in1060
-rw-r--r--support/nfsidmap/README126
-rw-r--r--support/nfsidmap/gums.c787
-rw-r--r--support/nfsidmap/idmapd.conf169
-rw-r--r--support/nfsidmap/idmapd.conf.5411
-rw-r--r--support/nfsidmap/libnfsidmap.c712
-rw-r--r--support/nfsidmap/libnfsidmap.pc.in11
-rw-r--r--support/nfsidmap/libtest.c160
-rw-r--r--support/nfsidmap/nfs4_uid_to_name.3174
-rw-r--r--support/nfsidmap/nfsidmap.h68
-rw-r--r--support/nfsidmap/nfsidmap_common.c118
-rw-r--r--support/nfsidmap/nfsidmap_plugin.h70
-rw-r--r--support/nfsidmap/nfsidmap_private.h54
-rw-r--r--support/nfsidmap/nss.c494
-rw-r--r--support/nfsidmap/regex.c549
-rw-r--r--support/nfsidmap/static.c426
-rw-r--r--support/nfsidmap/umich_ldap.c1615
20 files changed, 7110 insertions, 0 deletions
diff --git a/support/nfsidmap/AUTHORS b/support/nfsidmap/AUTHORS
new file mode 100644
index 0000000..1101630
--- /dev/null
+++ b/support/nfsidmap/AUTHORS
@@ -0,0 +1 @@
+J. Bruce Fields <bfields@citi.umich.edu>
diff --git a/support/nfsidmap/COPYING b/support/nfsidmap/COPYING
new file mode 100644
index 0000000..7571bb7
--- /dev/null
+++ b/support/nfsidmap/COPYING
@@ -0,0 +1,30 @@
+Copyright (c) 2004 The Regents of the University of Michigan.
+All rights reserved.
+
+Marius Aamodt Eriksen <marius@umich.edu>
+J. 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.
diff --git a/support/nfsidmap/Makefile.am b/support/nfsidmap/Makefile.am
new file mode 100644
index 0000000..f5b9de0
--- /dev/null
+++ b/support/nfsidmap/Makefile.am
@@ -0,0 +1,75 @@
+if PATH_PLUGINS
+pkgplugindir=$(PATH_PLUGINS)
+else
+pkgplugindir=$(libdir)/libnfsidmap
+endif
+
+if ENABLE_LDAP
+UMICH_LDAP_LIB = umich_ldap.la
+else
+UMICH_LDAP_LIB =
+endif
+if ENABLE_GUMS
+GUMS_MAPPING_LIB = gums.la
+else
+GUMS_MAPPING_LIB =
+endif
+if ENABLE_LDAP_SASL
+KRB5_GSS_LIB=-lgssapi_krb5
+endif
+lib_LTLIBRARIES = libnfsidmap.la
+pkgplugin_LTLIBRARIES = nsswitch.la static.la regex.la $(UMICH_LDAP_LIB) $(GUMS_MAPPING_LIB)
+
+# Library versioning notes from:
+# http://sources.redhat.com/autobook/autobook/autobook_91.html
+#
+# -version-info <current>:<revision>:<age>
+# <current> The number of the current interface exported by library.
+# <revision> The implementation number of the most recent interface
+# exported by the library. (i.e. revision should be updated
+# with each new release of the library, and reset to zero
+# when <current> is updated.)
+# <age> The number of previous additional interfaces supported
+# by this library.
+
+libnfsidmap_la_SOURCES = libnfsidmap.c nfsidmap_common.c
+libnfsidmap_la_LDFLAGS = -version-info 1:0:0
+libnfsidmap_la_LIBADD = -ldl ../../support/nfs/libnfsconf.la
+
+nsswitch_la_SOURCES = nss.c nfsidmap_common.c
+nsswitch_la_LDFLAGS = -module -avoid-version
+nsswitch_la_LIBADD = ../../support/nfs/libnfsconf.la
+
+static_la_SOURCES = static.c
+static_la_LDFLAGS = -module -avoid-version
+static_la_LIBADD = ../../support/nfs/libnfsconf.la
+
+regex_la_SOURCES = regex.c
+regex_la_LDFLAGS = -module -avoid-version
+regex_la_LIBADD = ../../support/nfs/libnfsconf.la
+
+umich_ldap_la_SOURCES = umich_ldap.c
+umich_ldap_la_LDFLAGS = -module -avoid-version
+umich_ldap_la_LIBADD = -lldap $(KRB5_GSS_LIB) ../../support/nfs/libnfsconf.la
+
+gums_la_SOURCES = gums.c
+gums_la_LDFLAGS = -module -avoid-version
+
+man3_MANS = nfs4_uid_to_name.3
+man5_MANS = idmapd.conf.5
+include_HEADERS = nfsidmap.h nfsidmap_plugin.h
+
+EXTRA_DIST = $(man3_MANS) \
+ $(man5_MANS) \
+ libtest.c \
+ idmapd.conf
+
+# XXX: also exclude debian/files and debian/files.new ? do a clean??
+dist-hook:
+ mkdir $(distdir)/debian/
+ find $(srcdir)/debian -maxdepth 1 -not -type d |xargs -i cp {} $(distdir)/debian/
+
+pkgconfigdir=$(libdir)/pkgconfig
+pkgconfig_DATA = libnfsidmap.pc
+
+$(pkgconfig_DATA): $(top_builddir)/config.status
diff --git a/support/nfsidmap/Makefile.in b/support/nfsidmap/Makefile.in
new file mode 100644
index 0000000..45ff4d6
--- /dev/null
+++ b/support/nfsidmap/Makefile.in
@@ -0,0 +1,1060 @@
+# 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@
+subdir = support/nfsidmap
+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 $(include_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/support/include/config.h
+CONFIG_CLEAN_FILES = libnfsidmap.pc
+CONFIG_CLEAN_VPATH_FILES =
+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; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgplugindir)" \
+ "$(DESTDIR)$(man3dir)" "$(DESTDIR)$(man5dir)" \
+ "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(includedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES) $(pkgplugin_LTLIBRARIES)
+gums_la_LIBADD =
+am_gums_la_OBJECTS = gums.lo
+gums_la_OBJECTS = $(am_gums_la_OBJECTS)
+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 =
+gums_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(gums_la_LDFLAGS) $(LDFLAGS) -o $@
+@ENABLE_GUMS_TRUE@am_gums_la_rpath = -rpath $(pkgplugindir)
+libnfsidmap_la_DEPENDENCIES = ../../support/nfs/libnfsconf.la
+am_libnfsidmap_la_OBJECTS = libnfsidmap.lo nfsidmap_common.lo
+libnfsidmap_la_OBJECTS = $(am_libnfsidmap_la_OBJECTS)
+libnfsidmap_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libnfsidmap_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+nsswitch_la_DEPENDENCIES = ../../support/nfs/libnfsconf.la
+am_nsswitch_la_OBJECTS = nss.lo nfsidmap_common.lo
+nsswitch_la_OBJECTS = $(am_nsswitch_la_OBJECTS)
+nsswitch_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(nsswitch_la_LDFLAGS) $(LDFLAGS) -o $@
+regex_la_DEPENDENCIES = ../../support/nfs/libnfsconf.la
+am_regex_la_OBJECTS = regex.lo
+regex_la_OBJECTS = $(am_regex_la_OBJECTS)
+regex_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(regex_la_LDFLAGS) $(LDFLAGS) -o $@
+static_la_DEPENDENCIES = ../../support/nfs/libnfsconf.la
+am_static_la_OBJECTS = static.lo
+static_la_OBJECTS = $(am_static_la_OBJECTS)
+static_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(static_la_LDFLAGS) $(LDFLAGS) -o $@
+am__DEPENDENCIES_1 =
+umich_ldap_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+ ../../support/nfs/libnfsconf.la
+am_umich_ldap_la_OBJECTS = umich_ldap.lo
+umich_ldap_la_OBJECTS = $(am_umich_ldap_la_OBJECTS)
+umich_ldap_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(umich_ldap_la_LDFLAGS) $(LDFLAGS) -o $@
+@ENABLE_LDAP_TRUE@am_umich_ldap_la_rpath = -rpath $(pkgplugindir)
+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)/gums.Plo ./$(DEPDIR)/libnfsidmap.Plo \
+ ./$(DEPDIR)/nfsidmap_common.Plo ./$(DEPDIR)/nss.Plo \
+ ./$(DEPDIR)/regex.Plo ./$(DEPDIR)/static.Plo \
+ ./$(DEPDIR)/umich_ldap.Plo
+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 = $(gums_la_SOURCES) $(libnfsidmap_la_SOURCES) \
+ $(nsswitch_la_SOURCES) $(regex_la_SOURCES) \
+ $(static_la_SOURCES) $(umich_ldap_la_SOURCES)
+DIST_SOURCES = $(gums_la_SOURCES) $(libnfsidmap_la_SOURCES) \
+ $(nsswitch_la_SOURCES) $(regex_la_SOURCES) \
+ $(static_la_SOURCES) $(umich_ldap_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+man3dir = $(mandir)/man3
+man5dir = $(mandir)/man5
+NROFF = nroff
+MANS = $(man3_MANS) $(man5_MANS)
+DATA = $(pkgconfig_DATA)
+HEADERS = $(include_HEADERS)
+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 $(srcdir)/libnfsidmap.pc.in \
+ $(top_srcdir)/depcomp AUTHORS COPYING README
+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@
+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@
+@PATH_PLUGINS_FALSE@pkgplugindir = $(libdir)/libnfsidmap
+@PATH_PLUGINS_TRUE@pkgplugindir = $(PATH_PLUGINS)
+@ENABLE_LDAP_FALSE@UMICH_LDAP_LIB =
+@ENABLE_LDAP_TRUE@UMICH_LDAP_LIB = umich_ldap.la
+@ENABLE_GUMS_FALSE@GUMS_MAPPING_LIB =
+@ENABLE_GUMS_TRUE@GUMS_MAPPING_LIB = gums.la
+@ENABLE_LDAP_SASL_TRUE@KRB5_GSS_LIB = -lgssapi_krb5
+lib_LTLIBRARIES = libnfsidmap.la
+pkgplugin_LTLIBRARIES = nsswitch.la static.la regex.la $(UMICH_LDAP_LIB) $(GUMS_MAPPING_LIB)
+
+# Library versioning notes from:
+# http://sources.redhat.com/autobook/autobook/autobook_91.html
+#
+# -version-info <current>:<revision>:<age>
+# <current> The number of the current interface exported by library.
+# <revision> The implementation number of the most recent interface
+# exported by the library. (i.e. revision should be updated
+# with each new release of the library, and reset to zero
+# when <current> is updated.)
+# <age> The number of previous additional interfaces supported
+# by this library.
+libnfsidmap_la_SOURCES = libnfsidmap.c nfsidmap_common.c
+libnfsidmap_la_LDFLAGS = -version-info 1:0:0
+libnfsidmap_la_LIBADD = -ldl ../../support/nfs/libnfsconf.la
+nsswitch_la_SOURCES = nss.c nfsidmap_common.c
+nsswitch_la_LDFLAGS = -module -avoid-version
+nsswitch_la_LIBADD = ../../support/nfs/libnfsconf.la
+static_la_SOURCES = static.c
+static_la_LDFLAGS = -module -avoid-version
+static_la_LIBADD = ../../support/nfs/libnfsconf.la
+regex_la_SOURCES = regex.c
+regex_la_LDFLAGS = -module -avoid-version
+regex_la_LIBADD = ../../support/nfs/libnfsconf.la
+umich_ldap_la_SOURCES = umich_ldap.c
+umich_ldap_la_LDFLAGS = -module -avoid-version
+umich_ldap_la_LIBADD = -lldap $(KRB5_GSS_LIB) ../../support/nfs/libnfsconf.la
+gums_la_SOURCES = gums.c
+gums_la_LDFLAGS = -module -avoid-version
+man3_MANS = nfs4_uid_to_name.3
+man5_MANS = idmapd.conf.5
+include_HEADERS = nfsidmap.h nfsidmap_plugin.h
+EXTRA_DIST = $(man3_MANS) \
+ $(man5_MANS) \
+ libtest.c \
+ idmapd.conf
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libnfsidmap.pc
+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 support/nfsidmap/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu support/nfsidmap/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):
+libnfsidmap.pc: $(top_builddir)/config.status $(srcdir)/libnfsidmap.pc.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+install-pkgpluginLTLIBRARIES: $(pkgplugin_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(pkgplugin_LTLIBRARIES)'; test -n "$(pkgplugindir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkgplugindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkgplugindir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkgplugindir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkgplugindir)"; \
+ }
+
+uninstall-pkgpluginLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkgplugin_LTLIBRARIES)'; test -n "$(pkgplugindir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkgplugindir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkgplugindir)/$$f"; \
+ done
+
+clean-pkgpluginLTLIBRARIES:
+ -test -z "$(pkgplugin_LTLIBRARIES)" || rm -f $(pkgplugin_LTLIBRARIES)
+ @list='$(pkgplugin_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+gums.la: $(gums_la_OBJECTS) $(gums_la_DEPENDENCIES) $(EXTRA_gums_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(gums_la_LINK) $(am_gums_la_rpath) $(gums_la_OBJECTS) $(gums_la_LIBADD) $(LIBS)
+
+libnfsidmap.la: $(libnfsidmap_la_OBJECTS) $(libnfsidmap_la_DEPENDENCIES) $(EXTRA_libnfsidmap_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libnfsidmap_la_LINK) -rpath $(libdir) $(libnfsidmap_la_OBJECTS) $(libnfsidmap_la_LIBADD) $(LIBS)
+
+nsswitch.la: $(nsswitch_la_OBJECTS) $(nsswitch_la_DEPENDENCIES) $(EXTRA_nsswitch_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(nsswitch_la_LINK) -rpath $(pkgplugindir) $(nsswitch_la_OBJECTS) $(nsswitch_la_LIBADD) $(LIBS)
+
+regex.la: $(regex_la_OBJECTS) $(regex_la_DEPENDENCIES) $(EXTRA_regex_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(regex_la_LINK) -rpath $(pkgplugindir) $(regex_la_OBJECTS) $(regex_la_LIBADD) $(LIBS)
+
+static.la: $(static_la_OBJECTS) $(static_la_DEPENDENCIES) $(EXTRA_static_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(static_la_LINK) -rpath $(pkgplugindir) $(static_la_OBJECTS) $(static_la_LIBADD) $(LIBS)
+
+umich_ldap.la: $(umich_ldap_la_OBJECTS) $(umich_ldap_la_DEPENDENCIES) $(EXTRA_umich_ldap_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(umich_ldap_la_LINK) $(am_umich_ldap_la_rpath) $(umich_ldap_la_OBJECTS) $(umich_ldap_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gums.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfsidmap.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfsidmap_common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nss.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regex.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/static.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/umich_ldap.Plo@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 $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man3: $(man3_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man3_MANS)'; \
+ list2=''; \
+ test -n "$(man3dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man3dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man3dir)" || 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 '/\.3[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,^[^3][0-9a-z]*$$,3,;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)$(man3dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man3dir)/$$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)$(man3dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man3dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man3:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man3_MANS)'; test -n "$(man3dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^3][0-9a-z]*$$,3,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man3dir)'; $(am__uninstall_files_from_dir)
+install-man5: $(man5_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man5_MANS)'; \
+ list2=''; \
+ test -n "$(man5dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man5dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man5dir)" || 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 '/\.5[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,^[^5][0-9a-z]*$$,5,;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)$(man5dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$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)$(man5dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man5:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man5_MANS)'; test -n "$(man5dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man5dir)'; $(am__uninstall_files_from_dir)
+install-pkgconfigDATA: $(pkgconfig_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \
+ done
+
+uninstall-pkgconfigDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir)
+install-includeHEADERS: $(include_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
+ done
+
+uninstall-includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(includedir)'; $(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
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$(top_distdir)" distdir="$(distdir)" \
+ dist-hook
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(MANS) $(DATA) $(HEADERS)
+install-pkgpluginLTLIBRARIES: install-libLTLIBRARIES
+
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgplugindir)" "$(DESTDIR)$(man3dir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(includedir)"; 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."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ clean-pkgpluginLTLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/gums.Plo
+ -rm -f ./$(DEPDIR)/libnfsidmap.Plo
+ -rm -f ./$(DEPDIR)/nfsidmap_common.Plo
+ -rm -f ./$(DEPDIR)/nss.Plo
+ -rm -f ./$(DEPDIR)/regex.Plo
+ -rm -f ./$(DEPDIR)/static.Plo
+ -rm -f ./$(DEPDIR)/umich_ldap.Plo
+ -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-includeHEADERS install-man \
+ install-pkgconfigDATA install-pkgpluginLTLIBRARIES
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man3 install-man5
+
+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)/gums.Plo
+ -rm -f ./$(DEPDIR)/libnfsidmap.Plo
+ -rm -f ./$(DEPDIR)/nfsidmap_common.Plo
+ -rm -f ./$(DEPDIR)/nss.Plo
+ -rm -f ./$(DEPDIR)/regex.Plo
+ -rm -f ./$(DEPDIR)/static.Plo
+ -rm -f ./$(DEPDIR)/umich_ldap.Plo
+ -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-includeHEADERS uninstall-libLTLIBRARIES \
+ uninstall-man uninstall-pkgconfigDATA \
+ uninstall-pkgpluginLTLIBRARIES
+
+uninstall-man: uninstall-man3 uninstall-man5
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libLTLIBRARIES clean-libtool \
+ clean-pkgpluginLTLIBRARIES cscopelist-am ctags ctags-am \
+ dist-hook 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-html install-html-am \
+ install-includeHEADERS install-info install-info-am \
+ install-libLTLIBRARIES install-man install-man3 install-man5 \
+ install-pdf install-pdf-am install-pkgconfigDATA \
+ install-pkgpluginLTLIBRARIES install-ps install-ps-am \
+ 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-includeHEADERS uninstall-libLTLIBRARIES \
+ uninstall-man uninstall-man3 uninstall-man5 \
+ uninstall-pkgconfigDATA uninstall-pkgpluginLTLIBRARIES
+
+.PRECIOUS: Makefile
+
+
+# XXX: also exclude debian/files and debian/files.new ? do a clean??
+dist-hook:
+ mkdir $(distdir)/debian/
+ find $(srcdir)/debian -maxdepth 1 -not -type d |xargs -i cp {} $(distdir)/debian/
+
+$(pkgconfig_DATA): $(top_builddir)/config.status
+
+# 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/support/nfsidmap/README b/support/nfsidmap/README
new file mode 100644
index 0000000..5a448ef
--- /dev/null
+++ b/support/nfsidmap/README
@@ -0,0 +1,126 @@
+Library to help mapping id's, mainly for NFSv4.
+
+When NFSv4 is using AUTH_GSS (which currently only supports Kerberos v5), the
+NFSv4 server mapping functions MUST use secure communications.
+
+We provide several mapping functions, configured using /etc/idmapd.conf
+
+As of the 0.21 version of this library, mapping methods are separate
+dynamically-loaded libaries. This allows the separation of any
+LDAP requirements from the main libnfsidmap library. The main library
+now basically loads and calls the functions in the method-specific
+libaries. The method libraries are expected to be named
+"libnfsidmap_<method>.so", for example, "libnfsidmap_nsswitch.so".
+
+Several methods may be specified in the /etc/idmapd.conf configuration
+file. Each method is called until a mapping is found.
+
+The following translation methods are delivered in the default distribution:
+
+nsswitch
+--------
+
+The default method is called nsswitch. This method uses the get password
+file entry functions getpwname(), getpwid(), and the get group file entry
+functions getgrnam(), getgrgid(). The nsswitch method can therefore be
+configured by the /etc/nss_switch.conf passwd data base stanza. If secure
+communications are required (AUTH_GSS), the passwd data base stanza can contain
+the 'file' entry because the rpc.idmapd and rpc.svcgssd run as root, and/or the
+'ldap' entry if the ldap service is configured to use SASL in /etc/ldap.conf.
+The 'nis' entry is NOT recommended, it does not have a secure communications
+mode.
+
+
+static
+------
+
+This method works only for translating GSS authenticated names to local
+names. It uses a static mapping setup defined in the [Static] section
+of the idmapd.conf file. The form of the entries are:
+ <GSS-Authenticated name> = <localuser>
+
+For example:
+ nfs/host.domain.org@DOMAIN.ORG = root
+
+It is recommended that this module be used in combination with another
+module (e.g. the nsswitch module).
+
+umich_ldap
+----------
+An experimental method, umich_ldap uses an LDAP schema and ldap functions
+to perform translations. This method is designed to service remote users,
+allowing remote users to set and get ACLs as well as map GSS principals
+to id's. The functions are LDAP based, and the ldap search filters look
+for attribute names set by idmapd.conf [UMICH_SCHEMA]
+NFSv4_name_attr, NFSv4_group_attr, and GSS_principal_attr.
+
+It is assumed that the LDAP server will index these attributes, and that these
+attributes will be associated with the nss.schema posixAccount uidNumber and
+gidNumber. We expect that the uidNumber and gidNumber attribute will be
+configurable via the idmapd.conf file soon.
+
+NFSv4_name_attr holds an NFSv4 name of the form user@domain, where the domain
+portion of the name is a valid NFSv4 domain name. There is a one-to-one
+mapping between the NFSv4_name_attr name and a UID.
+
+NFSv4_group_attr holds an NFSv4 name of the form group@domain, where the domain
+portion of the name is a valid NFSv4 domain name. There is a one-to-one
+mapping between the NFSv4_group_attr name and a GID.
+
+GSS_principal_attr holds a GSS security mechanism specific context principal
+name. For Kerberos v5, it is a Kerberos principal <service/>principal@REALM.
+For SPKM3, it is a PKI DN such as (line is split):`
+"/C=US/ST=Michigan/O=University of Michigan/OU=UMICH Kerberos
+ Certification Authority/CN=andros/USERID=andros/Email=andros@UMICH.EDU".
+There is a many-to-one relationship between the GSS_principal_attr
+name and a UID plus GID.
+
+We have defined LDAP object classes for our experimental NFSv4 id mapping.
+We made the attribute names configurable so that other sites could still use
+the TR_UMICH_LDAP translation functions with different LDAP attribute names.
+
+We use the same attribute name, NFSv4Name for the NFSv4_name_attr and the
+NFSv4_group_attr. For local users and remote users that we wish to give
+a local machine account, we add the NFSv4Name attribute and the GSSAuthName
+attribute to the existing inetorgPerson and posixAccount schema.
+For remote users that we do not wish to give a local machine account,
+we use the NFSv4RemotePerson object to contain the NFSv4Name, uidNumber,
+gidNumber, and GSSAuthName.
+
+nfsv4.schema
+------------
+attributetype ( 1.3.6.1.4.1.250.1.61
+ NAME ( 'NFSv4Name')
+ DESC 'NFS version 4 Name'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE)
+
+attributetype ( 1.3.6.1.4.1.250.1.62
+ NAME ( 'GSSAuthName')
+ DESC 'RPCSEC GSS authenticated user name'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26)
+
+#
+# minimal information for NFSv4 access. used when local filesystem
+# access is not permitted (nsswitch ldap calls fail), or when
+# inetorgPerson is too much info.
+#
+objectclass ( 1.3.6.1.4.1.250.1.60 NAME 'NFSv4RemotePerson'
+ DESC 'NFS version4 person from remote NFSv4 Domain'
+ SUP top STRUCTURAL
+ MUST ( uidNumber $ gidNumber $ NFSv4Name )
+ MAY ( cn $ GSSAuthName $ description) )
+
+#
+# minimal information for NFSv4 access. used when local filesystem
+# access is not permitted (nsswitch ldap calls fail), or when
+# inetorgPerson is too much info.
+#
+objectclass ( 1.3.6.1.4.1.250.1.63 NAME 'NFSv4RemoteGroup'
+ DESC 'NFS version4 group from remote NFSv4 Domain'
+ SUP top STRUCTURAL
+ MUST ( gidNumber $ NFSv4Name )
+ MAY ( cn $ memberUid $ description) )
+
diff --git a/support/nfsidmap/gums.c b/support/nfsidmap/gums.c
new file mode 100644
index 0000000..1d6eb31
--- /dev/null
+++ b/support/nfsidmap/gums.c
@@ -0,0 +1,787 @@
+/*
+ * gums.c
+ *
+ * Copyright (c) 2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Olga Kornievskaia <aglo@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.
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <err.h>
+#include <syslog.h>
+#include "nfsidmap.h"
+#include "nfsidmap_plugin.h"
+
+#include <voms_apic.h>
+
+#include <prima_logger.h>
+#include <prima_soap_client.h>
+#include <prima_saml_support.h>
+
+#define DEFAULT_PRIMA_CONF_LOCATION "/etc/grid-security/prima-authz.conf"
+#define DEFAULT_VOMSDIR "/etc/grid-security/vomsdir"
+#define DEFAULT_CADIR "/etc/grid-security/certificates"
+#define X509_DN_SIZE 1024
+
+//#define DEBUG_PRINT_VOMS
+
+#define USING_TEST_PROGRAM
+#ifdef USING_TEST_PROGRAM
+nfs4_idmap_log_function_t idmap_log_func = printf;
+int idmap_verbosity = 10;
+#endif
+
+/*
+ * GUMS Translation Methods
+ *
+ */
+
+/* global variables. voms/gums configuration attributes*/
+static char prima_conf[] = DEFAULT_PRIMA_CONF_LOCATION;
+typedef struct _plugin_config_params {
+ char *saml_schema_dir;
+ int saml_log_level;
+ char *server_cert;
+ char *server_key;
+ char *ca_dir;
+ char *gums_server_location;
+ char *voms_dir;
+} plugin_config_params;
+plugin_config_params conf;
+
+#ifdef VOMS_BUG
+static void my_VOMS_Delete(struct voms *v)
+{
+ int i;
+
+ if (!v) return;
+ if (v->user)
+ free(v->user);
+ if (v->server)
+ free(v->server);
+ if (v->fqan) {
+ for (i = 0; v->fqan[i] != NULL; i++)
+ free(v->fqan[i]);
+ free(v->fqan);
+ }
+ free(v);
+}
+
+static struct voms *my_VOMS_Copy(struct voms *v, int *err)
+{
+ struct voms *cv;
+ int i;
+
+ cv = calloc(1, sizeof(struct voms));
+ if (cv == NULL)
+ goto out;
+ cv->user = strdup(v->user);
+ if (cv->user == NULL)
+ goto out;
+ cv->server = strdup(v->server);
+ if (cv->server == NULL)
+ goto out;
+ for (i = 0; v->fqan[i] != NULL; i++) {
+ if (v->fqan[i] == NULL)
+ break;
+ }
+ cv->fqan = calloc(i+1, sizeof(char *));
+ if (cv->fqan == NULL)
+ goto out;
+ cv->fqan[i] = NULL;
+ for (i = 0; v->fqan[i] != NULL; i++) {
+ cv->fqan[i] = strdup(v->fqan[i]);
+ if (cv->fqan[i] == NULL)
+ goto out;
+ }
+ return cv;
+out:
+ if (cv)
+ my_VOMS_Delete(cv);
+
+ return NULL;
+}
+#endif
+
+
+#ifdef DEBUG_PRINT_VOMS
+void printvoms(struct voms *v)
+{
+ int j;
+
+ printf("SIGLEN: %d\nUSER: %s\n", v->siglen, v->user);
+ printf("UCA: %s\nSERVER: %s\n", v->userca, v->server);
+ printf("SCA: %s\nVO: %s\n", v->serverca, v->voname);
+ printf("URI: %s\nDATE1: %s\n", v->uri, v->date1);
+ printf("DATE2: %s\n", v->date2);
+
+ switch (v->type) {
+ case TYPE_NODATA:
+ printf("NO DATA\n");
+ break;
+ case TYPE_CUSTOM:
+ printf("%*s\n", v->datalen - 10, v->custom);
+ break;
+ case TYPE_STD:
+ j = 0;
+ while (v->std[j]) {
+ printf("GROUP: %s\nROLE: %s\nCAP: %s\n",v->std[j]->group,
+ v->std[j]->role,v->std[j]->cap);
+ j++;
+ }
+ }
+}
+
+void print(struct vomsdata *d)
+{
+ struct voms **vo = d->data;
+ struct voms *v;
+ int k = 0;
+
+ while(vo[k]) {
+ v = vo[k++];
+ printf("%d *******************************************\n",k);
+ printvoms(v);
+ }
+
+ if (d->workvo)
+ printf("WORKVO: %s\n", d->workvo);
+
+ if (d->extra_data)
+ printf("EXTRA: %s\n", d->extra_data);
+}
+#endif
+
+static void free_plugin_config_params()
+{
+ if (conf.saml_schema_dir)
+ free(conf.saml_schema_dir);
+ conf.saml_schema_dir = NULL;
+ if (conf.server_cert)
+ free(conf.server_cert);
+ conf.server_cert = NULL;
+ if (conf.server_key)
+ free(conf.server_key);
+ conf.server_key = NULL;
+ if (conf.ca_dir)
+ free(conf.ca_dir);
+ conf.ca_dir = NULL;
+ if (conf.voms_dir)
+ free(conf.voms_dir);
+ conf.voms_dir = NULL;
+}
+
+static int validate_plugin_config_params()
+{
+ if (conf.saml_schema_dir == NULL ||
+ conf.server_cert == NULL ||
+ conf.server_key == NULL ||
+ conf.gums_server_location == NULL)
+ return -1;
+
+ if (conf.ca_dir == NULL) {
+ conf.ca_dir = strdup(DEFAULT_CADIR);
+ if (conf.ca_dir == NULL)
+ return -1;
+ }
+ if (conf.voms_dir == NULL) {
+ conf.voms_dir = strdup(DEFAULT_VOMSDIR);
+ if (conf.voms_dir == NULL)
+ return -1;
+ }
+ return 0;
+}
+
+static int gums_init(void)
+{
+ FILE *f = NULL;
+ int ret = -1, i = 0;
+ char buf[512], type[128], value[256];
+ char *alt_conf = NULL;
+
+ alt_conf = nfsidmap_config_get("GUMS", "Conf_File");
+ if (alt_conf == NULL)
+ f = fopen(prima_conf, "r");
+ else
+ f = fopen(alt_conf, "r");
+ if (f == NULL)
+ goto out;
+
+ while (fgets(buf, 512, f)) {
+ i = 0;
+ while(buf[i] == ' ' || buf[i] == '\t')
+ i++;
+ if (buf[i] == '#' || buf[i] == '\0' || buf[i] == '\n')
+ continue;
+ if (sscanf(&buf[i], "%127s%255s",type,value) < 2) {
+ IDMAP_LOG(0, ("ERROR: malformed line: %s\n", &buf[i]));
+ goto out;
+ }
+ IDMAP_LOG(1, ("PRIMA conf: type=%s value=%s\n", type, value));
+ if (strncmp(type, "imsContact", 10) == 0) {
+ conf.gums_server_location = strdup(value);
+ } else if (strncmp(type, "serviceCert", 11) == 0) {
+ conf.server_cert = strdup(value);
+ } else if (strncmp(type, "serviceKey", 10) == 0) {
+ conf.server_key = strdup(value);
+ } else if (strncmp(type, "caCertDir", 9) == 0) {
+ conf.ca_dir = strdup(value);
+ } else if (strncmp(type, "samlSchemaDir", 13) == 0) {
+ conf.saml_schema_dir = strdup(value);
+ } else if (strncmp(type, "logLevel", 8) == 0) {
+ if (strncmp(value, "debug", 5) == 0)
+ conf.saml_log_level = PRIMA_LOG_DEBUG;
+ else if (strncmp(value, "error", 5) == 0)
+ conf.saml_log_level = PRIMA_LOG_ERROR;
+ else if (strncmp(value, "none", 4) == 0)
+ conf.saml_log_level = PRIMA_LOG_NONE;
+ else
+ conf.saml_log_level = PRIMA_LOG_INFO;
+ }
+ }
+
+ if (validate_plugin_config_params() != 0)
+ goto out;
+
+ ret = 0;
+out:
+ if (f)
+ fclose(f);
+ if (ret)
+ free_plugin_config_params();
+
+ return ret;
+}
+
+static int retrieve_attributes(X509 *cert, STACK_OF(X509) *cas,
+ struct voms **attrs)
+{
+ int ret = -1, err = 0;
+ struct vomsdata *vd = NULL;
+
+ vd = VOMS_Init(conf.voms_dir, conf.ca_dir);
+ if (vd == NULL) {
+ IDMAP_LOG(0, ("VOMS_Init failed\n"));
+ return -1;
+ }
+ ret = VOMS_Retrieve(cert, cas, RECURSE_CHAIN, vd, &err);
+ if (err) {
+ char *err_msg;
+ err_msg = VOMS_ErrorMessage(vd, err, NULL, 0);
+ if (err == VERR_NOEXT)
+ ret = 0;
+ else
+ IDMAP_LOG(0, ("VOMS error %s\n", err_msg));
+ goto out;
+ } else if (ret) {
+ struct voms *v, *v2;
+#ifdef DEBUG_PRINT_VOMS
+ print(vd);
+#endif
+ v = VOMS_DefaultData(vd, &err);
+ if (err == VERR_NONE) {
+#ifdef DEBUG_PRINT_VOMS
+ printvoms(v);
+ while (v->fqan[i] != NULL)
+ IDMAP_LOG(1, ("user's fqan: %s\n", v->fqan[i++]));
+#endif
+#ifdef VOMS_BUG
+ v2 = my_VOMS_Copy(v, &err);
+#else
+ v2 = VOMS_Copy(v, &err);
+#endif
+ if (v2 == NULL) {
+ IDMAP_LOG(0, ("VOMS_Copy failed err=%d\n", err));
+ goto out;
+ }
+ *attrs = v2;
+ }
+ }
+ ret = 0;
+out:
+ if (vd)
+ VOMS_Destroy(vd);
+ return ret;
+}
+
+static int get_server_dn(unsigned char **server_dn)
+{
+ BIO *tmp = NULL;
+ X509 *cert = NULL;
+ int ret = -1;
+ char dn[X509_DN_SIZE];
+
+ tmp = BIO_new(BIO_s_file());
+ if (tmp == NULL)
+ goto out;
+
+ ret = BIO_read_filename(tmp, conf.server_cert);
+ if (ret == 0) {
+ ret = errno;
+ goto out;
+ }
+
+ cert = (X509 *) PEM_read_bio_X509(tmp, NULL, NULL, NULL);
+ if (cert == NULL)
+ goto out;
+
+ X509_NAME_oneline(X509_get_subject_name(cert), dn, sizeof(dn));
+
+ *server_dn = strdup(dn);
+ if (*server_dn == NULL)
+ goto out;
+
+ ret = 0;
+out:
+ if (tmp)
+ BIO_free(tmp);
+ if (cert)
+ X509_free(cert);
+
+ return ret;
+}
+
+static int create_saml_request(char *dn, struct voms *attrs, char **saml_req)
+{
+ int ret = -1, i;
+ char *req = NULL;
+ unsigned char *server_dn = NULL;
+ prima_saml_fqans fqans;
+
+ IDMAP_LOG(2, ("create_saml_request start\n"));
+ ret = initPrimaSAMLFQANs(&fqans);
+ if (ret) {
+ IDMAP_LOG(0, ("initPrimaSAMLFQANs failed with %d\n", ret));
+ goto out;
+ }
+
+ if (attrs) {
+ for (i = 0; attrs->fqan[i] != NULL; i++) {
+ ret = addPrimaSAMLFQAN(&fqans, attrs->server, attrs->fqan[i]);
+ IDMAP_LOG(1, ("addPrimaSAMLFQAN returned %d\n", ret));
+ }
+ dn = attrs->user;
+ } else
+ IDMAP_LOG(1, ("No VOMS attributes present in the cert\n"));
+
+ if (get_server_dn(&server_dn) != 0)
+ goto out;
+ req = createSAMLQueryAndRequest(server_dn, dn, &fqans);
+ if (req == NULL) {
+ IDMAP_LOG(0, ("createSAMLQueryAndRequest failed to create "
+ "SAML request\n"));
+ goto out;
+ }
+ IDMAP_LOG(1, ("SAML Request %s\n", req));
+
+ ret = 0;
+ *saml_req = req;
+out:
+ cleanupPrimaSAMLFQANs(&fqans);
+
+ if (server_dn)
+ free(server_dn);
+
+ IDMAP_LOG(2, ("create_saml_request returning %d\n", ret));
+ return ret;
+}
+
+static int process_parameters(extra_mapping_params **ex, X509 **user_cert,
+ STACK_OF(X509) **user_chain)
+{
+
+ int ret = -1, i;
+ X509 *cert = NULL, *x;
+ STACK_OF(X509) *chain = NULL;
+ unsigned char *p;
+
+ if (ex[0]->content_type != X509_CERT)
+ return -1;
+
+ /* get user's x509 certificate */
+ p = ex[0]->content;
+ cert = d2i_X509(NULL, &p, ex[0]->content_len);
+ if (cert == NULL)
+ goto out;
+
+ /* get user's other certificates */
+ chain = sk_X509_new_null();
+ if (chain == NULL)
+ goto out;
+ for (i = 1; ex[i] != NULL; i++) {
+ if (ex[i]->content_type != X509_CERT)
+ continue;
+ p = ex[i]->content;
+ x = d2i_X509(NULL, &p, ex[i]->content_len);
+ if (x == NULL)
+ goto out;
+ sk_X509_push(chain, x);
+ }
+ ret = 0;
+
+ *user_cert = cert;
+ *user_chain = chain;
+out:
+ if (ret) {
+ int num;
+ if (cert)
+ X509_free(cert);
+ if (chain)
+ sk_X509_pop_free(chain, X509_free);
+ }
+
+ return ret;
+}
+
+struct pwbuf {
+ struct passwd pwbuf;
+ char buf[1];
+};
+
+static int translate_to_uid(char *local_uid, uid_t *uid, uid_t *gid)
+{
+ int ret = -1;
+ struct passwd *pw = NULL;
+ struct pwbuf *buf = NULL;
+ size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+
+ buf = malloc(sizeof(*buf) + buflen);
+ if (buf == NULL)
+ goto out;
+
+ ret = getpwnam_r(local_uid, &buf->pwbuf, buf->buf, buflen, &pw);
+ if (pw == NULL) {
+ IDMAP_LOG(0, ("getpwnam: name %s not found\n", local_uid));
+ goto out;
+ }
+ *uid = pw->pw_uid;
+ *gid = pw->pw_gid;
+
+ ret = 0;
+out:
+ if (buf)
+ free(buf);
+ return ret;
+}
+
+static int translate_to_gid(char *local_gid, uid_t *gid)
+{
+ struct group *gr = NULL;
+ struct group grbuf;
+ char *buf = NULL;
+ size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ int ret = -1;
+
+ do {
+ buf = malloc(buflen);
+ if (buf == NULL)
+ goto out;
+
+ ret = -getgrnam_r(local_gid, &grbuf, buf, buflen, &gr);
+ if (gr == NULL && !ret)
+ ret = -ENOENT;
+ if (ret == -ERANGE) {
+ buflen *= 2;
+ free(buf);
+ }
+ } while (ret == -ERANGE);
+
+ if (ret)
+ goto out;
+
+ *gid = gr->gr_gid;
+
+ ret = 0;
+out:
+ if (buf)
+ free(buf);
+ return ret;
+}
+
+static int gums_gss_princ_to_ids(char *secname, char *princ,
+ uid_t *uid, uid_t *gid,
+ extra_mapping_params **ex)
+{
+ int ret = -1, size, i;
+ X509 *cert = NULL;
+ STACK_OF(X509) *cas = NULL;
+ char dn[X509_DN_SIZE];
+ struct voms *attrs = NULL;
+ char *saml_req = NULL, *saml_resp = NULL;
+ int saml_result;
+ char *local_uid = NULL, *local_gid = NULL, *p;
+
+ /* accept only spkm3 translations */
+ if (strcmp(secname, "spkm3"))
+ return -EINVAL;
+
+ /* must supply either a DN and/or at least 1 binary blob */
+ if (princ == NULL && (ex == NULL || (ex && ex[0] == NULL)))
+ return -EINVAL;
+
+ /* process extra parameters */
+ if (process_parameters(ex, &cert, &cas) != 0)
+ goto out;
+
+ IDMAP_LOG(1, ("Processing name translation of client\n"));
+ X509_NAME_oneline(X509_get_subject_name(cert), dn, sizeof(dn));
+ IDMAP_LOG(1, ("DN=%s\n", dn));
+ size = sk_X509_num(cas);
+ IDMAP_LOG(1, ("Including following CAs (%d)\n", size));
+ for (i=0; i < size; i++) {
+ X509_NAME_oneline(X509_get_subject_name(sk_X509_value(cas, i)),
+ dn, sizeof(dn));
+ IDMAP_LOG(1, ("DN=%s\n", dn));
+ }
+
+ /* retrieve VOMS attributes */
+ if (retrieve_attributes(cert, cas, &attrs) != 0)
+ goto out;
+ if (attrs == NULL)
+ X509_NAME_oneline(X509_get_subject_name(cert), dn, sizeof(dn));
+
+ /* initialize SAML library */
+ if (initPrimaSAMLSupport(conf.saml_schema_dir,
+ conf.saml_log_level) != 0) {
+ IDMAP_LOG(0, ("initPrimaSAMLSupport failed\n"));
+ goto out;
+ }
+
+ /* create SAML request */
+ if (create_saml_request(dn, attrs, &saml_req) != 0)
+ goto out;
+
+ /* contact GUMS server */
+ saml_resp = queryIdentityMappingService(conf.gums_server_location,
+ saml_req, conf.server_cert, conf.server_key,
+ conf.ca_dir);
+ if (saml_resp != NULL) {
+ saml_result = processResponse(saml_resp, saml_req, &local_uid,
+ &local_gid);
+ IDMAP_LOG(1, ("processResponse returned %d\n", saml_result));
+ if (saml_result || local_uid == NULL) {
+ IDMAP_LOG(0, ("processResponse failed to return "
+ "local id\n"));
+ ret = -ENOENT;
+ goto out;
+ }
+ IDMAP_LOG(1, ("GUMS returned uid=%s gid=%s\n", local_uid,
+ local_gid));
+ }
+
+ /* translate account name to uid */
+ if (translate_to_uid(local_uid, uid, gid))
+ goto out;
+ if (local_gid)
+ if (translate_to_gid(local_gid, gid))
+ goto out;
+
+ ret = 0;
+out:
+ if (cert)
+ X509_free(cert);
+
+ if (cas)
+ sk_X509_pop_free(cas, X509_free);
+
+ if (attrs)
+#ifdef VOMS_BUG
+ my_VOMS_Delete(attrs);
+#else
+ VOMS_Delete(attrs);
+#endif
+
+ if (saml_req)
+ free(saml_req);
+
+ if (saml_resp)
+ free(saml_resp);
+
+ cleanupPrimaSAMLSupport();
+
+ return ret;
+}
+
+struct trans_func gums_trans = {
+ .name = "gums",
+ .init = gums_init,
+ .princ_to_ids = gums_gss_princ_to_ids,
+ .name_to_uid = NULL,
+ .name_to_gid = NULL,
+ .uid_to_name = NULL,
+ .gid_to_name = NULL,
+ .gss_princ_to_grouplist = NULL,
+};
+
+struct trans_func *libnfsidmap_plugin_init()
+{
+ return (&gums_trans);
+}
+
+#ifdef USING_TEST_PROGRAM
+static STACK_OF(X509) *load_chain(char *certfile)
+{
+ STACK_OF(X509_INFO) *sk=NULL;
+ STACK_OF(X509) *stack=NULL, *ret=NULL;
+ BIO *in=NULL;
+ X509_INFO *xi;
+ int first = 1;
+
+ if (!(stack = sk_X509_new_null())) {
+ printf("memory allocation failure\n");
+ goto end;
+ }
+
+ if (!(in=BIO_new_file(certfile, "r"))) {
+ printf("error opening the file, %s\n",certfile);
+ goto end;
+ }
+
+ /* This loads from a file, a stack of x509/crl/pkey sets */
+ if (!(sk=(STACK_OF(X509_INFO) *)PEM_X509_INFO_read_bio(in,NULL,NULL,NULL))) {
+ /* if (!(sk=PEM_X509_read_bio(in,NULL,NULL,NULL))) { */
+ printf("error reading the file, %s\n",certfile);
+ goto end;
+ }
+
+ /* scan over it and pull out the certs */
+ while (sk_X509_INFO_num(sk)) {
+ /* skip first cert */
+ if (first) {
+ xi=sk_X509_INFO_shift(sk);
+ X509_INFO_free(xi);
+ first = 0;
+ continue;
+ }
+ xi=sk_X509_INFO_shift(sk);
+ if (xi->x509 != NULL) {
+ sk_X509_push(stack,xi->x509);
+ xi->x509=NULL;
+ }
+ X509_INFO_free(xi);
+ }
+ if (!sk_X509_num(stack)) {
+ printf("no certificates in file, %s\n",certfile);
+ sk_X509_free(stack);
+ goto end;
+ }
+ ret=stack;
+end:
+ BIO_free(in);
+ sk_X509_INFO_free(sk);
+ return(ret);
+}
+
+void create_params(X509 *cert, STACK_OF(X509) *cas,
+ extra_mapping_params ***ret_params)
+{
+ int len = 0, i, size = 0;
+ unsigned char *p, *buf = NULL;
+ extra_mapping_params **params = NULL;
+ X509 *x;
+
+ if (cas)
+ size = sk_X509_num(cas);
+ params = malloc((size+2)*sizeof(extra_mapping_params *));
+ params[size+1] = NULL;
+
+ /* 1st element is user's certificate */
+ len = i2d_X509(cert, NULL);
+ p = buf = malloc(len);
+ i2d_X509(cert, &p);
+ params[0] = malloc(sizeof(extra_mapping_params));
+ params[0]->content_type = X509_CERT;
+ params[0]->content = buf;
+ params[0]->content_len = len;
+
+ /* add other certificates to the array */
+ for (i = 0; i < size; i++) {
+ x = sk_X509_value(cas, i);
+ params[i+1] = malloc(sizeof(extra_mapping_params));
+ len = i2d_X509(x, NULL);
+ p = buf = malloc(len);
+ i2d_X509(x, &p);
+ params[i+1]->content_type = X509_CERT;
+ params[i+1]->content = buf;
+ params[i+1]->content_len = len;
+ }
+ *ret_params = params;
+}
+
+int main(void)
+{
+ int uid, gid, ret, i;
+ extra_mapping_params **params = NULL;
+ BIO *tmp = NULL;
+ X509 *cert = NULL, *x;
+ STACK_OF(X509) *cas = NULL;
+ unsigned char *proxy_file;
+
+ if (gums_init())
+ return -1;
+ proxy_file = getenv("X509_USER_PROXY");
+ if (proxy_file == NULL) {
+ fprintf(stderr, "X509_USER_PROXY is not set\n");
+ return -1;
+ }
+ tmp = BIO_new(BIO_s_file());
+ BIO_read_filename(tmp, proxy_file);
+ cert = (X509 *) PEM_read_bio_X509(tmp, NULL, NULL, NULL);
+ cas = load_chain(proxy_file);
+ create_params(cert, cas, &params);
+ ret = gums_gss_princ_to_ids("spkm3", NULL, &uid, &gid, params);
+ fprintf(stderr, "gums_gss_princ_to_ids returns %d uid=%d gid=%d\n",
+ ret, uid, gid);
+
+ if (tmp)
+ BIO_free(tmp);
+ if (cert)
+ X509_free(cert);
+ if (cas)
+ sk_X509_pop_free(cas, X509_free);
+
+ free_plugin_config_params();
+
+ if (params) {
+ for (i=0; params[i] != NULL; i++) {
+ free(params[i]->content);
+ free(params[i]);
+ }
+ free(params);
+ }
+
+ return 0;
+}
+#endif
diff --git a/support/nfsidmap/idmapd.conf b/support/nfsidmap/idmapd.conf
new file mode 100644
index 0000000..2a2f79a
--- /dev/null
+++ b/support/nfsidmap/idmapd.conf
@@ -0,0 +1,169 @@
+[General]
+#Verbosity = 0
+# The following should be set to the local NFSv4 domain name
+# The default is the host's DNS domain name.
+#Domain = local.domain.edu
+
+# In multi-domain environments, some NFS servers will append the identity
+# management domain to the owner and owner_group in lieu of a true NFSv4
+# domain. This option can facilitate lookups in such environments. If
+# set to a value other than "none", the nsswitch plugin will first pass
+# the name to the password/group lookup function without stripping the
+# domain off. If that mapping fails then the plugin will try again using
+# the old method (comparing the domain in the string to the Domain value,
+# stripping it if it matches, and passing the resulting short name to the
+# lookup function). Valid values are "user", "group", "both", and
+# "none". The default is "none".
+#No-Strip = none
+
+# Winbind has a quirk whereby doing a group lookup in UPN format
+# (e.g. staff@americas.example.com) will cause the group to be
+# displayed prefixed with the full domain in uppercase
+# (e.g. AMERICAS.EXAMPLE.COM\staff) instead of in the familiar netbios
+# name format (e.g. AMERICAS\staff). Setting this option to true
+# causes the name to be reformatted before passing it to the group
+# lookup function in order to work around this. This setting is
+# ignored unless No-Strip is set to either "both" or "group".
+# The default is "false".
+#Reformat-Group = false
+
+# The following is a comma-separated list of Kerberos realm
+# names that should be considered to be equivalent to the
+# local realm, such that <user>@REALM.A can be assumed to
+# be the same user as <user>@REALM.B
+# If not specified, the default local realm is the domain name,
+# which defaults to the host's DNS domain name,
+# translated to upper-case.
+# Note that if this value is specified, the local realm name
+# must be included in the list!
+#Local-Realms =
+
+[Mapping]
+
+#Nobody-User = nobody
+#Nobody-Group = nobody
+
+[Translation]
+
+# Translation Method is an comma-separated, ordered list of
+# translation methods that can be used. Distributed methods
+# include "nsswitch", "umich_ldap", and "static". Each method
+# is a dynamically loadable plugin library.
+# New methods may be defined and inserted in the list.
+# The default is "nsswitch".
+#Method = nsswitch
+
+# Optional. This is a comma-separated, ordered list of
+# translation methods to be used for translating GSS
+# authenticated names to ids.
+# If this option is omitted, the same methods as those
+# specified in "Method" are used.
+#GSS-Methods = <alternate method list for translating GSS names>
+
+#-------------------------------------------------------------------#
+# The following are used only for the "static" Translation Method.
+#-------------------------------------------------------------------#
+[Static]
+
+# A "static" list of GSS-Authenticated names to
+# local user name mappings
+
+#someuser@REALM = localuser
+
+
+#-------------------------------------------------------------------#
+# The following are used only for the "umich_ldap" Translation Method.
+#-------------------------------------------------------------------#
+
+[UMICH_SCHEMA]
+
+# server information (REQUIRED)
+LDAP_server = ldap-server.local.domain.edu
+
+# the default search base (REQUIRED)
+LDAP_base = dc=local,dc=domain,dc=edu
+
+#-----------------------------------------------------------#
+# The remaining options have defaults (as shown)
+# and are therefore not required.
+#-----------------------------------------------------------#
+
+# whether or not to perform canonicalization on the
+# name given as LDAP_server
+#LDAP_canonicalize_name = true
+
+# absolute search base for (people) accounts
+#LDAP_people_base = <LDAP_base>
+
+# absolute search base for groups
+#LDAP_group_base = <LDAP_base>
+
+# Whether to follow ldap referrals
+#LDAP_follow_referrals = true
+
+# Set to true to enable SSL - anything else is not enabled
+#LDAP_use_ssl = false
+
+# Controls the LDAP server certificate validation behavior
+# It can take the same values as ldap.conf(5)'s TLS_REQCERT
+# tunable
+#LDAP_tls_reqcert = "hard"
+
+# Location of CA certificate, mandatory if LDAP_tls_reqcert
+# is not set to "never"
+#LDAP_ca_cert = /etc/ldapca.cert
+
+# SASL mechanism to use while binding to LDAP
+#LDAP_sasl_mech = <SASL mech>
+
+# SASL realm to be used for SASL auth
+#LDAP_sasl_realm = <SASL realm>
+
+# Authentication identity to be used for SASL auth
+#LDAP_sasl_authcid = <SASL authcid>
+
+# Authorization identity for SASL auth
+#LDAP_sasl_authzid = <SASL authzid>
+
+# Cyrus SASL security properties
+#LDAP_sasl_secprops = <secprops>
+
+# Specifies whether the LDAP server hostname should be canonicalised.
+# If set to yes LDAP lib with do a reverse hostname lookup.
+# If this is not set the LDAP library's default will be used.
+#LDAP_sasl_canonicalize <yes | no>
+
+# Specifies the kerberos ticket cache to be used
+#LDAP_sasl_krb5_ccname = <kerberos ticket cache>
+
+# Objectclass mapping information
+
+# Mapping for the person (account) object class
+#NFSv4_person_objectclass = NFSv4RemotePerson
+
+# Mapping for the nfsv4name attribute the person object
+#NFSv4_name_attr = NFSv4Name
+
+# Mapping for the UID number
+#NFSv4_uid_attr = UIDNumber
+
+# Mapping for the GSSAPI Principal name
+#GSS_principal_attr = GSSAuthName
+
+# Mapping for the account name attribute (usually uid)
+# The value for this attribute must match the value of
+# the group member attribute - NFSv4_member_attr
+#NFSv4_acctname_attr = uid
+
+# Mapping for the group object class
+#NFSv4_group_objectclass = NFSv4RemoteGroup
+
+# Mapping for the GID attribute
+#NFSv4_gid_attr = GIDNumber
+
+# Mapping for the Group NFSv4 name
+#NFSv4_group_attr = NFSv4Name
+
+# Mapping for the Group member attribute (usually memberUID)
+# The value of this attribute must match the value of NFSv4_acctname_attr
+#NFSv4_member_attr = memberUID
diff --git a/support/nfsidmap/idmapd.conf.5 b/support/nfsidmap/idmapd.conf.5
new file mode 100644
index 0000000..87e39bb
--- /dev/null
+++ b/support/nfsidmap/idmapd.conf.5
@@ -0,0 +1,411 @@
+.\"
+.\" idmapd.conf(5)
+.\"
+.\" COPYRIGHT (c) 2008
+.\" 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.
+.\"
+.TH idmapd.conf 5 "19 Nov 2008"
+.SH NAME
+idmapd.conf \- configuration file for libnfsidmap
+.SH SYNOPSIS
+Configuration file for libnfsidmap. Used by idmapd and svcgssd to map NFSv4 name to and from ids.
+.SH DESCRIPTION
+The
+.B idmapd.conf
+configuration file consists of several sections, initiated by strings of the
+form [General] and [Mapping]. Each section may contain lines of the form
+.nf
+ variable = value
+.fi
+The recognized sections and their recognized variables are as follows:
+.\"
+.\" -------------------------------------------------------------------
+.\" The [General] section
+.\" -------------------------------------------------------------------
+.\"
+.SS "[General] section variables"
+.nf
+
+
+.fi
+.TP
+.B Verbosity
+Verbosity level of debugging
+(Default: 0)
+.TP
+.B Domain
+The local NFSv4 domain name. An NFSv4 domain is a namespace with
+a unique username<->UID and groupname<->GID mapping.
+(Default: Host's fully-qualified DNS domain name)
+.TP
+.B No-Strip
+In multi-domain environments, some NFS servers will append the identity
+management domain to the owner and owner_group in lieu of a true NFSv4
+domain. This option can facilitate lookups in such environments. If
+set to a value other than "none", the nsswitch plugin will first pass
+the name to the password/group lookup function without stripping the
+domain off. If that mapping fails then the plugin will try again using
+the old method (comparing the domain in the string to the Domain value,
+stripping it if it matches, and passing the resulting short name to the
+lookup function). Valid values are "user", "group", "both", and
+"none".
+(Default: "none")
+.TP
+.B Reformat-Group
+Winbind has a quirk whereby doing a group lookup in UPN format
+(e.g. staff@americas.example.com) will cause the group to be
+displayed prefixed with the full domain in uppercase
+(e.g. AMERICAS.EXAMPLE.COM\\staff) instead of in the familiar netbios
+name format (e.g. AMERICAS\\staff). Setting this option to true
+causes the name to be reformatted before passing it to the group
+lookup function in order to work around this. This setting is
+ignored unless No-Strip is set to either "both" or "group".
+(Default: "false")
+.TP
+.B Local-Realms
+A comma-separated list of Kerberos realm names that may be considered equivalent to the
+local realm name. For example, users juser@ORDER.EDU and juser@MAIL.ORDER.EDU
+may be considered to be the same user in the specified
+.B Domain.
+(Default: the host's default realm name)
+.br
+.B Note:
+If a value is specified here, the default local realm must be included as well.
+.\"
+.\" -------------------------------------------------------------------
+.\" The [Mapping] section
+.\" -------------------------------------------------------------------
+.\"
+.SS "[Mapping] section variables"
+.nf
+
+.fi
+.TP
+.B Nobody-User
+Local user name to be used when a mapping cannot be completed.
+.TP
+.B Nobody-Group
+Local group name to be used when a mapping cannot be completed.
+.\"
+.\" -------------------------------------------------------------------
+.\" The [Translation] section
+.\" -------------------------------------------------------------------
+.\"
+.SS "[Translation] section variables"
+.nf
+
+.fi
+.TP
+.B Method
+A comma-separated, ordered list of mapping methods (plug-ins)
+to use when mapping between NFSv4 names and local IDs. Each
+specified method is tried in order until a mapping is found,
+or there are no more methods to try. The methods included in
+the default distribution include "nsswitch", "umich_ldap", and
+"static".
+(Default: nsswitch)
+.TP
+.B GSS-Methods
+An optional comma-separated, ordered list of mapping methods (plug-ins)
+to use when mapping between GSS Authenticated names and local IDs.
+(Default: the same list as specified for
+.B Method)
+.\"
+.\" -------------------------------------------------------------------
+.\" The [Static] section
+.\" -------------------------------------------------------------------
+.\"
+.SS "[Static] section variables"
+.nf
+
+.fi
+The "static" translation method uses a static list of GSS-Authenticated
+names to local user names. Entries in the list are of the form:
+.nf
+ principal@REALM = localusername
+.fi
+.\"
+.\" -------------------------------------------------------------------
+.\" The [REGEX] section
+.\" -------------------------------------------------------------------
+.\"
+.SS "[REGEX] section variables"
+.nf
+
+.fi
+If the "regex" translation method is specified, the following
+variables within the [REGEX] section are used to map between NFS4 names and local IDs.
+.TP
+.B User-Regex
+Case-insensitive regular expression that extracts the local user name from an NFSv4 name. Multiple expressions may be concatenated with '|'. The first match will be used.
+There is no default. A basic regular expression for domain DOMAIN.ORG and realm MY.DOMAIN.ORG would be:
+.nf
+^DOMAIN\\([^@]+)@MY.DOMAIN.ORG$
+.fi
+.TP
+.B Group-Regex
+Case-insensitive regular expression that extracts the local group name from an NFSv4 name. Multiple expressions may be concatenated with '|'. The first match will be used.
+There is no default. A basic regular expression for domain DOMAIN.ORG and realm MY.DOMAIN.ORG would be:
+.nf
+^([^@]+)@DOMAIN.ORG@MY.DOMAIN.ORG$|^DOMAIN\\([^@]+)@MY.DOMAIN.ORG$
+.fi
+.TP
+.B Prepend-Before-User
+Constant string to put before a local user name when building an NFSv4 name. Usually this is the short domain name followed by '\'.
+(Default: none)
+.TP
+.B Append-After-User
+Constant string to put after a local user name when building an NFSv4 name. Usually this is '@' followed by the default realm.
+(Default: none)
+.TP
+.B Prepend-Before-Group
+Constant string to put before a local group name when building an NFSv4 name. Usually not used.
+(Default: none)
+.TP
+.B Append-After-Group
+Constant string to put before a local group name when building an NFSv4 name. Usually this is '@' followed by the domain name followed by another '@' and the default realm.
+(Default: none)
+.TP
+.B Group-Name-Prefix
+Constant string that is prepended to a local group name when converting it to an NFSv4 name. If an NFSv4 group name has this prefix it is removed when converting it to a local group name.
+With this group names of a central directory can be shortened for an isolated organizational unit if all groups have a common prefix.
+(Default: none)
+.TP
+.B Group-Name-No-Prefix-Regex
+Case-insensitive regular expression to exclude groups from adding and removing the prefix set by
+.BR Group-Name-Prefix .
+The regular expression must match both the remote and local group names. Multiple expressions may be concatenated with '|'.
+(Default: none)
+.\"
+.\" -------------------------------------------------------------------
+.\" The [UMICH_SCHEMA] section
+.\" -------------------------------------------------------------------
+.\"
+.SS "[UMICH_SCHEMA] section variables"
+.nf
+
+.fi
+If the "umich_ldap" translation method is specified, the following
+variables within the [UMICH_SCHEMA] section are used.
+.TP
+.B LDAP_server
+LDAP server name or address
+(Required if using UMICH_LDAP)
+.TP
+.B LDAP_base
+Absolute LDAP search base.
+(Required if using UMICH_LDAP)
+.TP
+.B LDAP_people_base
+Absolute LDAP search base for people accounts.
+(Default: The
+.B LDAP_base
+value)
+.TP
+.B LDAP_group_base
+Absolute LDAP search base for group accounts.
+(Default: The
+.B LDAP_base
+value)
+.TP
+.B LDAP_canonicalize_name
+Whether or not to perform name canonicalization on the
+name given as
+.B LDAP_server
+(Default: "true")
+.TP
+.B LDAP_follow_referrals
+Whether or not to follow ldap referrals. (Default: "true")
+.TP
+.B LDAP_use_ssl
+Set to "true" to enable SSL communication with the LDAP server.
+(Default: "false")
+.TP
+.B LDAP_ca_cert
+Location of a trusted CA certificate used when SSL is enabled
+(Required if
+.B LDAP_use_ssl
+is true and
+.B LDAP_tls_reqcert
+is not set to never)
+.TP
+.B LDAP_tls_reqcert
+Controls the LDAP server certificate validation behavior.
+It can take the same values as ldap.conf(5)'s
+.B TLS_REQCERT
+tunable.
+(Default: "hard")
+.TP
+.B LDAP_timeout_seconds
+Number of seconds before timing out an LDAP request
+(Default: 4)
+.TP
+.B LDAP_sasl_mech
+SASL mechanism to be used for sasl authentication. Required
+if SASL auth is to be used (Default: None)
+.TP
+.B LDAP_realm
+SASL realm to be used for sasl authentication. (Default: None)
+.TP
+.B LDAP_sasl_authcid
+Authentication identity to be used for sasl authentication. (Default: None)
+.TP
+.B LDAP_sasl_authzid
+Authorization identity to be used for sasl authentication. (Default: None)
+.TP
+.B LDAP_sasl_secprops
+Cyrus SASL security properties. It can the same values as ldap.conf(5)'s
+sasl_secprops.
+.TP
+.B LDAP_sasl_canonicalize
+Specifies whether the LDAP server hostname should be canonicalised.
+If set to yes LDAP lib with do a reverse hostname lookup.
+If this is not set the LDAP library's default will be used. (Default:
+None)
+.TP
+.B LDAP_sasl_krb5_ccname
+Path to kerberos credential cache. If it is not set then the value
+of environment variable KRB5CCNAME will be used. If the environment
+variable is not set then the default mechanism of kerberos library
+will be used.
+.TP
+.B NFSv4_person_objectclass
+The object class name for people accounts in your local LDAP schema
+(Default: NFSv4RemotePerson)
+.TP
+.B NFSv4_name_attr
+Your local schema's attribute name to be used for NFSv4 user names
+(Default: NFSv4Name)
+.TP
+.B NFSv4_uid_attr
+Your local schema's attribute name to be used for uidNumber
+(Default: uidNumber)
+.TP
+.B GSS_principal_attr
+Your local schema's attribute name for GSSAPI Principal names
+(Default: GSSAuthName)
+.TP
+.B NFSv4_acctname_attr
+Your local schema's attribute name to be used for account names
+(Default: uid)
+.TP
+.B NFSv4_group_objectclass
+The object class name for group accounts in your local LDAP schema
+(Default: NFSv4RemoteGroup)
+.TP
+.B NFSv4_gid_attr
+Your local schema's attribute name to be used for gidNumber
+(Default: gidNumber)
+.TP
+.B NFSv4_group_attr
+Your local schema's attribute name to be used for NFSv4 group names
+(Default: NFSv4Name)
+.TP
+.B LDAP_use_memberof_for_groups
+Some LDAP servers do a better job with indexing where searching
+through all the groups searching for the user in the memberuid
+list. Others like SunOne directory that search can takes minutes
+if there are thousands of groups. So setting
+.B LDAP_use_memberof_for_groups
+to true in the configuration file will use the memberof lists of
+the account and search through only those groups to obtain gids.
+(Default: false)
+.TP
+.B NFSv4_member_attr
+If
+.B LDAP_use_memberof_for_groups
+is true, this is the attribute to be searched for.
+(Default: memberUid)
+.TP
+.B NFSv4_grouplist_filter
+An optional search filter for determining group membership.
+(No Default)
+.\"
+.\" -------------------------------------------------------------------
+.\" An Example
+.\" -------------------------------------------------------------------
+.\"
+.SH EXAMPLES
+An example
+.I /etc/idmapd.conf
+file:
+.nf
+
+
+[General]
+
+Verbosity = 0
+Domain = domain.org
+Local-Realms = DOMAIN.ORG,MY.DOMAIN.ORG,YOUR.DOMAIN.ORG
+
+[Mapping]
+
+Nobody-User = nfsnobody
+Nobody-Group = nfsnobody
+
+[Translation]
+
+Method = umich_ldap,regex,nsswitch
+GSS-Methods = umich_ldap,regex,static
+
+[Static]
+
+johndoe@OTHER.DOMAIN.ORG = johnny
+
+[Regex]
+
+User-Regex = ^DOMAIN\\([^@]+)@DOMAIN.ORG$
+Group-Regex = ^([^@]+)@DOMAIN.ORG@DOMAIN.ORG$|^DOMAIN\\([^@]+)@DOMAIN.ORG$
+Prepend-Before-User = DOMAIN\
+Append-After-User = @DOMAIN.ORG
+Append-After-Group = @domain.org@domain.org
+Group-Name-Prefix = sales-
+Group-Name-No-Prefix-Regex = -personal-group$
+
+[UMICH_SCHEMA]
+
+LDAP_server = ldap.domain.org
+LDAP_base = dc=org,dc=domain
+
+.fi
+.\"
+.\" -------------------------------------------------------------------
+.\" Additional sections
+.\" -------------------------------------------------------------------
+.\"
+.SH SEE ALSO
+.BR idmapd (8)
+.BR svcgssd (8)
+.\".SH COMPATIBILITY
+.\".SH STANDARDS
+.\".SH ACKNOWLEDGEMENTS
+.\".SH AUTHORS
+.\".SH HISTORY
+.SH BUGS
+Report bugs to <nfsv4@linux-nfs.org>
+.\".SH CAVEATS
diff --git a/support/nfsidmap/libnfsidmap.c b/support/nfsidmap/libnfsidmap.c
new file mode 100644
index 0000000..f8c3648
--- /dev/null
+++ b/support/nfsidmap/libnfsidmap.c
@@ -0,0 +1,712 @@
+/*
+ * libnfsidmap.c
+ *
+ * nfs idmapping library, primarily for nfs4 client/server kernel idmapping
+ * and for userland nfs4 idmapping by acl libraries.
+ *
+ * Copyright (c) 2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Marius Aamodt Eriksen <marius@umich.edu>
+ * J. 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.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <grp.h>
+#include <netdb.h>
+#include <err.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <dlfcn.h>
+#include <ctype.h>
+#include <resolv.h>
+#include <arpa/nameser.h>
+#include <arpa/nameser_compat.h>
+
+#include "nfsidmap.h"
+#include "nfsidmap_private.h"
+#include "nfsidmap_plugin.h"
+#include "conffile.h"
+
+#pragma GCC visibility push(hidden)
+
+void nfs4_cleanup_name_mapping(void);
+static char *default_domain;
+static struct mapping_plugin **nfs4_plugins = NULL;
+static struct mapping_plugin **gss_plugins = NULL;
+uid_t nobody_uid = (uid_t)-1;
+gid_t nobody_gid = (gid_t)-1;
+
+#ifndef PATH_PLUGINS
+#define PATH_PLUGINS "/usr/lib/libnfsidmap"
+#endif
+#define PLUGIN_INIT_FUNC "libnfsidmap_plugin_init"
+
+
+#ifndef PATH_IDMAPDCONF
+#define PATH_IDMAPDCONF "/etc/idmapd.conf"
+#endif
+
+#ifndef IDMAPD_DEFAULT_DOMAIN
+#define IDMAPD_DEFAULT_DOMAIN "localdomain"
+#endif
+
+#ifndef NFS4DNSTXTREC
+#define NFS4DNSTXTREC "_nfsv4idmapdomain"
+#endif
+
+#ifndef NS_MAXMSG
+#define NS_MAXMSG 65535
+#endif
+
+/* Default logging fuction */
+static void default_logger(const char *fmt, ...)
+{
+ va_list vp;
+
+ va_start(vp, fmt);
+ vsyslog(LOG_WARNING, fmt, vp);
+ va_end(vp);
+}
+
+#pragma GCC visibility pop
+nfs4_idmap_log_function_t idmap_log_func = default_logger;
+int idmap_verbosity = 0;
+#pragma GCC visibility push(hidden)
+
+static int id_as_chars(char *name, uid_t *id)
+{
+ long int value;
+
+ if (name == NULL)
+ return 0;
+ value = strtol(name, NULL, 10);
+ if (value == 0) {
+ /* zero value ids are valid */
+ if (strcmp(name, "0") != 0)
+ return 0;
+ }
+ *id = (int)value;
+ return 1;
+}
+
+static int dns_txt_query(char *domain, char **nfs4domain)
+{
+ char *txtname = NFS4DNSTXTREC;
+ unsigned char *msg, *eom, *mptr;
+ char *answ;
+ int len, status = -1;
+ HEADER *hdr;
+
+ msg = calloc(1, NS_MAXMSG);
+ if (msg == NULL)
+ return -1;
+
+ answ = calloc(1, NS_MAXMSG);
+ if (answ == NULL) {
+ free(msg);
+ return -1;
+ }
+
+ if (res_init() < 0) {
+ IDMAP_LOG(2, ("libnfsidmap: res_init() failed for %s.%s: %s\n",
+ txtname, domain, hstrerror(h_errno)));
+ goto freemem;
+ }
+ len = res_querydomain(txtname, domain, C_IN, T_TXT, msg, NS_MAXMSG);
+ if (len < 0) {
+ IDMAP_LOG(2, ("libnfsidmap: res_querydomain() failed for %s.%s: %s\n",
+ txtname, domain, hstrerror(h_errno)));
+ goto freemem;
+ }
+ hdr = (HEADER *)msg;
+
+ /* See if there is an answer */
+ if (ntohs(hdr->ancount) < 1) {
+ IDMAP_LOG(2, ("libnfsidmap: No TXT record for %s.%s\n",
+ txtname, domain));
+ goto freemem;
+ }
+ /* find the EndOfMessage */
+ eom = msg + len;
+
+ /* skip header */
+ mptr = &msg[HFIXEDSZ];
+
+ /* skip name field in question section */
+ mptr += dn_skipname(mptr, eom) + QFIXEDSZ;
+
+ /* read in the question */
+ len = dn_expand(msg, eom, mptr, answ, NS_MAXDNAME);
+ if (len < 0) { /* does this really matter?? */
+ IDMAP_LOG(2, ("libnfsidmap: No question section for %s.%s: %s\n",
+ txtname, domain, hstrerror(h_errno)));
+ goto freemem;
+ }
+
+ /*
+ * Now, dissect the answer section, Note: if there
+ * are more than one answer only the first
+ * one will be used.
+ */
+
+ /* skip passed the name field */
+ mptr += dn_skipname(mptr, eom);
+ /* skip pass the type class and ttl fields */
+ mptr += 2 + 2 + 4;
+
+ /* make sure there is some data */
+ GETSHORT(len, mptr);
+ if (len < 0) {
+ IDMAP_LOG(2, ("libnfsidmap: No data in answer for %s.%s\n",
+ txtname, domain));
+ goto freemem;
+ }
+ /* get the lenght field */
+ len = (int)*mptr++;
+ /* copy the data */
+ memcpy(answ, mptr, len);
+ answ[len] = '\0';
+
+ *nfs4domain = strdup(answ);
+ status = 0;
+
+freemem:
+ free(msg);
+ free(answ);
+
+ return (status);
+}
+
+static int domain_from_dns(char **domain)
+{
+ struct hostent *he;
+ char hname[64], *c;
+
+ if (gethostname(hname, sizeof(hname)) == -1)
+ return -1;
+ if ((he = gethostbyname(hname)) == NULL) {
+ IDMAP_LOG(1, ("libnfsidmap: DNS lookup of hostname failed. Attempting to use domain from hostname as is."));
+ if ((c = strchr(hname, '.')) == NULL || *++c == '\0')
+ return -1;
+ }
+ else {
+ if ((c = strchr(he->h_name, '.')) == NULL || *++c == '\0')
+ return -1;
+ }
+ /*
+ * Query DNS to see if the _nfsv4idmapdomain TXT record exists
+ * If so use it...
+ */
+ if (dns_txt_query(c, domain) < 0)
+ *domain = strdup(c);
+
+ return 0;
+}
+
+static int load_translation_plugin(char *method, struct mapping_plugin *plgn)
+{
+ void *dl = NULL;
+ struct trans_func *trans = NULL;
+ libnfsidmap_plugin_init_t init_func = NULL;
+ char plgname[128];
+ int ret = 0;
+
+ /* Look for library using search path first to allow overriding */
+ snprintf(plgname, sizeof(plgname), "%s.so", method);
+
+ dl = dlopen(plgname, RTLD_NOW | RTLD_LOCAL);
+ if (dl != NULL) {
+ /* Is it really one of our libraries */
+ init_func = (libnfsidmap_plugin_init_t) dlsym(dl, PLUGIN_INIT_FUNC);
+ if (init_func == NULL) {
+ dlclose(dl);
+ dl = NULL;
+ }
+ }
+
+ if (dl == NULL) {
+ /* Fallback to hard-coded path */
+ snprintf(plgname, sizeof(plgname), "%s/%s.so", PATH_PLUGINS, method);
+
+ dl = dlopen(plgname, RTLD_NOW | RTLD_LOCAL);
+ if (dl == NULL) {
+ IDMAP_LOG(1, ("libnfsidmap: Unable to load plugin: %s: %s",
+ plgname, dlerror()));
+ return -1;
+ }
+ init_func = (libnfsidmap_plugin_init_t) dlsym(dl, PLUGIN_INIT_FUNC);
+ if (init_func == NULL) {
+ IDMAP_LOG(1, ("libnfsidmap: Unable to get init function: %s: %s",
+ plgname, dlerror()));
+ dlclose(dl);
+ return -1;
+ }
+ }
+ trans = init_func();
+ if (trans == NULL) {
+ IDMAP_LOG(1, ("libnfsidmap: Failed to initialize plugin %s",
+ PLUGIN_INIT_FUNC, plgname));
+ dlclose(dl);
+ return -1;
+ }
+ if (trans->init) {
+ ret = trans->init();
+ if (ret) {
+ IDMAP_LOG(1, ("libnfsidmap: Failed in %s's init(), "
+ "returned %d", plgname, ret));
+ dlclose(dl);
+ return -1;
+ }
+ }
+ plgn->dl_handle = dl;
+ plgn->trans = trans;
+ IDMAP_LOG(1, ("libnfsidmap: loaded plugin %s for method %s",
+ plgname, method));
+
+ return 0;
+}
+
+static void unload_plugins(struct mapping_plugin **plgns)
+{
+ int i;
+ for (i = 0; plgns[i] != NULL; i++) {
+ if (plgns[i]->dl_handle && dlclose(plgns[i]->dl_handle))
+ IDMAP_LOG(1, ("libnfsidmap: failed to "
+ "unload plugin for method = %s",
+ plgns[i]->trans->name));
+ free(plgns[i]);
+ }
+ free(plgns);
+}
+
+static int load_plugins(struct conf_list *methods,
+ struct mapping_plugin ***plugins)
+{
+ int ret = -1, i = 0;
+ struct mapping_plugin **plgns;
+ struct conf_list_node *m;
+
+ plgns = calloc(methods->cnt + 1, sizeof(struct mapping_plugin *));
+ if (plgns == NULL)
+ return -1;
+ plgns[methods->cnt] = NULL;
+ for (m = TAILQ_FIRST(&methods->fields), i = 0; m;
+ m = TAILQ_NEXT(m, link), i++) {
+ plgns[i] = calloc(1, sizeof(struct mapping_plugin));
+ if (plgns[i] == NULL)
+ goto out;
+ if (load_translation_plugin(m->field, plgns[i]) == -1) {
+ IDMAP_LOG(0, ("libnfsidmap: requested translation "
+ "method, '%s', is not available",
+ m->field));
+ goto out;
+ }
+ }
+ ret = 0;
+ *plugins = plgns;
+out:
+ if (ret)
+ unload_plugins(plgns);
+ return ret;
+}
+
+static char *get_default_domain(void)
+{
+ int ret;
+
+ if (default_domain)
+ return default_domain;
+ ret = domain_from_dns(&default_domain);
+ if (ret) {
+ IDMAP_LOG(0, ("Unable to determine a default nfsv4 domain; "
+ " consider specifying one in idmapd.conf"));
+ default_domain = "";
+ }
+ return default_domain;
+}
+
+void nfs4_cleanup_name_mapping(void)
+{
+ if (nfs4_plugins)
+ unload_plugins(nfs4_plugins);
+ if (gss_plugins)
+ unload_plugins(gss_plugins);
+ nfs4_plugins = gss_plugins = NULL;
+}
+
+#pragma GCC visibility pop
+
+const char * nfsidmap_conf_path = PATH_IDMAPDCONF;
+
+int nfs4_init_name_mapping(char *conffile)
+{
+ int ret = -ENOENT;
+ int dflt = 0;
+ struct conf_list *nfs4_methods, *gss_methods;
+ char *nobody_user, *nobody_group;
+
+ /* XXX: need to be able to reload configurations... */
+ if (nfs4_plugins) /* already succesfully initialized */
+ return 0;
+ if (conffile)
+ nfsidmap_conf_path = conffile;
+ conf_init_file(nfsidmap_conf_path);
+
+ default_domain = conf_get_str("General", "Domain");
+ if (default_domain == NULL) {
+ dflt = 1;
+ ret = domain_from_dns(&default_domain);
+ if (ret) {
+ IDMAP_LOG(0, ("libnfsidmap: Unable to determine "
+ "the NFSv4 domain; Using '%s' as the NFSv4 domain "
+ "which means UIDs will be mapped to the 'Nobody-User' "
+ "user defined in %s",
+ IDMAPD_DEFAULT_DOMAIN, PATH_IDMAPDCONF));
+ default_domain = IDMAPD_DEFAULT_DOMAIN;
+ }
+ }
+ IDMAP_LOG(1, ("libnfsidmap: using%s domain: %s",
+ (dflt ? " (default)" : ""), default_domain));
+
+ struct conf_list *local_realms = get_local_realms();
+ if (local_realms == NULL) return -ENOMEM;
+
+ if (idmap_verbosity >= 1) {
+ struct conf_list_node *r;
+ char *buf = NULL;
+ int siz=0;
+
+ if (local_realms) {
+ TAILQ_FOREACH(r, &local_realms->fields, link) {
+ siz += (strlen(r->field)+4);
+ }
+ buf = malloc(siz);
+ if (buf) {
+ *buf = 0;
+ TAILQ_FOREACH(r, &local_realms->fields, link) {
+ sprintf(buf+strlen(buf), "'%s' ", r->field);
+ }
+ IDMAP_LOG(1, ("libnfsidmap: Realms list: %s", buf));
+ free(buf);
+ }
+ } else
+ IDMAP_LOG(1, ("libnfsidmap: Realms list: <NULL> "));
+ }
+
+ nfs4_methods = conf_get_list("Translation", "Method");
+ if (nfs4_methods) {
+ IDMAP_LOG(1, ("libnfsidmap: processing 'Method' list"));
+ if (load_plugins(nfs4_methods, &nfs4_plugins) == -1) {
+ conf_free_list(nfs4_methods);
+ return -ENOENT;
+ }
+ } else {
+ struct conf_list list;
+ struct conf_list_node node;
+
+ TAILQ_INIT(&list.fields);
+ list.cnt = 1;
+ node.field = "nsswitch";
+ TAILQ_INSERT_TAIL (&list.fields, &node, link);
+
+ if (load_plugins(&list, &nfs4_plugins) == -1)
+ return -ENOENT;
+ }
+
+ gss_methods = conf_get_list("Translation", "GSS-Methods");
+ if (gss_methods) {
+ IDMAP_LOG(1, ("libnfsidmap: processing 'GSS-Methods' list"));
+ if (load_plugins(gss_methods, &gss_plugins) == -1)
+ goto out;
+ }
+
+ nobody_user = conf_get_str("Mapping", "Nobody-User");
+ if (nobody_user) {
+ size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ struct passwd *buf;
+ struct passwd *pw = NULL;
+ int err;
+
+ buf = malloc(sizeof(*buf) + buflen);
+ if (buf) {
+ err = getpwnam_r(nobody_user, buf, ((char *)buf) + sizeof(*buf), buflen, &pw);
+ if (err == 0 && pw != NULL)
+ nobody_uid = pw->pw_uid;
+ else
+ IDMAP_LOG(1, ("libnfsidmap: Nobody-User (%s) not found: %s",
+ nobody_user, strerror(errno)));
+ free(buf);
+ } else
+ IDMAP_LOG(0,("libnfsidmap: Nobody-User: no memory : %s",
+ nobody_user, strerror(errno)));
+ }
+
+ nobody_group = conf_get_str("Mapping", "Nobody-Group");
+ if (nobody_group) {
+ size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ struct group *buf;
+ struct group *gr = NULL;
+ int err;
+
+ buf = malloc(sizeof(*buf) + buflen);
+ if (buf) {
+ err = getgrnam_r(nobody_group, buf, ((char *)buf) + sizeof(*buf), buflen, &gr);
+ if (err == 0 && gr != NULL)
+ nobody_gid = gr->gr_gid;
+ else
+ IDMAP_LOG(1, ("libnfsidmap: Nobody-Group (%s) not found: %s",
+ nobody_group, strerror(errno)));
+ free(buf);
+ } else
+ IDMAP_LOG(0,("libnfsidmap: Nobody-Group: no memory : %s",
+ nobody_group, strerror(errno)));
+ }
+
+ ret = 0;
+out:
+ if (ret) {
+ if (nfs4_plugins)
+ unload_plugins(nfs4_plugins);
+ if (gss_plugins) {
+ unload_plugins(gss_plugins);
+ }
+ nfs4_plugins = gss_plugins = NULL;
+ }
+
+ if (gss_methods)
+ conf_free_list(gss_methods);
+
+ if (nfs4_methods)
+ conf_free_list(nfs4_methods);
+
+ return ret ? -ENOENT: 0;
+}
+
+void nfs4_term_name_mapping(void)
+{
+ if (nfs4_plugins)
+ unload_plugins(nfs4_plugins);
+ if (gss_plugins)
+ unload_plugins(gss_plugins);
+
+ nfs4_plugins = gss_plugins = NULL;
+
+ free_local_realms();
+ conf_cleanup();
+}
+
+int
+nfs4_get_default_domain(char *UNUSED(server), char *domain, size_t len)
+{
+ char *d = get_default_domain();
+
+ if (strlen(d) + 1 > len)
+ return -ERANGE;
+ strcpy(domain, d);
+ return 0;
+}
+
+/*
+ * Run through each configured translation method for
+ * function "funcname".
+ * If "prefer_gss" is true, then use the gss_plugins list,
+ * if present. Otherwise, use the default nfs4_plugins list.
+ *
+ * If the plugin function returns -ENOENT, then continue
+ * to the next plugin.
+ */
+#define RUN_TRANSLATIONS(funcname, prefer_gss, args...) \
+ do { \
+ int ret, i; \
+ struct mapping_plugin **plgns; \
+ \
+ ret = nfs4_init_name_mapping(NULL); \
+ if (ret) \
+ return ret; \
+ \
+ if ((prefer_gss) && gss_plugins) \
+ plgns = gss_plugins; \
+ else \
+ plgns = nfs4_plugins; \
+ \
+ for (i = 0; plgns[i] != NULL; i++) { \
+ if (plgns[i]->trans->funcname == NULL) \
+ continue; \
+ \
+ IDMAP_LOG(4, ("%s: calling %s->%s", __func__, \
+ plgns[i]->trans->name, #funcname)); \
+ \
+ ret = plgns[i]->trans->funcname(args); \
+ \
+ IDMAP_LOG(4, ("%s: %s->%s returned %d", \
+ __func__, plgns[i]->trans->name, \
+ #funcname, ret)); \
+ \
+ if (ret == -ENOENT) \
+ continue; \
+ \
+ break; \
+ } \
+ IDMAP_LOG(4, ("%s: final return value is %d", \
+ __func__, ret)); \
+ return ret; \
+ } while (0)
+
+int nfs4_uid_to_name(uid_t uid, char *domain, char *name, size_t len)
+{
+ RUN_TRANSLATIONS(uid_to_name, 0, uid, domain, name, len);
+}
+
+int nfs4_gid_to_name(gid_t gid, char *domain, char *name, size_t len)
+{
+ RUN_TRANSLATIONS(gid_to_name, 0, gid, domain, name, len);
+}
+
+int nfs4_uid_to_owner(uid_t uid, char *domain, char *name, size_t len)
+{
+ if (nfs4_uid_to_name(uid, domain, name, len))
+ sprintf(name, "%u", uid);
+ return 0;
+}
+
+int nfs4_gid_to_group_owner(gid_t gid, char *domain, char *name, size_t len)
+{
+ if (nfs4_gid_to_name(gid, domain, name, len))
+ sprintf(name, "%u", gid);
+ return 0;
+}
+
+int nfs4_name_to_uid(char *name, uid_t *uid)
+{
+ RUN_TRANSLATIONS(name_to_uid, 0, name, uid);
+}
+
+int nfs4_name_to_gid(char *name, gid_t *gid)
+{
+ RUN_TRANSLATIONS(name_to_gid, 0, name, gid);
+}
+
+static int set_id_to_nobody(uid_t *id, uid_t is_uid)
+{
+ int rc = 0;
+ const char name[] = "nobody@";
+ char nobody[strlen(name) + strlen(get_default_domain()) + 1];
+
+ /* First try to see whether a Nobody-User/Nobody-Group was
+ * configured, before we try to do a full lookup for the
+ * NFS nobody user. */
+ if (is_uid && nobody_uid != (uid_t)-1) {
+ *id = (uid_t)nobody_uid;
+ return 0;
+ } else if (!is_uid && nobody_gid != (gid_t)-1) {
+ *id = (uid_t)nobody_gid;
+ return 0;
+ }
+
+ strcpy(nobody, name);
+ strcat(nobody, get_default_domain());
+
+ if (is_uid)
+ rc = nfs4_name_to_uid(nobody, id);
+ else
+ rc = nfs4_name_to_gid(nobody, id);
+
+ if (rc) {
+ *id = -2;
+ rc = 0;
+ }
+ return rc;
+}
+
+int nfs4_owner_to_uid(char *name, uid_t *uid)
+{
+ int rc = nfs4_name_to_uid(name, uid);
+ if (rc && id_as_chars(name, uid))
+ rc = 0;
+ else if (rc)
+ rc = set_id_to_nobody(uid, 1);
+ return rc;
+}
+
+int nfs4_group_owner_to_gid(char *name, gid_t *gid)
+{
+ int rc = nfs4_name_to_gid(name, gid);
+ if (rc && id_as_chars(name, gid))
+ rc = 0;
+ else if (rc)
+ rc = set_id_to_nobody((uid_t *)gid, 0);
+ return rc;
+}
+
+int nfs4_gss_princ_to_ids(char *secname, char *princ, uid_t *uid, gid_t *gid)
+{
+ RUN_TRANSLATIONS(princ_to_ids, 1, secname, princ, uid, gid, NULL);
+}
+
+int nfs4_gss_princ_to_grouplist(char *secname, char *princ,
+ gid_t *groups, int *ngroups)
+{
+ RUN_TRANSLATIONS(gss_princ_to_grouplist, 1, secname, princ,
+ groups, ngroups, NULL);
+}
+
+int nfs4_gss_princ_to_ids_ex(char *secname, char *princ, uid_t *uid,
+ gid_t *gid, extra_mapping_params **ex)
+{
+ RUN_TRANSLATIONS(princ_to_ids, 1, secname, princ, uid, gid, ex);
+}
+
+int nfs4_gss_princ_to_grouplist_ex(char *secname, char *princ, gid_t *groups,
+ int *ngroups, extra_mapping_params **ex)
+{
+ RUN_TRANSLATIONS(gss_princ_to_grouplist, 1, secname, princ,
+ groups, ngroups, ex);
+}
+
+void nfs4_set_debug(int dbg_level, void (*logger)(const char *, ...))
+{
+ if (logger)
+ idmap_log_func = logger;
+ idmap_verbosity = dbg_level;
+ IDMAP_LOG(0, ("Setting log level to %d\n", idmap_verbosity));
+}
+
+const char *nfsidmap_config_get(const char *section, const char *tag)
+{
+ return conf_get_section(section, NULL, tag);
+}
diff --git a/support/nfsidmap/libnfsidmap.pc.in b/support/nfsidmap/libnfsidmap.pc.in
new file mode 100644
index 0000000..a11dbec
--- /dev/null
+++ b/support/nfsidmap/libnfsidmap.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libnfsidmap
+Description: Library that handles mapping between names and ids for NFSv4.
+Requires:
+Version: @PACKAGE_VERSION@
+Libs: -L@libdir@ -lnfsidmap
+Cflags: -I@includedir@
diff --git a/support/nfsidmap/libtest.c b/support/nfsidmap/libtest.c
new file mode 100644
index 0000000..1c717b8
--- /dev/null
+++ b/support/nfsidmap/libtest.c
@@ -0,0 +1,160 @@
+/*
+ * libtest.c
+ *
+ * nfs idmapping library, primarily for nfs4 client/server kernel idmapping
+ * and for userland nfs4 idmapping by acl libraries.
+ *
+ * Copyright (c) 2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@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.
+ *
+ *
+ *
+ * libtest: Test the translation table functions
+ * Reads /etc/idmapd.conf
+ *
+ * To compile:
+ * gcc -g libtest.c -lnfsidmap -o libtest
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <nfsidmap.h>
+
+#define QUIT_ON_ERROR 1
+#define PATH_IDMAPDCONF "/etc/idmapd.conf"
+char *conf_path = PATH_IDMAPDCONF;
+
+main(int ac, char **av)
+{
+ char *name, *princ;
+ int err, uid = 0, gid = 0;
+ char name_buf[32];
+ int gids[1000];
+ int i, ngids;
+
+ if (ac != 3) {
+ printf("Usage: %s <user@nfsv4domain> <k5princ@REALM>\n",av[0]);
+ return 1;
+ }
+
+ nfs4_set_debug(3, NULL);
+
+ name = av[1];
+ princ = av[2];
+ err = nfs4_init_name_mapping(NULL);
+ if (err) {
+ printf("nfs4_init_name_mapping: error %d\n", err);
+ return 1;
+ }
+
+ err = nfs4_gss_princ_to_ids("krb5", princ, &uid, &gid);
+ if (err)
+ printf("nfs4_gss_princ_to_ids: error %d\n", err);
+ else
+ printf("nfs4_gss_princ_to_ids: princ %s has uid %d gid %d\n",
+ princ, uid, gid);
+#if QUIT_ON_ERROR
+ if (err) {
+ printf("calling it quits!\n");
+ return err;
+ }
+#endif
+
+ err = nfs4_name_to_uid(name, &uid);
+ if (err)
+ printf("nfs4_name_to_uid: error %d\n", err);
+ else
+ printf("nfs4_name_to_uid: name %s has uid %d\n",
+ name, uid);
+
+#if QUIT_ON_ERROR
+ if (err) {
+ printf("calling it quits!\n");
+ return err;
+ }
+#endif
+ err = nfs4_name_to_gid(name, &gid);
+ if (err)
+ printf("nfs4_name_to_gid: error %d\n", err);
+ else
+ printf("nfs4_name_to_gid: name %s has gid %d\n",
+ name, gid);
+
+ ngids = 1000;
+ err = nfs4_gss_princ_to_grouplist("krb5", princ, gids, &ngids);
+ if (err){
+ printf(" nfs4_gss_princ_to_grouplist: error %d\n", err);
+ } else {
+ printf(" nfs4_gss_princ_to_grouplist: princ %s has gids ",
+ princ);
+ for (i = 0; i < ngids; i++) printf("%d ", gids[i]);
+ printf("\n");
+ }
+
+#if QUIT_ON_ERROR
+ if (err) {
+ printf("calling it quits!\n");
+ return err;
+ }
+#endif
+ /* uid is set by nfs4_name_to_uid() */
+ memset(name_buf, 0, 32);
+ err = nfs4_uid_to_name(uid, NULL, name_buf, 32);
+ if (err)
+ printf("nfs4_uid_to_name: error %d\n", err);
+ else
+ printf("nfs4_uid_to_name: uid %d has name %s\n",
+ uid, name_buf);
+
+#if QUIT_ON_ERROR
+ if (err) {
+ printf("calling it quits!\n");
+ return err;
+ }
+#endif
+ /* gid is set by nfs4_name_to_gid() */
+ memset(name_buf, 0, 32);
+ err = nfs4_gid_to_name(gid, NULL, name_buf, 32);
+ if (err)
+ printf("nfs4_gid_to_name: error %d\n", err);
+ else
+ printf("nfs4_gid_to_name: gid %d has name %s\n",
+ gid, name_buf);
+
+#if QUIT_ON_ERROR
+ if (err) {
+ printf("calling it quits!\n");
+ return err;
+ }
+#endif
+ return 0;
+}
diff --git a/support/nfsidmap/nfs4_uid_to_name.3 b/support/nfsidmap/nfs4_uid_to_name.3
new file mode 100644
index 0000000..8a62d8a
--- /dev/null
+++ b/support/nfsidmap/nfs4_uid_to_name.3
@@ -0,0 +1,174 @@
+.TH nfs4_uid_to_name 3 2004-08-05
+.SH NAME
+nfs4_uid_to_name, nfs4_gid_to_name, nfs4_name_to_uid, nfs4_name_to_gid,
+nfs4_init_name_mapping, nfs4_get_default_domain,
+nfs4_gss_princ_to_ids, nfs4_gss_princ_to_grouplist,
+nfs4_gss_princ_to_ids_ex,
+nfs4_gss_princ_to_grouplist_ex,
+nfs4_set_debug \- ID mapping routines used for NFSv4
+.SH SYNOPSIS
+.B #include <nfs4_idmap.h>
+.sp
+.BI "int nfs4_init_name_mapping(char *conffile);"
+.sp
+.BI "int nfs4_get_default_domain(char *server, char *domain, size_t len);"
+.sp
+.BI "int nfs4_uid_to_name(uid_t uid, char *domain, char *name, size_t len);"
+.sp
+.BI "int nfs4_uid_to_owner(uid_t uid, char *domain, char *name, size_t len);"
+.sp
+.BI "int nfs4_gid_to_name(gid_t gid, char *domain, char *name, size_t len);"
+.sp
+.BI "int nfs4_gid_to_owner(gid_t gid, char *domain, char *name, size_t len);"
+.sp
+.BI "int nfs4_name_to_uid(char *name, uid_t *uid);"
+.sp
+.BI "int nfs4_name_to_gid(char *name, gid_t *gid);"
+.sp
+.BI "int nfs4_owner_to_uid(char *name, uid_t *uid);"
+.sp
+.BI "int nfs4_owner_to_gid(char *name, gid_t *gid);"
+.sp
+.BI "int nfs4_gss_princ_to_ids(char *secname, char *princ, uid_t *uid, gid_t *gid);"
+.sp
+.BI "int nfs4_gss_princ_to_grouplist(char *secname, char *princ, gid_t *groups, int *ngroups);"
+.sp
+.BI "int nfs4_gss_princ_to_ids_ex(char *secname, char *princ, uid_t *uid, gid_t *gid, extra_mapping_params **ex);"
+.sp
+.BI "int nfs4_gss_princ_to_grouplist_ex(char *secname, char *princ, gid_t *groups, int *ngroups, extra_mapping_params **ex);"
+.sp
+.BI "void nfs4_set_debug(int dbg_level, void (*logger)(const char *, ...));"
+.sp
+.fi
+.SH DESCRIPTION
+NFSv4 uses names of the form
+.IR user@domain .
+To write code that helps the kernel map uid's (as
+rpc.idmapd
+does) or that processes NFSv4 ACLs, you need to be able to convert between
+NFSv4 names and local uids and gids.
+.PP
+The
+.B nfs4_uid_to_name()
+and
+.B nfs4_gid_to_name()
+functions, given
+.I uid
+or
+.I gid
+and
+.I domain
+(as a null-terminated string),
+write the corresponding nfsv4 name into the buffer provided in
+.IR name ,
+which must be of length at least
+.IR len .
+.PP
+The
+.B nfs4_uid_to_owner()
+and
+.B nfs4_gid_to_group_owner()
+functions, given
+.I uid
+or
+.I gid
+and
+.I domain
+(as a null-terminated string),
+write the corresponding nfsv4 name into the buffer provided in
+.IR name ,
+which must be of length at least
+.IR len .
+If there is no valid mapping from
+.I uid
+or
+.I gid
+to
+.IR name ,
+then the numerical string representing uid or gid is returned instead.
+.PP
+The
+.B nfs4_name_to_uid()
+and
+.B nfs4_name_to_gid()
+functions, given
+.I name
+(as a null-terminated string), return the corresponding uid or gid in
+the second parameter.
+.PP
+The
+.B nfs4_owner_to_uid()
+and
+.B nfs4_group_owner_to_gid()
+functions, given
+.I name
+(as a null-terminated string), return the corresponding uid or gid in
+the second parameter.
+If there is no valid mapping from
+.I name
+to
+.I uid
+or
+.I gid
+the value for the user or group "nobody" will be returned instead.
+. PP
+The
+.B nfs4_init_name_mapping()
+function must be called before using any of these functions. It reads
+defaults from the configuration file at the provided path, usually
+"etc/idmapd.conf".
+.PP
+The
+.I domain
+argument to the id-to-name functions is there to provide a hint to the name
+mapper in the case where an id might be mapped to names in multiple domains.
+In most cases, this argument should just be the name returned in the
+.I domain
+argument to
+.B nfs4_get_default_domain()
+which should be called with
+.I server
+set to NULL. The
+.I domain
+should be a buffer of length
+.IR len .
+The constant NFS4_MAX_DOMAIN_LEN may be used to determine a reasonable
+value for that length.
+.PP
+The function
+.BR nfs4_get_grouplist() ,
+given a
+.IR name ,
+fills the provided array
+.I groups
+with up to
+.I *ngroups
+group IDs corresponding to which the user
+.I name
+belongs to, setting
+.I *ngroups
+to the actual number of such groups. If the user belongs to more than
+.I *ngroups
+groups, then an error is returned and the actual number of groups is stored in
+*ngroups.
+.PP
+Functions
+.BR nfs4_gss_princ_to_ids() ,
+.BR nfs4_gss_princ_to_grouplist() ,
+.BR nfs4_gss_princ_to_ids_ex() ,
+and
+.B nfs4_gss_princ_to_grouplist_ex()
+are used to convert from a gss principal name (as returned by
+.BR gss_display_name() )
+to a uid and gid, or list of gids.
+.PP
+Finally,
+.B nfs4_set_debug()
+allows the application to set a debugging level to produce extra
+debugging information from within the library. The optional
+.I logger
+function specifies an alternative logging function to call for
+the debug messages rather than the default internal function
+within the library.
+.SH RETURN VALUE
+All functions return 0 or, in the case of error, -ERRNO.
diff --git a/support/nfsidmap/nfsidmap.h b/support/nfsidmap/nfsidmap.h
new file mode 100644
index 0000000..5a79568
--- /dev/null
+++ b/support/nfsidmap/nfsidmap.h
@@ -0,0 +1,68 @@
+/*
+ * nfsidmap.h
+ *
+ * nfs idmapping library, primarily for nfs4 client/server kernel idmapping
+ * and for userland nfs4 idmapping by acl libraries.
+ *
+ * Copyright (c) 2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * J. 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.
+ */
+
+/* XXX arbitrary */
+#define NFS4_MAX_DOMAIN_LEN 512
+typedef enum {
+ X509_CERT = 1
+} extra_mapping_types;
+
+typedef struct _extra_mapping_params {
+ void *content;
+ int content_type;
+ int content_len;
+} extra_mapping_params;
+
+typedef void (*nfs4_idmap_log_function_t)(const char *, ...);
+
+int nfs4_init_name_mapping(char *conffile);
+void nfs4_term_name_mapping(void);
+int nfs4_get_default_domain(char *server, char *domain, size_t len);
+int nfs4_uid_to_name(uid_t uid, char *domain, char *name, size_t len);
+int nfs4_gid_to_name(gid_t gid, char *domain, char *name, size_t len);
+int nfs4_uid_to_owner(uid_t uid, char *domain, char *name, size_t len);
+int nfs4_gid_to_group_owner(gid_t gid, char *domain, char *name, size_t len);
+int nfs4_name_to_uid(char *name, uid_t *uid);
+int nfs4_name_to_gid(char *name, gid_t *gid);
+int nfs4_owner_to_uid(char *name, uid_t *uid);
+int nfs4_owner_to_gid(char *name, gid_t *gid);
+int nfs4_group_owner_to_gid(char *name, gid_t *gid);
+int nfs4_gss_princ_to_ids(char *secname, char *princ, uid_t *uid, gid_t *gid);
+int nfs4_gss_princ_to_grouplist(char *secname, char *princ, gid_t *groups, int *ngroups);
+int nfs4_gss_princ_to_ids_ex(char *secname, char *princ, uid_t *uid, gid_t *gid, extra_mapping_params **ex);
+int nfs4_gss_princ_to_grouplist_ex(char *secname, char *princ, gid_t *groups, int *ngroups, extra_mapping_params **ex);
+void nfs4_set_debug(int dbg_level, nfs4_idmap_log_function_t dbg_logfunc);
diff --git a/support/nfsidmap/nfsidmap_common.c b/support/nfsidmap/nfsidmap_common.c
new file mode 100644
index 0000000..4d2cb14
--- /dev/null
+++ b/support/nfsidmap/nfsidmap_common.c
@@ -0,0 +1,118 @@
+/*
+ * nfsidmap_common.c
+ *
+ * nfs idmapping library, primarily for nfs4 client/server kernel idmapping
+ * and for userland nfs4 idmapping by acl libraries.
+ *
+ * Code common to libnfsidmap and some of its bundled plugins
+ *
+ * If you make use of these functions you must initialise your own
+ * copy of the config file data using: conf_init_file(nfsidmap_conf_path)
+ * failure to do so will appear as if the config was empty
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "nfsidmap.h"
+#include "nfsidmap_private.h"
+#include "nfsidmap_plugin.h"
+#include "conffile.h"
+
+#pragma GCC visibility push(hidden)
+
+static char * toupper_str(char *s)
+{
+ size_t i;
+ for (i=0; i < strlen(s); i++)
+ s[i] = toupper(s[i]);
+ return s;
+}
+
+static struct conf_list *local_realms = NULL;
+
+void free_local_realms(void)
+{
+ if (local_realms) {
+ conf_free_list(local_realms);
+ local_realms = NULL;
+ }
+}
+
+/* Get list of "local equivalent" realms. Meaning the list of realms
+ * where john@REALM.A is considered the same user as john@REALM.B
+ * If not specified, default to upper-case of local domain name */
+struct conf_list *get_local_realms(void)
+{
+ if (local_realms) return local_realms;
+
+ local_realms = conf_get_list("General", "Local-Realms");
+ if (local_realms == NULL) {
+ struct conf_list_node *node;
+
+ local_realms = malloc(sizeof *local_realms);
+ if (local_realms == NULL)
+ return NULL;
+ local_realms->cnt = 0;
+ TAILQ_INIT(&local_realms->fields);
+
+ node = calloc(1, sizeof *node);
+ if (node == NULL)
+ return NULL;
+
+ node->field = calloc(1, NFS4_MAX_DOMAIN_LEN);
+ if (node->field == NULL) {
+ free(node);
+ return NULL;
+ }
+
+ nfs4_get_default_domain(NULL, node->field, NFS4_MAX_DOMAIN_LEN);
+ toupper_str(node->field);
+
+ TAILQ_INSERT_TAIL(&local_realms->fields, node, link);
+ local_realms->cnt++;
+ }
+ return local_realms;
+}
+
+static int no_strip = -1;
+static int reformat_group = 0;
+
+int get_nostrip(void)
+{
+ if (no_strip != -1) return no_strip;
+
+ char * nostrip = conf_get_str_with_def("General", "No-Strip", "none");
+ if (strcasecmp(nostrip, "both") == 0)
+ no_strip = IDTYPE_USER|IDTYPE_GROUP;
+ else if (strcasecmp(nostrip, "group") == 0)
+ no_strip = IDTYPE_GROUP;
+ else if (strcasecmp(nostrip, "user") == 0)
+ no_strip = IDTYPE_USER;
+ else
+ no_strip = 0;
+
+ if (no_strip & IDTYPE_GROUP) {
+ char * reformatgroup = conf_get_str_with_def("General", "Reformat-Group", "false");
+ if ((strcasecmp(reformatgroup, "true") == 0) ||
+ (strcasecmp(reformatgroup, "on") == 0) ||
+ (strcasecmp(reformatgroup, "yes") == 0))
+ reformat_group = 1;
+ else
+ reformat_group = 0;
+ }
+
+ return no_strip;
+}
+
+int get_reformat_group(void)
+{
+ if (no_strip != -1) return reformat_group;
+
+ return reformat_group;
+}
diff --git a/support/nfsidmap/nfsidmap_plugin.h b/support/nfsidmap/nfsidmap_plugin.h
new file mode 100644
index 0000000..66fcdaa
--- /dev/null
+++ b/support/nfsidmap/nfsidmap_plugin.h
@@ -0,0 +1,70 @@
+/*
+ * nfsidmap_plugin.h
+ *
+ * Essentials functions and structs required when building
+ * plugins for libnfsidmap that are otherwise not exposed
+ * in the public API
+ *
+ * Copyright (c) 2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@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.
+ */
+
+struct trans_func {
+ char *name;
+ int (*init)(void);
+ int (*princ_to_ids)(char *secname, char *princ, uid_t *uid, gid_t *gid,
+ extra_mapping_params **ex);
+ int (*name_to_uid)(char *name, uid_t *uid);
+ int (*name_to_gid)(char *name, gid_t *gid);
+ int (*uid_to_name)(uid_t uid, char *domain, char *name, size_t len);
+ int (*gid_to_name)(gid_t gid, char *domain, char *name, size_t len);
+ int (*gss_princ_to_grouplist)(char *secname, char *princ, gid_t *groups,
+ int *ngroups, extra_mapping_params **ex);
+};
+
+extern int idmap_verbosity;
+extern nfs4_idmap_log_function_t idmap_log_func;
+struct trans_func *libnfsidmap_plugin_init(void);
+
+/* Level zero always prints, others print depending on verbosity level */
+#define IDMAP_LOG(LVL, MSG) \
+ do { if (LVL <= idmap_verbosity) (*idmap_log_func)MSG; } while (0)
+
+#ifndef UNUSED
+#ifdef __GNUC__
+#define UNUSED(foo) UNUSED_ ## foo __attribute__((__unused__))
+#else
+#define UNUSED(foo) UNUSED_ ## foo
+#endif
+#endif
+
+extern const char *nfsidmap_conf_path;
+extern const char *nfsidmap_config_get(const char *section, const char *tag);
+
diff --git a/support/nfsidmap/nfsidmap_private.h b/support/nfsidmap/nfsidmap_private.h
new file mode 100644
index 0000000..a5cb6dd
--- /dev/null
+++ b/support/nfsidmap/nfsidmap_private.h
@@ -0,0 +1,54 @@
+/*
+ * nfsidmap_private.h
+ *
+ * For use only by bundled plugins, not for external use
+ *
+ * Copyright (c) 2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@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.
+ */
+
+#include "conffile.h"
+
+struct conf_list *get_local_realms(void);
+void free_local_realms(void);
+int get_nostrip(void);
+int get_reformat_group(void);
+
+typedef enum {
+ IDTYPE_USER = 1,
+ IDTYPE_GROUP = 2
+} idtypes;
+
+typedef struct trans_func * (*libnfsidmap_plugin_init_t)(void);
+
+struct mapping_plugin {
+ void *dl_handle;
+ struct trans_func *trans;
+};
diff --git a/support/nfsidmap/nss.c b/support/nfsidmap/nss.c
new file mode 100644
index 0000000..0f43076
--- /dev/null
+++ b/support/nfsidmap/nss.c
@@ -0,0 +1,494 @@
+/*
+ * nss.c
+ *
+ * nsswitch idmapping functions.
+ *
+ * Copyright (c) 2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * J. 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.
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <netdb.h>
+#include <err.h>
+#include <grp.h>
+#include <limits.h>
+#include <ctype.h>
+#include "nfsidmap.h"
+#include "nfsidmap_plugin.h"
+#include "nfsidmap_private.h"
+#include <syslog.h>
+
+static char *get_default_domain(void)
+{
+ static char default_domain[NFS4_MAX_DOMAIN_LEN] = "";
+ if (default_domain[0] == 0) {
+ nfs4_get_default_domain(NULL, default_domain, NFS4_MAX_DOMAIN_LEN);
+ }
+ return default_domain;
+}
+
+/*
+ * NSS Translation Methods
+ *
+ * These are all just wrappers around getpwnam and friends;
+ * we tack on the given domain to the results of getpwnam when looking up a uid,
+ * and ignore the domain entirely when looking up a name.
+ */
+
+static int write_name(char *dest, char *localname, char *domain, size_t len,
+ int doappend)
+{
+ if (doappend || !strchr(localname,'@')) {
+ if (strlen(localname) + 1 + strlen(domain) + 1 > len)
+ return -ENOMEM; /* XXX: Is there an -ETOOLONG? */
+ strcpy(dest, localname);
+ strcat(dest, "@");
+ strcat(dest, domain);
+ } else {
+ if (strlen(localname) + 1 > len)
+ return -ENOMEM;
+ strcpy(dest, localname);
+ }
+ return 0;
+}
+
+static int nss_uid_to_name(uid_t uid, char *domain, char *name, size_t len)
+{
+ struct passwd *pw = NULL;
+ struct passwd pwbuf;
+ char *buf;
+ size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ int err = -ENOMEM;
+
+ buf = malloc(buflen);
+ if (!buf)
+ goto out;
+ if (domain == NULL)
+ domain = get_default_domain();
+ err = -getpwuid_r(uid, &pwbuf, buf, buflen, &pw);
+ if (pw == NULL)
+ err = -ENOENT;
+ if (err)
+ goto out_buf;
+ if (get_nostrip() & IDTYPE_USER)
+ err = write_name(name, pw->pw_name, domain, len, 0);
+ else
+ err = write_name(name, pw->pw_name, domain, len, 1);
+out_buf:
+ free(buf);
+out:
+ return err;
+}
+
+static int nss_gid_to_name(gid_t gid, char *domain, char *name, size_t len)
+{
+ struct group *gr = NULL;
+ struct group grbuf;
+ char *buf;
+ size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ int err;
+
+ if (domain == NULL)
+ domain = get_default_domain();
+
+ do {
+ err = -ENOMEM;
+ buf = malloc(buflen);
+ if (!buf)
+ goto out;
+ err = -getgrgid_r(gid, &grbuf, buf, buflen, &gr);
+ if (gr == NULL && !err)
+ err = -ENOENT;
+ if (err == -ERANGE) {
+ buflen *= 2;
+ free(buf);
+ }
+ } while (err == -ERANGE);
+
+ if (err)
+ goto out_buf;
+ if (get_nostrip() & IDTYPE_GROUP)
+ err = write_name(name, gr->gr_name, domain, len, 0);
+ else
+ err = write_name(name, gr->gr_name, domain, len, 1);
+out_buf:
+ free(buf);
+out:
+ return err;
+}
+
+/* XXX: actually should return error, so can distinguish between
+ * memory allocation failure and failure to match domain */
+static char *strip_domain(const char *name, const char *domain)
+{
+ const char *c;
+ char *l = NULL;
+ int len;
+
+ if (name == NULL)
+ goto out;
+
+ c = strrchr(name, '@');
+ if (c == NULL && domain != NULL)
+ goto out;
+ if (c == NULL && domain == NULL) {
+ len = strlen(name) + 1;
+ } else {
+ if (domain && strcasecmp(c + 1, domain) != 0)
+ goto out;
+ len = c - name;
+ }
+
+ l = malloc(len + 1);
+ if (l == NULL)
+ goto out;
+ memcpy(l, name, len);
+ l[len] = '\0';
+out:
+ return l;
+}
+
+struct pwbuf {
+ struct passwd pwbuf;
+ char buf[1];
+};
+
+static struct passwd *nss_getpwnam(const char *name, const char *domain,
+ int *err_p, int dostrip)
+{
+ struct passwd *pw;
+ struct pwbuf *buf;
+ size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ char *localname;
+ int err = ENOMEM;
+
+ if (buflen > UINT_MAX)
+ goto err;
+
+ buf = malloc(sizeof(*buf) + buflen);
+ if (buf == NULL)
+ goto err;
+
+ err = EINVAL;
+ if (dostrip) {
+ localname = strip_domain(name, domain);
+ IDMAP_LOG(4, ("nss_getpwnam: name '%s' domain '%s': "
+ "resulting localname '%s'", name, domain, localname));
+ if (localname == NULL) {
+ IDMAP_LOG(0, ("nss_getpwnam: name '%s' does not map "
+ "into domain '%s'", name,
+ domain ? domain : "<not-provided>"));
+ goto err_free_buf;
+ }
+
+ err = getpwnam_r(localname, &buf->pwbuf, buf->buf, buflen, &pw);
+ if (pw == NULL && domain != NULL)
+ IDMAP_LOG(1,
+ ("nss_getpwnam: name '%s' not found in domain '%s'",
+ localname, domain));
+ free(localname);
+ } else {
+ err = getpwnam_r(name, &buf->pwbuf, buf->buf, buflen, &pw);
+ if (pw == NULL)
+ IDMAP_LOG(1,
+ ("nss_getpwnam: name '%s' not found (domain not stripped)", name));
+ }
+ if (err == 0 && pw != NULL) {
+ *err_p = 0;
+ return pw;
+ } else if (err == 0 && pw == NULL) {
+ err = ENOENT;
+ }
+
+err_free_buf:
+ free(buf);
+err:
+ *err_p = -err;
+ return NULL;
+}
+
+static int nss_name_to_uid(char *name, uid_t *uid)
+{
+ struct passwd *pw = NULL;
+ char *domain;
+ int err = -ENOENT;
+
+ domain = get_default_domain();
+ if (get_nostrip() & IDTYPE_USER) {
+ pw = nss_getpwnam(name, domain, &err, 0);
+ if (pw != NULL)
+ goto out_uid;
+ }
+ pw = nss_getpwnam(name, domain, &err, 1);
+ if (pw == NULL)
+ goto out;
+out_uid:
+ *uid = pw->pw_uid;
+ IDMAP_LOG(4, ("nss_name_to_uid: name '%s' uid %u", name, *uid));
+ free(pw);
+ err = 0;
+out:
+ return err;
+}
+
+static char *reformat_name(const char *name)
+{
+ const char *domain;
+ const char *c;
+ const char *d;
+ char *l = NULL;
+ int len;
+ int dlen = 0;
+ int i;
+
+ c = strchr(name, '@');
+ if (c == NULL)
+ goto out;
+ len = c - name;
+ domain = ++c;
+ d = strchr(domain, '.');
+ if (d == NULL)
+ goto out;
+ dlen = d - domain;
+ l = malloc(dlen + 1 + len + 1);
+ if (l == NULL)
+ goto out;
+ for (i = 0; i < dlen; i++)
+ l[i] = toupper(domain[i]);
+ l[dlen] = '\\';
+ memcpy(l + dlen + 1, name, len);
+ l[dlen + 1 + len] = '\0';
+out:
+ return l;
+}
+
+static int _nss_name_to_gid(char *name, gid_t *gid, int dostrip)
+{
+ struct group *gr = NULL;
+ struct group grbuf;
+ char *buf, *domain;
+ size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ int err = -EINVAL;
+ char *localname = NULL;
+ char *ref_name = NULL;
+
+ domain = get_default_domain();
+ if (dostrip) {
+ localname = strip_domain(name, domain);
+ IDMAP_LOG(4, ("nss_name_to_gid: name '%s' domain '%s': "
+ "resulting localname '%s'", name, domain, localname));
+ if (!localname) {
+ IDMAP_LOG(0, ("nss_name_to_gid: name '%s' does not map "
+ "into domain '%s'", name, domain));
+ goto out;
+ }
+ } else if (get_reformat_group()) {
+ ref_name = reformat_name(name);
+ if (ref_name == NULL) {
+ IDMAP_LOG(1, ("nss_name_to_gid: failed to reformat name '%s'",
+ name));
+ err = -ENOENT;
+ goto out;
+ }
+ }
+
+ err = -ENOMEM;
+ if (buflen > UINT_MAX)
+ goto out_name;
+
+ do {
+ buf = malloc(buflen);
+ if (!buf)
+ goto out_name;
+ if (dostrip)
+ err = -getgrnam_r(localname, &grbuf, buf, buflen, &gr);
+ else if (get_reformat_group())
+ err = -getgrnam_r(ref_name, &grbuf, buf, buflen, &gr);
+ else
+ err = -getgrnam_r(name, &grbuf, buf, buflen, &gr);
+ if (gr == NULL && !err) {
+ if (dostrip)
+ IDMAP_LOG(1, ("nss_name_to_gid: name '%s' not found "
+ "in domain '%s'", localname, domain));
+ else if (get_reformat_group())
+ IDMAP_LOG(1, ("nss_name_to_gid: name '%s' not found "
+ "(reformatted)", ref_name));
+ else
+ IDMAP_LOG(1, ("nss_name_to_gid: name '%s' not found "
+ "(domain not stripped)", name));
+ err = -ENOENT;
+ }
+ if (err == -ERANGE) {
+ buflen *= 2;
+ free(buf);
+ }
+ } while (err == -ERANGE);
+
+ if (err)
+ goto out_buf;
+ *gid = gr->gr_gid;
+ IDMAP_LOG(4, ("nss_name_to_gid: name '%s' gid %u", name, *gid));
+out_buf:
+ free(buf);
+out_name:
+ free(localname);
+ free(ref_name);
+out:
+ return err;
+}
+
+static int nss_name_to_gid(char *name, gid_t *gid)
+{
+ int err = 0;
+
+ if (get_nostrip() & IDTYPE_GROUP) {
+ err = _nss_name_to_gid(name, gid, 0);
+ if (!err)
+ goto out;
+ }
+ err = _nss_name_to_gid(name, gid, 1);
+out:
+ return err;
+}
+
+static int nss_gss_princ_to_ids(char *secname, char *princ,
+ uid_t *uid, uid_t *gid,
+ extra_mapping_params **UNUSED(ex))
+{
+ struct passwd *pw;
+ int err = 0;
+ char *princ_realm;
+ struct conf_list *realms;
+ struct conf_list_node *r;
+ int found = 0;
+
+ if (strcmp(secname, "spkm3") == 0)
+ return -ENOENT;
+
+ if (strcmp(secname, "krb5") != 0)
+ return -EINVAL;
+
+ /* get princ's realm */
+ princ_realm = strstr(princ, "@");
+ if (princ_realm == NULL)
+ return -EINVAL;
+ princ_realm++;
+
+ /* get list of "local-equivalent" realms and
+ * check against the principal's realm */
+ realms = get_local_realms();
+ TAILQ_FOREACH(r, &realms->fields, link) {
+ if (strcmp(r->field, princ_realm) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ IDMAP_LOG(1, ("nss_gss_princ_to_ids: Local-Realm '%s': NOT FOUND",
+ princ_realm));
+ return -ENOENT;
+ }
+ /* XXX: this should call something like getgssauthnam instead? */
+ pw = nss_getpwnam(princ, NULL, &err, 1);
+ if (pw == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+ *uid = pw->pw_uid;
+ *gid = pw->pw_gid;
+ free(pw);
+out:
+ return err;
+}
+
+static int nss_gss_princ_to_grouplist(char *secname, char *princ,
+ gid_t *groups, int *ngroups,
+ extra_mapping_params **UNUSED(ex))
+{
+ struct passwd *pw;
+ int ret = -EINVAL;
+
+ if (strcmp(secname, "krb5") != 0)
+ goto out;
+ /* XXX: not quite right? Need to know default realm? */
+ /* XXX: this should call something like getgssauthnam instead? */
+ pw = nss_getpwnam(princ, NULL, &ret, 1);
+ if (pw == NULL) {
+ ret = -ENOENT;
+ goto out;
+ }
+ if (getgrouplist(pw->pw_name, pw->pw_gid, groups, ngroups) < 0)
+ ret = -ERANGE;
+ free(pw);
+out:
+ return ret;
+}
+
+static int nss_plugin_init(void)
+{
+ if (nfsidmap_conf_path)
+ conf_init_file(nfsidmap_conf_path);
+ return 0;
+}
+
+/*
+ * Called by dlclose(). See dlopen(3) man page
+ */
+__attribute__((destructor))
+static int nss_plugin_term(void)
+{
+ free_local_realms();
+ conf_cleanup();
+ return 0;
+}
+
+
+struct trans_func nss_trans = {
+ .name = "nsswitch",
+ .init = nss_plugin_init,
+ .princ_to_ids = nss_gss_princ_to_ids,
+ .name_to_uid = nss_name_to_uid,
+ .name_to_gid = nss_name_to_gid,
+ .uid_to_name = nss_uid_to_name,
+ .gid_to_name = nss_gid_to_name,
+ .gss_princ_to_grouplist = nss_gss_princ_to_grouplist,
+};
+
+struct trans_func *libnfsidmap_plugin_init(void)
+{
+ return (&nss_trans);
+}
diff --git a/support/nfsidmap/regex.c b/support/nfsidmap/regex.c
new file mode 100644
index 0000000..8424179
--- /dev/null
+++ b/support/nfsidmap/regex.c
@@ -0,0 +1,549 @@
+/*
+ * regex.c
+ *
+ * regex idmapping functions.
+ *
+ * Copyright (c) 2017-2020 Stefan Walter <stefan.walter@inf.ethz.ch>.
+ * Copyright (c) 2008 David H?rdeman <david@hardeman.nu>.
+ * 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.
+ */
+
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <err.h>
+#include <regex.h>
+
+#include "nfsidmap.h"
+#include "nfsidmap_plugin.h"
+
+#define CONFIG_GET_STRING nfsidmap_config_get
+extern const char *nfsidmap_config_get(const char *, const char *);
+
+#define MAX_MATCHES 100
+
+regex_t group_re;
+regex_t user_re;
+regex_t gpx_re;
+int use_gpx;
+const char * group_prefix;
+const char * group_name_prefix;
+const char * group_suffix;
+const char * user_prefix;
+const char * user_suffix;
+const char * group_map_file;
+const char * group_map_section;
+char empty = '\0';
+size_t group_name_prefix_length;
+
+struct pwbuf {
+ struct passwd pwbuf;
+ char buf[1];
+};
+
+struct grbuf {
+ struct group grbuf;
+ char buf[1];
+};
+
+static char *get_default_domain(void)
+{
+ static char default_domain[NFS4_MAX_DOMAIN_LEN] = "";
+ if (default_domain[0] == 0) {
+ nfs4_get_default_domain(NULL, default_domain, NFS4_MAX_DOMAIN_LEN);
+ }
+ return default_domain;
+}
+
+/*
+ * Regexp Translation Methods
+ *
+ */
+
+static struct passwd *regex_getpwnam(const char *name, const char *UNUSED(domain),
+ int *err_p)
+{
+ struct passwd *pw;
+ struct pwbuf *buf;
+ size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ char *localname;
+ size_t namelen;
+ int err;
+ int status;
+ int index;
+ regmatch_t matches[MAX_MATCHES];
+
+ buf = malloc(sizeof(*buf) + buflen);
+ if (!buf) {
+ err = ENOMEM;
+ goto err;
+ }
+
+ status = regexec(&user_re, name, MAX_MATCHES, matches, 0);
+ if (status) {
+ IDMAP_LOG(4, ("regexp_getpwnam: user '%s' did not match regex", name));
+ err = ENOENT;
+ goto err_free_buf;
+ }
+
+ for (index = 1; index < MAX_MATCHES ; index++)
+ {
+ if (matches[index].rm_so >= 0)
+ break;
+ }
+
+ if (index == MAX_MATCHES) {
+ IDMAP_LOG(4, ("regexp_getpwnam: user '%s' did not match regex", name));
+ err = ENOENT;
+ goto err_free_buf;
+ }
+
+ namelen = matches[index].rm_eo - matches[index].rm_so;
+ localname= malloc(namelen + 1);
+ if (!localname)
+ {
+ err = ENOMEM;
+ goto err_free_buf;
+ }
+ strncpy(localname, name+matches[index].rm_so, namelen);
+ localname[namelen] = '\0';
+
+again:
+ err = getpwnam_r(localname, &buf->pwbuf, buf->buf, buflen, &pw);
+
+ if (err == EINTR)
+ goto again;
+
+ if (!pw) {
+ if (err == 0)
+ err = ENOENT;
+
+ IDMAP_LOG(4, ("regex_getpwnam: local user '%s' for '%s' not found",
+ localname, name));
+
+ goto err_free_name;
+ }
+
+ IDMAP_LOG(4, ("regexp_getpwnam: name '%s' mapped to '%s'",
+ name, localname));
+
+ free(localname);
+ *err_p = 0;
+ return pw;
+
+err_free_name:
+ free(localname);
+err_free_buf:
+ free(buf);
+err:
+ *err_p = err;
+ return NULL;
+}
+
+static struct group *regex_getgrnam(const char *name, const char *UNUSED(domain),
+ int *err_p)
+{
+ struct group *gr;
+ struct grbuf *buf;
+ size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ char *localgroup;
+ char *groupname;
+ size_t namelen;
+ int err = 0;
+ int index;
+ int status;
+ regmatch_t matches[MAX_MATCHES];
+
+ buf = malloc(sizeof(*buf) + buflen);
+ if (!buf) {
+ err = ENOMEM;
+ goto err;
+ }
+
+ status = regexec(&group_re, name, MAX_MATCHES, matches, 0);
+ if (status) {
+ IDMAP_LOG(4, ("regexp_getgrnam: group '%s' did not match regex", name));
+ err = ENOENT;
+ goto err_free_buf;
+ }
+
+ for (index = 1; index < MAX_MATCHES ; index++)
+ {
+ if (matches[index].rm_so >= 0)
+ break;
+ }
+
+ if (index == MAX_MATCHES) {
+ IDMAP_LOG(4, ("regexp_getgrnam: group '%s' did not match regex", name));
+ err = ENOENT;
+ goto err_free_buf;
+ }
+
+ namelen = matches[index].rm_eo - matches[index].rm_so;
+ localgroup = malloc(namelen + 1);
+ if (!localgroup)
+ {
+ err = ENOMEM;
+ goto err_free_buf;
+ }
+ strncpy(localgroup, name+matches[index].rm_so, namelen);
+ localgroup[namelen] = '\0';
+
+ IDMAP_LOG(4, ("regexp_getgrnam: group '%s' after match of regex", localgroup));
+
+ groupname = localgroup;
+ if (group_name_prefix_length && ! strncmp(group_name_prefix, localgroup, group_name_prefix_length))
+ {
+ err = 1;
+ if (use_gpx)
+ err = regexec(&gpx_re, localgroup, 0, NULL, 0);
+
+ if (err)
+ {
+ IDMAP_LOG(4, ("regexp_getgrnam: removing prefix '%s' (%d long) from group '%s'", group_name_prefix, group_name_prefix_length, localgroup));
+ groupname += group_name_prefix_length;
+ }
+ else
+ {
+ IDMAP_LOG(4, ("regexp_getgrnam: not removing prefix from group '%s'", localgroup));
+ }
+ }
+
+ IDMAP_LOG(4, ("regexp_getgrnam: will use '%s'", groupname));
+
+again:
+ err = getgrnam_r(groupname, &buf->grbuf, buf->buf, buflen, &gr);
+
+ if (err == EINTR)
+ goto again;
+
+ if (!gr) {
+ if (err == 0)
+ err = ENOENT;
+
+ IDMAP_LOG(4, ("regex_getgrnam: local group '%s' for '%s' not found", groupname, name));
+
+ goto err_free_name;
+ }
+
+ IDMAP_LOG(4, ("regex_getgrnam: group '%s' mapped to '%s'", name, groupname));
+
+ free(localgroup);
+
+ *err_p = 0;
+ return gr;
+
+err_free_name:
+ free(localgroup);
+err_free_buf:
+ free(buf);
+err:
+ *err_p = err;
+ return NULL;
+}
+
+static int regex_gss_princ_to_ids(char *secname, char *princ,
+ uid_t *uid, uid_t *gid,
+ extra_mapping_params **UNUSED(ex))
+{
+ struct passwd *pw;
+ int err;
+
+ /* XXX: Is this necessary? */
+ if (strcmp(secname, "krb5") != 0 && strcmp(secname, "spkm3") != 0)
+ return -EINVAL;
+
+ pw = regex_getpwnam(princ, NULL, &err);
+
+ if (pw) {
+ *uid = pw->pw_uid;
+ *gid = pw->pw_gid;
+ free(pw);
+ }
+
+ return -err;
+}
+
+static int regex_gss_princ_to_grouplist(char *secname, char *princ,
+ gid_t *groups, int *ngroups,
+ extra_mapping_params **UNUSED(ex))
+{
+ struct passwd *pw;
+ int err;
+
+ /* XXX: Is this necessary? */
+ if (strcmp(secname, "krb5") != 0 && strcmp(secname, "spkm3") != 0)
+ return -EINVAL;
+
+ pw = regex_getpwnam(princ, NULL, &err);
+
+ if (pw) {
+ if (getgrouplist(pw->pw_name, pw->pw_gid, groups, ngroups) < 0)
+ err = -ERANGE;
+ free(pw);
+ }
+
+ return -err;
+}
+
+static int regex_name_to_uid(char *name, uid_t *uid)
+{
+ struct passwd *pw;
+ int err;
+
+ pw = regex_getpwnam(name, NULL, &err);
+
+ if (pw) {
+ *uid = pw->pw_uid;
+ free(pw);
+ }
+
+ return -err;
+}
+
+static int regex_name_to_gid(char *name, gid_t *gid)
+{
+ struct group *gr;
+ int err;
+
+ gr = regex_getgrnam(name, NULL, &err);
+
+ if (gr) {
+ *gid = gr->gr_gid;
+ free(gr);
+ }
+
+ return -err;
+}
+
+static int write_name(char *dest, char *localname, const char* name_prefix, const char *prefix, const char *suffix, size_t len)
+{
+ if (strlen(localname) + strlen(name_prefix) + strlen(prefix) + strlen(suffix) + 1 > len) {
+ return -ENOMEM; /* XXX: Is there an -ETOOLONG? */
+ }
+ strcpy(dest, prefix);
+ strcat(dest, name_prefix);
+ strcat(dest, localname);
+ strcat(dest, suffix);
+
+ IDMAP_LOG(4, ("write_name: will use '%s'", dest));
+
+ return 0;
+}
+
+static int regex_uid_to_name(uid_t uid, char *domain, char *name, size_t len)
+{
+ struct passwd *pw = NULL;
+ struct passwd pwbuf;
+ char *buf;
+ size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ int err = -ENOMEM;
+
+ buf = malloc(buflen);
+ if (!buf)
+ goto out;
+ if (domain == NULL)
+ domain = get_default_domain();
+ err = -getpwuid_r(uid, &pwbuf, buf, buflen, &pw);
+ if (pw == NULL)
+ err = -ENOENT;
+ if (err)
+ goto out_buf;
+ err = write_name(name, pw->pw_name, &empty, user_prefix, user_suffix, len);
+out_buf:
+ free(buf);
+out:
+ return err;
+}
+
+static int regex_gid_to_name(gid_t gid, char *UNUSED(domain), char *name, size_t len)
+{
+ struct group *gr = NULL;
+ struct group grbuf;
+ char *buf;
+ const char *name_prefix;
+ size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ int err;
+ char * groupname = NULL;
+
+ do {
+ err = -ENOMEM;
+ buf = malloc(buflen);
+ if (!buf)
+ goto out;
+ err = -getgrgid_r(gid, &grbuf, buf, buflen, &gr);
+ if (gr == NULL && !err)
+ err = -ENOENT;
+ if (err == -ERANGE) {
+ buflen *= 2;
+ free(buf);
+ }
+ } while (err == -ERANGE);
+
+ if (err)
+ goto out_buf;
+
+ groupname = gr->gr_name;
+ name_prefix = group_name_prefix;
+ if (group_name_prefix_length)
+ {
+ if(! strncmp(group_name_prefix, groupname, group_name_prefix_length))
+ {
+ name_prefix = &empty;
+ }
+ else if (use_gpx)
+ {
+ err = regexec(&gpx_re, groupname, 0, NULL, 0);
+ if (!err)
+ {
+ IDMAP_LOG(4, ("regex_gid_to_name: not adding prefix to group '%s'", groupname));
+ name_prefix = &empty;
+ }
+ }
+ }
+
+ err = write_name(name, groupname, name_prefix, group_prefix, group_suffix, len);
+
+out_buf:
+ free(buf);
+out:
+ return err;
+}
+
+static int regex_init(void) {
+ const char *string;
+ int status;
+
+
+ string = CONFIG_GET_STRING("Regex", "User-Regex");
+ if (!string)
+ {
+ warnx("regex_init: regex for user mapping missing");
+ goto error1;
+ }
+
+ status = regcomp(&user_re, string, REG_EXTENDED|REG_ICASE);
+ if (status)
+ {
+ warnx("regex_init: compiling regex for user mapping failed with status %u", status);
+ goto error1;
+ }
+
+ string = CONFIG_GET_STRING("Regex", "Group-Regex");
+ if (!string)
+ {
+ warnx("regex_init: regex for group mapping missing");
+ goto error2;
+ }
+
+ status = regcomp(&group_re, string, REG_EXTENDED|REG_ICASE);
+ if (status)
+ {
+ warnx("regex_init: compiling regex for group mapping failed with status %u", status);
+ goto error2;
+ }
+
+ group_name_prefix = CONFIG_GET_STRING("Regex", "Group-Name-Prefix");
+ if (!group_name_prefix)
+ {
+ group_name_prefix = &empty;
+ }
+ group_name_prefix_length = strlen(group_name_prefix);
+
+ user_prefix = CONFIG_GET_STRING("Regex", "Prepend-Before-User");
+ if (!user_prefix)
+ {
+ user_prefix = &empty;
+ }
+
+ user_suffix = CONFIG_GET_STRING("Regex", "Append-After-User");
+ if (!user_suffix)
+ {
+ user_suffix = &empty;
+ }
+
+ group_prefix = CONFIG_GET_STRING("Regex", "Prepend-Before-Group");
+ if (!group_prefix)
+ {
+ group_prefix = &empty;
+ }
+
+ group_suffix = CONFIG_GET_STRING("Regex", "Append-After-Group");
+ if (!group_suffix)
+ {
+ group_suffix = &empty;
+ }
+
+ string = CONFIG_GET_STRING("Regex", "Group-Name-No-Prefix-Regex");
+ use_gpx = 0;
+ if (string)
+ {
+ status = regcomp(&gpx_re, string, REG_EXTENDED|REG_ICASE);
+
+ if (status)
+ {
+ warnx("regex_init: compiling regex for group prefix exclusion failed with status %u", status);
+ goto error3;
+ }
+
+ use_gpx = 1;
+ }
+
+ return 0;
+
+error3:
+ regfree(&group_re);
+error2:
+ regfree(&user_re);
+error1:
+ return 0;
+ /* return -EINVAL; */
+}
+
+
+struct trans_func regex_trans = {
+ .name = "regex",
+ .init = regex_init,
+ .name_to_uid = regex_name_to_uid,
+ .name_to_gid = regex_name_to_gid,
+ .uid_to_name = regex_uid_to_name,
+ .gid_to_name = regex_gid_to_name,
+ .princ_to_ids = regex_gss_princ_to_ids,
+ .gss_princ_to_grouplist = regex_gss_princ_to_grouplist,
+};
+
+struct trans_func *libnfsidmap_plugin_init(void)
+{
+ return (&regex_trans);
+}
+
diff --git a/support/nfsidmap/static.c b/support/nfsidmap/static.c
new file mode 100644
index 0000000..8ac4a39
--- /dev/null
+++ b/support/nfsidmap/static.c
@@ -0,0 +1,426 @@
+/*
+ * static.c
+ *
+ * static idmapping functions for gss principals.
+ *
+ * Copyright (c) 2008 David Härdeman <david@hardeman.nu>.
+ * 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.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <err.h>
+
+#include "conffile.h"
+#include "nfsidmap.h"
+#include "nfsidmap_plugin.h"
+
+/*
+ * Static Translation Methods
+ *
+ * These functions use getpwnam to find uid/gid(s) for gss principals
+ * which are first mapped to local user names using static mappings
+ * in idmapd.conf.
+ */
+
+struct pwbuf {
+ struct passwd pwbuf;
+ char buf[1];
+};
+
+struct grbuf {
+ struct group grbuf;
+ char buf[1];
+};
+
+struct uid_mapping {
+ LIST_ENTRY (uid_mapping) link;
+ uid_t uid;
+ char * principal;
+ char * localname;
+};
+
+struct gid_mapping {
+ LIST_ENTRY (gid_mapping) link;
+ gid_t gid;
+ char * principal;
+ char * localgroup;
+};
+
+static __inline__ u_int8_t uid_hash (uid_t uid)
+{
+ return uid % 256;
+}
+
+static __inline__ u_int8_t gid_hash (gid_t gid)
+{
+ return gid % 256;
+}
+
+//Hash tables of uid and guids to principals mappings.
+//We reuse some queue/hash functions from cfg.c.
+LIST_HEAD (uid_mappings, uid_mapping) uid_mappings[256];
+LIST_HEAD (gid_mappings, gid_mapping) gid_mappings[256];
+
+static struct passwd *static_getpwnam(const char *name,
+ const char *UNUSED(domain),
+ int *err_p)
+{
+ struct passwd *pw;
+ struct pwbuf *buf;
+ size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ char *localname;
+ int err;
+
+ buf = malloc(sizeof(*buf) + buflen);
+ if (!buf) {
+ err = ENOMEM;
+ goto err;
+ }
+
+ localname = conf_get_str("Static", (char *)name);
+ if (!localname) {
+ err = ENOENT;
+ goto err_free_buf;
+ }
+
+again:
+ err = getpwnam_r(localname, &buf->pwbuf, buf->buf, buflen, &pw);
+
+ if (err == EINTR)
+ goto again;
+
+ if (!pw) {
+ if (err == 0)
+ err = ENOENT;
+
+ IDMAP_LOG(0, ("static_getpwnam: localname '%s' for '%s' not found",
+ localname, name));
+
+ goto err_free_buf;
+ }
+
+ IDMAP_LOG(4, ("static_getpwnam: name '%s' mapped to '%s'",
+ name, localname));
+
+ *err_p = 0;
+ return pw;
+
+err_free_buf:
+ free(buf);
+err:
+ *err_p = err;
+ return NULL;
+}
+
+static struct group *static_getgrnam(const char *name,
+ const char *UNUSED(domain),
+ int *err_p)
+{
+ struct group *gr;
+ struct grbuf *buf;
+ size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ char *localgroup;
+ int err;
+
+ buf = malloc(sizeof(*buf) + buflen);
+ if (!buf) {
+ err = ENOMEM;
+ goto err;
+ }
+
+ localgroup = conf_get_str("Static", (char *)name);
+ if (!localgroup) {
+ err = ENOENT;
+ goto err_free_buf;
+ }
+
+again:
+ err = getgrnam_r(localgroup, &buf->grbuf, buf->buf, buflen, &gr);
+
+ if (err == EINTR)
+ goto again;
+
+ if (!gr) {
+ if (err == 0)
+ err = ENOENT;
+
+ IDMAP_LOG(0, ("static_getgrnam: local group '%s' for '%s' not found",
+ localgroup, name));
+
+ goto err_free_buf;
+ }
+
+ IDMAP_LOG(4, ("static_getgrnam: group '%s' mapped to '%s'",
+ name, localgroup));
+
+ *err_p = 0;
+ return gr;
+
+err_free_buf:
+ free(buf);
+err:
+ *err_p = err;
+ return NULL;
+}
+
+static int static_gss_princ_to_ids(char *secname, char *princ,
+ uid_t *uid, uid_t *gid,
+ extra_mapping_params **UNUSED(ex))
+{
+ struct passwd *pw;
+ int err;
+
+ /* XXX: Is this necessary? */
+ if (strcmp(secname, "krb5") != 0 && strcmp(secname, "spkm3") != 0)
+ return -EINVAL;
+
+ pw = static_getpwnam(princ, NULL, &err);
+
+ if (pw) {
+ *uid = pw->pw_uid;
+ *gid = pw->pw_gid;
+ free(pw);
+ }
+
+ return -err;
+}
+
+static int static_gss_princ_to_grouplist(char *secname, char *princ,
+ gid_t *groups, int *ngroups,
+ extra_mapping_params **UNUSED(ex))
+{
+ struct passwd *pw;
+ int err;
+
+ /* XXX: Is this necessary? */
+ if (strcmp(secname, "krb5") != 0 && strcmp(secname, "spkm3") != 0)
+ return -EINVAL;
+
+ pw = static_getpwnam(princ, NULL, &err);
+
+ if (pw) {
+ if (getgrouplist(pw->pw_name, pw->pw_gid, groups, ngroups) < 0)
+ err = -ERANGE;
+ free(pw);
+ }
+
+ return -err;
+}
+
+static int static_name_to_uid(char *name, uid_t *uid)
+{
+ struct passwd *pw;
+ int err;
+
+ pw = static_getpwnam(name, NULL, &err);
+
+ if (pw) {
+ *uid = pw->pw_uid;
+ free(pw);
+ }
+
+ return -err;
+}
+
+static int static_name_to_gid(char *name, gid_t *gid)
+{
+ struct group *gr;
+ int err;
+
+ gr = static_getgrnam(name, NULL, &err);
+
+ if (gr) {
+ *gid = gr->gr_gid;
+ free(gr);
+ }
+
+ return -err;
+}
+
+static int static_uid_to_name(uid_t uid, char *UNUSED(domain), char *name, size_t UNUSED(len))
+{
+ struct uid_mapping * um;
+
+ for (um = LIST_FIRST (&uid_mappings[uid_hash (uid)]); um;
+ um = LIST_NEXT (um, link)) {
+ if (um->uid == uid) {
+ strcpy(name, um->principal);
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+static int static_gid_to_name(gid_t gid, char *UNUSED(domain), char *name, size_t UNUSED(len))
+{
+ struct gid_mapping * gm;
+
+ for (gm = LIST_FIRST (&gid_mappings[gid_hash (gid)]); gm;
+ gm = LIST_NEXT (gm, link)) {
+ if (gm->gid == gid) {
+ strcpy(name, gm->principal);
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+/*
+ * We buffer all UID's for which static mappings is defined in advance, so the
+ * uid_to_name functions will be fast enough.
+ */
+
+static int static_init(void) {
+ int err;
+ struct conf_list * princ_list = NULL;
+ struct conf_list_node * cln, *next;
+ struct uid_mapping * unode;
+ struct gid_mapping * gnode;
+ struct passwd * pw = NULL;
+ struct group * gr = NULL;
+ unsigned int i;
+
+ //init hash_table first
+ for (i = 0; i < sizeof uid_mappings / sizeof uid_mappings[0]; i++)
+ LIST_INIT (&uid_mappings[i]);
+
+ if (nfsidmap_conf_path)
+ conf_init_file(nfsidmap_conf_path);
+
+ //get all principals for which we have mappings
+ princ_list = conf_get_tag_list("Static", NULL);
+
+ if (!princ_list) {
+ return -ENOENT;
+ }
+
+ /* As we can not distinguish between mappings for users and groups, we try to
+ * resolve all mappings for both cases.
+ */
+
+ //resolve uid of localname account for all such principals and cache it
+ for (cln = TAILQ_FIRST (&princ_list->fields); cln; cln = next)
+ {
+ next = TAILQ_NEXT (cln, link);
+
+ pw = static_getpwnam(cln->field, NULL, &err);
+ if (!pw) {
+ continue;
+ }
+
+ unode = calloc (1, sizeof *unode);
+ if (!unode)
+ {
+ warnx("static_init: calloc (1, %lu) failed",
+ (unsigned long)sizeof *unode);
+ free(pw);
+ conf_free_list(princ_list);
+ return -ENOMEM;
+ }
+ unode->uid = pw->pw_uid;
+ unode->principal = strdup(cln->field);
+
+ unode->localname = conf_get_str("Static", cln->field);
+ if (!unode->localname) {
+ free(pw);
+ free(unode->principal);
+ free(unode);
+ conf_free_list(princ_list);
+ return -ENOENT;
+ }
+
+ free(pw);
+
+ LIST_INSERT_HEAD (&uid_mappings[uid_hash(unode->uid)], unode, link);
+ }
+
+ //resolve gid of localgroup accounts and cache it
+ for (cln = TAILQ_FIRST (&princ_list->fields); cln; cln = next)
+ {
+ next = TAILQ_NEXT (cln, link);
+
+ gr = static_getgrnam(cln->field, NULL, &err);
+ if (!gr) {
+ continue;
+ }
+
+ gnode = calloc (1, sizeof *gnode);
+ if (!gnode)
+ {
+ warnx("static_init: calloc (1, %lu) failed",
+ (unsigned long)sizeof *gnode);
+ free(gr);
+ conf_free_list(princ_list);
+ return -ENOMEM;
+ }
+ gnode->gid = gr->gr_gid;
+ gnode->principal = strdup(cln->field);
+
+ gnode->localgroup = conf_get_str("Static", cln->field);
+ if (!gnode->localgroup) {
+ free(gr);
+ free(gnode->principal);
+ free(gnode);
+ conf_free_list(princ_list);
+ return -ENOENT;
+ }
+
+ free(gr);
+
+ LIST_INSERT_HEAD (&gid_mappings[gid_hash(gnode->gid)], gnode, link);
+ }
+
+ conf_free_list(princ_list);
+ return 0;
+}
+
+
+struct trans_func static_trans = {
+ .name = "static",
+ .init = static_init,
+ .name_to_uid = static_name_to_uid,
+ .name_to_gid = static_name_to_gid,
+ .uid_to_name = static_uid_to_name,
+ .gid_to_name = static_gid_to_name,
+ .princ_to_ids = static_gss_princ_to_ids,
+ .gss_princ_to_grouplist = static_gss_princ_to_grouplist,
+};
+
+struct trans_func *libnfsidmap_plugin_init(void)
+{
+ return (&static_trans);
+}
+
diff --git a/support/nfsidmap/umich_ldap.c b/support/nfsidmap/umich_ldap.c
new file mode 100644
index 0000000..1aa2af4
--- /dev/null
+++ b/support/nfsidmap/umich_ldap.c
@@ -0,0 +1,1615 @@
+/*
+ * umich_ldap.c
+ *
+ * Copyright (c) 2000 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Copyright (c) 2004 Andy Adamson <andros@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.
+ */
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <pwd.h>
+#include <err.h>
+#ifdef HAVE_GSSAPI_GSSAPI_KRB5_H
+#include <gssapi/gssapi_krb5.h>
+#endif /* HAVE_GSSAPI_GSSAPI_KRB5_H */
+#ifdef HAVE_SASL_H
+#include <sasl.h>
+#endif /* HAVE_SASL_H */
+#ifdef HAVE_SASL_SASL_H
+#include <sasl/sasl.h>
+#endif /* HAVE_SASL_SASL_H */
+/* We are using deprecated functions, get the prototypes... */
+#define LDAP_DEPRECATED 1
+#include <ldap.h>
+#include "nfslib.h"
+#include "nfsidmap.h"
+#include "nfsidmap_plugin.h"
+#include "nfsidmap_private.h"
+#include "conffile.h"
+
+/* attribute/objectclass default mappings */
+#define DEFAULT_UMICH_OBJCLASS_REMOTE_PERSON "NFSv4RemotePerson"
+#define DEFAULT_UMICH_OBJCLASS_REMOTE_GROUP "NFSv4RemoteGroup"
+#define DEFAULT_UMICH_ATTR_NFSNAME "NFSv4Name"
+#define DEFAULT_UMICH_ATTR_ACCTNAME "uid"
+#define DEFAULT_UMICH_ATTR_UIDNUMBER "uidNumber"
+#define DEFAULT_UMICH_ATTR_GROUP_NFSNAME "NFSv4Name"
+#define DEFAULT_UMICH_ATTR_GIDNUMBER "gidNumber"
+#define DEFAULT_UMICH_ATTR_MEMBERUID "memberUid"
+#define DEFAULT_UMICH_ATTR_GSSAUTHNAME "GSSAuthName"
+#define DEFAULT_UMICH_ATTR_MEMBEROF "memberof"
+
+#define DEFAULT_UMICH_SEARCH_TIMEOUT 4
+
+/* config section */
+#define LDAP_SECTION "UMICH_SCHEMA"
+
+#ifndef LDAP_FILT_MAXSIZ
+#define LDAP_FILT_MAXSIZ 1024
+#endif
+
+
+/* Local structure definitions */
+
+struct ldap_map_names{
+ char *NFSv4_person_objcls;
+ char *NFSv4_nfsname_attr;
+ char *NFSv4_acctname_attr;
+ char *NFSv4_uid_attr;
+ char *NFSv4_group_objcls;
+ char *NFSv4_group_nfsname_attr;
+ char *NFSv4_gid_attr;
+ char *NFSv4_member_attr;
+ char *NFSv4_member_of_attr;
+ char *GSS_principal_attr;
+ char *NFSv4_grouplist_filter; /* Filter for grouplist lookups */
+};
+
+struct umich_ldap_info {
+ char *server; /* server name/address */
+ int port; /* server port */
+ char *base; /* base DN */
+ char *people_tree; /* base DN to start searches for people */
+ char *group_tree; /* base DN to start searches for groups */
+ char *user_dn; /* optional DN for user account when binding */
+ char *passwd; /* Password to use when binding to directory */
+ int use_ssl; /* SSL flag */
+ char *ca_cert; /* File location of the ca_cert */
+ int tls_reqcert; /* req and validate server cert */
+ int memberof_for_groups;/* Use 'memberof' attribute when
+ looking up user groups */
+ int ldap_timeout; /* Timeout in seconds for searches
+ by ldap_search_st */
+ int follow_referrals; /* whether to follow ldap referrals */
+ char *sasl_mech; /* sasl mech to be used */
+ char *sasl_realm; /* SASL realm for SASL authentication */
+ char *sasl_authcid; /* authentication identity to be used */
+ char *sasl_authzid; /* authorization identity to be used */
+ char *sasl_secprops; /* Cyrus SASL security properties. */
+ int sasl_canonicalize; /* canonicalize LDAP server host name */
+ char *sasl_krb5_ccname; /* krb5 ticket cache */
+};
+
+/* GLOBAL data */
+
+static struct umich_ldap_info ldap_info = {
+ .server = NULL,
+ .port = 0,
+ .base = NULL,
+ .people_tree = NULL,
+ .group_tree = NULL,
+ .user_dn = NULL,
+ .passwd = NULL,
+ .use_ssl = 0,
+ .ca_cert = NULL,
+ .tls_reqcert = LDAP_OPT_X_TLS_HARD,
+ .memberof_for_groups = 0,
+ .ldap_timeout = DEFAULT_UMICH_SEARCH_TIMEOUT,
+ .follow_referrals = 1,
+ .sasl_mech = NULL,
+ .sasl_realm = NULL,
+ .sasl_authcid = NULL,
+ .sasl_authzid = NULL,
+ .sasl_secprops = NULL,
+ .sasl_canonicalize = -1, /* leave to the LDAP lib */
+ .sasl_krb5_ccname = NULL,
+};
+
+static struct ldap_map_names ldap_map = {
+ .NFSv4_person_objcls = NULL,
+ .NFSv4_nfsname_attr = NULL,
+ .NFSv4_uid_attr = NULL,
+ .NFSv4_acctname_attr = NULL,
+ .NFSv4_group_objcls = NULL,
+ .NFSv4_group_nfsname_attr = NULL,
+ .NFSv4_gid_attr = NULL,
+ .NFSv4_member_attr = NULL,
+ .NFSv4_member_of_attr = NULL,
+ .GSS_principal_attr = NULL,
+ .NFSv4_grouplist_filter = NULL,
+};
+
+#ifdef ENABLE_LDAP_SASL
+
+/**
+ * Set the path of the krb5 ticket cache
+ * use gss_krb5_ccache_name if available else set the env var
+ */
+static int set_krb5_ccname(const char *krb5_ccache_name)
+{
+ int retval = 0;
+#ifdef HAVE_GSS_KRB5_CCACHE_NAME
+ OM_uint32 status;
+
+ if (gss_krb5_ccache_name(&status, krb5_ccache_name, NULL) !=
+ GSS_S_COMPLETE) {
+ IDMAP_LOG(5,
+ ("Failed to set creds cache for kerberos, minor_status(%d)",
+ status));
+ retval = status;
+ goto out;
+ }
+#else /* HAVE_GSS_KRB5_CCACHE_NAME */
+ char *env;
+ int buflen = 0;
+
+ buflen = strlen("KRB5CCNAME=") + strlen(krb5_ccache_name) + 1;
+ env = malloc(buflen);
+ if (env == NULL) {
+ retval = ENOMEM;
+ goto out;
+ }
+ snprintf(env, buflen, "KRB5CCNAME=%s", krb5_ccache_name);
+ if (putenv(env) != 0) {
+ retval = errno;
+ IDMAP_LOG(5, ("Failed to set creds cache for kerberos, err(%d)",
+ retval));
+ }
+#endif /* else HAVE_GSS_KRB5_CCACHE_NAME */
+out:
+ return retval;
+}
+
+/**
+ * SASL interact callback
+ */
+static int sasl_interact_cb(__attribute__((unused)) LDAP * ld,
+ __attribute__((unused)) unsigned int flags, void *defaults,
+ void *ctx)
+{
+ struct umich_ldap_info *linfo = defaults;
+ sasl_interact_t *interact = ctx;
+
+ while (interact->id != SASL_CB_LIST_END) {
+ switch (interact->id) {
+ case SASL_CB_AUTHNAME:
+ if (linfo->sasl_authcid == NULL ||
+ linfo->sasl_authcid[0] == '\0') {
+ IDMAP_LOG(2, ("SASL_CB_AUTHNAME asked in "
+ "callback but not found in conf"));
+ } else {
+ IDMAP_LOG(5,
+ ("Setting SASL_CB_AUTHNAME to %s",
+ linfo->sasl_authcid));
+ interact->result = linfo->sasl_authcid;
+ interact->len = strlen(linfo->sasl_authcid);
+ }
+ break;
+ case SASL_CB_PASS:
+ if (linfo->passwd == NULL || linfo->passwd[0] == '\0') {
+ IDMAP_LOG(2, ("SASL_CB_PASS asked in callback "
+ "but not found in conf"));
+ } else {
+ IDMAP_LOG(5,
+ ("Setting SASL_CB_PASS to ***"));
+ interact->result = linfo->passwd;
+ interact->len = strlen(linfo->passwd);
+ }
+ break;
+ case SASL_CB_GETREALM:
+ if (linfo->sasl_realm == NULL ||
+ linfo->sasl_realm[0] == '\0') {
+ IDMAP_LOG(2, ("SASL_CB_GETREALM asked in "
+ "callback but not found in conf"));
+ } else {
+ IDMAP_LOG(5,
+ ("Setting SASL_CB_GETREALM to %s",
+ linfo->sasl_realm));
+ interact->result = linfo->sasl_realm;
+ interact->len = strlen(linfo->sasl_realm);
+ }
+ break;
+ case SASL_CB_USER:
+ if (linfo->sasl_authzid == NULL ||
+ linfo->sasl_authzid[0] == '\0') {
+ IDMAP_LOG(2, ("SASL_CB_USER asked in callback "
+ "but not found in conf"));
+ } else {
+ IDMAP_LOG(5, ("Setting SASL_CB_USER to %s",
+ linfo->sasl_authzid));
+ interact->result = linfo->sasl_authzid;
+ interact->len = strlen(linfo->sasl_authzid);
+ }
+ break;
+ default:
+ IDMAP_LOG(2, ("Undefined value requested %d",
+ interact->id));
+ break;
+ }
+ interact++;
+ }
+ return LDAP_SUCCESS;
+}
+#endif /* ENABLE_LDAP_SASL */
+
+/* Local routines */
+
+static int
+ldap_init_and_bind(LDAP **pld,
+ int *sizelimit,
+ struct umich_ldap_info *linfo)
+{
+ LDAP *ld;
+ int lerr;
+ int err = -1;
+ int current_version, new_version;
+ char server_url[1024];
+ int debug_level = 65535;
+ int i;
+ LDAPAPIInfo apiinfo = {.ldapai_info_version = LDAP_API_INFO_VERSION};
+
+ snprintf(server_url, sizeof(server_url), "%s://%s:%d",
+ (linfo->use_ssl) ? "ldaps" : "ldap",
+ linfo->server, linfo->port);
+
+ /*
+ * XXX We really, REALLY only want to initialize once, not for
+ * each request. Figure out how to do that!
+ */
+ if ((lerr = ldap_initialize(&ld, server_url)) != LDAP_SUCCESS) {
+ IDMAP_LOG(0, ("ldap_init_and_bind: ldap_initialize() failed "
+ "to [%s]: %s (%d)", server_url,
+ ldap_err2string(lerr), lerr));
+ goto out;
+ }
+
+ if ((ldap_set_option(ld, LDAP_OPT_DEBUG_LEVEL, &debug_level)
+ != LDAP_SUCCESS)) {
+ IDMAP_LOG(0, ("ldap_init_and_bind: error setting ldap "
+ "library debugging level"));
+ goto out;
+ }
+
+ /*
+ * Get LDAP API information and compare the protocol version there
+ * to the protocol version returned directly from get_option.
+ */
+ ldap_get_option(ld, LDAP_OPT_API_INFO, &apiinfo);
+ if (apiinfo.ldapai_info_version != LDAP_API_INFO_VERSION) {
+ IDMAP_LOG(0, ("ldap_init_and_bind: APIInfo version mismatch: "
+ "library %d, header %d",
+ apiinfo.ldapai_info_version, LDAP_API_INFO_VERSION));
+ goto out;
+ }
+ ldap_get_option(ld, LDAP_OPT_PROTOCOL_VERSION, &current_version);
+ if (apiinfo.ldapai_protocol_version == LDAP_VERSION3 &&
+ current_version != LDAP_VERSION3) {
+ new_version = LDAP_VERSION3;
+ IDMAP_LOG(4, ("ldap_init_and_bind: version mismatch between "
+ "API information and protocol version. Setting "
+ "protocol version to %d", new_version));
+ ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &new_version);
+ }
+
+ for (i = 0; apiinfo.ldapai_extensions[i]; i++) {
+ char *extension = apiinfo.ldapai_extensions[i];
+ ldap_memfree (extension);
+ }
+ ldap_memfree (apiinfo.ldapai_extensions);
+ ldap_memfree(apiinfo.ldapai_vendor_name);
+
+ /* Set sizelimit option if requested */
+ if (sizelimit) {
+ ldap_set_option(ld, LDAP_OPT_SIZELIMIT, (void *)sizelimit);
+ }
+
+ lerr = ldap_set_option(ld, LDAP_OPT_REFERRALS,
+ linfo->follow_referrals ? (void *)LDAP_OPT_ON :
+ (void *)LDAP_OPT_OFF);
+ if (lerr != LDAP_SUCCESS) {
+ IDMAP_LOG(2, ("ldap_init_and_bind: setting LDAP_OPT_REFERRALS "
+ "failed: %s (%d)", ldap_err2string(lerr), lerr));
+ goto out;
+ }
+
+ /* Set option to to use SSL/TLS if requested */
+ if (linfo->use_ssl) {
+ int tls_type = LDAP_OPT_X_TLS_HARD;
+ lerr = ldap_set_option(ld, LDAP_OPT_X_TLS, &tls_type);
+ if (lerr != LDAP_SUCCESS) {
+ IDMAP_LOG(2, ("ldap_init_and_bind: setting SSL "
+ "failed : %s (%d)",
+ ldap_err2string(lerr), lerr));
+ goto out;
+ }
+
+ if (linfo->ca_cert != NULL) {
+ lerr = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE,
+ linfo->ca_cert);
+ if (lerr != LDAP_SUCCESS) {
+ IDMAP_LOG(2, ("ldap_init_and_bind: setting CA "
+ "certificate file failed : %s (%d)",
+ ldap_err2string(lerr), lerr));
+ goto out;
+ }
+ }
+
+ lerr = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT,
+ &linfo->tls_reqcert);
+ if (lerr != LDAP_SUCCESS) {
+ IDMAP_LOG(2, ("ldap_init_and_bind: setting "
+ "req CA cert failed : %s(%d)",
+ ldap_err2string(lerr), lerr));
+ goto out;
+ }
+ }
+
+ /* If we have a DN (and password) attempt an authenticated bind */
+ if (linfo->user_dn) {
+retry_bind:
+#ifdef ENABLE_LDAP_SASL
+ if (linfo->sasl_mech != NULL && linfo->sasl_mech[0] != '\0') {
+ /* use sasl bind */
+ if (linfo->sasl_canonicalize != -1) {
+ lerr = ldap_set_option(ld,
+ LDAP_OPT_X_SASL_NOCANON,
+ linfo->sasl_canonicalize ?
+ LDAP_OPT_OFF : LDAP_OPT_ON);
+ if (lerr != LDAP_SUCCESS) {
+ IDMAP_LOG(2, ("ldap_init_and_bind: "
+ "setting sasl_canonicalize"
+ " failed: %s (%d)",
+ ldap_err2string(lerr),
+ lerr));
+ goto out;
+ }
+ }
+ if (linfo->sasl_secprops != NULL &&
+ linfo->sasl_secprops[0] != '\0') {
+ lerr = ldap_set_option(ld,
+ LDAP_OPT_X_SASL_SECPROPS,
+ (void *) linfo->sasl_secprops);
+ if (lerr != LDAP_SUCCESS) {
+ IDMAP_LOG(2, ("ldap_init_and_bind: "
+ "setting sasl_secprops"
+ " failed: %s (%d)",
+ ldap_err2string(lerr),
+ lerr));
+ goto out;
+ }
+ }
+ if (linfo->sasl_krb5_ccname != NULL &&
+ linfo->sasl_krb5_ccname[0] != '\0') {
+ lerr = set_krb5_ccname(linfo->sasl_krb5_ccname);
+ if (lerr != 0) {
+ IDMAP_LOG(2,
+ ("ldap_init_and_bind: Failed "
+ "to set krb5 ticket cache, "
+ "err=%d", lerr));
+ }
+ }
+ lerr = ldap_sasl_interactive_bind_s(ld, linfo->user_dn,
+ linfo->sasl_mech, NULL, NULL, LDAP_SASL_QUIET,
+ sasl_interact_cb, linfo);
+ } else {
+ lerr = ldap_simple_bind_s(ld, linfo->user_dn,
+ linfo->passwd);
+ }
+#else /* ENABLE_LDAP_SASL */
+ lerr = ldap_simple_bind_s(ld, linfo->user_dn, linfo->passwd);
+#endif /* else ENABLE_LDAP_SASL */
+ if (lerr) {
+ char *errmsg;
+ if (lerr == LDAP_PROTOCOL_ERROR) {
+ ldap_get_option(ld, LDAP_OPT_PROTOCOL_VERSION,
+ &current_version);
+ new_version = current_version == LDAP_VERSION2 ?
+ LDAP_VERSION3 : LDAP_VERSION2;
+ ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,
+ &new_version);
+ IDMAP_LOG(2, ("ldap_init_and_bind: "
+ "got protocol error while attempting "
+ "bind with protocol version %d, "
+ "trying protocol version %d",
+ current_version, new_version));
+ if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg) == LDAP_SUCCESS)
+ && (errmsg != NULL) && (*errmsg != '\0')) {
+ IDMAP_LOG(2, ("ldap_init_and_bind: "
+ "Additional info: %s", errmsg));
+ ldap_memfree(errmsg);
+ }
+ goto retry_bind;
+ }
+#ifdef ENABLE_LDAP_SASL
+ IDMAP_LOG(2, ("ldap_init_and_bind: %s "
+ "to [%s] as user '%s': %s (%d)",
+ (linfo->sasl_mech != NULL &&
+ linfo->sasl_mech[0] != '\0') ?
+ "ldap_sasl_interactive_bind_s" :
+ "ldap_simple_bind_s",
+ server_url, linfo->user_dn,
+ ldap_err2string(lerr), lerr));
+#else /* ENABLE_LDAP_SASL */
+ IDMAP_LOG(2, ("ldap_init_and_bind: ldap_simple_bind_s"
+ "to [%s] as user '%s': %s (%d)",
+ server_url, linfo->user_dn,
+ ldap_err2string(lerr), lerr));
+
+#endif /* else ENABLE_LDAP_SASL */
+ if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg) == LDAP_SUCCESS)
+ && (errmsg != NULL)&& (*errmsg != '\0')) {
+ IDMAP_LOG(2, ("ldap_init_and_bind: "
+ "Additional info: %s", errmsg));
+ ldap_memfree(errmsg);
+ }
+ goto out;
+ }
+ }
+#ifdef LDAP_ANONYMOUS_BIND_REQUIRED
+ else {
+ lerr = ldap_simple_bind_s(ld, NULL, NULL);
+ if (lerr) {
+ char *errmsg;
+
+ IDMAP_LOG(2, ("ldap_init_and_bind: ldap_simple_bind_s "
+ "to [%s] as anonymous: %s (%d)", server_url,
+ ldap_err2string(lerr), lerr));
+ if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg) == LDAP_SUCCESS)
+ && (errmsg != NULL) && (*errmsg != '\0')) {
+ IDMAP_LOG(2, ("ldap_init_and_bind: "
+ "Additional info: %s", errmsg));
+ ldap_memfree(errmsg);
+ }
+ goto out;
+ }
+ }
+#endif
+
+ *pld = ld;
+ err = 0;
+out:
+ return err;
+}
+
+static int
+umich_name_to_ids(char *name, int idtype, uid_t *uid, gid_t *gid,
+ char *attrtype, struct umich_ldap_info *linfo)
+{
+ LDAP *ld = NULL;
+ struct timeval timeout = {
+ .tv_sec = linfo->ldap_timeout,
+ };
+ LDAPMessage *result = NULL, *entry;
+ BerElement *ber = NULL;
+ char **idstr, filter[LDAP_FILT_MAXSIZ], *base;
+ char *attrs[3];
+ char *attr_res;
+ int count = 0, err, lerr, f_len;
+ int sizelimit = 1;
+
+ err = -EINVAL;
+ if (uid == NULL || gid == NULL || name == NULL ||
+ attrtype == NULL || linfo == NULL || linfo->server == NULL ||
+ linfo->people_tree == NULL || linfo->group_tree == NULL)
+ goto out;
+
+ *uid = -1;
+ *gid = -1;
+
+ if (idtype == IDTYPE_USER) {
+ if ((f_len = snprintf(filter, LDAP_FILT_MAXSIZ,
+ "(&(objectClass=%s)(%s=%s))",
+ ldap_map.NFSv4_person_objcls,
+ attrtype, name))
+ == LDAP_FILT_MAXSIZ) {
+ IDMAP_LOG(0, ("ERROR: umich_name_to_ids: filter "
+ "too long!"));
+ goto out;
+ }
+ base = linfo->people_tree;
+ }
+ else if (idtype == IDTYPE_GROUP) {
+ if ((f_len = snprintf(filter, LDAP_FILT_MAXSIZ,
+ "(&(objectClass=%s)(%s=%s))",
+ ldap_map.NFSv4_group_objcls,
+ attrtype, name))
+ == LDAP_FILT_MAXSIZ) {
+ IDMAP_LOG(0, ("ERROR: umich_name_to_ids: filter "
+ "too long!"));
+ goto out;
+ }
+ base = linfo->group_tree;
+ }
+ else {
+ IDMAP_LOG(0, ("ERROR: umich_name_to_ids: invalid idtype (%d)",
+ idtype));
+ goto out;
+ }
+
+ if (ldap_init_and_bind(&ld, &sizelimit, linfo))
+ goto out;
+
+ attrs[0] = ldap_map.NFSv4_uid_attr;
+ attrs[1] = ldap_map.NFSv4_gid_attr;
+ attrs[2] = NULL;
+
+ err = ldap_search_st(ld, base, LDAP_SCOPE_SUBTREE,
+ filter, (char **)attrs,
+ 0, &timeout, &result);
+ if (err) {
+ char *errmsg;
+
+ IDMAP_LOG(2, ("umich_name_to_ids: ldap_search_st for "
+ "base '%s', filter '%s': %s (%d)",
+ base, filter, ldap_err2string(err), err));
+ if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg) == LDAP_SUCCESS)
+ && (errmsg != NULL) && (*errmsg != '\0')) {
+ IDMAP_LOG(2, ("umich_name_to_ids: "
+ "Additional info: %s", errmsg));
+ ldap_memfree(errmsg);
+ }
+ err = -ENOENT;
+ goto out_unbind;
+ }
+
+ err = -ENOENT;
+ count = ldap_count_entries(ld, result);
+ if (count != 1) {
+ goto out_unbind;
+ }
+
+ if (!(entry = ldap_first_entry(ld, result))) {
+ lerr = ldap_result2error(ld, result, 0);
+ IDMAP_LOG(2, ("umich_name_to_ids: ldap_first_entry: "
+ "%s (%d)", ldap_err2string(lerr), lerr));
+ goto out_unbind;
+ }
+
+ /*
+ * Attributes come back in no particular order, so we need
+ * to check each one to see what it is before assigning values.
+ * XXX There must be a better way than comparing the
+ * name of each attribute?
+ */
+ for (attr_res = ldap_first_attribute(ld, result, &ber);
+ attr_res != NULL;
+ attr_res = ldap_next_attribute(ld, result, ber)) {
+
+ unsigned long tmp_u, tmp_g;
+ uid_t tmp_uid;
+ gid_t tmp_gid;
+
+ if ((idstr = ldap_get_values(ld, result, attr_res)) == NULL) {
+ lerr = ldap_result2error(ld, result, 0);
+ IDMAP_LOG(2, ("umich_name_to_ids: ldap_get_values: "
+ "%s (%d)", ldap_err2string(lerr), lerr));
+ goto out_memfree;
+ }
+ if (strcasecmp(attr_res, ldap_map.NFSv4_uid_attr) == 0) {
+ tmp_u = strtoul(*idstr, (char **)NULL, 10);
+ tmp_uid = tmp_u;
+ if (tmp_uid != tmp_u ||
+ (errno == ERANGE && tmp_u == ULONG_MAX)) {
+ IDMAP_LOG(0, ("ERROR: umich_name_to_ids: "
+ "uidNumber too long converting '%s'",
+ *idstr));
+ ldap_memfree(attr_res);
+ ldap_value_free(idstr);
+ goto out_memfree;
+ }
+ *uid = tmp_uid;
+ err = 0;
+ } else if (strcasecmp(attr_res, ldap_map.NFSv4_gid_attr) == 0) {
+ tmp_g = strtoul(*idstr, (char **)NULL, 10);
+ tmp_gid = tmp_g;
+ if (tmp_gid != tmp_g ||
+ (errno == ERANGE && tmp_g == ULONG_MAX)) {
+ IDMAP_LOG(0, ("ERROR: umich_name_to_ids: "
+ "gidNumber too long converting '%s'",
+ *idstr));
+ ldap_memfree(attr_res);
+ ldap_value_free(idstr);
+ goto out_memfree;
+ }
+ *gid = tmp_gid;
+ err = 0;
+ } else {
+ IDMAP_LOG(0, ("umich_name_to_ids: received attr "
+ "'%s' ???", attr_res));
+ ldap_memfree(attr_res);
+ ldap_value_free(idstr);
+ goto out_memfree;
+ }
+ ldap_memfree(attr_res);
+ ldap_value_free(idstr);
+ }
+
+out_memfree:
+ ber_free(ber, 0);
+out_unbind:
+ if (result)
+ ldap_msgfree(result);
+ ldap_unbind(ld);
+out:
+ return err;
+}
+
+static int
+umich_id_to_name(uid_t id, int idtype, char **name, size_t len,
+ struct umich_ldap_info *linfo)
+{
+ LDAP *ld = NULL;
+ struct timeval timeout = {
+ .tv_sec = linfo->ldap_timeout,
+ };
+ LDAPMessage *result = NULL, *entry;
+ BerElement *ber;
+ char **names = NULL, filter[LDAP_FILT_MAXSIZ], *base;
+ char idstr[16];
+ char *attrs[2];
+ char *attr_res;
+ int count = 0, err, lerr, f_len;
+ int sizelimit = 1;
+
+ err = -EINVAL;
+ if (name == NULL || linfo == NULL || linfo->server == NULL ||
+ linfo->people_tree == NULL || linfo->group_tree == NULL)
+ goto out;
+
+ snprintf(idstr, sizeof(idstr), "%d", id);
+
+
+ if (idtype == IDTYPE_USER) {
+ if ((f_len = snprintf(filter, LDAP_FILT_MAXSIZ,
+ "(&(objectClass=%s)(%s=%s))",
+ ldap_map.NFSv4_person_objcls,
+ ldap_map.NFSv4_uid_attr, idstr))
+ == LDAP_FILT_MAXSIZ) {
+ IDMAP_LOG(0, ("ERROR: umich_id_to_name: "
+ "uid filter too long!"));
+ goto out;
+ }
+ base = linfo->people_tree;
+ } else if (idtype == IDTYPE_GROUP) {
+ if ((f_len = snprintf(filter, LDAP_FILT_MAXSIZ,
+ "(&(objectClass=%s)(%s=%s))",
+ ldap_map.NFSv4_group_objcls,
+ ldap_map.NFSv4_gid_attr,idstr))
+ == LDAP_FILT_MAXSIZ) {
+ IDMAP_LOG(0, ("ERROR: umich_id_to_name: "
+ "gid filter too long!"));
+ goto out;
+ }
+ base = linfo->group_tree;
+ } else {
+ IDMAP_LOG(0, ("ERROR: umich_id_to_name: invalid idtype (%d)",
+ idtype));
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (ldap_init_and_bind(&ld, &sizelimit, linfo))
+ goto out;
+
+ if (idtype == IDTYPE_USER)
+ attrs[0] = ldap_map.NFSv4_nfsname_attr;
+ else
+ attrs[0] = ldap_map.NFSv4_group_nfsname_attr;
+ attrs[1] = NULL;
+
+ err = ldap_search_st(ld, base, LDAP_SCOPE_SUBTREE,
+ filter, (char **)attrs,
+ 0, &timeout, &result);
+ if (err) {
+ char * errmsg;
+
+ IDMAP_LOG(2, ("umich_id_to_name: ldap_search_st for "
+ "base '%s, filter '%s': %s (%d)", base, filter,
+ ldap_err2string(err), err));
+ if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg) == LDAP_SUCCESS)
+ && (errmsg != NULL) && (*errmsg != '\0')) {
+ IDMAP_LOG(2, ("umich_id_to_name: "
+ "Additional info: %s", errmsg));
+ ldap_memfree(errmsg);
+ }
+
+ err = -ENOENT;
+ goto out_unbind;
+ }
+
+ err = -ENOENT;
+ count = ldap_count_entries(ld, result);
+ if (count != 1)
+ goto out_unbind;
+
+ if (!(entry = ldap_first_entry(ld, result))) {
+ lerr = ldap_result2error(ld, result, 0);
+ IDMAP_LOG(2, ("umich_id_to_name: ldap_first_entry: "
+ "%s (%d)", ldap_err2string(lerr), lerr));
+ goto out_unbind;
+ }
+
+ if (!(attr_res = ldap_first_attribute(ld, result, &ber))) {
+ lerr = ldap_result2error(ld, result, 0);
+ IDMAP_LOG(2, ("umich_id_to_name: ldap_first_attribute: "
+ "%s (%d)", ldap_err2string(lerr), lerr));
+ goto out_unbind;
+ }
+
+ if ((names = ldap_get_values(ld, result, attr_res)) == NULL) {
+ lerr = ldap_result2error(ld, result, 0);
+ IDMAP_LOG(2, ("umich_id_to_name: ldap_get_values: "
+ "%s (%d)", ldap_err2string(lerr), lerr));
+ goto out_memfree;
+ }
+
+ /*
+ * Verify there is enough room in the output buffer before
+ * copying returned string. (strlen doesn't count the null,
+ * we make sure there is room for the null also, therefore
+ * we use ">=" not just ">")
+ */
+ if (strlen(names[0]) >= len) {
+ /* not enough space to return the name */
+ IDMAP_LOG(1, ("umich_id_to_name: output buffer size (%d) "
+ "too small to return string, '%s', of length %d",
+ len, names[0], strlen(names[0])));
+ goto out_memfree;
+ }
+ strcpy(*name, names[0]);
+
+ err = 0;
+out_memfree:
+ if (names)
+ ldap_value_free(names);
+ ldap_memfree(attr_res);
+ ber_free(ber, 0);
+out_unbind:
+ if (result)
+ ldap_msgfree(result);
+ ldap_unbind(ld);
+out:
+ return err;
+}
+
+static int
+umich_gss_princ_to_grouplist(char *principal, gid_t *groups, int *ngroups,
+ struct umich_ldap_info *linfo)
+{
+ LDAP *ld = NULL;
+ struct timeval timeout = {
+ .tv_sec = linfo->ldap_timeout,
+ };
+ LDAPMessage *result, *entry;
+ char **names, filter[LDAP_FILT_MAXSIZ];
+ char *attrs[2];
+ int count = 0, err = -ENOMEM, lerr, f_len;
+ int i, num_gids;
+ gid_t *curr_group = groups;
+
+ err = -EINVAL;
+ if (linfo == NULL || linfo->server == NULL ||
+ linfo->people_tree == NULL || linfo->group_tree == NULL)
+ goto out;
+
+
+ if (ldap_init_and_bind(&ld, NULL, linfo))
+ goto out;
+
+ /*
+ * First we need to map the gss principal name to a uid (name) string
+ */
+ err = -EINVAL;
+ if ((f_len = snprintf(filter, LDAP_FILT_MAXSIZ,
+ "(&(objectClass=%s)(%s=%s))",
+ ldap_map.NFSv4_person_objcls,
+ ldap_map.GSS_principal_attr, principal))
+ == LDAP_FILT_MAXSIZ) {
+ IDMAP_LOG(0, ("ERROR: umich_gss_princ_to_grouplist: "
+ "filter too long!"));
+ goto out;
+ }
+
+ attrs[0] = ldap_map.NFSv4_acctname_attr;
+ attrs[1] = NULL;
+
+ err = ldap_search_st(ld, linfo->people_tree, LDAP_SCOPE_SUBTREE,
+ filter, attrs, 0, &timeout, &result);
+ if (err) {
+ char *errmsg;
+
+ IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_search_st "
+ "for tree '%s, filter '%s': %s (%d)",
+ linfo->people_tree, filter,
+ ldap_err2string(err), err));
+ if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg) == LDAP_SUCCESS)
+ && (errmsg != NULL) && (*errmsg != '\0')) {
+ IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: "
+ "Additional info: %s", errmsg));
+ ldap_memfree(errmsg);
+ }
+ err = -ENOENT;
+ goto out_unbind;
+ }
+
+ err = -ENOENT;
+ count = ldap_count_entries(ld, result);
+ if (count != 1) {
+ IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: "
+ "ldap account lookup of gssauthname %s returned %d accounts",
+ principal,count));
+ goto out_unbind;
+ }
+
+ if (!(entry = ldap_first_entry(ld, result))) {
+ lerr = ldap_result2error(ld, result, 0);
+ IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_first_entry: "
+ "%s (%d)", ldap_err2string(lerr), lerr));
+ goto out_unbind;
+ }
+
+ if ((names = ldap_get_values(ld, result, attrs[0])) == NULL) {
+ lerr = ldap_result2error(ld, result, 0);
+ IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_get_values: "
+ "%s (%d)", ldap_err2string(lerr), lerr));
+ goto out_unbind;
+ }
+
+ if (ldap_info.memberof_for_groups) {
+
+ /*
+ * Collect the groups the user belongs to
+ */
+ if ((f_len = snprintf(filter, LDAP_FILT_MAXSIZ,
+ "(&(objectClass=%s)(%s=%s))",
+ ldap_map.NFSv4_person_objcls,
+ ldap_map.NFSv4_acctname_attr,
+ names[0])) == LDAP_FILT_MAXSIZ ) {
+ IDMAP_LOG(2, ("ERROR: umich_gss_princ_to_grouplist: "
+ "filter too long!"));
+ ldap_value_free(names);
+ goto out_unbind;
+ }
+
+ ldap_value_free(names);
+
+ attrs[0] = ldap_map.NFSv4_member_of_attr;
+ attrs[1] = NULL;
+
+ err = ldap_search_st(ld, linfo->people_tree, LDAP_SCOPE_SUBTREE,
+ filter, attrs, 0, &timeout, &result);
+
+ if (err) {
+ char *errmsg;
+
+ IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_search_st "
+ "for tree '%s, filter '%s': %s (%d)",
+ linfo->people_tree, filter,
+ ldap_err2string(err), err));
+ if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg) == LDAP_SUCCESS)
+ && (errmsg != NULL) && (*errmsg != '\0')) {
+ IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: "
+ "Additional info: %s", errmsg));
+ ldap_memfree(errmsg);
+ }
+ err = -ENOENT;
+ goto out_unbind;
+ }
+ err = -ENOENT;
+
+ /* pull the list of groups and place into names */
+ count = ldap_count_entries(ld, result);
+ if (count != 1) {
+ IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: "
+ "ldap group member lookup of gssauthname %s returned %d multiple entries",
+ principal,count));
+ goto out_unbind;
+ }
+
+ if (!(entry = ldap_first_entry(ld, result))) {
+ lerr = ldap_result2error(ld, result, 0);
+ IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_first_entry: "
+ "%s (%d)", ldap_err2string(lerr), lerr));
+ goto out_unbind;
+ }
+
+ if ((names = ldap_get_values(ld, result, attrs[0])) == NULL) {
+ lerr = ldap_result2error(ld, result, 0);
+ IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_get_values: "
+ "%s (%d)", ldap_err2string(lerr), lerr));
+ goto out_unbind;
+ }
+
+ /* Count the groups first before doing a lookup of the group.
+ If it exceeds the desired number of groups set the needed value
+ and abort. */
+ for (i = 0; names[i] != NULL; i++);
+ if ( i > *ngroups ) {
+ ldap_value_free(names);
+ err = -EINVAL;
+ IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: User %s, "
+ "number of groups %d, exceeds requested number %d",
+ principal, i, *ngroups));
+ *ngroups = i;
+ goto out_unbind;
+ }
+
+ /* Loop through the groupnames (names) and get the group gid */
+ num_gids = 0;
+ for (i = 0; names[i] != NULL; i++){
+ char **vals;
+ int valcount;
+ unsigned long tmp_g;
+ gid_t tmp_gid;
+ char *cnptr = NULL;
+
+ cnptr = strchr(names[i],',');
+ if (cnptr) *cnptr = '\0';
+
+ err = -ENOENT;
+ if (ldap_map.NFSv4_grouplist_filter)
+ f_len = snprintf(filter, LDAP_FILT_MAXSIZ,
+ "(&(objectClass=%s)(%s)%s)",
+ ldap_map.NFSv4_group_objcls,
+ names[i],
+ ldap_map.NFSv4_grouplist_filter);
+ else
+ f_len = snprintf(filter, LDAP_FILT_MAXSIZ,
+ "(&(objectClass=%s)(%s))",
+ ldap_map.NFSv4_group_objcls,
+ names[i]);
+
+ if ( f_len == LDAP_FILT_MAXSIZ ) {
+ IDMAP_LOG(2, ("ERROR: umich_gss_princ_to_grouplist: "
+ "filter too long!"));
+ ldap_value_free(names);
+ goto out_unbind;
+ }
+ attrs[0] = ldap_map.NFSv4_gid_attr;
+ attrs[1] = NULL;
+
+ err = ldap_search_st(ld, linfo->group_tree, LDAP_SCOPE_SUBTREE,
+ filter, attrs, 0, &timeout, &result);
+ if (err) {
+ char *errmsg;
+
+ IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_search_st "
+ "for tree '%s, filter '%s': %s (%d)",
+ linfo->group_tree, filter,
+ ldap_err2string(err), err));
+ if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg)==LDAP_SUCCESS)
+ &&
+ (errmsg != NULL) && (*errmsg != '\0')) {
+ IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: "
+ "Additional info: %s", errmsg));
+ ldap_memfree(errmsg);
+ }
+ continue;
+ }
+
+ count = ldap_count_entries(ld, result);
+ if (count == 0)
+ continue;
+ if (count != 1 ){
+ IDMAP_LOG(2, ("umich_gss_princ_to_grouplist:"
+ "Group %s has %d gids defined - aborting", names[i], count));
+ ldap_value_free(names);
+ err = -ENOENT;
+ goto out_unbind;
+ }
+
+ vals = ldap_get_values(ld, result, ldap_map.NFSv4_gid_attr);
+
+ /* There should be only one gidNumber attribute per group */
+ if ((valcount = ldap_count_values(vals)) != 1) {
+ IDMAP_LOG(2, ("DB problem getting gidNumber of "
+ "posixGroup! (count was %d)", valcount));
+ ldap_value_free(vals);
+ continue;
+ }
+
+ tmp_g = strtoul(vals[0], (char **)NULL, 10);
+ tmp_gid = tmp_g;
+ if (tmp_gid != tmp_g ||
+ (errno == ERANGE && tmp_g == ULONG_MAX)) {
+ IDMAP_LOG(2, ("ERROR: umich_gss_princ_to_grouplist: "
+ "gidNumber too long converting '%s'",
+ vals[0]));
+ ldap_value_free(vals);
+ continue;
+ }
+ *curr_group++ = tmp_gid;
+ num_gids++;
+ ldap_value_free(vals);
+ }
+ ldap_value_free(names);
+ *ngroups = num_gids;
+ err = 0;
+ } else {
+
+ /*
+ * Then determine the groups that uid (name) string is a member of
+ */
+ err = -EINVAL;
+ if (ldap_map.NFSv4_grouplist_filter)
+ f_len = snprintf(filter, LDAP_FILT_MAXSIZ,
+ "(&(objectClass=%s)(%s=%s)%s)",
+ ldap_map.NFSv4_group_objcls,
+ ldap_map.NFSv4_member_attr,
+ names[0],
+ ldap_map.NFSv4_grouplist_filter);
+
+ else
+ f_len = snprintf(filter, LDAP_FILT_MAXSIZ,
+ "(&(objectClass=%s)(%s=%s))",
+ ldap_map.NFSv4_group_objcls,
+ ldap_map.NFSv4_member_attr,
+ names[0]);
+
+ if ( f_len == LDAP_FILT_MAXSIZ ) {
+ IDMAP_LOG(0, ("ERROR: umich_gss_princ_to_grouplist: "
+ "filter too long!"));
+ ldap_value_free(names);
+ goto out_unbind;
+ }
+
+ ldap_value_free(names);
+
+ attrs[0] = ldap_map.NFSv4_gid_attr;
+ attrs[1] = NULL;
+
+ err = ldap_search_st(ld, linfo->group_tree, LDAP_SCOPE_SUBTREE,
+ filter, attrs, 0, &timeout, &result);
+
+ if (err) {
+ char *errmsg;
+
+ IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_search_st "
+ "for tree '%s, filter '%s': %s (%d)",
+ linfo->group_tree, filter,
+ ldap_err2string(err), err));
+ if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg) == LDAP_SUCCESS) &&
+ (errmsg != NULL) && (*errmsg != '\0')) {
+ IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: "
+ "Additional info: %s", errmsg));
+ ldap_memfree(errmsg);
+ }
+ err = -ENOENT;
+ goto out_unbind;
+ }
+
+ /*
+ * If we can't determine count, return that error
+ * If we have nothing to return, return success
+ * If we have more than they asked for, tell them the
+ * number required and return an error
+ */
+ count = ldap_count_entries(ld, result);
+
+ if (count < 0) {
+ err = count;
+ goto out_unbind;
+ }
+ if (count == 0) {
+ *ngroups = 0;
+ err = 0;
+ goto out_unbind;
+ }
+ if (count > *ngroups) {
+ *ngroups = count;
+ err = -EINVAL;
+ goto out_unbind;
+ }
+ *ngroups = count;
+
+ curr_group = groups;
+
+ err = -ENOENT;
+ for (entry = ldap_first_entry(ld, result);
+ entry != NULL;
+ entry = ldap_next_entry(ld, entry)) {
+
+ char **vals;
+ int valcount;
+ unsigned long tmp_g;
+ gid_t tmp_gid;
+
+ vals = ldap_get_values(ld, entry, ldap_map.NFSv4_gid_attr);
+
+ /* There should be only one gidNumber attribute per group */
+ if ((valcount = ldap_count_values(vals)) != 1) {
+ IDMAP_LOG(0, ("DB problem getting gidNumber of "
+ "posixGroup! (count was %d)", valcount));
+ goto out_unbind;
+ }
+ tmp_g = strtoul(vals[0], (char **)NULL, 10);
+ tmp_gid = tmp_g;
+ if (tmp_gid != tmp_g ||
+ (errno == ERANGE && tmp_g == ULONG_MAX)) {
+ IDMAP_LOG(0, ("ERROR: umich_gss_princ_to_grouplist: "
+ "gidNumber too long converting '%s'",
+ vals[0]));
+ ldap_value_free(vals);
+ goto out_unbind;
+ }
+ *curr_group++ = tmp_gid;
+ ldap_value_free(vals);
+ }
+ err = 0;
+ }
+
+out_unbind:
+ ldap_unbind(ld);
+out:
+ return err;
+}
+
+
+/*
+ * principal: krb5 - princ@realm, use KrbName ldap attribute
+ * spkm3 - X.509 dn, use X509Name ldap attribute
+ */
+static int
+umichldap_gss_princ_to_ids(char *secname, char *principal,
+ uid_t *uid, gid_t *gid,
+ extra_mapping_params **UNUSED(ex))
+{
+ uid_t rtnd_uid = -1;
+ gid_t rtnd_gid = -1;
+ int err = -EINVAL;
+
+ if ((strcmp(secname, "krb5") != 0) && (strcmp(secname, "spkm3") != 0)) {
+ IDMAP_LOG(0, ("ERROR: umichldap_gss_princ_to_ids: "
+ "invalid secname '%s'", secname));
+ return err;
+ }
+
+ err = umich_name_to_ids(principal, IDTYPE_USER, &rtnd_uid, &rtnd_gid,
+ ldap_map.GSS_principal_attr, &ldap_info);
+ if (err < 0)
+ goto out;
+
+ *uid = rtnd_uid;
+ *gid = rtnd_gid;
+out:
+ return err;
+}
+
+static int
+umichldap_name_to_uid(char *name, uid_t *uid)
+{
+ gid_t gid;
+
+ return umich_name_to_ids(name, IDTYPE_USER, uid,
+ &gid, ldap_map.NFSv4_nfsname_attr, &ldap_info);
+}
+
+static int
+umichldap_name_to_gid(char *name, gid_t *gid)
+{
+ uid_t uid;
+
+ return umich_name_to_ids(name, IDTYPE_GROUP, &uid, gid,
+ ldap_map.NFSv4_group_nfsname_attr, &ldap_info);
+}
+
+static int
+umichldap_uid_to_name(uid_t uid, char *UNUSED(domain), char *name, size_t len)
+{
+ return umich_id_to_name(uid, IDTYPE_USER, &name, len, &ldap_info);
+}
+
+static int
+umichldap_gid_to_name(gid_t gid, char *UNUSED(domain), char *name, size_t len)
+{
+ return umich_id_to_name(gid, IDTYPE_GROUP, &name, len, &ldap_info);
+}
+
+static int
+umichldap_gss_princ_to_grouplist(char *secname, char *principal,
+ gid_t *groups, int *ngroups, extra_mapping_params **UNUSED(ex))
+{
+ int err = -EINVAL;
+
+ if ((strcmp(secname, "krb5") != 0) && (strcmp(secname, "spkm3") != 0)) {
+ IDMAP_LOG(0, ("ERROR: umichldap_gss_princ_to_grouplist: "
+ "invalid secname '%s'", secname));
+ return err;
+ }
+
+ return umich_gss_princ_to_grouplist(principal, groups, ngroups,
+ &ldap_info);
+}
+
+/*
+ * TLS connections require that the hostname we specify matches
+ * the hostname in the certificate that the server uses.
+ * Get a canonical name for the host specified in the config file.
+ */
+static char *
+get_canonical_hostname(const char *inname)
+{
+ int aierr, error;
+ struct addrinfo *ap, aihints;
+ char *return_name = NULL;
+ char tmphost[NI_MAXHOST];
+
+ memset(&aihints, 0, sizeof(aihints));
+ aihints.ai_socktype = SOCK_STREAM;
+ aihints.ai_flags = AI_CANONNAME;
+ aihints.ai_family = PF_INET;
+ aierr = getaddrinfo(inname, NULL, &aihints, &ap);
+ if (aierr) {
+ const char *msg;
+ /* We want to customize some messages. */
+ switch (aierr) {
+ case EAI_NONAME:
+ msg = "host unknown";
+ break;
+ default:
+ msg = gai_strerror(aierr);
+ break;
+ }
+ IDMAP_LOG(1, ("%s: '%s': %s", __FUNCTION__, inname, msg));
+ goto out_err;
+ }
+ if (ap == 0) {
+ IDMAP_LOG(1, ("%s: no addresses for host '%s'?",
+ __FUNCTION__, inname));
+ goto out_err;
+ }
+
+ error = getnameinfo (ap->ai_addr, ap->ai_addrlen, tmphost,
+ sizeof(tmphost), NULL, 0, 0);
+ if (error) {
+ IDMAP_LOG(1, ("%s: getnameinfo for host '%s' failed (%d)",
+ __FUNCTION__, inname));
+ goto out_free;
+ }
+ return_name = strdup (tmphost);
+
+out_free:
+ nfs_freeaddrinfo(ap);
+out_err:
+ return return_name;
+}
+
+static int
+umichldap_init(void)
+{
+ char *tssl, *canonicalize, *memberof, *cert_req, *follow_referrals;
+ char missing_msg[128] = "";
+ char *server_in, *canon_name;
+
+ if (nfsidmap_conf_path)
+ conf_init_file(nfsidmap_conf_path);
+
+ server_in = conf_get_str(LDAP_SECTION, "LDAP_server");
+ ldap_info.base = conf_get_str(LDAP_SECTION, "LDAP_base");
+ ldap_info.people_tree = conf_get_str(LDAP_SECTION, "LDAP_people_base");
+ ldap_info.group_tree = conf_get_str(LDAP_SECTION, "LDAP_group_base");
+ ldap_info.user_dn = conf_get_str(LDAP_SECTION, "LDAP_user_dn");
+ ldap_info.passwd = conf_get_str(LDAP_SECTION, "LDAP_passwd");
+ tssl = conf_get_str_with_def(LDAP_SECTION, "LDAP_use_ssl", "false");
+ if ((strcasecmp(tssl, "true") == 0) ||
+ (strcasecmp(tssl, "on") == 0) ||
+ (strcasecmp(tssl, "yes") == 0))
+ ldap_info.use_ssl = 1;
+ else
+ ldap_info.use_ssl = 0;
+ ldap_info.ca_cert = conf_get_str(LDAP_SECTION, "LDAP_CA_CERT");
+ cert_req = conf_get_str(LDAP_SECTION, "LDAP_tls_reqcert");
+ if (cert_req != NULL) {
+ if (strcasecmp(cert_req, "hard") == 0)
+ ldap_info.tls_reqcert = LDAP_OPT_X_TLS_HARD;
+ else if (strcasecmp(cert_req, "demand") == 0)
+ ldap_info.tls_reqcert = LDAP_OPT_X_TLS_DEMAND;
+ else if (strcasecmp(cert_req, "try") == 0)
+ ldap_info.tls_reqcert = LDAP_OPT_X_TLS_TRY;
+ else if (strcasecmp(cert_req, "allow") == 0)
+ ldap_info.tls_reqcert = LDAP_OPT_X_TLS_ALLOW;
+ else if (strcasecmp(cert_req, "never") == 0)
+ ldap_info.tls_reqcert = LDAP_OPT_X_TLS_NEVER;
+ else {
+ IDMAP_LOG(0, ("umichldap_init: Invalid value(%s) for "
+ "LDAP_tls_reqcert."));
+ goto fail;
+ }
+ }
+ /* vary the default port depending on whether they use SSL or not */
+ ldap_info.port = conf_get_num(LDAP_SECTION, "LDAP_port",
+ (ldap_info.use_ssl) ?
+ LDAPS_PORT : LDAP_PORT);
+
+ ldap_info.sasl_mech = conf_get_str(LDAP_SECTION, "LDAP_sasl_mech");
+ ldap_info.sasl_realm = conf_get_str(LDAP_SECTION, "LDAP_sasl_realm");
+ ldap_info.sasl_authcid = conf_get_str(LDAP_SECTION,
+ "LDAP_sasl_authcid");
+ ldap_info.sasl_authzid = conf_get_str(LDAP_SECTION,
+ "LDAP_sasl_authzid");
+ ldap_info.sasl_secprops = conf_get_str(LDAP_SECTION,
+ "LDAP_sasl_secprops");
+
+ /* If it is not set let the ldap lib work with the lib default */
+ canonicalize = conf_get_str_with_def(LDAP_SECTION,
+ "LDAP_sasl_canonicalize", "undef");
+ if ((strcasecmp(canonicalize, "true") == 0) ||
+ (strcasecmp(canonicalize, "on") == 0) ||
+ (strcasecmp(canonicalize, "yes") == 0)) {
+ ldap_info.sasl_canonicalize = 1;
+ } else if ((strcasecmp(canonicalize, "false") == 0) ||
+ (strcasecmp(canonicalize, "off") == 0) ||
+ (strcasecmp(canonicalize, "no") == 0)) {
+ ldap_info.sasl_canonicalize = 0;
+ }
+ ldap_info.sasl_krb5_ccname = conf_get_str(LDAP_SECTION,
+ "LDAP_sasl_krb5_ccname");
+
+ follow_referrals = conf_get_str_with_def(LDAP_SECTION,
+ "LDAP_follow_referrals",
+ "true");
+ if ((strcasecmp(follow_referrals, "true") == 0) ||
+ (strcasecmp(follow_referrals, "on") == 0) ||
+ (strcasecmp(follow_referrals, "yes") == 0))
+ ldap_info.follow_referrals = 1;
+ else
+ ldap_info.follow_referrals = 0;
+
+ /* Verify required information is supplied */
+ if (server_in == NULL || strlen(server_in) == 0)
+ strncat(missing_msg, "LDAP_server ", sizeof(missing_msg)-1);
+ if (ldap_info.base == NULL || strlen(ldap_info.base) == 0)
+ strncat(missing_msg, "LDAP_base ", sizeof(missing_msg)-1);
+ if (strlen(missing_msg) != 0) {
+ IDMAP_LOG(0, ("umichldap_init: Missing required information: "
+ "%s", missing_msg));
+ goto fail;
+ }
+
+ ldap_info.server = server_in;
+ canonicalize = conf_get_str_with_def(LDAP_SECTION,
+ "LDAP_canonicalize_name", "yes");
+ if ((strcasecmp(canonicalize, "true") == 0) ||
+ (strcasecmp(canonicalize, "on") == 0) ||
+ (strcasecmp(canonicalize, "yes") == 0)) {
+ canon_name = get_canonical_hostname(server_in);
+ if (canon_name == NULL)
+ IDMAP_LOG(0, ("umichldap_init: Warning! Unable to "
+ "canonicalize server name '%s' as requested.",
+ server_in));
+ else
+ ldap_info.server = canon_name;
+ }
+
+ /* get the ldap mapping attributes/objectclasses (all have defaults) */
+ ldap_map.NFSv4_person_objcls =
+ conf_get_str_with_def(LDAP_SECTION, "NFSv4_person_objectclass",
+ DEFAULT_UMICH_OBJCLASS_REMOTE_PERSON);
+
+ ldap_map.NFSv4_group_objcls =
+ conf_get_str_with_def(LDAP_SECTION, "NFSv4_group_objectclass",
+ DEFAULT_UMICH_OBJCLASS_REMOTE_GROUP);
+
+ ldap_map.NFSv4_nfsname_attr =
+ conf_get_str_with_def(LDAP_SECTION, "NFSv4_name_attr",
+ DEFAULT_UMICH_ATTR_NFSNAME);
+
+ ldap_map.NFSv4_uid_attr =
+ conf_get_str_with_def(LDAP_SECTION, "NFSv4_uid_attr",
+ DEFAULT_UMICH_ATTR_UIDNUMBER);
+
+ ldap_map.NFSv4_acctname_attr =
+ conf_get_str_with_def(LDAP_SECTION, "NFSv4_acctname_attr",
+ DEFAULT_UMICH_ATTR_ACCTNAME);
+
+ ldap_map.NFSv4_group_nfsname_attr =
+ conf_get_str_with_def(LDAP_SECTION, "NFSv4_group_attr",
+ DEFAULT_UMICH_ATTR_GROUP_NFSNAME);
+
+ ldap_map.NFSv4_gid_attr =
+ conf_get_str_with_def(LDAP_SECTION, "NFSv4_gid_attr",
+ DEFAULT_UMICH_ATTR_GIDNUMBER);
+
+ ldap_map.NFSv4_member_attr =
+ conf_get_str_with_def(LDAP_SECTION, "NFSv4_member_attr",
+ DEFAULT_UMICH_ATTR_MEMBERUID);
+
+ ldap_map.GSS_principal_attr =
+ conf_get_str_with_def(LDAP_SECTION, "GSS_principal_attr",
+ DEFAULT_UMICH_ATTR_GSSAUTHNAME);
+
+ ldap_map.NFSv4_grouplist_filter =
+ conf_get_str_with_def(LDAP_SECTION, "NFSv4_grouplist_filter",
+ NULL);
+
+ ldap_map.NFSv4_member_of_attr =
+ conf_get_str_with_def(LDAP_SECTION, "NFSv4_member_of_attr",
+ DEFAULT_UMICH_ATTR_MEMBEROF);
+
+ ldap_info.ldap_timeout =
+ conf_get_num(LDAP_SECTION, "LDAP_timeout_seconds",
+ DEFAULT_UMICH_SEARCH_TIMEOUT);
+
+
+ /*
+ * Some LDAP servers do a better job with indexing where searching
+ * through all the groups searching for the user in the memberuid
+ * list. Others like SunOne directory that search can takes minutes
+ * if there are thousands of groups. So setting
+ * LDAP_use_memberof_for_groups to true in the configuration file
+ * will use the memberof lists of the account and search through
+ * only those groups to obtain gids.
+ */
+ memberof = conf_get_str_with_def(LDAP_SECTION,
+ "LDAP_use_memberof_for_groups", "false");
+ if ((strcasecmp(memberof, "true") == 0) ||
+ (strcasecmp(memberof, "on") == 0) ||
+ (strcasecmp(memberof, "yes") == 0))
+ ldap_info.memberof_for_groups = 1;
+ else
+ ldap_info.memberof_for_groups = 0;
+
+ /*
+ * If they specified a search base for the
+ * people tree or group tree we use that.
+ * Otherwise we use the default search base.
+ * Note: We no longer append the default base to the tree --
+ * that should already be specified.
+ * this functions much like the NSS_LDAP modules
+ */
+ if (ldap_info.people_tree == NULL || strlen(ldap_info.people_tree) == 0)
+ ldap_info.people_tree = ldap_info.base;
+ if (ldap_info.group_tree == NULL || strlen(ldap_info.group_tree) == 0)
+ ldap_info.group_tree = ldap_info.base;
+
+ if (ldap_info.use_ssl &&
+ ldap_info.tls_reqcert != LDAP_OPT_X_TLS_NEVER &&
+ ldap_info.ca_cert == NULL) {
+ IDMAP_LOG(0, ("umichldap_init: You must specify LDAP_ca_cert "
+ "with LDAP_use_ssl=yes and "
+ "LDAP_tls_reqcert not set to \"never\""));
+ goto fail;
+ }
+
+
+ /* print out some good debugging info */
+ IDMAP_LOG(1, ("umichldap_init: canonicalize_name: %s",
+ canonicalize));
+ IDMAP_LOG(1, ("umichldap_init: server : %s (from config value '%s')",
+ ldap_info.server, server_in));
+ IDMAP_LOG(1, ("umichldap_init: port : %d", ldap_info.port));
+ IDMAP_LOG(1, ("umichldap_init: people : %s", ldap_info.people_tree));
+ IDMAP_LOG(1, ("umichldap_init: groups : %s", ldap_info.group_tree));
+
+ IDMAP_LOG(1, ("umichldap_init: user_dn : %s",
+ (ldap_info.user_dn && strlen(ldap_info.user_dn) != 0)
+ ? ldap_info.user_dn : "<not-supplied>"));
+ /* Don't print actual password into the log. */
+ IDMAP_LOG(1, ("umichldap_init: passwd : %s",
+ (ldap_info.passwd && strlen(ldap_info.passwd) != 0) ?
+ "<supplied>" : "<not-supplied>"));
+ IDMAP_LOG(1, ("umichldap_init: use_ssl : %s",
+ ldap_info.use_ssl ? "yes" : "no"));
+ IDMAP_LOG(1, ("umichldap_init: ca_cert : %s",
+ ldap_info.ca_cert ? ldap_info.ca_cert : "<not-supplied>"));
+ IDMAP_LOG(1, ("umichldap_init: tls_reqcert : %s(%d)",
+ cert_req ? cert_req : "<not-supplied>",
+ ldap_info.tls_reqcert));
+ IDMAP_LOG(1, ("umichldap_init: use_memberof_for_groups : %s",
+ ldap_info.memberof_for_groups ? "yes" : "no"));
+ IDMAP_LOG(1, ("umichldap_init: sasl_mech: %s",
+ (ldap_info.sasl_mech && strlen(ldap_info.sasl_mech) != 0) ?
+ ldap_info.sasl_mech : "<not-supplied>"));
+ IDMAP_LOG(1, ("umichldap_init: sasl_realm: %s",
+ (ldap_info.sasl_realm && strlen(ldap_info.sasl_realm) != 0) ?
+ ldap_info.sasl_realm : "<not-supplied>"));
+ IDMAP_LOG(1, ("umichldap_init: sasl_authcid: %s",
+ (ldap_info.sasl_authcid &&
+ strlen(ldap_info.sasl_authcid) != 0) ?
+ ldap_info.sasl_authcid : "<not-supplied>"));
+ IDMAP_LOG(1, ("umichldap_init: sasl_authzid: %s",
+ (ldap_info.sasl_authzid &&
+ strlen(ldap_info.sasl_authzid) != 0) ?
+ ldap_info.sasl_authzid : "<not-supplied>"));
+ IDMAP_LOG(1, ("umichldap_init: sasl_secprops: %s",
+ (ldap_info.sasl_secprops &&
+ strlen(ldap_info.sasl_secprops) != 0) ?
+ ldap_info.sasl_secprops : "<not-supplied>"));
+ IDMAP_LOG(1, ("umichldap_init: sasl_canonicalize: %d",
+ ldap_info.sasl_canonicalize));
+ IDMAP_LOG(1, ("umichldap_init: sasl_krb5_ccname: %s",
+ ldap_info.sasl_krb5_ccname));
+ IDMAP_LOG(1, ("umichldap_init: follow_referrals: %s",
+ ldap_info.follow_referrals ? "yes" : "no"));
+
+ IDMAP_LOG(1, ("umichldap_init: NFSv4_person_objectclass : %s",
+ ldap_map.NFSv4_person_objcls));
+ IDMAP_LOG(1, ("umichldap_init: NFSv4_nfsname_attr : %s",
+ ldap_map.NFSv4_nfsname_attr));
+ IDMAP_LOG(1, ("umichldap_init: NFSv4_acctname_attr : %s",
+ ldap_map.NFSv4_acctname_attr));
+ IDMAP_LOG(1, ("umichldap_init: NFSv4_uid_attr : %s",
+ ldap_map.NFSv4_uid_attr));
+ IDMAP_LOG(1, ("umichldap_init: NFSv4_group_objectclass : %s",
+ ldap_map.NFSv4_group_objcls));
+ IDMAP_LOG(1, ("umichldap_init: NFSv4_gid_attr : %s",
+ ldap_map.NFSv4_gid_attr));
+ IDMAP_LOG(1, ("umichldap_init: NFSv4_group_nfsname_attr : %s",
+ ldap_map.NFSv4_group_nfsname_attr));
+ IDMAP_LOG(1, ("umichldap_init: NFSv4_member_attr : %s",
+ ldap_map.NFSv4_member_attr));
+ IDMAP_LOG(1, ("umichldap_init: NFSv4_member_of_attr : %s",
+ ldap_map.NFSv4_member_of_attr));
+ IDMAP_LOG(1, ("umichldap_init: NFSv4_grouplist_filter : %s",
+ ldap_map.NFSv4_grouplist_filter ?
+ ldap_map.NFSv4_grouplist_filter : "<not-specified>"));
+ IDMAP_LOG(1, ("umichldap_init: GSS_principal_attr : %s",
+ ldap_map.GSS_principal_attr));
+ return 0;
+fail:
+ return -1;
+}
+
+
+/* The external interface */
+
+struct trans_func umichldap_trans = {
+ .name = "umich_ldap",
+ .init = umichldap_init,
+ .princ_to_ids = umichldap_gss_princ_to_ids,
+ .name_to_uid = umichldap_name_to_uid,
+ .name_to_gid = umichldap_name_to_gid,
+ .uid_to_name = umichldap_uid_to_name,
+ .gid_to_name = umichldap_gid_to_name,
+ .gss_princ_to_grouplist = umichldap_gss_princ_to_grouplist,
+};
+
+struct trans_func *libnfsidmap_plugin_init(void)
+{
+ return (&umichldap_trans);
+}