diff options
Diffstat (limited to 'modules/pam_env')
-rw-r--r-- | modules/pam_env/Makefile.am | 13 | ||||
-rw-r--r-- | modules/pam_env/Makefile.in | 73 | ||||
-rw-r--r-- | modules/pam_env/README | 28 | ||||
-rw-r--r-- | modules/pam_env/README.xml | 34 | ||||
-rw-r--r-- | modules/pam_env/environment.5 | 2 | ||||
-rw-r--r-- | modules/pam_env/pam_env.8 | 20 | ||||
-rw-r--r-- | modules/pam_env/pam_env.8.xml | 107 | ||||
-rw-r--r-- | modules/pam_env/pam_env.c | 766 | ||||
-rw-r--r-- | modules/pam_env/pam_env.conf.5 | 10 | ||||
-rw-r--r-- | modules/pam_env/pam_env.conf.5.xml | 39 | ||||
-rw-r--r-- | modules/pam_env/tst-pam_env-retval.c | 287 |
11 files changed, 1007 insertions, 372 deletions
diff --git a/modules/pam_env/Makefile.am b/modules/pam_env/Makefile.am index c66112d6..f988f109 100644 --- a/modules/pam_env/Makefile.am +++ b/modules/pam_env/Makefile.am @@ -12,20 +12,27 @@ dist_man_MANS = pam_env.conf.5 pam_env.8 environment.5 endif XMLS = README.xml pam_env.conf.5.xml pam_env.8.xml dist_check_SCRIPTS = tst-pam_env -TESTS = $(dist_check_SCRIPTS) +TESTS = $(dist_check_SCRIPTS) $(check_PROGRAMS) securelibdir = $(SECUREDIR) +if HAVE_VENDORDIR +secureconfdir = $(VENDOR_SCONFIGDIR) +else secureconfdir = $(SCONFIGDIR) +endif AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \ - -DDEFAULT_CONF_FILE=\"$(SCONFIGDIR)/pam_env.conf\" $(WARN_CFLAGS) + $(WARN_CFLAGS) -DSYSCONFDIR=\"$(sysconfdir)\" $(ECONF_CFLAGS) AM_LDFLAGS = -no-undefined -avoid-version -module if HAVE_VERSIONING AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map endif securelib_LTLIBRARIES = pam_env.la -pam_env_la_LIBADD = $(top_builddir)/libpam/libpam.la +pam_env_la_LIBADD = $(top_builddir)/libpam/libpam.la $(ECONF_LIBS) + +check_PROGRAMS = tst-pam_env-retval +tst_pam_env_retval_LDADD = $(top_builddir)/libpam/libpam.la dist_secureconf_DATA = pam_env.conf dist_sysconf_DATA = environment diff --git a/modules/pam_env/Makefile.in b/modules/pam_env/Makefile.in index 255458f5..dd3fd05f 100644 --- a/modules/pam_env/Makefile.in +++ b/modules/pam_env/Makefile.in @@ -94,6 +94,7 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @HAVE_VERSIONING_TRUE@am__append_1 = -Wl,--version-script=$(srcdir)/../modules.map +check_PROGRAMS = tst-pam_env-retval$(EXEEXT) subdir = modules/pam_env ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/attribute.m4 \ @@ -151,13 +152,18 @@ am__installdirs = "$(DESTDIR)$(securelibdir)" "$(DESTDIR)$(man5dir)" \ "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(secureconfdir)" \ "$(DESTDIR)$(sysconfdir)" LTLIBRARIES = $(securelib_LTLIBRARIES) -pam_env_la_DEPENDENCIES = $(top_builddir)/libpam/libpam.la +am__DEPENDENCIES_1 = +pam_env_la_DEPENDENCIES = $(top_builddir)/libpam/libpam.la \ + $(am__DEPENDENCIES_1) pam_env_la_SOURCES = pam_env.c pam_env_la_OBJECTS = pam_env.lo 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 = +tst_pam_env_retval_SOURCES = tst-pam_env-retval.c +tst_pam_env_retval_OBJECTS = tst-pam_env-retval.$(OBJEXT) +tst_pam_env_retval_DEPENDENCIES = $(top_builddir)/libpam/libpam.la AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false @@ -173,7 +179,8 @@ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp am__maybe_remake_depfiles = depfiles -am__depfiles_remade = ./$(DEPDIR)/pam_env.Plo +am__depfiles_remade = ./$(DEPDIR)/pam_env.Plo \ + ./$(DEPDIR)/tst-pam_env-retval.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @@ -193,8 +200,8 @@ 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 = pam_env.c -DIST_SOURCES = pam_env.c +SOURCES = pam_env.c tst-pam_env-retval.c +DIST_SOURCES = pam_env.c tst-pam_env-retval.c am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -433,6 +440,7 @@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ +DOCBOOK_RNG = @DOCBOOK_RNG@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -445,11 +453,13 @@ EXEEXT = @EXEEXT@ EXE_CFLAGS = @EXE_CFLAGS@ EXE_LDFLAGS = @EXE_LDFLAGS@ FGREP = @FGREP@ +FILECMD = @FILECMD@ FO2PDF = @FO2PDF@ GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ GMSGFMT = @GMSGFMT@ GMSGFMT_015 = @GMSGFMT_015@ GREP = @GREP@ +HTML_STYLESHEET = @HTML_STYLESHEET@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -481,12 +491,14 @@ LIBSELINUX = @LIBSELINUX@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOGIND_CFLAGS = @LOGIND_CFLAGS@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ +MAN_STYLESHEET = @MAN_STYLESHEET@ MKDIR_P = @MKDIR_P@ MSGFMT = @MSGFMT@ MSGFMT_015 = @MSGFMT_015@ @@ -509,6 +521,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PDF_STYLESHEET = @PDF_STYLESHEET@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ @@ -519,12 +532,16 @@ SECUREDIR = @SECUREDIR@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ -STRINGPARAM_HMAC = @STRINGPARAM_HMAC@ +STRINGPARAM_PROFILECONDITIONS = @STRINGPARAM_PROFILECONDITIONS@ STRINGPARAM_VENDORDIR = @STRINGPARAM_VENDORDIR@ STRIP = @STRIP@ +SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ +SYSTEMD_LIBS = @SYSTEMD_LIBS@ TIRPC_CFLAGS = @TIRPC_CFLAGS@ TIRPC_LIBS = @TIRPC_LIBS@ +TXT_STYLESHEET = @TXT_STYLESHEET@ USE_NLS = @USE_NLS@ +VENDOR_SCONFIGDIR = @VENDOR_SCONFIGDIR@ VERSION = @VERSION@ WARN_CFLAGS = @WARN_CFLAGS@ XGETTEXT = @XGETTEXT@ @@ -596,15 +613,17 @@ EXTRA_DIST = $(XMLS) @HAVE_DOC_TRUE@dist_man_MANS = pam_env.conf.5 pam_env.8 environment.5 XMLS = README.xml pam_env.conf.5.xml pam_env.8.xml dist_check_SCRIPTS = tst-pam_env -TESTS = $(dist_check_SCRIPTS) +TESTS = $(dist_check_SCRIPTS) $(check_PROGRAMS) securelibdir = $(SECUREDIR) -secureconfdir = $(SCONFIGDIR) +@HAVE_VENDORDIR_FALSE@secureconfdir = $(SCONFIGDIR) +@HAVE_VENDORDIR_TRUE@secureconfdir = $(VENDOR_SCONFIGDIR) AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \ - -DDEFAULT_CONF_FILE=\"$(SCONFIGDIR)/pam_env.conf\" $(WARN_CFLAGS) + $(WARN_CFLAGS) -DSYSCONFDIR=\"$(sysconfdir)\" $(ECONF_CFLAGS) AM_LDFLAGS = -no-undefined -avoid-version -module $(am__append_1) securelib_LTLIBRARIES = pam_env.la -pam_env_la_LIBADD = $(top_builddir)/libpam/libpam.la +pam_env_la_LIBADD = $(top_builddir)/libpam/libpam.la $(ECONF_LIBS) +tst_pam_env_retval_LDADD = $(top_builddir)/libpam/libpam.la dist_secureconf_DATA = pam_env.conf dist_sysconf_DATA = environment @ENABLE_REGENERATE_MAN_TRUE@dist_noinst_DATA = README @@ -642,6 +661,15 @@ $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + install-securelibLTLIBRARIES: $(securelib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(securelib_LTLIBRARIES)'; test -n "$(securelibdir)" || list=; \ @@ -680,6 +708,10 @@ clean-securelibLTLIBRARIES: pam_env.la: $(pam_env_la_OBJECTS) $(pam_env_la_DEPENDENCIES) $(EXTRA_pam_env_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) -rpath $(securelibdir) $(pam_env_la_OBJECTS) $(pam_env_la_LIBADD) $(LIBS) +tst-pam_env-retval$(EXEEXT): $(tst_pam_env_retval_OBJECTS) $(tst_pam_env_retval_DEPENDENCIES) $(EXTRA_tst_pam_env_retval_DEPENDENCIES) + @rm -f tst-pam_env-retval$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(tst_pam_env_retval_OBJECTS) $(tst_pam_env_retval_LDADD) $(LIBS) + mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -687,6 +719,7 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_env.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-pam_env-retval.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @@ -1021,7 +1054,7 @@ $(TEST_SUITE_LOG): $(TEST_LOGS) fi; \ $$success || exit 1 -check-TESTS: $(dist_check_SCRIPTS) +check-TESTS: $(check_PROGRAMS) $(dist_check_SCRIPTS) @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @@ -1031,7 +1064,7 @@ check-TESTS: $(dist_check_SCRIPTS) log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ exit $$?; -recheck: all $(dist_check_SCRIPTS) +recheck: all $(check_PROGRAMS) $(dist_check_SCRIPTS) @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ bases=`for i in $$bases; do echo $$i; done \ @@ -1049,6 +1082,13 @@ tst-pam_env.log: tst-pam_env --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) +tst-pam_env-retval.log: tst-pam_env-retval$(EXEEXT) + @p='tst-pam_env-retval$(EXEEXT)'; \ + b='tst-pam_env-retval'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) .test.log: @p='$<'; \ $(am__set_b); \ @@ -1098,7 +1138,8 @@ distdir-am: $(DISTFILES) fi; \ done check-am: all-am - $(MAKE) $(AM_MAKEFLAGS) $(dist_check_SCRIPTS) + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) \ + $(dist_check_SCRIPTS) $(MAKE) $(AM_MAKEFLAGS) check-TESTS check: check-am all-am: Makefile $(LTLIBRARIES) $(MANS) $(DATA) @@ -1143,11 +1184,12 @@ maintainer-clean-generic: -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am -clean-am: clean-generic clean-libtool clean-securelibLTLIBRARIES \ - mostlyclean-am +clean-am: clean-checkPROGRAMS clean-generic clean-libtool \ + clean-securelibLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -f ./$(DEPDIR)/pam_env.Plo + -rm -f ./$(DEPDIR)/tst-pam_env-retval.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags @@ -1195,6 +1237,7 @@ installcheck-am: maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/pam_env.Plo + -rm -f ./$(DEPDIR)/tst-pam_env-retval.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic @@ -1219,7 +1262,7 @@ uninstall-man: uninstall-man5 uninstall-man8 .MAKE: check-am install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \ - check-am clean clean-generic clean-libtool \ + check-am clean clean-checkPROGRAMS clean-generic clean-libtool \ clean-securelibLTLIBRARIES cscopelist-am ctags ctags-am \ distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ diff --git a/modules/pam_env/README b/modules/pam_env/README index a040caf7..f10a02b4 100644 --- a/modules/pam_env/README +++ b/modules/pam_env/README @@ -8,10 +8,38 @@ The pam_env PAM module allows the (un)setting of environment variables. Supported is the use of previously set environment variables as well as PAM_ITEMs such as PAM_RHOST. +Rules for (un)setting of variables can be defined in an own config file. The +path to this file can be specified with the conffile option. If this file does +not exist, the default rules are taken from the config files /etc/security/ +pam_env.conf and /etc/security/pam_env.conf.d/*.conf. If the file /etc/security +/pam_env.conf does not exist, the rules are taken from the files %vendordir%/ +security/pam_env.conf, %vendordir%/security/pam_env.conf.d/*.conf and /etc/ +security/pam_env.conf.d/*.conf in that order. + +By default rules for (un)setting of variables are taken from the config file / +etc/security/pam_env.conf. If this file does not exist %vendordir%/security/ +pam_env.conf is used. An alternate file can be specified with the conffile +option, which overrules all other files. + By default rules for (un)setting of variables are taken from the config file / etc/security/pam_env.conf. An alternate file can be specified with the conffile option. +Environment variables can be defined in a file with simple KEY=VAL pairs on +separate lines. The path to this file can be specified with the envfile option. +If this file has not been defined, the settings are read from the files /etc/ +security/environment and /etc/security/environment.d/*. If the file /etc/ +environment does not exist, the settings are read from the files %vendordir%/ +environment, %vendordir%/environment.d/* and /etc/environment.d/* in that +order. And last but not least, with the readenv option this mechanism can be +completely disabled. + +Second a file (/etc/environment by default) with simple KEY=VAL pairs on +separate lines will be read. If this file does not exist, %vendordir%/etc/ +environment is used. With the envfile option an alternate file can be +specified, which overrules all other files. And with the readenv option this +can be completely disabled. + Second a file (/etc/environment by default) with simple KEY=VAL pairs on separate lines will be read. With the envfile option an alternate file can be specified. And with the readenv option this can be completely disabled. diff --git a/modules/pam_env/README.xml b/modules/pam_env/README.xml index 21a9b855..8becf870 100644 --- a/modules/pam_env/README.xml +++ b/modules/pam_env/README.xml @@ -1,39 +1,21 @@ -<?xml version="1.0" encoding='UTF-8'?> -<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" -"http://www.docbook.org/xml/4.3/docbookx.dtd" -[ -<!-- -<!ENTITY pamaccess SYSTEM "pam_env.8.xml"> ---> -<!-- -<!ENTITY accessconf SYSTEM "pam_env.conf.5.xml"> ---> -]> - -<article> - - <articleinfo> +<article xmlns="http://docbook.org/ns/docbook" version="5.0"> + <info> <title> - <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" - href="pam_env.8.xml" xpointer='xpointer(//refnamediv[@id = "pam_env-name"]/*)'/> + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_env.8.xml" xpointer='xpointer(id("pam_env-name")/*)'/> </title> - - </articleinfo> + </info> <section> - <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" - href="pam_env.8.xml" xpointer='xpointer(//refsect1[@id = "pam_env-description"]/*)'/> + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_env.8.xml" xpointer='xpointer(id("pam_env-description")/*)'/> </section> <section> - <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" - href="pam_env.8.xml" xpointer='xpointer(//refsect1[@id = "pam_env-options"]/*)'/> + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_env.8.xml" xpointer='xpointer(id("pam_env-options")/*)'/> </section> <section> - <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" - href="pam_env.conf.5.xml" xpointer='xpointer(//refsect1[@id = "pam_env.conf-examples"]/*)'/> + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_env.conf.5.xml" xpointer='xpointer(id("pam_env.conf-examples")/*)'/> </section> -</article> +</article>
\ No newline at end of file diff --git a/modules/pam_env/environment.5 b/modules/pam_env/environment.5 index aa670e2e..415dfc1c 100644 --- a/modules/pam_env/environment.5 +++ b/modules/pam_env/environment.5 @@ -1 +1 @@ -.so man5/pam_env.conf.5 +.so pam_env.conf.5 diff --git a/modules/pam_env/pam_env.8 b/modules/pam_env/pam_env.8 index 8b0d5199..f4e15f30 100644 --- a/modules/pam_env/pam_env.8 +++ b/modules/pam_env/pam_env.8 @@ -1,13 +1,13 @@ '\" t .\" Title: pam_env .\" Author: [see the "AUTHOR" section] -.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/> -.\" Date: 09/03/2021 +.\" Generator: DocBook XSL Stylesheets v1.79.2 <http://docbook.sf.net/> +.\" Date: 05/07/2023 .\" Manual: Linux-PAM Manual -.\" Source: Linux-PAM Manual +.\" Source: Linux-PAM .\" Language: English .\" -.TH "PAM_ENV" "8" "09/03/2021" "Linux-PAM Manual" "Linux-PAM Manual" +.TH "PAM_ENV" "8" "05/07/2023" "Linux\-PAM" "Linux\-PAM Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -62,20 +62,20 @@ option\&. Since setting of PAM environment variables can have side effects to other modules, this module should be the last one on the stack\&. .SH "OPTIONS" .PP -\fBconffile=\fR\fB\fI/path/to/pam_env\&.conf\fR\fR +conffile=/path/to/pam_env\&.conf .RS 4 Indicate an alternative pam_env\&.conf style configuration file to override the default\&. This can be useful when different services need different environments\&. .RE .PP -\fBdebug\fR +debug .RS 4 A lot of debug information is printed with \fBsyslog\fR(3)\&. .RE .PP -\fBenvfile=\fR\fB\fI/path/to/environment\fR\fR +envfile=/path/to/environment .RS 4 Indicate an alternative environment @@ -86,12 +86,12 @@ pairs on separate lines\&. The instruction can be specified for bash compatibility, but will be ignored\&. This can be useful when different services need different environments\&. .RE .PP -\fBreadenv=\fR\fB\fI0|1\fR\fR +readenv=0|1 .RS 4 Turns on or off the reading of the file specified by envfile (0 is off, 1 is on)\&. By default this option is on\&. .RE .PP -\fBuser_envfile=\fR\fB\fIfilename\fR\fR +user_envfile=filename .RS 4 Indicate an alternative \&.pam_environment @@ -99,7 +99,7 @@ file to override the default\&.The syntax is the same as for \fI/etc/security/pam_env\&.conf\fR\&. The filename is relative to the user home directory\&. This can be useful when different services need different environments\&. .RE .PP -\fBuser_readenv=\fR\fB\fI0|1\fR\fR +user_readenv=0|1 .RS 4 Turns on or off the reading of the user specific environment file\&. 0 is off, 1 is on\&. By default this option is off as user supplied environment variables in the PAM environment could affect behavior of subsequent modules in the stack without the consent of the system administrator\&. .sp diff --git a/modules/pam_env/pam_env.8.xml b/modules/pam_env/pam_env.8.xml index 75ff862b..fb172e17 100644 --- a/modules/pam_env/pam_env.8.xml +++ b/modules/pam_env/pam_env.8.xml @@ -1,16 +1,13 @@ -<?xml version="1.0" encoding="ISO-8859-1"?> -<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" - "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd"> - -<refentry id='pam_env'> +<refentry xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="pam_env"> <refmeta> <refentrytitle>pam_env</refentrytitle> <manvolnum>8</manvolnum> - <refmiscinfo class='setdesc'>Linux-PAM Manual</refmiscinfo> + <refmiscinfo class="source">Linux-PAM</refmiscinfo> + <refmiscinfo class="manual">Linux-PAM Manual</refmiscinfo> </refmeta> - <refnamediv id='pam_env-name'> + <refnamediv xml:id="pam_env-name"> <refname>pam_env</refname> <refpurpose> PAM module to set/unset environment variables @@ -20,31 +17,31 @@ <!-- body begins here --> <refsynopsisdiv> - <cmdsynopsis id="pam_env-cmdsynopsis"> + <cmdsynopsis xml:id="pam_env-cmdsynopsis" sepchar=" "> <command>pam_env.so</command> - <arg choice="opt"> + <arg choice="opt" rep="norepeat"> debug </arg> - <arg choice="opt"> + <arg choice="opt" rep="norepeat"> conffile=<replaceable>conf-file</replaceable> </arg> - <arg choice="opt"> + <arg choice="opt" rep="norepeat"> envfile=<replaceable>env-file</replaceable> </arg> - <arg choice="opt"> + <arg choice="opt" rep="norepeat"> readenv=<replaceable>0|1</replaceable> </arg> - <arg choice="opt"> + <arg choice="opt" rep="norepeat"> user_envfile=<replaceable>env-file</replaceable> </arg> - <arg choice="opt"> + <arg choice="opt" rep="norepeat"> user_readenv=<replaceable>0|1</replaceable> </arg> </cmdsynopsis> </refsynopsisdiv> - <refsect1 id="pam_env-description"> + <refsect1 xml:id="pam_env-description"> <title>DESCRIPTION</title> <para> The pam_env PAM module allows the (un)setting of environment @@ -52,13 +49,55 @@ variables as well as <emphasis>PAM_ITEM</emphasis>s such as <emphasis>PAM_RHOST</emphasis>. </para> - <para> + <para condition="with_vendordir_and_with_econf"> + Rules for (un)setting of variables can be defined in an own config + file. The path to this file can be specified with the + <emphasis>conffile</emphasis> option. + If this file does not exist, the default rules are taken from the + config files <filename>/etc/security/pam_env.conf</filename> and + <filename>/etc/security/pam_env.conf.d/*.conf</filename>. + If the file <filename>/etc/security/pam_env.conf</filename> does not + exist, the rules are taken from the files + <filename>%vendordir%/security/pam_env.conf</filename>, + <filename>%vendordir%/security/pam_env.conf.d/*.conf</filename> and + <filename>/etc/security/pam_env.conf.d/*.conf</filename> in that order. + </para> + <para condition="with_vendordir_and_without_econf"> + By default rules for (un)setting of variables are taken from the + config file <filename>/etc/security/pam_env.conf</filename>. + If this file does not exist <filename>%vendordir%/security/pam_env.conf</filename> is used. + An alternate file can be specified with the <emphasis>conffile</emphasis> + option, which overrules all other files. + </para> + <para condition="without_vendordir"> By default rules for (un)setting of variables are taken from the config file <filename>/etc/security/pam_env.conf</filename>. An alternate file can be specified with the <emphasis>conffile</emphasis> option. </para> - <para> + <para condition="with_vendordir_and_with_econf"> + Environment variables can be defined in a file with simple <emphasis>KEY=VAL</emphasis> + pairs on separate lines. The path to this file can be specified with the + <emphasis>envfile</emphasis> option. + If this file has not been defined, the settings are read from the + files <filename>/etc/security/environment</filename> and + <filename>/etc/security/environment.d/*</filename>. + If the file <filename>/etc/environment</filename> does not exist, the + settings are read from the files <filename>%vendordir%/environment</filename>, + <filename>%vendordir%/environment.d/*</filename> and + <filename>/etc/environment.d/*</filename> in that order. + And last but not least, with the <emphasis>readenv</emphasis> option this mechanism can + be completely disabled. + </para> + <para condition="with_vendordir_and_without_econf"> + Second a file (<filename>/etc/environment</filename> by default) with simple + <emphasis>KEY=VAL</emphasis> pairs on separate lines will be read. + If this file does not exist, <filename>%vendordir%/etc/environment</filename> is used. + With the <emphasis>envfile</emphasis> option an alternate file can be specified, + which overrules all other files. + And with the <emphasis>readenv</emphasis> option this can be completely disabled. + </para> + <para condition="without_vendordir"> Second a file (<filename>/etc/environment</filename> by default) with simple <emphasis>KEY=VAL</emphasis> pairs on separate lines will be read. With the <emphasis>envfile</emphasis> option an alternate file can be specified. @@ -77,13 +116,13 @@ </para> </refsect1> - <refsect1 id="pam_env-options"> + <refsect1 xml:id="pam_env-options"> <title>OPTIONS</title> <variablelist> <varlistentry> <term> - <option>conffile=<replaceable>/path/to/pam_env.conf</replaceable></option> + conffile=/path/to/pam_env.conf </term> <listitem> <para> @@ -96,7 +135,7 @@ <varlistentry> <term> - <option>debug</option> + debug </term> <listitem> <para> @@ -108,7 +147,7 @@ <varlistentry> <term> - <option>envfile=<replaceable>/path/to/environment</replaceable></option> + envfile=/path/to/environment </term> <listitem> <para> @@ -124,7 +163,7 @@ <varlistentry> <term> - <option>readenv=<replaceable>0|1</replaceable></option> + readenv=0|1 </term> <listitem> <para> @@ -137,7 +176,7 @@ <varlistentry> <term> - <option>user_envfile=<replaceable>filename</replaceable></option> + user_envfile=filename </term> <listitem> <para> @@ -153,7 +192,7 @@ <varlistentry> <term> - <option>user_readenv=<replaceable>0|1</replaceable></option> + user_readenv=0|1 </term> <listitem> <para> @@ -174,7 +213,7 @@ </variablelist> </refsect1> - <refsect1 id="pam_env-types"> + <refsect1 xml:id="pam_env-types"> <title>MODULE TYPES PROVIDED</title> <para> The <option>auth</option> and <option>session</option> module @@ -182,7 +221,7 @@ </para> </refsect1> - <refsect1 id="pam_env-return_values"> + <refsect1 xml:id="pam_env-return_values"> <title>RETURN VALUES</title> <variablelist> <varlistentry> @@ -220,23 +259,25 @@ </variablelist> </refsect1> - <refsect1 id="pam_env-files"> + <refsect1 xml:id="pam_env-files"> <title>FILES</title> <variablelist> <varlistentry> - <term><filename>/etc/security/pam_env.conf</filename></term> + <term condition="with_vendordir">%vendordir%/security/pam_env.conf</term> + <term>/etc/security/pam_env.conf</term> <listitem> <para>Default configuration file</para> </listitem> </varlistentry> <varlistentry> - <term><filename>/etc/environment</filename></term> + <term condition="with_vendordir">%vendordir%/environment</term> + <term>/etc/environment</term> <listitem> <para>Default environment file</para> </listitem> </varlistentry> <varlistentry> - <term><filename>$HOME/.pam_environment</filename></term> + <term>$HOME/.pam_environment</term> <listitem> <para>User specific environment file</para> </listitem> @@ -244,7 +285,7 @@ </variablelist> </refsect1> - <refsect1 id="pam_env-see_also"> + <refsect1 xml:id="pam_env-see_also"> <title>SEE ALSO</title> <para> <citerefentry> @@ -262,10 +303,10 @@ </para> </refsect1> - <refsect1 id="pam_env-authors"> + <refsect1 xml:id="pam_env-authors"> <title>AUTHOR</title> <para> pam_env was written by Dave Kinchlea <kinch@kinch.ark.com>. </para> </refsect1> -</refentry> +</refentry>
\ No newline at end of file diff --git a/modules/pam_env/pam_env.c b/modules/pam_env/pam_env.c index f5f8cead..d2b4cb10 100644 --- a/modules/pam_env/pam_env.c +++ b/modules/pam_env/pam_env.c @@ -7,6 +7,9 @@ */ #define DEFAULT_ETC_ENVFILE "/etc/environment" +#ifdef VENDORDIR +#define VENDOR_DEFAULT_ETC_ENVFILE (VENDORDIR "/environment") +#endif #define DEFAULT_READ_ENVFILE 1 #define DEFAULT_USER_ENVFILE ".pam_environment" @@ -25,6 +28,9 @@ #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> +#ifdef USE_ECONF +#include <libeconf.h> +#endif #include <security/pam_modules.h> #include <security/pam_modutil.h> @@ -41,6 +47,11 @@ typedef struct var { char *override; } VAR; +#define DEFAULT_CONF_FILE (SCONFIGDIR "/pam_env.conf") +#ifdef VENDOR_SCONFIGDIR +#define VENDOR_DEFAULT_CONF_FILE (VENDOR_SCONFIGDIR "/pam_env.conf") +#endif + #define BUF_SIZE 8192 #define MAX_ENV 8192 @@ -51,18 +62,20 @@ typedef struct var { #define UNDEFINE_VAR 102 #define ILLEGAL_VAR 103 -static int _assemble_line(FILE *, char *, int); -static int _parse_line(const pam_handle_t *, const char *, VAR *); -static int _check_var(pam_handle_t *, VAR *); /* This is the real meat */ -static void _clean_var(VAR *); -static int _expand_arg(pam_handle_t *, char **); -static const char * _pam_get_item_byname(pam_handle_t *, const char *); -static int _define_var(pam_handle_t *, int, VAR *); -static int _undefine_var(pam_handle_t *, int, VAR *); - /* This is a special value used to designate an empty string */ static char quote='\0'; +static void free_string_array(char **array) +{ + if (array == NULL) + return; + for (char **entry = array; *entry != NULL; ++entry) { + pam_overwrite_string(*entry); + free(*entry); + } + free(array); +} + /* argument parsing */ #define PAM_DEBUG_ARG 0x01 @@ -75,10 +88,10 @@ _pam_parse (const pam_handle_t *pamh, int argc, const char **argv, int ctrl=0; *user_envfile = DEFAULT_USER_ENVFILE; - *envfile = DEFAULT_ETC_ENVFILE; + *envfile = NULL; *readenv = DEFAULT_READ_ENVFILE; *user_readenv = DEFAULT_USER_READ_ENVFILE; - *conffile = DEFAULT_CONF_FILE; + *conffile = NULL; /* step through arguments */ for (; argc-- > 0; ++argv) { @@ -126,166 +139,155 @@ _pam_parse (const pam_handle_t *pamh, int argc, const char **argv, return ctrl; } -static int -_parse_config_file(pam_handle_t *pamh, int ctrl, const char *file) -{ - int retval; - char buffer[BUF_SIZE]; - FILE *conf; - VAR Var, *var=&Var; - - D(("Called.")); - - var->name=NULL; var->defval=NULL; var->override=NULL; - - D(("Config file name is: %s", file)); - - /* - * Lets try to open the config file, parse it and process - * any variables found. - */ - - if ((conf = fopen(file,"r")) == NULL) { - pam_syslog(pamh, LOG_ERR, "Unable to open config file: %s: %m", file); - return PAM_IGNORE; - } - - /* _pam_assemble_line will provide a complete line from the config file, - * with all comments removed and any escaped newlines fixed up - */ - - while (( retval = _assemble_line(conf, buffer, BUF_SIZE)) > 0) { - D(("Read line: %s", buffer)); - - if ((retval = _parse_line(pamh, buffer, var)) == GOOD_LINE) { - retval = _check_var(pamh, var); - - if (DEFINE_VAR == retval) { - retval = _define_var(pamh, ctrl, var); - - } else if (UNDEFINE_VAR == retval) { - retval = _undefine_var(pamh, ctrl, var); - } - } - if (PAM_SUCCESS != retval && ILLEGAL_VAR != retval - && BAD_LINE != retval && PAM_BAD_ITEM != retval) break; - - _clean_var(var); - - } /* while */ +#ifdef USE_ECONF - (void) fclose(conf); +#define ENVIRONMENT "environment" +#define PAM_ENV "pam_env" - /* tidy up */ - _clean_var(var); /* We could have got here prematurely, - * this is safe though */ - D(("Exit.")); - return (retval != 0 ? PAM_ABORT : PAM_SUCCESS); +static int +isDirectory(const char *path) { + struct stat statbuf; + if (stat(path, &statbuf) != 0) + return 0; + return S_ISDIR(statbuf.st_mode); } static int -_parse_env_file(pam_handle_t *pamh, int ctrl, const char *file) +econf_read_file(const pam_handle_t *pamh, const char *filename, const char *delim, + const char *name, const char *suffix, const char *subpath, + char ***lines) { - int retval=PAM_SUCCESS, i, t; - char buffer[BUF_SIZE], *key, *mark; - FILE *conf; - - D(("Env file name is: %s", file)); - - if ((conf = fopen(file,"r")) == NULL) { - pam_syslog(pamh, LOG_ERR, "Unable to open env file: %s: %m", file); - return PAM_IGNORE; + econf_file *key_file = NULL; + econf_err error; + size_t key_number = 0; + char **keys = NULL; + const char *base_dir = ""; + + if (filename != NULL) { + if (isDirectory(filename)) { + /* Set base directory which can be different from root */ + D(("filename argument is a directory: %s", filename)); + base_dir = filename; + } else { + /* Read only one file */ + error = econf_readFile (&key_file, filename, delim, "#"); + D(("File name is: %s", filename)); + if (error != ECONF_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "Unable to open env file: %s: %s", filename, + econf_errString(error)); + if (error == ECONF_NOFILE) + return PAM_IGNORE; + else + return PAM_ABORT; + } + } } + if (filename == NULL || base_dir[0] != '\0') { + /* Read and merge all setting in e.g. /usr/etc and /etc */ + char *vendor_dir = NULL, *sysconf_dir; + if (subpath != NULL && subpath[0] != '\0') { +#ifdef VENDORDIR + if (asprintf(&vendor_dir, "%s%s/%s/", base_dir, VENDORDIR, subpath) < 0) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory."); + return PAM_BUF_ERR; + } +#endif + if (asprintf(&sysconf_dir, "%s%s/%s/", base_dir, SYSCONFDIR, subpath) < 0) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory."); + free(vendor_dir); + return PAM_BUF_ERR; + } + } else { +#ifdef VENDORDIR + if (asprintf(&vendor_dir, "%s%s/", base_dir, VENDORDIR) < 0) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory."); + return PAM_BUF_ERR; + } +#endif + if (asprintf(&sysconf_dir, "%s%s/", base_dir, SYSCONFDIR) < 0) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory."); + free(vendor_dir); + return PAM_BUF_ERR; + } + } - while (_assemble_line(conf, buffer, BUF_SIZE) > 0) { - D(("Read line: %s", buffer)); - key = buffer; - - /* skip leading white space */ - key += strspn(key, " \n\t"); - - /* skip blanks lines and comments */ - if (key[0] == '#') - continue; - - /* skip over "export " if present so we can be compat with - bash type declarations */ - if (strncmp(key, "export ", (size_t) 7) == 0) - key += 7; - - /* now find the end of value */ - mark = key; - while(mark[0] != '\n' && mark[0] != '#' && mark[0] != '\0') - mark++; - if (mark[0] != '\0') - mark[0] = '\0'; - - /* - * sanity check, the key must be alphanumeric - */ - - if (key[0] == '=') { - pam_syslog(pamh, LOG_ERR, - "missing key name '%s' in %s', ignoring", - key, file); - continue; + D(("Read configuration from directory %s and %s", vendor_dir, sysconf_dir)); + error = econf_readDirs (&key_file, vendor_dir, sysconf_dir, name, suffix, + delim, "#"); + free(vendor_dir); + free(sysconf_dir); + if (error != ECONF_SUCCESS) { + if (error == ECONF_NOFILE) { + pam_syslog(pamh, LOG_ERR, "Configuration file not found: %s%s", name, suffix); + return PAM_IGNORE; + } else { + char *error_filename = NULL; + uint64_t error_line = 0; + + econf_errLocation(&error_filename, &error_line); + pam_syslog(pamh, LOG_ERR, "Unable to read configuration file %s line %ld: %s", + error_filename, + error_line, + econf_errString(error)); + free(error_filename); + return PAM_ABORT; } + } + } - for ( i = 0 ; key[i] != '=' && key[i] != '\0' ; i++ ) - if (!isalnum(key[i]) && key[i] != '_') { - pam_syslog(pamh, LOG_ERR, - "non-alphanumeric key '%s' in %s', ignoring", - key, file); - break; - } - /* non-alphanumeric key, ignore this line */ - if (key[i] != '=' && key[i] != '\0') - continue; + error = econf_getKeys(key_file, NULL, &key_number, &keys); + if (error != ECONF_SUCCESS && error != ECONF_NOKEY) { + pam_syslog(pamh, LOG_ERR, "Unable to read keys: %s", + econf_errString(error)); + econf_freeFile(key_file); + return PAM_ABORT; + } - /* now we try to be smart about quotes around the value, - but not too smart, we can't get all fancy with escaped - values like bash */ - if (key[i] == '=' && (key[++i] == '\"' || key[i] == '\'')) { - for ( t = i+1 ; key[t] != '\0' ; t++) - if (key[t] != '\"' && key[t] != '\'') - key[i++] = key[t]; - else if (key[t+1] != '\0') - key[i++] = key[t]; - key[i] = '\0'; - } + *lines = malloc((key_number +1)* sizeof(char**)); + if (*lines == NULL) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory."); + econf_free(keys); + econf_freeFile(key_file); + return PAM_BUF_ERR; + } - /* if this is a request to delete a variable, check that it's - actually set first, so we don't get a vague error back from - pam_putenv() */ - for (i = 0; key[i] != '=' && key[i] != '\0'; i++); + (*lines)[key_number] = 0; - if (key[i] == '\0' && !pam_getenv(pamh,key)) - continue; + for (size_t i = 0; i < key_number; i++) { + char *val; - /* set the env var, if it fails, we break out of the loop */ - retval = pam_putenv(pamh, key); - if (retval != PAM_SUCCESS) { - D(("error setting env \"%s\"", key)); - break; - } else if (ctrl & PAM_DEBUG_ARG) { - pam_syslog(pamh, LOG_DEBUG, - "pam_putenv(\"%s\")", key); + error = econf_getStringValue (key_file, NULL, keys[i], &val); + if (error != ECONF_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "Unable to get string from key %s: %s", + keys[i], + econf_errString(error)); + } else { + if (asprintf(&(*lines)[i],"%s%c%s", keys[i], delim[0], val) < 0) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory."); + econf_free(keys); + econf_freeFile(key_file); + (*lines)[i] = NULL; + free_string_array(*lines); + free (val); + return PAM_BUF_ERR; } + free (val); + } } - (void) fclose(conf); - - /* tidy up */ - D(("Exit.")); - return retval; + econf_free(keys); + econf_free(key_file); + return PAM_SUCCESS; } +#else + /* * This is where we read a line of the PAM config file. The line may be * preceded by lines of comments and also extended with "\\\n" */ - -static int _assemble_line(FILE *f, char *buffer, int buf_len) +static int +_assemble_line(FILE *f, char *buffer, int buf_len) { char *p = buffer; char *s, *os; @@ -373,8 +375,57 @@ static int _assemble_line(FILE *f, char *buffer, int buf_len) return used; } +static int read_file(const pam_handle_t *pamh, const char*filename, char ***lines) +{ + FILE *conf; + char buffer[BUF_SIZE]; + + D(("Parsed file name is: %s", filename)); + + if ((conf = fopen(filename,"r")) == NULL) { + pam_syslog(pamh, LOG_ERR, "Unable to open env file: %s", filename); + return PAM_IGNORE; + } + + size_t i = 0; + *lines = malloc((i + 1)* sizeof(char**)); + if (*lines == NULL) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory."); + (void) fclose(conf); + return PAM_BUF_ERR; + } + (*lines)[i] = 0; + while (_assemble_line(conf, buffer, BUF_SIZE) > 0) { + char **tmp = NULL; + D(("Read line: %s", buffer)); + tmp = realloc(*lines, (++i + 1) * sizeof(char**)); + if (tmp == NULL) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory."); + (void) fclose(conf); + free_string_array(*lines); + pam_overwrite_array(buffer); + return PAM_BUF_ERR; + } + *lines = tmp; + (*lines)[i-1] = strdup(buffer); + if ((*lines)[i-1] == NULL) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory."); + (void) fclose(conf); + free_string_array(*lines); + pam_overwrite_array(buffer); + return PAM_BUF_ERR; + } + (*lines)[i] = 0; + } + + (void) fclose(conf); + pam_overwrite_array(buffer); + return PAM_SUCCESS; +} +#endif + static int -_parse_line (const pam_handle_t *pamh, const char *buffer, VAR *var) +_parse_line(const pam_handle_t *pamh, const char *buffer, VAR *var) { /* * parse buffer into var, legal syntax is @@ -454,7 +505,8 @@ _parse_line (const pam_handle_t *pamh, const char *buffer, VAR *var) } (void)strncpy(*valptr,ptr,length); (*valptr)[length]='\0'; - } else if (quoteflg--) { + } else if (quoteflg) { + quoteflg--; *valptr = "e; /* a quick hack to handle the empty string */ } ptr = tmpptr; /* Start the search where we stopped */ @@ -469,75 +521,57 @@ _parse_line (const pam_handle_t *pamh, const char *buffer, VAR *var) return GOOD_LINE; } -static int _check_var(pam_handle_t *pamh, VAR *var) +static const char * +_pam_get_item_byname(pam_handle_t *pamh, const char *name) { /* - * Examine the variable and determine what action to take. - * Returns DEFINE_VAR, UNDEFINE_VAR depending on action to take - * or a PAM_* error code if passed back from other routines - * - * if no DEFAULT provided, the empty string is assumed - * if no OVERRIDE provided, the empty string is assumed - * if DEFAULT= and OVERRIDE evaluates to the empty string, - * this variable should be undefined - * if DEFAULT="" and OVERRIDE evaluates to the empty string, - * this variable should be defined with no value - * if OVERRIDE=value and value turns into the empty string, DEFAULT is used - * - * If DEFINE_VAR is to be returned, the correct value to define will - * be pointed to by var->value + * This function just allows me to use names as given in the config + * file and translate them into the appropriate PAM_ITEM macro */ - int retval; + int item; + const void *itemval; D(("Called.")); - - /* - * First thing to do is to expand any arguments, but only - * if they are not the special quote values (cause expand_arg - * changes memory). - */ - - if (var->defval && ("e != var->defval) && - ((retval = _expand_arg(pamh, &(var->defval))) != PAM_SUCCESS)) { - return retval; - } - if (var->override && ("e != var->override) && - ((retval = _expand_arg(pamh, &(var->override))) != PAM_SUCCESS)) { - return retval; + if (strcmp(name, "PAM_USER") == 0 || strcmp(name, "HOME") == 0 || strcmp(name, "SHELL") == 0) { + item = PAM_USER; + } else if (strcmp(name, "PAM_USER_PROMPT") == 0) { + item = PAM_USER_PROMPT; + } else if (strcmp(name, "PAM_TTY") == 0) { + item = PAM_TTY; + } else if (strcmp(name, "PAM_RUSER") == 0) { + item = PAM_RUSER; + } else if (strcmp(name, "PAM_RHOST") == 0) { + item = PAM_RHOST; + } else { + D(("Unknown PAM_ITEM: <%s>", name)); + pam_syslog (pamh, LOG_ERR, "Unknown PAM_ITEM: <%s>", name); + return NULL; } - /* Now its easy */ - - if (var->override && *(var->override)) { - /* if there is a non-empty string in var->override, we use it */ - D(("OVERRIDE variable <%s> being used: <%s>", var->name, var->override)); - var->value = var->override; - retval = DEFINE_VAR; - } else { + if (pam_get_item(pamh, item, &itemval) != PAM_SUCCESS) { + D(("pam_get_item failed")); + return NULL; /* let pam_get_item() log the error */ + } - var->value = var->defval; - if ("e == var->defval) { - /* - * This means that the empty string was given for defval value - * which indicates that a variable should be defined with no value - */ - D(("An empty variable: <%s>", var->name)); - retval = DEFINE_VAR; - } else if (var->defval) { - D(("DEFAULT variable <%s> being used: <%s>", var->name, var->defval)); - retval = DEFINE_VAR; - } else { - D(("UNDEFINE variable <%s>", var->name)); - retval = UNDEFINE_VAR; + if (itemval && (strcmp(name, "HOME") == 0 || strcmp(name, "SHELL") == 0)) { + struct passwd *user_entry; + user_entry = pam_modutil_getpwnam (pamh, itemval); + if (!user_entry) { + pam_syslog(pamh, LOG_ERR, "No such user!?"); + return NULL; } + return (strcmp(name, "SHELL") == 0) ? + user_entry->pw_shell : + user_entry->pw_dir; } D(("Exit.")); - return retval; + return itemval; } -static int _expand_arg(pam_handle_t *pamh, char **value) +static int +_expand_arg(pam_handle_t *pamh, char **value) { const char *orig=*value, *tmpptr=NULL; char *ptr; /* @@ -550,12 +584,8 @@ static int _expand_arg(pam_handle_t *pamh, char **value) char type, tmpval[BUF_SIZE]; /* I know this shouldn't be hard-coded but it's so much easier this way */ - char tmp[MAX_ENV]; - size_t idx; - - D(("Remember to initialize tmp!")); - memset(tmp, 0, MAX_ENV); - idx = 0; + char tmp[MAX_ENV] = {}; + size_t idx = 0; /* * (possibly non-existent) environment variables can be used as values @@ -580,7 +610,7 @@ static int _expand_arg(pam_handle_t *pamh, char **value) D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr)); pam_syslog (pamh, LOG_ERR, "Variable buffer overflow: <%s> + <%s>", tmp, tmpptr); - return PAM_BUF_ERR; + goto buf_err; } continue; } @@ -605,7 +635,7 @@ static int _expand_arg(pam_handle_t *pamh, char **value) D(("Unterminated expandable variable: <%s>", orig-2)); pam_syslog(pamh, LOG_ERR, "Unterminated expandable variable: <%s>", orig-2); - return PAM_ABORT; + goto abort_err; } strncpy(tmpval, orig, sizeof(tmpval)); tmpval[sizeof(tmpval)-1] = '\0'; @@ -631,7 +661,7 @@ static int _expand_arg(pam_handle_t *pamh, char **value) default: D(("Impossible error, type == <%c>", type)); pam_syslog(pamh, LOG_CRIT, "Impossible error, type == <%c>", type); - return PAM_ABORT; + goto abort_err; } /* switch */ if (tmpptr) { @@ -644,7 +674,7 @@ static int _expand_arg(pam_handle_t *pamh, char **value) D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr)); pam_syslog (pamh, LOG_ERR, "Variable buffer overflow: <%s> + <%s>", tmp, tmpptr); - return PAM_BUF_ERR; + goto buf_err; } } } /* if ('{' != *orig++) */ @@ -656,7 +686,7 @@ static int _expand_arg(pam_handle_t *pamh, char **value) D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr)); pam_syslog(pamh, LOG_ERR, "Variable buffer overflow: <%s> + <%s>", tmp, tmpptr); - return PAM_BUF_ERR; + goto buf_err; } } } /* for (;*orig;) */ @@ -667,65 +697,118 @@ static int _expand_arg(pam_handle_t *pamh, char **value) D(("Couldn't malloc %d bytes for expanded var", idx + 1)); pam_syslog (pamh, LOG_CRIT, "Couldn't malloc %lu bytes for expanded var", (unsigned long)idx+1); - return PAM_BUF_ERR; + goto buf_err; } } strcpy(*value, tmp); - memset(tmp, '\0', sizeof(tmp)); + pam_overwrite_array(tmp); + pam_overwrite_array(tmpval); D(("Exit.")); return PAM_SUCCESS; +buf_err: + pam_overwrite_array(tmp); + pam_overwrite_array(tmpval); + return PAM_BUF_ERR; +abort_err: + pam_overwrite_array(tmp); + pam_overwrite_array(tmpval); + return PAM_ABORT; } -static const char * _pam_get_item_byname(pam_handle_t *pamh, const char *name) +static int +_check_var(pam_handle_t *pamh, VAR *var) { /* - * This function just allows me to use names as given in the config - * file and translate them into the appropriate PAM_ITEM macro + * Examine the variable and determine what action to take. + * Returns DEFINE_VAR, UNDEFINE_VAR depending on action to take + * or a PAM_* error code if passed back from other routines + * + * if no DEFAULT provided, the empty string is assumed + * if no OVERRIDE provided, the empty string is assumed + * if DEFAULT= and OVERRIDE evaluates to the empty string, + * this variable should be undefined + * if DEFAULT="" and OVERRIDE evaluates to the empty string, + * this variable should be defined with no value + * if OVERRIDE=value and value turns into the empty string, DEFAULT is used + * + * If DEFINE_VAR is to be returned, the correct value to define will + * be pointed to by var->value */ - int item; - const void *itemval; + int retval; D(("Called.")); - if (strcmp(name, "PAM_USER") == 0 || strcmp(name, "HOME") == 0 || strcmp(name, "SHELL") == 0) { - item = PAM_USER; - } else if (strcmp(name, "PAM_USER_PROMPT") == 0) { - item = PAM_USER_PROMPT; - } else if (strcmp(name, "PAM_TTY") == 0) { - item = PAM_TTY; - } else if (strcmp(name, "PAM_RUSER") == 0) { - item = PAM_RUSER; - } else if (strcmp(name, "PAM_RHOST") == 0) { - item = PAM_RHOST; - } else { - D(("Unknown PAM_ITEM: <%s>", name)); - pam_syslog (pamh, LOG_ERR, "Unknown PAM_ITEM: <%s>", name); - return NULL; - } - if (pam_get_item(pamh, item, &itemval) != PAM_SUCCESS) { - D(("pam_get_item failed")); - return NULL; /* let pam_get_item() log the error */ + /* + * First thing to do is to expand any arguments, but only + * if they are not the special quote values (cause expand_arg + * changes memory). + */ + + if (var->defval && ("e != var->defval) && + ((retval = _expand_arg(pamh, &(var->defval))) != PAM_SUCCESS)) { + return retval; + } + if (var->override && ("e != var->override) && + ((retval = _expand_arg(pamh, &(var->override))) != PAM_SUCCESS)) { + return retval; } - if (itemval && (strcmp(name, "HOME") == 0 || strcmp(name, "SHELL") == 0)) { - struct passwd *user_entry; - user_entry = pam_modutil_getpwnam (pamh, itemval); - if (!user_entry) { - pam_syslog(pamh, LOG_ERR, "No such user!?"); - return NULL; + /* Now its easy */ + + if (var->override && *(var->override)) { + /* if there is a non-empty string in var->override, we use it */ + D(("OVERRIDE variable <%s> being used: <%s>", var->name, var->override)); + var->value = var->override; + retval = DEFINE_VAR; + } else { + + var->value = var->defval; + if ("e == var->defval) { + /* + * This means that the empty string was given for defval value + * which indicates that a variable should be defined with no value + */ + D(("An empty variable: <%s>", var->name)); + retval = DEFINE_VAR; + } else if (var->defval) { + D(("DEFAULT variable <%s> being used: <%s>", var->name, var->defval)); + retval = DEFINE_VAR; + } else { + D(("UNDEFINE variable <%s>", var->name)); + retval = UNDEFINE_VAR; } - return (strcmp(name, "SHELL") == 0) ? - user_entry->pw_shell : - user_entry->pw_dir; } D(("Exit.")); - return itemval; + return retval; +} + +static void +_clean_var(VAR *var) +{ + if (var->name) { + pam_overwrite_string(var->name); + free(var->name); + } + if (var->defval && ("e != var->defval)) { + pam_overwrite_string(var->defval); + free(var->defval); + } + if (var->override && ("e != var->override)) { + pam_overwrite_string(var->override); + free(var->override); + } + var->name = NULL; + var->value = NULL; /* never has memory specific to it */ + var->defval = NULL; + var->override = NULL; + return; } -static int _define_var(pam_handle_t *pamh, int ctrl, VAR *var) +static int +_define_var(pam_handle_t *pamh, int ctrl, VAR *var) { /* We have a variable to define, this is a simple function */ @@ -747,7 +830,8 @@ static int _define_var(pam_handle_t *pamh, int ctrl, VAR *var) return retval; } -static int _undefine_var(pam_handle_t *pamh, int ctrl, VAR *var) +static int +_undefine_var(pam_handle_t *pamh, int ctrl, VAR *var) { /* We have a variable to undefine, this is a simple function */ @@ -758,25 +842,175 @@ static int _undefine_var(pam_handle_t *pamh, int ctrl, VAR *var) return pam_putenv(pamh, var->name); } -static void _clean_var(VAR *var) +static int +_parse_config_file(pam_handle_t *pamh, int ctrl, const char *file) { - if (var->name) { - free(var->name); - } - if (var->defval && ("e != var->defval)) { - free(var->defval); - } - if (var->override && ("e != var->override)) { - free(var->override); + int retval; + VAR Var, *var=&Var; + char **conf_list = NULL; + + var->name=NULL; var->defval=NULL; var->override=NULL; + + D(("Called.")); + +#ifdef USE_ECONF + /* If "file" is not NULL, only this file will be parsed. */ + retval = econf_read_file(pamh, file, " \t", PAM_ENV, ".conf", "security", &conf_list); +#else + /* Only one file will be parsed. So, file has to be set. */ + if (file == NULL) /* No filename has been set via argv. */ + file = DEFAULT_CONF_FILE; +#ifdef VENDOR_DEFAULT_CONF_FILE + /* + * Check whether file is available. + * If it does not exist, fall back to VENDOR_DEFAULT_CONF_FILE file. + */ + struct stat stat_buffer; + if (stat(file, &stat_buffer) != 0 && errno == ENOENT) { + file = VENDOR_DEFAULT_CONF_FILE; } - var->name = NULL; - var->value = NULL; /* never has memory specific to it */ - var->defval = NULL; - var->override = NULL; - return; +#endif + retval = read_file(pamh, file, &conf_list); +#endif + + if (retval != PAM_SUCCESS) + return retval; + + for (char **conf = conf_list; *conf != NULL; ++conf) { + if ((retval = _parse_line(pamh, *conf, var)) == GOOD_LINE) { + retval = _check_var(pamh, var); + + if (DEFINE_VAR == retval) { + retval = _define_var(pamh, ctrl, var); + + } else if (UNDEFINE_VAR == retval) { + retval = _undefine_var(pamh, ctrl, var); + } + } + if (PAM_SUCCESS != retval && ILLEGAL_VAR != retval + && BAD_LINE != retval && PAM_BAD_ITEM != retval) break; + + _clean_var(var); + + } /* for */ + + /* tidy up */ + free_string_array(conf_list); + _clean_var(var); /* We could have got here prematurely, + * this is safe though */ + D(("Exit.")); + return (retval != 0 ? PAM_ABORT : PAM_SUCCESS); } +static int +_parse_env_file(pam_handle_t *pamh, int ctrl, const char *file) +{ + int retval=PAM_SUCCESS, i, t; + char *key, *mark; + char **env_list = NULL; + +#ifdef USE_ECONF + retval = econf_read_file(pamh, file, "=", ENVIRONMENT, "", "", &env_list); +#else + /* Only one file will be parsed. So, file has to be set. */ + if (file == NULL) /* No filename has been set via argv. */ + file = DEFAULT_ETC_ENVFILE; +#ifdef VENDOR_DEFAULT_ETC_ENVFILE + /* + * Check whether file is available. + * If it does not exist, fall back to VENDOR_DEFAULT_ETC_ENVFILE; file. + */ + struct stat stat_buffer; + if (stat(file, &stat_buffer) != 0 && errno == ENOENT) { + file = VENDOR_DEFAULT_ETC_ENVFILE; + } +#endif + retval = read_file(pamh, file, &env_list); +#endif + + if (retval != PAM_SUCCESS) + return retval == PAM_IGNORE ? PAM_SUCCESS : retval; + + for (char **env = env_list; *env != NULL; ++env) { + key = *env; + + /* skip leading white space */ + key += strspn(key, " \n\t"); + + /* skip blanks lines and comments */ + if (key[0] == '#') + continue; + + /* skip over "export " if present so we can be compat with + bash type declarations */ + if (strncmp(key, "export ", (size_t) 7) == 0) + key += 7; + + /* now find the end of value */ + mark = key; + while(mark[0] != '\n' && mark[0] != '#' && mark[0] != '\0') + mark++; + if (mark[0] != '\0') + mark[0] = '\0'; + + /* + * sanity check, the key must be alphanumeric + */ + + if (key[0] == '=') { + pam_syslog(pamh, LOG_ERR, + "missing key name '%s' in %s', ignoring", + key, file); + continue; + } + for ( i = 0 ; key[i] != '=' && key[i] != '\0' ; i++ ) + if (!isalnum(key[i]) && key[i] != '_') { + pam_syslog(pamh, LOG_ERR, + "non-alphanumeric key '%s' in %s', ignoring", + key, file); + break; + } + /* non-alphanumeric key, ignore this line */ + if (key[i] != '=' && key[i] != '\0') + continue; + + /* now we try to be smart about quotes around the value, + but not too smart, we can't get all fancy with escaped + values like bash */ + if (key[i] == '=' && (key[++i] == '\"' || key[i] == '\'')) { + for ( t = i+1 ; key[t] != '\0' ; t++) + if (key[t] != '\"' && key[t] != '\'') + key[i++] = key[t]; + else if (key[t+1] != '\0') + key[i++] = key[t]; + key[i] = '\0'; + } + + /* if this is a request to delete a variable, check that it's + actually set first, so we don't get a vague error back from + pam_putenv() */ + for (i = 0; key[i] != '=' && key[i] != '\0'; i++); + + if (key[i] == '\0' && !pam_getenv(pamh,key)) + continue; + + /* set the env var, if it fails, we break out of the loop */ + retval = pam_putenv(pamh, key); + if (retval != PAM_SUCCESS) { + D(("error setting env \"%s\"", key)); + break; + } else if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_DEBUG, + "pam_putenv(\"%s\")", key); + } + } + + /* tidy up */ + free_string_array(env_list); + D(("Exit.")); + return retval; +} /* --- authentication management functions (only) --- */ diff --git a/modules/pam_env/pam_env.conf.5 b/modules/pam_env/pam_env.conf.5 index 40fd118b..90de5eaf 100644 --- a/modules/pam_env/pam_env.conf.5 +++ b/modules/pam_env/pam_env.conf.5 @@ -1,13 +1,13 @@ '\" t .\" Title: pam_env.conf .\" Author: [see the "AUTHOR" section] -.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/> -.\" Date: 09/03/2021 +.\" Generator: DocBook XSL Stylesheets v1.79.2 <http://docbook.sf.net/> +.\" Date: 05/07/2023 .\" Manual: Linux-PAM Manual -.\" Source: Linux-PAM Manual +.\" Source: Linux-PAM .\" Language: English .\" -.TH "PAM_ENV\&.CONF" "5" "09/03/2021" "Linux-PAM Manual" "Linux\-PAM Manual" +.TH "PAM_ENV\&.CONF" "5" "05/07/2023" "Linux\-PAM" "Linux\-PAM Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -36,7 +36,7 @@ The file specifies the environment variables to be set, unset or modified by \fBpam_env\fR(8)\&. When someone logs in, this file is read and the environment variables are set according\&. .PP -Each line starts with the variable name, there are then two possible options for each variable DEFAULT and OVERRIDE\&. DEFAULT allows an administrator to set the value of the variable to some default value, if none is supplied then the empty string is assumed\&. The OVERRIDE option tells pam_env that it should enter in its value (overriding the default value) if there is one to use\&. OVERRIDE is not used, "" is assumed and no override will be done\&. +Each line starts with the variable name, there are then two possible options for each variable DEFAULT and OVERRIDE\&. DEFAULT allows an administrator to set the value of the variable to some default value, if none is supplied then the empty string is assumed\&. The OVERRIDE option tells pam_env that it should enter in its value (overriding the default value) if there is one to use\&. When OVERRIDE is not used, "" is assumed and no override will be done\&. .PP \fIVARIABLE\fR [\fIDEFAULT=[value]\fR] [\fIOVERRIDE=[value]\fR] diff --git a/modules/pam_env/pam_env.conf.5.xml b/modules/pam_env/pam_env.conf.5.xml index fca046fe..81fc9613 100644 --- a/modules/pam_env/pam_env.conf.5.xml +++ b/modules/pam_env/pam_env.conf.5.xml @@ -1,13 +1,10 @@ -<?xml version="1.0" encoding='UTF-8'?> -<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" - "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"> - -<refentry id="pam_env.conf"> +<refentry xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="pam_env.conf"> <refmeta> <refentrytitle>pam_env.conf</refentrytitle> <manvolnum>5</manvolnum> - <refmiscinfo class="sectdesc">Linux-PAM Manual</refmiscinfo> + <refmiscinfo class="source">Linux-PAM</refmiscinfo> + <refmiscinfo class="manual">Linux-PAM Manual</refmiscinfo> </refmeta> <refnamediv> @@ -17,10 +14,18 @@ </refnamediv> - <refsect1 id='pam_env.conf-description'> + <refsect1 xml:id="pam_env.conf-description"> <title>DESCRIPTION</title> - <para> + <para condition="with_vendordir"> + The <filename>%vendordir%/security/pam_env.conf</filename> and + <filename>/etc/security/pam_env.conf</filename> files specify + the environment variables to be set, unset or modified by + <citerefentry><refentrytitle>pam_env</refentrytitle><manvolnum>8</manvolnum></citerefentry>. + When someone logs in, these files are read and the environment + variables are set according. + </para> + <para condition="without_vendordir"> The <filename>/etc/security/pam_env.conf</filename> file specifies the environment variables to be set, unset or modified by <citerefentry><refentrytitle>pam_env</refentrytitle><manvolnum>8</manvolnum></citerefentry>. @@ -33,7 +38,7 @@ administrator to set the value of the variable to some default value, if none is supplied then the empty string is assumed. The OVERRIDE option tells pam_env that it should enter in its value - (overriding the default value) if there is one to use. OVERRIDE is + (overriding the default value) if there is one to use. When OVERRIDE is not used, "" is assumed and no override will be done. </para> <para> @@ -61,7 +66,15 @@ at front) can be used to mark this line as a comment line. </para> - <para> + <para condition="with_vendordir"> + The <filename>%vendordir%/environment</filename> and <filename>/etc/environment</filename> files specify + the environment variables to be set. These files must consist of simple + <emphasis>NAME=VALUE</emphasis> pairs on separate lines. + The <citerefentry><refentrytitle>pam_env</refentrytitle><manvolnum>8</manvolnum></citerefentry> + module will read these files after the <filename>pam_env.conf</filename> + file. + </para> + <para condition="without_vendordir"> The <filename>/etc/environment</filename> file specifies the environment variables to be set. The file must consist of simple <emphasis>NAME=VALUE</emphasis> pairs on separate lines. @@ -71,7 +84,7 @@ </para> </refsect1> - <refsect1 id="pam_env.conf-examples"> + <refsect1 xml:id="pam_env.conf-examples"> <title>EXAMPLES</title> <para> These are some example lines which might be specified in @@ -117,7 +130,7 @@ </programlisting> </refsect1> - <refsect1 id="pam_env.conf-see_also"> + <refsect1 xml:id="pam_env.conf-see_also"> <title>SEE ALSO</title> <para> <citerefentry><refentrytitle>pam_env</refentrytitle><manvolnum>8</manvolnum></citerefentry>, @@ -127,7 +140,7 @@ </para> </refsect1> - <refsect1 id="pam_env.conf-author"> + <refsect1 xml:id="pam_env.conf-author"> <title>AUTHOR</title> <para> pam_env was written by Dave Kinchlea <kinch@kinch.ark.com>. diff --git a/modules/pam_env/tst-pam_env-retval.c b/modules/pam_env/tst-pam_env-retval.c new file mode 100644 index 00000000..23ad10b9 --- /dev/null +++ b/modules/pam_env/tst-pam_env-retval.c @@ -0,0 +1,287 @@ +/* + * Check pam_env return values. + * + * Copyright (c) 2020-2022 Dmitry V. Levin <ldv@altlinux.org> + * Copyright (c) 2022 Stefan Schubert <schubi@suse.de> + */ + +#include "test_assert.h" + +#include <errno.h> +#include <libgen.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <security/pam_appl.h> + +#define MODULE_NAME "pam_env" +#define TEST_NAME "tst-" MODULE_NAME "-retval" +#define TEST_NAME_DIR TEST_NAME ".dir" + +static const char service_file[] = TEST_NAME ".service"; +static const char missing_file[] = TEST_NAME ".missing"; +static const char my_conf[] = TEST_NAME ".conf"; +static const char my_env[] = TEST_NAME ".env"; +#ifdef VENDORDIR +static const char dir_usr_etc_security[] = TEST_NAME_DIR VENDOR_SCONFIGDIR; +static const char usr_env[] = TEST_NAME_DIR VENDORDIR "/environment"; +static const char usr_conf[] = TEST_NAME_DIR VENDOR_SCONFIGDIR "/pam_env.conf"; +#endif + +static struct pam_conv conv; + +#ifdef VENDORDIR +static void +mkdir_p(const char *pathname, mode_t mode) +{ + if (mkdir(pathname, mode) == 0 || errno == EEXIST) + return; + ASSERT_EQ(errno, ENOENT); + + char *buf; + ASSERT_NE(NULL, buf = strdup(pathname)); + mkdir_p(dirname(buf), mode); + free(buf); + + ASSERT_EQ(0, mkdir(pathname, mode)); +} + +static void +rmdir_p(const char *pathname) +{ + if (rmdir(pathname) != 0) + return; + + char *buf; + ASSERT_NE(NULL, buf = strdup(pathname)); + rmdir_p(dirname(buf)); + free(buf); +} +#endif + +static void +setup(void) +{ + FILE *fp; + + ASSERT_NE(NULL, fp = fopen(my_conf, "w")); + ASSERT_LT(0, fprintf(fp, + "EDITOR\tDEFAULT=vim\n" + "PAGER\tDEFAULT=more\n")); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_NE(NULL, fp = fopen(my_env, "w")); + ASSERT_LT(0, fprintf(fp, + "test_value=foo\n" + "test2_value=bar\n")); + ASSERT_EQ(0, fclose(fp)); + +#ifdef VENDORDIR + mkdir_p(dir_usr_etc_security, 0755); + + ASSERT_NE(NULL, fp = fopen(usr_env, "w")); + ASSERT_LT(0, fprintf(fp, + "usr_etc_test=foo\n" + "usr_etc_test2=bar\n")); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_NE(NULL, fp = fopen(usr_conf, "w")); + ASSERT_LT(0, fprintf(fp, + "PAGER DEFAULT=emacs\n" + "MANPAGER DEFAULT=less\n")); + ASSERT_EQ(0, fclose(fp)); +#endif +} + +static void +cleanup(void) +{ + ASSERT_EQ(0, unlink(my_conf)); + ASSERT_EQ(0, unlink(my_env)); +#ifdef VENDORDIR + ASSERT_EQ(0, unlink(usr_env)); + ASSERT_EQ(0, unlink(usr_conf)); + rmdir_p(dir_usr_etc_security); +#endif +} + +static void +check_array(const char **array1, char **array2) +{ + for (const char **a1 = array1; *a1 != NULL; ++a1) { + char **a2; + for (a2 = array2; *a2 != NULL; ++a2) { + if (strcmp(*a1, *a2) == 0) + break; + } + ASSERT_NE(NULL, *a2); + } +} + +static void +check_env(const char **list) +{ + pam_handle_t *pamh = NULL; + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + + char **env_list = pam_getenvlist(pamh); + ASSERT_NE(NULL, env_list); + + check_array(list, env_list); + + for (char **e = env_list; *e != NULL; ++e) + free(*e); + free(env_list); + + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); +} + +int +main(void) +{ + pam_handle_t *pamh = NULL; + FILE *fp; + char cwd[PATH_MAX]; + + ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd))); + + setup(); + + /* + * When conffile= specifies a missing file, all methods except + * pam_sm_acct_mgmt and pam_sm_chauthtok return PAM_IGNORE. + * The return code of the stack where every module returns PAM_IGNORE + * is PAM_PERM_DENIED. + */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/.libs/%s.so conffile=%s/%s\n" + "account required %s/.libs/%s.so conffile=%s/%s\n" + "password required %s/.libs/%s.so conffile=%s/%s\n" + "session required %s/.libs/%s.so conffile=%s/%s\n", + cwd, MODULE_NAME, cwd, missing_file, + cwd, MODULE_NAME, cwd, missing_file, + cwd, MODULE_NAME, cwd, missing_file, + cwd, MODULE_NAME, cwd, missing_file)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* + * When conffile= specifies a missing file, all methods except + * pam_sm_acct_mgmt and pam_sm_chauthtok return PAM_IGNORE. + * pam_permit is added after pam_env to convert PAM_IGNORE to PAM_SUCCESS. + */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/.libs/%s.so conffile=%s/%s\n" + "auth required %s/../pam_permit/.libs/pam_permit.so\n" + "account required %s/.libs/%s.so conffile=%s/%s\n" + "account required %s/../pam_permit/.libs/pam_permit.so\n" + "password required %s/.libs/%s.so conffile=%s/%s\n" + "password required %s/../pam_permit/.libs/pam_permit.so\n" + "session required %s/.libs/%s.so conffile=%s/%s\n" + "session required %s/../pam_permit/.libs/pam_permit.so\n", + cwd, MODULE_NAME, cwd, missing_file, cwd, + cwd, MODULE_NAME, cwd, missing_file, cwd, + cwd, MODULE_NAME, cwd, missing_file, cwd, + cwd, MODULE_NAME, cwd, missing_file, cwd)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* + * conffile= specifies an existing file, + * envfile= specifies an empty file. + */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "session required %s/.libs/%s.so" + " conffile=%s/%s envfile=%s\n", + cwd, MODULE_NAME, + cwd, my_conf, "/dev/null")); + ASSERT_EQ(0, fclose(fp)); + + const char *env1[] = { "EDITOR=vim", "PAGER=more", NULL }; + check_env(env1); + + /* + * conffile= specifies an empty file, + * envfile= specifies an existing file. + */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "session required %s/.libs/%s.so" + " conffile=%s envfile=%s/%s\n", + cwd, MODULE_NAME, + "/dev/null", cwd, my_env)); + ASSERT_EQ(0, fclose(fp)); + + const char *env2[] = { "test_value=foo", "test2_value=bar", NULL }; + check_env(env2); + +#if defined (USE_ECONF) && defined (VENDORDIR) + + /* envfile is a directory. So values will be read from {TEST_NAME_DIR}/usr/etc and {TEST_NAME_DIR}/etc */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "session required %s/.libs/%s.so" + " conffile=%s envfile=%s/%s/\n", + cwd, MODULE_NAME, + "/dev/null", + cwd, TEST_NAME_DIR)); + ASSERT_EQ(0, fclose(fp)); + + const char *env3[] = {"usr_etc_test=foo", "usr_etc_test2=bar", NULL}; + check_env(env3); + + /* conffile is a directory. So values will be read from {TEST_NAME_DIR}/usr/etc and {TEST_NAME_DIR}/etc */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "session required %s/.libs/%s.so" + " conffile=%s/%s/ envfile=%s\n", + cwd, MODULE_NAME, + cwd, TEST_NAME_DIR, + "/dev/null")); + ASSERT_EQ(0, fclose(fp)); + + const char *env4[] = {"PAGER=emacs", "MANPAGER=less", NULL}; + check_env(env4); + +#endif + + /* cleanup */ + cleanup(); + ASSERT_EQ(0, unlink(service_file)); + + return 0; +} |