From 8f9816b57e3a475fc2d2cbb106c188b778098f85 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Fri, 4 Feb 2022 10:17:47 +0100 Subject: pam_access: use vendor specific access.conf as fallback Use the vendor directory as fallback for a distribution provided default config if there is no configuration in /etc. * pam_access.c: Take care about the fallback configuration in vendor directory. * pam_access.8.xml: Added description for vendor directory. --- modules/pam_access/pam_access.8.xml | 29 +++++++- modules/pam_access/pam_access.c | 135 +++++++++++++++++++++++++++++++----- 2 files changed, 145 insertions(+), 19 deletions(-) (limited to 'modules/pam_access') 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 $DISPLAY values, or PAM service names in case of non-networked logins. - + By default rules for access management are taken from config file /etc/security/access.conf if you don't specify another file. @@ -66,6 +66,26 @@ If a config file is explicitly specified with the option the files in the above directory are not parsed. + + By default rules for access management are taken from config file + /etc/security/access.conf or, if that one is not + present, the file %vendordir%/security/access.conf. + These settings can be overruled by setting in a config file explicitly + specified with the option. + Then individual *.conf files from the + /etc/security/access.d/ and + %vendordir%/security/access.d directories are read. + If /etc/security/access.d/@filename@.conf exists, then + %vendordir%/security/access.d/@filename@.conf will not be used. + All access.d/*.conf files are sorted by their + @filename@.conf 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 the files in the above directories are not parsed. + 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 @@ Default configuration file + + %vendordir%/security/access.conf + + Default configuration file if + /etc/security/access.conf does not exist. + + diff --git a/modules/pam_access/pam_access.c b/modules/pam_access/pam_access.c index 3cec542b..f7b47227 100644 --- a/modules/pam_access/pam_access.c +++ b/modules/pam_access/pam_access.c @@ -58,6 +58,10 @@ #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: */ @@ -154,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 *); @@ -888,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) { @@ -913,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) @@ -976,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) -- cgit v1.2.3