diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 06:03:02 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 06:03:02 +0000 |
commit | 4897093455a2bf08f3db3a1132cc2f6f5484d77c (patch) | |
tree | 9e6373544263f003139431fb4b08f9766e1ed530 /support/nfsidmap | |
parent | Initial commit. (diff) | |
download | nfs-utils-upstream.tar.xz nfs-utils-upstream.zip |
Adding upstream version 1:2.6.4.upstream/1%2.6.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | support/nfsidmap/AUTHORS | 1 | ||||
-rw-r--r-- | support/nfsidmap/COPYING | 30 | ||||
-rw-r--r-- | support/nfsidmap/Makefile.am | 75 | ||||
-rw-r--r-- | support/nfsidmap/Makefile.in | 1060 | ||||
-rw-r--r-- | support/nfsidmap/README | 126 | ||||
-rw-r--r-- | support/nfsidmap/gums.c | 787 | ||||
-rw-r--r-- | support/nfsidmap/idmapd.conf | 169 | ||||
-rw-r--r-- | support/nfsidmap/idmapd.conf.5 | 411 | ||||
-rw-r--r-- | support/nfsidmap/libnfsidmap.c | 712 | ||||
-rw-r--r-- | support/nfsidmap/libnfsidmap.pc.in | 11 | ||||
-rw-r--r-- | support/nfsidmap/libtest.c | 160 | ||||
-rw-r--r-- | support/nfsidmap/nfs4_uid_to_name.3 | 174 | ||||
-rw-r--r-- | support/nfsidmap/nfsidmap.h | 68 | ||||
-rw-r--r-- | support/nfsidmap/nfsidmap_common.c | 118 | ||||
-rw-r--r-- | support/nfsidmap/nfsidmap_plugin.h | 70 | ||||
-rw-r--r-- | support/nfsidmap/nfsidmap_private.h | 54 | ||||
-rw-r--r-- | support/nfsidmap/nss.c | 494 | ||||
-rw-r--r-- | support/nfsidmap/regex.c | 549 | ||||
-rw-r--r-- | support/nfsidmap/static.c | 426 | ||||
-rw-r--r-- | support/nfsidmap/umich_ldap.c | 1615 |
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, ¶ms); + 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 = ∅ + } + 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 = ∅ + } + } + } + + 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 = ∅ + } + group_name_prefix_length = strlen(group_name_prefix); + + user_prefix = CONFIG_GET_STRING("Regex", "Prepend-Before-User"); + if (!user_prefix) + { + user_prefix = ∅ + } + + user_suffix = CONFIG_GET_STRING("Regex", "Append-After-User"); + if (!user_suffix) + { + user_suffix = ∅ + } + + group_prefix = CONFIG_GET_STRING("Regex", "Prepend-Before-Group"); + if (!group_prefix) + { + group_prefix = ∅ + } + + group_suffix = CONFIG_GET_STRING("Regex", "Append-After-Group"); + if (!group_suffix) + { + group_suffix = ∅ + } + + 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 (®ex_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, ¤t_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, + ¤t_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); +} |