- Merge pam_unix back into pam, seperate package not needed anymore - Update pam-git.diff to current upstream - pam_env: Use vendor specific pam_env.conf and environment as fallback - pam_shells: Use the vendor directory obsoletes pam_env_econf.patch - Refresh docbook5.patch OBS-URL: https://build.opensuse.org/request/show/1043306 OBS-URL: https://build.opensuse.org/package/show/Linux-PAM/pam?expand=0&rev=268
6966 lines
228 KiB
Diff
6966 lines
228 KiB
Diff
diff --git a/README b/README
|
||
index 21af8c4c..aa99927e 100644
|
||
--- a/README
|
||
+++ b/README
|
||
@@ -6,7 +6,7 @@ NOTES:
|
||
|
||
How to use it is as follows:
|
||
|
||
-Please look at the ci/install_dependencies.sh for the necessary
|
||
+Please look at the ci/install-dependencies.sh for the necessary
|
||
prerequisite packages to be able to build the Linux-PAM. The script
|
||
is targeted at Debian based Linux distributions so the package
|
||
names and availability might differ on other distributions.
|
||
diff --git a/configure.ac b/configure.ac
|
||
index c06bc7dd..538195e5 100644
|
||
--- a/configure.ac
|
||
+++ b/configure.ac
|
||
@@ -243,6 +243,29 @@ if test x"$enable_debug" = x"yes" ; then
|
||
[lots of stuff gets written to /var/run/pam-debug.log])
|
||
fi
|
||
|
||
+AC_ARG_ENABLE(html_stylesheet,
|
||
+ AS_HELP_STRING([--enable-html-stylesheet=FILE],[html stylesheet path @<:@default=http://docbook.sourceforge.net/release/xsl/current/html/chunk.xsl@:>@]),
|
||
+ HTML_STYLESHEET=$enableval, HTML_STYLESHEET=http://docbook.sourceforge.net/release/xsl/current/html/chunk.xsl)
|
||
+AC_SUBST(HTML_STYLESHEET)
|
||
+
|
||
+AC_ARG_ENABLE(txt_stylesheet,
|
||
+ AS_HELP_STRING([--enable-txt-stylesheet=FILE],[text stylesheet path @<:@default=http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl@:>@]),
|
||
+ TXT_STYLESHEET=$enableval, TXT_STYLESHEET=http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl)
|
||
+AC_SUBST(TXT_STYLESHEET)
|
||
+# It has to be TXT_STYLESHEET otherwise a html tree will be generated while generating all README files.
|
||
+sed "s+HTML_STYLESHEET+$TXT_STYLESHEET+g" <doc/custom-html.xsl.in >doc/custom-html.xsl
|
||
+
|
||
+AC_ARG_ENABLE(pdf_stylesheet,
|
||
+ AS_HELP_STRING([--enable-pdf-stylesheet=FILE],[pdf stylesheet path @<:@default=http://docbook.sourceforge.net/release/xsl/current/fo/docbook.xsl@:>@]),
|
||
+ PDF_STYLESHEET=$enableval, PDF_STYLESHEET=http://docbook.sourceforge.net/release/xsl/current/fo/docbook.xsl)
|
||
+AC_SUBST(PDF_STYLESHEET)
|
||
+
|
||
+AC_ARG_ENABLE(man_stylesheet,
|
||
+ AS_HELP_STRING([--enable-man-stylesheet=FILE],[man stylesheet path @<:@default=http://docbook.sourceforge.net/release/xsl/current/manpages/profile-docbook.xsl@:>@]),
|
||
+ MAN_STYLESHEET=$enableval, MAN_STYLESHEET=http://docbook.sourceforge.net/release/xsl/current/manpages/profile-docbook.xsl)
|
||
+AC_SUBST(MAN_STYLESHEET)
|
||
+sed "s+MAN_STYLESHEET+$MAN_STYLESHEET+g" <doc/custom-man.xsl.in >doc/custom-man.xsl
|
||
+
|
||
AC_ARG_ENABLE(securedir,
|
||
AS_HELP_STRING([--enable-securedir=DIR],[path to location of PAMs @<:@default=$libdir/security@:>@]),
|
||
SECUREDIR=$enableval, SECUREDIR=$libdir/security)
|
||
@@ -259,6 +282,8 @@ AC_MSG_RESULT([Defining \$ISA to "$ISA"])
|
||
AC_ARG_ENABLE(sconfigdir,
|
||
AS_HELP_STRING([--enable-sconfigdir=DIR],[path to module conf files @<:@default=$sysconfdir/security@:>@]),
|
||
SCONFIGDIR=$enableval, SCONFIGDIR=$sysconfdir/security)
|
||
+AC_DEFINE_UNQUOTED([SCONFIGDIR], ["$SCONFIGDIR"],
|
||
+ [Directory for PAM modules system configuration files])
|
||
AC_SUBST(SCONFIGDIR)
|
||
|
||
AC_ARG_ENABLE(pamlocking,
|
||
@@ -295,6 +320,7 @@ if test x$with_mailspool != x ; then
|
||
else
|
||
AC_RUN_IFELSE([AC_LANG_SOURCE([[
|
||
#include <paths.h>
|
||
+#include <stdlib.h>
|
||
int main() {
|
||
#ifdef _PATH_MAILDIR
|
||
exit(0);
|
||
@@ -490,26 +516,31 @@ if test -n "$LIBSELINUX" ; then
|
||
LIBS=$BACKUP_LIBS
|
||
fi
|
||
|
||
+ECONF_CFLAGS=
|
||
+ECONF_LIBS=
|
||
AC_ARG_ENABLE([econf],
|
||
AS_HELP_STRING([--disable-econf], [do not use libeconf]),
|
||
- [WITH_ECONF=$enableval], WITH_ECONF=yes)
|
||
-if test "$WITH_ECONF" = "yes" ; then
|
||
- PKG_CHECK_MODULES([ECONF], [libeconf], [],
|
||
- [AC_CHECK_LIB([econf],[econf_readDirs],[ECONF_LIBS="-leconf"],[ECONF_LIBS=""])])
|
||
- if test -n "$ECONF_LIBS" ; then
|
||
- ECONF_CFLAGS="-DUSE_ECONF=1 $ECONF_CFLAGS"
|
||
- fi
|
||
+ [WITH_ECONF=$enableval], [WITH_ECONF=yes])
|
||
+if test "$WITH_ECONF" = "yes"; then
|
||
+ PKG_CHECK_MODULES([ECONF], [libeconf >= 0.5.0], [ECONF_CFLAGS="-DUSE_ECONF=1 $ECONF_CFLAGS"], [:])
|
||
fi
|
||
AC_SUBST([ECONF_CFLAGS])
|
||
AC_SUBST([ECONF_LIBS])
|
||
+
|
||
AC_ARG_ENABLE([vendordir],
|
||
AS_HELP_STRING([--enable-vendordir=DIR], [Directory for distribution provided configuration files]),,[])
|
||
if test -n "$enable_vendordir"; then
|
||
AC_DEFINE_UNQUOTED([VENDORDIR], ["$enable_vendordir"],
|
||
[Directory for distribution provided configuration files])
|
||
- STRINGPARAM_VENDORDIR="--stringparam vendordir '$enable_vendordir'"
|
||
+ AC_DEFINE_UNQUOTED([VENDOR_SCONFIGDIR], ["$enable_vendordir/security"],
|
||
+ [Directory for PAM modules distribution provided configuration files])
|
||
+ if test "$WITH_ECONF" = "yes" ; then
|
||
+ STRINGPARAM_VENDORDIR="--stringparam vendordir '$enable_vendordir' --stringparam profile.condition 'with_vendordir;with_vendordir_and_with_econf'"
|
||
+ else
|
||
+ STRINGPARAM_VENDORDIR="--stringparam vendordir '$enable_vendordir' --stringparam profile.condition 'with_vendordir;with_vendordir_and_without_econf"
|
||
+ fi
|
||
else
|
||
- STRINGPARAM_VENDORDIR="--stringparam vendordir '<vendordir>'"
|
||
+ STRINGPARAM_VENDORDIR="--stringparam profile.condition 'without_vendordir'"
|
||
fi
|
||
AC_SUBST([STRINGPARAM_VENDORDIR])
|
||
|
||
@@ -628,11 +659,6 @@ test -n "$opt_uidmin" ||
|
||
opt_uidmin=1000
|
||
AC_DEFINE_UNQUOTED(PAM_USERTYPE_UIDMIN, $opt_uidmin, [Minimum regular user uid.])
|
||
|
||
-AC_ARG_WITH([sysuidmin], AS_HELP_STRING([--with-sysuidmin=<number>],[default value for system user min uid (101)]), opt_sysuidmin=$withval)
|
||
-test -n "$opt_sysuidmin" ||
|
||
- opt_sysuidmin=101
|
||
-AC_DEFINE_UNQUOTED(PAM_USERTYPE_SYSUIDMIN, $opt_sysuidmin, [Minimum system user uid.])
|
||
-
|
||
AC_ARG_WITH([kernel-overflow-uid], AS_HELP_STRING([--with-kernel-overflow-uid=<number>],[kernel overflow uid, default (uint16_t)-2=65534]), opt_kerneloverflowuid=$withval)
|
||
test -n "$opt_kerneloverflowuid" ||
|
||
opt_kerneloverflowuid=65534
|
||
diff --git a/doc/adg/Makefile.am b/doc/adg/Makefile.am
|
||
index 77bd7a99..b795b1a4 100644
|
||
--- a/doc/adg/Makefile.am
|
||
+++ b/doc/adg/Makefile.am
|
||
@@ -21,7 +21,7 @@ if ENABLE_GENERATE_PDF
|
||
--stringparam section.autolabel 1 \
|
||
--stringparam section.label.includes.component.label 1 \
|
||
--stringparam toc.max.depth 3 --xinclude --nonet \
|
||
- http://docbook.sourceforge.net/release/xsl/current/fo/docbook.xsl $< > Linux-PAM_ADG.fo
|
||
+ $(PDF_STYLESHEET) $< > Linux-PAM_ADG.fo
|
||
$(FO2PDF) Linux-PAM_ADG.fo $@
|
||
else
|
||
echo "No fo2pdf processor installed, skip PDF generation"
|
||
@@ -33,7 +33,7 @@ Linux-PAM_ADG.txt: $(XMLS) $(DEP_XMLS)
|
||
--stringparam section.autolabel 1 \
|
||
--stringparam section.label.includes.component.label 1 \
|
||
--stringparam toc.max.depth 3 --xinclude --nonet \
|
||
- http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl $< | $(BROWSER) > $@
|
||
+ $(TXT_STYLESHEET) $< | $(BROWSER) > $@
|
||
|
||
html/Linux-PAM_ADG.html: $(XMLS) $(DEP_XMLS)
|
||
@test -d html || mkdir -p html
|
||
@@ -46,7 +46,7 @@ html/Linux-PAM_ADG.html: $(XMLS) $(DEP_XMLS)
|
||
--stringparam section.label.includes.component.label 1 \
|
||
--stringparam toc.max.depth 3 --xinclude --nonet \
|
||
--stringparam chunker.output.encoding UTF-8 \
|
||
- http://docbook.sourceforge.net/release/xsl/current/html/chunk.xsl $<
|
||
+ $(HTML_STYLESHEET) $<
|
||
|
||
distclean-local:
|
||
-rm -rf html Linux-PAM_ADG.txt Linux-PAM_ADG.pdf
|
||
diff --git a/doc/custom-html.xsl b/doc/custom-html.xsl.in
|
||
similarity index 87%
|
||
rename from doc/custom-html.xsl
|
||
rename to doc/custom-html.xsl.in
|
||
index fdd5df7d..b2eaf150 100644
|
||
--- a/doc/custom-html.xsl
|
||
+++ b/doc/custom-html.xsl.in
|
||
@@ -3,7 +3,7 @@
|
||
xmlns:ss="http://docbook.sf.net/xmlns/string.subst/1.0"
|
||
xmlns:exsl="http://exslt.org/common" version="1.0">
|
||
|
||
- <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl"/>
|
||
+ <xsl:import href="HTML_STYLESHEET"/>
|
||
<xsl:param name="vendordir"/>
|
||
|
||
<xsl:template match="filename">
|
||
diff --git a/doc/custom-man.xsl b/doc/custom-man.xsl.in
|
||
similarity index 77%
|
||
rename from doc/custom-man.xsl
|
||
rename to doc/custom-man.xsl.in
|
||
index a3408e6c..258627bf 100644
|
||
--- a/doc/custom-man.xsl
|
||
+++ b/doc/custom-man.xsl.in
|
||
@@ -1,6 +1,6 @@
|
||
<?xml version='1.0'?>
|
||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ss="http://docbook.sf.net/xmlns/string.subst/1.0" version="1.0">
|
||
- <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/manpages/profile-docbook.xsl"/>
|
||
+ <xsl:import href="MAN_STYLESHEET"/>
|
||
<xsl:param name="vendordir"/>
|
||
|
||
<xsl:param name="man.string.subst.map.local.pre">
|
||
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
|
||
index 78c891df..aec365cf 100644
|
||
--- a/doc/man/Makefile.am
|
||
+++ b/doc/man/Makefile.am
|
||
@@ -25,25 +25,25 @@ man_MANS = pam.3 PAM.8 pam.8 pam.conf.5 pam.d.5 \
|
||
pam_verror.3 pam_vinfo.3 pam_vprompt.3 pam_vsyslog.3 \
|
||
misc_conv.3 pam_misc_paste_env.3 pam_misc_drop_env.3 \
|
||
pam_misc_setenv.3
|
||
-XMLS = pam.3.xml pam.8.xml \
|
||
+XMLS = pam.3.xml pam.8.xml pam.conf.5.xml \
|
||
pam_acct_mgmt.3.xml pam_authenticate.3.xml \
|
||
pam_chauthtok.3.xml pam_close_session.3.xml pam_conv.3.xml \
|
||
- pam_end.3.xml pam_error.3.xml \
|
||
- pam_fail_delay.3.xml pam_xauth_data.3 \
|
||
+ pam_end.3.xml pam_error.3.xml pam_fail_delay.3.xml \
|
||
pam_get_authtok.3.xml pam_get_data.3.xml pam_get_item.3.xml \
|
||
pam_get_user.3.xml pam_getenv.3.xml pam_getenvlist.3.xml \
|
||
- pam_info.3.xml \
|
||
- pam_open_session.3.xml \
|
||
+ pam_info.3.xml pam_misc_drop_env.3.xml pam_misc_paste_env.3.xml \
|
||
+ pam_misc_setenv.3.xml pam_open_session.3.xml \
|
||
pam_prompt.3.xml pam_putenv.3.xml \
|
||
- pam_set_data.3.xml pam_set_item.3.xml pam_syslog.3.xml \
|
||
- pam_setcred.3.xml pam_sm_acct_mgmt.3.xml pam_sm_authenticate.3.xml \
|
||
- pam_sm_close_session.3.xml pam_sm_open_session.3.xml \
|
||
- pam_sm_setcred.3.xml pam_start.3.xml pam_strerror.3.xml \
|
||
- pam_sm_chauthtok.3.xml \
|
||
+ pam_set_data.3.xml pam_set_item.3.xml pam_setcred.3.xml \
|
||
+ pam_sm_acct_mgmt.3.xml pam_sm_authenticate.3.xml \
|
||
+ pam_sm_chauthtok.3.xml pam_sm_close_session.3.xml \
|
||
+ pam_sm_open_session.3.xml pam_sm_setcred.3.xml \
|
||
+ pam_start.3.xml pam_strerror.3.xml \
|
||
+ pam_syslog.3.xml pam_xauth_data.3.xml \
|
||
pam_item_types_std.inc.xml pam_item_types_ext.inc.xml \
|
||
pam.conf-desc.xml pam.conf-dir.xml pam.conf-syntax.xml \
|
||
- misc_conv.3.xml pam_misc_paste_env.3.xml pam_misc_drop_env.3.xml \
|
||
- pam_misc_setenv.3.xml
|
||
+ misc_conv.3.xml
|
||
+
|
||
|
||
if ENABLE_REGENERATE_MAN
|
||
PAM.8: pam.8
|
||
diff --git a/doc/man/pam.8.xml b/doc/man/pam.8.xml
|
||
index 464af0e5..8eef665a 100644
|
||
--- a/doc/man/pam.8.xml
|
||
+++ b/doc/man/pam.8.xml
|
||
@@ -158,15 +158,14 @@ closing hook for modules to affect the services available to a user.</para>
|
||
</para>
|
||
</listitem>
|
||
</varlistentry>
|
||
- <varlistentry>
|
||
+ <varlistentry condition="with_vendordir">
|
||
<term><filename>%vendordir%/pam.d</filename></term>
|
||
<listitem>
|
||
<para>
|
||
the <emphasis remap='B'>Linux-PAM</emphasis> vendor configuration
|
||
directory. Files in <filename>/etc/pam.d</filename> and
|
||
<filename>/usr/lib/pam.d</filename> override files with the same
|
||
- name in this directory. Only available if Linux-PAM was compiled
|
||
- with vendordir enabled.
|
||
+ name in this directory.
|
||
</para>
|
||
</listitem>
|
||
</varlistentry>
|
||
diff --git a/doc/mwg/Makefile.am b/doc/mwg/Makefile.am
|
||
index 2bbb2d0b..688e6cb3 100644
|
||
--- a/doc/mwg/Makefile.am
|
||
+++ b/doc/mwg/Makefile.am
|
||
@@ -21,7 +21,7 @@ if ENABLE_GENERATE_PDF
|
||
--stringparam section.autolabel 1 \
|
||
--stringparam section.label.includes.component.label 1 \
|
||
--stringparam toc.max.depth 3 --xinclude --nonet \
|
||
- http://docbook.sourceforge.net/release/xsl/current/fo/docbook.xsl $< > Linux-PAM_MWG.fo
|
||
+ $(PDF_STYLESHEET) $< > Linux-PAM_MWG.fo
|
||
$(FO2PDF) Linux-PAM_MWG.fo $@
|
||
else
|
||
echo "No fo2pdf processor installed, skip PDF generation"
|
||
@@ -33,7 +33,7 @@ Linux-PAM_MWG.txt: $(XMLS) $(DEP_XMLS)
|
||
--stringparam section.autolabel 1 \
|
||
--stringparam section.label.includes.component.label 1 \
|
||
--stringparam toc.max.depth 3 --xinclude --nonet \
|
||
- http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl $< | $(BROWSER) > $@
|
||
+ $(TXT_STYLESHEET) $< | $(BROWSER) > $@
|
||
|
||
html/Linux-PAM_MWG.html: $(XMLS) $(DEP_XMLS)
|
||
@test -d html || mkdir -p html
|
||
@@ -46,7 +46,7 @@ html/Linux-PAM_MWG.html: $(XMLS) $(DEP_XMLS)
|
||
--stringparam section.label.includes.component.label 1 \
|
||
--stringparam toc.max.depth 3 --xinclude --nonet \
|
||
--stringparam chunker.output.encoding UTF-8 \
|
||
- http://docbook.sourceforge.net/release/xsl/current/html/chunk.xsl $<
|
||
+ $(HTML_STYLESHEET) $<
|
||
|
||
distclean-local:
|
||
-rm -rf html Linux-PAM_MWG.txt Linux-PAM_MWG.pdf
|
||
diff --git a/doc/sag/Linux-PAM_SAG.xml b/doc/sag/Linux-PAM_SAG.xml
|
||
index 0f33e0f6..2adaef7d 100644
|
||
--- a/doc/sag/Linux-PAM_SAG.xml
|
||
+++ b/doc/sag/Linux-PAM_SAG.xml
|
||
@@ -408,6 +408,8 @@ session required pam_warn.so
|
||
href="pam_exec.xml"/>
|
||
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
|
||
href="pam_faildelay.xml"/>
|
||
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
|
||
+ href="pam_faillock.xml"/>
|
||
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
|
||
href="pam_filter.xml"/>
|
||
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
|
||
diff --git a/doc/sag/Makefile.am b/doc/sag/Makefile.am
|
||
index 31816aa0..84fd383f 100644
|
||
--- a/doc/sag/Makefile.am
|
||
+++ b/doc/sag/Makefile.am
|
||
@@ -22,7 +22,7 @@ if ENABLE_GENERATE_PDF
|
||
--stringparam section.autolabel 1 \
|
||
--stringparam section.label.includes.component.label 1 \
|
||
--stringparam toc.max.depth 2 --xinclude --nonet \
|
||
- http://docbook.sourceforge.net/release/xsl/current/fo/docbook.xsl $< > Linux-PAM_SAG.fo
|
||
+ $(PDF_STYLESHEET) $< > Linux-PAM_SAG.fo
|
||
$(FO2PDF) Linux-PAM_SAG.fo $@
|
||
else
|
||
echo "No fo2pdf processor installed, skip PDF generation"
|
||
@@ -34,7 +34,7 @@ Linux-PAM_SAG.txt: $(XMLS) $(DEP_XMLS)
|
||
--stringparam section.autolabel 1 \
|
||
--stringparam section.label.includes.component.label 1 \
|
||
--stringparam toc.max.depth 2 --xinclude --nonet \
|
||
- http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl $< | $(BROWSER) > $@
|
||
+ $(TXT_STYLESHEET) $< | $(BROWSER) > $@
|
||
|
||
html/Linux-PAM_SAG.html: $(XMLS) $(DEP_XMLS)
|
||
@test -d html || mkdir -p html
|
||
@@ -47,7 +47,7 @@ html/Linux-PAM_SAG.html: $(XMLS) $(DEP_XMLS)
|
||
--stringparam section.label.includes.component.label 1 \
|
||
--stringparam toc.max.depth 2 --xinclude --nonet \
|
||
--stringparam chunker.output.encoding UTF-8 \
|
||
- http://docbook.sourceforge.net/release/xsl/current/html/chunk.xsl $<
|
||
+ $(HTML_STYLESHEET) $<
|
||
|
||
distclean-local:
|
||
-rm -rf html Linux-PAM_SAG.txt Linux-PAM_SAG.pdf
|
||
diff --git a/examples/Makefile.am b/examples/Makefile.am
|
||
index 722ec686..c4c3c261 100644
|
||
--- a/examples/Makefile.am
|
||
+++ b/examples/Makefile.am
|
||
@@ -11,4 +11,4 @@ AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
|
||
LDADD = $(top_builddir)/libpam/libpam.la \
|
||
$(top_builddir)/libpam_misc/libpam_misc.la
|
||
|
||
-noinst_PROGRAMS = xsh vpass blank check_user
|
||
+noinst_PROGRAMS = xsh vpass blank check_user tty_conv
|
||
diff --git a/examples/tty_conv.c b/examples/tty_conv.c
|
||
new file mode 100644
|
||
index 00000000..23f0684c
|
||
--- /dev/null
|
||
+++ b/examples/tty_conv.c
|
||
@@ -0,0 +1,177 @@
|
||
+/* PlanC (hubenchang0515@outlook.com) -- an example application
|
||
+ * that implements a custom conversation */
|
||
+
|
||
+#include <stdio.h>
|
||
+#include <stdlib.h>
|
||
+#include <string.h>
|
||
+#include <errno.h>
|
||
+#include <unistd.h>
|
||
+#include <termio.h>
|
||
+#include <security/pam_appl.h>
|
||
+
|
||
+/***************************************
|
||
+ * @brief echo off/on
|
||
+ * @param[in] fd file descriptor
|
||
+ * @param[in] off 1 - echo off,0 - echo on
|
||
+ ***************************************/
|
||
+static void echoOff(int fd, int off)
|
||
+{
|
||
+ struct termio tty;
|
||
+ if (ioctl(fd, TCGETA, &tty) < 0)
|
||
+ {
|
||
+ fprintf(stderr, "TCGETA failed: %s\n", strerror(errno));
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ if (off)
|
||
+ {
|
||
+ tty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
|
||
+ if (ioctl(fd, TCSETAF, &tty) < 0)
|
||
+ {
|
||
+ fprintf(stderr, "TCSETAF failed: %s\n", strerror(errno));
|
||
+ }
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ tty.c_lflag |= (ECHO | ECHOE | ECHOK | ECHONL);
|
||
+ if (ioctl(fd, TCSETAW, &tty) < 0)
|
||
+ {
|
||
+ fprintf(stderr, "TCSETAW failed: %s\n", strerror(errno));
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+/***************************************
|
||
+ * @brief echo off stdin
|
||
+ ***************************************/
|
||
+static void echoOffStdin(void)
|
||
+{
|
||
+ echoOff(fileno(stdin), 1);
|
||
+}
|
||
+
|
||
+/***************************************
|
||
+ * @brief echo on stdin
|
||
+ ***************************************/
|
||
+static void echoOnStdin(void)
|
||
+{
|
||
+ echoOff(fileno(stdin), 0);
|
||
+}
|
||
+
|
||
+/***************************************
|
||
+ * @brief read a line input
|
||
+ * @return the input string
|
||
+ ***************************************/
|
||
+static char *readline(void)
|
||
+{
|
||
+ char input[PAM_MAX_RESP_SIZE];
|
||
+ int i;
|
||
+
|
||
+ flockfile(stdin);
|
||
+ for (i = 0; i < PAM_MAX_RESP_SIZE; i++)
|
||
+ {
|
||
+ int ch = getchar_unlocked();
|
||
+ if (ch == '\n' || ch == '\r' ||ch == EOF)
|
||
+ break;
|
||
+ input[i] = ch;
|
||
+ }
|
||
+ funlockfile(stdin);
|
||
+ input[i] = '\0';
|
||
+
|
||
+ return (strdup(input));
|
||
+}
|
||
+
|
||
+/**************************************************
|
||
+ * @brief callback of PAM conversation
|
||
+ * @param[in] num_msg the count of message
|
||
+ * @param[in] msg PAM message
|
||
+ * @param[out] resp our response
|
||
+ * @param[in] appdata_ptr custom data passed by struct pam_conv.appdata_ptr
|
||
+ * @return state
|
||
+ **************************************************/
|
||
+static int conversation(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
|
||
+{
|
||
+ (void)(appdata_ptr);
|
||
+ int i;
|
||
+
|
||
+ /* check the count of message */
|
||
+ if (num_msg <= 0 || num_msg >= PAM_MAX_MSG_SIZE)
|
||
+ {
|
||
+ fprintf(stderr, "invalid num_msg(%d)\n", num_msg);
|
||
+ return PAM_CONV_ERR;
|
||
+ }
|
||
+
|
||
+ /* alloc memory for response */
|
||
+ if ((resp[0] = malloc(num_msg * sizeof(struct pam_response))) == NULL)
|
||
+ {
|
||
+ fprintf(stderr, "bad alloc\n");
|
||
+ return PAM_BUF_ERR;
|
||
+ }
|
||
+
|
||
+ /* response for message */
|
||
+ for (i = 0; i < num_msg; i++)
|
||
+ {
|
||
+ const struct pam_message *m = *msg + i;
|
||
+ struct pam_response *r = *resp + i;
|
||
+ r->resp_retcode = 0; /* currently un-used, zero expected */
|
||
+ switch (m->msg_style)
|
||
+ {
|
||
+ case PAM_PROMPT_ECHO_OFF: /* get the input with echo off, like the password */
|
||
+ printf("%s", m->msg);
|
||
+ echoOffStdin();
|
||
+ r->resp = readline();
|
||
+ echoOnStdin();
|
||
+ printf("\n");
|
||
+ break;
|
||
+
|
||
+ case PAM_PROMPT_ECHO_ON: /* get the input with echo on, like the username */
|
||
+ printf("%s", m->msg);
|
||
+ r->resp = readline();
|
||
+ break;
|
||
+
|
||
+ case PAM_TEXT_INFO: /* normal info */
|
||
+ printf("%s\n", m->msg);
|
||
+ break;
|
||
+
|
||
+ case PAM_ERROR_MSG: /* error info */
|
||
+ fprintf(stderr, "%s\n", m->msg);
|
||
+ break;
|
||
+
|
||
+ default:
|
||
+ fprintf(stderr, "unexpected msg_style: %d\n", m->msg_style);
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ return PAM_SUCCESS;
|
||
+}
|
||
+
|
||
+int main(void)
|
||
+{
|
||
+ struct pam_conv pam_conv = {conversation, NULL};
|
||
+ pam_handle_t *pamh;
|
||
+
|
||
+ /* echo on while exist, like Ctrl+C on input password */
|
||
+ atexit(echoOnStdin);
|
||
+
|
||
+ if (PAM_SUCCESS != pam_start("login", NULL, &pam_conv, &pamh))
|
||
+ {
|
||
+ fprintf(stderr, "pam_start failed\n");
|
||
+ return EXIT_FAILURE;
|
||
+ }
|
||
+
|
||
+ if (PAM_SUCCESS != pam_authenticate(pamh, 0))
|
||
+ {
|
||
+ fprintf(stderr, "pam_authenticate failed\n");
|
||
+ pam_end(pamh, 0);
|
||
+ return EXIT_FAILURE;
|
||
+ }
|
||
+
|
||
+ if (PAM_SUCCESS != pam_acct_mgmt(pamh, 0))
|
||
+ {
|
||
+ fprintf(stderr, "pam_acct_mgmt failed\n");
|
||
+ pam_end(pamh, 0);
|
||
+ return EXIT_FAILURE;
|
||
+ }
|
||
+
|
||
+ pam_end(pamh, 0);
|
||
+ return EXIT_SUCCESS;
|
||
+}
|
||
diff --git a/examples/xsh.c b/examples/xsh.c
|
||
index ef4dca0c..5b34fc17 100644
|
||
--- a/examples/xsh.c
|
||
+++ b/examples/xsh.c
|
||
@@ -80,7 +80,7 @@ int main(int argc, char **argv)
|
||
tty = ttyname(fileno(stdin));
|
||
if (tty) {
|
||
retcode = pam_set_item(pamh, PAM_TTY, tty);
|
||
- bail_out(pamh,1,retcode,"pam_set_item(PAM_RHOST)");
|
||
+ bail_out(pamh,1,retcode,"pam_set_item(PAM_TTY)");
|
||
}
|
||
}
|
||
|
||
diff --git a/libpam/Makefile.am b/libpam/Makefile.am
|
||
index 55222afc..389d5d02 100644
|
||
--- a/libpam/Makefile.am
|
||
+++ b/libpam/Makefile.am
|
||
@@ -21,7 +21,7 @@ noinst_HEADERS = pam_prelude.h pam_private.h pam_tokens.h \
|
||
include/pam_inline.h include/test_assert.h
|
||
|
||
libpam_la_LDFLAGS = -no-undefined -version-info 85:1:85
|
||
-libpam_la_LIBADD = @LIBAUDIT@ $(LIBPRELUDE_LIBS) $(ECONF_LIBS) @LIBDL@
|
||
+libpam_la_LIBADD = @LIBAUDIT@ $(LIBPRELUDE_LIBS) $(ECONF_LIBS) @LIBDL@ @LTLIBINTL@
|
||
|
||
if HAVE_VERSIONING
|
||
libpam_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libpam.map
|
||
diff --git a/libpam/include/security/pam_modutil.h b/libpam/include/security/pam_modutil.h
|
||
index 33f87b90..c2578323 100644
|
||
--- a/libpam/include/security/pam_modutil.h
|
||
+++ b/libpam/include/security/pam_modutil.h
|
||
@@ -147,7 +147,16 @@ pam_modutil_sanitize_helper_fds(pam_handle_t *pamh,
|
||
enum pam_modutil_redirect_fd redirect_stdout,
|
||
enum pam_modutil_redirect_fd redirect_stderr);
|
||
|
||
-/* lookup a value for key in login.defs file or similar key value format */
|
||
+/**************************************************
|
||
+ * @brief Lookup a value for the key in the file (i.e. login.defs or a similar
|
||
+ * key-value format file).
|
||
+ *
|
||
+ * @param[in] pamh The pam handle structure
|
||
+ * @param[in] file_name Configuration file name
|
||
+ * @param[in] key Lookup key
|
||
+ *
|
||
+ * @return value, or NULL if key was not found.
|
||
+ **************************************************/
|
||
extern char * PAM_NONNULL((1,2,3))
|
||
pam_modutil_search_key(pam_handle_t *pamh,
|
||
const char *file_name,
|
||
diff --git a/libpam/pam.pc.in b/libpam/pam.pc.in
|
||
index a7cf852d..c3fafe4b 100644
|
||
--- a/libpam/pam.pc.in
|
||
+++ b/libpam/pam.pc.in
|
||
@@ -1,3 +1,5 @@
|
||
+prefix=@prefix@
|
||
+exec_prefix=@exec_prefix@
|
||
libdir=@libdir@
|
||
includedir=@includedir@
|
||
|
||
diff --git a/libpam/pam_handlers.c b/libpam/pam_handlers.c
|
||
index ffa5e4ae..12ebb8fc 100644
|
||
--- a/libpam/pam_handlers.c
|
||
+++ b/libpam/pam_handlers.c
|
||
@@ -889,8 +889,8 @@ int _pam_add_handler(pam_handle_t *pamh
|
||
handler_p = &((*handler_p)->next);
|
||
}
|
||
|
||
- if ((*handler_p = malloc(sizeof(struct handler))) == NULL) {
|
||
- pam_syslog(pamh, LOG_CRIT, "cannot malloc struct handler #1");
|
||
+ if ((*handler_p = calloc(1, sizeof(struct handler))) == NULL) {
|
||
+ pam_syslog(pamh, LOG_CRIT, "cannot allocate struct handler #1");
|
||
return (PAM_ABORT);
|
||
}
|
||
|
||
@@ -904,8 +904,6 @@ int _pam_add_handler(pam_handle_t *pamh
|
||
(*handler_p)->argv = argv; /* not a copy */
|
||
if (((*handler_p)->mod_name = extract_modulename(mod_path)) == NULL)
|
||
return PAM_ABORT;
|
||
- (*handler_p)->grantor = 0;
|
||
- (*handler_p)->next = NULL;
|
||
|
||
/* some of the modules have a second calling function */
|
||
if (handler_p2) {
|
||
@@ -914,8 +912,8 @@ int _pam_add_handler(pam_handle_t *pamh
|
||
handler_p2 = &((*handler_p2)->next);
|
||
}
|
||
|
||
- if ((*handler_p2 = malloc(sizeof(struct handler))) == NULL) {
|
||
- pam_syslog(pamh, LOG_CRIT, "cannot malloc struct handler #2");
|
||
+ if ((*handler_p2 = calloc(1, sizeof(struct handler))) == NULL) {
|
||
+ pam_syslog(pamh, LOG_CRIT, "cannot allocate struct handler #2");
|
||
return (PAM_ABORT);
|
||
}
|
||
|
||
@@ -933,13 +931,9 @@ int _pam_add_handler(pam_handle_t *pamh
|
||
return (PAM_ABORT);
|
||
}
|
||
memcpy((*handler_p2)->argv, argv, argvlen);
|
||
- } else {
|
||
- (*handler_p2)->argv = NULL; /* no arguments */
|
||
}
|
||
if (((*handler_p2)->mod_name = extract_modulename(mod_path)) == NULL)
|
||
return PAM_ABORT;
|
||
- (*handler_p2)->grantor = 0;
|
||
- (*handler_p2)->next = NULL;
|
||
}
|
||
|
||
D(("_pam_add_handler: returning successfully"));
|
||
diff --git a/libpam_misc/pam_misc.pc.in b/libpam_misc/pam_misc.pc.in
|
||
index 0c8898cd..c3e03c4f 100644
|
||
--- a/libpam_misc/pam_misc.pc.in
|
||
+++ b/libpam_misc/pam_misc.pc.in
|
||
@@ -1,3 +1,5 @@
|
||
+prefix=@prefix@
|
||
+exec_prefix=@exec_prefix@
|
||
libdir=@libdir@
|
||
includedir=@includedir@
|
||
|
||
diff --git a/libpamc/pamc.pc.in b/libpamc/pamc.pc.in
|
||
index 25a63854..2d841ebb 100644
|
||
--- a/libpamc/pamc.pc.in
|
||
+++ b/libpamc/pamc.pc.in
|
||
@@ -1,3 +1,5 @@
|
||
+prefix=@prefix@
|
||
+exec_prefix=@exec_prefix@
|
||
libdir=@libdir@
|
||
includedir=@includedir@
|
||
|
||
diff --git a/modules/pam_access/Makefile.am b/modules/pam_access/Makefile.am
|
||
index 5723dd59..b9fbefdb 100644
|
||
--- a/modules/pam_access/Makefile.am
|
||
+++ b/modules/pam_access/Makefile.am
|
||
@@ -18,8 +18,7 @@ securelibdir = $(SECUREDIR)
|
||
secureconfdir = $(SCONFIGDIR)
|
||
|
||
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
|
||
- -DPAM_ACCESS_CONFIG=\"$(SCONFIGDIR)/access.conf\" \
|
||
- -DACCESS_CONF_GLOB=\"$(SCONFIGDIR)/access.d/*.conf\" $(WARN_CFLAGS)
|
||
+ $(WARN_CFLAGS)
|
||
AM_LDFLAGS = -no-undefined -avoid-version -module
|
||
if HAVE_VERSIONING
|
||
AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
|
||
diff --git a/modules/pam_access/pam_access.8.xml b/modules/pam_access/pam_access.8.xml
|
||
index 9a6556cc..db853410 100644
|
||
--- a/modules/pam_access/pam_access.8.xml
|
||
+++ b/modules/pam_access/pam_access.8.xml
|
||
@@ -53,7 +53,7 @@
|
||
or on terminal line names, X <varname>$DISPLAY</varname> values,
|
||
or PAM service names in case of non-networked logins.
|
||
</para>
|
||
- <para>
|
||
+ <para condition="without_vendordir">
|
||
By default rules for access management are taken from config file
|
||
<filename>/etc/security/access.conf</filename> if you don't specify
|
||
another file.
|
||
@@ -66,6 +66,26 @@
|
||
If a config file is explicitly specified with the <option>accessfile</option>
|
||
option the files in the above directory are not parsed.
|
||
</para>
|
||
+ <para condition="with_vendordir">
|
||
+ By default rules for access management are taken from config file
|
||
+ <filename>/etc/security/access.conf</filename> or, if that one is not
|
||
+ present, the file <filename>%vendordir%/security/access.conf</filename>.
|
||
+ These settings can be overruled by setting in a config file explicitly
|
||
+ specified with the <option>accessfile</option> option.
|
||
+ Then individual <filename>*.conf</filename> files from the
|
||
+ <filename>/etc/security/access.d/</filename> and
|
||
+ <filename>%vendordir%/security/access.d</filename> directories are read.
|
||
+ If <filename>/etc/security/access.d/@filename@.conf</filename> exists, then
|
||
+ <filename>%vendordir%/security/access.d/@filename@.conf</filename> will not be used.
|
||
+ All <filename>access.d/*.conf</filename> files are sorted by their
|
||
+ <filename>@filename@.conf</filename> in lexicographic order regardless of which
|
||
+ of the directories they reside in.
|
||
+ The effect of the individual files is the same as if all the files were
|
||
+ concatenated together in the order of parsing. This means that once
|
||
+ a pattern is matched in some file no further files are parsed.
|
||
+ If a config file is explicitly specified with the <option>accessfile</option>
|
||
+ option the files in the above directories are not parsed.
|
||
+ </para>
|
||
<para>
|
||
If Linux PAM is compiled with audit support the module will report
|
||
when it denies access based on origin (host, tty, etc.).
|
||
@@ -233,6 +253,13 @@
|
||
<para>Default configuration file</para>
|
||
</listitem>
|
||
</varlistentry>
|
||
+ <varlistentry condition="with_vendordir">
|
||
+ <term><filename>%vendordir%/security/access.conf</filename></term>
|
||
+ <listitem>
|
||
+ <para>Default configuration file if
|
||
+ <filename>/etc/security/access.conf</filename> does not exist.</para>
|
||
+ </listitem>
|
||
+ </varlistentry>
|
||
</variablelist>
|
||
</refsect1>
|
||
|
||
diff --git a/modules/pam_access/pam_access.c b/modules/pam_access/pam_access.c
|
||
index 277192b9..f7b47227 100644
|
||
--- a/modules/pam_access/pam_access.c
|
||
+++ b/modules/pam_access/pam_access.c
|
||
@@ -56,6 +56,13 @@
|
||
#include "pam_cc_compat.h"
|
||
#include "pam_inline.h"
|
||
|
||
+#define PAM_ACCESS_CONFIG (SCONFIGDIR "/access.conf")
|
||
+#define ACCESS_CONF_GLOB (SCONFIGDIR "/access.d/*.conf")
|
||
+#ifdef VENDOR_SCONFIGDIR
|
||
+#define VENDOR_PAM_ACCESS_CONFIG (VENDOR_SCONFIGDIR "/access.conf")
|
||
+#define VENDOR_ACCESS_CONF_GLOB (VENDOR_SCONFIGDIR "/access.d/*.conf")
|
||
+#endif
|
||
+
|
||
/* login_access.c from logdaemon-5.6 with several changes by A.Nogin: */
|
||
|
||
/*
|
||
@@ -151,6 +158,95 @@ parse_args(pam_handle_t *pamh, struct login_info *loginfo,
|
||
return 1; /* OK */
|
||
}
|
||
|
||
+/* --- evaluting all files in VENDORDIR/security/access.d and /etc/security/access.d --- */
|
||
+static const char *base_name(const char *path)
|
||
+{
|
||
+ const char *base = strrchr(path, '/');
|
||
+ return base ? base+1 : path;
|
||
+}
|
||
+
|
||
+static int
|
||
+compare_filename(const void *a, const void *b)
|
||
+{
|
||
+ return strcmp(base_name(* (const char * const *) a),
|
||
+ base_name(* (const char * const *) b));
|
||
+}
|
||
+
|
||
+/* Evaluating a list of files which have to be parsed in the right order:
|
||
+ *
|
||
+ * - If etc/security/access.d/@filename@.conf exists, then
|
||
+ * %vendordir%/security/access.d/@filename@.conf should not be used.
|
||
+ * - All files in both access.d directories are sorted by their @filename@.conf in
|
||
+ * lexicographic order regardless of which of the directories they reside in. */
|
||
+static char **read_access_dir(pam_handle_t *pamh)
|
||
+{
|
||
+ glob_t globbuf;
|
||
+ size_t i=0;
|
||
+ int glob_rv = glob(ACCESS_CONF_GLOB, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf);
|
||
+ char **file_list;
|
||
+ size_t file_list_size = glob_rv == 0 ? globbuf.gl_pathc : 0;
|
||
+
|
||
+#ifdef VENDOR_ACCESS_CONF_GLOB
|
||
+ glob_t globbuf_vendor;
|
||
+ int glob_rv_vendor = glob(VENDOR_ACCESS_CONF_GLOB, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf_vendor);
|
||
+ if (glob_rv_vendor == 0)
|
||
+ file_list_size += globbuf_vendor.gl_pathc;
|
||
+#endif
|
||
+ file_list = malloc((file_list_size + 1) * sizeof(char*));
|
||
+ if (file_list == NULL) {
|
||
+ pam_syslog(pamh, LOG_ERR, "Cannot allocate memory for file list: %m");
|
||
+#ifdef VENDOR_ACCESS_CONF_GLOB
|
||
+ if (glob_rv_vendor == 0)
|
||
+ globfree(&globbuf_vendor);
|
||
+#endif
|
||
+ if (glob_rv == 0)
|
||
+ globfree(&globbuf);
|
||
+ return NULL;
|
||
+ }
|
||
+
|
||
+ if (glob_rv == 0) {
|
||
+ for (i = 0; i < globbuf.gl_pathc; i++) {
|
||
+ file_list[i] = strdup(globbuf.gl_pathv[i]);
|
||
+ if (file_list[i] == NULL) {
|
||
+ pam_syslog(pamh, LOG_ERR, "strdup failed: %m");
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+#ifdef VENDOR_ACCESS_CONF_GLOB
|
||
+ if (glob_rv_vendor == 0) {
|
||
+ for (size_t j = 0; j < globbuf_vendor.gl_pathc; j++) {
|
||
+ if (glob_rv == 0 && globbuf.gl_pathc > 0) {
|
||
+ int double_found = 0;
|
||
+ for (size_t k = 0; k < globbuf.gl_pathc; k++) {
|
||
+ if (strcmp(base_name(globbuf.gl_pathv[k]),
|
||
+ base_name(globbuf_vendor.gl_pathv[j])) == 0) {
|
||
+ double_found = 1;
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ if (double_found)
|
||
+ continue;
|
||
+ }
|
||
+ file_list[i] = strdup(globbuf_vendor.gl_pathv[j]);
|
||
+ if (file_list[i] == NULL) {
|
||
+ pam_syslog(pamh, LOG_ERR, "strdup failed: %m");
|
||
+ break;
|
||
+ }
|
||
+ i++;
|
||
+ }
|
||
+ globfree(&globbuf_vendor);
|
||
+ }
|
||
+#endif
|
||
+ file_list[i] = NULL;
|
||
+ qsort(file_list, i, sizeof(char *), compare_filename);
|
||
+
|
||
+ if (glob_rv == 0)
|
||
+ globfree(&globbuf);
|
||
+
|
||
+ return file_list;
|
||
+}
|
||
+
|
||
/* --- static functions for checking whether the user should be let in --- */
|
||
|
||
typedef int match_func (pam_handle_t *, char *, struct login_info *);
|
||
@@ -637,7 +733,7 @@ remote_match (pam_handle_t *pamh, char *tok, struct login_info *item)
|
||
if ((str_len = strlen(string)) > tok_len
|
||
&& strcasecmp(tok, string + str_len - tok_len) == 0)
|
||
return YES;
|
||
- } else if (tok[tok_len - 1] == '.') {
|
||
+ } else if (tok[tok_len - 1] == '.') { /* internet network numbers (end with ".") */
|
||
struct addrinfo hint;
|
||
|
||
memset (&hint, '\0', sizeof (hint));
|
||
@@ -678,7 +774,7 @@ remote_match (pam_handle_t *pamh, char *tok, struct login_info *item)
|
||
return NO;
|
||
}
|
||
|
||
- /* Assume network/netmask with an IP of a host. */
|
||
+ /* Assume network/netmask, IP address or hostname. */
|
||
return network_netmask_match(pamh, tok, string, item);
|
||
}
|
||
|
||
@@ -696,7 +792,7 @@ string_match (pam_handle_t *pamh, const char *tok, const char *string,
|
||
/*
|
||
* If the token has the magic value "ALL" the match always succeeds.
|
||
* Otherwise, return YES if the token fully matches the string.
|
||
- * "NONE" token matches NULL string.
|
||
+ * "NONE" token matches NULL string.
|
||
*/
|
||
|
||
if (strcasecmp(tok, "ALL") == 0) { /* all: always matches */
|
||
@@ -714,7 +810,8 @@ string_match (pam_handle_t *pamh, const char *tok, const char *string,
|
||
|
||
/* network_netmask_match - match a string against one token
|
||
* where string is a hostname or ip (v4,v6) address and tok
|
||
- * represents either a single ip (v4,v6) address or a network/netmask
|
||
+ * represents either a hostname, a single ip (v4,v6) address
|
||
+ * or a network/netmask
|
||
*/
|
||
static int
|
||
network_netmask_match (pam_handle_t *pamh,
|
||
@@ -723,10 +820,12 @@ network_netmask_match (pam_handle_t *pamh,
|
||
char *netmask_ptr;
|
||
char netmask_string[MAXHOSTNAMELEN + 1];
|
||
int addr_type;
|
||
+ struct addrinfo *ai = NULL;
|
||
|
||
if (item->debug)
|
||
- pam_syslog (pamh, LOG_DEBUG,
|
||
+ pam_syslog (pamh, LOG_DEBUG,
|
||
"network_netmask_match: tok=%s, item=%s", tok, string);
|
||
+
|
||
/* OK, check if tok is of type addr/mask */
|
||
if ((netmask_ptr = strchr(tok, '/')) != NULL)
|
||
{
|
||
@@ -760,54 +859,108 @@ network_netmask_match (pam_handle_t *pamh,
|
||
netmask_ptr = number_to_netmask(netmask, addr_type,
|
||
netmask_string, MAXHOSTNAMELEN);
|
||
}
|
||
- }
|
||
+
|
||
+ /*
|
||
+ * Construct an addrinfo list from the IP address.
|
||
+ * This should not fail as the input is a correct IP address...
|
||
+ */
|
||
+ if (getaddrinfo (tok, NULL, NULL, &ai) != 0)
|
||
+ {
|
||
+ return NO;
|
||
+ }
|
||
+ }
|
||
else
|
||
- /* NO, then check if it is only an addr */
|
||
- if (isipaddr(tok, NULL, NULL) != YES)
|
||
+ {
|
||
+ /*
|
||
+ * It is either an IP address or a hostname.
|
||
+ * Let getaddrinfo sort everything out
|
||
+ */
|
||
+ if (getaddrinfo (tok, NULL, NULL, &ai) != 0)
|
||
{
|
||
+ pam_syslog(pamh, LOG_ERR, "cannot resolve hostname \"%s\"", tok);
|
||
+
|
||
return NO;
|
||
}
|
||
+ netmask_ptr = NULL;
|
||
+ }
|
||
|
||
if (isipaddr(string, NULL, NULL) != YES)
|
||
{
|
||
- /* Assume network/netmask with a name of a host. */
|
||
struct addrinfo hint;
|
||
|
||
+ /* Assume network/netmask with a name of a host. */
|
||
memset (&hint, '\0', sizeof (hint));
|
||
hint.ai_flags = AI_CANONNAME;
|
||
hint.ai_family = AF_UNSPEC;
|
||
|
||
if (item->gai_rv != 0)
|
||
+ {
|
||
+ freeaddrinfo(ai);
|
||
return NO;
|
||
+ }
|
||
else if (!item->res &&
|
||
(item->gai_rv = getaddrinfo (string, NULL, &hint, &item->res)) != 0)
|
||
+ {
|
||
+ freeaddrinfo(ai);
|
||
return NO;
|
||
+ }
|
||
else
|
||
{
|
||
struct addrinfo *runp = item->res;
|
||
+ struct addrinfo *runp1;
|
||
|
||
while (runp != NULL)
|
||
{
|
||
char buf[INET6_ADDRSTRLEN];
|
||
|
||
- DIAG_PUSH_IGNORE_CAST_ALIGN;
|
||
- inet_ntop (runp->ai_family,
|
||
- runp->ai_family == AF_INET
|
||
- ? (void *) &((struct sockaddr_in *) runp->ai_addr)->sin_addr
|
||
- : (void *) &((struct sockaddr_in6 *) runp->ai_addr)->sin6_addr,
|
||
- buf, sizeof (buf));
|
||
- DIAG_POP_IGNORE_CAST_ALIGN;
|
||
+ if (getnameinfo (runp->ai_addr, runp->ai_addrlen, buf, sizeof (buf), NULL, 0, NI_NUMERICHOST) != 0)
|
||
+ {
|
||
+ freeaddrinfo(ai);
|
||
+ return NO;
|
||
+ }
|
||
|
||
- if (are_addresses_equal(buf, tok, netmask_ptr))
|
||
+ for (runp1 = ai; runp1 != NULL; runp1 = runp1->ai_next)
|
||
{
|
||
- return YES;
|
||
+ char buf1[INET6_ADDRSTRLEN];
|
||
+
|
||
+ if (runp->ai_family != runp1->ai_family)
|
||
+ continue;
|
||
+
|
||
+ if (getnameinfo (runp1->ai_addr, runp1->ai_addrlen, buf1, sizeof (buf1), NULL, 0, NI_NUMERICHOST) != 0)
|
||
+ {
|
||
+ freeaddrinfo(ai);
|
||
+ return NO;
|
||
+ }
|
||
+
|
||
+ if (are_addresses_equal (buf, buf1, netmask_ptr))
|
||
+ {
|
||
+ freeaddrinfo(ai);
|
||
+ return YES;
|
||
+ }
|
||
}
|
||
runp = runp->ai_next;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
- return (are_addresses_equal(string, tok, netmask_ptr));
|
||
+ {
|
||
+ struct addrinfo *runp1;
|
||
+
|
||
+ for (runp1 = ai; runp1 != NULL; runp1 = runp1->ai_next)
|
||
+ {
|
||
+ char buf1[INET6_ADDRSTRLEN];
|
||
+
|
||
+ (void) getnameinfo (runp1->ai_addr, runp1->ai_addrlen, buf1, sizeof (buf1), NULL, 0, NI_NUMERICHOST);
|
||
+
|
||
+ if (are_addresses_equal(string, buf1, netmask_ptr))
|
||
+ {
|
||
+ freeaddrinfo(ai);
|
||
+ return YES;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ freeaddrinfo(ai);
|
||
|
||
return NO;
|
||
}
|
||
@@ -828,7 +981,6 @@ pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
|
||
char hostname[MAXHOSTNAMELEN + 1];
|
||
int rv;
|
||
|
||
-
|
||
/* set username */
|
||
|
||
if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS) {
|
||
@@ -853,6 +1005,18 @@ pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
|
||
return PAM_ABORT;
|
||
}
|
||
|
||
+#ifdef VENDOR_PAM_ACCESS_CONFIG
|
||
+ if (loginfo.config_file == default_config) {
|
||
+ /* Check whether PAM_ACCESS_CONFIG file is available.
|
||
+ * If it does not exist, fall back to VENDOR_PAM_ACCESS_CONFIG file. */
|
||
+ struct stat buffer;
|
||
+ if (stat(loginfo.config_file, &buffer) != 0 && errno == ENOENT) {
|
||
+ default_config = VENDOR_PAM_ACCESS_CONFIG;
|
||
+ loginfo.config_file = default_config;
|
||
+ }
|
||
+ }
|
||
+#endif
|
||
+
|
||
/* remote host name */
|
||
|
||
if (pam_get_item(pamh, PAM_RHOST, &void_from)
|
||
@@ -916,23 +1080,18 @@ pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
|
||
rv = login_access(pamh, &loginfo);
|
||
|
||
if (rv == NOMATCH && loginfo.config_file == default_config) {
|
||
- glob_t globbuf;
|
||
- int i, glob_rv;
|
||
-
|
||
- /* We do not manipulate locale as setlocale() is not
|
||
- * thread safe. We could use uselocale() in future.
|
||
- */
|
||
- glob_rv = glob(ACCESS_CONF_GLOB, GLOB_ERR, NULL, &globbuf);
|
||
- if (!glob_rv) {
|
||
- /* Parse the *.conf files. */
|
||
- for (i = 0; globbuf.gl_pathv[i] != NULL; i++) {
|
||
- loginfo.config_file = globbuf.gl_pathv[i];
|
||
- rv = login_access(pamh, &loginfo);
|
||
- if (rv != NOMATCH)
|
||
- break;
|
||
- }
|
||
- globfree(&globbuf);
|
||
- }
|
||
+ char **filename_list = read_access_dir(pamh);
|
||
+ if (filename_list != NULL) {
|
||
+ for (int i = 0; filename_list[i] != NULL; i++) {
|
||
+ loginfo.config_file = filename_list[i];
|
||
+ rv = login_access(pamh, &loginfo);
|
||
+ if (rv != NOMATCH)
|
||
+ break;
|
||
+ }
|
||
+ for (int i = 0; filename_list[i] != NULL; i++)
|
||
+ free(filename_list[i]);
|
||
+ free(filename_list);
|
||
+ }
|
||
}
|
||
|
||
if (loginfo.gai_rv == 0 && loginfo.res)
|
||
diff --git a/modules/pam_env/.gitignore b/modules/pam_env/.gitignore
|
||
new file mode 100644
|
||
index 00000000..4c5b234b
|
||
--- /dev/null
|
||
+++ b/modules/pam_env/.gitignore
|
||
@@ -0,0 +1 @@
|
||
+tst-pam_env-retval
|
||
diff --git a/modules/pam_env/Makefile.am b/modules/pam_env/Makefile.am
|
||
index c66112d6..b99a83ec 100644
|
||
--- a/modules/pam_env/Makefile.am
|
||
+++ b/modules/pam_env/Makefile.am
|
||
@@ -12,20 +12,23 @@ 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)
|
||
secureconfdir = $(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
|
||
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/pam_env.8.xml b/modules/pam_env/pam_env.8.xml
|
||
index 75ff862b..d7687d6c 100644
|
||
--- a/modules/pam_env/pam_env.8.xml
|
||
+++ b/modules/pam_env/pam_env.8.xml
|
||
@@ -52,13 +52,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.
|
||
@@ -224,12 +266,14 @@
|
||
<title>FILES</title>
|
||
<variablelist>
|
||
<varlistentry>
|
||
+ <term condition="with_vendordir"><filename>/usr/etc/security/pam_env.conf</filename></term>
|
||
<term><filename>/etc/security/pam_env.conf</filename></term>
|
||
<listitem>
|
||
<para>Default configuration file</para>
|
||
</listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
+ <term condition="with_vendordir"><filename>/usr/etc/environment</filename></term>
|
||
<term><filename>/etc/environment</filename></term>
|
||
<listitem>
|
||
<para>Default environment file</para>
|
||
diff --git a/modules/pam_env/pam_env.c b/modules/pam_env/pam_env.c
|
||
index f5f8cead..aabab799 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 "/etc/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,19 @@ 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) {
|
||
+ free(*entry);
|
||
+ }
|
||
+ free(array);
|
||
+}
|
||
+
|
||
/* argument parsing */
|
||
|
||
#define PAM_DEBUG_ARG 0x01
|
||
@@ -75,10 +87,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 +138,154 @@ _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."));
|
||
+#ifdef USE_ECONF
|
||
|
||
- var->name=NULL; var->defval=NULL; var->override=NULL;
|
||
+#define ENVIRONMENT "environment"
|
||
+#define PAM_ENV "pam_env"
|
||
|
||
- 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 */
|
||
-
|
||
- (void) fclose(conf);
|
||
-
|
||
- /* 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);
|
||
+ 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 +373,54 @@ 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);
|
||
+ 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);
|
||
+ return PAM_BUF_ERR;
|
||
+ }
|
||
+ (*lines)[i] = 0;
|
||
+ }
|
||
+
|
||
+ (void) fclose(conf);
|
||
+ 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 +500,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 +516,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; /*
|
||
@@ -677,55 +706,96 @@ static int _expand_arg(pam_handle_t *pamh, char **value)
|
||
return PAM_SUCCESS;
|
||
}
|
||
|
||
-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) {
|
||
+ free(var->name);
|
||
+ }
|
||
+ if (var->defval && ("e != var->defval)) {
|
||
+ free(var->defval);
|
||
+ }
|
||
+ if (var->override && ("e != 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 +817,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 +829,176 @@ 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);
|
||
+ }
|
||
+ free(*env);
|
||
+ }
|
||
+
|
||
+ /* tidy up */
|
||
+ free(env_list);
|
||
+ D(("Exit."));
|
||
+ return retval;
|
||
+}
|
||
|
||
/* --- authentication management functions (only) --- */
|
||
|
||
diff --git a/modules/pam_env/pam_env.conf.5.xml b/modules/pam_env/pam_env.conf.5.xml
|
||
index fca046fe..5c0dbcb8 100644
|
||
--- a/modules/pam_env/pam_env.conf.5.xml
|
||
+++ b/modules/pam_env/pam_env.conf.5.xml
|
||
@@ -20,7 +20,15 @@
|
||
<refsect1 id='pam_env.conf-description'>
|
||
<title>DESCRIPTION</title>
|
||
|
||
- <para>
|
||
+ <para condition="with_vendordir">
|
||
+ The <filename>/usr/etc/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>.
|
||
@@ -61,7 +69,15 @@
|
||
at front) can be used to mark this line as a comment line.
|
||
</para>
|
||
|
||
- <para>
|
||
+ <para condition="with_vendordir">
|
||
+ The <filename>/usr/etc/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.
|
||
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..99e2e2a5
|
||
--- /dev/null
|
||
+++ b/modules/pam_env/tst-pam_env-retval.c
|
||
@@ -0,0 +1,259 @@
|
||
+/*
|
||
+ * 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 <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 dir[] = TEST_NAME_DIR;
|
||
+static const char dir_usr[] = TEST_NAME_DIR "/usr";
|
||
+static const char dir_usr_etc[] = TEST_NAME_DIR "/usr/etc";
|
||
+static const char dir_usr_etc_security[] = TEST_NAME_DIR "/usr/etc/security";
|
||
+static const char my_conf[] = TEST_NAME ".conf";
|
||
+static const char my_env[] = TEST_NAME ".env";
|
||
+static const char usr_env[] = TEST_NAME_DIR "/usr/etc/environment";
|
||
+static const char usr_conf[] = TEST_NAME_DIR "/usr/etc/security/pam_env.conf";
|
||
+
|
||
+static struct pam_conv conv;
|
||
+
|
||
+static void
|
||
+setup(void)
|
||
+{
|
||
+ FILE *fp;
|
||
+
|
||
+ ASSERT_EQ(0, mkdir(dir, 0755));
|
||
+ ASSERT_EQ(0, mkdir(dir_usr, 0755));
|
||
+ ASSERT_EQ(0, mkdir(dir_usr_etc, 0755));
|
||
+ ASSERT_EQ(0, mkdir(dir_usr_etc_security, 0755));
|
||
+
|
||
+ 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));
|
||
+
|
||
+ 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));
|
||
+}
|
||
+
|
||
+static void
|
||
+cleanup(void)
|
||
+{
|
||
+ ASSERT_EQ(0, unlink(my_conf));
|
||
+ ASSERT_EQ(0, unlink(my_env));
|
||
+ ASSERT_EQ(0, unlink(usr_env));
|
||
+ ASSERT_EQ(0, unlink(usr_conf));
|
||
+ ASSERT_EQ(0, rmdir(dir_usr_etc_security));
|
||
+ ASSERT_EQ(0, rmdir(dir_usr_etc));
|
||
+ ASSERT_EQ(0, rmdir(dir_usr));
|
||
+ ASSERT_EQ(0, rmdir(dir));
|
||
+}
|
||
+
|
||
+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, 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, 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;
|
||
+}
|
||
diff --git a/modules/pam_exec/pam_exec.c b/modules/pam_exec/pam_exec.c
|
||
index 05dec167..aeb98cdc 100644
|
||
--- a/modules/pam_exec/pam_exec.c
|
||
+++ b/modules/pam_exec/pam_exec.c
|
||
@@ -48,6 +48,7 @@
|
||
#include <sys/wait.h>
|
||
#include <sys/stat.h>
|
||
#include <sys/types.h>
|
||
+#include <signal.h>
|
||
|
||
#include <security/pam_modules.h>
|
||
#include <security/pam_modutil.h>
|
||
@@ -105,6 +106,7 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
|
||
FILE *stdout_file = NULL;
|
||
int retval;
|
||
const char *name;
|
||
+ struct sigaction newsa, oldsa;
|
||
|
||
if (argc < 1) {
|
||
pam_syslog (pamh, LOG_ERR,
|
||
@@ -226,6 +228,13 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
|
||
return PAM_SERVICE_ERR;
|
||
}
|
||
|
||
+ memset(&newsa, '\0', sizeof(newsa));
|
||
+ newsa.sa_handler = SIG_DFL;
|
||
+ if (sigaction(SIGCHLD, &newsa, &oldsa) == -1) {
|
||
+ pam_syslog(pamh, LOG_ERR, "failed to reset SIGCHLD handler: %m");
|
||
+ return PAM_SYSTEM_ERR;
|
||
+ }
|
||
+
|
||
pid = fork();
|
||
if (pid == -1)
|
||
return PAM_SYSTEM_ERR;
|
||
@@ -263,6 +272,7 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
|
||
|
||
while ((rc = waitpid (pid, &status, 0)) == -1 &&
|
||
errno == EINTR);
|
||
+ sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */
|
||
if (rc == (pid_t)-1)
|
||
{
|
||
pam_syslog (pamh, LOG_ERR, "waitpid returns with -1: %m");
|
||
@@ -305,9 +315,9 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
|
||
}
|
||
else /* child */
|
||
{
|
||
- char **arggv;
|
||
+ const char **arggv;
|
||
int i;
|
||
- char **envlist, **tmp;
|
||
+ char **envlist;
|
||
int envlen, nitems;
|
||
char *envstr;
|
||
enum pam_modutil_redirect_fd redirect_stdin =
|
||
@@ -418,7 +428,7 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
|
||
_exit (ENOMEM);
|
||
|
||
for (i = 0; i < (argc - optargc); i++)
|
||
- arggv[i] = strdup(argv[i+optargc]);
|
||
+ arggv[i] = argv[i+optargc];
|
||
arggv[i] = NULL;
|
||
|
||
/*
|
||
@@ -430,14 +440,12 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
|
||
/* nothing */ ;
|
||
nitems = PAM_ARRAY_SIZE(env_items);
|
||
/* + 2 because of PAM_TYPE and NULL entry */
|
||
- tmp = realloc(envlist, (envlen + nitems + 2) * sizeof(*envlist));
|
||
- if (tmp == NULL)
|
||
+ envlist = realloc(envlist, (envlen + nitems + 2) * sizeof(*envlist));
|
||
+ if (envlist == NULL)
|
||
{
|
||
- free(envlist);
|
||
pam_syslog (pamh, LOG_CRIT, "realloc environment failed: %m");
|
||
_exit (ENOMEM);
|
||
}
|
||
- envlist = tmp;
|
||
for (i = 0; i < nitems; ++i)
|
||
{
|
||
const void *item;
|
||
@@ -446,7 +454,6 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
|
||
continue;
|
||
if (asprintf(&envstr, "%s=%s", env_items[i].name, (const char *)item) < 0)
|
||
{
|
||
- free(envlist);
|
||
pam_syslog (pamh, LOG_CRIT, "prepare environment failed: %m");
|
||
_exit (ENOMEM);
|
||
}
|
||
@@ -456,7 +463,6 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
|
||
|
||
if (asprintf(&envstr, "PAM_TYPE=%s", pam_type) < 0)
|
||
{
|
||
- free(envlist);
|
||
pam_syslog (pamh, LOG_CRIT, "prepare environment failed: %m");
|
||
_exit (ENOMEM);
|
||
}
|
||
@@ -466,10 +472,11 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
|
||
if (debug)
|
||
pam_syslog (pamh, LOG_DEBUG, "Calling %s ...", arggv[0]);
|
||
|
||
- execve (arggv[0], arggv, envlist);
|
||
+ DIAG_PUSH_IGNORE_CAST_QUAL;
|
||
+ execve (arggv[0], (char **) arggv, envlist);
|
||
+ DIAG_POP_IGNORE_CAST_QUAL;
|
||
i = errno;
|
||
pam_syslog (pamh, LOG_ERR, "execve(%s,...) failed: %m", arggv[0]);
|
||
- free(envlist);
|
||
_exit (i);
|
||
}
|
||
return PAM_SYSTEM_ERR; /* will never be reached. */
|
||
diff --git a/modules/pam_faillock/Makefile.am b/modules/pam_faillock/Makefile.am
|
||
index 44a49660..ca73bd05 100644
|
||
--- a/modules/pam_faillock/Makefile.am
|
||
+++ b/modules/pam_faillock/Makefile.am
|
||
@@ -15,12 +15,12 @@ endif
|
||
XMLS = README.xml pam_faillock.8.xml faillock.8.xml faillock.conf.5.xml
|
||
|
||
dist_check_SCRIPTS = tst-pam_faillock
|
||
-TESTS = $(dist_check_SCRIPTS)
|
||
+TESTS = $(dist_check_SCRIPTS) $(check_PROGRAMS)
|
||
|
||
securelibdir = $(SECUREDIR)
|
||
secureconfdir = $(SCONFIGDIR)
|
||
|
||
-noinst_HEADERS = faillock.h
|
||
+noinst_HEADERS = faillock.h faillock_config.h
|
||
|
||
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
|
||
$(WARN_CFLAGS)
|
||
@@ -33,6 +33,9 @@ if HAVE_VERSIONING
|
||
pam_faillock_la_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
|
||
endif
|
||
|
||
+check_PROGRAMS = tst-pam_faillock-retval
|
||
+tst_pam_faillock_retval_LDADD = $(top_builddir)/libpam/libpam.la
|
||
+
|
||
faillock_LDFLAGS = @EXE_LDFLAGS@
|
||
faillock_LDADD = $(top_builddir)/libpam/libpam.la $(LIBAUDIT)
|
||
|
||
@@ -41,8 +44,8 @@ dist_secureconf_DATA = faillock.conf
|
||
securelib_LTLIBRARIES = pam_faillock.la
|
||
sbin_PROGRAMS = faillock
|
||
|
||
-pam_faillock_la_SOURCES = pam_faillock.c faillock.c
|
||
-faillock_SOURCES = main.c faillock.c
|
||
+pam_faillock_la_SOURCES = pam_faillock.c faillock.c faillock_config.c
|
||
+faillock_SOURCES = main.c faillock.c faillock_config.c
|
||
|
||
if ENABLE_REGENERATE_MAN
|
||
dist_noinst_DATA = README
|
||
diff --git a/modules/pam_faillock/faillock.8.xml b/modules/pam_faillock/faillock.8.xml
|
||
index 6c20593c..81d2107c 100644
|
||
--- a/modules/pam_faillock/faillock.8.xml
|
||
+++ b/modules/pam_faillock/faillock.8.xml
|
||
@@ -55,14 +55,31 @@
|
||
|
||
<title>OPTIONS</title>
|
||
<variablelist>
|
||
+ <varlistentry>
|
||
+ <term>
|
||
+ <option>--conf <replaceable>/path/to/config-file</replaceable></option>
|
||
+ </term>
|
||
+ <listitem>
|
||
+ <para>
|
||
+ The file where the configuration is located. The default is
|
||
+ <filename>/etc/security/faillock.conf</filename>.
|
||
+ </para>
|
||
+ </listitem>
|
||
+ </varlistentry>
|
||
<varlistentry>
|
||
<term>
|
||
<option>--dir <replaceable>/path/to/tally-directory</replaceable></option>
|
||
</term>
|
||
<listitem>
|
||
<para>
|
||
- The directory where the user files with the failure records are kept. The
|
||
- default is <filename>/var/run/faillock</filename>.
|
||
+ The directory where the user files with the failure records are kept.
|
||
+ </para>
|
||
+ <para>
|
||
+ The priority to set this option is to use the value provided
|
||
+ from the command line. If this isn't provided, then the value
|
||
+ from the configuration file is used. Finally, if neither of
|
||
+ them has been provided, then
|
||
+ <filename>/var/run/faillock</filename> is used.
|
||
</para>
|
||
</listitem>
|
||
</varlistentry>
|
||
diff --git a/modules/pam_faillock/faillock.conf.5.xml b/modules/pam_faillock/faillock.conf.5.xml
|
||
index 04a84107..8faa5915 100644
|
||
--- a/modules/pam_faillock/faillock.conf.5.xml
|
||
+++ b/modules/pam_faillock/faillock.conf.5.xml
|
||
@@ -44,6 +44,10 @@
|
||
The directory where the user files with the failure records are kept. The
|
||
default is <filename>/var/run/faillock</filename>.
|
||
</para>
|
||
+ <para>
|
||
+ Note: These files will disappear after reboot on systems configured with
|
||
+ directory <filename>/var/run/faillock</filename> mounted on virtual memory.
|
||
+ </para>
|
||
</listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
diff --git a/modules/pam_faillock/faillock.h b/modules/pam_faillock/faillock.h
|
||
index b22a9dfb..0ea0ffba 100644
|
||
--- a/modules/pam_faillock/faillock.h
|
||
+++ b/modules/pam_faillock/faillock.h
|
||
@@ -67,7 +67,6 @@ struct tally_data {
|
||
};
|
||
|
||
#define FAILLOCK_DEFAULT_TALLYDIR "/var/run/faillock"
|
||
-#define FAILLOCK_DEFAULT_CONF "/etc/security/faillock.conf"
|
||
|
||
int open_tally(const char *dir, const char *user, uid_t uid, int create);
|
||
int read_tally(int fd, struct tally_data *tallies);
|
||
diff --git a/modules/pam_faillock/faillock_config.c b/modules/pam_faillock/faillock_config.c
|
||
new file mode 100644
|
||
index 00000000..0d14aad1
|
||
--- /dev/null
|
||
+++ b/modules/pam_faillock/faillock_config.c
|
||
@@ -0,0 +1,266 @@
|
||
+/*
|
||
+ * Copyright (c) 2022 Tomas Mraz <tm@t8m.info>
|
||
+ * Copyright (c) 2022 Iker Pedrosa <ipedrosa@redhat.com>
|
||
+ *
|
||
+ * Redistribution and use in source and binary forms, with or without
|
||
+ * modification, are permitted provided that the following conditions
|
||
+ * are met:
|
||
+ * 1. Redistributions of source code must retain the above copyright
|
||
+ * notice, and the entire permission notice in its entirety,
|
||
+ * including the disclaimer of warranties.
|
||
+ * 2. Redistributions in binary form must reproduce the above copyright
|
||
+ * notice, this list of conditions and the following disclaimer in the
|
||
+ * documentation and/or other materials provided with the distribution.
|
||
+ * 3. The name of the author may not be used to endorse or promote
|
||
+ * products derived from this software without specific prior
|
||
+ * written permission.
|
||
+ *
|
||
+ * ALTERNATIVELY, this product may be distributed under the terms of
|
||
+ * the GNU Public License, in which case the provisions of the GPL are
|
||
+ * required INSTEAD OF the above restrictions. (This clause is
|
||
+ * necessary due to a potential bad interaction between the GPL and
|
||
+ * the restrictions contained in a BSD-style copyright.)
|
||
+ *
|
||
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
+ */
|
||
+
|
||
+#include "config.h"
|
||
+
|
||
+#include <ctype.h>
|
||
+#include <errno.h>
|
||
+#include <stdio.h>
|
||
+#include <stdlib.h>
|
||
+#include <string.h>
|
||
+#include <syslog.h>
|
||
+
|
||
+#include <security/pam_modules.h>
|
||
+
|
||
+#include "faillock_config.h"
|
||
+#include "faillock.h"
|
||
+
|
||
+#define FAILLOCK_DEFAULT_CONF SCONFIGDIR "/faillock.conf"
|
||
+#ifdef VENDOR_SCONFIGDIR
|
||
+#define VENDOR_FAILLOCK_DEFAULT_CONF VENDOR_SCONFIGDIR "/faillock.conf"
|
||
+#endif
|
||
+
|
||
+static void PAM_FORMAT((printf, 3, 4)) PAM_NONNULL((3))
|
||
+config_log(const pam_handle_t *pamh, int priority, const char *fmt, ...)
|
||
+{
|
||
+ va_list args;
|
||
+
|
||
+ va_start(args, fmt);
|
||
+ if (pamh) {
|
||
+ pam_vsyslog(pamh, priority, fmt, args);
|
||
+ } else {
|
||
+ char *buf = NULL;
|
||
+
|
||
+ if (vasprintf(&buf, fmt, args) < 0) {
|
||
+ fprintf(stderr, "vasprintf: %m");
|
||
+ va_end(args);
|
||
+ return;
|
||
+ }
|
||
+ fprintf(stderr, "%s\n", buf);
|
||
+ free(buf);
|
||
+ }
|
||
+ va_end(args);
|
||
+}
|
||
+
|
||
+/* parse a single configuration file */
|
||
+int
|
||
+read_config_file(pam_handle_t *pamh, struct options *opts, const char *cfgfile)
|
||
+{
|
||
+ char linebuf[FAILLOCK_CONF_MAX_LINELEN+1];
|
||
+ const char *fname = (cfgfile != NULL) ? cfgfile : FAILLOCK_DEFAULT_CONF;
|
||
+ FILE *f = fopen(fname, "r");
|
||
+
|
||
+#ifdef VENDOR_FAILLOCK_DEFAULT_CONF
|
||
+ if (f == NULL && errno == ENOENT && cfgfile == NULL) {
|
||
+ /*
|
||
+ * If the default configuration file in /etc does not exist,
|
||
+ * try the vendor configuration file as fallback.
|
||
+ */
|
||
+ f = fopen(VENDOR_FAILLOCK_DEFAULT_CONF, "r");
|
||
+ }
|
||
+#endif /* VENDOR_FAILLOCK_DEFAULT_CONF */
|
||
+
|
||
+ if (f == NULL) {
|
||
+ /* ignore non-existent default config file */
|
||
+ if (errno == ENOENT && cfgfile == NULL)
|
||
+ return PAM_SUCCESS;
|
||
+ return PAM_SERVICE_ERR;
|
||
+ }
|
||
+
|
||
+ while (fgets(linebuf, sizeof(linebuf), f) != NULL) {
|
||
+ size_t len;
|
||
+ char *ptr;
|
||
+ char *name;
|
||
+ int eq;
|
||
+
|
||
+ len = strlen(linebuf);
|
||
+ /* len cannot be 0 unless there is a bug in fgets */
|
||
+ if (len && linebuf[len - 1] != '\n' && !feof(f)) {
|
||
+ (void) fclose(f);
|
||
+ return PAM_SERVICE_ERR;
|
||
+ }
|
||
+
|
||
+ if ((ptr=strchr(linebuf, '#')) != NULL) {
|
||
+ *ptr = '\0';
|
||
+ } else {
|
||
+ ptr = linebuf + len;
|
||
+ }
|
||
+
|
||
+ /* drop terminating whitespace including the \n */
|
||
+ while (ptr > linebuf) {
|
||
+ if (!isspace(*(ptr-1))) {
|
||
+ *ptr = '\0';
|
||
+ break;
|
||
+ }
|
||
+ --ptr;
|
||
+ }
|
||
+
|
||
+ /* skip initial whitespace */
|
||
+ for (ptr = linebuf; isspace(*ptr); ptr++);
|
||
+ if (*ptr == '\0')
|
||
+ continue;
|
||
+
|
||
+ /* grab the key name */
|
||
+ eq = 0;
|
||
+ name = ptr;
|
||
+ while (*ptr != '\0') {
|
||
+ if (isspace(*ptr) || *ptr == '=') {
|
||
+ eq = *ptr == '=';
|
||
+ *ptr = '\0';
|
||
+ ++ptr;
|
||
+ break;
|
||
+ }
|
||
+ ++ptr;
|
||
+ }
|
||
+
|
||
+ /* grab the key value */
|
||
+ while (*ptr != '\0') {
|
||
+ if (*ptr != '=' || eq) {
|
||
+ if (!isspace(*ptr)) {
|
||
+ break;
|
||
+ }
|
||
+ } else {
|
||
+ eq = 1;
|
||
+ }
|
||
+ ++ptr;
|
||
+ }
|
||
+
|
||
+ /* set the key:value pair on opts */
|
||
+ set_conf_opt(pamh, opts, name, ptr);
|
||
+ }
|
||
+
|
||
+ (void)fclose(f);
|
||
+ return PAM_SUCCESS;
|
||
+}
|
||
+
|
||
+void
|
||
+set_conf_opt(pam_handle_t *pamh, struct options *opts, const char *name,
|
||
+ const char *value)
|
||
+{
|
||
+ if (strcmp(name, "dir") == 0) {
|
||
+ if (value[0] != '/') {
|
||
+ config_log(pamh, LOG_ERR,
|
||
+ "Tally directory is not absolute path (%s); keeping value",
|
||
+ value);
|
||
+ } else {
|
||
+ free(opts->dir);
|
||
+ opts->dir = strdup(value);
|
||
+ if (opts->dir == NULL) {
|
||
+ opts->fatal_error = 1;
|
||
+ config_log(pamh, LOG_CRIT, "Error allocating memory: %m");
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ else if (strcmp(name, "deny") == 0) {
|
||
+ if (sscanf(value, "%hu", &opts->deny) != 1) {
|
||
+ config_log(pamh, LOG_ERR,
|
||
+ "Bad number supplied for deny argument");
|
||
+ }
|
||
+ }
|
||
+ else if (strcmp(name, "fail_interval") == 0) {
|
||
+ unsigned int temp;
|
||
+ if (sscanf(value, "%u", &temp) != 1 ||
|
||
+ temp > MAX_TIME_INTERVAL) {
|
||
+ config_log(pamh, LOG_ERR,
|
||
+ "Bad number supplied for fail_interval argument");
|
||
+ } else {
|
||
+ opts->fail_interval = temp;
|
||
+ }
|
||
+ }
|
||
+ else if (strcmp(name, "unlock_time") == 0) {
|
||
+ unsigned int temp;
|
||
+
|
||
+ if (strcmp(value, "never") == 0) {
|
||
+ opts->unlock_time = 0;
|
||
+ }
|
||
+ else if (sscanf(value, "%u", &temp) != 1 ||
|
||
+ temp > MAX_TIME_INTERVAL) {
|
||
+ config_log(pamh, LOG_ERR,
|
||
+ "Bad number supplied for unlock_time argument");
|
||
+ }
|
||
+ else {
|
||
+ opts->unlock_time = temp;
|
||
+ }
|
||
+ }
|
||
+ else if (strcmp(name, "root_unlock_time") == 0) {
|
||
+ unsigned int temp;
|
||
+
|
||
+ if (strcmp(value, "never") == 0) {
|
||
+ opts->root_unlock_time = 0;
|
||
+ }
|
||
+ else if (sscanf(value, "%u", &temp) != 1 ||
|
||
+ temp > MAX_TIME_INTERVAL) {
|
||
+ config_log(pamh, LOG_ERR,
|
||
+ "Bad number supplied for root_unlock_time argument");
|
||
+ } else {
|
||
+ opts->root_unlock_time = temp;
|
||
+ }
|
||
+ }
|
||
+ else if (strcmp(name, "admin_group") == 0) {
|
||
+ free(opts->admin_group);
|
||
+ opts->admin_group = strdup(value);
|
||
+ if (opts->admin_group == NULL) {
|
||
+ opts->fatal_error = 1;
|
||
+ config_log(pamh, LOG_CRIT, "Error allocating memory: %m");
|
||
+ }
|
||
+ }
|
||
+ else if (strcmp(name, "even_deny_root") == 0) {
|
||
+ opts->flags |= FAILLOCK_FLAG_DENY_ROOT;
|
||
+ }
|
||
+ else if (strcmp(name, "audit") == 0) {
|
||
+ opts->flags |= FAILLOCK_FLAG_AUDIT;
|
||
+ }
|
||
+ else if (strcmp(name, "silent") == 0) {
|
||
+ opts->flags |= FAILLOCK_FLAG_SILENT;
|
||
+ }
|
||
+ else if (strcmp(name, "no_log_info") == 0) {
|
||
+ opts->flags |= FAILLOCK_FLAG_NO_LOG_INFO;
|
||
+ }
|
||
+ else if (strcmp(name, "local_users_only") == 0) {
|
||
+ opts->flags |= FAILLOCK_FLAG_LOCAL_ONLY;
|
||
+ }
|
||
+ else if (strcmp(name, "nodelay") == 0) {
|
||
+ opts->flags |= FAILLOCK_FLAG_NO_DELAY;
|
||
+ }
|
||
+ else {
|
||
+ config_log(pamh, LOG_ERR, "Unknown option: %s", name);
|
||
+ }
|
||
+}
|
||
+
|
||
+const char *get_tally_dir(const struct options *opts)
|
||
+{
|
||
+ return (opts->dir != NULL) ? opts->dir : FAILLOCK_DEFAULT_TALLYDIR;
|
||
+}
|
||
diff --git a/modules/pam_faillock/faillock_config.h b/modules/pam_faillock/faillock_config.h
|
||
new file mode 100644
|
||
index 00000000..04bc699b
|
||
--- /dev/null
|
||
+++ b/modules/pam_faillock/faillock_config.h
|
||
@@ -0,0 +1,90 @@
|
||
+/*
|
||
+ * Copyright (c) 2022 Tomas Mraz <tm@t8m.info>
|
||
+ * Copyright (c) 2022 Iker Pedrosa <ipedrosa@redhat.com>
|
||
+ *
|
||
+ * Redistribution and use in source and binary forms, with or without
|
||
+ * modification, are permitted provided that the following conditions
|
||
+ * are met:
|
||
+ * 1. Redistributions of source code must retain the above copyright
|
||
+ * notice, and the entire permission notice in its entirety,
|
||
+ * including the disclaimer of warranties.
|
||
+ * 2. Redistributions in binary form must reproduce the above copyright
|
||
+ * notice, this list of conditions and the following disclaimer in the
|
||
+ * documentation and/or other materials provided with the distribution.
|
||
+ * 3. The name of the author may not be used to endorse or promote
|
||
+ * products derived from this software without specific prior
|
||
+ * written permission.
|
||
+ *
|
||
+ * ALTERNATIVELY, this product may be distributed under the terms of
|
||
+ * the GNU Public License, in which case the provisions of the GPL are
|
||
+ * required INSTEAD OF the above restrictions. (This clause is
|
||
+ * necessary due to a potential bad interaction between the GPL and
|
||
+ * the restrictions contained in a BSD-style copyright.)
|
||
+ *
|
||
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
+ */
|
||
+
|
||
+/*
|
||
+ * faillock_config.h - load configuration options from file
|
||
+ *
|
||
+ */
|
||
+
|
||
+#ifndef _FAILLOCK_CONFIG_H
|
||
+#define _FAILLOCK_CONFIG_H
|
||
+
|
||
+#include <limits.h>
|
||
+#include <stdint.h>
|
||
+#include <sys/types.h>
|
||
+
|
||
+#include <security/pam_ext.h>
|
||
+
|
||
+#define FAILLOCK_FLAG_DENY_ROOT 0x1
|
||
+#define FAILLOCK_FLAG_AUDIT 0x2
|
||
+#define FAILLOCK_FLAG_SILENT 0x4
|
||
+#define FAILLOCK_FLAG_NO_LOG_INFO 0x8
|
||
+#define FAILLOCK_FLAG_UNLOCKED 0x10
|
||
+#define FAILLOCK_FLAG_LOCAL_ONLY 0x20
|
||
+#define FAILLOCK_FLAG_NO_DELAY 0x40
|
||
+
|
||
+#define FAILLOCK_CONF_MAX_LINELEN 1023
|
||
+#define MAX_TIME_INTERVAL 604800 /* 7 days */
|
||
+
|
||
+struct options {
|
||
+ unsigned int action;
|
||
+ unsigned int flags;
|
||
+ unsigned short deny;
|
||
+ unsigned int fail_interval;
|
||
+ unsigned int unlock_time;
|
||
+ unsigned int root_unlock_time;
|
||
+ char *dir;
|
||
+ const char *user;
|
||
+ char *admin_group;
|
||
+ int failures;
|
||
+ uint64_t latest_time;
|
||
+ uid_t uid;
|
||
+ int is_admin;
|
||
+ uint64_t now;
|
||
+ int fatal_error;
|
||
+
|
||
+ unsigned int reset;
|
||
+ const char *progname;
|
||
+ int legacy_output; /* show failure info in pam_tally2 style */
|
||
+};
|
||
+
|
||
+int read_config_file(pam_handle_t *pamh, struct options *opts,
|
||
+ const char *cfgfile);
|
||
+void set_conf_opt(pam_handle_t *pamh, struct options *opts, const char *name,
|
||
+ const char *value);
|
||
+const char *get_tally_dir(const struct options *opts);
|
||
+
|
||
+#endif /* _FAILLOCK_CONFIG_H */
|
||
diff --git a/modules/pam_faillock/main.c b/modules/pam_faillock/main.c
|
||
index f62e1bb2..136be834 100644
|
||
--- a/modules/pam_faillock/main.c
|
||
+++ b/modules/pam_faillock/main.c
|
||
@@ -51,32 +51,40 @@
|
||
#define AUDIT_NO_ID ((unsigned int) -1)
|
||
#endif
|
||
|
||
+#include "pam_inline.h"
|
||
#include "faillock.h"
|
||
-
|
||
-struct options {
|
||
- unsigned int reset;
|
||
- const char *dir;
|
||
- const char *user;
|
||
- const char *progname;
|
||
-};
|
||
+#include "faillock_config.h"
|
||
|
||
static int
|
||
args_parse(int argc, char **argv, struct options *opts)
|
||
{
|
||
int i;
|
||
+ int rv;
|
||
+ const char *dir = NULL;
|
||
+ const char *conf = NULL;
|
||
+
|
||
memset(opts, 0, sizeof(*opts));
|
||
|
||
- opts->dir = FAILLOCK_DEFAULT_TALLYDIR;
|
||
opts->progname = argv[0];
|
||
|
||
for (i = 1; i < argc; ++i) {
|
||
- if (strcmp(argv[i], "--dir") == 0) {
|
||
+ if (strcmp(argv[i], "--conf") == 0) {
|
||
+ ++i;
|
||
+ if (i >= argc || strlen(argv[i]) == 0) {
|
||
+ fprintf(stderr, "%s: No configuration file supplied.\n",
|
||
+ argv[0]);
|
||
+ return -1;
|
||
+ }
|
||
+ conf = argv[i];
|
||
+ }
|
||
+ else if (strcmp(argv[i], "--dir") == 0) {
|
||
++i;
|
||
if (i >= argc || strlen(argv[i]) == 0) {
|
||
- fprintf(stderr, "%s: No directory supplied.\n", argv[0]);
|
||
+ fprintf(stderr, "%s: No records directory supplied.\n",
|
||
+ argv[0]);
|
||
return -1;
|
||
}
|
||
- opts->dir = argv[i];
|
||
+ dir = argv[i];
|
||
}
|
||
else if (strcmp(argv[i], "--user") == 0) {
|
||
++i;
|
||
@@ -89,19 +97,113 @@ args_parse(int argc, char **argv, struct options *opts)
|
||
else if (strcmp(argv[i], "--reset") == 0) {
|
||
opts->reset = 1;
|
||
}
|
||
+ else if (!strcmp(argv[i], "--legacy-output")) {
|
||
+ opts->legacy_output = 1;
|
||
+ }
|
||
else {
|
||
fprintf(stderr, "%s: Unknown option: %s\n", argv[0], argv[i]);
|
||
return -1;
|
||
}
|
||
}
|
||
+
|
||
+ if ((rv = read_config_file(NULL, opts, conf)) != PAM_SUCCESS) {
|
||
+ fprintf(stderr, "Configuration file missing or broken");
|
||
+ return rv;
|
||
+ }
|
||
+
|
||
+ if (dir != NULL) {
|
||
+ free(opts->dir);
|
||
+ opts->dir = strdup(dir);
|
||
+ if (opts->dir == NULL) {
|
||
+ fprintf(stderr, "Error allocating memory: %m");
|
||
+ return -1;
|
||
+ }
|
||
+ }
|
||
+
|
||
return 0;
|
||
}
|
||
|
||
static void
|
||
usage(const char *progname)
|
||
{
|
||
- fprintf(stderr, _("Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset]\n"),
|
||
- progname);
|
||
+ fprintf(stderr,
|
||
+ _("Usage: %s [--dir /path/to/tally-directory]"
|
||
+ " [--user username] [--reset] [--legacy-output]\n"), progname);
|
||
+
|
||
+}
|
||
+
|
||
+static int
|
||
+get_local_time(time_t when, char *timebuf, size_t timebuf_size)
|
||
+{
|
||
+ struct tm *tm;
|
||
+
|
||
+ tm = localtime(&when);
|
||
+ if (tm == NULL) {
|
||
+ return -1;
|
||
+ }
|
||
+ strftime(timebuf, timebuf_size, "%Y-%m-%d %H:%M:%S", tm);
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static void
|
||
+print_in_new_format(struct options *opts, const struct tally_data *tallies, const char *user)
|
||
+{
|
||
+ uint32_t i;
|
||
+
|
||
+ printf("%s:\n", user);
|
||
+ printf("%-19s %-5s %-48s %-5s\n", "When", "Type", "Source", "Valid");
|
||
+
|
||
+ for (i = 0; i < tallies->count; i++) {
|
||
+ uint16_t status;
|
||
+ char timebuf[80];
|
||
+
|
||
+ if (get_local_time(tallies->records[i].time, timebuf, sizeof(timebuf)) != 0) {
|
||
+ fprintf(stderr, "%s: Invalid timestamp in the tally record\n",
|
||
+ opts->progname);
|
||
+ continue;
|
||
+ }
|
||
+
|
||
+ status = tallies->records[i].status;
|
||
+
|
||
+ printf("%-19s %-5s %-52.52s %s\n", timebuf,
|
||
+ status & TALLY_STATUS_RHOST ? "RHOST" : (status & TALLY_STATUS_TTY ? "TTY" : "SVC"),
|
||
+ tallies->records[i].source, status & TALLY_STATUS_VALID ? "V":"I");
|
||
+ }
|
||
+}
|
||
+
|
||
+static void
|
||
+print_in_legacy_format(struct options *opts, const struct tally_data *tallies, const char *user)
|
||
+{
|
||
+ uint32_t tally_count;
|
||
+ static uint32_t pr_once;
|
||
+
|
||
+ if (pr_once == 0) {
|
||
+ printf(_("Login Failures Latest failure From\n"));
|
||
+ pr_once = 1;
|
||
+ }
|
||
+
|
||
+ printf("%-15.15s ", user);
|
||
+
|
||
+ tally_count = tallies->count;
|
||
+
|
||
+ if (tally_count > 0) {
|
||
+ uint32_t i;
|
||
+ char timebuf[80];
|
||
+
|
||
+ i = tally_count - 1;
|
||
+
|
||
+ if (get_local_time(tallies->records[i].time, timebuf, sizeof(timebuf)) != 0) {
|
||
+ fprintf(stderr, "%s: Invalid timestamp in the tally record\n",
|
||
+ opts->progname);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ printf("%5u %25s %s\n",
|
||
+ tally_count, timebuf, tallies->records[i].source);
|
||
+ }
|
||
+ else {
|
||
+ printf("%5u\n", tally_count);
|
||
+ }
|
||
}
|
||
|
||
static int
|
||
@@ -111,10 +213,15 @@ do_user(struct options *opts, const char *user)
|
||
int rv;
|
||
struct tally_data tallies;
|
||
struct passwd *pwd;
|
||
+ const char *dir = get_tally_dir(opts);
|
||
|
||
pwd = getpwnam(user);
|
||
+ if (pwd == NULL) {
|
||
+ fprintf(stderr, "%s: Error no such user: %s\n", opts->progname, user);
|
||
+ return 1;
|
||
+ }
|
||
|
||
- fd = open_tally(opts->dir, user, pwd != NULL ? pwd->pw_uid : 0, 0);
|
||
+ fd = open_tally(dir, user, pwd->pw_uid, 1);
|
||
|
||
if (fd == -1) {
|
||
if (errno == ENOENT) {
|
||
@@ -153,8 +260,6 @@ do_user(struct options *opts, const char *user)
|
||
}
|
||
}
|
||
else {
|
||
- unsigned int i;
|
||
-
|
||
memset(&tallies, 0, sizeof(tallies));
|
||
if (read_tally(fd, &tallies) == -1) {
|
||
fprintf(stderr, "%s: Error reading the tally file for %s:",
|
||
@@ -164,21 +269,13 @@ do_user(struct options *opts, const char *user)
|
||
return 5;
|
||
}
|
||
|
||
- printf("%s:\n", user);
|
||
- printf("%-19s %-5s %-48s %-5s\n", "When", "Type", "Source", "Valid");
|
||
-
|
||
- for (i = 0; i < tallies.count; i++) {
|
||
- struct tm *tm;
|
||
- char timebuf[80];
|
||
- uint16_t status = tallies.records[i].status;
|
||
- time_t when = tallies.records[i].time;
|
||
-
|
||
- tm = localtime(&when);
|
||
- strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm);
|
||
- printf("%-19s %-5s %-52.52s %s\n", timebuf,
|
||
- status & TALLY_STATUS_RHOST ? "RHOST" : (status & TALLY_STATUS_TTY ? "TTY" : "SVC"),
|
||
- tallies.records[i].source, status & TALLY_STATUS_VALID ? "V":"I");
|
||
+ if (opts->legacy_output == 0) {
|
||
+ print_in_new_format(opts, &tallies, user);
|
||
}
|
||
+ else {
|
||
+ print_in_legacy_format(opts, &tallies, user);
|
||
+ }
|
||
+
|
||
free(tallies.records);
|
||
}
|
||
close(fd);
|
||
@@ -190,8 +287,9 @@ do_allusers(struct options *opts)
|
||
{
|
||
struct dirent **userlist;
|
||
int rv, i;
|
||
+ const char *dir = get_tally_dir(opts);
|
||
|
||
- rv = scandir(opts->dir, &userlist, NULL, alphasort);
|
||
+ rv = scandir(dir, &userlist, NULL, alphasort);
|
||
if (rv < 0) {
|
||
fprintf(stderr, "%s: Error reading tally directory: %m\n", opts->progname);
|
||
return 2;
|
||
diff --git a/modules/pam_faillock/pam_faillock.8.xml b/modules/pam_faillock/pam_faillock.8.xml
|
||
index 58c16442..b7b7b0db 100644
|
||
--- a/modules/pam_faillock/pam_faillock.8.xml
|
||
+++ b/modules/pam_faillock/pam_faillock.8.xml
|
||
@@ -134,10 +134,17 @@
|
||
<option>conf=/path/to/config-file</option>
|
||
</term>
|
||
<listitem>
|
||
- <para>
|
||
+ <para condition="without_vendordir">
|
||
Use another configuration file instead of the default
|
||
<filename>/etc/security/faillock.conf</filename>.
|
||
</para>
|
||
+ <para condition="with_vendordir">
|
||
+ Use another configuration file instead of the default
|
||
+ which is to use the file
|
||
+ <filename>/etc/security/faillock.conf</filename> or,
|
||
+ if that one is not present, the file
|
||
+ <filename>%vendordir%/security/faillock.conf</filename>.
|
||
+ </para>
|
||
</listitem>
|
||
</varlistentry>
|
||
</variablelist>
|
||
@@ -320,6 +327,12 @@ session required pam_selinux.so open
|
||
<term><filename>/var/run/faillock/*</filename></term>
|
||
<listitem>
|
||
<para>the files logging the authentication failures for users</para>
|
||
+ <para>
|
||
+ Note: These files will disappear after reboot on systems configured with
|
||
+ directory <filename>/var/run/faillock</filename> mounted on virtual memory.
|
||
+ For persistent storage use the option <emphasis>dir=</emphasis> in
|
||
+ file <filename>/etc/security/faillock.conf</filename>.
|
||
+ </para>
|
||
</listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
@@ -328,6 +341,15 @@ session required pam_selinux.so open
|
||
<para>the config file for pam_faillock options</para>
|
||
</listitem>
|
||
</varlistentry>
|
||
+ <varlistentry condition="with_vendordir">
|
||
+ <term><filename>%vendordir%/security/faillock.conf</filename></term>
|
||
+ <listitem>
|
||
+ <para>
|
||
+ the config file for pam_faillock options. It will be used if
|
||
+ <filename>/etc/security/faillock.conf</filename> does not exist.
|
||
+ </para>
|
||
+ </listitem>
|
||
+ </varlistentry>
|
||
</variablelist>
|
||
</refsect1>
|
||
|
||
diff --git a/modules/pam_faillock/pam_faillock.c b/modules/pam_faillock/pam_faillock.c
|
||
index 8328fbae..ca1c7035 100644
|
||
--- a/modules/pam_faillock/pam_faillock.c
|
||
+++ b/modules/pam_faillock/pam_faillock.c
|
||
@@ -38,7 +38,6 @@
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <unistd.h>
|
||
-#include <stdint.h>
|
||
#include <stdlib.h>
|
||
#include <errno.h>
|
||
#include <time.h>
|
||
@@ -56,55 +55,12 @@
|
||
|
||
#include "pam_inline.h"
|
||
#include "faillock.h"
|
||
+#include "faillock_config.h"
|
||
|
||
#define FAILLOCK_ACTION_PREAUTH 0
|
||
#define FAILLOCK_ACTION_AUTHSUCC 1
|
||
#define FAILLOCK_ACTION_AUTHFAIL 2
|
||
|
||
-#define FAILLOCK_FLAG_DENY_ROOT 0x1
|
||
-#define FAILLOCK_FLAG_AUDIT 0x2
|
||
-#define FAILLOCK_FLAG_SILENT 0x4
|
||
-#define FAILLOCK_FLAG_NO_LOG_INFO 0x8
|
||
-#define FAILLOCK_FLAG_UNLOCKED 0x10
|
||
-#define FAILLOCK_FLAG_LOCAL_ONLY 0x20
|
||
-#define FAILLOCK_FLAG_NO_DELAY 0x40
|
||
-
|
||
-#define MAX_TIME_INTERVAL 604800 /* 7 days */
|
||
-#define FAILLOCK_CONF_MAX_LINELEN 1023
|
||
-
|
||
-static const char default_faillock_conf[] = FAILLOCK_DEFAULT_CONF;
|
||
-
|
||
-struct options {
|
||
- unsigned int action;
|
||
- unsigned int flags;
|
||
- unsigned short deny;
|
||
- unsigned int fail_interval;
|
||
- unsigned int unlock_time;
|
||
- unsigned int root_unlock_time;
|
||
- char *dir;
|
||
- const char *user;
|
||
- char *admin_group;
|
||
- int failures;
|
||
- uint64_t latest_time;
|
||
- uid_t uid;
|
||
- int is_admin;
|
||
- uint64_t now;
|
||
- int fatal_error;
|
||
-};
|
||
-
|
||
-static int read_config_file(
|
||
- pam_handle_t *pamh,
|
||
- struct options *opts,
|
||
- const char *cfgfile
|
||
-);
|
||
-
|
||
-static void set_conf_opt(
|
||
- pam_handle_t *pamh,
|
||
- struct options *opts,
|
||
- const char *name,
|
||
- const char *value
|
||
-);
|
||
-
|
||
static int
|
||
args_parse(pam_handle_t *pamh, int argc, const char **argv,
|
||
int flags, struct options *opts)
|
||
@@ -112,11 +68,10 @@ args_parse(pam_handle_t *pamh, int argc, const char **argv,
|
||
int i;
|
||
int config_arg_index = -1;
|
||
int rv;
|
||
- const char *conf = default_faillock_conf;
|
||
+ const char *conf = NULL;
|
||
|
||
memset(opts, 0, sizeof(*opts));
|
||
|
||
- opts->dir = strdup(FAILLOCK_DEFAULT_TALLYDIR);
|
||
opts->deny = 3;
|
||
opts->fail_interval = 900;
|
||
opts->unlock_time = 600;
|
||
@@ -174,185 +129,11 @@ args_parse(pam_handle_t *pamh, int argc, const char **argv,
|
||
if (flags & PAM_SILENT)
|
||
opts->flags |= FAILLOCK_FLAG_SILENT;
|
||
|
||
- if (opts->dir == NULL) {
|
||
- pam_syslog(pamh, LOG_CRIT, "Error allocating memory: %m");
|
||
- opts->fatal_error = 1;
|
||
- }
|
||
-
|
||
if (opts->fatal_error)
|
||
return PAM_BUF_ERR;
|
||
return PAM_SUCCESS;
|
||
}
|
||
|
||
-/* parse a single configuration file */
|
||
-static int
|
||
-read_config_file(pam_handle_t *pamh, struct options *opts, const char *cfgfile)
|
||
-{
|
||
- FILE *f;
|
||
- char linebuf[FAILLOCK_CONF_MAX_LINELEN+1];
|
||
-
|
||
- f = fopen(cfgfile, "r");
|
||
- if (f == NULL) {
|
||
- /* ignore non-existent default config file */
|
||
- if (errno == ENOENT && cfgfile == default_faillock_conf)
|
||
- return PAM_SUCCESS;
|
||
- return PAM_SERVICE_ERR;
|
||
- }
|
||
-
|
||
- while (fgets(linebuf, sizeof(linebuf), f) != NULL) {
|
||
- size_t len;
|
||
- char *ptr;
|
||
- char *name;
|
||
- int eq;
|
||
-
|
||
- len = strlen(linebuf);
|
||
- /* len cannot be 0 unless there is a bug in fgets */
|
||
- if (len && linebuf[len - 1] != '\n' && !feof(f)) {
|
||
- (void) fclose(f);
|
||
- return PAM_SERVICE_ERR;
|
||
- }
|
||
-
|
||
- if ((ptr=strchr(linebuf, '#')) != NULL) {
|
||
- *ptr = '\0';
|
||
- } else {
|
||
- ptr = linebuf + len;
|
||
- }
|
||
-
|
||
- /* drop terminating whitespace including the \n */
|
||
- while (ptr > linebuf) {
|
||
- if (!isspace(*(ptr-1))) {
|
||
- *ptr = '\0';
|
||
- break;
|
||
- }
|
||
- --ptr;
|
||
- }
|
||
-
|
||
- /* skip initial whitespace */
|
||
- for (ptr = linebuf; isspace(*ptr); ptr++);
|
||
- if (*ptr == '\0')
|
||
- continue;
|
||
-
|
||
- /* grab the key name */
|
||
- eq = 0;
|
||
- name = ptr;
|
||
- while (*ptr != '\0') {
|
||
- if (isspace(*ptr) || *ptr == '=') {
|
||
- eq = *ptr == '=';
|
||
- *ptr = '\0';
|
||
- ++ptr;
|
||
- break;
|
||
- }
|
||
- ++ptr;
|
||
- }
|
||
-
|
||
- /* grab the key value */
|
||
- while (*ptr != '\0') {
|
||
- if (*ptr != '=' || eq) {
|
||
- if (!isspace(*ptr)) {
|
||
- break;
|
||
- }
|
||
- } else {
|
||
- eq = 1;
|
||
- }
|
||
- ++ptr;
|
||
- }
|
||
-
|
||
- /* set the key:value pair on opts */
|
||
- set_conf_opt(pamh, opts, name, ptr);
|
||
- }
|
||
-
|
||
- (void)fclose(f);
|
||
- return PAM_SUCCESS;
|
||
-}
|
||
-
|
||
-static void
|
||
-set_conf_opt(pam_handle_t *pamh, struct options *opts, const char *name, const char *value)
|
||
-{
|
||
- if (strcmp(name, "dir") == 0) {
|
||
- if (value[0] != '/') {
|
||
- pam_syslog(pamh, LOG_ERR,
|
||
- "Tally directory is not absolute path (%s); keeping default", value);
|
||
- } else {
|
||
- free(opts->dir);
|
||
- opts->dir = strdup(value);
|
||
- }
|
||
- }
|
||
- else if (strcmp(name, "deny") == 0) {
|
||
- if (sscanf(value, "%hu", &opts->deny) != 1) {
|
||
- pam_syslog(pamh, LOG_ERR,
|
||
- "Bad number supplied for deny argument");
|
||
- }
|
||
- }
|
||
- else if (strcmp(name, "fail_interval") == 0) {
|
||
- unsigned int temp;
|
||
- if (sscanf(value, "%u", &temp) != 1 ||
|
||
- temp > MAX_TIME_INTERVAL) {
|
||
- pam_syslog(pamh, LOG_ERR,
|
||
- "Bad number supplied for fail_interval argument");
|
||
- } else {
|
||
- opts->fail_interval = temp;
|
||
- }
|
||
- }
|
||
- else if (strcmp(name, "unlock_time") == 0) {
|
||
- unsigned int temp;
|
||
-
|
||
- if (strcmp(value, "never") == 0) {
|
||
- opts->unlock_time = 0;
|
||
- }
|
||
- else if (sscanf(value, "%u", &temp) != 1 ||
|
||
- temp > MAX_TIME_INTERVAL) {
|
||
- pam_syslog(pamh, LOG_ERR,
|
||
- "Bad number supplied for unlock_time argument");
|
||
- }
|
||
- else {
|
||
- opts->unlock_time = temp;
|
||
- }
|
||
- }
|
||
- else if (strcmp(name, "root_unlock_time") == 0) {
|
||
- unsigned int temp;
|
||
-
|
||
- if (strcmp(value, "never") == 0) {
|
||
- opts->root_unlock_time = 0;
|
||
- }
|
||
- else if (sscanf(value, "%u", &temp) != 1 ||
|
||
- temp > MAX_TIME_INTERVAL) {
|
||
- pam_syslog(pamh, LOG_ERR,
|
||
- "Bad number supplied for root_unlock_time argument");
|
||
- } else {
|
||
- opts->root_unlock_time = temp;
|
||
- }
|
||
- }
|
||
- else if (strcmp(name, "admin_group") == 0) {
|
||
- free(opts->admin_group);
|
||
- opts->admin_group = strdup(value);
|
||
- if (opts->admin_group == NULL) {
|
||
- opts->fatal_error = 1;
|
||
- pam_syslog(pamh, LOG_CRIT, "Error allocating memory: %m");
|
||
- }
|
||
- }
|
||
- else if (strcmp(name, "even_deny_root") == 0) {
|
||
- opts->flags |= FAILLOCK_FLAG_DENY_ROOT;
|
||
- }
|
||
- else if (strcmp(name, "audit") == 0) {
|
||
- opts->flags |= FAILLOCK_FLAG_AUDIT;
|
||
- }
|
||
- else if (strcmp(name, "silent") == 0) {
|
||
- opts->flags |= FAILLOCK_FLAG_SILENT;
|
||
- }
|
||
- else if (strcmp(name, "no_log_info") == 0) {
|
||
- opts->flags |= FAILLOCK_FLAG_NO_LOG_INFO;
|
||
- }
|
||
- else if (strcmp(name, "local_users_only") == 0) {
|
||
- opts->flags |= FAILLOCK_FLAG_LOCAL_ONLY;
|
||
- }
|
||
- else if (strcmp(name, "nodelay") == 0) {
|
||
- opts->flags |= FAILLOCK_FLAG_NO_DELAY;
|
||
- }
|
||
- else {
|
||
- pam_syslog(pamh, LOG_ERR, "Unknown option: %s", name);
|
||
- }
|
||
-}
|
||
-
|
||
static int
|
||
check_local_user (pam_handle_t *pamh, const char *user)
|
||
{
|
||
@@ -406,10 +187,11 @@ check_tally(pam_handle_t *pamh, struct options *opts, struct tally_data *tallies
|
||
unsigned int i;
|
||
uint64_t latest_time;
|
||
int failures;
|
||
+ const char *dir = get_tally_dir(opts);
|
||
|
||
opts->now = time(NULL);
|
||
|
||
- tfd = open_tally(opts->dir, opts->user, opts->uid, 0);
|
||
+ tfd = open_tally(dir, opts->user, opts->uid, 0);
|
||
|
||
*fd = tfd;
|
||
|
||
@@ -483,9 +265,10 @@ static void
|
||
reset_tally(pam_handle_t *pamh, struct options *opts, int *fd)
|
||
{
|
||
int rv;
|
||
+ const char *dir = get_tally_dir(opts);
|
||
|
||
if (*fd == -1) {
|
||
- *fd = open_tally(opts->dir, opts->user, opts->uid, 1);
|
||
+ *fd = open_tally(dir, opts->user, opts->uid, 1);
|
||
}
|
||
else {
|
||
while ((rv=ftruncate(*fd, 0)) == -1 && errno == EINTR);
|
||
@@ -504,9 +287,10 @@ write_tally(pam_handle_t *pamh, struct options *opts, struct tally_data *tallies
|
||
unsigned int oldest;
|
||
uint64_t oldtime;
|
||
const void *source = NULL;
|
||
+ const char *dir = get_tally_dir(opts);
|
||
|
||
if (*fd == -1) {
|
||
- *fd = open_tally(opts->dir, opts->user, opts->uid, 1);
|
||
+ *fd = open_tally(dir, opts->user, opts->uid, 1);
|
||
}
|
||
if (*fd == -1) {
|
||
if (errno == EACCES) {
|
||
@@ -590,9 +374,11 @@ write_tally(pam_handle_t *pamh, struct options *opts, struct tally_data *tallies
|
||
}
|
||
close(audit_fd);
|
||
#endif
|
||
- if (!(opts->flags & FAILLOCK_FLAG_NO_LOG_INFO)) {
|
||
- pam_syslog(pamh, LOG_INFO, "Consecutive login failures for user %s account temporarily locked",
|
||
- opts->user);
|
||
+ if (!(opts->flags & FAILLOCK_FLAG_NO_LOG_INFO) &&
|
||
+ ((opts->flags & FAILLOCK_FLAG_DENY_ROOT) || (opts->uid != 0))) {
|
||
+ pam_syslog(pamh, LOG_INFO,
|
||
+ "Consecutive login failures for user %s account temporarily locked",
|
||
+ opts->user);
|
||
}
|
||
}
|
||
|
||
diff --git a/modules/pam_faillock/tst-pam_faillock-retval.c b/modules/pam_faillock/tst-pam_faillock-retval.c
|
||
new file mode 100644
|
||
index 00000000..133026cb
|
||
--- /dev/null
|
||
+++ b/modules/pam_faillock/tst-pam_faillock-retval.c
|
||
@@ -0,0 +1,119 @@
|
||
+/*
|
||
+ * Check pam_faillock return values.
|
||
+ */
|
||
+
|
||
+#include "test_assert.h"
|
||
+
|
||
+#include <limits.h>
|
||
+#include <stdio.h>
|
||
+#include <string.h>
|
||
+#include <unistd.h>
|
||
+#include <security/pam_appl.h>
|
||
+
|
||
+#define MODULE_NAME "pam_faillock"
|
||
+#define TEST_NAME "tst-" MODULE_NAME "-retval"
|
||
+
|
||
+static const char service_file[] = TEST_NAME ".service";
|
||
+static const char config_filename[] = TEST_NAME ".conf";
|
||
+static const char user_name[] = "root";
|
||
+static struct pam_conv conv;
|
||
+
|
||
+int
|
||
+main(void)
|
||
+{
|
||
+ pam_handle_t *pamh = NULL;
|
||
+ FILE *fp;
|
||
+ char cwd[PATH_MAX];
|
||
+
|
||
+ ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd)));
|
||
+
|
||
+ ASSERT_NE(NULL, fp = fopen(config_filename, "w"));
|
||
+ ASSERT_LT(0, fprintf(fp,
|
||
+ "deny = 2\n"
|
||
+ "unlock_time = 5\n"
|
||
+ "root_unlock_time = 5\n"));
|
||
+ ASSERT_EQ(0, fclose(fp));
|
||
+
|
||
+ /* root has access */
|
||
+ ASSERT_NE(NULL, fp = fopen(service_file, "w"));
|
||
+ ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n"
|
||
+ "auth required %s/../pam_permit/.libs/pam_permit.so\n"
|
||
+ "auth required %s/.libs/%s.so authsucc even_deny_root dir=%s conf=%s\n"
|
||
+ "account required %s/.libs/%s.so dir=%s\n"
|
||
+ "password required %s/.libs/%s.so dir=%s\n"
|
||
+ "session required %s/.libs/%s.so dir=%s\n",
|
||
+ cwd,
|
||
+ cwd, MODULE_NAME, cwd, config_filename,
|
||
+ cwd, MODULE_NAME, cwd,
|
||
+ cwd, MODULE_NAME, cwd,
|
||
+ cwd, MODULE_NAME, cwd));
|
||
+
|
||
+ ASSERT_EQ(0, fclose(fp));
|
||
+
|
||
+ ASSERT_EQ(PAM_SUCCESS,
|
||
+ pam_start_confdir(service_file, user_name, &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_SUCCESS, pam_acct_mgmt(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0));
|
||
+ ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
|
||
+ ASSERT_EQ(0, unlink(service_file));
|
||
+ pamh = NULL;
|
||
+
|
||
+ /* root tries to login 2 times without success*/
|
||
+ ASSERT_NE(NULL, fp = fopen(service_file, "w"));
|
||
+ ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n"
|
||
+ "auth requisite %s/.libs/%s.so dir=%s preauth even_deny_root conf=%s\n"
|
||
+ "auth [success=1 default=bad] %s/../pam_debug/.libs/pam_debug.so auth=perm_denied cred=success\n"
|
||
+ "auth [default=die] %s/.libs/%s.so dir=%s authfail even_deny_root conf=%s\n"
|
||
+ "auth sufficient %s/.libs/%s.so dir=%s authsucc even_deny_root conf=%s\n",
|
||
+ cwd, MODULE_NAME, cwd, config_filename,
|
||
+ cwd,
|
||
+ cwd, MODULE_NAME, cwd, config_filename,
|
||
+ cwd, MODULE_NAME, cwd, config_filename));
|
||
+
|
||
+ ASSERT_EQ(0, fclose(fp));
|
||
+
|
||
+ ASSERT_EQ(PAM_SUCCESS,
|
||
+ pam_start_confdir(service_file, user_name, &conv, ".", &pamh));
|
||
+ ASSERT_NE(NULL, pamh);
|
||
+ ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0));
|
||
+ ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0));
|
||
+ pamh = NULL;
|
||
+ ASSERT_EQ(0, unlink(service_file));
|
||
+
|
||
+ /* root is locked for 5 sec*/
|
||
+ ASSERT_NE(NULL, fp = fopen(service_file, "w"));
|
||
+ ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n"
|
||
+ "auth requisite %s/.libs/%s.so dir=%s preauth even_deny_root conf=%s\n"
|
||
+ "auth [success=1 default=bad] %s/../pam_debug/.libs/pam_debug.so auth=success cred=success\n"
|
||
+ "auth [default=die] %s/.libs/%s.so dir=%s authfail even_deny_root conf=%s\n"
|
||
+ "auth sufficient %s/.libs/%s.so dir=%s authsucc even_deny_root conf=%s\n",
|
||
+ cwd, MODULE_NAME, cwd, config_filename,
|
||
+ cwd,
|
||
+ cwd, MODULE_NAME, cwd, config_filename,
|
||
+ cwd, MODULE_NAME, cwd, config_filename));
|
||
+
|
||
+ ASSERT_EQ(0, fclose(fp));
|
||
+
|
||
+ ASSERT_EQ(PAM_SUCCESS,
|
||
+ pam_start_confdir(service_file, user_name, &conv, ".", &pamh));
|
||
+ ASSERT_NE(NULL, pamh);
|
||
+ ASSERT_EQ(PAM_AUTH_ERR, pam_authenticate(pamh, 0));
|
||
+
|
||
+ /* waiting at least 5 sec --> login is working again*/
|
||
+ sleep(6);
|
||
+ ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0));
|
||
+
|
||
+ ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
|
||
+ ASSERT_EQ(0, unlink(service_file));
|
||
+ pamh = NULL;
|
||
+
|
||
+ ASSERT_EQ(0,unlink(user_name));
|
||
+ ASSERT_EQ(0,unlink(config_filename));
|
||
+
|
||
+ return 0;
|
||
+}
|
||
diff --git a/modules/pam_group/Makefile.am b/modules/pam_group/Makefile.am
|
||
index a9a0a1ef..fd88b952 100644
|
||
--- a/modules/pam_group/Makefile.am
|
||
+++ b/modules/pam_group/Makefile.am
|
||
@@ -18,7 +18,7 @@ securelibdir = $(SECUREDIR)
|
||
secureconfdir = $(SCONFIGDIR)
|
||
|
||
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
|
||
- -DPAM_GROUP_CONF=\"$(SCONFIGDIR)/group.conf\" $(WARN_CFLAGS)
|
||
+ $(WARN_CFLAGS)
|
||
AM_LDFLAGS = -no-undefined -avoid-version -module
|
||
if HAVE_VERSIONING
|
||
AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
|
||
diff --git a/modules/pam_group/pam_group.8.xml b/modules/pam_group/pam_group.8.xml
|
||
index 2c1c9058..e4a59dfd 100644
|
||
--- a/modules/pam_group/pam_group.8.xml
|
||
+++ b/modules/pam_group/pam_group.8.xml
|
||
@@ -38,6 +38,10 @@
|
||
By default rules for group memberships are taken from config file
|
||
<filename>/etc/security/group.conf</filename>.
|
||
</para>
|
||
+ <para condition="with_vendordir">
|
||
+ If <filename>/etc/security/group.conf</filename> does not exist,
|
||
+ <filename>%vendordir%/security/group.conf</filename> is used.
|
||
+ </para>
|
||
<para>
|
||
This module's usefulness relies on the file-systems
|
||
accessible to the user. The point being that once granted the
|
||
diff --git a/modules/pam_group/pam_group.c b/modules/pam_group/pam_group.c
|
||
index d9a35ea6..c49358a1 100644
|
||
--- a/modules/pam_group/pam_group.c
|
||
+++ b/modules/pam_group/pam_group.c
|
||
@@ -16,6 +16,7 @@
|
||
#include <time.h>
|
||
#include <syslog.h>
|
||
#include <string.h>
|
||
+#include <errno.h>
|
||
|
||
#include <grp.h>
|
||
#include <sys/types.h>
|
||
@@ -23,6 +24,10 @@
|
||
#include <fcntl.h>
|
||
#include <netdb.h>
|
||
|
||
+#define PAM_GROUP_CONF SCONFIGDIR "/group.conf"
|
||
+#ifdef VENDOR_SCONFIGDIR
|
||
+# define VENDOR_PAM_GROUP_CONF VENDOR_SCONFIGDIR "/group.conf"
|
||
+#endif
|
||
#define PAM_GROUP_BUFLEN 1000
|
||
#define FIELD_SEPARATOR ';' /* this is new as of .02 */
|
||
|
||
@@ -70,7 +75,8 @@ trim_spaces(char *buf, char *from)
|
||
#define STATE_EOF 3 /* end of file or error */
|
||
|
||
static int
|
||
-read_field(const pam_handle_t *pamh, int fd, char **buf, int *from, int *state)
|
||
+read_field(const pam_handle_t *pamh, int fd, char **buf, int *from, int *state,
|
||
+ const char *conf_filename)
|
||
{
|
||
char *to;
|
||
char *src;
|
||
@@ -89,9 +95,9 @@ read_field(const pam_handle_t *pamh, int fd, char **buf, int *from, int *state)
|
||
}
|
||
*from = 0;
|
||
*state = STATE_NL;
|
||
- fd = open(PAM_GROUP_CONF, O_RDONLY);
|
||
+ fd = open(conf_filename, O_RDONLY);
|
||
if (fd < 0) {
|
||
- pam_syslog(pamh, LOG_ERR, "error opening %s: %m", PAM_GROUP_CONF);
|
||
+ pam_syslog(pamh, LOG_ERR, "error opening %s: %m", conf_filename);
|
||
_pam_drop(*buf);
|
||
*state = STATE_EOF;
|
||
return -1;
|
||
@@ -106,7 +112,7 @@ read_field(const pam_handle_t *pamh, int fd, char **buf, int *from, int *state)
|
||
while (fd != -1 && to - *buf < PAM_GROUP_BUFLEN) {
|
||
i = pam_modutil_read(fd, to, PAM_GROUP_BUFLEN - (to - *buf));
|
||
if (i < 0) {
|
||
- pam_syslog(pamh, LOG_ERR, "error reading %s: %m", PAM_GROUP_CONF);
|
||
+ pam_syslog(pamh, LOG_ERR, "error reading %s: %m", conf_filename);
|
||
close(fd);
|
||
memset(*buf, 0, PAM_GROUP_BUFLEN);
|
||
_pam_drop(*buf);
|
||
@@ -573,6 +579,18 @@ static int check_account(pam_handle_t *pamh, const char *service,
|
||
int retval=PAM_SUCCESS;
|
||
gid_t *grps;
|
||
int no_grps;
|
||
+ const char *conf_filename = PAM_GROUP_CONF;
|
||
+
|
||
+#ifdef VENDOR_PAM_GROUP_CONF
|
||
+ /*
|
||
+ * Check whether PAM_GROUP_CONF file is available.
|
||
+ * If it does not exist, fall back to VENDOR_PAM_GROUP_CONF file.
|
||
+ */
|
||
+ struct stat stat_buffer;
|
||
+ if (stat(conf_filename, &stat_buffer) != 0 && errno == ENOENT) {
|
||
+ conf_filename = VENDOR_PAM_GROUP_CONF;
|
||
+ }
|
||
+#endif
|
||
|
||
/*
|
||
* first we get the current list of groups - the application
|
||
@@ -611,7 +629,7 @@ static int check_account(pam_handle_t *pamh, const char *service,
|
||
|
||
/* here we get the service name field */
|
||
|
||
- fd = read_field(pamh, fd, &buffer, &from, &state);
|
||
+ fd = read_field(pamh, fd, &buffer, &from, &state, conf_filename);
|
||
if (!buffer || !buffer[0]) {
|
||
/* empty line .. ? */
|
||
continue;
|
||
@@ -621,7 +639,7 @@ static int check_account(pam_handle_t *pamh, const char *service,
|
||
|
||
if (state != STATE_FIELD) {
|
||
pam_syslog(pamh, LOG_ERR,
|
||
- "%s: malformed rule #%d", PAM_GROUP_CONF, count);
|
||
+ "%s: malformed rule #%d", conf_filename, count);
|
||
continue;
|
||
}
|
||
|
||
@@ -630,10 +648,10 @@ static int check_account(pam_handle_t *pamh, const char *service,
|
||
|
||
/* here we get the terminal name field */
|
||
|
||
- fd = read_field(pamh, fd, &buffer, &from, &state);
|
||
+ fd = read_field(pamh, fd, &buffer, &from, &state, conf_filename);
|
||
if (state != STATE_FIELD) {
|
||
pam_syslog(pamh, LOG_ERR,
|
||
- "%s: malformed rule #%d", PAM_GROUP_CONF, count);
|
||
+ "%s: malformed rule #%d", conf_filename, count);
|
||
continue;
|
||
}
|
||
good &= logic_field(pamh,tty, buffer, count, is_same);
|
||
@@ -641,10 +659,10 @@ static int check_account(pam_handle_t *pamh, const char *service,
|
||
|
||
/* here we get the username field */
|
||
|
||
- fd = read_field(pamh, fd, &buffer, &from, &state);
|
||
+ fd = read_field(pamh, fd, &buffer, &from, &state, conf_filename);
|
||
if (state != STATE_FIELD) {
|
||
pam_syslog(pamh, LOG_ERR,
|
||
- "%s: malformed rule #%d", PAM_GROUP_CONF, count);
|
||
+ "%s: malformed rule #%d", conf_filename, count);
|
||
continue;
|
||
}
|
||
/* If buffer starts with @, we are using netgroups */
|
||
@@ -663,20 +681,20 @@ static int check_account(pam_handle_t *pamh, const char *service,
|
||
|
||
/* here we get the time field */
|
||
|
||
- fd = read_field(pamh, fd, &buffer, &from, &state);
|
||
+ fd = read_field(pamh, fd, &buffer, &from, &state, conf_filename);
|
||
if (state != STATE_FIELD) {
|
||
pam_syslog(pamh, LOG_ERR,
|
||
- "%s: malformed rule #%d", PAM_GROUP_CONF, count);
|
||
+ "%s: malformed rule #%d", conf_filename, count);
|
||
continue;
|
||
}
|
||
|
||
good &= logic_field(pamh,&here_and_now, buffer, count, check_time);
|
||
D(("with time: %s", good ? "passes":"fails" ));
|
||
|
||
- fd = read_field(pamh, fd, &buffer, &from, &state);
|
||
+ fd = read_field(pamh, fd, &buffer, &from, &state, conf_filename);
|
||
if (state == STATE_FIELD) {
|
||
pam_syslog(pamh, LOG_ERR,
|
||
- "%s: poorly terminated rule #%d", PAM_GROUP_CONF, count);
|
||
+ "%s: poorly terminated rule #%d", conf_filename, count);
|
||
continue;
|
||
}
|
||
|
||
diff --git a/modules/pam_issue/pam_issue.c b/modules/pam_issue/pam_issue.c
|
||
index 5b6a4669..2f53440f 100644
|
||
--- a/modules/pam_issue/pam_issue.c
|
||
+++ b/modules/pam_issue/pam_issue.c
|
||
@@ -36,98 +36,6 @@
|
||
|
||
static int _user_prompt_set = 0;
|
||
|
||
-static int read_issue_raw(pam_handle_t *pamh, FILE *fp, char **prompt);
|
||
-static int read_issue_quoted(pam_handle_t *pamh, FILE *fp, char **prompt);
|
||
-
|
||
-/* --- authentication management functions (only) --- */
|
||
-
|
||
-int
|
||
-pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
|
||
- int argc, const char **argv)
|
||
-{
|
||
- int retval = PAM_SERVICE_ERR;
|
||
- FILE *fp;
|
||
- const char *issue_file = NULL;
|
||
- int parse_esc = 1;
|
||
- const void *item = NULL;
|
||
- const char *cur_prompt;
|
||
- char *issue_prompt = NULL;
|
||
-
|
||
- /* If we've already set the prompt, don't set it again */
|
||
- if(_user_prompt_set)
|
||
- return PAM_IGNORE;
|
||
-
|
||
- /* We set this here so if we fail below, we won't get further
|
||
- than this next time around (only one real failure) */
|
||
- _user_prompt_set = 1;
|
||
-
|
||
- for ( ; argc-- > 0 ; ++argv ) {
|
||
- const char *str;
|
||
-
|
||
- if ((str = pam_str_skip_prefix(*argv, "issue=")) != NULL) {
|
||
- issue_file = str;
|
||
- D(("set issue_file to: %s", issue_file));
|
||
- } else if (!strcmp(*argv,"noesc")) {
|
||
- parse_esc = 0;
|
||
- D(("turning off escape parsing by request"));
|
||
- } else
|
||
- D(("unknown option passed: %s", *argv));
|
||
- }
|
||
-
|
||
- if (issue_file == NULL)
|
||
- issue_file = "/etc/issue";
|
||
-
|
||
- if ((fp = fopen(issue_file, "r")) == NULL) {
|
||
- pam_syslog(pamh, LOG_ERR, "error opening %s: %m", issue_file);
|
||
- return PAM_SERVICE_ERR;
|
||
- }
|
||
-
|
||
- if ((retval = pam_get_item(pamh, PAM_USER_PROMPT, &item)) != PAM_SUCCESS) {
|
||
- fclose(fp);
|
||
- return retval;
|
||
- }
|
||
-
|
||
- cur_prompt = item;
|
||
- if (cur_prompt == NULL)
|
||
- cur_prompt = "";
|
||
-
|
||
- if (parse_esc)
|
||
- retval = read_issue_quoted(pamh, fp, &issue_prompt);
|
||
- else
|
||
- retval = read_issue_raw(pamh, fp, &issue_prompt);
|
||
-
|
||
- fclose(fp);
|
||
-
|
||
- if (retval != PAM_SUCCESS)
|
||
- goto out;
|
||
-
|
||
- {
|
||
- size_t size = strlen(issue_prompt) + strlen(cur_prompt) + 1;
|
||
- char *new_prompt = realloc(issue_prompt, size);
|
||
-
|
||
- if (new_prompt == NULL) {
|
||
- pam_syslog(pamh, LOG_CRIT, "out of memory");
|
||
- retval = PAM_BUF_ERR;
|
||
- goto out;
|
||
- }
|
||
- issue_prompt = new_prompt;
|
||
- }
|
||
-
|
||
- strcat(issue_prompt, cur_prompt);
|
||
- retval = pam_set_item(pamh, PAM_USER_PROMPT,
|
||
- (const void *) issue_prompt);
|
||
- out:
|
||
- _pam_drop(issue_prompt);
|
||
- return (retval == PAM_SUCCESS) ? PAM_IGNORE : retval;
|
||
-}
|
||
-
|
||
-int
|
||
-pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED,
|
||
- int argc UNUSED, const char **argv UNUSED)
|
||
-{
|
||
- return PAM_IGNORE;
|
||
-}
|
||
-
|
||
static int
|
||
read_issue_raw(pam_handle_t *pamh, FILE *fp, char **prompt)
|
||
{
|
||
@@ -303,4 +211,91 @@ read_issue_quoted(pam_handle_t *pamh, FILE *fp, char **prompt)
|
||
return PAM_SUCCESS;
|
||
}
|
||
|
||
-/* end of module definition */
|
||
+/* --- authentication management functions (only) --- */
|
||
+
|
||
+int
|
||
+pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
|
||
+ int argc, const char **argv)
|
||
+{
|
||
+ int retval = PAM_SERVICE_ERR;
|
||
+ FILE *fp;
|
||
+ const char *issue_file = NULL;
|
||
+ int parse_esc = 1;
|
||
+ const void *item = NULL;
|
||
+ const char *cur_prompt;
|
||
+ char *issue_prompt = NULL;
|
||
+
|
||
+ /* If we've already set the prompt, don't set it again */
|
||
+ if(_user_prompt_set)
|
||
+ return PAM_IGNORE;
|
||
+
|
||
+ /* We set this here so if we fail below, we won't get further
|
||
+ than this next time around (only one real failure) */
|
||
+ _user_prompt_set = 1;
|
||
+
|
||
+ for ( ; argc-- > 0 ; ++argv ) {
|
||
+ const char *str;
|
||
+
|
||
+ if ((str = pam_str_skip_prefix(*argv, "issue=")) != NULL) {
|
||
+ issue_file = str;
|
||
+ D(("set issue_file to: %s", issue_file));
|
||
+ } else if (!strcmp(*argv,"noesc")) {
|
||
+ parse_esc = 0;
|
||
+ D(("turning off escape parsing by request"));
|
||
+ } else
|
||
+ D(("unknown option passed: %s", *argv));
|
||
+ }
|
||
+
|
||
+ if (issue_file == NULL)
|
||
+ issue_file = "/etc/issue";
|
||
+
|
||
+ if ((fp = fopen(issue_file, "r")) == NULL) {
|
||
+ pam_syslog(pamh, LOG_ERR, "error opening %s: %m", issue_file);
|
||
+ return PAM_SERVICE_ERR;
|
||
+ }
|
||
+
|
||
+ if ((retval = pam_get_item(pamh, PAM_USER_PROMPT, &item)) != PAM_SUCCESS) {
|
||
+ fclose(fp);
|
||
+ return retval;
|
||
+ }
|
||
+
|
||
+ cur_prompt = item;
|
||
+ if (cur_prompt == NULL)
|
||
+ cur_prompt = "";
|
||
+
|
||
+ if (parse_esc)
|
||
+ retval = read_issue_quoted(pamh, fp, &issue_prompt);
|
||
+ else
|
||
+ retval = read_issue_raw(pamh, fp, &issue_prompt);
|
||
+
|
||
+ fclose(fp);
|
||
+
|
||
+ if (retval != PAM_SUCCESS)
|
||
+ goto out;
|
||
+
|
||
+ {
|
||
+ size_t size = strlen(issue_prompt) + strlen(cur_prompt) + 1;
|
||
+ char *new_prompt = realloc(issue_prompt, size);
|
||
+
|
||
+ if (new_prompt == NULL) {
|
||
+ pam_syslog(pamh, LOG_CRIT, "out of memory");
|
||
+ retval = PAM_BUF_ERR;
|
||
+ goto out;
|
||
+ }
|
||
+ issue_prompt = new_prompt;
|
||
+ }
|
||
+
|
||
+ strcat(issue_prompt, cur_prompt);
|
||
+ retval = pam_set_item(pamh, PAM_USER_PROMPT,
|
||
+ (const void *) issue_prompt);
|
||
+ out:
|
||
+ _pam_drop(issue_prompt);
|
||
+ return (retval == PAM_SUCCESS) ? PAM_IGNORE : retval;
|
||
+}
|
||
+
|
||
+int
|
||
+pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
|
||
+ int argc UNUSED, const char **argv UNUSED)
|
||
+{
|
||
+ return PAM_IGNORE;
|
||
+}
|
||
diff --git a/modules/pam_keyinit/pam_keyinit.c b/modules/pam_keyinit/pam_keyinit.c
|
||
index 92e4953b..df9804b9 100644
|
||
--- a/modules/pam_keyinit/pam_keyinit.c
|
||
+++ b/modules/pam_keyinit/pam_keyinit.c
|
||
@@ -21,6 +21,7 @@
|
||
#include <security/pam_modutil.h>
|
||
#include <security/pam_ext.h>
|
||
#include <sys/syscall.h>
|
||
+#include <stdatomic.h>
|
||
|
||
#define KEY_SPEC_SESSION_KEYRING -3 /* ID for session keyring */
|
||
#define KEY_SPEC_USER_KEYRING -4 /* ID for UID-specific keyring */
|
||
@@ -31,12 +32,12 @@
|
||
#define KEYCTL_REVOKE 3 /* revoke a key */
|
||
#define KEYCTL_LINK 8 /* link a key into a keyring */
|
||
|
||
-static int my_session_keyring = 0;
|
||
-static int session_counter = 0;
|
||
-static int do_revoke = 0;
|
||
-static uid_t revoke_as_uid;
|
||
-static gid_t revoke_as_gid;
|
||
-static int xdebug = 0;
|
||
+static _Thread_local int my_session_keyring = 0;
|
||
+static _Atomic int session_counter = 0;
|
||
+static _Thread_local int do_revoke = 0;
|
||
+static _Thread_local uid_t revoke_as_uid;
|
||
+static _Thread_local gid_t revoke_as_gid;
|
||
+static _Thread_local int xdebug = 0;
|
||
|
||
static void debug(pam_handle_t *pamh, const char *fmt, ...)
|
||
__attribute__((format(printf, 2, 3)));
|
||
@@ -64,6 +65,33 @@ static void error(pam_handle_t *pamh, const char *fmt, ...)
|
||
va_end(va);
|
||
}
|
||
|
||
+static int pam_setreuid(uid_t ruid, uid_t euid)
|
||
+{
|
||
+#if defined(SYS_setreuid32)
|
||
+ return syscall(SYS_setreuid32, ruid, euid);
|
||
+#else
|
||
+ return syscall(SYS_setreuid, ruid, euid);
|
||
+#endif
|
||
+}
|
||
+
|
||
+static int pam_setregid(gid_t rgid, gid_t egid)
|
||
+{
|
||
+#if defined(SYS_setregid32)
|
||
+ return syscall(SYS_setregid32, rgid, egid);
|
||
+#else
|
||
+ return syscall(SYS_setregid, rgid, egid);
|
||
+#endif
|
||
+}
|
||
+
|
||
+static int pam_setresuid(uid_t ruid, uid_t euid, uid_t suid)
|
||
+{
|
||
+#if defined(SYS_setresuid32)
|
||
+ return syscall(SYS_setresuid32, ruid, euid, suid);
|
||
+#else
|
||
+ return syscall(SYS_setresuid, ruid, euid, suid);
|
||
+#endif
|
||
+}
|
||
+
|
||
/*
|
||
* initialise the session keyring for this process
|
||
*/
|
||
@@ -140,14 +168,14 @@ static int kill_keyrings(pam_handle_t *pamh, int error_ret)
|
||
|
||
/* switch to the real UID and GID so that we have permission to
|
||
* revoke the key */
|
||
- if (revoke_as_gid != old_gid && setregid(-1, revoke_as_gid) < 0) {
|
||
+ if (revoke_as_gid != old_gid && pam_setregid(-1, revoke_as_gid) < 0) {
|
||
error(pamh, "Unable to change GID to %d temporarily\n", revoke_as_gid);
|
||
return error_ret;
|
||
}
|
||
|
||
- if (revoke_as_uid != old_uid && setresuid(-1, revoke_as_uid, old_uid) < 0) {
|
||
+ if (revoke_as_uid != old_uid && pam_setresuid(-1, revoke_as_uid, old_uid) < 0) {
|
||
error(pamh, "Unable to change UID to %d temporarily\n", revoke_as_uid);
|
||
- if (getegid() != old_gid && setregid(-1, old_gid) < 0)
|
||
+ if (getegid() != old_gid && pam_setregid(-1, old_gid) < 0)
|
||
error(pamh, "Unable to change GID back to %d\n", old_gid);
|
||
return error_ret;
|
||
}
|
||
@@ -157,12 +185,12 @@ static int kill_keyrings(pam_handle_t *pamh, int error_ret)
|
||
}
|
||
|
||
/* return to the original UID and GID (probably root) */
|
||
- if (revoke_as_uid != old_uid && setreuid(-1, old_uid) < 0) {
|
||
+ if (revoke_as_uid != old_uid && pam_setreuid(-1, old_uid) < 0) {
|
||
error(pamh, "Unable to change UID back to %d\n", old_uid);
|
||
ret = error_ret;
|
||
}
|
||
|
||
- if (revoke_as_gid != old_gid && setregid(-1, old_gid) < 0) {
|
||
+ if (revoke_as_gid != old_gid && pam_setregid(-1, old_gid) < 0) {
|
||
error(pamh, "Unable to change GID back to %d\n", old_gid);
|
||
ret = error_ret;
|
||
}
|
||
@@ -215,14 +243,14 @@ static int do_keyinit(pam_handle_t *pamh, int argc, const char **argv, int error
|
||
|
||
/* switch to the real UID and GID so that the keyring ends up owned by
|
||
* the right user */
|
||
- if (gid != old_gid && setregid(gid, -1) < 0) {
|
||
+ if (gid != old_gid && pam_setregid(gid, -1) < 0) {
|
||
error(pamh, "Unable to change GID to %d temporarily\n", gid);
|
||
return error_ret;
|
||
}
|
||
|
||
- if (uid != old_uid && setreuid(uid, -1) < 0) {
|
||
+ if (uid != old_uid && pam_setreuid(uid, -1) < 0) {
|
||
error(pamh, "Unable to change UID to %d temporarily\n", uid);
|
||
- if (setregid(old_gid, -1) < 0)
|
||
+ if (pam_setregid(old_gid, -1) < 0)
|
||
error(pamh, "Unable to change GID back to %d\n", old_gid);
|
||
return error_ret;
|
||
}
|
||
@@ -230,12 +258,12 @@ static int do_keyinit(pam_handle_t *pamh, int argc, const char **argv, int error
|
||
ret = init_keyrings(pamh, force, error_ret);
|
||
|
||
/* return to the original UID and GID (probably root) */
|
||
- if (uid != old_uid && setreuid(old_uid, -1) < 0) {
|
||
+ if (uid != old_uid && pam_setreuid(old_uid, -1) < 0) {
|
||
error(pamh, "Unable to change UID back to %d\n", old_uid);
|
||
ret = error_ret;
|
||
}
|
||
|
||
- if (gid != old_gid && setregid(old_gid, -1) < 0) {
|
||
+ if (gid != old_gid && pam_setregid(old_gid, -1) < 0) {
|
||
error(pamh, "Unable to change GID back to %d\n", old_gid);
|
||
ret = error_ret;
|
||
}
|
||
diff --git a/modules/pam_lastlog/pam_lastlog.c b/modules/pam_lastlog/pam_lastlog.c
|
||
index abd048df..797a61ce 100644
|
||
--- a/modules/pam_lastlog/pam_lastlog.c
|
||
+++ b/modules/pam_lastlog/pam_lastlog.c
|
||
@@ -57,14 +57,13 @@ struct lastlog {
|
||
# define PATH_LOGIN_DEFS "/etc/login.defs"
|
||
#endif
|
||
|
||
-/* XXX - time before ignoring lock. Is 1 sec enough? */
|
||
-#define LASTLOG_IGNORE_LOCK_TIME 1
|
||
-
|
||
#define DEFAULT_HOST "" /* "[no.where]" */
|
||
#define DEFAULT_TERM "" /* "tt???" */
|
||
|
||
#define DEFAULT_INACTIVE_DAYS 90
|
||
#define MAX_INACTIVE_DAYS 100000
|
||
+#define LOCK_RETRIES 3 /* number of file lock retries */
|
||
+#define LOCK_RETRY_DELAY 1 /* seconds to wait between lock attempts */
|
||
|
||
#include <security/pam_modules.h>
|
||
#include <security/_pam_macros.h>
|
||
@@ -266,6 +265,7 @@ last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid, time_t
|
||
{
|
||
struct flock last_lock;
|
||
struct lastlog last_login;
|
||
+ int lock_retries = LOCK_RETRIES;
|
||
int retval = PAM_SUCCESS;
|
||
char the_time[256];
|
||
char *date = NULL;
|
||
@@ -278,11 +278,19 @@ last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid, time_t
|
||
last_lock.l_start = sizeof(last_login) * (off_t) uid;
|
||
last_lock.l_len = sizeof(last_login);
|
||
|
||
- if (fcntl(last_fd, F_SETLK, &last_lock) < 0) {
|
||
+ while (fcntl(last_fd, F_SETLK, &last_lock) < 0) {
|
||
+ if (0 == --lock_retries) {
|
||
+ /* read lock failed, proceed anyway to avoid possible DoS */
|
||
+ D(("locking %s failed", _PATH_LASTLOG));
|
||
+ pam_syslog(pamh, LOG_INFO,
|
||
+ "file %s is locked/read, proceeding anyway",
|
||
+ _PATH_LASTLOG);
|
||
+ break;
|
||
+ }
|
||
D(("locking %s failed..(waiting a little)", _PATH_LASTLOG));
|
||
- pam_syslog(pamh, LOG_WARNING,
|
||
- "file %s is locked/read", _PATH_LASTLOG);
|
||
- sleep(LASTLOG_IGNORE_LOCK_TIME);
|
||
+ pam_syslog(pamh, LOG_INFO,
|
||
+ "file %s is locked/read, retrying", _PATH_LASTLOG);
|
||
+ sleep(LOCK_RETRY_DELAY);
|
||
}
|
||
|
||
if (pam_modutil_read(last_fd, (char *) &last_login,
|
||
@@ -380,6 +388,7 @@ last_login_write(pam_handle_t *pamh, int announce, int last_fd,
|
||
int setrlimit_res;
|
||
struct flock last_lock;
|
||
struct lastlog last_login;
|
||
+ int lock_retries = LOCK_RETRIES;
|
||
time_t ll_time;
|
||
const void *void_remote_host = NULL;
|
||
const char *remote_host;
|
||
@@ -426,10 +435,17 @@ last_login_write(pam_handle_t *pamh, int announce, int last_fd,
|
||
last_lock.l_start = sizeof(last_login) * (off_t) uid;
|
||
last_lock.l_len = sizeof(last_login);
|
||
|
||
- if (fcntl(last_fd, F_SETLK, &last_lock) < 0) {
|
||
+ while (fcntl(last_fd, F_SETLK, &last_lock) < 0) {
|
||
+ if (0 == --lock_retries) {
|
||
+ D(("locking %s failed", _PATH_LASTLOG));
|
||
+ pam_syslog(pamh, LOG_ERR,
|
||
+ "file %s is locked/write", _PATH_LASTLOG);
|
||
+ return PAM_SERVICE_ERR;
|
||
+ }
|
||
D(("locking %s failed..(waiting a little)", _PATH_LASTLOG));
|
||
- pam_syslog(pamh, LOG_WARNING, "file %s is locked/write", _PATH_LASTLOG);
|
||
- sleep(LASTLOG_IGNORE_LOCK_TIME);
|
||
+ pam_syslog(pamh, LOG_INFO,
|
||
+ "file %s is locked/write, retrying", _PATH_LASTLOG);
|
||
+ sleep(LOCK_RETRY_DELAY);
|
||
}
|
||
|
||
/*
|
||
@@ -573,12 +589,12 @@ last_login_failed(pam_handle_t *pamh, int announce, const char *user, time_t llt
|
||
time_t lf_time;
|
||
|
||
lf_time = utuser.ut_tv.tv_sec;
|
||
- tm = localtime_r (&lf_time, &tm_buf);
|
||
- strftime (the_time, sizeof (the_time),
|
||
- /* TRANSLATORS: "strftime options for date of last login" */
|
||
- _(" %a %b %e %H:%M:%S %Z %Y"), tm);
|
||
-
|
||
- date = the_time;
|
||
+ if ((tm = localtime_r (&lf_time, &tm_buf)) != NULL) {
|
||
+ strftime (the_time, sizeof (the_time),
|
||
+ /* TRANSLATORS: "strftime options for date of last login" */
|
||
+ _(" %a %b %e %H:%M:%S %Z %Y"), tm);
|
||
+ date = the_time;
|
||
+ }
|
||
}
|
||
|
||
/* we want & have the host? */
|
||
diff --git a/modules/pam_limits/Makefile.am b/modules/pam_limits/Makefile.am
|
||
index 911b07b3..9ae1794d 100644
|
||
--- a/modules/pam_limits/Makefile.am
|
||
+++ b/modules/pam_limits/Makefile.am
|
||
@@ -19,8 +19,8 @@ secureconfdir = $(SCONFIGDIR)
|
||
limits_conf_dir = $(SCONFIGDIR)/limits.d
|
||
|
||
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
|
||
- -DLIMITS_FILE_DIR=\"$(limits_conf_dir)/*.conf\" \
|
||
- -DLIMITS_FILE=\"$(SCONFIGDIR)/limits.conf\" $(WARN_CFLAGS)
|
||
+ -DLIMITS_FILE_DIR=\"$(limits_conf_dir)\" \
|
||
+ $(WARN_CFLAGS)
|
||
AM_LDFLAGS = -no-undefined -avoid-version -module
|
||
if HAVE_VERSIONING
|
||
AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
|
||
diff --git a/modules/pam_limits/pam_limits.8.xml b/modules/pam_limits/pam_limits.8.xml
|
||
index bc46cbf4..422924fe 100644
|
||
--- a/modules/pam_limits/pam_limits.8.xml
|
||
+++ b/modules/pam_limits/pam_limits.8.xml
|
||
@@ -48,7 +48,7 @@
|
||
obtained in a user-session. Users of <emphasis>uid=0</emphasis> are affected
|
||
by this limits, too.
|
||
</para>
|
||
- <para>
|
||
+ <para condition="without_vendordir">
|
||
By default limits are taken from the <filename>/etc/security/limits.conf</filename>
|
||
config file. Then individual *.conf files from the <filename>/etc/security/limits.d/</filename>
|
||
directory are read. The files are parsed one after another in the order of "C" locale.
|
||
@@ -57,6 +57,23 @@
|
||
If a config file is explicitly specified with a module option then the
|
||
files in the above directory are not parsed.
|
||
</para>
|
||
+ <para condition="with_vendordir">
|
||
+ By default limits are taken from the <filename>/etc/security/limits.conf</filename>
|
||
+ config file or, if that one is not present, the file
|
||
+ <filename>%vendordir%/security/limits.conf</filename>.
|
||
+ Then individual <filename>*.conf</filename> files from the
|
||
+ <filename>/etc/security/limits.d/</filename> and
|
||
+ <filename>%vendordir%/security/limits.d</filename> directories are read.
|
||
+ If <filename>/etc/security/limits.d/@filename@.conf</filename> exists, then
|
||
+ <filename>%vendordir%/security/limits.d/@filename@.conf</filename> will not be used.
|
||
+ All <filename>limits.d/*.conf</filename> files are sorted by their
|
||
+ <filename>@filename@.conf</filename> in lexicographic order regardless of which
|
||
+ of the directories they reside in.
|
||
+ The effect of the individual files is the same as if all the files were
|
||
+ concatenated together in the order of parsing.
|
||
+ If a config file is explicitly specified with the <option>config</option>
|
||
+ option the files in the above directories are not parsed.
|
||
+ </para>
|
||
<para>
|
||
The module must not be called by a multithreaded application.
|
||
</para>
|
||
@@ -211,6 +228,13 @@
|
||
<para>Default configuration file</para>
|
||
</listitem>
|
||
</varlistentry>
|
||
+ <varlistentry condition="with_vendordir">
|
||
+ <term><filename>%vendordir%/security/limits.conf</filename></term>
|
||
+ <listitem>
|
||
+ <para>Default configuration file if
|
||
+ <filename>/etc/security/limits.conf</filename> does not exist.</para>
|
||
+ </listitem>
|
||
+ </varlistentry>
|
||
</variablelist>
|
||
</refsect1>
|
||
|
||
diff --git a/modules/pam_limits/pam_limits.c b/modules/pam_limits/pam_limits.c
|
||
index 7cc45d77..f9489dbe 100644
|
||
--- a/modules/pam_limits/pam_limits.c
|
||
+++ b/modules/pam_limits/pam_limits.c
|
||
@@ -47,6 +47,10 @@
|
||
#include <libaudit.h>
|
||
#endif
|
||
|
||
+#ifndef PR_SET_NO_NEW_PRIVS
|
||
+# define PR_SET_NO_NEW_PRIVS 38 /* from <linux/prctl.h> */
|
||
+#endif
|
||
+
|
||
/* Module defines */
|
||
#define LINE_LENGTH 1024
|
||
|
||
@@ -119,9 +123,14 @@ struct pam_limit_s {
|
||
#define PAM_SET_ALL 0x0010
|
||
|
||
/* Limits from globbed files. */
|
||
-#define LIMITS_CONF_GLOB LIMITS_FILE_DIR
|
||
+#define LIMITS_CONF_GLOB (LIMITS_FILE_DIR "/*.conf")
|
||
+
|
||
+#define LIMITS_FILE (SCONFIGDIR "/limits.conf")
|
||
|
||
-#define CONF_FILE (pl->conf_file != NULL)?pl->conf_file:LIMITS_FILE
|
||
+#ifdef VENDOR_SCONFIGDIR
|
||
+#define VENDOR_LIMITS_FILE (VENDOR_SCONFIGDIR "/limits.conf")
|
||
+#define VENDOR_LIMITS_CONF_GLOB (VENDOR_SCONFIGDIR "/limits.d/*.conf")
|
||
+#endif
|
||
|
||
static int
|
||
_pam_parse (const pam_handle_t *pamh, int argc, const char **argv,
|
||
@@ -806,18 +815,22 @@ parse_uid_range(pam_handle_t *pamh, const char *domain,
|
||
|
||
static int
|
||
parse_config_file(pam_handle_t *pamh, const char *uname, uid_t uid, gid_t gid,
|
||
- int ctrl, struct pam_limit_s *pl)
|
||
+ int ctrl, struct pam_limit_s *pl, const int conf_file_set_by_user)
|
||
{
|
||
FILE *fil;
|
||
char buf[LINE_LENGTH];
|
||
|
||
- /* check for the LIMITS_FILE */
|
||
+ /* check for the conf_file */
|
||
if (ctrl & PAM_DEBUG_ARG)
|
||
- pam_syslog(pamh, LOG_DEBUG, "reading settings from '%s'", CONF_FILE);
|
||
- fil = fopen(CONF_FILE, "r");
|
||
+ pam_syslog(pamh, LOG_DEBUG, "reading settings from '%s'", pl->conf_file);
|
||
+ fil = fopen(pl->conf_file, "r");
|
||
if (fil == NULL) {
|
||
- pam_syslog (pamh, LOG_WARNING,
|
||
- "cannot read settings from %s: %m", CONF_FILE);
|
||
+ if (errno == ENOENT && !conf_file_set_by_user)
|
||
+ return PAM_SUCCESS; /* file is not there and it has not been set by the conf= argument */
|
||
+
|
||
+ pam_syslog(pamh, LOG_WARNING,
|
||
+ "cannot read settings from %s: %s", pl->conf_file,
|
||
+ strerror(errno));
|
||
return PAM_SERVICE_ERR;
|
||
}
|
||
|
||
@@ -1074,33 +1087,132 @@ static int setup_limits(pam_handle_t *pamh,
|
||
return retval;
|
||
}
|
||
|
||
+/* --- evaluting all files in VENDORDIR/security/limits.d and /etc/security/limits.d --- */
|
||
+static const char *
|
||
+base_name(const char *path)
|
||
+{
|
||
+ const char *base = strrchr(path, '/');
|
||
+ return base ? base+1 : path;
|
||
+}
|
||
+
|
||
+static int
|
||
+compare_filename(const void *a, const void *b)
|
||
+{
|
||
+ return strcmp(base_name(* (const char * const *) a),
|
||
+ base_name(* (const char * const *) b));
|
||
+}
|
||
+
|
||
+/* Evaluating a list of files which have to be parsed in the right order:
|
||
+ *
|
||
+ * - If etc/security/limits.d/@filename@.conf exists, then
|
||
+ * %vendordir%/security/limits.d/@filename@.conf should not be used.
|
||
+ * - All files in both limits.d directories are sorted by their @filename@.conf in
|
||
+ * lexicographic order regardless of which of the directories they reside in. */
|
||
+static char **
|
||
+read_limits_dir(pam_handle_t *pamh)
|
||
+{
|
||
+ glob_t globbuf;
|
||
+ size_t i=0;
|
||
+ int glob_rv = glob(LIMITS_CONF_GLOB, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf);
|
||
+ char **file_list;
|
||
+ size_t file_list_size = glob_rv == 0 ? globbuf.gl_pathc : 0;
|
||
+
|
||
+#ifdef VENDOR_LIMITS_CONF_GLOB
|
||
+ glob_t globbuf_vendor;
|
||
+ int glob_rv_vendor = glob(VENDOR_LIMITS_CONF_GLOB, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf_vendor);
|
||
+ if (glob_rv_vendor == 0)
|
||
+ file_list_size += globbuf_vendor.gl_pathc;
|
||
+#endif
|
||
+ file_list = malloc((file_list_size + 1) * sizeof(char*));
|
||
+ if (file_list == NULL) {
|
||
+ pam_syslog(pamh, LOG_ERR, "Cannot allocate memory for file list: %m");
|
||
+#ifdef VENDOR_ACCESS_CONF_GLOB
|
||
+ if (glob_rv_vendor == 0)
|
||
+ globfree(&globbuf_vendor);
|
||
+#endif
|
||
+ if (glob_rv == 0)
|
||
+ globfree(&globbuf);
|
||
+ return NULL;
|
||
+ }
|
||
+
|
||
+ if (glob_rv == 0) {
|
||
+ for (i = 0; i < globbuf.gl_pathc; i++) {
|
||
+ file_list[i] = strdup(globbuf.gl_pathv[i]);
|
||
+ if (file_list[i] == NULL) {
|
||
+ pam_syslog(pamh, LOG_ERR, "strdup failed: %m");
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+#ifdef VENDOR_LIMITS_CONF_GLOB
|
||
+ if (glob_rv_vendor == 0) {
|
||
+ for (size_t j = 0; j < globbuf_vendor.gl_pathc; j++) {
|
||
+ if (glob_rv == 0 && globbuf.gl_pathc > 0) {
|
||
+ int double_found = 0;
|
||
+ for (size_t k = 0; k < globbuf.gl_pathc; k++) {
|
||
+ if (strcmp(base_name(globbuf.gl_pathv[k]),
|
||
+ base_name(globbuf_vendor.gl_pathv[j])) == 0) {
|
||
+ double_found = 1;
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ if (double_found)
|
||
+ continue;
|
||
+ }
|
||
+ file_list[i] = strdup(globbuf_vendor.gl_pathv[j]);
|
||
+ if (file_list[i] == NULL) {
|
||
+ pam_syslog(pamh, LOG_ERR, "strdup failed: %m");
|
||
+ break;
|
||
+ }
|
||
+ i++;
|
||
+ }
|
||
+ globfree(&globbuf_vendor);
|
||
+ }
|
||
+#endif
|
||
+ file_list[i] = NULL;
|
||
+ qsort(file_list, i, sizeof(char *), compare_filename);
|
||
+ if (glob_rv == 0)
|
||
+ globfree(&globbuf);
|
||
+
|
||
+ return file_list;
|
||
+}
|
||
+
|
||
/* now the session stuff */
|
||
int
|
||
pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED,
|
||
int argc, const char **argv)
|
||
{
|
||
- int retval;
|
||
- int i;
|
||
- int glob_rc;
|
||
+ int retval, i;
|
||
char *user_name;
|
||
struct passwd *pwd;
|
||
int ctrl;
|
||
struct pam_limit_s plstruct;
|
||
struct pam_limit_s *pl = &plstruct;
|
||
- glob_t globbuf;
|
||
- const char *oldlocale;
|
||
|
||
D(("called."));
|
||
|
||
memset(pl, 0, sizeof(*pl));
|
||
- memset(&globbuf, 0, sizeof(globbuf));
|
||
|
||
ctrl = _pam_parse(pamh, argc, argv, pl);
|
||
retval = pam_get_item( pamh, PAM_USER, (void*) &user_name );
|
||
if ( user_name == NULL || retval != PAM_SUCCESS ) {
|
||
pam_syslog(pamh, LOG_ERR, "open_session - error recovering username");
|
||
return PAM_SESSION_ERR;
|
||
- }
|
||
+ }
|
||
+
|
||
+ int conf_file_set_by_user = (pl->conf_file != NULL);
|
||
+ if (pl->conf_file == NULL) {
|
||
+ pl->conf_file = LIMITS_FILE;
|
||
+#ifdef VENDOR_LIMITS_FILE
|
||
+ /*
|
||
+ * Check whether LIMITS_FILE file is available.
|
||
+ * If it does not exist, fall back to VENDOR_LIMITS_FILE file.
|
||
+ */
|
||
+ struct stat buffer;
|
||
+ if (stat(pl->conf_file, &buffer) != 0 && errno == ENOENT)
|
||
+ pl->conf_file = VENDOR_LIMITS_FILE;
|
||
+#endif
|
||
+ }
|
||
|
||
pwd = pam_modutil_getpwnam(pamh, user_name);
|
||
if (!pwd) {
|
||
@@ -1116,46 +1228,39 @@ pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED,
|
||
return PAM_ABORT;
|
||
}
|
||
|
||
- retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl);
|
||
+ retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid,
|
||
+ ctrl, pl, conf_file_set_by_user);
|
||
if (retval == PAM_IGNORE) {
|
||
- D(("the configuration file ('%s') has an applicable '<domain> -' entry", CONF_FILE));
|
||
+ D(("the configuration file ('%s') has an applicable '<domain> -' entry", pl->conf_file));
|
||
return PAM_SUCCESS;
|
||
}
|
||
- if (retval != PAM_SUCCESS || pl->conf_file != NULL)
|
||
+ if (retval != PAM_SUCCESS || conf_file_set_by_user)
|
||
/* skip reading limits.d if config file explicitly specified */
|
||
goto out;
|
||
|
||
/* Read subsequent *.conf files, if they exist. */
|
||
-
|
||
- /* set the LC_COLLATE so the sorting order doesn't depend
|
||
- on system locale */
|
||
-
|
||
- oldlocale = setlocale(LC_COLLATE, "C");
|
||
- glob_rc = glob(LIMITS_CONF_GLOB, GLOB_ERR, NULL, &globbuf);
|
||
-
|
||
- if (oldlocale != NULL)
|
||
- setlocale (LC_COLLATE, oldlocale);
|
||
-
|
||
- if (!glob_rc) {
|
||
- /* Parse the *.conf files. */
|
||
- for (i = 0; globbuf.gl_pathv[i] != NULL; i++) {
|
||
- pl->conf_file = globbuf.gl_pathv[i];
|
||
- retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl);
|
||
- if (retval == PAM_IGNORE) {
|
||
- D(("the configuration file ('%s') has an applicable '<domain> -' entry", pl->conf_file));
|
||
- globfree(&globbuf);
|
||
- return PAM_SUCCESS;
|
||
- }
|
||
- if (retval != PAM_SUCCESS)
|
||
- goto out;
|
||
+ char **filename_list = read_limits_dir(pamh);
|
||
+ if (filename_list != NULL) {
|
||
+ for (i = 0; filename_list[i] != NULL; i++) {
|
||
+ pl->conf_file = filename_list[i];
|
||
+ retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl, 0);
|
||
+ if (retval != PAM_SUCCESS)
|
||
+ break;
|
||
}
|
||
+ for (i = 0; filename_list[i] != NULL; i++)
|
||
+ free(filename_list[i]);
|
||
+ free(filename_list);
|
||
+ }
|
||
+
|
||
+ if (retval == PAM_IGNORE) {
|
||
+ D(("the configuration file ('%s') has an applicable '<domain> -' entry", pl->conf_file));
|
||
+ return PAM_SUCCESS;
|
||
}
|
||
|
||
out:
|
||
- globfree(&globbuf);
|
||
if (retval != PAM_SUCCESS)
|
||
{
|
||
- pam_syslog(pamh, LOG_ERR, "error parsing the configuration file: '%s' ",CONF_FILE);
|
||
+ pam_syslog(pamh, LOG_ERR, "error parsing the configuration file: '%s' ", pl->conf_file);
|
||
return retval;
|
||
}
|
||
|
||
diff --git a/modules/pam_mail/pam_mail.c b/modules/pam_mail/pam_mail.c
|
||
index 17383c7b..7eb94fc7 100644
|
||
--- a/modules/pam_mail/pam_mail.c
|
||
+++ b/modules/pam_mail/pam_mail.c
|
||
@@ -286,7 +286,7 @@ report_mail(pam_handle_t *pamh, int ctrl, int type, const char *folder)
|
||
switch (type)
|
||
{
|
||
case HAVE_NO_MAIL:
|
||
- retval = pam_info (pamh, "%s", _("You have no mail."));
|
||
+ retval = pam_info (pamh, "%s", _("You do not have any new mail."));
|
||
break;
|
||
case HAVE_NEW_MAIL:
|
||
retval = pam_info (pamh, "%s", _("You have new mail."));
|
||
diff --git a/modules/pam_mkhomedir/pam_mkhomedir.c b/modules/pam_mkhomedir/pam_mkhomedir.c
|
||
index 48e578fa..6ddcd5a8 100644
|
||
--- a/modules/pam_mkhomedir/pam_mkhomedir.c
|
||
+++ b/modules/pam_mkhomedir/pam_mkhomedir.c
|
||
@@ -125,15 +125,6 @@ create_homedir (pam_handle_t *pamh, options_t *opt,
|
||
|
||
D(("called."));
|
||
|
||
- /*
|
||
- * This code arranges that the demise of the child does not cause
|
||
- * the application to receive a signal it is not expecting - which
|
||
- * may kill the application or worse.
|
||
- */
|
||
- memset(&newsa, '\0', sizeof(newsa));
|
||
- newsa.sa_handler = SIG_DFL;
|
||
- sigaction(SIGCHLD, &newsa, &oldsa);
|
||
-
|
||
if (opt->ctrl & MKHOMEDIR_DEBUG) {
|
||
pam_syslog(pamh, LOG_DEBUG, "Executing mkhomedir_helper.");
|
||
}
|
||
@@ -153,6 +144,15 @@ create_homedir (pam_handle_t *pamh, options_t *opt,
|
||
login_homemode = _pam_conv_str_umask_to_homemode(opt->umask);
|
||
}
|
||
|
||
+ /*
|
||
+ * This code arranges that the demise of the child does not cause
|
||
+ * the application to receive a signal it is not expecting - which
|
||
+ * may kill the application or worse.
|
||
+ */
|
||
+ memset(&newsa, '\0', sizeof(newsa));
|
||
+ newsa.sa_handler = SIG_DFL;
|
||
+ sigaction(SIGCHLD, &newsa, &oldsa);
|
||
+
|
||
/* fork */
|
||
child = fork();
|
||
if (child == 0) {
|
||
diff --git a/modules/pam_motd/pam_motd.c b/modules/pam_motd/pam_motd.c
|
||
index 6ac8cba2..5ca486e4 100644
|
||
--- a/modules/pam_motd/pam_motd.c
|
||
+++ b/modules/pam_motd/pam_motd.c
|
||
@@ -166,11 +166,6 @@ static int compare_strings(const void *a, const void *b)
|
||
}
|
||
}
|
||
|
||
-static int filter_dirents(const struct dirent *d)
|
||
-{
|
||
- return (d->d_type == DT_REG || d->d_type == DT_LNK);
|
||
-}
|
||
-
|
||
static void try_to_display_directories_with_overrides(pam_handle_t *pamh,
|
||
char **motd_dir_path_split, unsigned int num_motd_dirs, int report_missing)
|
||
{
|
||
@@ -199,8 +194,7 @@ static void try_to_display_directories_with_overrides(pam_handle_t *pamh,
|
||
|
||
for (i = 0; i < num_motd_dirs; i++) {
|
||
int rv;
|
||
- rv = scandir(motd_dir_path_split[i], &(dirscans[i]),
|
||
- filter_dirents, alphasort);
|
||
+ rv = scandir(motd_dir_path_split[i], &(dirscans[i]), NULL, NULL);
|
||
if (rv < 0) {
|
||
if (errno != ENOENT || report_missing) {
|
||
pam_syslog(pamh, LOG_ERR, "error scanning directory %s: %m",
|
||
@@ -215,6 +209,41 @@ static void try_to_display_directories_with_overrides(pam_handle_t *pamh,
|
||
if (dirscans_size_total == 0)
|
||
goto out;
|
||
|
||
+ /* filter out unwanted names, directories, and complement data with lstat() */
|
||
+ for (i = 0; i < num_motd_dirs; i++) {
|
||
+ struct dirent **d = dirscans[i];
|
||
+ for (unsigned int j = 0; j < dirscans_sizes[i]; j++) {
|
||
+ int rc;
|
||
+ char *fullpath;
|
||
+ struct stat s;
|
||
+
|
||
+ switch(d[j]->d_type) { /* the filetype determines how to proceed */
|
||
+ case DT_REG: /* regular files and */
|
||
+ case DT_LNK: /* symlinks */
|
||
+ continue; /* are good. */
|
||
+ case DT_UNKNOWN: /* for file systems that do not provide */
|
||
+ /* a filetype, we use lstat() */
|
||
+ if (join_dir_strings(&fullpath, motd_dir_path_split[i],
|
||
+ d[j]->d_name) <= 0)
|
||
+ break;
|
||
+ rc = lstat(fullpath, &s);
|
||
+ _pam_drop(fullpath); /* free the memory alloc'ed by join_dir_strings */
|
||
+ if (rc != 0) /* if the lstat() somehow failed */
|
||
+ break;
|
||
+
|
||
+ if (S_ISREG(s.st_mode) || /* regular files and */
|
||
+ S_ISLNK(s.st_mode)) continue; /* symlinks are good */
|
||
+ break;
|
||
+ case DT_DIR: /* We don't want directories */
|
||
+ default: /* nor anything else */
|
||
+ break;
|
||
+ }
|
||
+ _pam_drop(d[j]); /* free memory */
|
||
+ d[j] = NULL; /* indicate this one was dropped */
|
||
+ dirscans_size_total--;
|
||
+ }
|
||
+ }
|
||
+
|
||
/* Allocate space for all file names found in the directories, including duplicates. */
|
||
if ((dirnames_all = calloc(dirscans_size_total, sizeof(*dirnames_all))) == NULL) {
|
||
pam_syslog(pamh, LOG_CRIT, "failed to allocate dirname array");
|
||
@@ -225,8 +254,10 @@ static void try_to_display_directories_with_overrides(pam_handle_t *pamh,
|
||
unsigned int j;
|
||
|
||
for (j = 0; j < dirscans_sizes[i]; j++) {
|
||
- dirnames_all[i_dirnames] = dirscans[i][j]->d_name;
|
||
- i_dirnames++;
|
||
+ if (NULL != dirscans[i][j]) {
|
||
+ dirnames_all[i_dirnames] = dirscans[i][j]->d_name;
|
||
+ i_dirnames++;
|
||
+ }
|
||
}
|
||
}
|
||
|
||
diff --git a/modules/pam_namespace/Makefile.am b/modules/pam_namespace/Makefile.am
|
||
index 47cc38e1..33375857 100644
|
||
--- a/modules/pam_namespace/Makefile.am
|
||
+++ b/modules/pam_namespace/Makefile.am
|
||
@@ -21,7 +21,7 @@ namespaceddir = $(SCONFIGDIR)/namespace.d
|
||
servicedir = $(systemdunitdir)
|
||
|
||
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
|
||
- -DSECURECONF_DIR=\"$(SCONFIGDIR)/\" $(WARN_CFLAGS)
|
||
+ $(WARN_CFLAGS)
|
||
AM_LDFLAGS = -no-undefined -avoid-version -module
|
||
if HAVE_VERSIONING
|
||
AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
|
||
diff --git a/modules/pam_namespace/namespace.conf.5.xml b/modules/pam_namespace/namespace.conf.5.xml
|
||
index a94b49e2..67f8c043 100644
|
||
--- a/modules/pam_namespace/namespace.conf.5.xml
|
||
+++ b/modules/pam_namespace/namespace.conf.5.xml
|
||
@@ -30,13 +30,29 @@
|
||
directory path and the instance directory path as its arguments.
|
||
</para>
|
||
|
||
- <para>
|
||
+ <para condition="without_vendordir">
|
||
The <filename>/etc/security/namespace.conf</filename> file specifies
|
||
which directories are polyinstantiated, how they are polyinstantiated,
|
||
how instance directories would be named, and any users for whom
|
||
polyinstantiation would not be performed.
|
||
</para>
|
||
|
||
+ <para condition="with_vendordir">
|
||
+ The <filename>/etc/security/namespace.conf</filename> file
|
||
+ ( or <filename>%vendordir%/security/namespace.conf</filename> if it does
|
||
+ not exist) specifies which directories are polyinstantiated, how they are
|
||
+ polyinstantiated, how instance directories would be named, and any users
|
||
+ for whom polyinstantiation would not be performed.
|
||
+ Then individual <filename>*.conf</filename> files from the
|
||
+ <filename>/etc/security/namespace.d/</filename> and
|
||
+ <filename>%vendordir%/security/namespace.d</filename> directories are taken too.
|
||
+ If <filename>/etc/security/namespace.d/@filename@.conf</filename> exists, then
|
||
+ <filename>%vendordir%/security/namespace.d/@filename@.conf</filename> will not be used.
|
||
+ All <filename>namespace.d/*.conf</filename> files are sorted by their
|
||
+ <filename>@filename@.conf</filename> in lexicographic order regardless of which
|
||
+ of the directories they reside in.
|
||
+ </para>
|
||
+
|
||
<para>
|
||
When someone logs in, the file <filename>namespace.conf</filename> is
|
||
scanned. Comments are marked by <emphasis>#</emphasis> characters.
|
||
diff --git a/modules/pam_namespace/pam_namespace.8.xml b/modules/pam_namespace/pam_namespace.8.xml
|
||
index 57c44c4b..ddaa00b4 100644
|
||
--- a/modules/pam_namespace/pam_namespace.8.xml
|
||
+++ b/modules/pam_namespace/pam_namespace.8.xml
|
||
@@ -74,6 +74,12 @@
|
||
and the user name as its arguments.
|
||
</para>
|
||
|
||
+ <para condition="with_vendordir">
|
||
+ If <filename>/etc/security/namespace.init</filename> does not exist,
|
||
+ <filename>%vendordir%/security/namespace.init</filename> is the
|
||
+ alternative to be used for it.
|
||
+ </para>
|
||
+
|
||
<para>
|
||
The pam_namespace module disassociates the session namespace from
|
||
the parent namespace. Any mounts/unmounts performed in the parent
|
||
@@ -313,6 +319,14 @@
|
||
</listitem>
|
||
</varlistentry>
|
||
|
||
+ <varlistentry condition="with_vendordir">
|
||
+ <term><filename>%vendordir%/security/namespace.conf</filename></term>
|
||
+ <listitem>
|
||
+ <para>Default configuration file if
|
||
+ <filename>/etc/security/namespace.conf</filename> does not exist.</para>
|
||
+ </listitem>
|
||
+ </varlistentry>
|
||
+
|
||
<varlistentry>
|
||
<term><filename>/etc/security/namespace.d</filename></term>
|
||
<listitem>
|
||
@@ -320,12 +334,28 @@
|
||
</listitem>
|
||
</varlistentry>
|
||
|
||
+ <varlistentry condition="with_vendordir">
|
||
+ <term><filename>%vendordir%/security/namespace.d</filename></term>
|
||
+ <listitem>
|
||
+ <para>Directory for additional vendor specific configuration files.</para>
|
||
+ </listitem>
|
||
+ </varlistentry>
|
||
+
|
||
<varlistentry>
|
||
<term><filename>/etc/security/namespace.init</filename></term>
|
||
<listitem>
|
||
<para>Init script for instance directories</para>
|
||
</listitem>
|
||
</varlistentry>
|
||
+
|
||
+ <varlistentry condition="with_vendordir">
|
||
+ <term><filename>%vendordir%/security/namespace.init</filename></term>
|
||
+ <listitem>
|
||
+ <para>Vendor init script for instance directories if
|
||
+ /etc/security/namespace.init does not exist.
|
||
+ </para>
|
||
+ </listitem>
|
||
+ </varlistentry>
|
||
</variablelist>
|
||
</refsect1>
|
||
|
||
diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c
|
||
index 4d4188d0..f34ce934 100644
|
||
--- a/modules/pam_namespace/pam_namespace.c
|
||
+++ b/modules/pam_namespace/pam_namespace.c
|
||
@@ -39,6 +39,94 @@
|
||
#include "pam_namespace.h"
|
||
#include "argv_parse.h"
|
||
|
||
+/* --- evaluting all files in VENDORDIR/security/namespace.d and /etc/security/namespace.d --- */
|
||
+static const char *base_name(const char *path)
|
||
+{
|
||
+ const char *base = strrchr(path, '/');
|
||
+ return base ? base+1 : path;
|
||
+}
|
||
+
|
||
+static int
|
||
+compare_filename(const void *a, const void *b)
|
||
+{
|
||
+ return strcmp(base_name(* (char * const *) a),
|
||
+ base_name(* (char * const *) b));
|
||
+}
|
||
+
|
||
+/* Evaluating a list of files which have to be parsed in the right order:
|
||
+ *
|
||
+ * - If etc/security/namespace.d/@filename@.conf exists, then
|
||
+ * %vendordir%/security/namespace.d/@filename@.conf should not be used.
|
||
+ * - All files in both namespace.d directories are sorted by their @filename@.conf in
|
||
+ * lexicographic order regardless of which of the directories they reside in. */
|
||
+static char **read_namespace_dir(struct instance_data *idata)
|
||
+{
|
||
+ glob_t globbuf;
|
||
+ size_t i=0;
|
||
+ int glob_rv = glob(NAMESPACE_D_GLOB, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf);
|
||
+ char **file_list;
|
||
+ size_t file_list_size = glob_rv == 0 ? globbuf.gl_pathc : 0;
|
||
+
|
||
+#ifdef VENDOR_NAMESPACE_D_GLOB
|
||
+ glob_t globbuf_vendor;
|
||
+ int glob_rv_vendor = glob(VENDOR_NAMESPACE_D_GLOB, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf_vendor);
|
||
+ if (glob_rv_vendor == 0)
|
||
+ file_list_size += globbuf_vendor.gl_pathc;
|
||
+#endif
|
||
+ file_list = malloc((file_list_size + 1) * sizeof(char*));
|
||
+ if (file_list == NULL) {
|
||
+ pam_syslog(idata->pamh, LOG_ERR, "Cannot allocate memory for file list: %m");
|
||
+#ifdef VENDOR_NAMESPACE_D_GLOB
|
||
+ if (glob_rv_vendor == 0)
|
||
+ globfree(&globbuf_vendor);
|
||
+#endif
|
||
+ if (glob_rv == 0)
|
||
+ globfree(&globbuf);
|
||
+ return NULL;
|
||
+ }
|
||
+
|
||
+ if (glob_rv == 0) {
|
||
+ for (i = 0; i < globbuf.gl_pathc; i++) {
|
||
+ file_list[i] = strdup(globbuf.gl_pathv[i]);
|
||
+ if (file_list[i] == NULL) {
|
||
+ pam_syslog(idata->pamh, LOG_ERR, "strdup failed: %m");
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+#ifdef VENDOR_NAMESPACE_D_GLOB
|
||
+ if (glob_rv_vendor == 0) {
|
||
+ for (size_t j = 0; j < globbuf_vendor.gl_pathc; j++) {
|
||
+ if (glob_rv == 0 && globbuf.gl_pathc > 0) {
|
||
+ int double_found = 0;
|
||
+ for (size_t k = 0; k < globbuf.gl_pathc; k++) {
|
||
+ if (strcmp(base_name(globbuf.gl_pathv[k]),
|
||
+ base_name(globbuf_vendor.gl_pathv[j])) == 0) {
|
||
+ double_found = 1;
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ if (double_found)
|
||
+ continue;
|
||
+ }
|
||
+ file_list[i] = strdup(globbuf_vendor.gl_pathv[j]);
|
||
+ if (file_list[i] == NULL) {
|
||
+ pam_syslog(idata->pamh, LOG_ERR, "strdup failed: %m");
|
||
+ break;
|
||
+ }
|
||
+ i++;
|
||
+ }
|
||
+ globfree(&globbuf_vendor);
|
||
+ }
|
||
+#endif
|
||
+ file_list[i] = NULL;
|
||
+ qsort(file_list, i, sizeof(char *), compare_filename);
|
||
+ if (glob_rv == 0)
|
||
+ globfree(&globbuf);
|
||
+
|
||
+ return file_list;
|
||
+}
|
||
+
|
||
/*
|
||
* Adds an entry for a polyinstantiated directory to the linked list of
|
||
* polyinstantiated directories. It is called from process_line() while
|
||
@@ -624,8 +712,6 @@ static int parse_config_file(struct instance_data *idata)
|
||
char *line;
|
||
int retval;
|
||
size_t len = 0;
|
||
- glob_t globbuf;
|
||
- const char *oldlocale;
|
||
size_t n;
|
||
|
||
/*
|
||
@@ -664,13 +750,16 @@ static int parse_config_file(struct instance_data *idata)
|
||
* process_line to process each line.
|
||
*/
|
||
|
||
- memset(&globbuf, '\0', sizeof(globbuf));
|
||
- oldlocale = setlocale(LC_COLLATE, "C");
|
||
- glob(NAMESPACE_D_GLOB, 0, NULL, &globbuf);
|
||
- if (oldlocale != NULL)
|
||
- setlocale(LC_COLLATE, oldlocale);
|
||
-
|
||
confname = PAM_NAMESPACE_CONFIG;
|
||
+#ifdef VENDOR_PAM_NAMESPACE_CONFIG
|
||
+ /* Check whether PAM_NAMESPACE_CONFIG file is available.
|
||
+ * If it does not exist, fall back to VENDOR_PAM_NAMESPACE_CONFIG file. */
|
||
+ struct stat buffer;
|
||
+ if (stat(confname, &buffer) != 0 && errno == ENOENT) {
|
||
+ confname = VENDOR_PAM_NAMESPACE_CONFIG;
|
||
+ }
|
||
+#endif
|
||
+ char **filename_list = read_namespace_dir(idata);
|
||
n = 0;
|
||
for (;;) {
|
||
if (idata->flags & PAMNS_DEBUG)
|
||
@@ -680,7 +769,6 @@ static int parse_config_file(struct instance_data *idata)
|
||
if (fil == NULL) {
|
||
pam_syslog(idata->pamh, LOG_ERR, "Error opening config file %s",
|
||
confname);
|
||
- globfree(&globbuf);
|
||
free(rhome);
|
||
free(home);
|
||
return PAM_SERVICE_ERR;
|
||
@@ -698,7 +786,6 @@ static int parse_config_file(struct instance_data *idata)
|
||
"Error processing conf file %s line %s", confname, line);
|
||
fclose(fil);
|
||
free(line);
|
||
- globfree(&globbuf);
|
||
free(rhome);
|
||
free(home);
|
||
return PAM_SERVICE_ERR;
|
||
@@ -707,14 +794,18 @@ static int parse_config_file(struct instance_data *idata)
|
||
fclose(fil);
|
||
free(line);
|
||
|
||
- if (n >= globbuf.gl_pathc)
|
||
+ if (filename_list == NULL || filename_list[n] == NULL)
|
||
break;
|
||
|
||
- confname = globbuf.gl_pathv[n];
|
||
- n++;
|
||
+ confname = filename_list[n++];
|
||
+ }
|
||
+
|
||
+ if (filename_list != NULL) {
|
||
+ for (size_t i = 0; filename_list[i] != NULL; i++)
|
||
+ free(filename_list[i]);
|
||
+ free(filename_list);
|
||
}
|
||
|
||
- globfree(&globbuf);
|
||
free(rhome);
|
||
free(home);
|
||
|
||
@@ -1250,16 +1341,17 @@ static int inst_init(const struct polydir_s *polyptr, const char *ipath,
|
||
struct instance_data *idata, int newdir)
|
||
{
|
||
pid_t rc, pid;
|
||
- struct sigaction newsa, oldsa;
|
||
int status;
|
||
const char *init_script = NAMESPACE_INIT_SCRIPT;
|
||
|
||
- memset(&newsa, '\0', sizeof(newsa));
|
||
- newsa.sa_handler = SIG_DFL;
|
||
- if (sigaction(SIGCHLD, &newsa, &oldsa) == -1) {
|
||
- pam_syslog(idata->pamh, LOG_ERR, "Cannot set signal value");
|
||
- return PAM_SESSION_ERR;
|
||
+#ifdef VENDOR_NAMESPACE_INIT_SCRIPT
|
||
+ /* Check whether NAMESPACE_INIT_SCRIPT file is available.
|
||
+ * If it does not exist, fall back to VENDOR_NAMESPACE_INIT_SCRIPT file. */
|
||
+ struct stat buffer;
|
||
+ if (stat(init_script, &buffer) != 0 && errno == ENOENT) {
|
||
+ init_script = VENDOR_NAMESPACE_INIT_SCRIPT;
|
||
}
|
||
+#endif
|
||
|
||
if ((polyptr->flags & POLYDIR_ISCRIPT) && polyptr->init_script)
|
||
init_script = polyptr->init_script;
|
||
@@ -1269,9 +1361,17 @@ static int inst_init(const struct polydir_s *polyptr, const char *ipath,
|
||
if (idata->flags & PAMNS_DEBUG)
|
||
pam_syslog(idata->pamh, LOG_ERR,
|
||
"Namespace init script not executable");
|
||
- rc = PAM_SESSION_ERR;
|
||
- goto out;
|
||
+ return PAM_SESSION_ERR;
|
||
} else {
|
||
+ struct sigaction newsa, oldsa;
|
||
+
|
||
+ memset(&newsa, '\0', sizeof(newsa));
|
||
+ newsa.sa_handler = SIG_DFL;
|
||
+ if (sigaction(SIGCHLD, &newsa, &oldsa) == -1) {
|
||
+ pam_syslog(idata->pamh, LOG_ERR, "failed to reset SIGCHLD handler");
|
||
+ return PAM_SESSION_ERR;
|
||
+ }
|
||
+
|
||
pid = fork();
|
||
if (pid == 0) {
|
||
static char *envp[] = { NULL };
|
||
@@ -1309,13 +1409,13 @@ static int inst_init(const struct polydir_s *polyptr, const char *ipath,
|
||
rc = PAM_SESSION_ERR;
|
||
goto out;
|
||
}
|
||
+ rc = PAM_SUCCESS;
|
||
+out:
|
||
+ (void) sigaction(SIGCHLD, &oldsa, NULL);
|
||
+ return rc;
|
||
}
|
||
}
|
||
- rc = PAM_SUCCESS;
|
||
-out:
|
||
- (void) sigaction(SIGCHLD, &oldsa, NULL);
|
||
-
|
||
- return rc;
|
||
+ return PAM_SUCCESS;
|
||
}
|
||
|
||
static int create_polydir(struct polydir_s *polyptr,
|
||
diff --git a/modules/pam_namespace/pam_namespace.h b/modules/pam_namespace/pam_namespace.h
|
||
index b51f2841..0b974ea7 100644
|
||
--- a/modules/pam_namespace/pam_namespace.h
|
||
+++ b/modules/pam_namespace/pam_namespace.h
|
||
@@ -90,15 +90,17 @@
|
||
/*
|
||
* Module defines
|
||
*/
|
||
-#ifndef SECURECONF_DIR
|
||
-#define SECURECONF_DIR "/etc/security/"
|
||
+#define PAM_NAMESPACE_CONFIG (SCONFIGDIR "/namespace.conf")
|
||
+#define NAMESPACE_INIT_SCRIPT (SCONFIGDIR "/namespace.init")
|
||
+#define NAMESPACE_D_DIR (SCONFIGDIR "/namespace.d/")
|
||
+#define NAMESPACE_D_GLOB (SCONFIGDIR "/namespace.d/*.conf")
|
||
+#ifdef VENDOR_SCONFIGDIR
|
||
+#define VENDOR_NAMESPACE_INIT_SCRIPT (VENDOR_SCONFIGDIR "/namespace.init")
|
||
+#define VENDOR_PAM_NAMESPACE_CONFIG (VENDOR_SCONFIGDIR "/namespace.conf")
|
||
+#define VENDOR_NAMESPACE_D_DIR (VENDOR_SCONFIGDIR "/namespace.d/")
|
||
+#define VENDOR_NAMESPACE_D_GLOB (VENDOR_SCONFIGDIR "/namespace.d/*.conf")
|
||
#endif
|
||
|
||
-#define PAM_NAMESPACE_CONFIG (SECURECONF_DIR "namespace.conf")
|
||
-#define NAMESPACE_INIT_SCRIPT (SECURECONF_DIR "namespace.init")
|
||
-#define NAMESPACE_D_DIR (SECURECONF_DIR "namespace.d/")
|
||
-#define NAMESPACE_D_GLOB (SECURECONF_DIR "namespace.d/*.conf")
|
||
-
|
||
/* module flags */
|
||
#define PAMNS_DEBUG 0x00000100 /* Running in debug mode */
|
||
#define PAMNS_SELINUX_ENABLED 0x00000400 /* SELinux is enabled */
|
||
diff --git a/modules/pam_nologin/pam_nologin.c b/modules/pam_nologin/pam_nologin.c
|
||
index b7f9bab0..d7f83e0c 100644
|
||
--- a/modules/pam_nologin/pam_nologin.c
|
||
+++ b/modules/pam_nologin/pam_nologin.c
|
||
@@ -79,7 +79,6 @@ static int perform_check(pam_handle_t *pamh, struct opt_s *opts)
|
||
|
||
if (fd >= 0) {
|
||
|
||
- char *mtmp=NULL;
|
||
int msg_style = PAM_TEXT_INFO;
|
||
struct passwd *user_pwd;
|
||
struct stat st;
|
||
@@ -99,21 +98,25 @@ static int perform_check(pam_handle_t *pamh, struct opt_s *opts)
|
||
goto clean_up_fd;
|
||
}
|
||
|
||
- mtmp = malloc(st.st_size+1);
|
||
- if (!mtmp) {
|
||
- pam_syslog(pamh, LOG_CRIT, "out of memory");
|
||
- retval = PAM_BUF_ERR;
|
||
- goto clean_up_fd;
|
||
- }
|
||
-
|
||
- if (pam_modutil_read(fd, mtmp, st.st_size) == st.st_size) {
|
||
- mtmp[st.st_size] = '\0';
|
||
- (void) pam_prompt (pamh, msg_style, NULL, "%s", mtmp);
|
||
+ /* Don't print anything if the message is empty, will only
|
||
+ disturb the output with empty lines */
|
||
+ if (st.st_size > 0) {
|
||
+ char *mtmp = malloc(st.st_size+1);
|
||
+ if (!mtmp) {
|
||
+ pam_syslog(pamh, LOG_CRIT, "out of memory");
|
||
+ retval = PAM_BUF_ERR;
|
||
+ goto clean_up_fd;
|
||
+ }
|
||
+
|
||
+ if (pam_modutil_read(fd, mtmp, st.st_size) == st.st_size) {
|
||
+ mtmp[st.st_size] = '\0';
|
||
+ (void) pam_prompt (pamh, msg_style, NULL, "%s", mtmp);
|
||
+ }
|
||
+ else
|
||
+ retval = PAM_SYSTEM_ERR;
|
||
+
|
||
+ free(mtmp);
|
||
}
|
||
- else
|
||
- retval = PAM_SYSTEM_ERR;
|
||
-
|
||
- free(mtmp);
|
||
|
||
clean_up_fd:
|
||
|
||
diff --git a/modules/pam_pwhistory/Makefile.am b/modules/pam_pwhistory/Makefile.am
|
||
index 8a4dbcb2..c29a8e11 100644
|
||
--- a/modules/pam_pwhistory/Makefile.am
|
||
+++ b/modules/pam_pwhistory/Makefile.am
|
||
@@ -26,12 +27,14 @@ if HAVE_VERSIONING
|
||
pam_pwhistory_la_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
|
||
endif
|
||
|
||
-noinst_HEADERS = opasswd.h
|
||
+noinst_HEADERS = opasswd.h pwhistory_config.h
|
||
+
|
||
+dist_secureconf_DATA = pwhistory.conf
|
||
|
||
securelib_LTLIBRARIES = pam_pwhistory.la
|
||
pam_pwhistory_la_CFLAGS = $(AM_CFLAGS)
|
||
pam_pwhistory_la_LIBADD = $(top_builddir)/libpam/libpam.la @LIBCRYPT@ @LIBSELINUX@
|
||
-pam_pwhistory_la_SOURCES = pam_pwhistory.c opasswd.c
|
||
+pam_pwhistory_la_SOURCES = pam_pwhistory.c opasswd.c pwhistory_config.c
|
||
|
||
sbin_PROGRAMS = pwhistory_helper
|
||
pwhistory_helper_CFLAGS = $(AM_CFLAGS) -DHELPER_COMPILE=\"pwhistory_helper\" @EXE_CFLAGS@
|
||
diff --git a/modules/pam_pwhistory/opasswd.c b/modules/pam_pwhistory/opasswd.c
|
||
index a6cd3d2a..1d3242ca 100644
|
||
--- a/modules/pam_pwhistory/opasswd.c
|
||
+++ b/modules/pam_pwhistory/opasswd.c
|
||
@@ -44,6 +44,7 @@
|
||
#include <ctype.h>
|
||
#include <errno.h>
|
||
#include <fcntl.h>
|
||
+#include <limits.h>
|
||
#include <stdio.h>
|
||
#include <unistd.h>
|
||
#include <string.h>
|
||
@@ -74,8 +75,7 @@
|
||
#define RANDOM_DEVICE "/dev/urandom"
|
||
#endif
|
||
|
||
-#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
|
||
-#define TMP_PASSWORDS_FILE OLD_PASSWORDS_FILE".tmpXXXXXX"
|
||
+#define DEFAULT_OLD_PASSWORDS_FILE SCONFIGDIR "/opasswd"
|
||
|
||
#define DEFAULT_BUFLEN 4096
|
||
|
||
@@ -142,7 +142,7 @@ compare_password(const char *newpass, const char *oldpass)
|
||
|
||
/* Check, if the new password is already in the opasswd file. */
|
||
PAMH_ARG_DECL(int
|
||
-check_old_pass, const char *user, const char *newpass, int debug)
|
||
+check_old_pass, const char *user, const char *newpass, const char *filename, int debug)
|
||
{
|
||
int retval = PAM_SUCCESS;
|
||
FILE *oldpf;
|
||
@@ -156,10 +156,13 @@ check_old_pass, const char *user, const char *newpass, int debug)
|
||
return PAM_PWHISTORY_RUN_HELPER;
|
||
#endif
|
||
|
||
- if ((oldpf = fopen (OLD_PASSWORDS_FILE, "r")) == NULL)
|
||
+ const char *opasswd_file =
|
||
+ (filename != NULL ? filename : DEFAULT_OLD_PASSWORDS_FILE);
|
||
+
|
||
+ if ((oldpf = fopen (opasswd_file, "r")) == NULL)
|
||
{
|
||
if (errno != ENOENT)
|
||
- pam_syslog (pamh, LOG_ERR, "Cannot open %s: %m", OLD_PASSWORDS_FILE);
|
||
+ pam_syslog (pamh, LOG_ERR, "Cannot open %s: %m", opasswd_file);
|
||
return PAM_SUCCESS;
|
||
}
|
||
|
||
@@ -242,9 +245,8 @@ check_old_pass, const char *user, const char *newpass, int debug)
|
||
}
|
||
|
||
PAMH_ARG_DECL(int
|
||
-save_old_pass, const char *user, int howmany, int debug UNUSED)
|
||
+save_old_pass, const char *user, int howmany, const char *filename, int debug UNUSED)
|
||
{
|
||
- char opasswd_tmp[] = TMP_PASSWORDS_FILE;
|
||
struct stat opasswd_stat;
|
||
FILE *oldpf, *newpf;
|
||
int newpf_fd;
|
||
@@ -256,6 +258,15 @@ save_old_pass, const char *user, int howmany, int debug UNUSED)
|
||
struct passwd *pwd;
|
||
const char *oldpass;
|
||
|
||
+ /* Define opasswd file and temp file for opasswd */
|
||
+ const char *opasswd_file =
|
||
+ (filename != NULL ? filename : DEFAULT_OLD_PASSWORDS_FILE);
|
||
+ char opasswd_tmp[PATH_MAX];
|
||
+
|
||
+ if ((size_t) snprintf (opasswd_tmp, sizeof (opasswd_tmp), "%s.tmpXXXXXX",
|
||
+ opasswd_file) >= sizeof (opasswd_tmp))
|
||
+ return PAM_BUF_ERR;
|
||
+
|
||
pwd = pam_modutil_getpwnam (pamh, user);
|
||
if (pwd == NULL)
|
||
return PAM_USER_UNKNOWN;
|
||
@@ -285,24 +296,22 @@ save_old_pass, const char *user, int howmany, int debug UNUSED)
|
||
if (oldpass == NULL || *oldpass == '\0')
|
||
return PAM_SUCCESS;
|
||
|
||
- if ((oldpf = fopen (OLD_PASSWORDS_FILE, "r")) == NULL)
|
||
+ if ((oldpf = fopen (opasswd_file, "r")) == NULL)
|
||
{
|
||
if (errno == ENOENT)
|
||
{
|
||
- pam_syslog (pamh, LOG_NOTICE, "Creating %s",
|
||
- OLD_PASSWORDS_FILE);
|
||
+ pam_syslog (pamh, LOG_NOTICE, "Creating %s", opasswd_file);
|
||
do_create = 1;
|
||
}
|
||
else
|
||
{
|
||
- pam_syslog (pamh, LOG_ERR, "Cannot open %s: %m",
|
||
- OLD_PASSWORDS_FILE);
|
||
+ pam_syslog (pamh, LOG_ERR, "Cannot open %s: %m", opasswd_file);
|
||
return PAM_AUTHTOK_ERR;
|
||
}
|
||
}
|
||
else if (fstat (fileno (oldpf), &opasswd_stat) < 0)
|
||
{
|
||
- pam_syslog (pamh, LOG_ERR, "Cannot stat %s: %m", OLD_PASSWORDS_FILE);
|
||
+ pam_syslog (pamh, LOG_ERR, "Cannot stat %s: %m", opasswd_file);
|
||
fclose (oldpf);
|
||
return PAM_AUTHTOK_ERR;
|
||
}
|
||
@@ -312,7 +321,7 @@ save_old_pass, const char *user, int howmany, int debug UNUSED)
|
||
if (newpf_fd == -1)
|
||
{
|
||
pam_syslog (pamh, LOG_ERR, "Cannot create %s temp file: %m",
|
||
- OLD_PASSWORDS_FILE);
|
||
+ opasswd_file);
|
||
if (oldpf)
|
||
fclose (oldpf);
|
||
return PAM_AUTHTOK_ERR;
|
||
@@ -321,23 +330,19 @@ save_old_pass, const char *user, int howmany, int debug UNUSED)
|
||
{
|
||
if (fchmod (newpf_fd, S_IRUSR|S_IWUSR) != 0)
|
||
pam_syslog (pamh, LOG_ERR,
|
||
- "Cannot set permissions of %s temp file: %m",
|
||
- OLD_PASSWORDS_FILE);
|
||
+ "Cannot set permissions of %s temp file: %m", opasswd_file);
|
||
if (fchown (newpf_fd, 0, 0) != 0)
|
||
pam_syslog (pamh, LOG_ERR,
|
||
- "Cannot set owner/group of %s temp file: %m",
|
||
- OLD_PASSWORDS_FILE);
|
||
+ "Cannot set owner/group of %s temp file: %m", opasswd_file);
|
||
}
|
||
else
|
||
{
|
||
if (fchmod (newpf_fd, opasswd_stat.st_mode) != 0)
|
||
pam_syslog (pamh, LOG_ERR,
|
||
- "Cannot set permissions of %s temp file: %m",
|
||
- OLD_PASSWORDS_FILE);
|
||
+ "Cannot set permissions of %s temp file: %m", opasswd_file);
|
||
if (fchown (newpf_fd, opasswd_stat.st_uid, opasswd_stat.st_gid) != 0)
|
||
pam_syslog (pamh, LOG_ERR,
|
||
- "Cannot set owner/group of %s temp file: %m",
|
||
- OLD_PASSWORDS_FILE);
|
||
+ "Cannot set owner/group of %s temp file: %m", opasswd_file);
|
||
}
|
||
newpf = fdopen (newpf_fd, "w+");
|
||
if (newpf == NULL)
|
||
@@ -550,12 +555,20 @@ save_old_pass, const char *user, int howmany, int debug UNUSED)
|
||
goto error_opasswd;
|
||
}
|
||
|
||
- unlink (OLD_PASSWORDS_FILE".old");
|
||
- if (link (OLD_PASSWORDS_FILE, OLD_PASSWORDS_FILE".old") != 0 &&
|
||
+ char opasswd_backup[PATH_MAX];
|
||
+ if ((size_t) snprintf (opasswd_backup, sizeof (opasswd_backup), "%s.old",
|
||
+ opasswd_file) >= sizeof (opasswd_backup))
|
||
+ {
|
||
+ retval = PAM_BUF_ERR;
|
||
+ goto error_opasswd;
|
||
+ }
|
||
+
|
||
+ unlink (opasswd_backup);
|
||
+ if (link (opasswd_file, opasswd_backup) != 0 &&
|
||
errno != ENOENT)
|
||
pam_syslog (pamh, LOG_ERR, "Cannot create backup file of %s: %m",
|
||
- OLD_PASSWORDS_FILE);
|
||
- rename (opasswd_tmp, OLD_PASSWORDS_FILE);
|
||
+ opasswd_file);
|
||
+ rename (opasswd_tmp, opasswd_file);
|
||
error_opasswd:
|
||
unlink (opasswd_tmp);
|
||
free (buf);
|
||
diff --git a/modules/pam_pwhistory/opasswd.h b/modules/pam_pwhistory/opasswd.h
|
||
index 3f257288..19a4062c 100644
|
||
--- a/modules/pam_pwhistory/opasswd.h
|
||
+++ b/modules/pam_pwhistory/opasswd.h
|
||
@@ -57,10 +57,10 @@ void
|
||
helper_log_err(int err, const char *format, ...);
|
||
#endif
|
||
|
||
-PAMH_ARG_DECL(int
|
||
-check_old_pass, const char *user, const char *newpass, int debug);
|
||
+PAMH_ARG_DECL(int check_old_pass, const char *user, const char *newpass,
|
||
+ const char *filename, int debug);
|
||
|
||
-PAMH_ARG_DECL(int
|
||
-save_old_pass, const char *user, int howmany, int debug);
|
||
+PAMH_ARG_DECL(int save_old_pass, const char *user, int howmany,
|
||
+ const char *filename, int debug);
|
||
|
||
#endif /* __OPASSWD_H__ */
|
||
diff --git a/modules/pam_pwhistory/pam_pwhistory.c b/modules/pam_pwhistory/pam_pwhistory.c
|
||
index ce2c21f5..5a7fb811 100644
|
||
--- a/modules/pam_pwhistory/pam_pwhistory.c
|
||
+++ b/modules/pam_pwhistory/pam_pwhistory.c
|
||
@@ -63,14 +63,8 @@
|
||
|
||
#include "opasswd.h"
|
||
#include "pam_inline.h"
|
||
+#include "pwhistory_config.h"
|
||
|
||
-struct options_t {
|
||
- int debug;
|
||
- int enforce_for_root;
|
||
- int remember;
|
||
- int tries;
|
||
-};
|
||
-typedef struct options_t options_t;
|
||
|
||
|
||
static void
|
||
@@ -104,13 +98,23 @@ parse_option (pam_handle_t *pamh, const char *argv, options_t *options)
|
||
options->enforce_for_root = 1;
|
||
else if (pam_str_skip_icase_prefix(argv, "authtok_type=") != NULL)
|
||
{ /* ignore, for pam_get_authtok */; }
|
||
+ else if ((str = pam_str_skip_icase_prefix(argv, "file=")) != NULL)
|
||
+ {
|
||
+ if (*str != '/')
|
||
+ {
|
||
+ pam_syslog (pamh, LOG_ERR,
|
||
+ "pam_pwhistory: file path should be absolute: %s", argv);
|
||
+ }
|
||
+ else
|
||
+ options->filename = str;
|
||
+ }
|
||
else
|
||
pam_syslog (pamh, LOG_ERR, "pam_pwhistory: unknown option: %s", argv);
|
||
}
|
||
|
||
static int
|
||
run_save_helper(pam_handle_t *pamh, const char *user,
|
||
- int howmany, int debug)
|
||
+ int howmany, const char *filename, int debug)
|
||
{
|
||
int retval, child;
|
||
struct sigaction newsa, oldsa;
|
||
@@ -123,7 +127,7 @@ run_save_helper(pam_handle_t *pamh, const char *user,
|
||
if (child == 0)
|
||
{
|
||
static char *envp[] = { NULL };
|
||
- char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
|
||
+ char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
|
||
|
||
if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_PIPE_FD,
|
||
PAM_MODUTIL_PIPE_FD,
|
||
@@ -137,9 +141,10 @@ run_save_helper(pam_handle_t *pamh, const char *user,
|
||
args[0] = (char *)PWHISTORY_HELPER;
|
||
args[1] = (char *)"save";
|
||
args[2] = (char *)user;
|
||
+ args[3] = (char *)filename;
|
||
DIAG_POP_IGNORE_CAST_QUAL;
|
||
- if (asprintf(&args[3], "%d", howmany) < 0 ||
|
||
- asprintf(&args[4], "%d", debug) < 0)
|
||
+ if (asprintf(&args[4], "%d", howmany) < 0 ||
|
||
+ asprintf(&args[5], "%d", debug) < 0)
|
||
{
|
||
pam_syslog(pamh, LOG_ERR, "asprintf: %m");
|
||
_exit(PAM_SYSTEM_ERR);
|
||
@@ -185,7 +190,7 @@ run_save_helper(pam_handle_t *pamh, const char *user,
|
||
|
||
static int
|
||
run_check_helper(pam_handle_t *pamh, const char *user,
|
||
- const char *newpass, int debug)
|
||
+ const char *newpass, const char *filename, int debug)
|
||
{
|
||
int retval, child, fds[2];
|
||
struct sigaction newsa, oldsa;
|
||
@@ -202,7 +207,7 @@ run_check_helper(pam_handle_t *pamh, const char *user,
|
||
if (child == 0)
|
||
{
|
||
static char *envp[] = { NULL };
|
||
- char *args[] = { NULL, NULL, NULL, NULL, NULL };
|
||
+ char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
|
||
|
||
/* reopen stdin as pipe */
|
||
if (dup2(fds[0], STDIN_FILENO) != STDIN_FILENO)
|
||
@@ -223,8 +228,9 @@ run_check_helper(pam_handle_t *pamh, const char *user,
|
||
args[0] = (char *)PWHISTORY_HELPER;
|
||
args[1] = (char *)"check";
|
||
args[2] = (char *)user;
|
||
+ args[3] = (char *)filename;
|
||
DIAG_POP_IGNORE_CAST_QUAL;
|
||
- if (asprintf(&args[3], "%d", debug) < 0)
|
||
+ if (asprintf(&args[4], "%d", debug) < 0)
|
||
{
|
||
pam_syslog(pamh, LOG_ERR, "asprintf: %m");
|
||
_exit(PAM_SYSTEM_ERR);
|
||
@@ -299,6 +305,8 @@ pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
|
||
options.remember = 10;
|
||
options.tries = 1;
|
||
|
||
+ parse_config_file(pamh, argc, argv, &options);
|
||
+
|
||
/* Parse parameters for module */
|
||
for ( ; argc-- > 0; argv++)
|
||
parse_option (pamh, *argv, &options);
|
||
@@ -306,7 +314,6 @@ pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
|
||
if (options.debug)
|
||
pam_syslog (pamh, LOG_DEBUG, "pam_sm_chauthtok entered");
|
||
|
||
-
|
||
if (options.remember == 0)
|
||
return PAM_IGNORE;
|
||
|
||
@@ -323,10 +330,10 @@ pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
|
||
return PAM_SUCCESS;
|
||
}
|
||
|
||
- retval = save_old_pass (pamh, user, options.remember, options.debug);
|
||
+ retval = save_old_pass (pamh, user, options.remember, options.filename, options.debug);
|
||
|
||
if (retval == PAM_PWHISTORY_RUN_HELPER)
|
||
- retval = run_save_helper(pamh, user, options.remember, options.debug);
|
||
+ retval = run_save_helper(pamh, user, options.remember, options.filename, options.debug);
|
||
|
||
if (retval != PAM_SUCCESS)
|
||
return retval;
|
||
@@ -358,9 +365,9 @@ pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
|
||
if (options.debug)
|
||
pam_syslog (pamh, LOG_DEBUG, "check against old password file");
|
||
|
||
- retval = check_old_pass (pamh, user, newpass, options.debug);
|
||
+ retval = check_old_pass (pamh, user, newpass, options.filename, options.debug);
|
||
if (retval == PAM_PWHISTORY_RUN_HELPER)
|
||
- retval = run_check_helper(pamh, user, newpass, options.debug);
|
||
+ retval = run_check_helper(pamh, user, newpass, options.filename, options.debug);
|
||
|
||
if (retval != PAM_SUCCESS)
|
||
{
|
||
diff --git a/modules/pam_pwhistory/pwhistory.conf b/modules/pam_pwhistory/pwhistory.conf
|
||
new file mode 100644
|
||
index 00000000..070b7197
|
||
--- /dev/null
|
||
+++ b/modules/pam_pwhistory/pwhistory.conf
|
||
@@ -0,0 +1,21 @@
|
||
+# Configuration for remembering the last passwords used by a user.
|
||
+#
|
||
+# Enable the debugging logs.
|
||
+# Enabled if option is present.
|
||
+# debug
|
||
+#
|
||
+# root account's passwords are also remembered.
|
||
+# Enabled if option is present.
|
||
+# enforce_for_root
|
||
+#
|
||
+# Number of passwords to remember.
|
||
+# The default is 10.
|
||
+# remember = 10
|
||
+#
|
||
+# Number of times to prompt for the password.
|
||
+# The default is 1.
|
||
+# retry = 1
|
||
+#
|
||
+# The directory where the last passwords are kept.
|
||
+# The default is /etc/security/opasswd.
|
||
+# file = /etc/security/opasswd
|
||
diff --git a/modules/pam_pwhistory/pwhistory_config.c b/modules/pam_pwhistory/pwhistory_config.c
|
||
new file mode 100644
|
||
index 00000000..b21879c6
|
||
--- /dev/null
|
||
+++ b/modules/pam_pwhistory/pwhistory_config.c
|
||
@@ -0,0 +1,115 @@
|
||
+/*
|
||
+ * Copyright (c) 2022 Iker Pedrosa <ipedrosa@redhat.com>
|
||
+ *
|
||
+ * Redistribution and use in source and binary forms, with or without
|
||
+ * modification, are permitted provided that the following conditions
|
||
+ * are met:
|
||
+ * 1. Redistributions of source code must retain the above copyright
|
||
+ * notice, and the entire permission notice in its entirety,
|
||
+ * including the disclaimer of warranties.
|
||
+ * 2. Redistributions in binary form must reproduce the above copyright
|
||
+ * notice, this list of conditions and the following disclaimer in the
|
||
+ * documentation and/or other materials provided with the distribution.
|
||
+ * 3. The name of the author may not be used to endorse or promote
|
||
+ * products derived from this software without specific prior
|
||
+ * written permission.
|
||
+ *
|
||
+ * ALTERNATIVELY, this product may be distributed under the terms of
|
||
+ * the GNU Public License, in which case the provisions of the GPL are
|
||
+ * required INSTEAD OF the above restrictions. (This clause is
|
||
+ * necessary due to a potential bad interaction between the GPL and
|
||
+ * the restrictions contained in a BSD-style copyright.)
|
||
+ *
|
||
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
+ */
|
||
+
|
||
+#include "config.h"
|
||
+
|
||
+#include <stdio.h>
|
||
+#include <stdlib.h>
|
||
+#include <string.h>
|
||
+#include <syslog.h>
|
||
+
|
||
+#include <security/pam_modutil.h>
|
||
+
|
||
+#include "pam_inline.h"
|
||
+#include "pwhistory_config.h"
|
||
+
|
||
+#define PWHISTORY_DEFAULT_CONF SCONFIGDIR "/pwhistory.conf"
|
||
+
|
||
+void
|
||
+parse_config_file(pam_handle_t *pamh, int argc, const char **argv,
|
||
+ struct options_t *options)
|
||
+{
|
||
+ const char *fname = NULL;
|
||
+ int i;
|
||
+ char *val;
|
||
+
|
||
+ for (i = 0; i < argc; ++i) {
|
||
+ const char *str = pam_str_skip_prefix(argv[i], "conf=");
|
||
+
|
||
+ if (str != NULL) {
|
||
+ fname = str;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (fname == NULL) {
|
||
+ fname = PWHISTORY_DEFAULT_CONF;
|
||
+ }
|
||
+
|
||
+ val = pam_modutil_search_key (pamh, fname, "debug");
|
||
+ if (val != NULL) {
|
||
+ options->debug = 1;
|
||
+ free(val);
|
||
+ }
|
||
+
|
||
+ val = pam_modutil_search_key (pamh, fname, "enforce_for_root");
|
||
+ if (val != NULL) {
|
||
+ options->enforce_for_root = 1;
|
||
+ free(val);
|
||
+ }
|
||
+
|
||
+ val = pam_modutil_search_key (pamh, fname, "remember");
|
||
+ if (val != NULL) {
|
||
+ unsigned int temp;
|
||
+ if (sscanf(val, "%u", &temp) != 1) {
|
||
+ pam_syslog(pamh, LOG_ERR,
|
||
+ "Bad number supplied for remember argument");
|
||
+ } else {
|
||
+ options->remember = temp;
|
||
+ }
|
||
+ free(val);
|
||
+ }
|
||
+
|
||
+ val = pam_modutil_search_key (pamh, fname, "retry");
|
||
+ if (val != NULL) {
|
||
+ unsigned int temp;
|
||
+ if (sscanf(val, "%u", &temp) != 1) {
|
||
+ pam_syslog(pamh, LOG_ERR,
|
||
+ "Bad number supplied for retry argument");
|
||
+ } else {
|
||
+ options->tries = temp;
|
||
+ }
|
||
+ free(val);
|
||
+ }
|
||
+
|
||
+ val = pam_modutil_search_key (pamh, fname, "file");
|
||
+ if (val != NULL) {
|
||
+ if (*val != '/') {
|
||
+ pam_syslog (pamh, LOG_ERR,
|
||
+ "File path should be absolute: %s", val);
|
||
+ } else {
|
||
+ options->filename = val;
|
||
+ }
|
||
+ }
|
||
+}
|
||
diff --git a/modules/pam_pwhistory/pwhistory_config.h b/modules/pam_pwhistory/pwhistory_config.h
|
||
new file mode 100644
|
||
index 00000000..e2b3bc83
|
||
--- /dev/null
|
||
+++ b/modules/pam_pwhistory/pwhistory_config.h
|
||
@@ -0,0 +1,54 @@
|
||
+/*
|
||
+ * Copyright (c) 2022 Iker Pedrosa <ipedrosa@redhat.com>
|
||
+ *
|
||
+ * Redistribution and use in source and binary forms, with or without
|
||
+ * modification, are permitted provided that the following conditions
|
||
+ * are met:
|
||
+ * 1. Redistributions of source code must retain the above copyright
|
||
+ * notice, and the entire permission notice in its entirety,
|
||
+ * including the disclaimer of warranties.
|
||
+ * 2. Redistributions in binary form must reproduce the above copyright
|
||
+ * notice, this list of conditions and the following disclaimer in the
|
||
+ * documentation and/or other materials provided with the distribution.
|
||
+ * 3. The name of the author may not be used to endorse or promote
|
||
+ * products derived from this software without specific prior
|
||
+ * written permission.
|
||
+ *
|
||
+ * ALTERNATIVELY, this product may be distributed under the terms of
|
||
+ * the GNU Public License, in which case the provisions of the GPL are
|
||
+ * required INSTEAD OF the above restrictions. (This clause is
|
||
+ * necessary due to a potential bad interaction between the GPL and
|
||
+ * the restrictions contained in a BSD-style copyright.)
|
||
+ *
|
||
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
+ */
|
||
+
|
||
+#ifndef _PWHISTORY_CONFIG_H
|
||
+#define _PWHISTORY_CONFIG_H
|
||
+
|
||
+#include <security/pam_ext.h>
|
||
+
|
||
+struct options_t {
|
||
+ int debug;
|
||
+ int enforce_for_root;
|
||
+ int remember;
|
||
+ int tries;
|
||
+ const char *filename;
|
||
+};
|
||
+typedef struct options_t options_t;
|
||
+
|
||
+void
|
||
+parse_config_file(pam_handle_t *pamh, int argc, const char **argv,
|
||
+ struct options_t *options);
|
||
+
|
||
+#endif /* _PWHISTORY_CONFIG_H */
|
||
diff --git a/modules/pam_pwhistory/pwhistory_helper.c b/modules/pam_pwhistory/pwhistory_helper.c
|
||
index b08a14a7..7a61ae53 100644
|
||
--- a/modules/pam_pwhistory/pwhistory_helper.c
|
||
+++ b/modules/pam_pwhistory/pwhistory_helper.c
|
||
@@ -51,7 +51,7 @@
|
||
|
||
|
||
static int
|
||
-check_history(const char *user, const char *debug)
|
||
+check_history(const char *user, const char *filename, const char *debug)
|
||
{
|
||
char pass[PAM_MAX_RESP_SIZE + 1];
|
||
char *passwords[] = { pass };
|
||
@@ -68,7 +68,7 @@ check_history(const char *user, const char *debug)
|
||
return PAM_AUTHTOK_ERR;
|
||
}
|
||
|
||
- retval = check_old_pass(user, pass, dbg);
|
||
+ retval = check_old_pass(user, pass, filename, dbg);
|
||
|
||
memset(pass, '\0', PAM_MAX_RESP_SIZE); /* clear memory of the password */
|
||
|
||
@@ -76,13 +76,13 @@ check_history(const char *user, const char *debug)
|
||
}
|
||
|
||
static int
|
||
-save_history(const char *user, const char *howmany, const char *debug)
|
||
+save_history(const char *user, const char *filename, const char *howmany, const char *debug)
|
||
{
|
||
int num = atoi(howmany);
|
||
int dbg = atoi(debug); /* no need to be too fancy here */
|
||
int retval;
|
||
|
||
- retval = save_old_pass(user, num, dbg);
|
||
+ retval = save_old_pass(user, num, filename, dbg);
|
||
|
||
return retval;
|
||
}
|
||
@@ -92,13 +92,14 @@ main(int argc, char *argv[])
|
||
{
|
||
const char *option;
|
||
const char *user;
|
||
+ const char *filename;
|
||
|
||
/*
|
||
* we establish that this program is running with non-tty stdin.
|
||
* this is to discourage casual use.
|
||
*/
|
||
|
||
- if (isatty(STDIN_FILENO) || argc < 4)
|
||
+ if (isatty(STDIN_FILENO) || argc < 5)
|
||
{
|
||
fprintf(stderr,
|
||
"This binary is not designed for running in this way.\n");
|
||
@@ -107,11 +108,12 @@ main(int argc, char *argv[])
|
||
|
||
option = argv[1];
|
||
user = argv[2];
|
||
+ filename = argv[3];
|
||
|
||
- if (strcmp(option, "check") == 0 && argc == 4)
|
||
- return check_history(user, argv[3]);
|
||
- else if (strcmp(option, "save") == 0 && argc == 5)
|
||
- return save_history(user, argv[3], argv[4]);
|
||
+ if (strcmp(option, "check") == 0 && argc == 5)
|
||
+ return check_history(user, filename, argv[4]);
|
||
+ else if (strcmp(option, "save") == 0 && argc == 6)
|
||
+ return save_history(user, filename, argv[4], argv[5]);
|
||
|
||
fprintf(stderr, "This binary is not designed for running in this way.\n");
|
||
|
||
diff --git a/modules/pam_rootok/pam_rootok.c b/modules/pam_rootok/pam_rootok.c
|
||
index dd374c53..9bc15abf 100644
|
||
--- a/modules/pam_rootok/pam_rootok.c
|
||
+++ b/modules/pam_rootok/pam_rootok.c
|
||
@@ -53,11 +53,10 @@ static int
|
||
PAM_FORMAT((printf, 2, 3))
|
||
log_callback (int type UNUSED, const char *fmt, ...)
|
||
{
|
||
- int audit_fd;
|
||
va_list ap;
|
||
|
||
#ifdef HAVE_LIBAUDIT
|
||
- audit_fd = audit_open();
|
||
+ int audit_fd = audit_open();
|
||
|
||
if (audit_fd >= 0) {
|
||
char *buf;
|
||
diff --git a/modules/pam_sepermit/Makefile.am b/modules/pam_sepermit/Makefile.am
|
||
index 18a89b60..bed3b149 100644
|
||
--- a/modules/pam_sepermit/Makefile.am
|
||
+++ b/modules/pam_sepermit/Makefile.am
|
||
@@ -13,7 +13,7 @@ dist_man_MANS = pam_sepermit.8 sepermit.conf.5
|
||
endif
|
||
XMLS = README.xml pam_sepermit.8.xml sepermit.conf.5.xml
|
||
dist_check_SCRIPTS = tst-pam_sepermit
|
||
-TESTS = $(dist_check_SCRIPTS)
|
||
+TESTS = $(dist_check_SCRIPTS) $(check_PROGRAMS)
|
||
|
||
securelibdir = $(SECUREDIR)
|
||
secureconfdir = $(SCONFIGDIR)
|
||
@@ -21,7 +21,6 @@ sepermitlockdir = ${localstatedir}/run/sepermit
|
||
|
||
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
|
||
-I$(top_srcdir)/libpam_misc/include \
|
||
- -D SEPERMIT_CONF_FILE=\"$(SCONFIGDIR)/sepermit.conf\" \
|
||
-D SEPERMIT_LOCKDIR=\"$(sepermitlockdir)\" $(WARN_CFLAGS)
|
||
|
||
pam_sepermit_la_LIBADD = $(top_builddir)/libpam/libpam.la @LIBSELINUX@
|
||
@@ -33,6 +32,9 @@ endif
|
||
dist_secureconf_DATA = sepermit.conf
|
||
securelib_LTLIBRARIES = pam_sepermit.la
|
||
|
||
+check_PROGRAMS = tst-pam_sepermit-retval
|
||
+tst_pam_sepermit_retval_LDADD = $(top_builddir)/libpam/libpam.la
|
||
+
|
||
install-data-local:
|
||
mkdir -p $(DESTDIR)$(sepermitlockdir)
|
||
|
||
diff --git a/modules/pam_sepermit/pam_sepermit.8.xml b/modules/pam_sepermit/pam_sepermit.8.xml
|
||
index 30d9cc54..5763c346 100644
|
||
--- a/modules/pam_sepermit/pam_sepermit.8.xml
|
||
+++ b/modules/pam_sepermit/pam_sepermit.8.xml
|
||
@@ -54,7 +54,11 @@
|
||
<refentrytitle>sepermit.conf</refentrytitle><manvolnum>5</manvolnum>
|
||
</citerefentry> for details.
|
||
</para>
|
||
-
|
||
+ <para condition="with_vendordir">
|
||
+ If there is no explicitly specified configuration file and
|
||
+ <filename>/etc/security/sepermit.conf</filename> does not exist,
|
||
+ <filename>%vendordir%/security/sepermit.conf</filename> is used.
|
||
+ </para>
|
||
</refsect1>
|
||
|
||
<refsect1 id="pam_sepermit-options">
|
||
diff --git a/modules/pam_sepermit/pam_sepermit.c b/modules/pam_sepermit/pam_sepermit.c
|
||
index f7d98d5b..5fbc8fdd 100644
|
||
--- a/modules/pam_sepermit/pam_sepermit.c
|
||
+++ b/modules/pam_sepermit/pam_sepermit.c
|
||
@@ -61,6 +61,12 @@
|
||
|
||
#include <selinux/selinux.h>
|
||
|
||
+#include "pam_inline.h"
|
||
+
|
||
+#define SEPERMIT_CONF_FILE (SCONFIGDIR "/sepermit.conf")
|
||
+#ifdef VENDOR_SCONFIGDIR
|
||
+# define SEPERMIT_VENDOR_CONF_FILE (VENDOR_SCONFIGDIR "/sepermit.conf");
|
||
+#endif
|
||
#define MODULE "pam_sepermit"
|
||
#define OPT_DELIM ":"
|
||
|
||
@@ -370,16 +376,31 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
|
||
const char *user = NULL;
|
||
char *seuser = NULL;
|
||
char *level = NULL;
|
||
- const char *cfgfile = SEPERMIT_CONF_FILE;
|
||
+ const char *cfgfile = NULL;
|
||
|
||
/* Parse arguments. */
|
||
for (i = 0; i < argc; i++) {
|
||
+ const char *str;
|
||
+
|
||
if (strcmp(argv[i], "debug") == 0) {
|
||
debug = 1;
|
||
+ } else if ((str = pam_str_skip_prefix(argv[i], "conf=")) != NULL) {
|
||
+ cfgfile = str;
|
||
+ } else {
|
||
+ pam_syslog(pamh, LOG_ERR, "unknown option: %s", argv[i]);
|
||
}
|
||
- if (strcmp(argv[i], "conf=") == 0) {
|
||
- cfgfile = argv[i] + 5;
|
||
- }
|
||
+ }
|
||
+
|
||
+ if (cfgfile == NULL) {
|
||
+#ifdef SEPERMIT_VENDOR_CONF_FILE
|
||
+ struct stat buffer;
|
||
+
|
||
+ cfgfile = SEPERMIT_CONF_FILE;
|
||
+ if (stat(cfgfile, &buffer) != 0 && errno == ENOENT)
|
||
+ cfgfile = SEPERMIT_VENDOR_CONF_FILE;
|
||
+#else
|
||
+ cfgfile = SEPERMIT_CONF_FILE;
|
||
+#endif
|
||
}
|
||
|
||
if (debug)
|
||
diff --git a/modules/pam_sepermit/tst-pam_sepermit-retval.c b/modules/pam_sepermit/tst-pam_sepermit-retval.c
|
||
new file mode 100644
|
||
index 00000000..321bd6d1
|
||
--- /dev/null
|
||
+++ b/modules/pam_sepermit/tst-pam_sepermit-retval.c
|
||
@@ -0,0 +1,158 @@
|
||
+/*
|
||
+ * Check pam_sepermit return values and conf= option.
|
||
+ *
|
||
+ * Copyright (c) 2020-2022 Dmitry V. Levin <ldv@altlinux.org>
|
||
+ */
|
||
+
|
||
+#include "test_assert.h"
|
||
+
|
||
+#include <limits.h>
|
||
+#include <stdio.h>
|
||
+#include <string.h>
|
||
+#include <unistd.h>
|
||
+#include <security/pam_appl.h>
|
||
+
|
||
+#define MODULE_NAME "pam_sepermit"
|
||
+#define TEST_NAME "tst-" MODULE_NAME "-retval"
|
||
+
|
||
+static const char service_file[] = TEST_NAME ".service";
|
||
+static const char missing_file[] = TEST_NAME ".missing";
|
||
+static const char config_file[] = TEST_NAME ".conf";
|
||
+static struct pam_conv conv;
|
||
+
|
||
+int
|
||
+main(void)
|
||
+{
|
||
+ pam_handle_t *pamh = NULL;
|
||
+ FILE *fp;
|
||
+ char cwd[PATH_MAX];
|
||
+
|
||
+ ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd)));
|
||
+
|
||
+ /* PAM_USER_UNKNOWN */
|
||
+ ASSERT_NE(NULL, fp = fopen(service_file, "w"));
|
||
+ ASSERT_LT(0,
|
||
+ fprintf(fp, "#%%PAM-1.0\n"
|
||
+ "auth required %s/.libs/%s.so\n"
|
||
+ "account required %s/.libs/%s.so\n"
|
||
+ "password required %s/.libs/%s.so\n"
|
||
+ "session required %s/.libs/%s.so\n",
|
||
+ cwd, MODULE_NAME,
|
||
+ cwd, MODULE_NAME,
|
||
+ cwd, MODULE_NAME,
|
||
+ cwd, MODULE_NAME));
|
||
+ ASSERT_EQ(0, fclose(fp));
|
||
+
|
||
+ ASSERT_EQ(PAM_SUCCESS,
|
||
+ pam_start_confdir(service_file, "", &conv, ".", &pamh));
|
||
+ ASSERT_NE(NULL, pamh);
|
||
+ ASSERT_EQ(PAM_USER_UNKNOWN, pam_authenticate(pamh, 0));
|
||
+ ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0));
|
||
+ ASSERT_EQ(PAM_USER_UNKNOWN, pam_acct_mgmt(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0));
|
||
+ ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
|
||
+ pamh = NULL;
|
||
+
|
||
+ ASSERT_NE(NULL, fp = fopen(config_file, "w"));
|
||
+ ASSERT_LT(0, fprintf(fp, "nosuchuser:ignore\n"));
|
||
+ ASSERT_EQ(0, fclose(fp));
|
||
+
|
||
+ /*
|
||
+ * conf= specifies an existing file,
|
||
+ * PAM_IGNORE -> 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 conf=%s\n"
|
||
+ "account required %s/.libs/%s.so conf=%s\n"
|
||
+ "password required %s/.libs/%s.so conf=%s\n"
|
||
+ "session required %s/.libs/%s.so conf=%s\n",
|
||
+ cwd, MODULE_NAME, config_file,
|
||
+ cwd, MODULE_NAME, config_file,
|
||
+ cwd, MODULE_NAME, config_file,
|
||
+ cwd, MODULE_NAME, config_file));
|
||
+ ASSERT_EQ(0, fclose(fp));
|
||
+
|
||
+ ASSERT_EQ(PAM_SUCCESS,
|
||
+ pam_start_confdir(service_file, "root", &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_PERM_DENIED, pam_acct_mgmt(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0));
|
||
+ ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
|
||
+ pamh = NULL;
|
||
+
|
||
+ /*
|
||
+ * conf= specifies an existing file,
|
||
+ * PAM_IGNORE -> 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 conf=%s\n"
|
||
+ "auth required %s/../pam_permit/.libs/pam_permit.so\n"
|
||
+ "account required %s/.libs/%s.so conf=%s\n"
|
||
+ "account required %s/../pam_permit/.libs/pam_permit.so\n"
|
||
+ "password required %s/.libs/%s.so conf=%s\n"
|
||
+ "password required %s/../pam_permit/.libs/pam_permit.so\n"
|
||
+ "session required %s/.libs/%s.so conf=%s\n"
|
||
+ "session required %s/../pam_permit/.libs/pam_permit.so\n",
|
||
+ cwd, MODULE_NAME, config_file, cwd,
|
||
+ cwd, MODULE_NAME, config_file, cwd,
|
||
+ cwd, MODULE_NAME, config_file, cwd,
|
||
+ cwd, MODULE_NAME, config_file, cwd));
|
||
+ ASSERT_EQ(0, fclose(fp));
|
||
+
|
||
+ ASSERT_EQ(PAM_SUCCESS,
|
||
+ pam_start_confdir(service_file, "root", &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_SUCCESS, pam_acct_mgmt(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0));
|
||
+ ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
|
||
+ pamh = NULL;
|
||
+
|
||
+ /*
|
||
+ * conf= specifies a missing file,
|
||
+ * PAM_IGNORE -> 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 conf=%s\n"
|
||
+ "account required %s/.libs/%s.so conf=%s\n"
|
||
+ "password required %s/.libs/%s.so conf=%s\n"
|
||
+ "session required %s/.libs/%s.so conf=%s\n",
|
||
+ cwd, MODULE_NAME, missing_file,
|
||
+ cwd, MODULE_NAME, missing_file,
|
||
+ cwd, MODULE_NAME, missing_file,
|
||
+ cwd, MODULE_NAME, missing_file));
|
||
+ ASSERT_EQ(0, fclose(fp));
|
||
+
|
||
+ ASSERT_EQ(PAM_SUCCESS,
|
||
+ pam_start_confdir(service_file, "root", &conv, ".", &pamh));
|
||
+ ASSERT_NE(NULL, pamh);
|
||
+ ASSERT_EQ(PAM_SERVICE_ERR, 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_MODULE_UNKNOWN, pam_chauthtok(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0));
|
||
+ ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
|
||
+ pamh = NULL;
|
||
+
|
||
+ /* cleanup */
|
||
+ ASSERT_EQ(0, unlink(config_file));
|
||
+ ASSERT_EQ(0, unlink(service_file));
|
||
+
|
||
+ return 0;
|
||
+}
|
||
diff --git a/modules/pam_shells/Makefile.am b/modules/pam_shells/Makefile.am
|
||
index b91bada5..3ce3e1d0 100644
|
||
--- a/modules/pam_shells/Makefile.am
|
||
+++ b/modules/pam_shells/Makefile.am
|
||
@@ -18,14 +18,14 @@ securelibdir = $(SECUREDIR)
|
||
secureconfdir = $(SCONFIGDIR)
|
||
|
||
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
|
||
- $(WARN_CFLAGS)
|
||
+ $(WARN_CFLAGS) $(ECONF_CFLAGS)
|
||
AM_LDFLAGS = -no-undefined -avoid-version -module
|
||
if HAVE_VERSIONING
|
||
AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
|
||
endif
|
||
|
||
securelib_LTLIBRARIES = pam_shells.la
|
||
-pam_shells_la_LIBADD = $(top_builddir)/libpam/libpam.la
|
||
+pam_shells_la_LIBADD = $(top_builddir)/libpam/libpam.la $(ECONF_LIBS)
|
||
|
||
if ENABLE_REGENERATE_MAN
|
||
dist_noinst_DATA = README
|
||
diff --git a/modules/pam_shells/pam_shells.8.xml b/modules/pam_shells/pam_shells.8.xml
|
||
index 15f47671..73b4855a 100644
|
||
--- a/modules/pam_shells/pam_shells.8.xml
|
||
+++ b/modules/pam_shells/pam_shells.8.xml
|
||
@@ -29,9 +29,17 @@
|
||
pam_shells is a PAM module that only allows access to the
|
||
system if the user's shell is listed in <filename>/etc/shells</filename>.
|
||
</para>
|
||
+
|
||
+ <para condition="with_vendordir_and_with_econf">
|
||
+ If this file does not exist, entries are taken from files
|
||
+ <filename>%vendordir%/shells</filename>,
|
||
+ <filename>%vendordir%/shells.d/*</filename> and
|
||
+ <filename>/etc/shells.d/*</filename> in that order.
|
||
+ </para>
|
||
+
|
||
<para>
|
||
- It also checks if <filename>/etc/shells</filename> is a plain
|
||
- file and not world writable.
|
||
+ It also checks if needed files (e.g. <filename>/etc/shells</filename>) are plain
|
||
+ files and not world writable.
|
||
</para>
|
||
</refsect1>
|
||
|
||
diff --git a/modules/pam_shells/pam_shells.c b/modules/pam_shells/pam_shells.c
|
||
index dc8f4878..abebdd0c 100644
|
||
--- a/modules/pam_shells/pam_shells.c
|
||
+++ b/modules/pam_shells/pam_shells.c
|
||
@@ -13,27 +13,47 @@
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
+#include <stdbool.h>
|
||
#include <sys/stat.h>
|
||
#include <syslog.h>
|
||
#include <unistd.h>
|
||
+#if defined (USE_ECONF) && defined (VENDORDIR)
|
||
+#include <libeconf.h>
|
||
+#endif
|
||
|
||
#include <security/pam_modules.h>
|
||
#include <security/pam_modutil.h>
|
||
#include <security/pam_ext.h>
|
||
|
||
#define SHELL_FILE "/etc/shells"
|
||
-
|
||
+#define SHELLS "shells"
|
||
+#define ETCDIR "/etc"
|
||
#define DEFAULT_SHELL "/bin/sh"
|
||
|
||
+static bool check_file(const char *filename, const void *pamh)
|
||
+{
|
||
+ struct stat sb;
|
||
+
|
||
+ if (stat(filename, &sb)) {
|
||
+ pam_syslog(pamh, LOG_ERR, "Cannot stat %s: %m", filename);
|
||
+ return false; /* must have /etc/shells */
|
||
+ }
|
||
+
|
||
+ if ((sb.st_mode & S_IWOTH) || !S_ISREG(sb.st_mode)) {
|
||
+ pam_syslog(pamh, LOG_ERR,
|
||
+ "%s is either world writable or not a normal file",
|
||
+ filename);
|
||
+ return false;
|
||
+ }
|
||
+ return true;
|
||
+}
|
||
+
|
||
static int perform_check(pam_handle_t *pamh)
|
||
{
|
||
int retval = PAM_AUTH_ERR;
|
||
const char *userName;
|
||
const char *userShell;
|
||
- char shellFileLine[256];
|
||
- struct stat sb;
|
||
struct passwd * pw;
|
||
- FILE * shellFile;
|
||
|
||
retval = pam_get_user(pamh, &userName, NULL);
|
||
if (retval != PAM_SUCCESS) {
|
||
@@ -48,18 +68,50 @@ static int perform_check(pam_handle_t *pamh)
|
||
if (userShell[0] == '\0')
|
||
userShell = DEFAULT_SHELL;
|
||
|
||
- if (stat(SHELL_FILE,&sb)) {
|
||
- pam_syslog(pamh, LOG_ERR, "Cannot stat %s: %m", SHELL_FILE);
|
||
- return PAM_AUTH_ERR; /* must have /etc/shells */
|
||
+#if defined (USE_ECONF) && defined (VENDORDIR)
|
||
+ size_t size = 0;
|
||
+ econf_err error;
|
||
+ char **keys;
|
||
+ econf_file *key_file;
|
||
+
|
||
+ error = econf_readDirsWithCallback(&key_file,
|
||
+ VENDORDIR,
|
||
+ ETCDIR,
|
||
+ SHELLS,
|
||
+ NULL,
|
||
+ "", /* key only */
|
||
+ "#", /* comment */
|
||
+ check_file, pamh);
|
||
+ if (error) {
|
||
+ pam_syslog(pamh, LOG_ERR,
|
||
+ "Cannot parse shell files: %s",
|
||
+ econf_errString(error));
|
||
+ return PAM_AUTH_ERR;
|
||
}
|
||
|
||
- if ((sb.st_mode & S_IWOTH) || !S_ISREG(sb.st_mode)) {
|
||
+ error = econf_getKeys(key_file, NULL, &size, &keys);
|
||
+ if (error) {
|
||
pam_syslog(pamh, LOG_ERR,
|
||
- "%s is either world writable or not a normal file",
|
||
- SHELL_FILE);
|
||
+ "Cannot evaluate entries in shell files: %s",
|
||
+ econf_errString(error));
|
||
+ econf_free (key_file);
|
||
return PAM_AUTH_ERR;
|
||
}
|
||
|
||
+ retval = 1;
|
||
+ for (size_t i = 0; i < size; i++) {
|
||
+ retval = strcmp(keys[i], userShell);
|
||
+ if (!retval)
|
||
+ break;
|
||
+ }
|
||
+ econf_free (key_file);
|
||
+#else
|
||
+ char shellFileLine[256];
|
||
+ FILE * shellFile;
|
||
+
|
||
+ if (!check_file(SHELL_FILE, pamh))
|
||
+ return PAM_AUTH_ERR;
|
||
+
|
||
shellFile = fopen(SHELL_FILE,"r");
|
||
if (shellFile == NULL) { /* Check that we opened it successfully */
|
||
pam_syslog(pamh, LOG_ERR, "Error opening %s: %m", SHELL_FILE);
|
||
@@ -75,6 +127,7 @@ static int perform_check(pam_handle_t *pamh)
|
||
}
|
||
|
||
fclose(shellFile);
|
||
+ #endif
|
||
|
||
if (retval) {
|
||
return PAM_AUTH_ERR;
|
||
diff --git a/modules/pam_time/Makefile.am b/modules/pam_time/Makefile.am
|
||
index 833d51a6..ad53f1cc 100644
|
||
--- a/modules/pam_time/Makefile.am
|
||
+++ b/modules/pam_time/Makefile.am
|
||
@@ -12,13 +12,13 @@ dist_man_MANS = time.conf.5 pam_time.8
|
||
endif
|
||
XMLS = README.xml time.conf.5.xml pam_time.8.xml
|
||
dist_check_SCRIPTS = tst-pam_time
|
||
-TESTS = $(dist_check_SCRIPTS)
|
||
+TESTS = $(dist_check_SCRIPTS) $(check_PROGRAMS)
|
||
|
||
securelibdir = $(SECUREDIR)
|
||
secureconfdir = $(SCONFIGDIR)
|
||
|
||
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
|
||
- -DPAM_TIME_CONF=\"$(SCONFIGDIR)/time.conf\" $(WARN_CFLAGS)
|
||
+ $(WARN_CFLAGS)
|
||
AM_LDFLAGS = -no-undefined -avoid-version -module
|
||
if HAVE_VERSIONING
|
||
AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
|
||
@@ -28,6 +28,9 @@ pam_time_la_LIBADD = $(top_builddir)/libpam/libpam.la
|
||
securelib_LTLIBRARIES = pam_time.la
|
||
dist_secureconf_DATA = time.conf
|
||
|
||
+check_PROGRAMS = tst-pam_time-retval
|
||
+tst_pam_time_retval_LDADD = $(top_builddir)/libpam/libpam.la
|
||
+
|
||
if ENABLE_REGENERATE_MAN
|
||
dist_noinst_DATA = README
|
||
-include $(top_srcdir)/Make.xml.rules
|
||
diff --git a/modules/pam_time/pam_time.8.xml b/modules/pam_time/pam_time.8.xml
|
||
index 4708220c..a33744ea 100644
|
||
--- a/modules/pam_time/pam_time.8.xml
|
||
+++ b/modules/pam_time/pam_time.8.xml
|
||
@@ -51,6 +51,11 @@
|
||
<filename>/etc/security/time.conf</filename>.
|
||
An alternative file can be specified with the <emphasis>conffile</emphasis> option.
|
||
</para>
|
||
+ <para condition="with_vendordir">
|
||
+ If there is no explicitly specified configuration file and
|
||
+ <filename>/etc/security/time.conf</filename> does not exist,
|
||
+ <filename>%vendordir%/security/time.conf</filename> is used.
|
||
+ </para>
|
||
<para>
|
||
If Linux PAM is compiled with audit support the module will report
|
||
when it denies access.
|
||
diff --git a/modules/pam_time/pam_time.c b/modules/pam_time/pam_time.c
|
||
index 089ae22d..9092597a 100644
|
||
--- a/modules/pam_time/pam_time.c
|
||
+++ b/modules/pam_time/pam_time.c
|
||
@@ -33,6 +33,11 @@
|
||
#include <libaudit.h>
|
||
#endif
|
||
|
||
+#define PAM_TIME_CONF (SCONFIGDIR "/time.conf")
|
||
+#ifdef VENDOR_SCONFIGDIR
|
||
+#define VENDOR_PAM_TIME_CONF (VENDOR_SCONFIGDIR "/time.conf")
|
||
+#endif
|
||
+
|
||
#define PAM_TIME_BUFLEN 1000
|
||
#define FIELD_SEPARATOR ';' /* this is new as of .02 */
|
||
|
||
@@ -53,7 +58,7 @@ _pam_parse (const pam_handle_t *pamh, int argc, const char **argv, const char **
|
||
{
|
||
int ctrl = 0;
|
||
|
||
- *conffile = PAM_TIME_CONF;
|
||
+ *conffile = NULL;
|
||
/* step through arguments */
|
||
for (; argc-- > 0; ++argv) {
|
||
const char *str;
|
||
@@ -77,6 +82,20 @@ _pam_parse (const pam_handle_t *pamh, int argc, const char **argv, const char **
|
||
}
|
||
}
|
||
|
||
+ if (*conffile == NULL) {
|
||
+ *conffile = PAM_TIME_CONF;
|
||
+#ifdef VENDOR_PAM_TIME_CONF
|
||
+ /*
|
||
+ * Check whether PAM_TIME_CONF file is available.
|
||
+ * If it does not exist, fall back to VENDOR_PAM_TIME_CONF file.
|
||
+ */
|
||
+ struct stat buffer;
|
||
+ if (stat(*conffile, &buffer) != 0 && errno == ENOENT) {
|
||
+ *conffile = VENDOR_PAM_TIME_CONF;
|
||
+ }
|
||
+#endif
|
||
+ }
|
||
+
|
||
return ctrl;
|
||
}
|
||
|
||
diff --git a/modules/pam_time/tst-pam_time-retval.c b/modules/pam_time/tst-pam_time-retval.c
|
||
new file mode 100644
|
||
index 00000000..281ac80d
|
||
--- /dev/null
|
||
+++ b/modules/pam_time/tst-pam_time-retval.c
|
||
@@ -0,0 +1,107 @@
|
||
+/*
|
||
+ * Check pam_time 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 <limits.h>
|
||
+#include <stdio.h>
|
||
+#include <string.h>
|
||
+#include <unistd.h>
|
||
+#include <security/pam_appl.h>
|
||
+
|
||
+#define MODULE_NAME "pam_time"
|
||
+#define TEST_NAME "tst-" MODULE_NAME "-retval"
|
||
+
|
||
+static const char service_file[] = TEST_NAME ".service";
|
||
+static const char config_file[] = TEST_NAME ".conf";
|
||
+static struct pam_conv conv;
|
||
+
|
||
+int
|
||
+main(void)
|
||
+{
|
||
+ pam_handle_t *pamh = NULL;
|
||
+ FILE *fp;
|
||
+ char cwd[PATH_MAX];
|
||
+
|
||
+ ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd)));
|
||
+
|
||
+ /* PAM_USER_UNKNOWN */
|
||
+ ASSERT_NE(NULL, fp = fopen(service_file, "w"));
|
||
+ ASSERT_LT(0,
|
||
+ fprintf(fp, "#%%PAM-1.0\n"
|
||
+ "auth required %s/.libs/%s.so\n"
|
||
+ "account required %s/.libs/%s.so\n"
|
||
+ "password required %s/.libs/%s.so\n"
|
||
+ "session required %s/.libs/%s.so\n",
|
||
+ cwd, MODULE_NAME,
|
||
+ cwd, MODULE_NAME,
|
||
+ cwd, MODULE_NAME,
|
||
+ cwd, MODULE_NAME));
|
||
+ ASSERT_EQ(0, fclose(fp));
|
||
+
|
||
+ ASSERT_EQ(PAM_SUCCESS,
|
||
+ pam_start_confdir(service_file, "", &conv, ".", &pamh));
|
||
+ ASSERT_NE(NULL, pamh);
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_authenticate(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_setcred(pamh, 0));
|
||
+ ASSERT_EQ(PAM_USER_UNKNOWN, pam_acct_mgmt(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0));
|
||
+ ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
|
||
+ pamh = NULL;
|
||
+
|
||
+ ASSERT_NE(NULL, fp = fopen(config_file, "w"));
|
||
+ ASSERT_LT(0, fprintf(fp, "# only root can access %s\n"
|
||
+ "%s ; * ; !root ; !Al0000-2400\n",
|
||
+ service_file, service_file));
|
||
+ ASSERT_EQ(0, fclose(fp));
|
||
+
|
||
+ /* conffile= specifies an existing file */
|
||
+ 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\n"
|
||
+ "account required %s/.libs/%s.so conffile=%s\n"
|
||
+ "password required %s/.libs/%s.so conffile=%s\n"
|
||
+ "session required %s/.libs/%s.so conffile=%s\n",
|
||
+ cwd, MODULE_NAME, config_file,
|
||
+ cwd, MODULE_NAME, config_file,
|
||
+ cwd, MODULE_NAME, config_file,
|
||
+ cwd, MODULE_NAME, config_file));
|
||
+ ASSERT_EQ(0, fclose(fp));
|
||
+
|
||
+ ASSERT_EQ(PAM_SUCCESS,
|
||
+ pam_start_confdir(service_file, "root", &conv, ".", &pamh));
|
||
+ ASSERT_NE(NULL, pamh);
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_authenticate(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_setcred(pamh, 0));
|
||
+ ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0));
|
||
+ ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
|
||
+ pamh = NULL;
|
||
+
|
||
+ ASSERT_EQ(PAM_SUCCESS,
|
||
+ pam_start_confdir(service_file, "noone", &conv, ".", &pamh));
|
||
+ ASSERT_NE(NULL, pamh);
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_authenticate(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_setcred(pamh, 0));
|
||
+ ASSERT_EQ(PAM_PERM_DENIED, pam_acct_mgmt(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0));
|
||
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0));
|
||
+ ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
|
||
+ pamh = NULL;
|
||
+
|
||
+ /* cleanup */
|
||
+ ASSERT_EQ(0, unlink(config_file));
|
||
+ ASSERT_EQ(0, unlink(service_file));
|
||
+
|
||
+ return 0;
|
||
+}
|
||
diff --git a/modules/pam_unix/passverify.c b/modules/pam_unix/passverify.c
|
||
index f2474a5b..c8ab49f3 100644
|
||
--- a/modules/pam_unix/passverify.c
|
||
+++ b/modules/pam_unix/passverify.c
|
||
@@ -334,7 +334,7 @@ PAMH_ARG_DECL(int check_shadow_expiry,
|
||
|
||
#define PW_TMPFILE "/etc/npasswd"
|
||
#define SH_TMPFILE "/etc/nshadow"
|
||
-#define OPW_TMPFILE "/etc/security/nopasswd"
|
||
+#define OPW_TMPFILE SCONFIGDIR "/nopasswd"
|
||
|
||
/*
|
||
* i64c - convert an integer to a radix 64 character
|
||
diff --git a/modules/pam_unix/passverify.h b/modules/pam_unix/passverify.h
|
||
index c07037d2..463ef185 100644
|
||
--- a/modules/pam_unix/passverify.h
|
||
+++ b/modules/pam_unix/passverify.h
|
||
@@ -8,7 +8,7 @@
|
||
|
||
#define PAM_UNIX_RUN_HELPER PAM_CRED_INSUFFICIENT
|
||
|
||
-#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
|
||
+#define OLD_PASSWORDS_FILE SCONFIGDIR "/opasswd"
|
||
|
||
int
|
||
is_pwd_shadowed(const struct passwd *pwd);
|
||
diff --git a/modules/pam_usertype/pam_usertype.8.xml b/modules/pam_usertype/pam_usertype.8.xml
|
||
index 7651da6e..d9307ba3 100644
|
||
--- a/modules/pam_usertype/pam_usertype.8.xml
|
||
+++ b/modules/pam_usertype/pam_usertype.8.xml
|
||
@@ -31,7 +31,7 @@
|
||
pam_usertype.so is designed to succeed or fail authentication
|
||
based on type of the account of the authenticated user.
|
||
The type of the account is decided with help of
|
||
- <emphasis>SYS_UID_MIN</emphasis> and <emphasis>SYS_UID_MAX</emphasis>
|
||
+ <emphasis>SYS_UID_MAX</emphasis>
|
||
settings in <emphasis>/etc/login.defs</emphasis>. One use is to select
|
||
whether to load other modules based on this test.
|
||
</para>
|
||
diff --git a/modules/pam_usertype/pam_usertype.c b/modules/pam_usertype/pam_usertype.c
|
||
index d03b73b5..cfd9c8bb 100644
|
||
--- a/modules/pam_usertype/pam_usertype.c
|
||
+++ b/modules/pam_usertype/pam_usertype.c
|
||
@@ -194,7 +194,6 @@ static int
|
||
pam_usertype_is_system(pam_handle_t *pamh, uid_t uid)
|
||
{
|
||
uid_t uid_min;
|
||
- uid_t sys_min;
|
||
uid_t sys_max;
|
||
|
||
if (uid == (uid_t)-1) {
|
||
@@ -202,21 +201,19 @@ pam_usertype_is_system(pam_handle_t *pamh, uid_t uid)
|
||
return PAM_USER_UNKNOWN;
|
||
}
|
||
|
||
- if (uid <= 99) {
|
||
- /* Reserved. */
|
||
- return PAM_SUCCESS;
|
||
- }
|
||
-
|
||
if (uid == PAM_USERTYPE_OVERFLOW_UID) {
|
||
/* nobody */
|
||
return PAM_SUCCESS;
|
||
}
|
||
|
||
uid_min = pam_usertype_get_id(pamh, "UID_MIN", PAM_USERTYPE_UIDMIN);
|
||
- sys_min = pam_usertype_get_id(pamh, "SYS_UID_MIN", PAM_USERTYPE_SYSUIDMIN);
|
||
sys_max = pam_usertype_get_id(pamh, "SYS_UID_MAX", uid_min - 1);
|
||
|
||
- return uid >= sys_min && uid <= sys_max ? PAM_SUCCESS : PAM_AUTH_ERR;
|
||
+ if (uid <= sys_max && uid < uid_min) {
|
||
+ return PAM_SUCCESS;
|
||
+ }
|
||
+
|
||
+ return PAM_AUTH_ERR;
|
||
}
|
||
|
||
static int
|
||
@@ -253,7 +250,7 @@ pam_usertype_evaluate(struct pam_usertype_opts *opts,
|
||
|
||
/**
|
||
* Arguments:
|
||
- * - issystem: uid in <SYS_UID_MIN, SYS_UID_MAX>
|
||
+ * - issystem: uid less than SYS_UID_MAX
|
||
* - isregular: not issystem
|
||
* - use_uid: use user that runs application not that is being authenticate (same as in pam_succeed_if)
|
||
* - audit: log unknown users to syslog
|
||
diff --git a/modules/pam_xauth/pam_xauth.c b/modules/pam_xauth/pam_xauth.c
|
||
index 03f8dc78..bbb7743b 100644
|
||
--- a/modules/pam_xauth/pam_xauth.c
|
||
+++ b/modules/pam_xauth/pam_xauth.c
|
||
@@ -52,6 +52,7 @@
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <syslog.h>
|
||
+#include <signal.h>
|
||
|
||
#include <security/pam_modules.h>
|
||
#include <security/_pam_macros.h>
|
||
@@ -99,6 +100,7 @@ run_coprocess(pam_handle_t *pamh, const char *input, char **output,
|
||
char *buffer = NULL;
|
||
size_t buffer_size = 0;
|
||
va_list ap;
|
||
+ struct sigaction newsa, oldsa;
|
||
|
||
*output = NULL;
|
||
|
||
@@ -114,6 +116,17 @@ run_coprocess(pam_handle_t *pamh, const char *input, char **output,
|
||
return -1;
|
||
}
|
||
|
||
+ memset(&newsa, '\0', sizeof(newsa));
|
||
+ newsa.sa_handler = SIG_DFL;
|
||
+ if (sigaction(SIGCHLD, &newsa, &oldsa) == -1) {
|
||
+ pam_syslog(pamh, LOG_ERR, "failed to reset SIGCHLD handler: %m");
|
||
+ close(ipipe[0]);
|
||
+ close(ipipe[1]);
|
||
+ close(opipe[0]);
|
||
+ close(opipe[1]);
|
||
+ return -1;
|
||
+ }
|
||
+
|
||
/* Fork off a child. */
|
||
child = fork();
|
||
if (child == -1) {
|
||
@@ -209,6 +222,7 @@ run_coprocess(pam_handle_t *pamh, const char *input, char **output,
|
||
}
|
||
close(opipe[0]);
|
||
waitpid(child, NULL, 0);
|
||
+ sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */
|
||
return -1;
|
||
}
|
||
/* Save the new buffer location, copy the newly-read data into
|
||
@@ -225,6 +239,7 @@ run_coprocess(pam_handle_t *pamh, const char *input, char **output,
|
||
close(opipe[0]);
|
||
*output = buffer;
|
||
waitpid(child, NULL, 0);
|
||
+ sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */
|
||
return 0;
|
||
}
|
||
|
||
diff --git a/po/Linux-PAM.pot b/po/Linux-PAM.pot
|
||
index 5e92d7e9..d7cd325d 100644
|
||
--- a/po/Linux-PAM.pot
|
||
+++ b/po/Linux-PAM.pot
|
||
@@ -8,7 +8,7 @@ msgid ""
|
||
msgstr ""
|
||
"Project-Id-Version: Linux-PAM 1.5.2\n"
|
||
"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n"
|
||
-"POT-Creation-Date: 2021-07-20 20:00+0000\n"
|
||
+"POT-Creation-Date: 2022-11-11 11:11+0000\n"
|
||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||
@@ -18,7 +18,7 @@ msgstr ""
|
||
"Content-Transfer-Encoding: 8bit\n"
|
||
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
|
||
|
||
-#: libpam/pam_get_authtok.c:39 modules/pam_exec/pam_exec.c:181
|
||
+#: libpam/pam_get_authtok.c:39 modules/pam_exec/pam_exec.c:183
|
||
#: modules/pam_userdb/pam_userdb.c:53
|
||
msgid "Password: "
|
||
msgstr ""
|
||
@@ -212,34 +212,40 @@ msgstr ""
|
||
msgid "erroneous conversation (%d)\n"
|
||
msgstr ""
|
||
|
||
-#: modules/pam_exec/pam_exec.c:279
|
||
+#: modules/pam_exec/pam_exec.c:289
|
||
#, c-format
|
||
msgid "%s failed: exit code %d"
|
||
msgstr ""
|
||
|
||
-#: modules/pam_exec/pam_exec.c:289
|
||
+#: modules/pam_exec/pam_exec.c:299
|
||
#, c-format
|
||
msgid "%s failed: caught signal %d%s"
|
||
msgstr ""
|
||
|
||
-#: modules/pam_exec/pam_exec.c:299
|
||
+#: modules/pam_exec/pam_exec.c:309
|
||
#, c-format
|
||
msgid "%s failed: unknown status 0x%x"
|
||
msgstr ""
|
||
|
||
-#: modules/pam_faillock/main.c:103
|
||
+#: modules/pam_faillock/main.c:130
|
||
#, c-format
|
||
msgid ""
|
||
-"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset]\n"
|
||
+"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--"
|
||
+"legacy-output]\n"
|
||
msgstr ""
|
||
|
||
-#: modules/pam_faillock/pam_faillock.c:618
|
||
+#: modules/pam_faillock/main.c:181
|
||
+#, c-format
|
||
+msgid "Login Failures Latest failure From\n"
|
||
+msgstr ""
|
||
+
|
||
+#: modules/pam_faillock/pam_faillock.c:404
|
||
#, c-format
|
||
msgid "The account is locked due to %u failed logins."
|
||
msgstr ""
|
||
|
||
-#: modules/pam_faillock/pam_faillock.c:627
|
||
-#: modules/pam_faillock/pam_faillock.c:633
|
||
+#: modules/pam_faillock/pam_faillock.c:413
|
||
+#: modules/pam_faillock/pam_faillock.c:419
|
||
#, c-format
|
||
msgid "(%d minute left to unlock)"
|
||
msgid_plural "(%d minutes left to unlock)"
|
||
@@ -247,45 +253,45 @@ msgstr[0] ""
|
||
msgstr[1] ""
|
||
|
||
#. TRANSLATORS: only used if dngettext is not supported.
|
||
-#: modules/pam_faillock/pam_faillock.c:636
|
||
+#: modules/pam_faillock/pam_faillock.c:422
|
||
#, c-format
|
||
msgid "(%d minutes left to unlock)"
|
||
msgstr ""
|
||
|
||
#. TRANSLATORS: "strftime options for date of last login"
|
||
-#: modules/pam_lastlog/pam_lastlog.c:318 modules/pam_lastlog/pam_lastlog.c:579
|
||
+#: modules/pam_lastlog/pam_lastlog.c:326 modules/pam_lastlog/pam_lastlog.c:595
|
||
msgid " %a %b %e %H:%M:%S %Z %Y"
|
||
msgstr ""
|
||
|
||
#. TRANSLATORS: " from <host>"
|
||
-#: modules/pam_lastlog/pam_lastlog.c:327 modules/pam_lastlog/pam_lastlog.c:588
|
||
+#: modules/pam_lastlog/pam_lastlog.c:335 modules/pam_lastlog/pam_lastlog.c:604
|
||
#, c-format
|
||
msgid " from %.*s"
|
||
msgstr ""
|
||
|
||
#. TRANSLATORS: " on <terminal>"
|
||
-#: modules/pam_lastlog/pam_lastlog.c:339 modules/pam_lastlog/pam_lastlog.c:600
|
||
+#: modules/pam_lastlog/pam_lastlog.c:347 modules/pam_lastlog/pam_lastlog.c:616
|
||
#, c-format
|
||
msgid " on %.*s"
|
||
msgstr ""
|
||
|
||
#. TRANSLATORS: "Last login: <date> from <host> on <terminal>"
|
||
-#: modules/pam_lastlog/pam_lastlog.c:349
|
||
+#: modules/pam_lastlog/pam_lastlog.c:357
|
||
#, c-format
|
||
msgid "Last login:%s%s%s"
|
||
msgstr ""
|
||
|
||
-#: modules/pam_lastlog/pam_lastlog.c:355
|
||
+#: modules/pam_lastlog/pam_lastlog.c:363
|
||
msgid "Welcome to your new account!"
|
||
msgstr ""
|
||
|
||
#. TRANSLATORS: "Last failed login: <date> from <host> on <terminal>"
|
||
-#: modules/pam_lastlog/pam_lastlog.c:610
|
||
+#: modules/pam_lastlog/pam_lastlog.c:626
|
||
#, c-format
|
||
msgid "Last failed login:%s%s%s"
|
||
msgstr ""
|
||
|
||
-#: modules/pam_lastlog/pam_lastlog.c:619 modules/pam_lastlog/pam_lastlog.c:626
|
||
+#: modules/pam_lastlog/pam_lastlog.c:635 modules/pam_lastlog/pam_lastlog.c:642
|
||
#, c-format
|
||
msgid "There was %d failed login attempt since the last successful login."
|
||
msgid_plural ""
|
||
@@ -294,18 +300,18 @@ msgstr[0] ""
|
||
msgstr[1] ""
|
||
|
||
#. TRANSLATORS: only used if dngettext is not supported
|
||
-#: modules/pam_lastlog/pam_lastlog.c:631
|
||
+#: modules/pam_lastlog/pam_lastlog.c:647
|
||
#, c-format
|
||
msgid "There were %d failed login attempts since the last successful login."
|
||
msgstr ""
|
||
|
||
-#: modules/pam_limits/pam_limits.c:1164
|
||
+#: modules/pam_limits/pam_limits.c:1269
|
||
#, c-format
|
||
msgid "There were too many logins for '%s'."
|
||
msgstr ""
|
||
|
||
#: modules/pam_mail/pam_mail.c:289
|
||
-msgid "You have no mail."
|
||
+msgid "You do not have any new mail."
|
||
msgstr ""
|
||
|
||
#: modules/pam_mail/pam_mail.c:292
|
||
@@ -350,12 +356,12 @@ msgstr ""
|
||
msgid "Unable to create and initialize directory '%s'."
|
||
msgstr ""
|
||
|
||
-#: modules/pam_pwhistory/pam_pwhistory.c:371
|
||
+#: modules/pam_pwhistory/pam_pwhistory.c:378
|
||
#: modules/pam_unix/pam_unix_passwd.c:589
|
||
msgid "Password has been already used. Choose another."
|
||
msgstr ""
|
||
|
||
-#: modules/pam_pwhistory/pam_pwhistory.c:378
|
||
+#: modules/pam_pwhistory/pam_pwhistory.c:385
|
||
msgid "Password has been already used."
|
||
msgstr ""
|
||
|
||
diff --git a/xtests/Makefile.am b/xtests/Makefile.am
|
||
index 70f8441e..acf97469 100644
|
||
--- a/xtests/Makefile.am
|
||
+++ b/xtests/Makefile.am
|
||
@@ -25,6 +25,7 @@ EXTRA_DIST = run-xtests.sh tst-pam_dispatch1.pamd tst-pam_dispatch2.pamd \
|
||
tst-pam_succeed_if1.pamd tst-pam_succeed_if1.sh \
|
||
group.conf tst-pam_group1.pamd tst-pam_group1.sh \
|
||
tst-pam_authfail.pamd tst-pam_authsucceed.pamd \
|
||
+ tst-pam_shells.pamd shells.conf tst-pam_shells.sh \
|
||
tst-pam_substack1.pamd tst-pam_substack1a.pamd tst-pam_substack1.sh \
|
||
tst-pam_substack2.pamd tst-pam_substack2a.pamd tst-pam_substack2.sh \
|
||
tst-pam_substack3.pamd tst-pam_substack3a.pamd tst-pam_substack3.sh \
|
||
@@ -43,7 +44,8 @@ XTESTS = tst-pam_dispatch1 tst-pam_dispatch2 tst-pam_dispatch3 \
|
||
tst-pam_access1 tst-pam_access2 tst-pam_access3 \
|
||
tst-pam_access4 tst-pam_limits1 tst-pam_succeed_if1 \
|
||
tst-pam_group1 tst-pam_authfail tst-pam_authsucceed \
|
||
- tst-pam_pwhistory1 tst-pam_time1 tst-pam_motd
|
||
+ tst-pam_pwhistory1 tst-pam_time1 tst-pam_motd \
|
||
+ tst-pam_shells
|
||
|
||
NOSRCTESTS = tst-pam_substack1 tst-pam_substack2 tst-pam_substack3 \
|
||
tst-pam_substack4 tst-pam_substack5 tst-pam_assemble_line1
|
||
diff --git a/xtests/run-xtests.sh b/xtests/run-xtests.sh
|
||
index 14f585d9..e580e0ab 100755
|
||
--- a/xtests/run-xtests.sh
|
||
+++ b/xtests/run-xtests.sh
|
||
@@ -18,10 +18,16 @@ all=0
|
||
|
||
mkdir -p /etc/security
|
||
for config in access.conf group.conf time.conf limits.conf ; do
|
||
- cp /etc/security/$config /etc/security/$config-pam-xtests
|
||
+ [ -f "/etc/security/$config" ] &&
|
||
+ mv /etc/security/$config /etc/security/$config-pam-xtests
|
||
install -m 644 "${SRCDIR}"/$config /etc/security/$config
|
||
done
|
||
-mv /etc/security/opasswd /etc/security/opasswd-pam-xtests
|
||
+[ -f /etc/shells ] &&
|
||
+ mv /etc/shells /etc/shells-pam-xtests
|
||
+install -m 644 "${SRCDIR}"/shells.conf /etc/shells
|
||
+
|
||
+[ -f /etc/security/opasswd ] &&
|
||
+ mv /etc/security/opasswd /etc/security/opasswd-pam-xtests
|
||
|
||
for testname in $XTESTS ; do
|
||
for cfg in "${SRCDIR}"/$testname*.pamd ; do
|
||
@@ -47,11 +53,18 @@ for testname in $XTESTS ; do
|
||
all=`expr $all + 1`
|
||
rm -f /etc/pam.d/$testname*
|
||
done
|
||
-mv /etc/security/access.conf-pam-xtests /etc/security/access.conf
|
||
-mv /etc/security/group.conf-pam-xtests /etc/security/group.conf
|
||
-mv /etc/security/time.conf-pam-xtests /etc/security/time.conf
|
||
-mv /etc/security/limits.conf-pam-xtests /etc/security/limits.conf
|
||
-mv /etc/security/opasswd-pam-xtests /etc/security/opasswd
|
||
+
|
||
+for config in access.conf group.conf time.conf limits.conf opasswd ; do
|
||
+ if [ -f "/etc/security/$config-pam-xtests" ]; then
|
||
+ mv /etc/security/$config-pam-xtests /etc/security/$config
|
||
+ else
|
||
+ rm -f /etc/security/$config
|
||
+ fi
|
||
+done
|
||
+
|
||
+[ -f "/etc/shells-pam-xtests" ] &&
|
||
+ mv /etc/shells-pam-xtests /etc/shells
|
||
+
|
||
if test "$failed" -ne 0; then
|
||
echo "==================="
|
||
echo "$failed of $all tests failed"
|
||
diff --git a/xtests/shells.conf b/xtests/shells.conf
|
||
new file mode 100644
|
||
index 00000000..74776e68
|
||
--- /dev/null
|
||
+++ b/xtests/shells.conf
|
||
@@ -0,0 +1,3 @@
|
||
+/bin/ash
|
||
+/bin/testbash
|
||
+/bin/csh
|
||
diff --git a/xtests/tst-pam_shells.c b/xtests/tst-pam_shells.c
|
||
new file mode 100644
|
||
index 00000000..b6ba938e
|
||
--- /dev/null
|
||
+++ b/xtests/tst-pam_shells.c
|
||
@@ -0,0 +1,68 @@
|
||
+/*
|
||
+ * Redistribution and use in source and binary forms, with or without
|
||
+ * modification, are permitted provided that the following conditions
|
||
+ * are met:
|
||
+ * 1. Redistributions of source code must retain the above copyright
|
||
+ * notice, and the entire permission notice in its entirety,
|
||
+ * including the disclaimer of warranties.
|
||
+ * 2. Redistributions in binary form must reproduce the above copyright
|
||
+ * notice, this list of conditions and the following disclaimer in the
|
||
+ * documentation and/or other materials provided with the distribution.
|
||
+ * 3. The name of the author may not be used to endorse or promote
|
||
+ * products derived from this software without specific prior
|
||
+ * written permission.
|
||
+ *
|
||
+ * ALTERNATIVELY, this product may be distributed under the terms of
|
||
+ * the GNU Public License, in which case the provisions of the GPL are
|
||
+ * required INSTEAD OF the above restrictions. (This clause is
|
||
+ * necessary due to a potential bad interaction between the GPL and
|
||
+ * the restrictions contained in a BSD-style copyright.)
|
||
+ *
|
||
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
+ */
|
||
+
|
||
+/*
|
||
+ test case:
|
||
+
|
||
+ shells.conf:
|
||
+ /bin/testbash
|
||
+
|
||
+*/
|
||
+
|
||
+#include "test_assert.h"
|
||
+
|
||
+#include <stdio.h>
|
||
+#include <stdlib.h>
|
||
+#include <string.h>
|
||
+#include <security/pam_appl.h>
|
||
+
|
||
+static struct pam_conv conv;
|
||
+
|
||
+int
|
||
+main(void)
|
||
+{
|
||
+ pam_handle_t *pamh = NULL;
|
||
+ int retval;
|
||
+
|
||
+ // /bin/testbash is defined in shell definition file(s)
|
||
+ ASSERT_EQ(PAM_SUCCESS, pam_start("tst-pam_shells", "tstpamshells", &conv, &pamh));
|
||
+ ASSERT_EQ(PAM_SUCCESS, retval=pam_authenticate (pamh, 0));
|
||
+ ASSERT_EQ(PAM_SUCCESS, pam_end (pamh,retval));
|
||
+
|
||
+ // /bin/testnoshell is not defined in shell definition file(s)
|
||
+ ASSERT_EQ(PAM_SUCCESS, pam_start("tst-pam_shells", "tstnoshell", &conv, &pamh));
|
||
+ ASSERT_EQ(PAM_AUTH_ERR, retval=pam_authenticate (pamh, 0));
|
||
+ ASSERT_EQ(PAM_SUCCESS, pam_end (pamh,retval));
|
||
+
|
||
+ return 0;
|
||
+}
|
||
diff --git a/xtests/tst-pam_shells.pamd b/xtests/tst-pam_shells.pamd
|
||
new file mode 100644
|
||
index 00000000..6ad4f319
|
||
--- /dev/null
|
||
+++ b/xtests/tst-pam_shells.pamd
|
||
@@ -0,0 +1,2 @@
|
||
+#%PAM-1.0
|
||
+auth required pam_shells.so
|
||
diff --git a/xtests/tst-pam_shells.sh b/xtests/tst-pam_shells.sh
|
||
new file mode 100755
|
||
index 00000000..5093f689
|
||
--- /dev/null
|
||
+++ b/xtests/tst-pam_shells.sh
|
||
@@ -0,0 +1,11 @@
|
||
+#!/bin/sh
|
||
+
|
||
+/usr/sbin/groupadd tstpamshells1
|
||
+/usr/sbin/useradd -s /bin/testbash -G tstpamshells1 -p '!!' tstpamshells
|
||
+/usr/sbin/useradd -s /bin/testnoshell -G tstpamshells1 -p '!!' tstnoshell
|
||
+./tst-pam_shells
|
||
+RET=$?
|
||
+/usr/sbin/userdel -r tstpamshells 2> /dev/null
|
||
+/usr/sbin/userdel -r tstnoshell 2> /dev/null
|
||
+/usr/sbin/groupdel tstpamshells1 2> /dev/null
|
||
+exit $RET
|
||
--- /dev/null 2022-12-11 17:12:47.416337843 +0100
|
||
+++ b/doc/man/pam.conf.5.xml 2019-09-24 13:06:13.527781974 +0200
|
||
@@ -0,0 +1,50 @@
|
||
+<?xml version="1.0" encoding="UTF-8"?>
|
||
+<!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.conf'>
|
||
+
|
||
+ <refmeta>
|
||
+ <refentrytitle>pam.conf</refentrytitle>
|
||
+ <manvolnum>5</manvolnum>
|
||
+ <refmiscinfo class='setdesc'>Linux-PAM Manual</refmiscinfo>
|
||
+ </refmeta>
|
||
+
|
||
+ <refnamediv id='pam.conf-name'>
|
||
+ <refname>pam.conf</refname>
|
||
+ <refname>pam.d</refname>
|
||
+ <refpurpose>PAM configuration files</refpurpose>
|
||
+ </refnamediv>
|
||
+
|
||
+<!-- body begins here -->
|
||
+
|
||
+ <refsect1 id='pam.conf-description'>
|
||
+ <title>DESCRIPTION</title>
|
||
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
|
||
+ href="pam.conf-desc.xml"
|
||
+ xpointer='xpointer(//section[@id = "pam.conf-desc"]/*)' />
|
||
+
|
||
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
|
||
+ href="pam.conf-syntax.xml"
|
||
+ xpointer='xpointer(//section[@id = "pam.conf-syntax"]/*)' />
|
||
+
|
||
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
|
||
+ href="pam.conf-dir.xml"
|
||
+ xpointer='xpointer(//section[@id = "pam.conf-dir"]/*)' />
|
||
+ </refsect1>
|
||
+
|
||
+ <refsect1 id='pam.conf-see_also'>
|
||
+ <title>SEE ALSO</title>
|
||
+ <para>
|
||
+ <citerefentry>
|
||
+ <refentrytitle>pam</refentrytitle><manvolnum>3</manvolnum>
|
||
+ </citerefentry>,
|
||
+ <citerefentry>
|
||
+ <refentrytitle>PAM</refentrytitle><manvolnum>8</manvolnum>
|
||
+ </citerefentry>,
|
||
+ <citerefentry>
|
||
+ <refentrytitle>pam_start</refentrytitle><manvolnum>3</manvolnum>
|
||
+ </citerefentry>
|
||
+ </para>
|
||
+
|
||
+ </refsect1>
|
||
+</refentry>
|