summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPer Jessen <per@jessen.ch>2022-04-22 18:15:36 +0200
committerDmitry V. Levin <ldv@altlinux.org>2022-06-30 20:59:39 +0000
commit42404548721c653317c911c83d885e2fc7fbca70 (patch)
treeaf3e4c4e2f38f7005880a6420b6fd5f88bc71f1a
parent543e122a80e25e9597cff418b837e214114bad1f (diff)
downloadlinux-pam-git-42404548721c653317c911c83d885e2fc7fbca70.tar.gz
pam_motd: do not rely on all filesystems providing a filetype
When using scandir() to look for MOTD files to display, we wrongly relied on all filesystems providing a filetype. This is a fix to divert to lstat() when we have no filetype. To maintain MT safety, it isn't possible to use lstat() in the scandir() filter function, so all of the filtering has been moved to an additional loop after scanning all the motd dirs. Also, remove superfluous alphasort from scandir(), we are doing a qsort() later. Resolves: https://github.com/linux-pam/linux-pam/issues/455
-rw-r--r--modules/pam_motd/pam_motd.c49
1 files changed, 40 insertions, 9 deletions
diff --git a/modules/pam_motd/pam_motd.c b/modules/pam_motd/pam_motd.c
index 6ac8cba2..5ca486e4 100644
--- a/modules/pam_motd/pam_motd.c
+++ b/modules/pam_motd/pam_motd.c
@@ -166,11 +166,6 @@ static int compare_strings(const void *a, const void *b)
}
}
-static int filter_dirents(const struct dirent *d)
-{
- return (d->d_type == DT_REG || d->d_type == DT_LNK);
-}
-
static void try_to_display_directories_with_overrides(pam_handle_t *pamh,
char **motd_dir_path_split, unsigned int num_motd_dirs, int report_missing)
{
@@ -199,8 +194,7 @@ static void try_to_display_directories_with_overrides(pam_handle_t *pamh,
for (i = 0; i < num_motd_dirs; i++) {
int rv;
- rv = scandir(motd_dir_path_split[i], &(dirscans[i]),
- filter_dirents, alphasort);
+ rv = scandir(motd_dir_path_split[i], &(dirscans[i]), NULL, NULL);
if (rv < 0) {
if (errno != ENOENT || report_missing) {
pam_syslog(pamh, LOG_ERR, "error scanning directory %s: %m",
@@ -215,6 +209,41 @@ static void try_to_display_directories_with_overrides(pam_handle_t *pamh,
if (dirscans_size_total == 0)
goto out;
+ /* filter out unwanted names, directories, and complement data with lstat() */
+ for (i = 0; i < num_motd_dirs; i++) {
+ struct dirent **d = dirscans[i];
+ for (unsigned int j = 0; j < dirscans_sizes[i]; j++) {
+ int rc;
+ char *fullpath;
+ struct stat s;
+
+ switch(d[j]->d_type) { /* the filetype determines how to proceed */
+ case DT_REG: /* regular files and */
+ case DT_LNK: /* symlinks */
+ continue; /* are good. */
+ case DT_UNKNOWN: /* for file systems that do not provide */
+ /* a filetype, we use lstat() */
+ if (join_dir_strings(&fullpath, motd_dir_path_split[i],
+ d[j]->d_name) <= 0)
+ break;
+ rc = lstat(fullpath, &s);
+ _pam_drop(fullpath); /* free the memory alloc'ed by join_dir_strings */
+ if (rc != 0) /* if the lstat() somehow failed */
+ break;
+
+ if (S_ISREG(s.st_mode) || /* regular files and */
+ S_ISLNK(s.st_mode)) continue; /* symlinks are good */
+ break;
+ case DT_DIR: /* We don't want directories */
+ default: /* nor anything else */
+ break;
+ }
+ _pam_drop(d[j]); /* free memory */
+ d[j] = NULL; /* indicate this one was dropped */
+ dirscans_size_total--;
+ }
+ }
+
/* Allocate space for all file names found in the directories, including duplicates. */
if ((dirnames_all = calloc(dirscans_size_total, sizeof(*dirnames_all))) == NULL) {
pam_syslog(pamh, LOG_CRIT, "failed to allocate dirname array");
@@ -225,8 +254,10 @@ static void try_to_display_directories_with_overrides(pam_handle_t *pamh,
unsigned int j;
for (j = 0; j < dirscans_sizes[i]; j++) {
- dirnames_all[i_dirnames] = dirscans[i][j]->d_name;
- i_dirnames++;
+ if (NULL != dirscans[i][j]) {
+ dirnames_all[i_dirnames] = dirscans[i][j]->d_name;
+ i_dirnames++;
+ }
}
}