summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThorsten Kukuk <5908016+thkukuk@users.noreply.github.com>2019-09-16 17:17:49 +0200
committerGitHub <noreply@github.com>2019-09-16 17:17:49 +0200
commit65d6735c5949ec233df9813f734e918a93fa36cf (patch)
treec147e1f9ab27479abb3e2be94a2969aad6d87b68
parent3a3e70739834cd5cbd17469907ef718c81ae40c0 (diff)
Add support for a vendor directory and libeconf (#136)
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.
-rw-r--r--Make.xml.rules10
-rw-r--r--configure.ac17
-rw-r--r--doc/Makefile.am2
-rw-r--r--doc/custom-html.xsl19
-rw-r--r--doc/custom-man.xsl10
-rw-r--r--doc/man/Makefile.am5
-rw-r--r--doc/man/pam.8.xml17
-rw-r--r--libpam/Makefile.am8
-rw-r--r--libpam/pam_handlers.c47
-rw-r--r--libpam/pam_modutil_searchkey.c37
-rw-r--r--libpam/pam_private.h5
-rw-r--r--modules/pam_securetty/Makefile.am8
-rw-r--r--modules/pam_securetty/pam_securetty.8.xml15
-rw-r--r--modules/pam_securetty/pam_securetty.c28
14 files changed, 185 insertions, 43 deletions
diff --git a/Make.xml.rules b/Make.xml.rules
index bee30cda..d19a02ef 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 e4995fc9..62b98c73 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], [Directory for distribution 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 f4762f2d..38319f5b 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/custom-html.xsl b/doc/custom-html.xsl
new file mode 100644
index 00000000..081beaf6
--- /dev/null
+++ b/doc/custom-html.xsl
@@ -0,0 +1,19 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ 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:param name="vendordir"/>
+
+ <xsl:template match="filename">
+ <xsl:variable name="replacements">
+ <ss:substitution oldstring="%vendordir%" newstring="{$vendordir}" />
+ </xsl:variable>
+ <xsl:call-template name="apply-string-subst-map">
+ <xsl:with-param name="content" select="."/>
+ <xsl:with-param name="map.contents" select="exsl:node-set($replacements)/*" />
+ </xsl:call-template>
+ </xsl:template>
+</xsl:stylesheet>
+
diff --git a/doc/custom-man.xsl b/doc/custom-man.xsl
new file mode 100644
index 00000000..bf01d583
--- /dev/null
+++ b/doc/custom-man.xsl
@@ -0,0 +1,10 @@
+<?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/docbook.xsl"/>
+ <xsl:param name="vendordir"/>
+
+ <xsl:param name="man.string.subst.map.local.pre">
+ <ss:substitution oldstring="%vendordir%" newstring="{$vendordir}" />
+ </xsl:param>
+</xsl:stylesheet>
+
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
index 78c891df..8e76897e 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 9839defb..464af0e5 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
+ a 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 875031ed..ba57b98e 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) $(ECONF_CFLAGS) \
+ -DPAM_VERSION=\"$(VERSION)\" -DSYSCONFDIR=\"$(sysconfdir)\"
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) $(ECONF_LIBS) @LIBDL@
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 106ef7c2..8e513da3 100644
--- a/libpam/pam_handlers.c
+++ b/libpam/pam_handlers.c
@@ -280,9 +280,14 @@ _pam_open_config_file(pam_handle_t *pamh
, char **path
, FILE **file)
{
+ const char *pamd_dirs[] = { PAM_CONFIG_DF, PAM_CONFIG_DIST_DF
+#ifdef VENDORDIR
+ , PAM_CONFIG_DIST2_DF
+#endif
+ };
char *p;
FILE *f;
- int err = 0;
+ size_t i;
/* Absolute path */
if (service[0] == '/') {
@@ -303,33 +308,20 @@ _pam_open_config_file(pam_handle_t *pamh
return PAM_ABORT;
}
- /* Local Machine Configuration /etc/pam.d/ */
- if (asprintf (&p, PAM_CONFIG_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;
- }
-
- /* System Configuration /usr/lib/pam.d/ */
- _pam_drop(p);
- if (asprintf (&p, PAM_CONFIG_DIST_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) {
+ for (i = 0; i < sizeof (pamd_dirs)/sizeof (char *); i++) {
+ if (asprintf (&p, pamd_dirs[i], 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;
+ }
+ _pam_drop(p);
}
- _pam_drop(p);
return PAM_ABORT;
}
@@ -447,7 +439,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 338b44fd..4e565974 100644
--- a/libpam/pam_modutil_searchkey.c
+++ b/libpam/pam_modutil_searchkey.c
@@ -13,9 +13,41 @@
#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"
+
+#ifndef VENDORDIR
+#define VENDORDIR NULL
+#endif
+
+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, VENDORDIR, SYSCONFDIR, name, suffix,
+ " \t", "#"))
+ return NULL;
+
+ if (econf_getStringValue (key_file, NULL, key, &val)) {
+ econf_free (key_file);
+ 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 +59,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 58a26f58..8cb77528 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 30cc879a..9bcbbd95 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 48215f5f..b5e83691 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 built 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 cb1da252..e8a9273b 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;
}