diff options
Diffstat (limited to '')
42 files changed, 15963 insertions, 0 deletions
diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am new file mode 100644 index 0000000..2e1577c --- /dev/null +++ b/support/nfs/Makefile.am @@ -0,0 +1,17 @@ +## Process this file with automake to produce Makefile.in + +noinst_LIBRARIES = +noinst_LTLIBRARIES = libnfs.la libnfsconf.la + +libnfs_la_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \ + xcommon.c wildmat.c mydaemon.c \ + rpc_socket.c getport.c \ + svc_socket.c cacheio.c closeall.c nfs_mntent.c \ + svc_create.c atomicio.c strlcat.c strlcpy.c +libnfs_la_LIBADD = libnfsconf.la +libnfs_la_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) -I$(top_srcdir)/support/reexport + +libnfsconf_la_SOURCES = conffile.c xlog.c + +MAINTAINERCLEANFILES = Makefile.in + diff --git a/support/nfs/Makefile.in b/support/nfs/Makefile.in new file mode 100644 index 0000000..c4e2f8f --- /dev/null +++ b/support/nfs/Makefile.in @@ -0,0 +1,916 @@ +# 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/nfs +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/aclocal/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/aclocal/bsdsignals.m4 \ + $(top_srcdir)/aclocal/getrandom.m4 \ + $(top_srcdir)/aclocal/ipv6.m4 \ + $(top_srcdir)/aclocal/kerberos5.m4 \ + $(top_srcdir)/aclocal/keyutils.m4 \ + $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ + $(top_srcdir)/aclocal/libevent.m4 \ + $(top_srcdir)/aclocal/libpthread.m4 \ + $(top_srcdir)/aclocal/libsqlite3.m4 \ + $(top_srcdir)/aclocal/libtirpc.m4 \ + $(top_srcdir)/aclocal/libxml2.m4 \ + $(top_srcdir)/aclocal/nfs-utils.m4 \ + $(top_srcdir)/aclocal/rpcsec_vers.m4 \ + $(top_srcdir)/aclocal/tcp-wrappers.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/support/include/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +LTLIBRARIES = $(noinst_LTLIBRARIES) +libnfs_la_DEPENDENCIES = libnfsconf.la +am_libnfs_la_OBJECTS = libnfs_la-exports.lo libnfs_la-rmtab.lo \ + libnfs_la-xio.lo libnfs_la-rpcmisc.lo libnfs_la-rpcdispatch.lo \ + libnfs_la-xcommon.lo libnfs_la-wildmat.lo \ + libnfs_la-mydaemon.lo libnfs_la-rpc_socket.lo \ + libnfs_la-getport.lo libnfs_la-svc_socket.lo \ + libnfs_la-cacheio.lo libnfs_la-closeall.lo \ + libnfs_la-nfs_mntent.lo libnfs_la-svc_create.lo \ + libnfs_la-atomicio.lo libnfs_la-strlcat.lo \ + libnfs_la-strlcpy.lo +libnfs_la_OBJECTS = $(am_libnfs_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 = +libnfsconf_la_LIBADD = +am_libnfsconf_la_OBJECTS = conffile.lo xlog.lo +libnfsconf_la_OBJECTS = $(am_libnfsconf_la_OBJECTS) +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)/conffile.Plo \ + ./$(DEPDIR)/libnfs_la-atomicio.Plo \ + ./$(DEPDIR)/libnfs_la-cacheio.Plo \ + ./$(DEPDIR)/libnfs_la-closeall.Plo \ + ./$(DEPDIR)/libnfs_la-exports.Plo \ + ./$(DEPDIR)/libnfs_la-getport.Plo \ + ./$(DEPDIR)/libnfs_la-mydaemon.Plo \ + ./$(DEPDIR)/libnfs_la-nfs_mntent.Plo \ + ./$(DEPDIR)/libnfs_la-rmtab.Plo \ + ./$(DEPDIR)/libnfs_la-rpc_socket.Plo \ + ./$(DEPDIR)/libnfs_la-rpcdispatch.Plo \ + ./$(DEPDIR)/libnfs_la-rpcmisc.Plo \ + ./$(DEPDIR)/libnfs_la-strlcat.Plo \ + ./$(DEPDIR)/libnfs_la-strlcpy.Plo \ + ./$(DEPDIR)/libnfs_la-svc_create.Plo \ + ./$(DEPDIR)/libnfs_la-svc_socket.Plo \ + ./$(DEPDIR)/libnfs_la-wildmat.Plo \ + ./$(DEPDIR)/libnfs_la-xcommon.Plo \ + ./$(DEPDIR)/libnfs_la-xio.Plo ./$(DEPDIR)/xlog.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 = $(libnfs_la_SOURCES) $(libnfsconf_la_SOURCES) +DIST_SOURCES = $(libnfs_la_SOURCES) $(libnfsconf_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_CFLAGS = @AM_CFLAGS@ +AM_CPPFLAGS = @AM_CPPFLAGS@ +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@ +noinst_LIBRARIES = +noinst_LTLIBRARIES = libnfs.la libnfsconf.la +libnfs_la_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \ + xcommon.c wildmat.c mydaemon.c \ + rpc_socket.c getport.c \ + svc_socket.c cacheio.c closeall.c nfs_mntent.c \ + svc_create.c atomicio.c strlcat.c strlcpy.c + +libnfs_la_LIBADD = libnfsconf.la +libnfs_la_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) -I$(top_srcdir)/support/reexport +libnfsconf_la_SOURCES = conffile.c xlog.c +MAINTAINERCLEANFILES = Makefile.in +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu support/nfs/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu support/nfs/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): + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_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}; \ + } + +libnfs.la: $(libnfs_la_OBJECTS) $(libnfs_la_DEPENDENCIES) $(EXTRA_libnfs_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libnfs_la_OBJECTS) $(libnfs_la_LIBADD) $(LIBS) + +libnfsconf.la: $(libnfsconf_la_OBJECTS) $(libnfsconf_la_DEPENDENCIES) $(EXTRA_libnfsconf_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libnfsconf_la_OBJECTS) $(libnfsconf_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conffile.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-atomicio.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-cacheio.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-closeall.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-exports.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-getport.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-mydaemon.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-nfs_mntent.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-rmtab.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-rpc_socket.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-rpcdispatch.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-rpcmisc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-strlcat.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-strlcpy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-svc_create.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-svc_socket.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-wildmat.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-xcommon.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-xio.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xlog.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 $@ $< + +libnfs_la-exports.lo: exports.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-exports.lo -MD -MP -MF $(DEPDIR)/libnfs_la-exports.Tpo -c -o libnfs_la-exports.lo `test -f 'exports.c' || echo '$(srcdir)/'`exports.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-exports.Tpo $(DEPDIR)/libnfs_la-exports.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exports.c' object='libnfs_la-exports.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-exports.lo `test -f 'exports.c' || echo '$(srcdir)/'`exports.c + +libnfs_la-rmtab.lo: rmtab.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-rmtab.lo -MD -MP -MF $(DEPDIR)/libnfs_la-rmtab.Tpo -c -o libnfs_la-rmtab.lo `test -f 'rmtab.c' || echo '$(srcdir)/'`rmtab.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-rmtab.Tpo $(DEPDIR)/libnfs_la-rmtab.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rmtab.c' object='libnfs_la-rmtab.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-rmtab.lo `test -f 'rmtab.c' || echo '$(srcdir)/'`rmtab.c + +libnfs_la-xio.lo: xio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-xio.lo -MD -MP -MF $(DEPDIR)/libnfs_la-xio.Tpo -c -o libnfs_la-xio.lo `test -f 'xio.c' || echo '$(srcdir)/'`xio.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-xio.Tpo $(DEPDIR)/libnfs_la-xio.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xio.c' object='libnfs_la-xio.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-xio.lo `test -f 'xio.c' || echo '$(srcdir)/'`xio.c + +libnfs_la-rpcmisc.lo: rpcmisc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-rpcmisc.lo -MD -MP -MF $(DEPDIR)/libnfs_la-rpcmisc.Tpo -c -o libnfs_la-rpcmisc.lo `test -f 'rpcmisc.c' || echo '$(srcdir)/'`rpcmisc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-rpcmisc.Tpo $(DEPDIR)/libnfs_la-rpcmisc.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rpcmisc.c' object='libnfs_la-rpcmisc.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-rpcmisc.lo `test -f 'rpcmisc.c' || echo '$(srcdir)/'`rpcmisc.c + +libnfs_la-rpcdispatch.lo: rpcdispatch.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-rpcdispatch.lo -MD -MP -MF $(DEPDIR)/libnfs_la-rpcdispatch.Tpo -c -o libnfs_la-rpcdispatch.lo `test -f 'rpcdispatch.c' || echo '$(srcdir)/'`rpcdispatch.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-rpcdispatch.Tpo $(DEPDIR)/libnfs_la-rpcdispatch.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rpcdispatch.c' object='libnfs_la-rpcdispatch.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-rpcdispatch.lo `test -f 'rpcdispatch.c' || echo '$(srcdir)/'`rpcdispatch.c + +libnfs_la-xcommon.lo: xcommon.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-xcommon.lo -MD -MP -MF $(DEPDIR)/libnfs_la-xcommon.Tpo -c -o libnfs_la-xcommon.lo `test -f 'xcommon.c' || echo '$(srcdir)/'`xcommon.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-xcommon.Tpo $(DEPDIR)/libnfs_la-xcommon.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xcommon.c' object='libnfs_la-xcommon.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-xcommon.lo `test -f 'xcommon.c' || echo '$(srcdir)/'`xcommon.c + +libnfs_la-wildmat.lo: wildmat.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-wildmat.lo -MD -MP -MF $(DEPDIR)/libnfs_la-wildmat.Tpo -c -o libnfs_la-wildmat.lo `test -f 'wildmat.c' || echo '$(srcdir)/'`wildmat.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-wildmat.Tpo $(DEPDIR)/libnfs_la-wildmat.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wildmat.c' object='libnfs_la-wildmat.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-wildmat.lo `test -f 'wildmat.c' || echo '$(srcdir)/'`wildmat.c + +libnfs_la-mydaemon.lo: mydaemon.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-mydaemon.lo -MD -MP -MF $(DEPDIR)/libnfs_la-mydaemon.Tpo -c -o libnfs_la-mydaemon.lo `test -f 'mydaemon.c' || echo '$(srcdir)/'`mydaemon.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-mydaemon.Tpo $(DEPDIR)/libnfs_la-mydaemon.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mydaemon.c' object='libnfs_la-mydaemon.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-mydaemon.lo `test -f 'mydaemon.c' || echo '$(srcdir)/'`mydaemon.c + +libnfs_la-rpc_socket.lo: rpc_socket.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-rpc_socket.lo -MD -MP -MF $(DEPDIR)/libnfs_la-rpc_socket.Tpo -c -o libnfs_la-rpc_socket.lo `test -f 'rpc_socket.c' || echo '$(srcdir)/'`rpc_socket.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-rpc_socket.Tpo $(DEPDIR)/libnfs_la-rpc_socket.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rpc_socket.c' object='libnfs_la-rpc_socket.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-rpc_socket.lo `test -f 'rpc_socket.c' || echo '$(srcdir)/'`rpc_socket.c + +libnfs_la-getport.lo: getport.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-getport.lo -MD -MP -MF $(DEPDIR)/libnfs_la-getport.Tpo -c -o libnfs_la-getport.lo `test -f 'getport.c' || echo '$(srcdir)/'`getport.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-getport.Tpo $(DEPDIR)/libnfs_la-getport.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getport.c' object='libnfs_la-getport.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-getport.lo `test -f 'getport.c' || echo '$(srcdir)/'`getport.c + +libnfs_la-svc_socket.lo: svc_socket.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-svc_socket.lo -MD -MP -MF $(DEPDIR)/libnfs_la-svc_socket.Tpo -c -o libnfs_la-svc_socket.lo `test -f 'svc_socket.c' || echo '$(srcdir)/'`svc_socket.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-svc_socket.Tpo $(DEPDIR)/libnfs_la-svc_socket.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svc_socket.c' object='libnfs_la-svc_socket.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-svc_socket.lo `test -f 'svc_socket.c' || echo '$(srcdir)/'`svc_socket.c + +libnfs_la-cacheio.lo: cacheio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-cacheio.lo -MD -MP -MF $(DEPDIR)/libnfs_la-cacheio.Tpo -c -o libnfs_la-cacheio.lo `test -f 'cacheio.c' || echo '$(srcdir)/'`cacheio.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-cacheio.Tpo $(DEPDIR)/libnfs_la-cacheio.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cacheio.c' object='libnfs_la-cacheio.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-cacheio.lo `test -f 'cacheio.c' || echo '$(srcdir)/'`cacheio.c + +libnfs_la-closeall.lo: closeall.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-closeall.lo -MD -MP -MF $(DEPDIR)/libnfs_la-closeall.Tpo -c -o libnfs_la-closeall.lo `test -f 'closeall.c' || echo '$(srcdir)/'`closeall.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-closeall.Tpo $(DEPDIR)/libnfs_la-closeall.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='closeall.c' object='libnfs_la-closeall.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-closeall.lo `test -f 'closeall.c' || echo '$(srcdir)/'`closeall.c + +libnfs_la-nfs_mntent.lo: nfs_mntent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-nfs_mntent.lo -MD -MP -MF $(DEPDIR)/libnfs_la-nfs_mntent.Tpo -c -o libnfs_la-nfs_mntent.lo `test -f 'nfs_mntent.c' || echo '$(srcdir)/'`nfs_mntent.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-nfs_mntent.Tpo $(DEPDIR)/libnfs_la-nfs_mntent.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nfs_mntent.c' object='libnfs_la-nfs_mntent.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-nfs_mntent.lo `test -f 'nfs_mntent.c' || echo '$(srcdir)/'`nfs_mntent.c + +libnfs_la-svc_create.lo: svc_create.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-svc_create.lo -MD -MP -MF $(DEPDIR)/libnfs_la-svc_create.Tpo -c -o libnfs_la-svc_create.lo `test -f 'svc_create.c' || echo '$(srcdir)/'`svc_create.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-svc_create.Tpo $(DEPDIR)/libnfs_la-svc_create.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svc_create.c' object='libnfs_la-svc_create.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-svc_create.lo `test -f 'svc_create.c' || echo '$(srcdir)/'`svc_create.c + +libnfs_la-atomicio.lo: atomicio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-atomicio.lo -MD -MP -MF $(DEPDIR)/libnfs_la-atomicio.Tpo -c -o libnfs_la-atomicio.lo `test -f 'atomicio.c' || echo '$(srcdir)/'`atomicio.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-atomicio.Tpo $(DEPDIR)/libnfs_la-atomicio.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='atomicio.c' object='libnfs_la-atomicio.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-atomicio.lo `test -f 'atomicio.c' || echo '$(srcdir)/'`atomicio.c + +libnfs_la-strlcat.lo: strlcat.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-strlcat.lo -MD -MP -MF $(DEPDIR)/libnfs_la-strlcat.Tpo -c -o libnfs_la-strlcat.lo `test -f 'strlcat.c' || echo '$(srcdir)/'`strlcat.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-strlcat.Tpo $(DEPDIR)/libnfs_la-strlcat.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strlcat.c' object='libnfs_la-strlcat.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-strlcat.lo `test -f 'strlcat.c' || echo '$(srcdir)/'`strlcat.c + +libnfs_la-strlcpy.lo: strlcpy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-strlcpy.lo -MD -MP -MF $(DEPDIR)/libnfs_la-strlcpy.Tpo -c -o libnfs_la-strlcpy.lo `test -f 'strlcpy.c' || echo '$(srcdir)/'`strlcpy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-strlcpy.Tpo $(DEPDIR)/libnfs_la-strlcpy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strlcpy.c' object='libnfs_la-strlcpy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-strlcpy.lo `test -f 'strlcpy.c' || echo '$(srcdir)/'`strlcpy.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ + clean-noinstLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/conffile.Plo + -rm -f ./$(DEPDIR)/libnfs_la-atomicio.Plo + -rm -f ./$(DEPDIR)/libnfs_la-cacheio.Plo + -rm -f ./$(DEPDIR)/libnfs_la-closeall.Plo + -rm -f ./$(DEPDIR)/libnfs_la-exports.Plo + -rm -f ./$(DEPDIR)/libnfs_la-getport.Plo + -rm -f ./$(DEPDIR)/libnfs_la-mydaemon.Plo + -rm -f ./$(DEPDIR)/libnfs_la-nfs_mntent.Plo + -rm -f ./$(DEPDIR)/libnfs_la-rmtab.Plo + -rm -f ./$(DEPDIR)/libnfs_la-rpc_socket.Plo + -rm -f ./$(DEPDIR)/libnfs_la-rpcdispatch.Plo + -rm -f ./$(DEPDIR)/libnfs_la-rpcmisc.Plo + -rm -f ./$(DEPDIR)/libnfs_la-strlcat.Plo + -rm -f ./$(DEPDIR)/libnfs_la-strlcpy.Plo + -rm -f ./$(DEPDIR)/libnfs_la-svc_create.Plo + -rm -f ./$(DEPDIR)/libnfs_la-svc_socket.Plo + -rm -f ./$(DEPDIR)/libnfs_la-wildmat.Plo + -rm -f ./$(DEPDIR)/libnfs_la-xcommon.Plo + -rm -f ./$(DEPDIR)/libnfs_la-xio.Plo + -rm -f ./$(DEPDIR)/xlog.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-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +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)/conffile.Plo + -rm -f ./$(DEPDIR)/libnfs_la-atomicio.Plo + -rm -f ./$(DEPDIR)/libnfs_la-cacheio.Plo + -rm -f ./$(DEPDIR)/libnfs_la-closeall.Plo + -rm -f ./$(DEPDIR)/libnfs_la-exports.Plo + -rm -f ./$(DEPDIR)/libnfs_la-getport.Plo + -rm -f ./$(DEPDIR)/libnfs_la-mydaemon.Plo + -rm -f ./$(DEPDIR)/libnfs_la-nfs_mntent.Plo + -rm -f ./$(DEPDIR)/libnfs_la-rmtab.Plo + -rm -f ./$(DEPDIR)/libnfs_la-rpc_socket.Plo + -rm -f ./$(DEPDIR)/libnfs_la-rpcdispatch.Plo + -rm -f ./$(DEPDIR)/libnfs_la-rpcmisc.Plo + -rm -f ./$(DEPDIR)/libnfs_la-strlcat.Plo + -rm -f ./$(DEPDIR)/libnfs_la-strlcpy.Plo + -rm -f ./$(DEPDIR)/libnfs_la-svc_create.Plo + -rm -f ./$(DEPDIR)/libnfs_la-svc_socket.Plo + -rm -f ./$(DEPDIR)/libnfs_la-wildmat.Plo + -rm -f ./$(DEPDIR)/libnfs_la-xcommon.Plo + -rm -f ./$(DEPDIR)/libnfs_la-xio.Plo + -rm -f ./$(DEPDIR)/xlog.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: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLIBRARIES \ + clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am 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 + +.PRECIOUS: Makefile + + +# 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/nfs/atomicio.c b/support/nfs/atomicio.c new file mode 100644 index 0000000..0e81838 --- /dev/null +++ b/support/nfs/atomicio.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2002 Marius Aamodt Eriksen <marius@monkey.org> + * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <unistd.h> +#include <errno.h> + +#include "nfslib.h" + +/* + * ensure all of data on socket comes through. f==read || f==write + */ +ssize_t atomicio(ssize_t(*f) (int, void *, size_t), int fd, void *_s, size_t n) +{ + char *s = _s; + ssize_t res, pos = 0; + + while ((ssize_t)n > pos) { + res = (f) (fd, s + pos, n - pos); + switch (res) { + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; + /* FALLTHRU */ + case 0: + if (pos != 0) + return pos; + return res; + default: + pos += res; + } + } + return pos; +} diff --git a/support/nfs/cacheio.c b/support/nfs/cacheio.c new file mode 100644 index 0000000..bd4da0e --- /dev/null +++ b/support/nfs/cacheio.c @@ -0,0 +1,253 @@ +/* + * support/nfs/cacheio.c + * support IO on the cache channel files in 2.5 and beyond. + * These use 'qwords' which are like words, but with a little quoting. + * + */ + + +/* + * Support routines for text-based upcalls. + * Fields are separated by spaces. + * Fields are either mangled to quote space tab newline slosh with slosh + * or a hexified with a leading \x + * Record is terminated with newline. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <nfslib.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <time.h> +#include <errno.h> + +void qword_add(char **bpp, int *lp, char *str) +{ + char *bp = *bpp; + int len = *lp; + char c; + + if (len < 0) return; + + while ((c=*str++) && len > 0) + switch(c) { + case ' ': + case '\t': + case '\n': + case '\\': + if (len >= 4) { + *bp++ = '\\'; + *bp++ = '0' + ((c & 0300)>>6); + *bp++ = '0' + ((c & 0070)>>3); + *bp++ = '0' + ((c & 0007)>>0); + } + len -= 4; + break; + default: + *bp++ = c; + len--; + } + if (c || len <1) len = -1; + else { + *bp++ = ' '; + len--; + } + *bpp = bp; + *lp = len; +} + +void qword_addhex(char **bpp, int *lp, char *buf, int blen) +{ + char *bp = *bpp; + int len = *lp; + + if (len < 0) return; + + if (len > 2) { + *bp++ = '\\'; + *bp++ = 'x'; + len -= 2; + while (blen && len >= 2) { + unsigned char c = *buf++; + *bp++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1); + *bp++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1); + len -= 2; + blen--; + } + } + if (blen || len<1) len = -1; + else { + *bp++ = ' '; + len--; + } + *bpp = bp; + *lp = len; +} + +void qword_addint(char **bpp, int *lp, int n) +{ + int len; + + len = snprintf(*bpp, *lp, "%d ", n); + if (len > *lp) + len = *lp; + *bpp += len; + *lp -= len; +} + +void qword_adduint(char **bpp, int *lp, unsigned int n) +{ + int len; + + len = snprintf(*bpp, *lp, "%u ", n); + if (len > *lp) + len = *lp; + *bpp += len; + *lp -= len; +} + +void qword_addeol(char **bpp, int *lp) +{ + if (*lp <= 0) + return; + **bpp = '\n'; + (*bpp)++; + (*lp)--; +} + +#define isodigit(c) (isdigit(c) && c <= '7') +int qword_get(char **bpp, char *dest, int bufsize) +{ + /* return bytes copied, or -1 on error */ + char *bp = *bpp; + int len = 0; + + while (*bp == ' ') bp++; + + if (bp[0] == '\\' && bp[1] == 'x') { + /* HEX STRING */ + bp += 2; + while (isxdigit(bp[0]) && isxdigit(bp[1]) && len < bufsize) { + int byte = isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10; + bp++; + byte <<= 4; + byte |= isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10; + *dest++ = byte; + bp++; + len++; + } + } else { + /* text with \nnn octal quoting */ + while (*bp != ' ' && *bp != '\n' && *bp && len < bufsize-1) { + if (*bp == '\\' && + isodigit(bp[1]) && (bp[1] <= '3') && + isodigit(bp[2]) && + isodigit(bp[3])) { + int byte = (*++bp -'0'); + bp++; + byte = (byte << 3) | (*bp++ - '0'); + byte = (byte << 3) | (*bp++ - '0'); + *dest++ = byte; + len++; + } else { + *dest++ = *bp++; + len++; + } + } + } + + if (*bp != ' ' && *bp != '\n' && *bp != '\0') + return -1; + while (*bp == ' ') bp++; + *bpp = bp; + *dest = '\0'; + return len; +} + +int qword_get_int(char **bpp, int *anint) +{ + char buf[50]; + char *ep; + int rv; + int len = qword_get(bpp, buf, 50); + if (len < 0) return -1; + if (len ==0) return -1; + rv = strtol(buf, &ep, 0); + if (*ep) return -1; + *anint = rv; + return 0; +} + +int qword_get_uint(char **bpp, unsigned int *anint) +{ + char buf[50]; + char *ep; + unsigned int rv; + int len = qword_get(bpp, buf, 50); + if (len < 0) return -1; + if (len ==0) return -1; + rv = strtoul(buf, &ep, 0); + if (*ep) return -1; + *anint = rv; + return 0; +} + +/* flush the kNFSd caches. + * Set the flush time to the mtime of the etab state file or + * if force, to now. + * the caches to flush are: + * auth.unix.ip nfsd.export nfsd.fh + */ + +void +cache_flush(void) +{ + int c; + char stime[32]; + char path[200]; + time_t now; + /* Note: the order of these caches is important. + * They need to be flushed in dependancy order. So + * a cache that references items in another cache, + * as nfsd.fh entries reference items in nfsd.export, + * must be flushed before the cache that it references. + */ + static char *cachelist[] = { + "auth.unix.ip", + "auth.unix.gid", + "nfsd.fh", + "nfsd.export", + NULL + }; + now = time(0); + + /* Since v4.16-rc2-3-g3b68e6ee3cbd the timestamp written is ignored. + * It is safest always to flush caches if there is any doubt. + * For earlier kernels, writing the next second from now is + * the best we can do. + */ + sprintf(stime, "%" PRId64 "\n", (int64_t)now+1); + for (c=0; cachelist[c]; c++) { + int fd; + sprintf(path, "/proc/net/rpc/%s/flush", cachelist[c]); + fd = open(path, O_RDWR); + if (fd >= 0) { + if (write(fd, stime, strlen(stime)) != (ssize_t)strlen(stime)) { + xlog_warn("Writing to '%s' failed: errno %d (%s)", + path, errno, strerror(errno)); + } + close(fd); + } + } +} diff --git a/support/nfs/closeall.c b/support/nfs/closeall.c new file mode 100644 index 0000000..e07253e --- /dev/null +++ b/support/nfs/closeall.c @@ -0,0 +1,39 @@ +/* + * support/nfs/closeall.c + * Close all file descriptors greater than some limit, + * Use readdir "/proc/self/fd" to avoid excess close(2) calls. + */ + +#include <unistd.h> +#include <stdlib.h> +#include <dirent.h> +#include <errno.h> + +#include "nfslib.h" + +void +closeall(int min) +{ + char *endp; + long n; + DIR *dir = opendir("/proc/self/fd"); + + if (dir != NULL) { + int dfd = dirfd(dir); + struct dirent *d; + + while ((d = readdir(dir)) != NULL) { + errno = 0; + n = strtol(d->d_name, &endp, 10); + if (!errno && *endp == '\0' && endp != d->d_name && + n >= min && n != dfd) + (void) close(n); + } + closedir(dir); + } else { + int fd = sysconf(_SC_OPEN_MAX); + while (--fd >= min) + if(fd >= 0) + (void) close(fd); + } +} diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c new file mode 100644 index 0000000..fd4a17a --- /dev/null +++ b/support/nfs/conffile.c @@ -0,0 +1,2345 @@ +/* $OpenBSD: conf.c,v 1.55 2003/06/03 14:28:16 ho Exp $ */ +/* $EOM: conf.c,v 1.48 2000/12/04 02:04:29 angelos Exp $ */ + +/* + * Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist. All rights reserved. + * Copyright (c) 2000, 2001, 2002 Håkan Olsson. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/param.h> +#include <sys/mman.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <ctype.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <err.h> +#include <syslog.h> +#include <libgen.h> +#include <sys/file.h> +#include <time.h> +#include <dirent.h> + +#include "conffile.h" +#include "xlog.h" + +#define CONF_FILE_EXT ".conf" +#define CONF_FILE_EXT_LEN ((int) (sizeof(CONF_FILE_EXT) - 1)) + +#pragma GCC visibility push(hidden) + +static void conf_load_defaults(void); +static char * conf_readfile(const char *path); +static int conf_set(int , const char *, const char *, const char *, + const char *, int , int ); +static void conf_parse(int trans, char *buf, + char **section, char **subsection, const char *filename); + +struct conf_trans { + TAILQ_ENTRY (conf_trans) link; + int trans; + enum conf_op { CONF_SET, CONF_REMOVE, CONF_REMOVE_SECTION } op; + char *section; + char *arg; + char *tag; + char *value; + int override; + int is_default; +}; + +TAILQ_HEAD (conf_trans_head, conf_trans) conf_trans_queue; + +/* + * Radix-64 Encoding. + */ + +static const uint8_t asc2bin[] = +{ + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 62, 255, 255, 255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 255, 255, 255, 255, 255, 255, + 255, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 255, 255, 255, 255, 255, + 255, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255 +}; + +struct conf_binding { + LIST_ENTRY (conf_binding) link; + char *section; + char *arg; + char *tag; + char *value; + int is_default; +}; + +LIST_HEAD (conf_bindings, conf_binding) conf_bindings[256]; + +const char *modified_by = NULL; + +static __inline__ uint8_t +conf_hash(const char *s) +{ + uint8_t hash = 0; + + while (*s) { + hash = ((hash << 1) | (hash >> 7)) ^ tolower (*s); + s++; + } + return hash; +} + +/* + * free all the component parts of a conf_binding struct + */ +static void free_confbind(struct conf_binding *cb) +{ + if (!cb) + return; + if (cb->section) + free(cb->section); + if (cb->arg) + free(cb->arg); + if (cb->tag) + free(cb->tag); + if (cb->value) + free(cb->value); + free(cb); +} + +static void free_conftrans(struct conf_trans *ct) +{ + if (!ct) + return; + if (ct->section) + free(ct->section); + if (ct->arg) + free(ct->arg); + if (ct->tag) + free(ct->tag); + if (ct->value) + free(ct->value); + free(ct); +} + +/* + * Insert a tag-value combination from LINE (the equal sign is at POS) + */ +static int +conf_remove_now(const char *section, const char *tag) +{ + struct conf_binding *cb, *next; + + cb = LIST_FIRST(&conf_bindings[conf_hash (section)]); + for (; cb; cb = next) { + next = LIST_NEXT(cb, link); + if (strcasecmp(cb->section, section) == 0 + && strcasecmp(cb->tag, tag) == 0) { + LIST_REMOVE(cb, link); + xlog(LOG_INFO,"[%s]:%s->%s removed", section, tag, cb->value); + free_confbind(cb); + return 0; + } + } + return 1; +} + +static int +conf_remove_section_now(const char *section) +{ + struct conf_binding *cb, *next; + int unseen = 1; + + cb = LIST_FIRST(&conf_bindings[conf_hash (section)]); + for (; cb; cb = next) { + next = LIST_NEXT(cb, link); + if (strcasecmp(cb->section, section) == 0) { + unseen = 0; + LIST_REMOVE(cb, link); + xlog(LOG_INFO, "[%s]:%s->%s removed", section, cb->tag, cb->value); + free_confbind(cb); + } + } + return unseen; +} + +/* + * Insert a tag-value combination from LINE (the equal sign is at POS) + * into SECTION of our configuration database. + */ +static int +conf_set_now(const char *section, const char *arg, const char *tag, + const char *value, int override, int is_default) +{ + struct conf_binding *node = 0; + + if (override) + conf_remove_now(section, tag); + else if (conf_get_section(section, arg, tag)) { + if (!is_default) { + xlog(LOG_INFO, "conf_set: duplicate tag [%s]:%s, ignoring...", + section, tag); + } + return 1; + } + node = calloc(1, sizeof *node); + if (!node) { + xlog_warn("conf_set: calloc (1, %lu) failed", (unsigned long)sizeof *node); + return 1; + } + node->section = strdup(section); + if (arg) + node->arg = strdup(arg); + node->tag = strdup(tag); + node->value = strdup(value); + node->is_default = is_default; + LIST_INSERT_HEAD(&conf_bindings[conf_hash (section)], node, link); + return 0; +} + +/* Attempt to construct a relative path to the new file */ +static char * +relative_path(const char *oldfile, const char *newfile) +{ + char *tmpcopy = NULL; + char *dir = NULL; + char *relpath = NULL; + size_t pathlen; + + if (!newfile) + return strdup(oldfile); + + if (newfile[0] == '/') + return strdup(newfile); + + tmpcopy = strdup(oldfile); + if (!tmpcopy) + goto mem_err; + + dir = dirname(tmpcopy); + + pathlen = strlen(dir) + strlen(newfile) + 2; + relpath = calloc(1, pathlen); + if (!relpath) + goto mem_err; + + snprintf(relpath, pathlen, "%s/%s", dir, newfile); + + free(tmpcopy); + return relpath; +mem_err: + if (tmpcopy) + free(tmpcopy); + return NULL; +} + + +/* + * Parse the line LINE of SZ bytes. Skip Comments, recognize section + * headers and feed tag-value pairs into our configuration database. + */ +static void +conf_parse_line(int trans, char *line, const char *filename, int lineno, char **section, char **subsection) +{ + char *val, *ptr; + char *inc_section = NULL, *inc_subsection = NULL; + char *relpath, *subconf; + + /* Strip off any leading blanks */ + while (isspace(*line)) + line++; + + /* Ignore blank lines */ + if (*line == '\0') + return; + + /* Lines starting with '#' or ';' are comments. */ + if (*line == '#' || *line == ';') + return; + + /* '[section]' parsing... */ + if (*line == '[') { + line++; + + if (*section) { + free(*section); + *section = NULL; + } + if (*subsection) { + free(*subsection); + *subsection = NULL; + } + + /* Strip off any blanks after '[' */ + while (isblank(*line)) + line++; + + /* find the closing ] */ + ptr = strchr(line, ']'); + if (ptr == NULL) { + xlog_warn("config error at %s:%d: " + "non-matched ']', ignoring until next section", + filename, lineno); + return; + } + + /* just ignore everything after the closing ] */ + *(ptr--) = '\0'; + + /* Strip off any blanks before ']' */ + while (ptr >= line && isblank(*ptr)) + *(ptr--)='\0'; + + /* look for an arg to split from the section name */ + val = strchr(line, '"'); + if (val != NULL) { + ptr = val - 1; + *(val++) = '\0'; + + /* trim away any whitespace before the " */ + while (ptr > line && isblank(*ptr)) + *(ptr--)='\0'; + } + + /* copy the section name */ + *section = strdup(line); + if (!*section) { + xlog_warn("config error at %s:%d:" + "malloc failed", filename, lineno); + return; + } + + /* there is no arg, we are done */ + if (val == NULL) + return; + + /* check for the closing " */ + ptr = strchr(val, '"'); + if (ptr == NULL) { + xlog_warn("config error at %s:%d: " + "non-matched '\"', ignoring until next section", + filename, lineno); + return; + } + *ptr = '\0'; + *subsection = strdup(val); + if (!*subsection) + xlog_warn("config error at %s:%d: " + "malloc failed", filename, lineno); + return; + } + + /* Deal with assignments. */ + ptr = strchr(line, '='); + + /* not an assignment line */ + if (ptr == NULL) { + /* Other non-empty lines are weird. */ + if (line[strspn(line, " \t")]) + xlog_warn("config error at %s:%d: " + "line not empty and not an assignment", + filename, lineno); + return; + } + + /* If no section, we are ignoring the line. */ + if (!*section) { + xlog_warn("config error at %s:%d: " + "ignoring line not in a section", + filename, lineno); + return; + } + + val = ptr + 1; + *(ptr--) = '\0'; + + /* strip spaces before and after the = */ + while (ptr >= line && isblank(*ptr)) + *(ptr--)='\0'; + while (*val != '\0' && isblank(*val)) + val++; + + if (*val == '"') { + val++; + ptr = strchr(val, '"'); + if (ptr == NULL) { + xlog_warn("config error at %s:%d: " + "unmatched quotes",filename, lineno); + return; + } + *ptr = '\0'; + } else + if (*val == '\'') { + val++; + ptr = strchr(val, '\''); + if (ptr == NULL) { + xlog_warn("config error at %s:%d: " + "unmatched quotes", filename, lineno); + return; + } + *ptr = '\0'; + } else { + /* Trim any trailing spaces and comments */ + if ((ptr=strchr(val, '#'))!=NULL) + *ptr = '\0'; + if ((ptr=strchr(val, ';'))!=NULL) + *ptr = '\0'; + + ptr = val + strlen(val) - 1; + while (ptr > val && isspace(*ptr)) + *(ptr--) = '\0'; + } + + if (*line == '\0') { + xlog_warn("config error at %s:%d: " + "missing tag in assignment", filename, lineno); + return; + } + + if (strcasecmp(line, "include")==0) { + /* load and parse subordinate config files */ + _Bool optional = false; + + if (val && *val == '-') { + optional = true; + val++; + } + + relpath = relative_path(filename, val); + if (relpath == NULL) { + if (!optional) + xlog_warn("config error at %s:%d: error loading included config", + filename, lineno); + return; + } + + subconf = conf_readfile(relpath); + if (subconf == NULL) { + if (!optional) + xlog_warn("config error at %s:%d: error loading included config", + filename, lineno); + if (relpath) + free(relpath); + return; + } + + /* copy the section data so the included file can inherit it + * without accidentally changing it for us */ + if (*section != NULL) { + inc_section = strdup(*section); + if (*subsection != NULL) + inc_subsection = strdup(*subsection); + } + + conf_parse(trans, subconf, &inc_section, &inc_subsection, relpath); + + if (inc_section) + free(inc_section); + if (inc_subsection) + free(inc_subsection); + if (relpath) + free(relpath); + free(subconf); + } else { + /* XXX Perhaps should we not ignore errors? */ + conf_set(trans, *section, *subsection, line, val, 1, 0); + } +} + +/* Parse the mapped configuration file. */ +static void +conf_parse(int trans, char *buf, char **section, char **subsection, const char *filename) +{ + char *cp = buf; + char *bufend = NULL; + char *line; + int lineno = 0; + + line = cp; + bufend = buf + strlen(buf); + while (cp < bufend) { + if (*cp == '\n') { + /* Check for escaped newlines. */ + if (cp > buf && *(cp - 1) == '\\') + *(cp - 1) = *cp = ' '; + else { + *cp = '\0'; + lineno++; + conf_parse_line(trans, line, filename, lineno, section, subsection); + line = cp + 1; + } + } + cp++; + } + if (cp != line) + xlog_warn("conf_parse: last line non-terminated, ignored."); +} + +static void +conf_load_defaults(void) +{ + /* No defaults */ + return; +} + +static char * +conf_readfile(const char *path) +{ + struct stat sb; + if (!path) { + xlog(L_ERROR, "conf_readfile: no path given"); + return NULL; + } + + if ((stat (path, &sb) == 0) || (errno != ENOENT)) { + char *new_conf_addr = NULL; + off_t sz; + int fd = open (path, O_RDONLY, 0); + + if (fd == -1) { + xlog_warn("conf_readfile: open (\"%s\", O_RDONLY) failed", path); + return NULL; + } + + /* Grab a shared lock to ensure its not mid-rewrite */ + if (flock(fd, LOCK_SH)) { + xlog_warn("conf_readfile: attempt to grab read lock failed: %s", + strerror(errno)); + goto fail; + } + + /* only after we have the lock, check the file size ready to read it */ + sz = lseek(fd, 0, SEEK_END); + if (sz < 0) { + xlog_warn("conf_readfile: unable to determine file size: %s", + strerror(errno)); + goto fail; + } + lseek(fd, 0, SEEK_SET); + + new_conf_addr = malloc(sz+1); + if (!new_conf_addr) { + xlog_warn("conf_readfile: malloc (%lu) failed", (unsigned long)sz); + goto fail; + } + + /* XXX I assume short reads won't happen here. */ + if (read (fd, new_conf_addr, sz) != (int)sz) { + xlog_warn("conf_readfile: read (%d, %p, %lu) failed", + fd, new_conf_addr, (unsigned long)sz); + goto fail; + } + close(fd); + + /* XXX Should we not care about errors and rollback? */ + new_conf_addr[sz] = '\0'; + return new_conf_addr; + fail: + close(fd); + if (new_conf_addr) + free(new_conf_addr); + } + return NULL; +} + +/* remove and free up any existing config state */ +static void conf_free_bindings(void) +{ + unsigned int i; + for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) { + struct conf_binding *cb, *next; + + cb = LIST_FIRST(&conf_bindings[i]); + for (; cb; cb = next) { + next = LIST_NEXT(cb, link); + LIST_REMOVE(cb, link); + free_confbind(cb); + } + LIST_INIT(&conf_bindings[i]); + } +} + +static int +conf_load_files(int trans, const char *conf_file) +{ + char *conf_data; + char *section = NULL; + char *subsection = NULL; + + conf_data = conf_readfile(conf_file); + if (conf_data == NULL) + return 1; + + /* Load default configuration values. */ + conf_load_defaults(); + + /* Parse config contents into the transaction queue */ + conf_parse(trans, conf_data, §ion, &subsection, conf_file); + if (section) + free(section); + if (subsection) + free(subsection); + free(conf_data); + + return 0; +} +/* Open the config file and map it into our address space, then parse it. */ +static int +conf_load_file(const char *conf_file) +{ + int trans; + char * conf_data; + + trans = conf_begin(); + conf_data = conf_readfile(conf_file); + + if (conf_data == NULL) + return 1; + + /* Load default configuration values. */ + conf_load_defaults(); + + /* Parse config contents into the transaction queue */ + char *section = NULL; + char *subsection = NULL; + conf_parse(trans, conf_data, §ion, &subsection, conf_file); + if (section) free(section); + if (subsection) free(subsection); + free(conf_data); + + /* Free potential existing configuration. */ + conf_free_bindings(); + + /* Apply the new configuration values */ + conf_end(trans, 1); + return 0; +} + +static void +conf_init_dir(const char *conf_file) +{ + struct dirent **namelist = NULL; + char *dname, fname[PATH_MAX], *cname; + int n = 0, nfiles = 0, i, fname_len, dname_len; + int trans, rv, path_len; + + dname = malloc(strlen(conf_file) + 3); + if (dname == NULL) { + xlog(L_WARNING, "conf_init_dir: malloc: %s", strerror(errno)); + return; + } + sprintf(dname, "%s.d", conf_file); + + n = scandir(dname, &namelist, NULL, versionsort); + if (n < 0) { + if (errno != ENOENT) { + xlog(L_WARNING, "conf_init_dir: scandir %s: %s", + dname, strerror(errno)); + } + free(dname); + return; + } else if (n == 0) { + free(dname); + return; + } + + trans = conf_begin(); + dname_len = strlen(dname); + for (i = 0; i < n; i++ ) { + struct dirent *d = namelist[i]; + + switch (d->d_type) { + case DT_UNKNOWN: + case DT_REG: + case DT_LNK: + break; + default: + continue; + } + if (*d->d_name == '.') + continue; + + fname_len = strlen(d->d_name); + path_len = (fname_len + dname_len); + if (!fname_len || path_len > PATH_MAX) { + xlog(L_WARNING, "conf_init_dir: Too long file name: %s in %s", + d->d_name, dname); + continue; + } + + /* + * Check the naming of the file. Only process files + * that end with CONF_FILE_EXT + */ + if (fname_len <= CONF_FILE_EXT_LEN) { + xlog(D_GENERAL, "conf_init_dir: %s: name too short", + d->d_name); + continue; + } + cname = (d->d_name + (fname_len - CONF_FILE_EXT_LEN)); + if (strcmp(cname, CONF_FILE_EXT) != 0) { + xlog(D_GENERAL, "conf_init_dir: %s: invalid file extension", + d->d_name); + continue; + } + + rv = snprintf(fname, PATH_MAX, "%s/%s", dname, d->d_name); + if (rv < path_len) { + xlog(L_WARNING, "conf_init_dir: file name: %s/%s too short", + d->d_name, dname); + continue; + } + + if (conf_load_files(trans, fname)) + continue; + nfiles++; + } + + if (nfiles) { + /* Apply the configuration values */ + conf_end(trans, 1); + } + for (i = 0; i < n; i++) + free(namelist[i]); + free(namelist); + free(dname); + + return; +} + +int +conf_init_file(const char *conf_file) +{ + unsigned int i; + int ret; + + for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) + LIST_INIT (&conf_bindings[i]); + + TAILQ_INIT (&conf_trans_queue); + + if (conf_file == NULL) + conf_file=NFS_CONFFILE; + + /* + * First parse the give config file + * then parse the config.conf.d directory + * (if it exists) + * + */ + ret = conf_load_file(conf_file); + + /* + * When the same variable is set in both files + * the conf.d file will override the config file. + * This allows automated admin systems to + * have the final say. + */ + conf_init_dir(conf_file); + + return ret; +} + +/* + * Empty the config and free up any used memory + */ +void +conf_cleanup(void) +{ + conf_free_bindings(); + + struct conf_trans *node, *next; + for (node = TAILQ_FIRST(&conf_trans_queue); node; node = next) { + next = TAILQ_NEXT(node, link); + TAILQ_REMOVE (&conf_trans_queue, node, link); + free_conftrans(node); + } + TAILQ_INIT(&conf_trans_queue); +} + +/* + * Return the numeric value denoted by TAG in section SECTION or DEF + * if that tag does not exist. + */ +int +conf_get_num(const char *section, const char *tag, int def) +{ + char *value = conf_get_str(section, tag); + + if (value) + return atoi(value); + + return def; +} + +/* + * Return the Boolean value denoted by TAG in section SECTION, or DEF + * if that tags does not exist. + * FALSE is returned for case-insensitive comparisons with 0, f, false, n, no, off + * TRUE is returned for 1, t, true, y, yes, on + * A failure to match one of these results in DEF + */ +_Bool +conf_get_bool(const char *section, const char *tag, _Bool def) +{ + char *value = conf_get_str(section, tag); + + if (!value) + return def; + if (strcasecmp(value, "1") == 0 || + strcasecmp(value, "t") == 0 || + strcasecmp(value, "true") == 0 || + strcasecmp(value, "y") == 0 || + strcasecmp(value, "yes") == 0 || + strcasecmp(value, "on") == 0) + return true; + + if (strcasecmp(value, "0") == 0 || + strcasecmp(value, "f") == 0 || + strcasecmp(value, "false") == 0 || + strcasecmp(value, "n") == 0 || + strcasecmp(value, "no") == 0 || + strcasecmp(value, "off") == 0) + return false; + return def; +} + +/* Validate X according to the range denoted by TAG in section SECTION. */ +int +conf_match_num(const char *section, const char *tag, int x) +{ + char *value = conf_get_str (section, tag); + int val, min, max, n; + + if (!value) + return 0; + n = sscanf (value, "%d,%d:%d", &val, &min, &max); + switch (n) { + case 1: + xlog(LOG_INFO, "conf_match_num: %s:%s %d==%d?", section, tag, val, x); + return x == val; + case 3: + xlog(LOG_INFO, "conf_match_num: %s:%s %d<=%d<=%d?", section, + tag, min, x, max); + return min <= x && max >= x; + default: + xlog(LOG_INFO, "conf_match_num: section %s tag %s: invalid number spec %s", + section, tag, value); + } + return 0; +} + +/* Return the string value denoted by TAG in section SECTION. */ +char * +conf_get_str(const char *section, const char *tag) +{ + return conf_get_section(section, NULL, tag); +} + +/* Return the string value denoted by TAG in section SECTION, + * unless it is not set, in which case return def + */ +char * +conf_get_str_with_def(const char *section, const char *tag, char *def) +{ + char * result = conf_get_section(section, NULL, tag); + if (!result) + return def; + return result; +} + +/* + * Retrieve an entry without interpreting its contents + */ +char * +conf_get_entry(const char *section, const char *arg, const char *tag) +{ + struct conf_binding *cb; + + cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); + for (; cb; cb = LIST_NEXT (cb, link)) { + if (strcasecmp(section, cb->section) != 0) + continue; + if (arg && (cb->arg == NULL || strcasecmp(arg, cb->arg) != 0)) + continue; + if (!arg && cb->arg) + continue; + if (strcasecmp(tag, cb->tag) != 0) + continue; + return cb->value; + } + return 0; +} + +/* + * Find a section that may or may not have an argument + */ +char * +conf_get_section(const char *section, const char *arg, const char *tag) +{ + struct conf_binding *cb; +retry: + cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); + for (; cb; cb = LIST_NEXT (cb, link)) { + if (strcasecmp(section, cb->section) != 0) + continue; + if (arg && (cb->arg == NULL || strcasecmp(arg, cb->arg) != 0)) + continue; + if (!arg && cb->arg) + continue; + if (strcasecmp(tag, cb->tag) != 0) + continue; + if (cb->value[0] == '$') { + /* expand $name from [environment] section, + * or from environment + */ + char *env = getenv(cb->value+1); + if (env && *env) + return env; + section = "environment"; + tag = cb->value + 1; + goto retry; + } + return cb->value; + } + return 0; +} + +/* + * Build a list of string values out of the comma separated value denoted by + * TAG in SECTION. + */ +struct conf_list * +conf_get_list(const char *section, const char *tag) +{ + char *liststr = 0, *p, *field, *t; + struct conf_list *list = 0; + struct conf_list_node *node; + + list = malloc (sizeof *list); + if (!list) + goto cleanup; + TAILQ_INIT (&list->fields); + list->cnt = 0; + liststr = conf_get_str(section, tag); + if (!liststr) + goto cleanup; + liststr = strdup (liststr); + if (!liststr) + goto cleanup; + p = liststr; + while ((field = strsep (&p, ",")) != NULL) { + /* Skip leading whitespace */ + while (isspace (*field)) + field++; + /* Skip trailing whitespace */ + if (p) { + for (t = p - 1; t > field && isspace (*t); t--) + *t = '\0'; + } + if (*field == '\0') { + xlog(LOG_INFO, "conf_get_list: empty field, ignoring..."); + continue; + } + list->cnt++; + node = calloc (1, sizeof *node); + if (!node) + goto cleanup; + node->field = strdup (field); + if (!node->field) { + free(node); + goto cleanup; + } + TAILQ_INSERT_TAIL (&list->fields, node, link); + } + free (liststr); + return list; + +cleanup: + if (list) + conf_free_list(list); + if (liststr) + free(liststr); + return 0; +} + +struct conf_list * +conf_get_tag_list(const char *section, const char *arg) +{ + struct conf_list *list = 0; + struct conf_list_node *node; + struct conf_binding *cb; + + list = malloc(sizeof *list); + if (!list) + goto cleanup; + TAILQ_INIT(&list->fields); + list->cnt = 0; + cb = LIST_FIRST(&conf_bindings[conf_hash (section)]); + for (; cb; cb = LIST_NEXT(cb, link)) { + if (strcasecmp (section, cb->section) == 0) { + if (arg != NULL && strcasecmp(arg, cb->arg) != 0) + continue; + list->cnt++; + node = calloc(1, sizeof *node); + if (!node) + goto cleanup; + node->field = strdup(cb->tag); + if (!node->field) { + free(node); + goto cleanup; + } + TAILQ_INSERT_TAIL(&list->fields, node, link); + } + } + return list; + +cleanup: + if (list) + conf_free_list(list); + return 0; +} + +/* Decode a PEM encoded buffer. */ +int +conf_decode_base64 (uint8_t *out, uint32_t *len, const unsigned char *buf) +{ + uint32_t c = 0; + uint8_t c1, c2, c3, c4; + + while (*buf) { + if (*buf > 127 || (c1 = asc2bin[*buf]) == 255) + return 0; + + buf++; + if (*buf > 127 || (c2 = asc2bin[*buf]) == 255) + return 0; + + buf++; + if (*buf == '=') { + c3 = c4 = 0; + c++; + + /* Check last four bit */ + if (c2 & 0xF) + return 0; + + if (strcmp((char *)buf, "==") == 0) + buf++; + else + return 0; + } else if (*buf > 127 || (c3 = asc2bin[*buf]) == 255) + return 0; + else { + if (*++buf == '=') { + c4 = 0; + c += 2; + + /* Check last two bit */ + if (c3 & 3) + return 0; + + if (strcmp((char *)buf, "=")) + return 0; + } else if (*buf > 127 || (c4 = asc2bin[*buf]) == 255) + return 0; + else + c += 3; + } + + buf++; + *out++ = (c1 << 2) | (c2 >> 4); + *out++ = (c2 << 4) | (c3 >> 2); + *out++ = (c3 << 6) | c4; + } + + *len = c; + return 1; +} + +void +conf_free_list(struct conf_list *list) +{ + struct conf_list_node *node = TAILQ_FIRST(&list->fields); + + while (node) { + TAILQ_REMOVE(&list->fields, node, link); + if (node->field) + free(node->field); + free (node); + node = TAILQ_FIRST(&list->fields); + } + free (list); +} + +int +conf_begin(void) +{ + static int seq = 0; + + return ++seq; +} + +static struct conf_trans * +conf_trans_node(int transaction, enum conf_op op) +{ + struct conf_trans *node; + + node = calloc (1, sizeof *node); + if (!node) { + xlog_warn("conf_trans_node: calloc (1, %lu) failed", + (unsigned long)sizeof *node); + return 0; + } + node->trans = transaction; + node->op = op; + TAILQ_INSERT_TAIL (&conf_trans_queue, node, link); + return node; +} + +/* Queue a set operation. */ +static int +conf_set(int transaction, const char *section, const char *arg, + const char *tag, const char *value, int override, int is_default) +{ + struct conf_trans *node; + + if (!value || !*value) + return 0; + node = conf_trans_node(transaction, CONF_SET); + if (!node) + return 1; + node->section = strdup(section); + if (!node->section) { + xlog_warn("conf_set: strdup(\"%s\") failed", section); + goto fail; + } + /* Make Section names case-insensitive */ + upper2lower(node->section); + + if (arg) { + node->arg = strdup(arg); + if (!node->arg) { + xlog_warn("conf_set: strdup(\"%s\") failed", arg); + goto fail; + } + } else + node->arg = NULL; + + node->tag = strdup(tag); + if (!node->tag) { + xlog_warn("conf_set: strdup(\"%s\") failed", tag); + goto fail; + } + node->value = strdup(value); + if (!node->value) { + xlog_warn("conf_set: strdup(\"%s\") failed", value); + goto fail; + } + node->override = override; + node->is_default = is_default; + return 0; + +fail: + free_conftrans(node); + return 1; +} + +/* Queue a remove operation. */ +int +conf_remove(int transaction, const char *section, const char *tag) +{ + struct conf_trans *node; + + node = conf_trans_node(transaction, CONF_REMOVE); + if (!node) + goto fail; + node->section = strdup(section); + if (!node->section) { + xlog_warn("conf_remove: strdup(\"%s\") failed", section); + goto fail; + } + node->tag = strdup(tag); + if (!node->tag) { + xlog_warn("conf_remove: strdup(\"%s\") failed", tag); + goto fail; + } + return 0; + +fail: + free_conftrans(node); + return 1; +} + +/* Queue a remove section operation. */ +int +conf_remove_section(int transaction, const char *section) +{ + struct conf_trans *node; + + node = conf_trans_node(transaction, CONF_REMOVE_SECTION); + if (!node) + goto fail; + node->section = strdup(section); + if (!node->section) { + xlog_warn("conf_remove_section: strdup(\"%s\") failed", section); + goto fail; + } + return 0; + +fail: + free_conftrans(node); + return 1; +} + +/* Execute all queued operations for this transaction. Cleanup. */ +int +conf_end(int transaction, int commit) +{ + struct conf_trans *node, *next; + + for (node = TAILQ_FIRST(&conf_trans_queue); node; node = next) { + next = TAILQ_NEXT(node, link); + if (node->trans == transaction) { + if (commit) { + switch (node->op) { + case CONF_SET: + conf_set_now(node->section, node->arg, + node->tag, node->value, node->override, + node->is_default); + break; + case CONF_REMOVE: + conf_remove_now(node->section, node->tag); + break; + case CONF_REMOVE_SECTION: + conf_remove_section_now(node->section); + break; + default: + xlog(LOG_INFO, "conf_end: unknown operation: %d", node->op); + } + } + TAILQ_REMOVE (&conf_trans_queue, node, link); + free_conftrans(node); + } + } + return 0; +} + +/* + * Dump running configuration upon SIGUSR1. + * Configuration is "stored in reverse order", so reverse it again. + */ +struct dumper { + char *section; + char *arg; + char *tag; + char *value; + struct dumper *next; +}; + +/* + * Test if two nodes belong to the same (sub)sections + */ +static int +dumper_section_compare(const struct dumper *nodea, const struct dumper *nodeb) +{ + int ret; + + /* missing node, shouldnt happen */ + if (!nodea || !nodeb) + return -1; + + /* no section names at all, they are equal */ + if (!nodea->section && !nodeb->section) + return 0; + + /* if only one has a section name, the blank one goes first */ + if (!nodea->section && nodeb->section) + return -1; + + if (nodea->section && !nodeb->section) + return 1; + + /* both have section names, but do they match */ + ret = strcmp(nodea->section, nodeb->section); + + /* section names differ, that was easy */ + if (ret != 0) + return ret; + + /* sections matched, but how about sub-sections, + * again, if only one has a value the blank goes first + */ + if (!nodea->arg && nodeb->arg) + return -1; + + if (nodea->arg && !nodeb->arg) + return 1; + + /* both have sub-section args and they differ */ + if (nodea->arg && nodeb->arg + && (ret=strcmp(nodea->arg, nodeb->arg))!=0) + return ret; + + return 0; +} + +/* If a string starts or ends with a space it should be quoted */ +static bool +should_escape(const char *text) +{ + int len; + + /* no string, no escaping needed */ + if (!text) + return false; + + /* first character is a space */ + if (isspace(text[0])) + return true; + + /* last character is a space */ + len = strlen(text); + if (isspace(text[len-1])) + return true; + + return false; +} + +static void +conf_report_dump_text(struct dumper *head, FILE *ff) +{ + const struct dumper *node = head; + const struct dumper *last = NULL; + + for (node=head; node!=NULL; node=node->next) { + /* starting a new section, print the section header */ + if (dumper_section_compare(last, node)!=0) { + if (node != head) + fprintf(ff, "\n"); + if (node->arg) + fprintf(ff, "[%s \"%s\"]\n", node->section, node->arg); + else + fprintf(ff, "[%s]\n", node->section); + } + + /* now print the tag and its value */ + fprintf(ff, " %s", node->tag); + if (node->value) { + if (should_escape(node->value)) + fprintf(ff, " = \"%s\"", node->value); + else + fprintf(ff, " = %s", node->value); + } + fprintf(ff, "\n"); + + last = node; + } +} + +/* sort by tag compare function */ +static int +dumper_compare(const void *a, const void *b) +{ + const struct dumper *nodea = *(struct dumper **)a; + const struct dumper *nodeb = *(struct dumper **)b; + int ret; + + /* missing node, shouldnt happen */ + if (!nodea || !nodeb) + return -1; + + /* are the two nodes in different (sub)sections */ + ret = dumper_section_compare(nodea, nodeb); + if (ret != 0) + return ret; + + /* sub-sections match (both blank, or both same) + * so we compare the tag names + */ + + /* blank tags shouldnt happen, but paranoia */ + if (!nodea->tag && !nodeb->tag) + return 0; + + /* still shouldnt happen, but use the blank-goes-first logic */ + if (!nodea->tag && nodeb->tag) + return -1; + if ( nodea->tag && !nodeb->tag) + return 1; + + /* last test, compare the tags directly */ + ret = strcmp(nodea->tag, nodeb->tag); + return ret; +} + +/* sort all of the report nodes */ +static struct dumper * +conf_report_sort(struct dumper *start) +{ + struct dumper **list; + struct dumper *node; + unsigned int count = 0; + unsigned int i=0; + + /* how long is this list */ + for (node=start; node!=NULL; node=node->next) + count++; + + /* no need to sort a list with less than 2 items */ + if (count < 2) + return start; + + /* build an array of all the nodes */ + list = calloc(count, sizeof(struct dumper *)); + if (!list) + goto mem_err; + + for (node=start,i=0; node!=NULL; node=node->next) { + list[i++] = node; + } + + /* sort the array alphabetically by section and tag */ + qsort(list, count, sizeof(struct dumper *), dumper_compare); + + /* rebuild the linked list in sorted order */ + for (i=0; i<count-1; i++) { + list[i]->next = list[i+1]; + } + list[count-1]->next = NULL; + + /* remember the new head of list and discard the sorting array */ + node = list[0]; + free(list); + + /* return the new head of list */ + return node; + +mem_err: + free(list); + return NULL; +} + +/* Output a copy of the current configuration to file */ +void +conf_report(FILE *outfile) +{ + struct conf_binding *cb = NULL; + unsigned int i; + struct dumper *dumper = NULL, *dnode = NULL; + + xlog(LOG_INFO, "conf_report: dumping running configuration"); + + /* build a linked list of all the config nodes */ + for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) { + for (cb = LIST_FIRST(&conf_bindings[i]); cb; cb = LIST_NEXT(cb, link)) { + struct dumper *newnode = calloc(1, sizeof (struct dumper)); + if (!newnode) + goto mem_fail; + + newnode->next = dumper; + dumper = newnode; + + newnode->section = cb->section; + newnode->arg = cb->arg; + newnode->tag = cb->tag; + newnode->value = cb->value; + } + } + + /* sort the list then print it */ + dumper = conf_report_sort(dumper); + conf_report_dump_text(dumper, outfile); + goto cleanup; + +mem_fail: + xlog_warn("conf_report: malloc/calloc failed"); +cleanup: + /* traverse the linked list freeing all the nodes */ + while ((dnode = dumper) != 0) { + dumper = dumper->next; + free(dnode); + } + return; +} + +/* struct and queue for buffing output lines */ +TAILQ_HEAD(tailhead, outbuffer); + +struct outbuffer { + TAILQ_ENTRY(outbuffer) link; + char *text; +}; + +static struct outbuffer * +make_outbuffer(char *line) +{ + struct outbuffer *new; + + if (line == NULL) + return NULL; + + new = calloc(1, sizeof(struct outbuffer)); + if (new == NULL) { + xlog(L_ERROR, "malloc error creating outbuffer"); + return NULL; + } + new->text = line; + return new; +} + +/* compose a properly escaped tag=value line */ +static char * +make_tagline(const char *tag, const char *value) +{ + char *line; + int ret; + + if (!value) + return NULL; + + if (should_escape(value)) + ret = asprintf(&line, "%s = \"%s\"\n", tag, value); + else + ret = asprintf(&line, "%s = %s\n", tag, value); + + if (ret == -1) { + xlog(L_ERROR, "malloc error composing a tag line"); + return NULL; + } + return line; +} + +/* compose a section header line */ +static char * +make_section(const char *section, const char *arg) +{ + char *line; + int ret; + + if (arg) + ret = asprintf(&line, "[%s \"%s\"]\n", section, arg); + else + ret = asprintf(&line, "[%s]\n", section); + + if (ret == -1) { + xlog(L_ERROR, "malloc error composing section header"); + return NULL; + } + return line; +} + +/* compose a comment line (with or without tag) */ +static char * +make_comment(const char *tag, const char *comment) +{ + char *line; + int ret; + + if (tag == NULL || *tag == '\0') { + ret = asprintf(&line, "# %s\n", comment); + } else { + ret = asprintf(&line, "# %s: %s\n", tag, comment); + } + + if (ret == -1) { + xlog(L_ERROR, "malloc error composing header"); + return NULL; + } + + return line; +} + +/* compose a 'file modified' comment */ +static char * +make_timestamp(const char *tag, time_t when) +{ + struct tm *tstamp; + char datestr[80]; + char *result = NULL, *tmpstr = NULL; + int ret; + + tstamp = localtime(&when); + if (strftime(datestr, sizeof(datestr), "%b %d %Y %H:%M:%S", tstamp) == 0) { + xlog(L_ERROR, "error composing date"); + datestr[0] = '\0'; + } + + if (modified_by) { + ret = asprintf(&tmpstr, "%s on %s", modified_by, datestr); + if (ret == -1) { + xlog(L_ERROR, "malloc error composing a time stamp"); + return NULL; + } + result = make_comment(tag, tmpstr); + free(tmpstr); + } else { + result = make_comment(tag, datestr); + } + return result; +} + +/* does the supplied line contain the named section header */ +static bool +is_section(const char *line, const char *section, const char *arg) +{ + char *end; + char *name; + char *sub; + bool found = false; + + /* Not a valid section name */ + if (strcmp(section, "#") == 0) + return false; + + /* skip leading white space */ + while (*line == '[' || isspace(*line)) + line++; + + name = strdup(line); + if (name == NULL) { + xlog_warn("conf_write: malloc failed "); + return false; + } + + /* find the end */ + end = strchr(name, ']'); + + /* malformed line */ + if (end == NULL) { + xlog_warn("conf_write: warning: malformed section name"); + goto cleanup; + } + + while (*end && ( *end == ']' || isblank(*end))) + *(end--) = '\0'; + + /* is there a subsection name (aka arg) */ + sub = strchr(name, '"'); + if (sub) { + end = sub - 1; + *(sub++) = '\0'; + + /* trim whitespace between section name and arg */ + while (end > name && isblank(*end)) + *(end--) = '\0'; + + /* trim off closing quote */ + end = strchr(sub, '"'); + if (end == NULL) { + xlog_warn("conf_write: warning: malformed sub-section name"); + goto cleanup; + } + *end = '\0'; + } + + /* ready to compare */ + if (strcasecmp(section, name)!=0) + goto cleanup; + + if (arg != NULL) { + if (sub == NULL || strcasecmp(arg, sub)!=0) + goto cleanup; + } else { + if (sub != NULL) + goto cleanup; + } + + found = true; + +cleanup: + free(name); + return found; +} + +/* check that line contains the specified tag assignment */ +static bool +is_tag(const char *line, const char *tagname) +{ + char *end; + char *name; + bool found = false; + + /* quick check, is this even an assignment line */ + end = strchr(line, '='); + if (end == NULL) + return false; + + /* skip leading white space before tag name */ + while (isblank(*line)) + line++; + + name = strdup(line); + if (name == NULL) { + xlog_warn("conf_write: malloc failed"); + return false; + } + + /* trim any newline characters */ + end = strchr(name, '\n'); + if (end) + *end = '\0'; + end = strchr(name, '\r'); + if (end) + *end = '\0'; + + /* find the assignment equals sign */ + end = strchr(name, '='); + + /* malformed line, i swear the equals was there earlier */ + if (end == NULL) { + xlog_warn("conf_write: warning: malformed tag name"); + goto cleanup; + } + + /* trim trailing whitespace after tag name */ + do { + *(end--) = '\0'; + }while (end > name && *end && isblank(*end)); + + /* quoted string, take contents of quotes only */ + if (*name == '"') { + char * new = strdup(name+1); + end = strchr(new, '"'); + if (end != NULL) { + *end = 0; + free(name); + name = new; + } else { + free(new); + } + } + + /* now compare */ + if (strcasecmp(tagname, name) == 0) + found = true; + +cleanup: + free(name); + return found; +} + +/* is this an empty line ? */ +static bool +is_empty(const char *line) +{ + const char *p = line; + + if (line == NULL) + return true; + if (*line == '\0') + return true; + + while (*p != '\0' && isspace(*p)) + p++; + + if (*p == '\0') + return true; + + return false; +} + +/* is this line just a comment ? */ +static bool +is_comment(const char *line) +{ + if (line == NULL) + return false; + + while (isblank(*line)) + line++; + + if (*line == '#') + return true; + + return false; +} + +/* check that line contains the specified comment header */ +static bool +is_taggedcomment(const char *line, const char *field) +{ + char *end; + char *name; + bool found = false; + + if (line == NULL) + return false; + + while (isblank(*line)) + line++; + + if (*line != '#') + return false; + + line++; + + /* quick check, is this even a likely formatted line */ + end = strchr(line, ':'); + if (end == NULL) + return false; + + /* skip leading white space before field name */ + while (isblank(*line)) + line++; + + name = strdup(line); + if (name == NULL) { + xlog_warn("conf_write: malloc failed"); + return false; + } + + /* strip trailing spaces from the name */ + end = strchr(name, ':'); + if (end) *(end--) = 0; + while (end && end > name && isblank(*end)) + *(end--)=0; + + if (strcasecmp(name, field)==0) + found = true; + + free(name); + return found; +} + + +/* delete a buffer queue whilst optionally outputting to file */ +static int +flush_outqueue(struct tailhead *queue, FILE *fout) +{ + int ret = 0; + while (queue->tqh_first != NULL) { + struct outbuffer *ob = queue->tqh_first; + TAILQ_REMOVE(queue, ob, link); + if (ob->text) { + if (fout) { + ret = fprintf(fout, "%s", ob->text); + if (ret == -1) { + xlog(L_ERROR, "Error writing to config file: %s", + strerror(errno)); + fout = NULL; + } + } + free(ob->text); + } + free(ob); + } + if (ret == -1) + return 1; + return 0; +} + +/* append one queue to another */ +static void +append_queue(struct tailhead *inq, struct tailhead *outq) +{ + while (inq->tqh_first != NULL) { + struct outbuffer *ob = inq->tqh_first; + TAILQ_REMOVE(inq, ob, link); + TAILQ_INSERT_TAIL(outq, ob, link); + } +} + +/* read one line of text from a file, growing the buffer as necessary */ +static int +read_line(char **buff, int *buffsize, FILE *in) +{ + char *readp; + int used = 0; + bool again = false; + + /* make sure we have a buffer to read into */ + if (*buff == NULL) { + *buffsize = 4096; + *buff = calloc(1, *buffsize); + if (*buff == NULL) { + xlog(L_ERROR, "malloc error for read buffer"); + return -1; + } + } + + readp = *buff; + + do { + int len; + + /* read in a chunk */ + if (fgets(readp, *buffsize-used, in)==NULL) + return -1; + + len = strlen(*buff); + if (len == 0) + return -1; + + /* was this the end of a line, or partial read */ + readp = *buff + len - 1; + + if (*readp != '\n' && *readp !='\r') { + /* no nl/cr must be partial read, go again */ + readp++; + again = true; + } else { + /* that was a normal end of line */ + again = false; + } + + /* do we need more space */ + if (again && *buffsize - len < 1024) { + int offset = readp - *buff; + char *newbuff; + *buffsize += 4096; + newbuff = realloc(*buff, *buffsize); + if (newbuff == NULL) { + xlog(L_ERROR, "malloc error reading line"); + return -1; + } + *buff = newbuff; + readp = newbuff + offset; + } + } while(again); + return 0; +} + +/* append a line to the given location in the queue */ +static int +append_line(struct tailhead *queue, struct outbuffer *entry, char *line) +{ + int ret = 0; + char *end; + bool splitmode = false; + char *start = line; + + if (line == NULL) + return -1; + + /* if there are \n's in the middle of the string + * then we need to split it into folded lines */ + do { + char *thisline; + struct outbuffer *qbuff; + + end = strchr(start, '\n'); + if (end && *(end+1) != '\0') { + *end = '\0'; + + ret = asprintf(&thisline, "%s\\\n", start); + if (ret == -1) { + xlog(L_ERROR, "malloc error composing output"); + return -1; + } + splitmode = true; + start = end+1; + } else { + end = NULL; + if (splitmode) { + thisline = strdup(start); + if (thisline == NULL) + return -1; + } else { + thisline = start; + } + } + + qbuff = make_outbuffer(thisline); + if (qbuff == NULL) + return -1; + + if (entry) { + TAILQ_INSERT_AFTER(queue, entry, qbuff, link); + entry = TAILQ_NEXT(entry, link); + } else { + TAILQ_INSERT_TAIL(queue, qbuff, link); + } + }while (end != NULL); + + /* we malloced copies of this, so free the original */ + if (splitmode) + free(line); + + return 0; +} + +/* is this a "folded" line, i.e. ends in backslash */ +static bool +is_folded(const char *line) +{ + const char *end; + if (line == NULL) + return false; + + end = line + strlen(line); + while (end > line) { + end--; + if (*end != '\n' && *end != '\r') + break; + } + + if (*end == '\\') + return true; + + return false; +} + +static int +lock_file(FILE *f) +{ + int ret; + ret = flock(fileno(f), LOCK_EX); + if (ret) + xlog(L_ERROR, "Error could not lock the file"); + return ret; +} + +/*** + * Write a value to an nfs.conf style filename + * + * create the file if it doesnt already exist + * if value==NULL removes the setting (if present) + */ +int +conf_write(const char *filename, const char *section, const char *arg, + const char *tag, const char *value) +{ + FILE *infile = NULL; + int ret = 1; + struct tailhead outqueue; + struct tailhead inqueue; + char * buff = NULL; + int buffsize = 0; + time_t now = time(NULL); + + TAILQ_INIT(&inqueue); + TAILQ_INIT(&outqueue); + + if (!filename) { + xlog_warn("conf_write: no filename supplied"); + return ret; + } + + if (!section || !tag) { + xlog_warn("conf_write: section or tag name missing"); + return ret; + } + + infile = fopen(filename, "r+"); + if (!infile) { + if (!value) { + xlog_warn("conf_write: config file \"%s\" not found, nothing to do", filename); + ret = 0; + goto cleanup; + } + + xlog_warn("conf_write: config file \"%s\" not found, creating.", filename); + infile = fopen(filename, "wx"); + if (!infile) { + xlog(L_ERROR, "conf_write: Error creating config file \"%s\".", filename); + goto cleanup; + } + + if (lock_file(infile)) + goto cleanup; + + if (strcmp(section, "#") == 0) { + if (append_line(&inqueue, NULL, make_comment(tag, value))) + goto cleanup; + } else { + if (append_line(&inqueue, NULL, make_section(section, arg))) + goto cleanup; + + if (append_line(&inqueue, NULL, make_tagline(tag, value))) + goto cleanup; + } + + append_queue(&inqueue, &outqueue); + } else + if (strcmp(section, "#") == 0) { + /* Adding a comment line */ + struct outbuffer *where = NULL; + struct outbuffer *next = NULL; + bool found = false; + int err = 0; + + if (lock_file(infile)) + goto cleanup; + + buffsize = 4096; + buff = calloc(1, buffsize); + if (buff == NULL) { + xlog(L_ERROR, "malloc error for read buffer"); + goto cleanup; + } + buff[0] = '\0'; + + /* read in the file */ + do { + if (*buff != '\0' + && !is_taggedcomment(buff, "Modified")) { + if (append_line(&inqueue, NULL, strdup(buff))) + goto cleanup; + } + + err = read_line(&buff, &buffsize, infile); + } while (err == 0); + + /* if a tagged comment, look for an existing instance */ + if (tag && *tag != '\0') { + where = TAILQ_FIRST(&inqueue); + while (where != NULL) { + next = TAILQ_NEXT(where, link); + struct outbuffer *prev = TAILQ_PREV(where, tailhead, link); + if (is_taggedcomment(where->text, tag)) { + TAILQ_REMOVE(&inqueue, where, link); + free(where->text); + free(where); + found = true; + if (append_line(&inqueue, prev, make_comment(tag, value))) + goto cleanup; + } + where = next; + } + } + /* it wasn't tagged or we didn't find it */ + if (!found) { + /* does the file end in a blank line or a comment */ + if (!TAILQ_EMPTY(&inqueue)) { + struct outbuffer *tail = TAILQ_LAST(&inqueue, tailhead); + if (tail && !is_empty(tail->text) && !is_comment(tail->text)) { + /* no, so add one for clarity */ + if (append_line(&inqueue, NULL, strdup("\n"))) + goto cleanup; + } + } + /* add the new comment line */ + if (append_line(&inqueue, NULL, make_comment(tag, value))) + goto cleanup; + } + /* move everything over to the outqueue for writing */ + append_queue(&inqueue, &outqueue); + } else { + bool found = false; + int err = 0; + + if (lock_file(infile)) + goto cleanup; + + buffsize = 4096; + buff = calloc(1, buffsize); + if (buff == NULL) { + xlog(L_ERROR, "malloc error for read buffer"); + goto cleanup; + } + + buff[0] = '\0'; + do { + struct outbuffer *where = NULL; + + /* read in one section worth of lines */ + do { + if (*buff != '\0' + && !is_taggedcomment(buff, "Modified")) { + if (append_line(&inqueue, NULL, strdup(buff))) + goto cleanup; + } + + err = read_line(&buff, &buffsize, infile); + } while (err == 0 && buff[0] != '['); + + /* find the section header */ + where = TAILQ_FIRST(&inqueue); + while (where != NULL) { + if (where->text != NULL && where->text[0] == '[') + break; + where = TAILQ_NEXT(where, link); + } + + /* this is the section we care about */ + if (where != NULL && is_section(where->text, section, arg)) { + struct outbuffer *section_start = where; + + /* is there an existing assignment */ + while ((where = TAILQ_NEXT(where, link)) != NULL) { + if (is_tag(where->text, tag)) { + found = true; + break; + } + } + + /* no active assignment, but is there a commented one */ + if (!found) { + where = section_start; + while ((where = TAILQ_NEXT(where, link)) != NULL) { + if (is_comment(where->text)) { + char *cline = where->text; + while (isspace(*cline)) + cline++; + + if (*cline != '#') + continue; + cline++; + + if (is_tag(cline, tag)) { + found = true; + break; + } + } + } + } + + /* replace the located tag with an updated one */ + if (found) { + struct outbuffer *prev = TAILQ_PREV(where, tailhead, link); + bool again = false; + + /* remove current tag */ + do { + struct outbuffer *next = TAILQ_NEXT(where, link); + TAILQ_REMOVE(&inqueue, where, link); + if (is_folded(where->text)) + again = true; + else + again = false; + free(where->text); + free(where); + where = next; + } while(again && where != NULL); + + /* insert new tag */ + if (value) { + if (append_line(&inqueue, prev, make_tagline(tag, value))) + goto cleanup; + } + } else + /* no existing assignment found and we need to add one */ + if (value) { + /* rewind past blank lines and comments */ + struct outbuffer *tail = TAILQ_LAST(&inqueue, tailhead); + + /* comments immediately before a section usually relate + * to the section below them */ + while (tail != NULL && is_comment(tail->text)) + tail = TAILQ_PREV(tail, tailhead, link); + + /* there is usually blank line(s) between sections */ + while (tail != NULL && is_empty(tail->text)) + tail = TAILQ_PREV(tail, tailhead, link); + + /* now add the tag here */ + if (append_line(&inqueue, tail, make_tagline(tag, value))) + goto cleanup; + + found = true; + } + } + + /* EOF and correct section not found, so add one */ + if (err && !found && value) { + /* did the last section end in a blank line */ + struct outbuffer *tail = TAILQ_LAST(&inqueue, tailhead); + if (tail && !is_empty(tail->text)) { + /* no, so add one for clarity */ + if (append_line(&inqueue, NULL, strdup("\n"))) + goto cleanup; + } + + /* add the new section header */ + if (append_line(&inqueue, NULL, make_section(section, arg))) + goto cleanup; + + /* now add the tag */ + if (append_line(&inqueue, NULL, make_tagline(tag, value))) + goto cleanup; + } + + /* we are done with this section, move it to the out queue */ + append_queue(&inqueue, &outqueue); + } while(err == 0); + } + + if (modified_by) { + /* check for and update the Modified header */ + /* does the file end in a blank line or a comment */ + if (!TAILQ_EMPTY(&outqueue)) { + struct outbuffer *tail = TAILQ_LAST(&outqueue, tailhead); + if (tail && !is_empty(tail->text) && !is_comment(tail->text)) { + /* no, so add one for clarity */ + if (append_line(&outqueue, NULL, strdup("\n"))) + goto cleanup; + } + } + + /* now append the modified date comment */ + if (append_line(&outqueue, NULL, make_timestamp("Modified", now))) + goto cleanup; + } + + /* now rewind and overwrite the file with the updated data */ + rewind(infile); + + if (ftruncate(fileno(infile), 0)) { + xlog(L_ERROR, "Error truncating config file"); + goto cleanup; + } + + if (flush_outqueue(&outqueue, infile)) + goto cleanup; + + if (infile) { + fclose(infile); + infile = NULL; + } + + ret = 0; + +cleanup: + flush_outqueue(&inqueue, NULL); + flush_outqueue(&outqueue, NULL); + + if (buff) + free(buff); + if (infile) + fclose(infile); + return ret; +} diff --git a/support/nfs/exports.c b/support/nfs/exports.c new file mode 100644 index 0000000..15dc574 --- /dev/null +++ b/support/nfs/exports.c @@ -0,0 +1,999 @@ +/* + * support/nfs/export.c + * + * Parse the exports file. Derived from the unfsd implementation. + * + * Authors: Donald J. Becker, <becker@super.org> + * Rick Sladkey, <jrs@world.std.com> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Olaf Kirch, <okir@monad.swb.de> + * Alexander O. Yuriev, <alex@bach.cis.temple.edu> + * + * This software maybe be used for any purpose provided + * the above copyright notice is retained. It is supplied + * as is, with no warranty expressed or implied. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/param.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <unistd.h> +#include <errno.h> +#include "nfslib.h" +#include "exportfs.h" +#include "xmalloc.h" +#include "xlog.h" +#include "xio.h" +#include "pseudoflavors.h" +#include "reexport.h" + +#define EXPORT_DEFAULT_FLAGS \ + (NFSEXP_READONLY|NFSEXP_ROOTSQUASH|NFSEXP_GATHERED_WRITES|NFSEXP_NOSUBTREECHECK) + +struct flav_info flav_map[] = { + { "krb5", RPC_AUTH_GSS_KRB5, 1}, + { "krb5i", RPC_AUTH_GSS_KRB5I, 1}, + { "krb5p", RPC_AUTH_GSS_KRB5P, 1}, + { "unix", AUTH_UNIX, 0}, + { "sys", AUTH_SYS, 0}, + { "null", AUTH_NULL, 0}, + { "none", AUTH_NONE, 0}, +}; + +const int flav_map_size = sizeof(flav_map)/sizeof(flav_map[0]); + +int default_ttl = 30 * 60; + +static char *efname = NULL; +static XFILE *efp = NULL; +static int first; +static int has_default_opts, has_default_subtree_opts; +static int *squids = NULL, nsquids = 0, + *sqgids = NULL, nsqgids = 0; + +static int getexport(char *exp, int len); +static int getpath(char *path, int len); +static int parseopts(char *cp, struct exportent *ep, int warn, int *had_subtree_opt_ptr); +static int parsesquash(char *list, int **idp, int *lenp, char **ep); +static int parsenum(char **cpp); +static void freesquash(void); +static void syntaxerr(char *msg); +static struct flav_info *find_flavor(char *name); + +void +setexportent(char *fname, char *type) +{ + if (efp) + endexportent(); + if (!fname) + fname = _PATH_EXPORTS; + if (!(efp = xfopen(fname, type))) + xlog(L_ERROR, "can't open %s for %sing", + fname, strcmp(type, "r")? "writ" : "read"); + efname = strdup(fname); + first = 1; +} + +static void init_exportent (struct exportent *ee, int fromkernel) +{ + ee->e_flags = EXPORT_DEFAULT_FLAGS; + /* some kernels assume the default is sync rather than + * async. More recent kernels always report one or other, + * but this test makes sure we assume same as kernel + * Ditto for wgather + */ + if (fromkernel) { + ee->e_flags &= ~NFSEXP_ASYNC; + ee->e_flags &= ~NFSEXP_GATHERED_WRITES; + } + ee->e_anonuid = 65534; + ee->e_anongid = 65534; + ee->e_squids = NULL; + ee->e_sqgids = NULL; + ee->e_mountpoint = NULL; + ee->e_fslocmethod = FSLOC_NONE; + ee->e_fslocdata = NULL; + ee->e_secinfo[0].flav = NULL; + ee->e_xprtsec[0].info = NULL; + ee->e_nsquids = 0; + ee->e_nsqgids = 0; + ee->e_uuid = NULL; + ee->e_ttl = default_ttl; + ee->e_reexport = REEXP_NONE; +} + +struct exportent * +getexportent(int fromkernel, int fromexports) +{ + static struct exportent ee, def_ee; + char exp[512], *hostname; + char rpath[MAXPATHLEN+1]; + char *opt, *sp; + int ok; + + if (!efp) + return NULL; + + freesquash(); + + if (first || (ok = getexport(exp, sizeof(exp))) == 0) { + has_default_opts = 0; + has_default_subtree_opts = 0; + + init_exportent(&def_ee, fromkernel); + + ok = getpath(def_ee.e_path, sizeof(def_ee.e_path)); + if (ok <= 0) + return NULL; + + ok = getexport(exp, sizeof(exp)); + } + if (ok < 0) { + xlog(L_ERROR, "expected client(options...)"); + return NULL; + } + first = 0; + + /* + * Check for default options. The kernel will never have default + * options in /proc/fs/nfs/exports, however due to the initial '-' in + * the -test-client- string from the test export we have to check that + * we're not reading from the kernel. + */ + if (exp[0] == '-' && !fromkernel) { + if (parseopts(exp + 1, &def_ee, 0, &has_default_subtree_opts) < 0) + return NULL; + + has_default_opts = 1; + + ok = getexport(exp, sizeof(exp)); + if (ok < 0) { + xlog(L_ERROR, "expected client(options...)"); + return NULL; + } + } + + xfree(ee.e_hostname); + xfree(ee.e_realpath); + ee = def_ee; + + /* Check for default client */ + if (ok == 0) + exp[0] = '\0'; + + hostname = exp; + if ((opt = strchr(exp, '(')) != NULL) { + if (opt == exp) { + xlog(L_WARNING, "No host name given with %s %s, suggest *%s to avoid warning", ee.e_path, exp, exp); + hostname = "*"; + } + *opt++ = '\0'; + if (!(sp = strchr(opt, ')')) || sp[1] != '\0') { + syntaxerr("bad option list"); + return NULL; + } + *sp = '\0'; + } else { + if (!has_default_opts) + xlog(L_WARNING, "No options for %s %s: suggest %s(sync) to avoid warning", ee.e_path, exp, exp); + } + ee.e_hostname = xstrdup(hostname); + + if (parseopts(opt, &ee, fromexports && !has_default_subtree_opts, NULL) < 0) { + if(ee.e_hostname) + { + xfree(ee.e_hostname); + ee.e_hostname=NULL; + } + if(ee.e_uuid) + { + xfree(ee.e_uuid); + ee.e_uuid=NULL; + } + + return NULL; + } + /* resolve symlinks */ + if (realpath(ee.e_path, rpath) != NULL) { + rpath[sizeof (rpath) - 1] = '\0'; + strncpy(ee.e_path, rpath, sizeof (ee.e_path) - 1); + ee.e_path[sizeof (ee.e_path) - 1] = '\0'; + } + + return ⅇ +} + +static const struct secinfo_flag_displaymap { + unsigned int flag; + const char *set; + const char *unset; +} secinfo_flag_displaymap[] = { + { NFSEXP_READONLY, "ro", "rw" }, + { NFSEXP_INSECURE_PORT, "insecure", "secure" }, + { NFSEXP_ROOTSQUASH, "root_squash", "no_root_squash" }, + { NFSEXP_ALLSQUASH, "all_squash", "no_all_squash" }, + { 0, NULL, NULL } +}; + +static void secinfo_flags_show(FILE *fp, unsigned int flags, unsigned int mask) +{ + const struct secinfo_flag_displaymap *p; + + for (p = &secinfo_flag_displaymap[0]; p->flag != 0; p++) { + if (!(mask & p->flag)) + continue; + fprintf(fp, ",%s", (flags & p->flag) ? p->set : p->unset); + } +} + +void secinfo_show(FILE *fp, struct exportent *ep) +{ + const struct export_features *ef; + struct sec_entry *p1, *p2; + + ef = get_export_features(); + + if (ep->e_secinfo[0].flav == NULL) + secinfo_addflavor(find_flavor("sys"), ep); + for (p1=ep->e_secinfo; p1->flav; p1=p2) { + fprintf(fp, ",sec=%s", p1->flav->flavour); + for (p2=p1+1; (p2->flav != NULL) && (p1->flags == p2->flags); + p2++) { + fprintf(fp, ":%s", p2->flav->flavour); + } + secinfo_flags_show(fp, p1->flags, ef->secinfo_flags); + } +} + +void xprtsecinfo_show(FILE *fp, struct exportent *ep) +{ + struct xprtsec_entry *p1, *p2; + + for (p1 = ep->e_xprtsec; p1->info; p1 = p2) { + fprintf(fp, ",xprtsec=%s", p1->info->name); + for (p2 = p1 + 1; p2->info && (p1->flags == p2->flags); p2++) + fprintf(fp, ":%s", p2->info->name); + } +} + +static void +fprintpath(FILE *fp, const char *path) +{ + int i; + for (i=0; path[i]; i++) + if (iscntrl(path[i]) || path[i] == '"' || path[i] == '\\' || path[i] == '#' || isspace(path[i])) + fprintf(fp, "\\%03o", path[i]); + else + fprintf(fp, "%c", path[i]); +} + +void +putexportent(struct exportent *ep) +{ + FILE *fp; + int *id, i; + + if (!efp) + return; + + fp = efp->x_fp; + fprintpath(fp, ep->e_path); + fprintf(fp, "\t%s(", ep->e_hostname); + fprintf(fp, "%s,", (ep->e_flags & NFSEXP_READONLY)? "ro" : "rw"); + fprintf(fp, "%ssync,", (ep->e_flags & NFSEXP_ASYNC)? "a" : ""); + fprintf(fp, "%swdelay,", (ep->e_flags & NFSEXP_GATHERED_WRITES)? + "" : "no_"); + fprintf(fp, "%shide,", (ep->e_flags & NFSEXP_NOHIDE)? + "no" : ""); + fprintf(fp, "%scrossmnt,", (ep->e_flags & NFSEXP_CROSSMOUNT)? + "" : "no"); + fprintf(fp, "%ssecure,", (ep->e_flags & NFSEXP_INSECURE_PORT)? + "in" : ""); + fprintf(fp, "%sroot_squash,", (ep->e_flags & NFSEXP_ROOTSQUASH)? + "" : "no_"); + fprintf(fp, "%sall_squash,", (ep->e_flags & NFSEXP_ALLSQUASH)? + "" : "no_"); + fprintf(fp, "%ssubtree_check,", (ep->e_flags & NFSEXP_NOSUBTREECHECK)? + "no_" : ""); + fprintf(fp, "%ssecure_locks,", (ep->e_flags & NFSEXP_NOAUTHNLM)? + "in" : ""); + fprintf(fp, "%sacl,", (ep->e_flags & NFSEXP_NOACL)? + "no_" : ""); + if (ep->e_flags & NFSEXP_NOREADDIRPLUS) + fprintf(fp, "nordirplus,"); + if (ep->e_flags & NFSEXP_SECURITY_LABEL) + fprintf(fp, "security_label,"); + fprintf(fp, "%spnfs,", (ep->e_flags & NFSEXP_PNFS)? "" : "no_"); + if (ep->e_flags & NFSEXP_FSID) { + fprintf(fp, "fsid=%d,", ep->e_fsid); + } + if (ep->e_uuid) + fprintf(fp, "fsid=%s,", ep->e_uuid); + + if (ep->e_reexport) { + fprintf(fp, "reexport="); + switch (ep->e_reexport) { + case REEXP_AUTO_FSIDNUM: + fprintf(fp, "auto-fsidnum"); + break; + case REEXP_PREDEFINED_FSIDNUM: + fprintf(fp, "predefined-fsidnum"); + break; + default: + xlog(L_ERROR, "unknown reexport method %i", ep->e_reexport); + fprintf(fp, "none"); + } + fprintf(fp, ","); + } + + if (ep->e_mountpoint) + fprintf(fp, "mountpoint%s%s,", + ep->e_mountpoint[0]?"=":"", ep->e_mountpoint); + switch (ep->e_fslocmethod) { + case FSLOC_NONE: + break; + case FSLOC_REFER: + fprintf(fp, "refer="); + fprintpath(fp, ep->e_fslocdata); + fprintf(fp, ","); + break; + case FSLOC_REPLICA: + fprintf(fp, "replicas="); + fprintpath(fp, ep->e_fslocdata); + fprintf(fp, ","); + break; +#ifdef DEBUG + case FSLOC_STUB: + fprintf(fp, "fsloc=stub,"); + break; +#endif + default: + xlog(L_ERROR, "unknown fsloc method for %s:%s", + ep->e_hostname, ep->e_path); + } + if ((id = ep->e_squids) != NULL) { + fprintf(fp, "squash_uids="); + for (i = 0; i < ep->e_nsquids; i += 2) + if (id[i] != id[i+1]) + fprintf(fp, "%d-%d,", id[i], id[i+1]); + else + fprintf(fp, "%d,", id[i]); + } + if ((id = ep->e_sqgids) != NULL) { + fprintf(fp, "squash_gids="); + for (i = 0; i < ep->e_nsquids; i += 2) + if (id[i] != id[i+1]) + fprintf(fp, "%d-%d,", id[i], id[i+1]); + else + fprintf(fp, "%d,", id[i]); + } + fprintf(fp, "anonuid=%d,anongid=%d", ep->e_anonuid, ep->e_anongid); + secinfo_show(fp, ep); + xprtsecinfo_show(fp, ep); + fprintf(fp, ")\n"); +} + +void +endexportent(void) +{ + if (efp) + xfclose(efp); + efp = NULL; + if (efname) + free(efname); + efname = NULL; + freesquash(); +} + +void +dupexportent(struct exportent *dst, struct exportent *src) +{ + int n; + + *dst = *src; + if ((n = src->e_nsquids) != 0) { + dst->e_squids = (int *) xmalloc(n * sizeof(int)); + memcpy(dst->e_squids, src->e_squids, n * sizeof(int)); + } + if ((n = src->e_nsqgids) != 0) { + dst->e_sqgids = (int *) xmalloc(n * sizeof(int)); + memcpy(dst->e_sqgids, src->e_sqgids, n * sizeof(int)); + } + if (src->e_mountpoint) + dst->e_mountpoint = strdup(src->e_mountpoint); + if (src->e_fslocdata) + dst->e_fslocdata = strdup(src->e_fslocdata); + if (src->e_uuid) + dst->e_uuid = strdup(src->e_uuid); + dst->e_hostname = NULL; + dst->e_realpath = NULL; +} + +struct exportent * +mkexportent(char *hname, char *path, char *options) +{ + static struct exportent ee; + + init_exportent(&ee, 0); + + xfree(ee.e_hostname); + ee.e_hostname = xstrdup(hname); + xfree(ee.e_realpath); + ee.e_realpath = NULL; + + if (strlen(path) >= sizeof(ee.e_path)) { + xlog(L_ERROR, "path name %s too long", path); + return NULL; + } + strncpy(ee.e_path, path, sizeof (ee.e_path)); + ee.e_path[sizeof (ee.e_path) - 1] = '\0'; + if (parseopts(options, &ee, 0, NULL) < 0) + return NULL; + return ⅇ +} + +int +updateexportent(struct exportent *eep, char *options) +{ + if (parseopts(options, eep, 0, NULL) < 0) + return 0; + return 1; +} + + +static int valid_uuid(char *uuid) +{ + /* must have 32 hex digits */ + int cnt; + for (cnt = 0 ; *uuid; uuid++) + if (isxdigit(*uuid)) + cnt++; + return cnt == 32; +} + +/* + * Append the given flavor to the exportent's e_secinfo array, or + * do nothing if it's already there. Returns the index of flavor + * in the resulting array in any case. + */ +int secinfo_addflavor(struct flav_info *flav, struct exportent *ep) +{ + struct sec_entry *p; + + for (p=ep->e_secinfo; p->flav; p++) { + if (p->flav == flav || p->flav->fnum == flav->fnum) + return p - ep->e_secinfo; + } + if (p - ep->e_secinfo >= SECFLAVOR_COUNT) { + xlog(L_ERROR, "more than %d security flavors on an export\n", + SECFLAVOR_COUNT); + return -1; + } + p->flav = flav; + p->flags = ep->e_flags; + (p+1)->flav = NULL; + return p - ep->e_secinfo; +} + +static struct flav_info *find_flavor(char *name) +{ + struct flav_info *flav; + for (flav = flav_map; flav < flav_map + flav_map_size; flav++) + if (strcmp(flav->flavour, name) == 0) + return flav; + return NULL; +} + +/* @str is a colon seperated list of security flavors. Their order + * is recorded in @ep, and a bitmap corresponding to the list is returned. + * A zero return indicates an error. + */ +static unsigned int parse_flavors(char *str, struct exportent *ep) +{ + unsigned int out=0; + char *flavor; + int bit; + + while ( (flavor=strsep(&str, ":")) ) { + struct flav_info *flav = find_flavor(flavor); + if (flav == NULL) { + xlog(L_ERROR, "unknown flavor %s\n", flavor); + return 0; + } + bit = secinfo_addflavor(flav, ep); + if (bit < 0) + return 0; + out |= 1<<bit; + } + return out; +} + +static const struct xprtsec_info xprtsec_name2info[] = { + { "none", NFSEXP_XPRTSEC_NONE }, + { "tls", NFSEXP_XPRTSEC_TLS }, + { "mtls", NFSEXP_XPRTSEC_MTLS }, + { NULL, 0 } +}; + +static const struct xprtsec_info *find_xprtsec_info(const char *name) +{ + const struct xprtsec_info *info; + + for (info = xprtsec_name2info; info->name; info++) + if (strcmp(info->name, name) == 0) + return info; + return NULL; +} + +/* + * Append the given xprtsec mode to the exportent's e_xprtsec array, + * or do nothing if it's already there. Returns the index of flavor in + * the resulting array in any case. + */ +static int xprtsec_addmode(const struct xprtsec_info *info, struct exportent *ep) +{ + struct xprtsec_entry *p; + + for (p = ep->e_xprtsec; p->info; p++) + if (p->info == info || p->info->number == info->number) + return p - ep->e_xprtsec; + + if (p - ep->e_xprtsec >= XPRTSECMODE_COUNT) { + xlog(L_ERROR, "more than %d xprtsec modes on an export\n", + XPRTSECMODE_COUNT); + return -1; + } + p->info = info; + p->flags = ep->e_flags; + (p + 1)->info = NULL; + return p - ep->e_xprtsec; +} + +/* + * @str is a colon seperated list of transport layer security modes. + * Their order is recorded in @ep, and a bitmap corresponding to the + * list is returned. + * + * A zero return indicates an error. + */ +static unsigned int parse_xprtsec(char *str, struct exportent *ep) +{ + unsigned int out = 0; + char *name; + + while ((name = strsep(&str, ":"))) { + const struct xprtsec_info *info = find_xprtsec_info(name); + int bit; + + if (!info) { + xlog(L_ERROR, "unknown xprtsec mode %s\n", name); + return 0; + } + bit = xprtsec_addmode(info, ep); + if (bit < 0) + return 0; + out |= 1 << bit; + } + return out; +} + +/* Sets the bits in @mask for the appropriate security flavor flags. */ +static void setflags(int mask, unsigned int active, struct exportent *ep) +{ + int bit=0; + + ep->e_flags |= mask; + + while (active) { + if (active & 1) + ep->e_secinfo[bit].flags |= mask; + bit++; + active >>= 1; + } +} + +/* Clears the bits in @mask for the appropriate security flavor flags. */ +static void clearflags(int mask, unsigned int active, struct exportent *ep) +{ + int bit=0; + + ep->e_flags &= ~mask; + + while (active) { + if (active & 1) + ep->e_secinfo[bit].flags &= ~mask; + bit++; + active >>= 1; + } +} + +/* + * For those flags which are not allowed to vary by pseudoflavor, + * ensure that the export flags agree with the flags on each + * pseudoflavor: + */ +void fix_pseudoflavor_flags(struct exportent *ep) +{ + struct export_features *ef; + struct sec_entry *p; + + ef = get_export_features(); + for (p = ep->e_secinfo; p->flav; p++) + p->flags |= ep->e_flags & ~ef->secinfo_flags; +} + +/* + * Parse option string pointed to by cp and set mount options accordingly. + */ +static int +parseopts(char *cp, struct exportent *ep, int warn, int *had_subtree_opt_ptr) +{ + int had_subtree_opt = 0; + char *flname = efname?efname:"command line"; + int flline = efp?efp->x_line:0; + unsigned int active = 0; + int saw_reexport = 0; + + squids = ep->e_squids; nsquids = ep->e_nsquids; + sqgids = ep->e_sqgids; nsqgids = ep->e_nsqgids; + if (!cp) + goto out; + + while (isblank(*cp)) + cp++; + + while (*cp) { + char *opt = strdup(cp); + char *optstart = cp; + while (*cp && *cp != ',') + cp++; + if (*cp) { + opt[cp-optstart] = '\0'; + cp++; + } + + /* process keyword */ + if (strcmp(opt, "ro") == 0) + setflags(NFSEXP_READONLY, active, ep); + else if (strcmp(opt, "rw") == 0) + clearflags(NFSEXP_READONLY, active, ep); + else if (!strcmp(opt, "secure")) + clearflags(NFSEXP_INSECURE_PORT, active, ep); + else if (!strcmp(opt, "insecure")) + setflags(NFSEXP_INSECURE_PORT, active, ep); + else if (!strcmp(opt, "sync")) + clearflags(NFSEXP_ASYNC, active, ep); + else if (!strcmp(opt, "async")) + setflags(NFSEXP_ASYNC, active, ep); + else if (!strcmp(opt, "nordirplus")) + setflags(NFSEXP_NOREADDIRPLUS, active, ep); + else if (!strcmp(opt, "security_label")) + setflags(NFSEXP_SECURITY_LABEL, active, ep); + else if (!strcmp(opt, "nohide")) + setflags(NFSEXP_NOHIDE, active, ep); + else if (!strcmp(opt, "hide")) + clearflags(NFSEXP_NOHIDE, active, ep); + else if (!strcmp(opt, "crossmnt")) + setflags(NFSEXP_CROSSMOUNT, active, ep); + else if (!strcmp(opt, "nocrossmnt")) + clearflags(NFSEXP_CROSSMOUNT, active, ep); + else if (!strcmp(opt, "wdelay")) + setflags(NFSEXP_GATHERED_WRITES, active, ep); + else if (!strcmp(opt, "no_wdelay")) + clearflags(NFSEXP_GATHERED_WRITES, active, ep); + else if (strcmp(opt, "root_squash") == 0) + setflags(NFSEXP_ROOTSQUASH, active, ep); + else if (!strcmp(opt, "no_root_squash")) + clearflags(NFSEXP_ROOTSQUASH, active, ep); + else if (strcmp(opt, "all_squash") == 0) + setflags(NFSEXP_ALLSQUASH, active, ep); + else if (strcmp(opt, "no_all_squash") == 0) + clearflags(NFSEXP_ALLSQUASH, active, ep); + else if (strcmp(opt, "subtree_check") == 0) { + had_subtree_opt = 1; + clearflags(NFSEXP_NOSUBTREECHECK, active, ep); + } else if (strcmp(opt, "no_subtree_check") == 0) { + had_subtree_opt = 1; + setflags(NFSEXP_NOSUBTREECHECK, active, ep); + } else if (strcmp(opt, "auth_nlm") == 0) + clearflags(NFSEXP_NOAUTHNLM, active, ep); + else if (strcmp(opt, "no_auth_nlm") == 0) + setflags(NFSEXP_NOAUTHNLM, active, ep); + else if (strcmp(opt, "secure_locks") == 0) + clearflags(NFSEXP_NOAUTHNLM, active, ep); + else if (strcmp(opt, "insecure_locks") == 0) + setflags(NFSEXP_NOAUTHNLM, active, ep); + else if (strcmp(opt, "acl") == 0) + clearflags(NFSEXP_NOACL, active, ep); + else if (strcmp(opt, "no_acl") == 0) + setflags(NFSEXP_NOACL, active, ep); + else if (!strcmp(opt, "pnfs")) + setflags(NFSEXP_PNFS, active, ep); + else if (!strcmp(opt, "no_pnfs")) + clearflags(NFSEXP_PNFS, active, ep); + else if (strncmp(opt, "anonuid=", 8) == 0) { + char *oe; + ep->e_anonuid = strtol(opt+8, &oe, 10); + if (opt[8]=='\0' || *oe != '\0') { + xlog(L_ERROR, "%s: %d: bad anonuid \"%s\"\n", + flname, flline, opt); +bad_option: + free(opt); + return -1; + } + } else if (strncmp(opt, "anongid=", 8) == 0) { + char *oe; + ep->e_anongid = strtol(opt+8, &oe, 10); + if (opt[8]=='\0' || *oe != '\0') { + xlog(L_ERROR, "%s: %d: bad anongid \"%s\"\n", + flname, flline, opt); + goto bad_option; + } + } else if (strncmp(opt, "squash_uids=", 12) == 0) { + if (parsesquash(opt+12, &squids, &nsquids, &cp) < 0) { + goto bad_option; + } + } else if (strncmp(opt, "squash_gids=", 12) == 0) { + if (parsesquash(opt+12, &sqgids, &nsqgids, &cp) < 0) { + goto bad_option; + } + } else if (strncmp(opt, "fsid=", 5) == 0) { + char *oe; + + if (saw_reexport) { + xlog(L_ERROR, "%s:%d: 'fsid=' has to be before 'reexport=' %s\n", + flname, flline, opt); + goto bad_option; + } + + if (strcmp(opt+5, "root") == 0) { + ep->e_fsid = 0; + setflags(NFSEXP_FSID, active, ep); + } else { + ep->e_fsid = strtoul(opt+5, &oe, 0); + if (opt[5]!='\0' && *oe == '\0') + setflags(NFSEXP_FSID, active, ep); + else if (valid_uuid(opt+5)) + ep->e_uuid = strdup(opt+5); + else { + xlog(L_ERROR, "%s: %d: bad fsid \"%s\"\n", + flname, flline, opt); + goto bad_option; + } + } + } else if (strcmp(opt, "mountpoint")==0 || + strcmp(opt, "mp") == 0 || + strncmp(opt, "mountpoint=", 11)==0 || + strncmp(opt, "mp=", 3) == 0) { + char * mp = strchr(opt, '='); + if (mp) + ep->e_mountpoint = strdup(mp+1); + else + ep->e_mountpoint = strdup(""); +#ifdef DEBUG + } else if (strncmp(opt, "fsloc=", 6) == 0) { + if (strcmp(opt+6, "stub") == 0) + ep->e_fslocmethod = FSLOC_STUB; + else { + xlog(L_ERROR, "%s:%d: bad option %s\n", + flname, flline, opt); + goto bad_option; + } +#endif + } else if (strncmp(opt, "refer=", 6) == 0) { + ep->e_fslocmethod = FSLOC_REFER; + ep->e_fslocdata = strdup(opt+6); + } else if (strncmp(opt, "replicas=", 9) == 0) { + ep->e_fslocmethod = FSLOC_REPLICA; + ep->e_fslocdata = strdup(opt+9); + } else if (strncmp(opt, "sec=", 4) == 0) { + active = parse_flavors(opt+4, ep); + if (!active) + goto bad_option; + } else if (strncmp(opt, "xprtsec=", 8) == 0) { + if (!parse_xprtsec(opt + 8, ep)) + goto bad_option; + } else if (strncmp(opt, "reexport=", 9) == 0) { + char *strategy = strchr(opt, '='); + + if (!strategy) { + xlog(L_ERROR, "%s:%d: bad option %s\n", + flname, flline, opt); + goto bad_option; + } + strategy++; + + if (saw_reexport) { + xlog(L_ERROR, "%s:%d: only one 'reexport=' is allowed%s\n", + flname, flline, opt); + goto bad_option; + } + + if (strcmp(strategy, "auto-fsidnum") == 0) { + ep->e_reexport = REEXP_AUTO_FSIDNUM; + } else if (strcmp(strategy, "predefined-fsidnum") == 0) { + ep->e_reexport = REEXP_PREDEFINED_FSIDNUM; + } else if (strcmp(strategy, "none") == 0) { + ep->e_reexport = REEXP_NONE; + } else { + xlog(L_ERROR, "%s:%d: bad option %s\n", + flname, flline, strategy); + goto bad_option; + } + + if (reexpdb_apply_reexport_settings(ep, flname, flline) != 0) + goto bad_option; + + if (ep->e_fsid) + setflags(NFSEXP_FSID, active, ep); + + saw_reexport = 1; + } else { + xlog(L_ERROR, "%s:%d: unknown keyword \"%s\"\n", + flname, flline, opt); + setflags(NFSEXP_ALLSQUASH | NFSEXP_READONLY, active, ep); + goto bad_option; + } + free(opt); + while (isblank(*cp)) + cp++; + } + + fix_pseudoflavor_flags(ep); + ep->e_squids = squids; + ep->e_sqgids = sqgids; + ep->e_nsquids = nsquids; + ep->e_nsqgids = nsqgids; + +out: + if (warn && !had_subtree_opt) + xlog(L_WARNING, "%s [%d]: Neither 'subtree_check' or 'no_subtree_check' specified for export \"%s:%s\".\n" + " Assuming default behaviour ('no_subtree_check').\n" + " NOTE: this default has changed since nfs-utils version 1.0.x\n", + + flname, flline, + ep->e_hostname, ep->e_path); + if (had_subtree_opt_ptr) + *had_subtree_opt_ptr = had_subtree_opt; + + return 1; +} + +static int +parsesquash(char *list, int **idp, int *lenp, char **ep) +{ + char *cp = list; + int id0, id1; + int len = *lenp; + int *id = *idp; + + if (**ep) + *--(*ep) = ','; + + do { + id0 = parsenum(&cp); + if (*cp == '-') { + cp++; + id1 = parsenum(&cp); + } else { + id1 = id0; + } + if (id0 == -1 || id1 == -1) { + syntaxerr("uid/gid -1 not permitted"); + xfree(id); + return -1; + } + if ((len % 8) == 0) + id = (int *) xrealloc(id, (len + 8) * sizeof(*id)); + id[len++] = id0; + id[len++] = id1; + if (!*cp || *cp == ')' || (*cp == ',' && !isdigit(cp[1]))) + break; + if (*cp != ',') { + syntaxerr("bad uid/gid list"); + xfree(id); + return -1; + } + cp++; + } while(1); + + if (**ep == ',') (*ep)++; + + *lenp = len; + *idp = id; + return 1; +} + +static void +freesquash(void) +{ + if (squids) { + xfree (squids); + squids = NULL; + nsquids = 0; + } + if (sqgids) { + xfree (sqgids); + sqgids = NULL; + nsqgids = 0; + } +} + +static int +parsenum(char **cpp) +{ + char *cp = *cpp, c; + int num = 0; + + if (**cpp == '-') + (*cpp)++; + while (isdigit(**cpp)) + (*cpp)++; + c = **cpp; **cpp = '\0'; num = atoi(cp); **cpp = c; + return num; +} + +static int +getpath(char *path, int len) +{ + xskip(efp, " \t\n"); + return xgettok(efp, 0, path, len); +} + +static int +getexport(char *exp, int len) +{ + int ok; + + xskip(efp, " \t"); + if ((ok = xgettok(efp, 0, exp, len)) < 0) + xlog(L_ERROR, "%s:%d: syntax error", + efname?"command line":efname, efp->x_line); + return ok; +} + +static void +syntaxerr(char *msg) +{ + xlog(L_ERROR, "%s:%d: syntax error: %s", + efname, efp?efp->x_line:0, msg); +} +struct export_features *get_export_features(void) +{ + static char *path = "/proc/fs/nfsd/export_features"; + static struct export_features ef; + static int cached = 0; + char buf[50]; + int c; + int fd; + + if (cached) + return &ef; + + ef.flags = NFSEXP_OLDFLAGS; + ef.secinfo_flags = NFSEXP_OLD_SECINFO_FLAGS; + + fd = open(path, O_RDONLY); + if (fd == -1) + goto good; + c = read(fd, buf, 50); + close(fd); + if (c == -1) + goto err; + buf[c] = 0; + c = sscanf(buf, "%x %x", &ef.flags, &ef.secinfo_flags); + if (c != 2) + goto err; +good: + cached = 1; + return &ef; +err: + xlog(L_WARNING, "unexpected error reading %s", path); + return &ef; +} diff --git a/support/nfs/getport.c b/support/nfs/getport.c new file mode 100644 index 0000000..813f7bf --- /dev/null +++ b/support/nfs/getport.c @@ -0,0 +1,1127 @@ +/* + * Provide a variety of APIs that query an rpcbind daemon to + * discover RPC service ports and allowed protocol version + * numbers. + * + * Copyright (C) 2008 Oracle Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <sys/time.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> + +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> + +#ifdef HAVE_LIBTIRPC +#include <netconfig.h> +#include <rpc/rpcb_prot.h> +#endif + +#include "sockaddr.h" +#include "nfsrpc.h" +#include "nfslib.h" + +/* + * Try a local socket first to access the local rpcbind daemon + * + * Rpcbind's local socket service does not seem to be working. + * Disable this logic for now. + */ +#ifdef HAVE_LIBTIRPC +#undef NFS_GP_LOCAL +#else /* !HAVE_LIBTIRPC */ +#undef NFS_GP_LOCAL +#endif /* !HAVE_LIBTIRPC */ + +#ifdef HAVE_LIBTIRPC +static const rpcvers_t default_rpcb_version = RPCBVERS_4; +#else /* !HAVE_LIBTIRPC */ +static const rpcvers_t default_rpcb_version = PMAPVERS; +#endif /* !HAVE_LIBTIRPC */ + +/* + * Historical: Map TCP connect timeouts to timeout + * error code used by UDP. + */ +static void +nfs_gp_map_tcp_errorcodes(const unsigned short protocol) +{ + if (protocol != IPPROTO_TCP) + return; + + switch (rpc_createerr.cf_error.re_errno) { + case ETIMEDOUT: + rpc_createerr.cf_stat = RPC_TIMEDOUT; + break; + case ECONNREFUSED: + rpc_createerr.cf_stat = RPC_CANTRECV; + break; + } +} + +/* + * There's no easy way to tell how the local system's networking + * and rpcbind is configured (ie. whether we want to use IPv6 or + * IPv4 loopback to contact RPC services on the local host). We + * punt and simply try to look up "localhost". + * + * Returns TRUE on success. + */ +static int nfs_gp_loopback_address(struct sockaddr *sap, socklen_t *salen) +{ + struct addrinfo *gai_results; + int ret = 0; + + if (getaddrinfo("localhost", NULL, NULL, &gai_results)) + return 0; + + if (*salen >= gai_results->ai_addrlen) { + memcpy(sap, gai_results->ai_addr, + gai_results->ai_addrlen); + *salen = gai_results->ai_addrlen; + ret = 1; + } + + nfs_freeaddrinfo(gai_results); + return ret; +} + +/* + * Look up a network service in /etc/services and return the + * network-order port number of that service. + */ +static in_port_t nfs_gp_getservbyname(const char *service, + const unsigned short protocol) +{ + const struct addrinfo gai_hint = { + .ai_family = AF_INET, + .ai_protocol = protocol, + .ai_flags = AI_PASSIVE, + }; + struct addrinfo *gai_results; + const struct sockaddr_in *sin; + in_port_t port; + + if (getaddrinfo(NULL, service, &gai_hint, &gai_results) != 0) + return 0; + + sin = (const struct sockaddr_in *)gai_results->ai_addr; + port = sin->sin_port; + + nfs_freeaddrinfo(gai_results); + return port; +} + +/* + * Discover the port number that should be used to contact an + * rpcbind service. This will detect if the port has a local + * value that may have been set in /etc/services. + * + * Returns network byte-order port number of rpcbind service + * on this system. + */ +static in_port_t nfs_gp_get_rpcb_port(const unsigned short protocol) +{ + static const char *rpcb_netnametbl[] = { + "rpcbind", + "portmapper", + "sunrpc", + NULL, + }; + unsigned int i; + + for (i = 0; rpcb_netnametbl[i] != NULL; i++) { + in_port_t port; + + port = nfs_gp_getservbyname(rpcb_netnametbl[i], protocol); + if (port != 0) + return port; + } + + return (in_port_t)htons((uint16_t)PMAPPORT); +} + +/* + * Set up an RPC client for communicating with an rpcbind daemon at + * @sap over @transport with protocol version @version. + * + * Returns a pointer to a prepared RPC client if successful, and + * @timeout is initialized; caller must destroy a non-NULL returned RPC + * client. Otherwise returns NULL, and rpc_createerr.cf_stat is set to + * reflect the error. + */ +static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr *sap, + const socklen_t salen, + const unsigned short transport, + const rpcvers_t version, + struct timeval *timeout) +{ + static const char *rpcb_pgmtbl[] = { + "rpcbind", + "portmap", + "portmapper", + "sunrpc", + NULL, + }; + rpcprog_t rpcb_prog = nfs_getrpcbyname(RPCBPROG, rpcb_pgmtbl); + CLIENT *clnt; + + nfs_set_port(sap, ntohs(nfs_gp_get_rpcb_port(transport))); + clnt = nfs_get_rpcclient(sap, salen, transport, rpcb_prog, + version, timeout); + nfs_gp_map_tcp_errorcodes(transport); + return clnt; +} + +/** + * nfs_get_proto - Convert a netid to an address family and protocol number + * @netid: C string containing a netid + * @family: OUT: address family + * @protocol: OUT: protocol number + * + * Returns 1 and fills in @protocol if the netid was recognized; + * otherwise zero is returned. + */ +#ifdef HAVE_LIBTIRPC +int +nfs_get_proto(const char *netid, sa_family_t *family, unsigned long *protocol) +{ + struct netconfig *nconf; + struct protoent *proto; + + /* + * IANA does not define a protocol number for rdma netids, + * since "rdma" is not an IP protocol. + */ + if (strcmp(netid, "rdma") == 0) { + *family = AF_INET; + *protocol = NFSPROTO_RDMA; + return 1; + } + if (strcmp(netid, "rdma6") == 0) { + *family = AF_INET6; + *protocol = NFSPROTO_RDMA; + return 1; + } + + nconf = getnetconfigent(netid); + if (nconf == NULL) + return 0; + + proto = getprotobyname(nconf->nc_proto); + if (proto == NULL) { + freenetconfigent(nconf); + return 0; + } + + *family = AF_UNSPEC; + if (strcmp(nconf->nc_protofmly, NC_INET) == 0) + *family = AF_INET; + if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) + *family = AF_INET6; + freenetconfigent(nconf); + + *protocol = (unsigned long)proto->p_proto; + return 1; +} +#else /* !HAVE_LIBTIRPC */ +int +nfs_get_proto(const char *netid, sa_family_t *family, unsigned long *protocol) +{ + struct protoent *proto; + + /* + * IANA does not define a protocol number for rdma netids, + * since "rdma" is not an IP protocol. + */ + if (strcmp(netid, "rdma") == 0) { + *family = AF_INET; + *protocol = NFSPROTO_RDMA; + return 1; + } + + proto = getprotobyname(netid); + if (proto == NULL) + return 0; + + *family = AF_INET; + *protocol = (unsigned long)proto->p_proto; + return 1; +} +#endif /* !HAVE_LIBTIRPC */ + +/** + * nfs_get_netid - Convert a protocol family and protocol name to a netid + * @family: protocol family + * @protocol: protocol number + * + * One of the arguments passed when querying remote rpcbind services + * via rpcbind v3 or v4 is a netid string. This replaces the pm_prot + * field used in legacy PMAP_GETPORT calls. + * + * RFC 1833 says netids are not standard but rather defined on the local + * host. There are, however, standard definitions for nc_protofmly and + * nc_proto that can be used to derive a netid string on the local host, + * based on the contents of /etc/netconfig. + * + * Walk through the local netconfig database and grab the netid of the + * first entry that matches @family and @protocol and whose netid string + * fits in the provided buffer. + * + * Returns a '\0'-terminated string if successful. Caller must + * free the returned string. Otherwise NULL is returned, and + * rpc_createerr.cf_stat is set to reflect the error. + */ +#ifdef HAVE_LIBTIRPC +char *nfs_get_netid(const sa_family_t family, const unsigned long protocol) +{ + char *nc_protofmly, *nc_proto, *nc_netid; + struct netconfig *nconf; + struct protoent *proto; + void *handle; + + switch (family) { + case AF_LOCAL: + case AF_INET: + nc_protofmly = NC_INET; + break; + case AF_INET6: + nc_protofmly = NC_INET6; + break; + default: + goto out; + } + + proto = getprotobynumber(protocol); + if (proto == NULL) + goto out; + nc_proto = proto->p_name; + + handle = setnetconfig(); + while ((nconf = getnetconfig(handle)) != NULL) { + + if (nconf->nc_protofmly != NULL && + strcmp(nconf->nc_protofmly, nc_protofmly) != 0) + continue; + if (nconf->nc_proto != NULL && + strcmp(nconf->nc_proto, nc_proto) != 0) + continue; + + nc_netid = strdup(nconf->nc_netid); + endnetconfig(handle); + + if (nc_netid == NULL) + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + return nc_netid; + } + endnetconfig(handle); + +out: + rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; + return NULL; +} +#else /* !HAVE_LIBTIRPC */ +char *nfs_get_netid(const sa_family_t family, const unsigned long protocol) +{ + struct protoent *proto; + char *netid; + + if (family != AF_INET) + goto out; + proto = getprotobynumber((int)protocol); + if (proto == NULL) + goto out; + + netid = strdup(proto->p_name); + if (netid == NULL) + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + return netid; + +out: + rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; + return NULL; +} +#endif /* !HAVE_LIBTIRPC */ + +/* + * Extract a port number from a universal address, and terminate the + * string in @addrstr just after the address part. + * + * Returns -1 if unsuccesful; otherwise a decoded port number (possibly 0) + * is returned. + */ +static int nfs_gp_universal_porthelper(char *addrstr) +{ + char *p, *endptr; + unsigned long portlo, porthi; + int port = -1; + + p = strrchr(addrstr, '.'); + if (p == NULL) + goto out; + portlo = strtoul(p + 1, &endptr, 10); + if (*endptr != '\0' || portlo > 255) + goto out; + *p = '\0'; + + p = strrchr(addrstr, '.'); + if (p == NULL) + goto out; + porthi = strtoul(p + 1, &endptr, 10); + if (*endptr != '\0' || porthi > 255) + goto out; + *p = '\0'; + port = (porthi << 8) | portlo; + +out: + return port; +} + +/** + * nfs_universal2port - extract port number from a "universal address" + * @uaddr: '\0'-terminated C string containing a universal address + * + * Universal addresses (defined in RFC 1833) are used when calling an + * rpcbind daemon via protocol versions 3 or 4.. + * + * Returns -1 if unsuccesful; otherwise a decoded port number (possibly 0) + * is returned. + */ +int nfs_universal2port(const char *uaddr) +{ + char *addrstr; + int port = -1; + + addrstr = strdup(uaddr); + if (addrstr != NULL) { + port = nfs_gp_universal_porthelper(addrstr); + free(addrstr); + } + return port; +} + +/** + * nfs_sockaddr2universal - convert a sockaddr to a "universal address" + * @sap: pointer to a socket address + * + * Universal addresses (defined in RFC 1833) are used when calling an + * rpcbind daemon via protocol versions 3 or 4.. + * + * Returns a '\0'-terminated string if successful; caller must free + * the returned string. Otherwise NULL is returned and + * rpc_createerr.cf_stat is set to reflect the error. + * + * inet_ntop(3) is used here, since getnameinfo(3) is not available + * in some earlier glibc releases, and we don't require support for + * scope IDs for universal addresses. + */ +char *nfs_sockaddr2universal(const struct sockaddr *sap) +{ + const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sap; + const struct sockaddr_un *sun = (const struct sockaddr_un *)sap; + const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; + char buf[INET6_ADDRSTRLEN + 8 /* for port information */]; + uint16_t port; + size_t count; + char *result; + int len; + + switch (sap->sa_family) { + case AF_LOCAL: + return strndup(sun->sun_path, sizeof(sun->sun_path)); + case AF_INET: + if (inet_ntop(AF_INET, (const void *)&sin->sin_addr.s_addr, + buf, (socklen_t)sizeof(buf)) == NULL) + goto out_err; + port = ntohs(sin->sin_port); + break; + case AF_INET6: + if (inet_ntop(AF_INET6, (const void *)&sin6->sin6_addr, + buf, (socklen_t)sizeof(buf)) == NULL) + goto out_err; + port = ntohs(sin6->sin6_port); + break; + default: + goto out_err; + } + + count = sizeof(buf) - strlen(buf); + len = snprintf(buf + strlen(buf), count, ".%u.%u", + (unsigned)(port >> 8), (unsigned)(port & 0xff)); + /* before glibc 2.0.6, snprintf(3) could return -1 */ + if (len < 0 || (size_t)len > count) + goto out_err; + + result = strdup(buf); + if (result != NULL) + return result; + +out_err: + rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE; + return NULL; +} + +/* + * Send a NULL request to the indicated RPC service. + * + * Returns 1 if the service responded; otherwise 0; + */ +static int nfs_gp_ping(CLIENT *client, struct timeval timeout) +{ + enum clnt_stat status; + + status = CLNT_CALL(client, NULLPROC, + (xdrproc_t)xdr_void, NULL, + (xdrproc_t)xdr_void, NULL, + timeout); + + if (status != RPC_SUCCESS) { + rpc_createerr.cf_stat = status; + CLNT_GETERR(client, &rpc_createerr.cf_error); + } + return (int)(status == RPC_SUCCESS); +} + +#ifdef HAVE_LIBTIRPC + +/* + * Initialize the rpcb argument for a GETADDR request. + * + * Returns 1 if successful, and caller must free strings pointed + * to by r_netid and r_addr; otherwise 0. + */ +static int nfs_gp_init_rpcb_parms(const struct sockaddr *sap, + const rpcprog_t program, + const rpcvers_t version, + const unsigned short protocol, + struct rpcb *parms) +{ + char *netid, *addr; + + netid = nfs_get_netid(sap->sa_family, protocol); + if (netid == NULL) + return 0; + + addr = nfs_sockaddr2universal(sap); + if (addr == NULL) { + free(netid); + return 0; + } + + memset(parms, 0, sizeof(*parms)); + parms->r_prog = program; + parms->r_vers = version; + parms->r_netid = netid; + parms->r_addr = addr; + parms->r_owner = ""; + + return 1; +} + +static void nfs_gp_free_rpcb_parms(struct rpcb *parms) +{ + free(parms->r_netid); + free(parms->r_addr); +} + +/* + * Try rpcbind GETADDR via version 4. If that fails, try same + * request via version 3. + * + * Returns non-zero port number on success; otherwise returns + * zero. rpccreateerr is set to reflect the nature of the error. + */ +static unsigned short nfs_gp_rpcb_getaddr(CLIENT *client, + struct rpcb *parms, + struct timeval timeout) +{ + rpcvers_t rpcb_version; + struct rpc_err rpcerr; + int port = 0; + + for (rpcb_version = RPCBVERS_4; + rpcb_version >= RPCBVERS_3; + rpcb_version--) { + enum clnt_stat status; + char *uaddr = NULL; + + CLNT_CONTROL(client, CLSET_VERS, (void *)&rpcb_version); + status = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDR, + (xdrproc_t)xdr_rpcb, (void *)parms, + (xdrproc_t)xdr_wrapstring, (void *)&uaddr, + timeout); + + switch (status) { + case RPC_SUCCESS: + if ((uaddr == NULL) || (uaddr[0] == '\0')) { + rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; + return 0; + } + + port = nfs_universal2port(uaddr); + xdr_free((xdrproc_t)xdr_wrapstring, (char *)&uaddr); + if (port == -1) { + rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE; + return 0; + } + return (unsigned short)port; + case RPC_PROGVERSMISMATCH: + clnt_geterr(client, &rpcerr); + if (rpcerr.re_vers.low > RPCBVERS4) + return 0; + continue; + case RPC_PROCUNAVAIL: + case RPC_PROGUNAVAIL: + continue; + default: + /* Most likely RPC_TIMEDOUT or RPC_CANTRECV */ + rpc_createerr.cf_stat = status; + clnt_geterr(client, &rpc_createerr.cf_error); + return 0; + } + + } + + if (port == 0) { + rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; + clnt_geterr(client, &rpc_createerr.cf_error); + } + return port; +} + +#endif /* HAVE_LIBTIRPC */ + +/* + * Try GETPORT request via rpcbind version 2. + * + * Returns non-zero port number on success; otherwise returns + * zero. rpccreateerr is set to reflect the nature of the error. + */ +static unsigned long nfs_gp_pmap_getport(CLIENT *client, + struct pmap *parms, + struct timeval timeout) +{ + enum clnt_stat status; + unsigned long port; + + status = CLNT_CALL(client, (rpcproc_t)PMAPPROC_GETPORT, + (xdrproc_t)xdr_pmap, (void *)parms, + (xdrproc_t)xdr_u_long, (void *)&port, + timeout); + + if (status != RPC_SUCCESS) { + rpc_createerr.cf_stat = status; + CLNT_GETERR(client, &rpc_createerr.cf_error); + port = 0; + } else if (port == 0) + rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; + + return port; +} + +#ifdef HAVE_LIBTIRPC + +static unsigned short nfs_gp_getport_rpcb(CLIENT *client, + const struct sockaddr *sap, + const rpcprog_t program, + const rpcvers_t version, + const unsigned short protocol, + struct timeval timeout) +{ + unsigned short port = 0; + struct rpcb parms; + + if (nfs_gp_init_rpcb_parms(sap, program, version, + protocol, &parms) != 0) { + port = nfs_gp_rpcb_getaddr(client, &parms, timeout); + nfs_gp_free_rpcb_parms(&parms); + } + + return port; +} + +#endif /* HAVE_LIBTIRPC */ + +static unsigned long nfs_gp_getport_pmap(CLIENT *client, + const rpcprog_t program, + const rpcvers_t version, + const unsigned short protocol, + struct timeval timeout) +{ + struct pmap parms = { + .pm_prog = program, + .pm_vers = version, + .pm_prot = protocol, + }; + rpcvers_t pmap_version = PMAPVERS; + + CLNT_CONTROL(client, CLSET_VERS, (void *)&pmap_version); + return nfs_gp_pmap_getport(client, &parms, timeout); +} + +/* + * Try an AF_INET6 request via rpcbind v4/v3; try an AF_INET + * request via rpcbind v2. + * + * Returns non-zero port number on success; otherwise returns + * zero. rpccreateerr is set to reflect the nature of the error. + */ +static unsigned short nfs_gp_getport(CLIENT *client, + const struct sockaddr *sap, + const rpcprog_t program, + const rpcvers_t version, + const unsigned short protocol, + struct timeval timeout) +{ + switch (sap->sa_family) { +#ifdef HAVE_LIBTIRPC + case AF_INET6: + return nfs_gp_getport_rpcb(client, sap, program, + version, protocol, timeout); +#endif /* HAVE_LIBTIRPC */ + case AF_INET: + return nfs_gp_getport_pmap(client, program, version, + protocol, timeout); + } + + rpc_createerr.cf_stat = RPC_UNKNOWNADDR; + return 0; +} + +/** + * nfs_rpc_ping - Determine if RPC service is responding to requests + * @sap: pointer to address of server to query (port is already filled in) + * @salen: length of server address + * @program: requested RPC program number + * @version: requested RPC version number + * @protocol: requested IPPROTO_ value of transport protocol + * @timeout: pointer to request timeout (NULL means use default timeout) + * + * Returns 1 if the remote service responded without an error; otherwise + * zero. + */ +int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen, + const rpcprog_t program, const rpcvers_t version, + const unsigned short protocol, const struct timeval *timeout) +{ + union nfs_sockaddr address; + struct sockaddr *saddr = &address.sa; + CLIENT *client; + struct timeval tout = { -1, 0 }; + int result = 0; + + if (timeout != NULL) + tout = *timeout; + + nfs_clear_rpc_createerr(); + + memcpy(saddr, sap, (size_t)salen); + client = nfs_get_rpcclient(saddr, salen, protocol, + program, version, &tout); + if (client != NULL) { + result = nfs_gp_ping(client, tout); + nfs_gp_map_tcp_errorcodes(protocol); + CLNT_DESTROY(client); + } + + return result; +} + +/** + * nfs_getport - query server's rpcbind to get port number for an RPC service + * @sap: pointer to address of server to query + * @salen: length of server's address + * @program: requested RPC program number + * @version: requested RPC version number + * @protocol: IPPROTO_ value of requested transport protocol + * + * Uses any acceptable rpcbind version to discover the port number for the + * RPC service described by the given [program, version, transport] tuple. + * Uses a quick timeout and an ephemeral source port. Supports AF_INET and + * AF_INET6 server addresses. + * + * Returns a positive integer representing the port number of the RPC + * service advertised by the server (in host byte order), or zero if the + * service is not advertised or there was some problem querying the server's + * rpcbind daemon. rpccreateerr is set to reflect the underlying cause of + * the error. + * + * There are a variety of ways to choose which transport and rpcbind versions + * to use. We chose to conserve local resources and try to avoid incurring + * timeouts. + * + * Transport + * To provide rudimentary support for traversing firewalls, query the remote + * using the same transport as the requested service. This provides some + * guarantee that the requested transport is available between this client + * and the server, and if the caller specifically requests TCP, for example, + * this may be becuase a firewall is in place that blocks UDP traffic. We + * could try both, but that could involve a lengthy timeout in several cases, + * and would often consume an extra ephemeral port. + * + * Rpcbind version + * To avoid using up too many ephemeral ports, AF_INET queries use tried-and- + * true rpcbindv2, and don't try the newer versions; and AF_INET6 queries use + * rpcbindv4, then rpcbindv3 on the same socket. The newer rpcbind protocol + * versions can adequately detect if a remote RPC service does not support + * AF_INET6 at all. The rpcbind socket is re-used in an attempt to keep the + * overall number of consumed ephemeral ports low. + */ +unsigned short nfs_getport(const struct sockaddr *sap, + const socklen_t salen, + const rpcprog_t program, + const rpcvers_t version, + const unsigned short protocol) +{ + union nfs_sockaddr address; + struct sockaddr *saddr = &address.sa; + struct timeval timeout = { -1, 0 }; + unsigned short port = 0; + CLIENT *client; + + nfs_clear_rpc_createerr(); + + memcpy(saddr, sap, (size_t)salen); + client = nfs_gp_get_rpcbclient(saddr, salen, protocol, + default_rpcb_version, &timeout); + if (client != NULL) { + port = nfs_gp_getport(client, saddr, program, + version, protocol, timeout); + CLNT_DESTROY(client); + } + + return port; +} + +/** + * nfs_getport_ping - query server's rpcbind and do RPC ping to verify result + * @sap: IN: pointer to address of server to query; + * OUT: pointer to updated address + * @salen: length of server's address + * @program: requested RPC program number + * @version: requested RPC version number + * @protocol: IPPROTO_ value of requested transport protocol + * + * Uses any acceptable rpcbind version to discover the port number for the + * RPC service described by the given [program, version, transport] tuple. + * Uses a quick timeout and an ephemeral source port. Supports AF_INET and + * AF_INET6 server addresses. + * + * Returns a 1 and sets the port number in the passed-in server address + * if both the query and the ping were successful; otherwise zero. + * rpccreateerr is set to reflect the underlying cause of the error. + */ +int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen, + const rpcprog_t program, const rpcvers_t version, + const unsigned short protocol) +{ + struct timeval timeout = { -1, 0 }; + unsigned short port = 0; + CLIENT *client; + int result = 0; + + nfs_clear_rpc_createerr(); + + client = nfs_gp_get_rpcbclient(sap, salen, protocol, + default_rpcb_version, &timeout); + if (client != NULL) { + port = nfs_gp_getport(client, sap, program, + version, protocol, timeout); + CLNT_DESTROY(client); + client = NULL; + } + + if (port != 0) { + union nfs_sockaddr address; + struct sockaddr *saddr = &address.sa; + + memcpy(saddr, sap, (size_t)salen); + nfs_set_port(saddr, port); + + nfs_clear_rpc_createerr(); + + client = nfs_get_rpcclient(saddr, salen, protocol, + program, version, &timeout); + if (client != NULL) { + result = nfs_gp_ping(client, timeout); + nfs_gp_map_tcp_errorcodes(protocol); + CLNT_DESTROY(client); + } + } + + if (result) + nfs_set_port(sap, port); + + return result; +} + +/** + * nfs_getlocalport - query local rpcbind to get port number for an RPC service + * @program: requested RPC program number + * @version: requested RPC version number + * @protocol: IPPROTO_ value of requested transport protocol + * + * Uses any acceptable rpcbind version to discover the port number for the + * RPC service described by the given [program, version, transport] tuple. + * Uses a quick timeout and an ephemeral source port. Supports AF_INET and + * AF_INET6 local addresses. + * + * Returns a positive integer representing the port number of the RPC + * service advertised by the server (in host byte order), or zero if the + * service is not advertised or there was some problem querying the server's + * rpcbind daemon. rpccreateerr is set to reflect the underlying cause of + * the error. + * + * Try an AF_LOCAL connection first. The rpcbind daemon implementation should + * listen on AF_LOCAL. + * + * If that doesn't work (for example, if portmapper is running, or rpcbind + * isn't listening on /run/rpcbind.sock), send a query via UDP to localhost + * (UDP doesn't leave a socket in TIME_WAIT, and the timeout is a relatively + * short 3 seconds). + */ +unsigned short nfs_getlocalport(const rpcprot_t program, + const rpcvers_t version, + const unsigned short protocol) +{ + union nfs_sockaddr address; + struct sockaddr *lb_addr = &address.sa; + socklen_t lb_len = sizeof(*lb_addr); + unsigned short port = 0; + +#ifdef NFS_GP_LOCAL + const struct sockaddr_un sun = { + .sun_family = AF_LOCAL, + .sun_path = _PATH_RPCBINDSOCK, + }; + const struct sockaddr *sap = (struct sockaddr *)&sun; + const socklen_t salen = SUN_LEN(&sun); + CLIENT *client; + struct timeval timeout = { -1, 0 }; + + nfs_clear_rpc_createerr(); + + client = nfs_gp_get_rpcbclient(sap, salen, 0, RPCBVERS_4, &timeout); + if (client != NULL) { + struct rpcb parms; + + if (nfs_gp_init_rpcb_parms(sap, program, version, + protocol, &parms) != 0) { + port = nfs_gp_rpcb_getaddr(client, &parms, timeout); + nfs_gp_free_rpcb_parms(&parms); + } + CLNT_DESTROY(client); + } +#endif /* NFS_GP_LOCAL */ + + if (port == 0) { + nfs_clear_rpc_createerr(); + + if (nfs_gp_loopback_address(lb_addr, &lb_len)) { + port = nfs_getport(lb_addr, lb_len, + program, version, protocol); + } else + rpc_createerr.cf_stat = RPC_UNKNOWNADDR; + } + + return port; +} + +/** + * nfs_rpcb_getaddr - query rpcbind via rpcbind versions 4 and 3 + * @sap: pointer to address of server to query + * @salen: length of server address + * @transport: transport protocol to use for the query + * @addr: pointer to r_addr address + * @program: requested RPC program number + * @version: requested RPC version number + * @protocol: requested IPPROTO_ value of transport protocol + * @timeout: pointer to request timeout (NULL means use default timeout) + * + * Returns a positive integer representing the port number of the RPC + * service advertised by the server (in host byte order), or zero if the + * service is not advertised or there was some problem querying the + * server's rpcbind daemon. rpccreateerr is set to reflect the + * underlying cause of the error. + * + * This function provides similar functionality to nfs_pmap_getport(), + * but performs the rpcbind lookup via rpcbind version 4. If the server + * doesn't support rpcbind version 4, it will retry with version 3. + * The GETADDR procedure is exactly the same in these two versions of + * the rpcbind protocol, so the socket, RPC client, and arguments are + * re-used when retrying, saving ephemeral port space. + * + * These RPC procedures take a universal address as an argument, so the + * query will fail if the remote rpcbind daemon doesn't find an entry + * with a matching address. A matching address includes an ANYADDR + * address of the same address family. In this way an RPC server can + * advertise via rpcbind that it does not support AF_INET6. + */ +#ifdef HAVE_LIBTIRPC + +unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap, + const socklen_t salen, + const unsigned short transport, + const struct sockaddr *addr, + const rpcprog_t program, + const rpcvers_t version, + const unsigned short protocol, + const struct timeval *timeout) +{ + union nfs_sockaddr address; + struct sockaddr *saddr = &address.sa; + CLIENT *client; + struct rpcb parms; + struct timeval tout = { -1, 0 }; + unsigned short port = 0; + + if (timeout != NULL) + tout = *timeout; + + nfs_clear_rpc_createerr(); + + memcpy(saddr, sap, (size_t)salen); + client = nfs_gp_get_rpcbclient(saddr, salen, transport, + RPCBVERS_4, &tout); + if (client != NULL) { + if (nfs_gp_init_rpcb_parms(addr, program, version, + protocol, &parms) != 0) { + port = nfs_gp_rpcb_getaddr(client, &parms, tout); + nfs_gp_free_rpcb_parms(&parms); + } + CLNT_DESTROY(client); + } + + return port; +} + +#else /* !HAVE_LIBTIRPC */ + +unsigned short nfs_rpcb_getaddr(__attribute__((unused)) const struct sockaddr *sap, + __attribute__((unused)) const socklen_t salen, + __attribute__((unused)) const unsigned short transport, + __attribute__((unused)) const struct sockaddr *addr, + __attribute__((unused)) const rpcprog_t program, + __attribute__((unused)) const rpcvers_t version, + __attribute__((unused)) const unsigned short protocol, + __attribute__((unused)) const struct timeval *timeout) +{ + nfs_clear_rpc_createerr(); + + rpc_createerr.cf_stat = RPC_UNKNOWNADDR; + return 0; +} + +#endif /* !HAVE_LIBTIRPC */ + +/** + * nfs_pmap_getport - query rpcbind via the portmap protocol (rpcbindv2) + * @sin: pointer to AF_INET address of server to query + * @transport: transport protocol to use for the query + * @program: requested RPC program number + * @version: requested RPC version number + * @protocol: requested IPPROTO_ value of transport protocol + * @timeout: pointer to request timeout (NULL means use default timeout) + * + * Returns a positive integer representing the port number of the RPC service + * advertised by the server (in host byte order), or zero if the service is + * not advertised or there was some problem querying the server's rpcbind + * daemon. rpccreateerr is set to reflect the underlying cause of the error. + * + * nfs_pmap_getport() is very similar to pmap_getport(), except that: + * + * 1. This version always tries to use an ephemeral port, since reserved + * ports are not needed for GETPORT queries. This conserves the very + * limited reserved port space, helping reduce failed socket binds + * during mount storms. + * + * 2. This version times out quickly by default. It time-limits the + * connect process as well as the actual RPC call, and even allows the + * caller to specify the timeout. + * + * 3. This version shares code with the rpcbindv3 and rpcbindv4 query + * functions. It can use a TI-RPC generated CLIENT. + */ +unsigned long nfs_pmap_getport(const struct sockaddr_in *sin, + const unsigned short transport, + const unsigned long program, + const unsigned long version, + const unsigned long protocol, + const struct timeval *timeout) +{ + struct sockaddr_in address; + struct sockaddr *saddr = (struct sockaddr *)&address; + CLIENT *client; + struct pmap parms = { + .pm_prog = program, + .pm_vers = version, + .pm_prot = protocol, + }; + struct timeval tout = { -1, 0 }; + unsigned long port = 0; + + if (timeout != NULL) + tout = *timeout; + + nfs_clear_rpc_createerr(); + + memcpy(saddr, sin, sizeof(address)); + client = nfs_gp_get_rpcbclient(saddr, (socklen_t)sizeof(*sin), + transport, PMAPVERS, &tout); + if (client != NULL) { + port = nfs_gp_pmap_getport(client, &parms, tout); + CLNT_DESTROY(client); + } + + return port; +} + +static const char *nfs_ns_pgmtbl[] = { + "status", + NULL, +}; + +/* + * nfs_probe_statd - use nfs_pmap_getport to see if statd is running locally + * + * Returns non-zero if statd is running locally. + */ +int nfs_probe_statd(void) +{ + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_LOOPBACK), + }; + rpcprog_t program = nfs_getrpcbyname(NSMPROG, nfs_ns_pgmtbl); + + return nfs_getport_ping((struct sockaddr *)(char *)&addr, sizeof(addr), + program, (rpcvers_t)1, IPPROTO_UDP); +} diff --git a/support/nfs/mydaemon.c b/support/nfs/mydaemon.c new file mode 100644 index 0000000..d1cf08d --- /dev/null +++ b/support/nfs/mydaemon.c @@ -0,0 +1,153 @@ +/* + mydaemon.c + + Copyright (c) 2000 The Regents of the University of Michigan. + All rights reserved. + + Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>. + Copyright (c) 2002 Andy Adamson <andros@UMICH.EDU>. + Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>. + Copyright (c) 2002 J. Bruce Fields <bfields@UMICH.EDU>. + Copyright (c) 2013 Jeff Layton <jlayton@redhat.com> + + All rights reserved, all wrongs reversed. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the University nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <xlog.h> + +#include "nfslib.h" + +static int pipefds[2] = { -1, -1}; + +/** + * daemon_init - initial daemon setup + * @fg: whether to run in the foreground + * + * This function is like daemon(), but with our own special sauce to delay + * the exit of the parent until the child is set up properly. A pipe is created + * between parent and child. The parent process will wait to exit until the + * child dies or writes an int on the pipe signaling its status. + */ +void +daemon_init(bool fg) +{ + int pid, status, tempfd; + + if (fg) + return; + + if (pipe(pipefds) < 0) { + xlog_err("mydaemon: pipe() failed: errno %d (%s)\n", + errno, strerror(errno)); + exit(EXIT_FAILURE); + } + + pid = fork(); + if (pid < 0) { + xlog_err("mydaemon: fork() failed: errno %d (%s)\n", + errno, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (pid > 0) { + /* Parent */ + close(pipefds[1]); + if (read(pipefds[0], &status, sizeof(status)) != sizeof(status)) + exit(EXIT_FAILURE); + exit(status); + } + + /* Child */ + close(pipefds[0]); + setsid (); + + if (chdir ("/")) { + xlog_err("mydaemon: chdir() failed: errno %d (%s)\n", + errno, strerror(errno)); + exit(EXIT_FAILURE); + } + + while (pipefds[1] <= 2) { + pipefds[1] = dup(pipefds[1]); + if (pipefds[1] < 0) { + xlog_err("mydaemon: dup() failed: errno %d (%s)\n", + errno, strerror(errno)); + exit(EXIT_FAILURE); + } + } + + tempfd = open("/dev/null", O_RDWR); + if (tempfd < 0) { + xlog_err("mydaemon: can't open /dev/null: errno %d " + "(%s)\n", errno, strerror(errno)); + exit(EXIT_FAILURE); + } + + dup2(tempfd, 0); + dup2(tempfd, 1); + dup2(tempfd, 2); + close(tempfd); + closelog(); + dup2(pipefds[1], 3); + pipefds[1] = 3; + closeall(4); +} + +/** + * daemon_ready - tell interested parties that the daemon is ready + * + * This function tells e.g. the parent process that the daemon is up + * and running. + */ +void +daemon_ready(void) +{ + int status = 0; + + if (pipefds[1] > 0) { + if (write(pipefds[1], &status, sizeof(status)) != sizeof(status)) { + xlog_err("WARN: writing to parent pipe failed: errno " + "%d (%s)\n", errno, strerror(errno)); + } + close(pipefds[1]); + pipefds[1] = -1; + } +} + diff --git a/support/nfs/nfs_mntent.c b/support/nfs/nfs_mntent.c new file mode 100644 index 0000000..25e5944 --- /dev/null +++ b/support/nfs/nfs_mntent.c @@ -0,0 +1,240 @@ +/* Private version of the libc *mntent() routines. */ +/* Note slightly different prototypes. */ + +/* 1999-02-22 Arkadiusz Miskiewicz <misiek@pld.ORG.PL> + * - added Native Language Support + * + * 2006-06-08 Amit Gud <agud@redhat.com> + * - Moved to nfs-utils/support/nfs from util-linux/mount + */ + +#include <stdio.h> +#include <string.h> /* for strchr */ +#include <ctype.h> /* for isdigit */ +#include <sys/stat.h> /* for umask */ +#include <unistd.h> /* for ftruncate */ +#include <errno.h> /* for errno */ + +#include "nfs_mntent.h" +#include "nls.h" +#include "xcommon.h" + +/* Unfortunately the classical Unix /etc/mtab and /etc/fstab + do not handle directory names containing spaces. + Here we mangle them, replacing a space by \040. + What do other Unices do? */ + +static unsigned char need_escaping[] = { ' ', '\t', '\n', '\\' }; + +static char * +mangle(const char *arg) { + const unsigned char *s = (const unsigned char *)arg; + char *ss, *sp; + unsigned int n; + + n = strlen(arg); + ss = sp = xmalloc(4*n+1); + while(1) { + for (n = 0; n < sizeof(need_escaping); n++) { + if (*s == need_escaping[n]) { + *sp++ = '\\'; + *sp++ = '0' + ((*s & 0300) >> 6); + *sp++ = '0' + ((*s & 070) >> 3); + *sp++ = '0' + (*s & 07); + goto next; + } + } + *sp++ = *s; + if (*s == 0) + break; + next: + s++; + } + return ss; +} + +static int +is_space_or_tab (char c) { + return (c == ' ' || c == '\t'); +} + +static char * +skip_spaces(char *s) { + while (is_space_or_tab(*s)) + s++; + return s; +} + +static char * +skip_nonspaces(char *s) { + while (*s && !is_space_or_tab(*s)) + s++; + return s; +} + +#define isoctal(a) (((a) & ~7) == '0') + +/* returns malloced pointer - no more strdup required */ +static char * +unmangle(char *s) { + char *ret, *ss, *sp; + + ss = skip_nonspaces(s); + ret = sp = xmalloc(ss-s+1); + while(s != ss) { + if (*s == '\\' && isoctal(s[1]) && isoctal(s[2]) && isoctal(s[3])) { + *sp++ = 64*(s[1] & 7) + 8*(s[2] & 7) + (s[3] & 7); + s += 4; + } else + *sp++ = *s++; + } + *sp = 0; + return ret; +} + +/* + * fstat'ing the file and allocating a buffer holding all of it + * may be a bad idea: if the file is /proc/mounts, the stat + * returns 0. + * (On the other hand, mangling and unmangling is meaningless + * for /proc/mounts.) + */ + +mntFILE * +nfs_setmntent (const char *file, char *mode) { + mntFILE *mfp = xmalloc(sizeof(*mfp)); + mode_t old_umask = umask(077); + + mfp->mntent_fp = fopen(file, mode); + umask(old_umask); + mfp->mntent_file = xstrdup(file); + mfp->mntent_errs = (mfp->mntent_fp == NULL); + mfp->mntent_softerrs = 0; + mfp->mntent_lineno = 0; + return mfp; +} + +void +nfs_endmntent (mntFILE *mfp) { + if (mfp) { + if (mfp->mntent_fp) + fclose(mfp->mntent_fp); + if (mfp->mntent_file) + free(mfp->mntent_file); + free(mfp); + } +} + +int +nfs_addmntent (mntFILE *mfp, struct mntent *mnt) { + char *m1, *m2, *m3, *m4; + int res; + off_t length; + + if (fseek (mfp->mntent_fp, 0, SEEK_END)) + return 1; /* failure */ + length = ftell(mfp->mntent_fp); + + m1 = mangle(mnt->mnt_fsname); + m2 = mangle(mnt->mnt_dir); + m3 = mangle(mnt->mnt_type); + m4 = mangle(mnt->mnt_opts); + + res = fprintf (mfp->mntent_fp, "%s %s %s %s %d %d\n", + m1, m2, m3, m4, mnt->mnt_freq, mnt->mnt_passno); + + free(m1); + free(m2); + free(m3); + free(m4); + if (res >= 0) { + res = fflush(mfp->mntent_fp); + if (res < 0) { + nfs_error("Cant't flush out mtab: %s", strerror(errno)); + /* Avoid leaving a corrupt mtab file */ + if (ftruncate(fileno(mfp->mntent_fp), length)) + {/* Ignore this failure; Why confuse things */} + } + } + return (res < 0) ? 1 : 0; +} + +/* Read the next entry from the file fp. Stop reading at an incorrect entry. */ +struct mntent * +nfs_getmntent (mntFILE *mfp) { + static char buf[4096]; + static struct mntent me; + char *s; + + again: + if (mfp->mntent_errs || mfp->mntent_softerrs >= ERR_MAX) + return NULL; + + /* read the next non-blank non-comment line */ + do { + if (fgets (buf, sizeof(buf), mfp->mntent_fp) == NULL) + return NULL; + + mfp->mntent_lineno++; + s = strchr (buf, '\n'); + if (s == NULL) { + /* Missing final newline? Otherwise extremely */ + /* long line - assume file was corrupted */ + if (feof(mfp->mntent_fp)) { + fprintf(stderr, _("[mntent]: warning: no final " + "newline at the end of %s\n"), + mfp->mntent_file); + s = strchr (buf, 0); + } else { + mfp->mntent_errs = 1; + goto err; + } + } + *s = 0; + if (--s >= buf && *s == '\r') + *s = 0; + s = skip_spaces(buf); + } while (*s == '\0' || *s == '#'); + + me.mnt_fsname = unmangle(s); + s = skip_nonspaces(s); + s = skip_spaces(s); + me.mnt_dir = unmangle(s); + s = skip_nonspaces(s); + s = skip_spaces(s); + me.mnt_type = unmangle(s); + s = skip_nonspaces(s); + s = skip_spaces(s); + me.mnt_opts = unmangle(s); + s = skip_nonspaces(s); + s = skip_spaces(s); + + if (isdigit(*s)) { + me.mnt_freq = atoi(s); + while(isdigit(*s)) s++; + } else + me.mnt_freq = 0; + if(*s && !is_space_or_tab(*s)) + goto err; + + s = skip_spaces(s); + if(isdigit(*s)) { + me.mnt_passno = atoi(s); + while(isdigit(*s)) s++; + } else + me.mnt_passno = 0; + if(*s && !is_space_or_tab(*s)) + goto err; + + /* allow more stuff, e.g. comments, on this line */ + + return &me; + + err: + mfp->mntent_softerrs++; + fprintf(stderr, _("[mntent]: line %d in %s is bad%s\n"), + mfp->mntent_lineno, mfp->mntent_file, + (mfp->mntent_errs || mfp->mntent_softerrs >= ERR_MAX) ? + _("; rest of file ignored") : ""); + goto again; +} diff --git a/support/nfs/rmtab.c b/support/nfs/rmtab.c new file mode 100644 index 0000000..154b26f --- /dev/null +++ b/support/nfs/rmtab.c @@ -0,0 +1,173 @@ +/* + * support/nfs/rmtab.c + * + * Handling for rmtab. + * + * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include "nfslib.h" + +/* + * Colons in incoming IPv6 presentation addresses have to + * replaced with another character, since rmtab already + * uses colons to delineate fields. + * + * Use a printable character, but one that would never be + * found in a presentation address or domain name + */ +#define IPV6_COLON ';' + +#define LINELEN (2048) + +static FILE *rmfp = NULL; + +struct state_paths rmtab; + +int +setrmtabent(char *type) +{ + if (rmfp) + fclose(rmfp); + rmfp = fsetrmtabent(rmtab.statefn, type); + return (rmfp != NULL); +} + +FILE * +fsetrmtabent(char *fname, char *type) +{ + int readonly = !strcmp(type, "r"); + FILE *fp; + + if (!fname) + return NULL; + if ((fp = fopen(fname, type)) == NULL) { + xlog(L_ERROR, "can't open %s for %sing", fname, + readonly ? "read" : "writ"); + return NULL; + } + return fp; +} + +struct rmtabent * +getrmtabent(int log, long *pos) +{ + return fgetrmtabent(rmfp, log, pos); +} + +struct rmtabent * +fgetrmtabent(FILE *fp, int log, long *pos) +{ + static struct rmtabent re; + char *count, *host, *path, *c; + static char buf[LINELEN]; + + errno = 0; + if (!fp) + return NULL; + do { + if (pos) + *pos = ftell (fp); + if (fgets(buf, sizeof(buf)-1, fp) == NULL) + return NULL; + host = buf; + if ((path = strchr(host, '\n')) != NULL) + *path = '\0'; + if (!(path = strchr(host, ':'))) { + if (log) + xlog(L_ERROR, "malformed entry in rmtab file"); + errno = EINVAL; + return NULL; + } + *path++ = '\0'; + count = strchr(path, ':'); + if (count) { + *count++ = '\0'; + re.r_count = strtol (count, NULL, 0); + } + else + re.r_count = 1; + } while (0); + + strncpy(re.r_client, host, sizeof (re.r_client) - 1); + re.r_client[sizeof (re.r_client) - 1] = '\0'; + for (c = re.r_client; *c != '\0'; c++) + if (*c == IPV6_COLON) + *c = ':'; + + strncpy(re.r_path, path, sizeof (re.r_path) - 1); + re.r_path[sizeof (re.r_path) - 1] = '\0'; + + return &re; +} + +void +putrmtabent(struct rmtabent *rep, long *pos) +{ + fputrmtabent(rmfp, rep, pos); +} + +void +fputrmtabent(FILE *fp, struct rmtabent *rep, long *pos) +{ + static char buf[LINELEN]; + char *c; + + if (!fp || (pos && fseek (fp, *pos, SEEK_SET) != 0)) + return; + + /* + * To avoid confusing the token parser in fgetrmtabent(), + * convert colons in incoming IPv6 presentation addresses + * to semicolons. + */ + if (strlen(rep->r_client) > sizeof(buf)) { + xlog(L_ERROR, "client name too large"); + return; + } + strncpy(buf, rep->r_client, sizeof(buf)); + for (c = buf; *c != '\0'; c++) + if (*c == ':') + *c = IPV6_COLON; + + (void)fprintf(fp, "%s:%s:0x%.8x\n", buf, rep->r_path, rep->r_count); +} + +void +endrmtabent(void) +{ + fendrmtabent(rmfp); + rmfp = NULL; +} + +void +fendrmtabent(FILE *fp) +{ + if (fp) + fclose(fp); +} + +void +rewindrmtabent(void) +{ + if (rmfp) + rewind(rmfp); +} + +void +frewindrmtabent(FILE *fp) +{ + if (fp) + rewind (fp); +} diff --git a/support/nfs/rpc_socket.c b/support/nfs/rpc_socket.c new file mode 100644 index 0000000..5fabf5a --- /dev/null +++ b/support/nfs/rpc_socket.c @@ -0,0 +1,560 @@ +/* + * Generic RPC client socket-level APIs for nfs-utils + * + * Copyright (C) 2008 Oracle Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <sys/time.h> + +#include <stdbool.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> + +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> + +#include "sockaddr.h" +#include "nfsrpc.h" + +#ifdef HAVE_LIBTIRPC +#include <netconfig.h> +#include <rpc/rpcb_prot.h> +#endif /* HAVE_LIBTIRPC */ + +/* + * If "-1" is specified in the tv_sec field, use these defaults instead. + */ +#define NFSRPC_TIMEOUT_UDP (3) +#define NFSRPC_TIMEOUT_TCP (10) + + +/* + * Set up an RPC client for communicating via a AF_LOCAL socket. + * + * @timeout is initialized upon return + * + * Returns a pointer to a prepared RPC client if successful; caller + * must destroy a non-NULL returned RPC client. Otherwise NULL, and + * rpc_createerr.cf_stat is set to reflect the error. + */ +static CLIENT *nfs_get_localclient(const struct sockaddr *sap, + const socklen_t salen, + const rpcprog_t program, + const rpcvers_t version, + struct timeval *timeout) +{ +#ifdef HAVE_LIBTIRPC + struct sockaddr_storage address; + const struct netbuf nbuf = { + .maxlen = sizeof(struct sockaddr_un), + .len = (size_t)salen, + .buf = &address, + }; +#else + (void) salen; +#endif /* HAVE_LIBTIRPC */ + CLIENT *client; + int sock; + + sock = socket(AF_LOCAL, SOCK_STREAM, 0); + if (sock == -1) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + return NULL; + } + + if (timeout->tv_sec == -1) + timeout->tv_sec = NFSRPC_TIMEOUT_TCP; + +#ifdef HAVE_LIBTIRPC + memcpy(nbuf.buf, sap, (size_t)salen); + client = clnt_vc_create(sock, &nbuf, program, version, 0, 0); +#else /* !HAVE_LIBTIRPC */ + client = clntunix_create((struct sockaddr_un *)sap, + program, version, &sock, 0, 0); +#endif /* !HAVE_LIBTIRPC */ + if (client != NULL) + CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL); + else + (void)close(sock); + + return client; +} + +#ifdef HAVE_LIBTIRPC + +/* + * Bind a socket using an unused privileged source port. + * + * Returns zero on success, or returns -1 on error. errno is + * set to reflect the nature of the error. + */ +static int nfs_bindresvport(const int sock, const sa_family_t family) +{ + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_ANY), + }; + struct sockaddr_in6 sin6 = { + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_ANY_INIT, + }; + + switch (family) { + case AF_INET: + return bindresvport_sa(sock, (struct sockaddr *)(char *)&sin); + case AF_INET6: + return bindresvport_sa(sock, (struct sockaddr *)(char *)&sin6); + } + + errno = EAFNOSUPPORT; + return -1; +} + +#else /* !HAVE_LIBTIRPC */ + +/* + * Bind a socket using an unused privileged source port. + * + * Returns zero on success, or returns -1 on error. errno is + * set to reflect the nature of the error. + */ +static int nfs_bindresvport(const int sock, const sa_family_t family) +{ + if (family != AF_INET) { + errno = EAFNOSUPPORT; + return -1; + } + + return bindresvport(sock, NULL); +} + +#endif /* !HAVE_LIBTIRPC */ + +/* + * Perform a non-blocking connect on the socket fd. + * + * @timeout is modified to contain the time remaining (i.e. time provided + * minus time elasped). + * + * Returns zero on success, or returns -1 on error. errno is + * set to reflect the nature of the error. + */ +static int nfs_connect_nb(const int fd, const struct sockaddr *sap, + const socklen_t salen, struct timeval *timeout) +{ + int flags, ret; + fd_set rset; + + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) + return -1; + + ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + if (ret < 0) + return -1; + + /* + * From here on subsequent sys calls could change errno so + * we set ret = -errno to capture it in case we decide to + * use it later. + */ + ret = connect(fd, sap, salen); + if (ret < 0 && errno != EINPROGRESS && errno != EINTR) { + ret = -1; + goto done; + } + + if (ret == 0) + goto done; + + /* now wait */ + FD_ZERO(&rset); + FD_SET(fd, &rset); + + while ((ret = select(fd + 1, NULL, &rset, NULL, timeout)) < 0) { + if (errno != EINTR) { + ret = -1; + goto done; + } else { + continue; + } + } + if (ret == 0) { + errno = ETIMEDOUT; + ret = -1; + goto done; + } + + if (FD_ISSET(fd, &rset)) { + int status; + socklen_t len = (socklen_t)sizeof(ret); + + status = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len); + if (status < 0) { + ret = -1; + goto done; + } + + /* Oops - something wrong with connect */ + if (ret != 0) { + errno = ret; + ret = -1; + } + } + +done: + (void)fcntl(fd, F_SETFL, flags); + return ret; +} + +/* + * Set up an RPC client for communicating via a datagram socket. + * A connected UDP socket is used to detect a missing remote + * listener as quickly as possible. + * + * @timeout is initialized upon return + * + * Returns a pointer to a prepared RPC client if successful; caller + * must destroy a non-NULL returned RPC client. Otherwise NULL, and + * rpc_createerr.cf_stat is set to reflect the error. + */ +static CLIENT *nfs_get_udpclient(const struct sockaddr *sap, + const socklen_t salen, + const rpcprog_t program, + const rpcvers_t version, + struct timeval *timeout, + const int resvport) +{ + CLIENT *client; + int ret = 0; + int sock = 0; +#ifdef HAVE_LIBTIRPC + struct sockaddr_storage address; + const struct netbuf nbuf = { + .maxlen = salen, + .len = salen, + .buf = &address, + }; + +#else /* !HAVE_LIBTIRPC */ + + if (sap->sa_family != AF_INET) { + rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; + return NULL; + } +#endif /* !HAVE_LIBTIRPC */ + + sock = socket((int)sap->sa_family, SOCK_DGRAM, IPPROTO_UDP); + if (sock == -1) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + return NULL; + } + + if (resvport) { + ret = nfs_bindresvport(sock, sap->sa_family); + + if (ret < 0) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + (void)close(sock); + return NULL; + } + } + + if (timeout->tv_sec == -1) + timeout->tv_sec = NFSRPC_TIMEOUT_UDP; + + ret = nfs_connect_nb(sock, sap, salen, timeout); + if (ret != 0) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + (void)close(sock); + return NULL; + } + +#ifdef HAVE_LIBTIRPC + memcpy(nbuf.buf, sap, (size_t)salen); + client = clnt_dg_create(sock, &nbuf, program, version, 0, 0); +#else /* !HAVE_LIBTIRPC */ + client = clntudp_create((struct sockaddr_in *)sap, program, + version, *timeout, &sock); +#endif /* !HAVE_LIBTIRPC */ + if (client != NULL) { + struct timeval retry_timeout = { 1, 0 }; + CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, + (char *)&retry_timeout); + CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL); + } else + (void)close(sock); + + return client; +} + +/* + * Set up and connect an RPC client for communicating via a stream socket. + * + * @timeout is initialized upon return + * + * Returns a pointer to a prepared and connected RPC client if + * successful; caller must destroy a non-NULL returned RPC client. + * Otherwise NULL, and rpc_createerr.cf_stat is set to reflect the + * error. + */ +static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap, + const socklen_t salen, + const rpcprog_t program, + const rpcvers_t version, + struct timeval *timeout, + const int resvport) +{ + CLIENT *client; + int ret = 0; + int sock = 0; +#ifdef HAVE_LIBTIRPC + struct sockaddr_storage address; + const struct netbuf nbuf = { + .maxlen = salen, + .len = salen, + .buf = &address, + }; + +#else /* !HAVE_LIBTIRPC */ + + if (sap->sa_family != AF_INET) { + rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; + return NULL; + } +#endif /* !HAVE_LIBTIRPC */ + + sock = socket((int)sap->sa_family, SOCK_STREAM, IPPROTO_TCP); + if (sock == -1) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + return NULL; + } + + if (resvport) { + ret = nfs_bindresvport(sock, sap->sa_family); + + if (ret < 0) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + (void)close(sock); + return NULL; + } + } + + if (timeout->tv_sec == -1) + timeout->tv_sec = NFSRPC_TIMEOUT_TCP; + + ret = nfs_connect_nb(sock, sap, salen, timeout); + if (ret != 0) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + (void)close(sock); + return NULL; + } + +#ifdef HAVE_LIBTIRPC + memcpy(nbuf.buf, sap, (size_t)salen); + client = clnt_vc_create(sock, &nbuf, program, version, 0, 0); +#else /* !HAVE_LIBTIRPC */ + client = clnttcp_create((struct sockaddr_in *)sap, + program, version, &sock, 0, 0); +#endif /* !HAVE_LIBTIRPC */ + if (client != NULL) + CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL); + else + (void)close(sock); + + return client; +} + +/** + * nfs_get_rpcclient - acquire an RPC client + * @sap: pointer to socket address of RPC server + * @salen: length of socket address + * @transport: IPPROTO_ value of transport protocol to use + * @program: RPC program number + * @version: RPC version number + * @timeout: pointer to request timeout (must not be NULL) + * + * Set up an RPC client for communicating with an RPC program @program + * and @version on the server @sap over @transport. An unprivileged + * source port is used. + * + * Returns a pointer to a prepared RPC client if successful, and + * @timeout is initialized; caller must destroy a non-NULL returned RPC + * client. Otherwise returns NULL, and rpc_createerr.cf_stat is set to + * reflect the error. + */ +CLIENT *nfs_get_rpcclient(const struct sockaddr *sap, + const socklen_t salen, + const unsigned short transport, + const rpcprog_t program, + const rpcvers_t version, + struct timeval *timeout) +{ + nfs_clear_rpc_createerr(); + + switch (sap->sa_family) { + case AF_LOCAL: + return nfs_get_localclient(sap, salen, program, + version, timeout); + case AF_INET: + case AF_INET6: + if (nfs_get_port(sap) == 0) { + rpc_createerr.cf_stat = RPC_UNKNOWNADDR; + return NULL; + } + break; + default: + rpc_createerr.cf_stat = RPC_UNKNOWNADDR; + return NULL; + } + + switch (transport) { + case IPPROTO_TCP: + return nfs_get_tcpclient(sap, salen, program, version, + timeout, 0); + case 0: + case IPPROTO_UDP: + return nfs_get_udpclient(sap, salen, program, version, + timeout, 0); + } + + rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; + return NULL; +} + +/** + * nfs_get_priv_rpcclient - acquire an RPC client + * @sap: pointer to socket address of RPC server + * @salen: length of socket address + * @transport: IPPROTO_ value of transport protocol to use + * @program: RPC program number + * @version: RPC version number + * @timeout: pointer to request timeout (must not be NULL) + * + * Set up an RPC client for communicating with an RPC program @program + * and @version on the server @sap over @transport. A privileged + * source port is used. + * + * Returns a pointer to a prepared RPC client if successful, and + * @timeout is initialized; caller must destroy a non-NULL returned RPC + * client. Otherwise returns NULL, and rpc_createerr.cf_stat is set to + * reflect the error. + */ +CLIENT *nfs_get_priv_rpcclient(const struct sockaddr *sap, + const socklen_t salen, + const unsigned short transport, + const rpcprog_t program, + const rpcvers_t version, + struct timeval *timeout) +{ + nfs_clear_rpc_createerr(); + + switch (sap->sa_family) { + case AF_LOCAL: + return nfs_get_localclient(sap, salen, program, + version, timeout); + case AF_INET: + case AF_INET6: + if (nfs_get_port(sap) == 0) { + rpc_createerr.cf_stat = RPC_UNKNOWNADDR; + return NULL; + } + break; + default: + rpc_createerr.cf_stat = RPC_UNKNOWNADDR; + return NULL; + } + + switch (transport) { + case IPPROTO_TCP: + return nfs_get_tcpclient(sap, salen, program, version, + timeout, 1); + case 0: + case IPPROTO_UDP: + return nfs_get_udpclient(sap, salen, program, version, + timeout, 1); + } + + rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; + return NULL; +} + +/** + * nfs_getrpcbyname - convert an RPC program name to a rpcprog_t + * @program: default program number to use if names not found in db + * @table: pointer to table of 'char *' names to try to find + * + * Returns program number of first name to be successfully looked + * up, or the default program number if all lookups fail. + */ +rpcprog_t nfs_getrpcbyname(const rpcprog_t program, const char *table[]) +{ +#ifdef HAVE_GETRPCBYNAME + struct rpcent *entry; + unsigned int i; + + if (table != NULL) + for (i = 0; table[i] != NULL; i++) { + entry = getrpcbyname(table[i]); + if (entry) + return (rpcprog_t)entry->r_number; + } +#endif /* HAVE_GETRPCBYNAME */ + + return program; +} + +/* + * AUTH_SYS doesn't allow more than 16 gids in the supplemental group list. + * If there are more than that, trying to determine which ones to include + * in the list is problematic. This function creates an auth handle that + * only has the primary gid in the supplemental gids list. It's intended to + * be used for protocols where credentials really don't matter much (the MNT + * protocol, for instance). + */ +AUTH * +nfs_authsys_create(void) +{ + char machname[MAXHOSTNAMELEN + 1]; + uid_t uid = geteuid(); + gid_t gid = getegid(); + + if (gethostname(machname, sizeof(machname)) == -1) + return NULL; + + return authunix_create(machname, uid, gid, 1, &gid); +} diff --git a/support/nfs/rpcdispatch.c b/support/nfs/rpcdispatch.c new file mode 100644 index 0000000..7329f41 --- /dev/null +++ b/support/nfs/rpcdispatch.c @@ -0,0 +1,69 @@ +/* + * support/nfs/rcpdispatch.c + * + * Generic RPC dispatcher. + * + * Copyright (C) 1995, 1996, Olaf Kirch <okir@monad.swb.de> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <rpc/rpc.h> +#include <rpc/pmap_clnt.h> +#include <signal.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <string.h> +#include "rpcmisc.h" +#include "xlog.h" + +void +rpc_dispatch(struct svc_req *rqstp, SVCXPRT *transp, + struct rpc_dtable *dtable, int nvers, + void *argp, void *resp) +{ + struct rpc_dentry *dent; + int rq_vers = (int)rqstp->rq_vers; + + if (rq_vers < 1 || rq_vers > nvers) { + svcerr_progvers(transp, 1, nvers); + return; + } + dtable += (rq_vers - 1); + if (rqstp->rq_proc > dtable->nproc) { + svcerr_noproc(transp); + return; + } + + if (dtable->nproc <= rqstp->rq_proc) { + svcerr_noproc(transp); + return; + } + + dent = dtable->entries + rqstp->rq_proc; + + if (dent->func == NULL) { + svcerr_noproc(transp); + return; + } + + memset(argp, 0, dent->xdr_arg_size); + memset(resp, 0, dent->xdr_res_size); + + if (!svc_getargs(transp, dent->xdr_arg_fn, argp)) { + svcerr_decode(transp); + return; + } + + if ((dent->func)(rqstp, argp, resp) && resp != 0) { + if (!svc_sendreply(transp, dent->xdr_res_fn, (caddr_t)resp)) + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, dent->xdr_arg_fn, argp)) { + xlog(L_ERROR, "failed to free RPC arguments"); + exit (2); + } +} diff --git a/support/nfs/rpcmisc.c b/support/nfs/rpcmisc.c new file mode 100644 index 0000000..d84c04f --- /dev/null +++ b/support/nfs/rpcmisc.c @@ -0,0 +1,213 @@ +/* + * Miscellaneous functions for RPC service startup and shutdown. + * + * This code is partially snarfed from rpcgen -s tcp -s udp, + * partly written by Mark Shand, Donald Becker, and Rick + * Sladkey. It was tweaked slightly by Olaf Kirch to be + * usable by both unfsd and mountd. + * + * This software may be used for any purpose provided + * the above copyright notice is retained. It is supplied + * as is, with no warranty expressed or implied. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <rpc/rpc.h> +#include <rpc/pmap_clnt.h> +#include <netinet/in.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <fcntl.h> +#include <memory.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include "nfslib.h" +#include "rpcmisc.h" + +#if SIZEOF_SOCKLEN_T - 0 == 0 +#define socklen_t int +#endif + +#define _RPCSVC_CLOSEDOWN 120 +int _rpcpmstart = 0; +unsigned int _rpcprotobits = (NFSCTL_UDPBIT|NFSCTL_TCPBIT); +int _rpcsvcdirty = 0; + +static void +closedown(int sig) +{ + (void) signal(sig, closedown); + + if (_rpcsvcdirty == 0) { + static int size; + int i, openfd; + + if (NFSCTL_TCPISSET(_rpcprotobits) == 0) + exit(0); + + if (size == 0) + size = getdtablesize(); + + for (i = 0, openfd = 0; i < size && openfd < 2; i++) + if (FD_ISSET(i, &svc_fdset)) + openfd++; + if (openfd <= 1) + exit(0); + } + + (void) alarm(_RPCSVC_CLOSEDOWN); +} + +/* + * Create listener socket for a given port + * + * Return an open network socket on success; otherwise return -1 + * if some error occurs. + */ +static int +makesock(int port, int proto) +{ + struct sockaddr_in sin; + int sock, sock_type, val; + + sock_type = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM; + sock = socket(AF_INET, sock_type, proto); + if (sock < 0) { + xlog(L_FATAL, "Could not make a socket: %s", + strerror(errno)); + return -1; + } + memset((char *) &sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = htons(port); + + val = 1; + if (proto == IPPROTO_TCP) + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + &val, sizeof(val)) < 0) + xlog(L_ERROR, "setsockopt failed: %s", + strerror(errno)); + + if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) == -1) { + xlog(L_FATAL, "Could not bind name to socket: %s", + strerror(errno)); + close(sock); + return -1; + } + + return svcsock_nonblock(sock); +} + +void +rpc_init(char *name, int prog, int vers, + void (*dispatch)(struct svc_req *, register SVCXPRT *), + int defport) +{ + struct sockaddr_in saddr; + SVCXPRT *transp; + int sock; + socklen_t asize; + + asize = sizeof(saddr); + sock = 0; + if (getsockname(0, (struct sockaddr *) &saddr, &asize) == 0 + && saddr.sin_family == AF_INET) { + socklen_t ssize = sizeof(int); + int fdtype = 0; + if (getsockopt(0, SOL_SOCKET, SO_TYPE, + (char *)&fdtype, &ssize) == -1) + xlog(L_FATAL, "getsockopt failed: %s", strerror(errno)); + /* inetd passes a UDP socket or a listening TCP socket. + * listen will fail on a connected TCP socket(passed by rsh). + */ + if (!(fdtype == SOCK_STREAM && listen(0,5) == -1)) { + switch(fdtype) { + case SOCK_DGRAM: + NFSCTL_UDPSET(_rpcprotobits); + break; + case SOCK_STREAM: + NFSCTL_TCPSET(_rpcprotobits); + break; + default: + xlog(L_FATAL, "getsockopt returns bad socket type: %d", fdtype); + } + _rpcpmstart = 1; + } + } + if (!_rpcpmstart) { + pmap_unset(prog, vers); + sock = RPC_ANYSOCK; + } + + if (NFSCTL_UDPISSET(_rpcprotobits)) { + static SVCXPRT *last_transp = NULL; + + if (_rpcpmstart == 0) { + if (last_transp + && (!defport || defport == last_transp->xp_port)) { + transp = last_transp; + goto udp_transport; + } + if (defport == 0) + sock = RPC_ANYSOCK; + else + sock = makesock(defport, IPPROTO_UDP); + } + if (sock == RPC_ANYSOCK) + sock = svcudp_socket (prog); + transp = svcudp_create(sock); + if (transp == NULL) { + xlog(L_FATAL, "cannot create udp service."); + } + udp_transport: + if (!svc_register(transp, prog, vers, dispatch, IPPROTO_UDP)) { + xlog(L_FATAL, "unable to register (%s, %d, udp).", + name, vers); + } + last_transp = transp; + } + + if (NFSCTL_TCPISSET(_rpcprotobits)) { + static SVCXPRT *last_transp = NULL; + + if (_rpcpmstart == 0) { + if (last_transp + && (!defport || defport == last_transp->xp_port)) { + transp = last_transp; + goto tcp_transport; + } + if (defport == 0) + sock = RPC_ANYSOCK; + else + sock = makesock(defport, IPPROTO_TCP); + } + if (sock == RPC_ANYSOCK) + sock = svctcp_socket (prog, 1); + transp = svctcp_create(sock, 0, 0); + if (transp == NULL) { + xlog(L_FATAL, "cannot create tcp service."); + } + tcp_transport: + if (!svc_register(transp, prog, vers, dispatch, IPPROTO_TCP)) { + xlog(L_FATAL, "unable to register (%s, %d, tcp).", + name, vers); + } + last_transp = transp; + } + + if (_rpcpmstart) { + signal(SIGALRM, closedown); + alarm(_RPCSVC_CLOSEDOWN); + } +} diff --git a/support/nfs/strlcat.c b/support/nfs/strlcat.c new file mode 100644 index 0000000..0edee14 --- /dev/null +++ b/support/nfs/strlcat.c @@ -0,0 +1,78 @@ +/* $OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * 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. The name of the author may not 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 AUTHOR 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include <sys/types.h> +#include <string.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "nfslib.h" + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(char *dst, + const char *src, + size_t siz) +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} diff --git a/support/nfs/strlcpy.c b/support/nfs/strlcpy.c new file mode 100644 index 0000000..23e3ae9 --- /dev/null +++ b/support/nfs/strlcpy.c @@ -0,0 +1,74 @@ +/* $OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * 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. The name of the author may not 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 AUTHOR 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include <sys/types.h> +#include <string.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "nfslib.h" + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, + const char *src, + size_t siz) +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} diff --git a/support/nfs/svc_create.c b/support/nfs/svc_create.c new file mode 100644 index 0000000..976c2d2 --- /dev/null +++ b/support/nfs/svc_create.c @@ -0,0 +1,525 @@ +/* + * Copyright 2009 Oracle. All rights reserved. + * + * This file is part of nfs-utils. + * + * nfs-utils is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * nfs-utils is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with nfs-utils. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <memory.h> +#include <signal.h> +#include <unistd.h> +#include <errno.h> +#include <netdb.h> +#include "nfslib.h" + +#include <netinet/in.h> + +#include <sys/socket.h> +#include <sys/resource.h> + +#include <rpc/rpc.h> +#include <rpc/svc.h> + +#ifdef HAVE_TCP_WRAPPER +#include "tcpwrapper.h" +#endif + +#include "sockaddr.h" +#include "rpcmisc.h" +#include "xlog.h" + +#ifdef HAVE_LIBTIRPC + +#include <rpc/rpc_com.h> + +#define SVC_CREATE_XPRT_CACHE_SIZE (8) +static SVCXPRT *svc_create_xprt_cache[SVC_CREATE_XPRT_CACHE_SIZE] = { NULL, }; + +/* + * Cache an SVC xprt, in case there are more programs or versions to + * register against it. + */ +static void +svc_create_cache_xprt(SVCXPRT *xprt) +{ + unsigned int i; + + /* Check if we've already got this one... */ + for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++) + if (svc_create_xprt_cache[i] == xprt) + return; + + /* No, we don't. Cache it. */ + for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++) + if (svc_create_xprt_cache[i] == NULL) { + svc_create_xprt_cache[i] = xprt; + return; + } + + xlog(L_ERROR, "%s: Failed to cache an xprt", __func__); +} + +/* + * Find a previously cached SVC xprt structure with the given bind address + * and transport semantics. + * + * Returns pointer to a cached SVC xprt. + * + * If no matching SVC XPRT can be found, NULL is returned. + */ +static SVCXPRT * +svc_create_find_xprt(const struct sockaddr *bindaddr, const struct netconfig *nconf) +{ + unsigned int i; + + for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++) { + SVCXPRT *xprt = svc_create_xprt_cache[i]; + struct sockaddr *sap; + + if (xprt == NULL) + continue; + if (strcmp(nconf->nc_netid, xprt->xp_netid) != 0) + continue; + sap = (struct sockaddr *)xprt->xp_ltaddr.buf; + if (!nfs_compare_sockaddr(bindaddr, sap)) + continue; + return xprt; + } + return NULL; +} + +/* + * Set up an appropriate bind address, given @port and @nconf. + * + * Returns getaddrinfo(3) results if successful. Caller must + * invoke freeaddrinfo(3) on these results. + * + * Otherwise NULL is returned if an error occurs. + */ +__attribute__((__malloc__)) +static struct addrinfo * +svc_create_bindaddr(struct netconfig *nconf, const uint16_t port) +{ + struct addrinfo *ai = NULL; + struct addrinfo hint = { + .ai_flags = AI_PASSIVE | AI_NUMERICSERV, + }; + char buf[8]; + int error; + + if (strcmp(nconf->nc_protofmly, NC_INET) == 0) + hint.ai_family = AF_INET; +#ifdef IPV6_SUPPORTED + else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) + hint.ai_family = AF_INET6; +#endif /* IPV6_SUPPORTED */ + else { + xlog(L_ERROR, "Unrecognized bind address family: %s", + nconf->nc_protofmly); + return NULL; + } + + if (strcmp(nconf->nc_proto, NC_UDP) == 0) + hint.ai_protocol = (int)IPPROTO_UDP; + else if (strcmp(nconf->nc_proto, NC_TCP) == 0) + hint.ai_protocol = (int)IPPROTO_TCP; + else { + xlog(L_ERROR, "Unrecognized bind address protocol: %s", + nconf->nc_proto); + return NULL; + } + + (void)snprintf(buf, sizeof(buf), "%u", port); + error = getaddrinfo(NULL, buf, &hint, &ai); + if (error != 0) { + xlog(L_ERROR, "Failed to construct bind address: %s", + gai_strerror(error)); + return NULL; + } + + return ai; +} + +/* + * Create a listener socket on a specific bindaddr, and set + * special socket options to allow it to share the same port + * as other listeners. + * + * Returns an open, bound, and possibly listening network + * socket on success. + * + * Otherwise returns -1 if some error occurs. + */ +static int +svc_create_sock(const struct sockaddr *sap, socklen_t salen, + struct netconfig *nconf) +{ + int fd, type, protocol; + int one = 1; + + switch(nconf->nc_semantics) { + case NC_TPI_CLTS: + type = SOCK_DGRAM; + break; + case NC_TPI_COTS_ORD: + type = SOCK_STREAM; + break; + default: + xlog(D_GENERAL, "%s: Unrecognized bind address semantics: %lu", + __func__, nconf->nc_semantics); + return -1; + } + + if (strcmp(nconf->nc_proto, NC_UDP) == 0) + protocol = (int)IPPROTO_UDP; + else if (strcmp(nconf->nc_proto, NC_TCP) == 0) + protocol = (int)IPPROTO_TCP; + else { + xlog(D_GENERAL, "%s: Unrecognized bind address protocol: %s", + __func__, nconf->nc_proto); + return -1; + } + + fd = socket((int)sap->sa_family, type, protocol); + if (fd == -1) { + xlog(L_ERROR, "Could not make a socket: (%d) %m", + errno); + return -1; + } + +#ifdef IPV6_SUPPORTED + if (sap->sa_family == AF_INET6) { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, + &one, sizeof(one)) == -1) { + xlog(L_ERROR, "Failed to set IPV6_V6ONLY: (%d) %m", + errno); + (void)close(fd); + return -1; + } + } +#endif /* IPV6_SUPPORTED */ + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + &one, sizeof(one)) == -1) { + xlog(L_ERROR, "Failed to set SO_REUSEADDR: (%d) %m", + errno); + (void)close(fd); + return -1; + } + + if (bind(fd, sap, salen) == -1) { + xlog(L_ERROR, "Could not bind socket: (%d) %m", + errno); + (void)close(fd); + return -1; + } + + if (nconf->nc_semantics == NC_TPI_COTS_ORD) + if (listen(fd, SOMAXCONN) == -1) { + xlog(L_ERROR, "Could not listen on socket: (%d) %m", + errno); + (void)close(fd); + return -1; + } + + return fd; +} + +/* + * The simple case is allowing the TI-RPC library to create a + * transport itself, given just the bind address and transport + * semantics. + * + * Our local xprt cache is ignored in this path, since the + * caller is not interested in sharing listeners or ports, and + * the library automatically avoids ports already in use. + * + * Returns the count of started listeners (one or zero). + */ +static unsigned int +svc_create_nconf_rand_port(const char *name, const rpcprog_t program, + const rpcvers_t version, + void (*dispatch)(struct svc_req *, SVCXPRT *), + struct netconfig *nconf) +{ + struct t_bind bindaddr; + struct addrinfo *ai; + SVCXPRT *xprt; + + ai = svc_create_bindaddr(nconf, 0); + if (ai == NULL) + return 0; + + bindaddr.addr.buf = ai->ai_addr; + bindaddr.qlen = SOMAXCONN; + + xprt = svc_tli_create(RPC_ANYFD, nconf, &bindaddr, 0, 0); + nfs_freeaddrinfo(ai); + if (xprt == NULL) { + xlog(L_ERROR, "Failed to create listener xprt " + "(%s, %u, %s)", name, version, nconf->nc_netid); + return 0; + } + if (svcsock_nonblock(xprt->xp_fd) < 0) { + /* close() already done by svcsock_nonblock() */ + xprt->xp_fd = RPC_ANYFD; + SVC_DESTROY(xprt); + return 0; + } + + rpc_createerr.cf_stat = rpc_createerr.cf_error.re_errno = 0; + if (!svc_reg(xprt, program, version, dispatch, nconf)) { + /* svc_reg(3) destroys @xprt in this case */ + xlog(L_ERROR, "Failed to register (%s, %u, %s): %s", + name, version, nconf->nc_netid, + clnt_spcreateerror("svc_reg() err")); + return 0; + } + + return 1; +} + +/* + * If a port is specified on the command line, that port value will be + * the same for all listeners created here. Create each listener + * socket in advance and set SO_REUSEADDR, rather than allowing the + * RPC library to create the listeners for us on a randomly chosen + * port via svc_tli_create(RPC_ANYFD). + * + * Some callers want to listen for more than one RPC version using the + * same port number. For example, mountd could want to listen for MNT + * version 1, 2, and 3 requests. This means mountd must use the same + * set of listener sockets for multiple RPC versions, since, on one + * system, you can't have two listener sockets with the exact same + * bind address (and port) and transport protocol. + * + * To accomplish this, this function caches xprts as they are created. + * This cache is checked to see if a previously created xprt can be + * used, before creating a new xprt for this [program, version]. If + * there is a cached xprt with the same bindaddr and transport + * semantics, we simply register the new version with that xprt, + * rather than creating a fresh xprt for it. + * + * The xprt cache implemented here is local to a process. Two + * separate RPC daemons can not share a set of listeners. + * + * Returns the count of started listeners (one or zero). + */ +static unsigned int +svc_create_nconf_fixed_port(const char *name, const rpcprog_t program, + const rpcvers_t version, + void (*dispatch)(struct svc_req *, SVCXPRT *), + const uint16_t port, struct netconfig *nconf) +{ + struct addrinfo *ai; + SVCXPRT *xprt; + + ai = svc_create_bindaddr(nconf, port); + if (ai == NULL) + return 0; + + xprt = svc_create_find_xprt(ai->ai_addr, nconf); + if (xprt == NULL) { + int fd; + + fd = svc_create_sock(ai->ai_addr, ai->ai_addrlen, nconf); + fd = svcsock_nonblock(fd); + if (fd == -1) + goto out_free; + + xprt = svc_tli_create(fd, nconf, NULL, 0, 0); + if (xprt == NULL) { + xlog(D_GENERAL, "Failed to create listener xprt " + "(%s, %u, %s)", name, version, nconf->nc_netid); + (void)close(fd); + goto out_free; + } + } + + if (!svc_reg(xprt, program, version, dispatch, nconf)) { + /* svc_reg(3) destroys @xprt in this case */ + xlog(D_GENERAL, "Failed to register (%s, %u, %s)", + name, version, nconf->nc_netid); + goto out_free; + } + + svc_create_cache_xprt(xprt); + + nfs_freeaddrinfo(ai); + return 1; + +out_free: + nfs_freeaddrinfo(ai); + return 0; +} + +static unsigned int +svc_create_nconf(const char *name, const rpcprog_t program, + const rpcvers_t version, + void (*dispatch)(struct svc_req *, SVCXPRT *), + const uint16_t port, struct netconfig *nconf) +{ + if (port != 0) + return svc_create_nconf_fixed_port(name, program, + version, dispatch, port, nconf); + + return svc_create_nconf_rand_port(name, program, + version, dispatch, nconf); +} + +/** + * nfs_svc_create - start up RPC svc listeners + * @name: C string containing name of new service + * @program: RPC program number to register + * @version: RPC version number to register + * @dispatch: address of function that handles incoming RPC requests + * @port: if not zero, transport listens on this port + * + * Sets up network transports for receiving RPC requests, and starts + * the RPC dispatcher. Returns the number of started network transports. + */ +unsigned int +nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version, + void (*dispatch)(struct svc_req *, SVCXPRT *), + const uint16_t port) +{ + const struct sigaction create_sigaction = { + .sa_handler = SIG_IGN, + }; + int maxrec = RPC_MAXDATASIZE; + unsigned int visible, up, servport; + struct netconfig *nconf; + void *handlep; + + /* + * Ignore SIGPIPE to avoid exiting sideways when peers + * close their TCP connection while we're trying to reply + * to them. + */ + (void)sigaction(SIGPIPE, &create_sigaction, NULL); + + /* + * Setting MAXREC also enables non-blocking mode for tcp connections. + * This avoids DOS attacks by a client sending many requests but never + * reading the reply: + * - if a second request already is present for reading in the socket, + * after the first request just was read, libtirpc will break the + * connection. Thus an attacker can't simply send requests as fast as + * he can without waiting for the response. + * - if the write buffer of the socket is full, the next write() will + * fail with EAGAIN. libtirpc will retry the write in a loop for max. + * 2 seconds. If write still fails, the connection will be closed. + */ + rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec); + + handlep = setnetconfig(); + if (handlep == NULL) { + xlog(L_ERROR, "Failed to access local netconfig database: %s", + nc_sperror()); + return 0; + } + + visible = 0; + up = 0; + while ((nconf = getnetconfig(handlep)) != NULL) { + if (!(nconf->nc_flag & NC_VISIBLE)) + continue; + visible++; + + if (!strcmp(nconf->nc_proto, NC_UDP) && !NFSCTL_UDPISSET(_rpcprotobits)) + continue; + + if (!strcmp(nconf->nc_proto, NC_TCP) && !NFSCTL_TCPISSET(_rpcprotobits)) + continue; + + if (port == 0) + servport = getservport(program, nconf->nc_proto); + else + servport = port; + + up += svc_create_nconf(name, program, version, dispatch, + servport, nconf); + } + + if (visible == 0) + xlog(L_ERROR, "Failed to find any visible netconfig entries"); + + if (endnetconfig(handlep) == -1) + xlog(L_ERROR, "Failed to close local netconfig database: %s", + nc_sperror()); + + return up; +} + +/** + * nfs_svc_unregister - remove service registrations from local rpcbind database + * @program: RPC program number to unregister + * @version: RPC version number to unregister + * + * Removes all registrations for [ @program, @version ] . + */ +void +nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version) +{ + if (rpcb_unset(program, version, NULL) == FALSE) + xlog(D_GENERAL, "Failed to unregister program %lu, version %lu", + (unsigned long)program, (unsigned long)version); +} + +#else /* !HAVE_LIBTIRPC */ + +/** + * nfs_svc_create - start up RPC svc listeners + * @name: C string containing name of new service + * @program: RPC program number to register + * @version: RPC version number to register + * @dispatch: address of function that handles incoming RPC requests + * @port: if not zero, transport listens on this port + * + * Sets up network transports for receiving RPC requests, and starts + * the RPC dispatcher. Returns the number of started network transports. + */ +unsigned int +nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version, + void (*dispatch)(struct svc_req *, SVCXPRT *), + const uint16_t port) +{ + rpc_init(name, (int)program, (int)version, dispatch, (int)port); + return 1; +} + +/** + * nfs_svc_unregister - remove service registrations from local rpcbind database + * @program: RPC program number to unregister + * @version: RPC version number to unregister + * + * Removes all registrations for [ @program, @version ] . + */ +void +nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version) +{ + if (pmap_unset((unsigned long)program, (unsigned long)version) == FALSE) + xlog(D_GENERAL, "Failed to unregister program %lu, version %lu", + (unsigned long)program, (unsigned long)version); +} + +#endif /* !HAVE_LIBTIRPC */ diff --git a/support/nfs/svc_socket.c b/support/nfs/svc_socket.c new file mode 100644 index 0000000..2e8fe1a --- /dev/null +++ b/support/nfs/svc_socket.c @@ -0,0 +1,218 @@ +/* Copyright (C) 2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 0211-1301 USA */ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <netdb.h> +#include <rpc/rpc.h> +#include <sys/socket.h> +#include <fcntl.h> +#include <errno.h> +#include "xlog.h" +#include "rpcmisc.h" +#include "nfslib.h" + +#include "config.h" + +#ifdef _LIBC +# include <libintl.h> +#else +# ifndef _ +# define _(s) (s) +# endif +# define __socket(d, t, p) socket ((d), (t), (p)) +# define __close(f) close ((f)) +#endif + +int getservport(u_long number, const char *proto) +{ + char servdata[1024]; + struct rpcent *rpcp; + struct servent servbuf, *servp = NULL; + int ret = 0; +#ifdef HAVE_GETRPCBYNUMBER_R + char rpcdata[1024]; + struct rpcent rpcbuf; + + ret = getrpcbynumber_r(number, &rpcbuf, rpcdata, sizeof rpcdata, + &rpcp); +#else + rpcp = getrpcbynumber(number); +#endif + + if (ret == 0 && rpcp != NULL) { + /* First try name. */ + ret = getservbyname_r(rpcp->r_name, proto, &servbuf, servdata, + sizeof servdata, &servp); + if ((ret != 0 || servp == NULL) && rpcp->r_aliases) { + const char **a; + + /* Then we try aliases. */ + for (a = (const char **) rpcp->r_aliases; *a != NULL; a++) { + ret = getservbyname_r(*a, proto, &servbuf, servdata, + sizeof servdata, &servp); + if (ret == 0 && servp != NULL) + break; + } + } + } + + if (ret == 0 && servp != NULL) + return ntohs(servp->s_port); + + return 0; +} + +int +svcsock_nonblock(int sock) +{ + int flags; + + if (sock < 0) + return sock; + + /* This socket might be shared among multiple processes + * if mountd is run multi-threaded. So it is safest to + * make it non-blocking, else all threads might wake + * one will get the data, and the others will block + * indefinitely. + * In all cases, transaction on this socket are atomic + * (accept for TCP, packet-read and packet-write for UDP) + * so O_NONBLOCK will not confuse unprepared code causing + * it to corrupt messages. + * It generally safest to have O_NONBLOCK when doing an accept + * as if we get a RST after the SYN and before accept runs, + * we can block despite being told there was an acceptable + * connection. + */ + if ((flags = fcntl(sock, F_GETFL)) < 0) + xlog(L_ERROR, "svc_socket: can't get socket flags: %m"); + else if (fcntl(sock, F_SETFL, flags|O_NONBLOCK) < 0) + xlog(L_ERROR, "svc_socket: can't set socket flags: %m"); + else + return sock; + + (void) __close(sock); + return -1; +} + +static int +svc_socket (u_long number, int type, int protocol, int reuse) +{ + struct sockaddr_in addr; + socklen_t len = sizeof (struct sockaddr_in); + int sock, ret; + const char *proto = protocol == IPPROTO_TCP ? "tcp" : "udp"; + + if ((sock = __socket (AF_INET, type, protocol)) < 0) + { + xlog(L_ERROR, "svc_socket: socket creation problem: %m"); + return sock; + } + + if (reuse) + { + ret = 1; + ret = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &ret, + sizeof (ret)); + if (ret < 0) + { + xlog(L_ERROR, "svc_socket: socket reuse problem: %m"); + (void) __close(sock); + return ret; + } + } + + memset (&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(getservport(number, proto)); + + if (bind(sock, (struct sockaddr *) &addr, len) < 0) + { + xlog(L_ERROR, "svc_socket: bind problem: %m"); + (void) __close(sock); + sock = -1; + } + + return svcsock_nonblock(sock); +} + +/* + * Create and bind a TCP socket based on program number + */ +int +svctcp_socket (u_long number, int reuse) +{ + return svc_socket (number, SOCK_STREAM, IPPROTO_TCP, reuse); +} + +/* + * Create and bind a UDP socket based on program number + */ +int +svcudp_socket (u_long number) +{ + return svc_socket (number, SOCK_DGRAM, IPPROTO_UDP, FALSE); +} + +#ifdef TEST +static int +check (u_long number, u_short port, int protocol, int reuse) +{ + int socket; + int result; + struct sockaddr_in addr; + socklen_t len = sizeof (struct sockaddr_in); + + if (protocol == IPPROTO_TCP) + socket = svctcp_socket (number, reuse); + else + socket = svcudp_socket (number); + + if (socket < 0) + return 1; + + result = getsockname (socket, (struct sockaddr *) &addr, &len); + if (result == 0) + { + if (port != 0 && ntohs (addr.sin_port) != port) + printf ("Program: %ld, expect port: %d, got: %d\n", + number, port, ntohs (addr.sin_port)); + else + printf ("Program: %ld, port: %d\n", + number, ntohs (addr.sin_port)); + } + + close (socket); + return result; +} + +int +main (void) +{ + int result = 0; + + result += check (100001, 0, IPPROTO_TCP, 0); + result += check (100001, 0, IPPROTO_UDP, 0); + result += check (100003, 2049, IPPROTO_TCP, 1); + result += check (100003, 2049, IPPROTO_UDP, 1); + + return result; +} +#endif diff --git a/support/nfs/wildmat.c b/support/nfs/wildmat.c new file mode 100644 index 0000000..437b2d1 --- /dev/null +++ b/support/nfs/wildmat.c @@ -0,0 +1,182 @@ +/* $Revision: 0.2.18.1 $ +** +** Do shell-style pattern matching for ?, \, [], and * characters. +** Might not be robust in face of malformed patterns; e.g., "foo[a-" +** could cause a segmentation violation. It is 8bit clean. +** +** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. +** Rich $alz is now <rsalz@osf.org>. +** April, 1991: Replaced mutually-recursive calls with in-line code +** for the star character. +** +** Special thanks to Lars Mathiesen <thorinn@diku.dk> for the ABORT code. +** This can greatly speed up failing wildcard patterns. For example: +** pattern: -*-*-*-*-*-*-12-*-*-*-m-*-*-* +** text 1: -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1 +** text 2: -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1 +** Text 1 matches with 51 calls, while text 2 fails with 54 calls. Without +** the ABORT code, it takes 22310 calls to fail. Ugh. The following +** explanation is from Lars: +** The precondition that must be fulfilled is that DoMatch will consume +** at least one character in text. This is true if *p is neither '*' nor +** '\0'.) The last return has ABORT instead of FALSE to avoid quadratic +** behaviour in cases like pattern "*a*b*c*d" with text "abcxxxxx". With +** FALSE, each star-loop has to run to the end of the text; with ABORT +** only the last one does. +** +** Once the control of one instance of DoMatch enters the star-loop, that +** instance will return either TRUE or ABORT, and any calling instance +** will therefore return immediately after (without calling recursively +** again). In effect, only one star-loop is ever active. It would be +** possible to modify the code to maintain this context explicitly, +** eliminating all recursive calls at the cost of some complication and +** loss of clarity (and the ABORT stuff seems to be unclear enough by +** itself). I think it would be unwise to try to get this into a +** released version unless you have a good test data base to try it out +** on. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <ctype.h> +#include "nfslib.h" + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define ABORT -1 + + + /* What character marks an inverted character class? */ +#define NEGATE_CLASS '^' + /* Is "*" a common pattern? */ +#define OPTIMIZE_JUST_STAR + /* Do tar(1) matching rules, which ignore a trailing slash? */ +#undef MATCH_TAR_PATTERN + + +/* +** Match text and p, return TRUE, FALSE, or ABORT. +*/ +static int +DoMatch(char *text, char *p) +{ + register int last; + register int matched; + register int reverse; + + for ( ; *p; text++, p++) { + if (*text == '\0' && *p != '*') + return ABORT; + switch (*p) { + case '\\': + /* Literal match with following character. */ + p++; + /* FALLTHROUGH */ + default: + if (toupper (*text) != toupper (*p)) + return FALSE; + continue; + case '?': + /* Match anything. */ + continue; + case '*': + while (*++p == '*') + /* Consecutive stars act just like one. */ + continue; + if (*p == '\0') + /* Trailing star matches everything. */ + return TRUE; + while (*text) + if ((matched = DoMatch(text++, p)) != FALSE) + return matched; + return ABORT; + case '[': + reverse = p[1] == NEGATE_CLASS ? TRUE : FALSE; + if (reverse) + /* Inverted character class. */ + p++; + matched = FALSE; + if (p[1] == ']' || p[1] == '-') + if (toupper (*++p) == toupper(*text)) + matched = TRUE; + for (last = *p; *++p && *p != ']'; last = *p) + /* This next line requires a good C compiler. */ + if (*p == '-' && p[1] != ']' + ? *text <= *++p && *text >= last + : toupper (*text) == toupper (*p)) + matched = TRUE; + if (matched == reverse) + return FALSE; + continue; + } + } + +#ifdef MATCH_TAR_PATTERN + if (*text == '/') + return TRUE; +#endif /* MATCH_TAR_ATTERN */ + return *text == '\0'; +} + + +/* +** User-level routine. Returns TRUE or FALSE. +*/ +int +wildmat(char *text, char *p) +{ +#ifdef OPTIMIZE_JUST_STAR + if (p[0] == '*' && p[1] == '\0') + return TRUE; +#endif /* OPTIMIZE_JUST_STAR */ + return DoMatch(text, p) == TRUE; +} + + + +#if defined(TEST) +#include <stdio.h> + +/* Yes, we use gets not fgets. Sue me. */ +extern char *gets(); + + +int +main() +{ + char p[80]; + char text[80]; + + printf("Wildmat tester. Enter pattern, then strings to test.\n"); + printf("A blank line gets prompts for a new pattern; a blank pattern\n"); + printf("exits the program.\n"); + + for ( ; ; ) { + printf("\nEnter pattern: "); + (void)fflush(stdout); + if (gets(p) == NULL || p[0] == '\0') + break; + for ( ; ; ) { + printf("Enter text: "); + (void)fflush(stdout); + if (gets(text) == NULL) + exit(0); + if (text[0] == '\0') + /* Blank line; go back and get a new pattern. */ + break; + printf(" %s\n", wildmat(text, p) ? "YES" : "NO"); + } + } + + exit(0); + /* NOTREACHED */ +} +#endif /* defined(TEST) */ diff --git a/support/nfs/xcommon.c b/support/nfs/xcommon.c new file mode 100644 index 0000000..3989f0b --- /dev/null +++ b/support/nfs/xcommon.c @@ -0,0 +1,191 @@ +/* + * xcommon.c - various functions put together to avoid basic error checking. + * + * added fcntl locking by Kjetil T. (kjetilho@math.uio.no) - aeb, 950927 + * + * 1999-02-22 Arkadiusz Miskiewicz <misiek@pld.ORG.PL> + * - added Native Language Support + * + * 2006-06-06 Amit Gud <agud@redhat.com> + * - Moved code snippets here from mount/sundries.c of util-linux + * and merged code from support/nfs/xmalloc.c by Olaf Kirch <okir@monad.swb.de> here. + */ + +#include <unistd.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "xcommon.h" +#include "nls.h" /* _() */ + +void (*at_die)(void ) = NULL; + +char * +xstrndup (const char *s, int n) { + char *t; + + if (s == NULL) + die (EX_SOFTWARE, _("bug in xstrndup call")); + + t = xmalloc(n+1); + strncpy(t,s,n); + t[n] = 0; + + return t; +} + +char * +xstrconcat2 (const char *s, const char *t) { + char *res; + + if (!s) s = ""; + if (!t) t = ""; + res = xmalloc(strlen(s) + strlen(t) + 1); + strcpy(res, s); + strcat(res, t); + return res; +} + +/* frees its first arg - typical use: s = xstrconcat3(s,t,u); */ +char * +xstrconcat3 (const char *s, const char *t, const char *u) { + char *res; + + int dofree = 1; + + if (!s) s = "", dofree=0; + if (!t) t = ""; + if (!u) u = ""; + res = xmalloc(strlen(s) + strlen(t) + strlen(u) + 1); + strcpy(res, s); + strcat(res, t); + strcat(res, u); + if (dofree) + free((void *) s); + return res; +} + +/* frees its first arg - typical use: s = xstrconcat4(s,t,u,v); */ +char * +xstrconcat4 (const char *s, const char *t, const char *u, const char *v) { + char *res; + + int dofree = 1; + + if (!s) s = "", dofree=0; + if (!t) t = ""; + if (!u) u = ""; + if (!v) v = ""; + res = xmalloc(strlen(s) + strlen(t) + strlen(u) + strlen(v) + 1); + strcpy(res, s); + strcat(res, t); + strcat(res, u); + strcat(res, v); + if (dofree) + free((void *) s); + return res; +} + +/* Non-fatal error. Print message and return. */ +/* (print the message in a single printf, in an attempt + to avoid mixing output of several threads) */ +void +nfs_error (const char *fmt, ...) { + va_list args; + char *fmt2; + + fmt2 = xstrconcat2 (fmt, "\n"); + va_start (args, fmt); + vfprintf (stderr, fmt2, args); + va_end (args); + free (fmt2); +} + +/* Make a canonical pathname from PATH. Returns a freshly malloced string. + It is up the *caller* to ensure that the PATH is sensible. i.e. + canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.'' + is not a legal pathname for ``/dev/fd0''. Anything we cannot parse + we return unmodified. */ +char *canonicalize (const char *path) { + char canonical[PATH_MAX+2]; + + if (path == NULL) + return NULL; + +#if 1 + if (streq(path, "none") || + streq(path, "proc") || + streq(path, "devpts")) + return xstrdup(path); +#endif + if (realpath (path, canonical)) + return xstrdup(canonical); + + return xstrdup(path); +} + +/* Fatal error. Print message and exit. */ +void +die(int err, const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + + if (at_die) + (*at_die)(); + + exit(err); +} + +static void +die_if_null(void *t) { + if (t == NULL) + die(EX_SYSERR, _("not enough memory")); +} + +void * +xmalloc (size_t size) { + void *t; + + if (size == 0) + return NULL; + + t = malloc(size); + die_if_null(t); + + return t; +} + +void * +xrealloc (void *p, size_t size) { + void *t; + + t = realloc(p, size); + die_if_null(t); + + return t; +} + +void +xfree(void *ptr) +{ + free(ptr); +} + +char * +xstrdup (const char *s) { + char *t; + + if (s == NULL) + return NULL; + + t = strdup(s); + die_if_null(t); + + return t; +} diff --git a/support/nfs/xio.c b/support/nfs/xio.c new file mode 100644 index 0000000..6962751 --- /dev/null +++ b/support/nfs/xio.c @@ -0,0 +1,170 @@ +/* + * support/nfs/xio.c + * + * Simple I/O functions for the parsing of /etc/exports and /etc/nfsclients. + * + * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <signal.h> +#include <unistd.h> +#include <errno.h> +#include "xmalloc.h" +#include "xlog.h" +#include "xio.h" + +XFILE * +xfopen(char *fname, char *type) +{ + XFILE *xfp; + FILE *fp; + + if (!(fp = fopen(fname, type))) + return NULL; + xfp = (XFILE *) xmalloc(sizeof(*xfp)); + xfp->x_fp = fp; + xfp->x_line = 1; + + return xfp; +} + +void +xfclose(XFILE *xfp) +{ + fclose(xfp->x_fp); + xfree(xfp); +} + +int +xflock(char *fname, char *type) +{ + int readonly = !strcmp(type, "r"); + struct flock fl = { readonly? F_RDLCK : F_WRLCK, SEEK_SET, 0, 0, 0 }; + int fd; + + if (readonly) + fd = open(fname, (O_RDONLY|O_CREAT), 0600); + else + fd = open(fname, (O_RDWR|O_CREAT), 0600); + if (fd < 0) { + xlog(L_WARNING, "could not open %s for locking: errno %d (%s)", + fname, errno, strerror(errno)); + return -1; + } + + if (fcntl(fd, F_SETLKW, &fl) < 0) { + xlog(L_WARNING, "failed to lock %s: errno %d (%s)", + fname, errno, strerror(errno)); + close(fd); + fd = -1; + } + + return fd; +} + +void +xfunlock(int fd) +{ + close(fd); +} + +#define isoctal(x) (isdigit(x) && ((x)<'8')) +int +xgettok(XFILE *xfp, char sepa, char *tok, int len) +{ + int i = 0; + int c = 0; + int quoted=0; + + while (i < len && (c = xgetc(xfp)) != EOF && + (quoted || (c != sepa && !isspace(c)))) { + if (c == '"') { + quoted = !quoted; + continue; + } + tok[i++] = c; + if (i >= 4 && + tok[i-4] == '\\' && + isoctal(tok[i-3]) && + isoctal(tok[i-2]) && + isoctal(tok[i-1]) && + ((tok[i]=0), + (c = strtol(tok+i-3,NULL, 8)) < 256)) { + i -= 4; + tok[i++] = c; + } + } + if (c == '\n') + xungetc(c, xfp); + if (!i) + return 0; + if (i >= len || (sepa && c != sepa)) + return -1; + tok[i] = '\0'; + return 1; +} + +int +xgetc(XFILE *xfp) +{ + int c = getc(xfp->x_fp); + + if (c == EOF) + return c; + if (c == '\\') { + if ((c = getc(xfp->x_fp)) != '\n') { + ungetc(c, xfp->x_fp); + return '\\'; + } + xfp->x_line++; + while ((c = getc(xfp->x_fp)) == ' ' || c == '\t'); + ungetc(c, xfp->x_fp); + return ' '; + } + if (c == '\n') + xfp->x_line++; + return c; +} + +void +xungetc(int c, XFILE *xfp) +{ + if (c == EOF) + return; + + ungetc(c, xfp->x_fp); + if (c == '\n') + xfp->x_line--; +} + +void +xskip(XFILE *xfp, char *str) +{ + int c; + + while ((c = xgetc(xfp)) != EOF) { + if (c == '#') + c = xskipcomment(xfp); + if (strchr(str, c) == NULL) + break; + } + xungetc(c, xfp); +} + +char +xskipcomment(XFILE *xfp) +{ + int c; + + while ((c = getc(xfp->x_fp)) != EOF && c != '\n'); + return c; +} diff --git a/support/nfs/xlog.c b/support/nfs/xlog.c new file mode 100644 index 0000000..fa125ce --- /dev/null +++ b/support/nfs/xlog.c @@ -0,0 +1,254 @@ +/* + * support/nfs/xlog.c + * + * This module handles the logging of requests. + * + * TODO: Merge the two "XXX_log() calls. + * + * Authors: Donald J. Becker, <becker@super.org> + * Rick Sladkey, <jrs@world.std.com> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Olaf Kirch, <okir@monad.swb.de> + * + * This software maybe be used for any purpose provided + * the above copyright notice is retained. It is supplied + * as is, with no warranty expressed or implied. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <unistd.h> +#include <signal.h> +#include <time.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <stdarg.h> +#include <syslog.h> +#include <errno.h> +#include "nfslib.h" +#include "conffile.h" + +#undef VERBOSE_PRINTF + +#pragma GCC visibility push(hidden) + +static int log_stderr = 1; +static int log_syslog = 1; +static int logging = 0; /* enable/disable DEBUG logs */ +static int logmask = 0; /* What will be logged */ +static char log_name[256]; /* name of this program */ +static int log_pid = -1; /* PID of this program */ + +int export_errno = 0; + +static void xlog_toggle(int sig); +static struct xlog_debugfac debugnames[] = { + { "0", 0, }, + { "general", D_GENERAL, }, + { "call", D_CALL, }, + { "auth", D_AUTH, }, + { "parse", D_PARSE, }, + { "all", D_ALL, }, + { "1", D_ALL, }, + { NULL, 0, }, +}; + +void +xlog_open(char *progname) +{ + openlog(progname, LOG_PID, LOG_DAEMON); + + strncpy(log_name, progname, sizeof (log_name) - 1); + log_name [sizeof (log_name) - 1] = '\0'; + log_pid = getpid(); + + signal(SIGUSR1, xlog_toggle); + signal(SIGUSR2, xlog_toggle); +} + +void +xlog_stderr(int on) +{ + log_stderr = on; +} + +void +xlog_syslog(int on) +{ + log_syslog = on; +} + +static void +xlog_toggle(int sig) +{ + unsigned int tmp, i; + + if (sig == SIGUSR1) { + if ((logmask & D_ALL) && !logging) { + xlog(D_GENERAL, "turned on logging"); + logging = 1; + return; + } + tmp = ~logmask; + logmask |= ((logmask & D_ALL) << 1) | D_GENERAL; + for (i = -1, tmp &= logmask; tmp; tmp >>= 1, i++) + if (tmp & 1) + xlog(D_GENERAL, + "turned on logging level %d", i); + } else { + xlog(D_GENERAL, "turned off logging"); + logging = 0; + } + signal(sig, xlog_toggle); +} + +void +xlog_config(int fac, int on) +{ + if (on) + logmask |= fac; + else + logmask &= ~fac; + if (on) + logging = 1; +} + +void +xlog_sconfig(char *kind, int on) +{ + struct xlog_debugfac *tbl = debugnames; + + while (tbl->df_name != NULL && strcasecmp(tbl->df_name, kind)) + tbl++; + if (!tbl->df_name) { + xlog (L_WARNING, "Invalid debug facility: %s\n", kind); + return; + } + if (tbl->df_fac) + xlog_config(tbl->df_fac, on); +} + +void +xlog_set_debug(char *service) +{ + struct conf_list *kinds; + struct conf_list_node *n; + + kinds = conf_get_list(service, "debug"); + if (!kinds || !kinds->cnt) { + free(kinds); + return; + } + TAILQ_FOREACH(n, &(kinds->fields), link) + xlog_sconfig(n->field, 1); + + conf_free_list(kinds); +} + +int +xlog_enabled(int fac) +{ + return (logging && (fac & logmask)); +} + + +/* Write something to the system logfile and/or stderr */ +void +xlog_backend(int kind, const char *fmt, va_list args) +{ + if (!(kind & (L_ALL)) && !(logging && (kind & logmask))) + return; + + if (log_stderr) { + va_list args2; +#ifdef VERBOSE_PRINTF + time_t now; + struct tm *tm; + + time(&now); + tm = localtime(&now); + fprintf(stderr, "%s[%d] %04d-%02d-%02d %02d:%02d:%02d ", + log_name, log_pid, + tm->tm_year+1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); +#else + fprintf(stderr, "%s: ", log_name); +#endif + va_copy(args2, args); + vfprintf(stderr, fmt, args2); + fprintf(stderr, "\n"); + va_end(args2); + } + + if (log_syslog) { + switch (kind) { + case L_FATAL: + vsyslog(LOG_ERR, fmt, args); + break; + case L_ERROR: + vsyslog(LOG_ERR, fmt, args); + break; + case L_WARNING: + vsyslog(LOG_WARNING, fmt, args); + break; + case L_NOTICE: + vsyslog(LOG_NOTICE, fmt, args); + break; + default: + if (!log_stderr) + vsyslog(LOG_INFO, fmt, args); + break; + } + } + + if (kind == L_FATAL) + exit(1); +} + +void +xlog(int kind, const char* fmt, ...) +{ + va_list args; + + if (kind & (L_ERROR|D_GENERAL)) + export_errno = 1; + + va_start(args, fmt); + xlog_backend(kind, fmt, args); + va_end(args); +} + +void +xlog_warn(const char* fmt, ...) +{ + va_list args; + + va_start(args, fmt); + xlog_backend(L_WARNING, fmt, args); + va_end(args); +} + + +void +xlog_err(const char* fmt, ...) +{ + va_list args; + + va_start(args, fmt); + xlog_backend(L_FATAL, fmt, args); + va_end(args); +} + +void +xlog_errno(int err, const char *fmt, ...) +{ + va_list args; + + errno = err; + va_start(args, fmt); + xlog_backend(L_FATAL, fmt, args); + va_end(args); +} 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); +} |