diff options
author | Stefan Schubert <schubi@suse.de> | 2022-02-04 10:17:47 +0100 |
---|---|---|
committer | Thorsten Kukuk <5908016+thkukuk@users.noreply.github.com> | 2022-06-30 11:48:52 +0200 |
commit | 8f9816b57e3a475fc2d2cbb106c188b778098f85 (patch) | |
tree | 28bf87239b620e643e996c0a0e07e27fe2ca605c /modules/pam_access | |
parent | eb2149d82adc912d6d24aa1a004c5e5d58e00b2b (diff) | |
download | pam-8f9816b57e3a475fc2d2cbb106c188b778098f85.tar.gz pam-8f9816b57e3a475fc2d2cbb106c188b778098f85.tar.bz2 pam-8f9816b57e3a475fc2d2cbb106c188b778098f85.zip |
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.
Diffstat (limited to 'modules/pam_access')
-rw-r--r-- | modules/pam_access/pam_access.8.xml | 29 | ||||
-rw-r--r-- | modules/pam_access/pam_access.c | 135 |
2 files changed, 145 insertions, 19 deletions
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 <varname>$DISPLAY</varname> values, or PAM service names in case of non-networked logins. </para> - <para> + <para condition="without_vendordir"> By default rules for access management are taken from config file <filename>/etc/security/access.conf</filename> if you don't specify another file. @@ -66,6 +66,26 @@ If a config file is explicitly specified with the <option>accessfile</option> option the files in the above directory are not parsed. </para> + <para condition="with_vendordir"> + By default rules for access management are taken from config file + <filename>/etc/security/access.conf</filename> or, if that one is not + present, the file <filename>%vendordir%/security/access.conf</filename>. + These settings can be overruled by setting in a config file explicitly + specified with the <option>accessfile</option> option. + Then individual <filename>*.conf</filename> files from the + <filename>/etc/security/access.d/</filename> and + <filename>%vendordir%/security/access.d</filename> directories are read. + If <filename>/etc/security/access.d/@filename@.conf</filename> exists, then + <filename>%vendordir%/security/access.d/@filename@.conf</filename> will not be used. + All <filename>access.d/*.conf</filename> files are sorted by their + <filename>@filename@.conf</filename> 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>accessfile</option> + option the files in the above directories are not parsed. + </para> <para> 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 @@ <para>Default configuration file</para> </listitem> </varlistentry> + <varlistentry condition="with_vendordir"> + <term><filename>%vendordir%/security/access.conf</filename></term> + <listitem> + <para>Default configuration file if + <filename>/etc/security/access.conf</filename> does not exist.</para> + </listitem> + </varlistentry> </variablelist> </refsect1> 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) |