summaryrefslogtreecommitdiff
path: root/modules/pam_env
diff options
context:
space:
mode:
Diffstat (limited to 'modules/pam_env')
-rw-r--r--modules/pam_env/Makefile.am13
-rw-r--r--modules/pam_env/Makefile.in73
-rw-r--r--modules/pam_env/README28
-rw-r--r--modules/pam_env/README.xml34
-rw-r--r--modules/pam_env/environment.52
-rw-r--r--modules/pam_env/pam_env.820
-rw-r--r--modules/pam_env/pam_env.8.xml107
-rw-r--r--modules/pam_env/pam_env.c766
-rw-r--r--modules/pam_env/pam_env.conf.510
-rw-r--r--modules/pam_env/pam_env.conf.5.xml39
-rw-r--r--modules/pam_env/tst-pam_env-retval.c287
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 &lt;kinch@kinch.ark.com&gt;.
</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 = &quote; /* 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 && (&quote != var->defval) &&
- ((retval = _expand_arg(pamh, &(var->defval))) != PAM_SUCCESS)) {
- return retval;
- }
- if (var->override && (&quote != 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 (&quote == 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 && (&quote != var->defval) &&
+ ((retval = _expand_arg(pamh, &(var->defval))) != PAM_SUCCESS)) {
+ return retval;
+ }
+ if (var->override && (&quote != 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 (&quote == 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 && (&quote != var->defval)) {
+ pam_overwrite_string(var->defval);
+ free(var->defval);
+ }
+ if (var->override && (&quote != 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 && (&quote != var->defval)) {
- free(var->defval);
- }
- if (var->override && (&quote != 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 &lt;kinch@kinch.ark.com&gt;.
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;
+}