Hi,
attached an updated patch with the typo fixes and two files missing
in the last commit.
If somebody wants to rewrite our Makefiles how we use variables there,
fine with me, but that is not part of this patch and should be done
separately and consistent for the whole project.
Thorsten
On Thu, Sep 05, Thorsten Kukuk wrote:
Hi,
attached patch is identical to pull request #136.
Background: on openSUSE we want to split the configuration files:
the distribution provided configuration files should be stored in
/usr/etc, while /etc should longterm only contain host specific
configuration files and user made modifications.
The following patch introduces a configuareable vendordir, where
Linux-PAM should look for pam.d, login.defs and securetty, if there
is no such configuration file in /etc. libeconf is then used to
merge login.defs transparently for the application like systemd is
doing.
Thorsten
--
Thorsten Kukuk, Distinguished Engineer, Senior Architect SLES & MicroOS
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nuernberg, Germany
Managing Director: Felix Imendoerffer (HRB 247165, AG München)
>From 69d74f61e8e0b11b45a5262c84da16cb3683df60 Mon Sep 17 00:00:00
2001
From: Thorsten Kukuk <kukuk(a)suse.com>
Date: Thu, 5 Sep 2019 15:53:52 +0200
Subject: [PATCH 1/1] Add support for a vendor directory and libeconf
With this, it is possible for Linux distributors to store their
supplied default configuration files somewhere below /usr, while
/etc only contains the changes made by the user. The new option
--enable-vendordir defines where Linux-PAM should additional look
for pam.d/*, login.defs and securetty if this files are not in /etc.
libeconf is a key/value configuration file reading library, which
handles the split of configuration files in different locations
and merges them transparently for the application.
---
Make.xml.rules | 10 +++++-----
configure.ac | 17 +++++++++++++++++
doc/Makefile.am | 2 ++
doc/man/Makefile.am | 5 +++++
doc/man/pam.8.xml | 17 +++++++++++++++--
libpam/Makefile.am | 8 ++++++--
libpam/pam_handlers.c | 25 +++++++++++++++++++++++--
libpam/pam_modutil_searchkey.c | 30 ++++++++++++++++++++++++++++++
libpam/pam_private.h | 5 +++++
modules/pam_securetty/Makefile.am | 8 ++++++++
modules/pam_securetty/pam_securetty.8.xml | 15 +++++++++------
modules/pam_securetty/pam_securetty.c | 28 +++++++++++++++++++++++++---
12 files changed, 150 insertions(+), 20 deletions(-)
diff --git a/Make.xml.rules b/Make.xml.rules
index bee30cd..d19a02e 100644
--- a/Make.xml.rules
+++ b/Make.xml.rules
@@ -3,22 +3,22 @@
#
README: README.xml
- $(XSLTPROC) --path $(srcdir) --xinclude --stringparam generate.toc "none"
--nonet
http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl $< |
$(BROWSER) > $(srcdir)/$@
+ $(XSLTPROC) --path $(srcdir) --xinclude --stringparam generate.toc "none"
$(XSLTPROC_CUSTOM) --nonet $(top_srcdir)/doc/custom-html.xsl $< | $(BROWSER) >
$(srcdir)/$@
%.1: %.1.xml
$(XMLLINT) --nonet --xinclude --postvalid --noout $<
- $(XSLTPROC) -o $(srcdir)/$@ --path $(srcdir) --xinclude --nonet
http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
+ $(XSLTPROC) -o $(srcdir)/$@ --path $(srcdir) --xinclude $(XSLTPROC_CUSTOM) --nonet
$(top_srcdir)/doc/custom-man.xsl $<
%.3: %.3.xml
$(XMLLINT) --nonet --xinclude --postvalid --noout $<
- $(XSLTPROC) -o $(srcdir)/$@ --path $(srcdir) --xinclude --nonet
http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
+ $(XSLTPROC) -o $(srcdir)/$@ --path $(srcdir) --xinclude $(XSLTPROC_CUSTOM) --nonet
$(top_srcdir)/doc/custom-man.xsl $<
%.5: %.5.xml
$(XMLLINT) --nonet --xinclude --postvalid --noout $<
- $(XSLTPROC) -o $(srcdir)/$@ --path $(srcdir) --xinclude --nonet
http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
+ $(XSLTPROC) -o $(srcdir)/$@ --path $(srcdir) --xinclude $(XSLTPROC_CUSTOM) --nonet
$(top_srcdir)/doc/custom-man.xsl $<
%.8: %.8.xml
$(XMLLINT) --nonet --xinclude --postvalid --noout $<
- $(XSLTPROC) -o $(srcdir)/$@ --path $(srcdir) --xinclude --nonet
http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
+ $(XSLTPROC) -o $(srcdir)/$@ --path $(srcdir) --xinclude $(XSLTPROC_CUSTOM) --nonet
$(top_srcdir)/doc/custom-man.xsl $<
#CLEANFILES += $(man_MANS) README
diff --git a/configure.ac b/configure.ac
index e4995fc..c557d2c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -504,6 +504,23 @@ if test ! -z "$LIBSELINUX" ; then
LIBS=$BACKUP_LIBS
fi
+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
+fi
+AC_SUBST([ECONF_CFLAGS])
+AC_SUBST([ECONF_LIBS])
+AC_ARG_ENABLE([vendordir],
+ AS_HELP_STRING([--enable-vendordir=DIR], [Direcotry for istribution provided
configuration files]),,[])
+AC_SUBST([VENDORDIR], [$enable_vendordir])
+AM_CONDITIONAL([HAVE_VENDORDIR], [test "x$enable_vendordir" != x])
+
dnl Checks for header files.
AC_HEADER_DIRENT
AC_HEADER_STDC
diff --git a/doc/Makefile.am b/doc/Makefile.am
index f4762f2..38319f5 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -8,6 +8,8 @@ CLEANFILES = *~
dist_html_DATA = index.html
+EXTRA_DIST = custom-html.xsl custom-man.xsl
+
#######################################################
releasedocs: all
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
index 78c891d..8e76897 100644
--- a/doc/man/Makefile.am
+++ b/doc/man/Makefile.am
@@ -59,5 +59,10 @@ pam.d.5: pam.conf.5
pam_get_item.3: pam_item_types_std.inc.xml pam_item_types_ext.inc.xml
pam_set_data.3: pam_item_types_std.inc.xml pam_item_types_ext.inc.xml
pam.conf.5: pam.conf-desc.xml pam.conf-dir.xml pam.conf-syntax.xml
+if HAVE_VENDORDIR
+XSLTPROC_CUSTOM = --stringparam vendordir $(VENDORDIR)
+else
+XSLTPROC_CUSTOM = --stringparam vendordir "<vendordir>"
+endif
-include $(top_srcdir)/Make.xml.rules
endif
diff --git a/doc/man/pam.8.xml b/doc/man/pam.8.xml
index 9839def..ce2d91b 100644
--- a/doc/man/pam.8.xml
+++ b/doc/man/pam.8.xml
@@ -53,11 +53,12 @@
<para>
Vendor-supplied PAM configuration files might be installed in
- the system directory <filename>/usr/lib/pam.d/</filename> instead
+ the system directory <filename>/usr/lib/pam.d/</filename> or
+ an configurable vendor specific directory instead
of the machine configuration directory
<filename>/etc/pam.d/</filename>.
If no machine configuration file is found, the vendor-supplied file
is used. All files in <filename>/etc/pam.d/</filename> override
- files with the same name in <filename>/usr/lib/pam.d/</filename>.
+ files with the same name in other directories.
</para>
<para>From the point of view of the system administrator, for whom this
@@ -157,6 +158,18 @@ closing hook for modules to affect the services available to a
user.</para>
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <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.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/libpam/Makefile.am b/libpam/Makefile.am
index 875031e..1c59140 100644
--- a/libpam/Makefile.am
+++ b/libpam/Makefile.am
@@ -3,10 +3,14 @@
#
AM_CFLAGS = -DDEFAULT_MODULE_PATH=\"$(SECUREDIR)/\" -DLIBPAM_COMPILE \
- -I$(srcdir)/include $(LIBPRELUDE_CFLAGS) -DPAM_VERSION=\"$(VERSION)\"
+ -I$(srcdir)/include $(LIBPRELUDE_CFLAGS) \
+ -DPAM_VERSION=\"$(VERSION)\" @ECONF_CFLAGS@
if HAVE_LIBSELINUX
AM_CFLAGS += -D"WITH_SELINUX"
endif
+if HAVE_VENDORDIR
+ AM_CFLAGS += -DVENDORDIR=\"@VENDORDIR@\"
+endif
CLEANFILES = *~
@@ -21,7 +25,7 @@ noinst_HEADERS = pam_prelude.h pam_private.h pam_tokens.h \
pam_modutil_private.h
libpam_la_LDFLAGS = -no-undefined -version-info 84:2:84
-libpam_la_LIBADD = @LIBAUDIT@ $(LIBPRELUDE_LIBS) @LIBDL@
+libpam_la_LIBADD = @LIBAUDIT@ $(LIBPRELUDE_LIBS) @LIBDL@ @ECONF_LIBS@
if HAVE_VERSIONING
libpam_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libpam.map
diff --git a/libpam/pam_handlers.c b/libpam/pam_handlers.c
index 106ef7c..51a5f7b 100644
--- a/libpam/pam_handlers.c
+++ b/libpam/pam_handlers.c
@@ -282,7 +282,6 @@ _pam_open_config_file(pam_handle_t *pamh
{
char *p;
FILE *f;
- int err = 0;
/* Absolute path */
if (service[0] == '/') {
@@ -329,6 +328,23 @@ _pam_open_config_file(pam_handle_t *pamh
*file = f;
return PAM_SUCCESS;
}
+
+#ifdef PAM_CONFIG_DIST2_D
+ /* System Configuration /<vendordir>/pam.d/ */
+ _pam_drop(p);
+ if (asprintf (&p, PAM_CONFIG_DIST2_DF, service) < 0) {
+ pam_syslog(pamh, LOG_CRIT, "asprintf failed");
+ return PAM_BUF_ERR;
+ }
+ D(("opening %s", p));
+ f = fopen(p, "r");
+ if (f != NULL) {
+ *path = p;
+ *file = f;
+ return PAM_SUCCESS;
+ }
+#endif
+
_pam_drop(p);
return PAM_ABORT;
@@ -447,7 +463,12 @@ int _pam_init_handlers(pam_handle_t *pamh)
/* Is there a PAM_CONFIG_D directory? */
if ((stat(PAM_CONFIG_D, &test_d) == 0 && S_ISDIR(test_d.st_mode)) ||
- (stat(PAM_CONFIG_DIST_D, &test_d) == 0 && S_ISDIR(test_d.st_mode))) {
+ (stat(PAM_CONFIG_DIST_D, &test_d) == 0 && S_ISDIR(test_d.st_mode))
+#ifdef PAM_CONFIG_DIST2_D
+ || (stat(PAM_CONFIG_DIST2_D, &test_d) == 0
+ && S_ISDIR(test_d.st_mode))
+#endif
+ ) {
char *path = NULL;
int read_something=0;
diff --git a/libpam/pam_modutil_searchkey.c b/libpam/pam_modutil_searchkey.c
index 338b44f..1daed8d 100644
--- a/libpam/pam_modutil_searchkey.c
+++ b/libpam/pam_modutil_searchkey.c
@@ -13,9 +13,34 @@
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
+#ifdef USE_ECONF
+#include <libeconf.h>
+#endif
#define BUF_SIZE 8192
+#ifdef USE_ECONF
+#define LOGIN_DEFS "/etc/login.defs"
+
+static char *
+econf_search_key (const char *name, const char *suffix, const char *key)
+{
+ econf_file *key_file = NULL;
+ char *val;
+
+ if (econf_readDirs (&key_file, "/usr/etc", "/etc", name,
suffix, " \t", "#"))
+ return NULL;
+
+ if (econf_getStringValue (key_file, NULL, key, &val))
+ return NULL;
+
+ econf_free (key_file);
+
+ return val;
+}
+
+#endif
+
/* lookup a value for key in login.defs file or similar key value format */
char *
pam_modutil_search_key(pam_handle_t *pamh UNUSED,
@@ -27,6 +52,11 @@ pam_modutil_search_key(pam_handle_t *pamh UNUSED,
size_t buflen = 0;
char *retval = NULL;
+#ifdef USE_ECONF
+ if (strcmp (file_name, LOGIN_DEFS) == 0)
+ return econf_search_key ("login", ".defs", key);
+#endif
+
fp = fopen(file_name, "r");
if (NULL == fp)
return NULL;
diff --git a/libpam/pam_private.h b/libpam/pam_private.h
index 58a26f5..8cb7752 100644
--- a/libpam/pam_private.h
+++ b/libpam/pam_private.h
@@ -29,6 +29,11 @@
#define PAM_CONFIG_DF "/etc/pam.d/%s"
#define PAM_CONFIG_DIST_D "/usr/lib/pam.d"
#define PAM_CONFIG_DIST_DF "/usr/lib/pam.d/%s"
+#ifdef VENDORDIR
+#define PAM_CONFIG_DIST2_D VENDORDIR"/pam.d"
+#define PAM_CONFIG_DIST2_DF VENDORDIR"/pam.d/%s"
+#endif
+
#define PAM_DEFAULT_SERVICE "other" /* lower case */
diff --git a/modules/pam_securetty/Makefile.am b/modules/pam_securetty/Makefile.am
index 30cc879..94ba04d 100644
--- a/modules/pam_securetty/Makefile.am
+++ b/modules/pam_securetty/Makefile.am
@@ -20,6 +20,9 @@ AM_LDFLAGS = -no-undefined -avoid-version -module
if HAVE_VERSIONING
AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
endif
+if HAVE_VENDORDIR
+ AM_CFLAGS += -DVENDORDIR=\"@VENDORDIR@\"
+endif
securelib_LTLIBRARIES = pam_securetty.la
pam_securetty_la_LIBADD = $(top_builddir)/libpam/libpam.la
@@ -27,5 +30,10 @@ pam_securetty_la_LIBADD = $(top_builddir)/libpam/libpam.la
if ENABLE_REGENERATE_MAN
noinst_DATA = README
README: pam_securetty.8.xml
+if HAVE_VENDORDIR
+XSLTPROC_CUSTOM = --stringparam vendordir $(VENDORDIR)
+else
+XSLTPROC_CUSTOM = --stringparam vendordir "<vendordir>"
+endif
-include $(top_srcdir)/Make.xml.rules
endif
diff --git a/modules/pam_securetty/pam_securetty.8.xml
b/modules/pam_securetty/pam_securetty.8.xml
index 48215f5..ce6b6f4 100644
--- a/modules/pam_securetty/pam_securetty.8.xml
+++ b/modules/pam_securetty/pam_securetty.8.xml
@@ -31,9 +31,12 @@
<para>
pam_securetty is a PAM module that allows root logins only if the
user is logging in on a "secure" tty, as defined by the listing
- in <filename>/etc/securetty</filename>. pam_securetty also checks
- to make sure that <filename>/etc/securetty</filename> is a plain
- file and not world writable. It will also allow root logins on
+ in the <filename>securetty</filename> file. pam_securetty checks at
+ first, if <filename>/etc/securetty</filename> exists. If not and
+ it was build with vendordir support, it will use
+ <filename>%vendordir%/securetty</filename>. pam_securetty also
+ checks that the <filename>securetty</filename> files are plain
+ files and not world writable. It will also allow root logins on
the tty specified with <option>console=</option> switch on the
kernel command line and on ttys from the
<filename>/sys/class/tty/console/active</filename>.
@@ -73,7 +76,7 @@
Do not automatically allow root logins on the kernel console
device, as specified on the kernel command line or by the sys file,
if it is not also specified in the
- <filename>/etc/securetty</filename> file.
+ <filename>securetty</filename> file.
</para>
</listitem>
</varlistentry>
@@ -106,7 +109,7 @@
<para>
Authentication is rejected. Either root is attempting to
log in via an unacceptable device, or the
- <filename>/etc/securetty</filename> file is world writable or
+ <filename>securetty</filename> file is world writable or
not a normal file.
</para>
</listitem>
@@ -127,7 +130,7 @@
<para>
An error occurred while the module was determining the
user's name or tty, or the module could not open
- <filename>/etc/securetty</filename>.
+ the <filename>securetty</filename> file.
</para>
</listitem>
</varlistentry>
diff --git a/modules/pam_securetty/pam_securetty.c
b/modules/pam_securetty/pam_securetty.c
index cb1da25..e8a9273 100644
--- a/modules/pam_securetty/pam_securetty.c
+++ b/modules/pam_securetty/pam_securetty.c
@@ -1,6 +1,9 @@
/* pam_securetty module */
#define SECURETTY_FILE "/etc/securetty"
+#ifdef VENDORDIR
+#define SECURETTY2_FILE VENDORDIR"/securetty"
+#endif
#define TTY_PREFIX "/dev/"
#define CMDLINE_FILE "/proc/cmdline"
#define CONSOLEACTIVE_FILE "/sys/class/tty/console/active"
@@ -25,6 +28,7 @@
#include <string.h>
#include <ctype.h>
#include <limits.h>
+#include <errno.h>
/*
* here, we make a definition for the externally accessible function
@@ -70,6 +74,7 @@ securetty_perform_check (pam_handle_t *pamh, int ctrl,
const char *function_name)
{
int retval = PAM_AUTH_ERR;
+ const char *securettyfile;
const char *username;
const char *uttyname;
const void *void_uttyname;
@@ -111,10 +116,27 @@ securetty_perform_check (pam_handle_t *pamh, int ctrl,
}
if (stat(SECURETTY_FILE, &ttyfileinfo)) {
+#ifdef VENDORDIR
+ if (errno == ENOENT) {
+ if (stat(SECURETTY2_FILE, &ttyfileinfo)) {
+ pam_syslog(pamh, LOG_NOTICE,
+ "Couldn't open %s: %m", SECURETTY2_FILE);
+ return PAM_SUCCESS; /* for compatibility with old securetty handling,
+ this needs to succeed. But we still log the
+ error. */
+ }
+ securettyfile = SECURETTY2_FILE;
+ } else {
+#endif
pam_syslog(pamh, LOG_NOTICE, "Couldn't open %s: %m", SECURETTY_FILE);
return PAM_SUCCESS; /* for compatibility with old securetty handling,
this needs to succeed. But we still log the
error. */
+#ifdef VENDORDIR
+ }
+#endif
+ } else {
+ securettyfile = SECURETTY_FILE;
}
if ((ttyfileinfo.st_mode & S_IWOTH) || !S_ISREG(ttyfileinfo.st_mode)) {
@@ -122,13 +144,13 @@ securetty_perform_check (pam_handle_t *pamh, int ctrl,
normal file, return error */
pam_syslog(pamh, LOG_ERR,
"%s is either world writable or not a normal file",
- SECURETTY_FILE);
+ securettyfile);
return PAM_AUTH_ERR;
}
- ttyfile = fopen(SECURETTY_FILE,"r");
+ ttyfile = fopen(securettyfile,"r");
if (ttyfile == NULL) { /* Check that we opened it successfully */
- pam_syslog(pamh, LOG_ERR, "Error opening %s: %m", SECURETTY_FILE);
+ pam_syslog(pamh, LOG_ERR, "Error opening %s: %m", securettyfile);
return PAM_SERVICE_ERR;
}
--
2.16.4
--
Thorsten Kukuk, Distinguished Engineer, Senior Architect SLES & MicroOS
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nuernberg, Germany
Managing Director: Felix Imendoerffer (HRB 247165, AG München)