summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Schubert <schubi@suse.de>2022-02-04 10:17:47 +0100
committerThorsten Kukuk <5908016+thkukuk@users.noreply.github.com>2022-06-30 11:48:52 +0200
commit8f9816b57e3a475fc2d2cbb106c188b778098f85 (patch)
tree28bf87239b620e643e996c0a0e07e27fe2ca605c
parenteb2149d82adc912d6d24aa1a004c5e5d58e00b2b (diff)
downloadlinux-pam-git-8f9816b57e3a475fc2d2cbb106c188b778098f85.tar.gz
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.
-rw-r--r--modules/pam_access/pam_access.8.xml29
-rw-r--r--modules/pam_access/pam_access.c135
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)