From 7a84910896d5579bd9c016696224d7d69a307bd9 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Mon, 24 Feb 2020 18:19:57 +0100 Subject: New API call pam_start_confdir() To load PAM stack configurations from specified directory --- NEWS | 4 +- doc/man/pam_start.3.xml | 22 ++++++++- libpam/Makefile.am | 2 +- libpam/include/security/pam_appl.h | 5 ++ libpam/libpam.map | 5 ++ libpam/pam_handlers.c | 20 ++++++-- libpam/pam_private.h | 1 + libpam/pam_start.c | 36 +++++++++++++- tests/Makefile.am | 2 +- tests/confdir | 1 + tests/tst-pam_start_confdir.c | 99 ++++++++++++++++++++++++++++++++++++++ 11 files changed, 187 insertions(+), 10 deletions(-) create mode 100644 tests/confdir create mode 100644 tests/tst-pam_start_confdir.c diff --git a/NEWS b/NEWS index c00a75a1..23e606b4 100644 --- a/NEWS +++ b/NEWS @@ -29,7 +29,9 @@ Release 1.4.0 * pam_unix: Support for (gost-)yescrypt hashing methods * pam_unix: Use bcrypt b-variant when it bcrypt is chosen * pam_usertype: New module to tell if uid is in login.defs ranges - +* Added new API call pam_start_confdir() for special applications that + cannot use the system-default PAM configuration paths and need to + explicitly specify another path Release 1.3.1 * pam_motd: add support for a motd.d directory diff --git a/doc/man/pam_start.3.xml b/doc/man/pam_start.3.xml index 50a65a47..1d544e64 100644 --- a/doc/man/pam_start.3.xml +++ b/doc/man/pam_start.3.xml @@ -12,6 +12,7 @@ pam_start + pam_start_confdir initialization of PAM transaction @@ -27,6 +28,14 @@ const struct pam_conv *pam_conversation pam_handle_t **pamh + + int pam_start_confdir + const char *service_name + const char *user + const struct pam_conv *pam_conversation + const char *confdir + pam_handle_t **pamh + @@ -87,6 +96,17 @@ same time as long as pam_end was not called on it before. + + + The pam_start_confdir function behaves + like the pam_start function but it also + allows setting confdir argument with + a path to a directory to override the default + (/etc/pam.d) path for service policy + files. If the confdir is NULL, the function + works exactly the same as pam_start. + + RETURN VALUES @@ -111,7 +131,7 @@ PAM_SUCCESS - Transaction was successful created. + Transaction was successfully started. diff --git a/libpam/Makefile.am b/libpam/Makefile.am index 9d5c844d..bd3dc5d3 100644 --- a/libpam/Makefile.am +++ b/libpam/Makefile.am @@ -25,7 +25,7 @@ include_HEADERS = include/security/_pam_compat.h \ noinst_HEADERS = pam_prelude.h pam_private.h pam_tokens.h \ pam_modutil_private.h include/pam_cc_compat.h -libpam_la_LDFLAGS = -no-undefined -version-info 84:2:84 +libpam_la_LDFLAGS = -no-undefined -version-info 85:1:85 libpam_la_LIBADD = @LIBAUDIT@ $(LIBPRELUDE_LIBS) $(ECONF_LIBS) @LIBDL@ if HAVE_VERSIONING diff --git a/libpam/include/security/pam_appl.h b/libpam/include/security/pam_appl.h index d4172c69..cf97a493 100644 --- a/libpam/include/security/pam_appl.h +++ b/libpam/include/security/pam_appl.h @@ -24,6 +24,11 @@ pam_start(const char *service_name, const char *user, const struct pam_conv *pam_conversation, pam_handle_t **pamh); +extern int PAM_NONNULL((1,3,5)) +pam_start_confdir(const char *service_name, const char *user, + const struct pam_conv *pam_conversation, + const char *confdir, pam_handle_t **pamh); + extern int PAM_NONNULL((1)) pam_end(pam_handle_t *pamh, int pam_status); diff --git a/libpam/libpam.map b/libpam/libpam.map index 74fb55b2..c9690a91 100644 --- a/libpam/libpam.map +++ b/libpam/libpam.map @@ -77,3 +77,8 @@ LIBPAM_MODUTIL_1.3.2 { global: pam_modutil_search_key; } LIBPAM_MODUTIL_1.1.9; + +LIBPAM_1.4 { + global: + pam_start_confdir; +} LIBPAM_1.0; diff --git a/libpam/pam_handlers.c b/libpam/pam_handlers.c index 8e513da3..5dff58c2 100644 --- a/libpam/pam_handlers.c +++ b/libpam/pam_handlers.c @@ -285,7 +285,7 @@ _pam_open_config_file(pam_handle_t *pamh , PAM_CONFIG_DIST2_DF #endif }; - char *p; + char *p = NULL; FILE *f; size_t i; @@ -296,14 +296,21 @@ _pam_open_config_file(pam_handle_t *pamh pam_syslog(pamh, LOG_CRIT, "strdup failed"); return PAM_BUF_ERR; } + } else if (pamh->confdir != NULL) { + if (asprintf (&p, "%s/%s", pamh->confdir, service) < 0) { + pam_syslog(pamh, LOG_CRIT, "asprintf failed"); + return PAM_BUF_ERR; + } + } - f = fopen(service, "r"); + if (p != NULL) { + D(("opening %s", p)); + f = fopen(p, "r"); if (f != NULL) { *path = p; *file = f; return PAM_SUCCESS; } - _pam_drop(p); return PAM_ABORT; } @@ -313,6 +320,7 @@ _pam_open_config_file(pam_handle_t *pamh pam_syslog(pamh, LOG_CRIT, "asprintf failed"); return PAM_BUF_ERR; } + D(("opening %s", p)); f = fopen(p, "r"); if (f != NULL) { @@ -438,7 +446,8 @@ int _pam_init_handlers(pam_handle_t *pamh) struct stat test_d; /* Is there a PAM_CONFIG_D directory? */ - if ((stat(PAM_CONFIG_D, &test_d) == 0 && S_ISDIR(test_d.st_mode)) || + if (pamh->confdir != NULL || + (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)) #ifdef PAM_CONFIG_DIST2_D || (stat(PAM_CONFIG_DIST2_D, &test_d) == 0 @@ -471,7 +480,8 @@ int _pam_init_handlers(pam_handle_t *pamh) #ifdef PAM_READ_BOTH_CONFS D(("checking %s", PAM_CONFIG)); - if ((f = fopen(PAM_CONFIG,"r")) != NULL) { + if (pamh->confdir == NULL + && (f = fopen(PAM_CONFIG,"r")) != NULL) { retval = _pam_parse_conf_file(pamh, f, NULL, PAM_T_ANY, 0, 1); fclose(f); } else diff --git a/libpam/pam_private.h b/libpam/pam_private.h index ed02bb02..69d2ef44 100644 --- a/libpam/pam_private.h +++ b/libpam/pam_private.h @@ -178,6 +178,7 @@ struct pam_handle { int audit_state; /* keep track of reported audit messages */ #endif int authtok_verified; + char *confdir; }; /* Values for select arg to _pam_dispatch() */ diff --git a/libpam/pam_start.c b/libpam/pam_start.c index e27c64bb..59d06224 100644 --- a/libpam/pam_start.c +++ b/libpam/pam_start.c @@ -15,10 +15,11 @@ #include #include -int pam_start ( +static int _pam_start_internal ( const char *service_name, const char *user, const struct pam_conv *pam_conversation, + const char *confdir, pam_handle_t **pamh) { D(("called pam_start: [%s] [%s] [%p] [%p]" @@ -80,6 +81,18 @@ int pam_start ( } else (*pamh)->user = NULL; + if (confdir) { + if (((*pamh)->confdir = _pam_strdup(confdir)) == NULL) { + pam_syslog(*pamh, LOG_CRIT, + "pam_start: _pam_strdup failed for confdir"); + _pam_drop((*pamh)->service_name); + _pam_drop((*pamh)->user); + _pam_drop(*pamh); + return (PAM_BUF_ERR); + } + } else + (*pamh)->confdir = NULL; + (*pamh)->tty = NULL; (*pamh)->prompt = NULL; /* prompt for pam_get_user() */ (*pamh)->ruser = NULL; @@ -140,3 +153,24 @@ int pam_start ( return PAM_SUCCESS; } + +int pam_start_confdir ( + const char *service_name, + const char *user, + const struct pam_conv *pam_conversation, + const char *confdir, + pam_handle_t **pamh) +{ + return _pam_start_internal(service_name, user, pam_conversation, + confdir, pamh); +} + +int pam_start ( + const char *service_name, + const char *user, + const struct pam_conv *pam_conversation, + pam_handle_t **pamh) +{ + return _pam_start_internal(service_name, user, pam_conversation, + NULL, pamh); +} diff --git a/tests/Makefile.am b/tests/Makefile.am index c44a2ee7..5b0e78d7 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -12,7 +12,7 @@ TESTS = tst-pam_start tst-pam_end tst-pam_fail_delay tst-pam_open_session \ tst-pam_close_session tst-pam_acct_mgmt tst-pam_authenticate \ tst-pam_chauthtok tst-pam_setcred tst-pam_get_item tst-pam_set_item \ tst-pam_getenvlist tst-pam_get_user tst-pam_set_data \ - tst-pam_mkargv + tst-pam_mkargv tst-pam_start_confdir check_PROGRAMS = ${TESTS} tst-dlopen diff --git a/tests/confdir b/tests/confdir new file mode 100644 index 00000000..3883c869 --- /dev/null +++ b/tests/confdir @@ -0,0 +1 @@ +# This is an empty pam service file for tst-pam_start_confdir diff --git a/tests/tst-pam_start_confdir.c b/tests/tst-pam_start_confdir.c new file mode 100644 index 00000000..e40b6e70 --- /dev/null +++ b/tests/tst-pam_start_confdir.c @@ -0,0 +1,99 @@ +/* + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include + + +int +main (void) +{ + const char *service = "confdir"; + const char *xservice = "nonexistent-service"; + const char *user = "root"; + const char *confdir; + const char *xconfdir = "/nonexistent-confdir"; + struct pam_conv conv; + pam_handle_t *pamh; + int retval; + + confdir = getenv("srcdir"); + + if (confdir == NULL) + { + fprintf (stderr, "Error: srcdir not set\n"); + return 1; + } + + /* 1: check with valid arguments */ + retval = pam_start_confdir (service, user, &conv, confdir, &pamh); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, "pam_start_confdir (%s, %s, &conv, %s, &pamh) returned %d\n", + service, user, confdir, retval); + return 1; + } + else if (pamh == NULL) + { + fprintf (stderr, "pam_start_confdir (%s, %s, &conv, %s, &pamh) returned NULL for pamh\n", + service, user, confdir); + return 1; + } + + /* 2: check with invalid service */ + retval = pam_start_confdir (xservice, user, &conv, confdir, &pamh); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, "pam_start_confdir (%s, %s, &conv, %s, &pamh) incorrectly succeeded\n", + xservice, user, confdir); + return 1; + } + + /* 3: check with invalid confdir */ + retval = pam_start_confdir (service, user, &conv, xconfdir, &pamh); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, "pam_start_confdir (%s, %s, &conv, %s, &pamh) incorrectly succeeded\n", + service, user, xconfdir); + return 1; + } + + return 0; +} -- cgit v1.2.3