diff options
author | Thorsten Kukuk <5908016+thkukuk@users.noreply.github.com> | 2019-09-16 17:17:49 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-09-16 17:17:49 +0200 |
commit | 65d6735c5949ec233df9813f734e918a93fa36cf (patch) | |
tree | c147e1f9ab27479abb3e2be94a2969aad6d87b68 | |
parent | 3a3e70739834cd5cbd17469907ef718c81ae40c0 (diff) | |
download | pam-65d6735c5949ec233df9813f734e918a93fa36cf.tar.gz pam-65d6735c5949ec233df9813f734e918a93fa36cf.tar.bz2 pam-65d6735c5949ec233df9813f734e918a93fa36cf.zip |
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.rules | 10 | ||||
-rw-r--r-- | configure.ac | 17 | ||||
-rw-r--r-- | doc/Makefile.am | 2 | ||||
-rw-r--r-- | doc/custom-html.xsl | 19 | ||||
-rw-r--r-- | doc/custom-man.xsl | 10 | ||||
-rw-r--r-- | doc/man/Makefile.am | 5 | ||||
-rw-r--r-- | doc/man/pam.8.xml | 17 | ||||
-rw-r--r-- | libpam/Makefile.am | 8 | ||||
-rw-r--r-- | libpam/pam_handlers.c | 47 | ||||
-rw-r--r-- | libpam/pam_modutil_searchkey.c | 37 | ||||
-rw-r--r-- | libpam/pam_private.h | 5 | ||||
-rw-r--r-- | modules/pam_securetty/Makefile.am | 8 | ||||
-rw-r--r-- | modules/pam_securetty/pam_securetty.8.xml | 15 | ||||
-rw-r--r-- | modules/pam_securetty/pam_securetty.c | 28 |
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; } |