diff options
author | Steve Langasek <steve.langasek@canonical.com> | 2020-08-11 14:54:29 -0700 |
---|---|---|
committer | Steve Langasek <steve.langasek@canonical.com> | 2020-08-11 14:54:29 -0700 |
commit | f6d08ed47a3da3c08345bce2ca366e961c52ad7c (patch) | |
tree | dcbd0efb229b17f696f7195671f05b354b4f70fc /modules/pam_motd/pam_motd.c | |
parent | 668b13da8f830c38388cecac45539972e80cb246 (diff) | |
parent | 9e5bea9e146dee574796259ca464ad2435be3590 (diff) | |
download | pam-f6d08ed47a3da3c08345bce2ca366e961c52ad7c.tar.gz pam-f6d08ed47a3da3c08345bce2ca366e961c52ad7c.tar.bz2 pam-f6d08ed47a3da3c08345bce2ca366e961c52ad7c.zip |
New upstream version 1.4.0
Diffstat (limited to 'modules/pam_motd/pam_motd.c')
-rw-r--r-- | modules/pam_motd/pam_motd.c | 312 |
1 files changed, 273 insertions, 39 deletions
diff --git a/modules/pam_motd/pam_motd.c b/modules/pam_motd/pam_motd.c index cc828d7e..46f4fe61 100644 --- a/modules/pam_motd/pam_motd.c +++ b/modules/pam_motd/pam_motd.c @@ -1,13 +1,8 @@ -/* pam_motd module */ - /* - * Modified for pam_motd by Ben Collins <bcollins@debian.org> - * - * Based off of: - * $Id$ + * pam_motd module * + * Modified for pam_motd by Ben Collins <bcollins@debian.org> * Written by Michael K. Johnson <johnsonm@redhat.com> 1996/10/24 - * */ #include "config.h" @@ -22,22 +17,16 @@ #include <sys/stat.h> #include <pwd.h> #include <syslog.h> +#include <errno.h> #include <security/_pam_macros.h> #include <security/pam_ext.h> -/* - * here, we make a definition for the externally accessible function - * in this file (this definition is required for static a module - * but strongly encouraged generally) it is used to instruct the - * modules include file to define the function prototypes. - */ - -#define PAM_SM_SESSION -#define DEFAULT_MOTD "/etc/motd" -#define DEFAULT_MOTD_D "/etc/motd.d" - #include <security/pam_modules.h> #include <security/pam_modutil.h> +#include "pam_inline.h" + +#define DEFAULT_MOTD "/etc/motd:/run/motd:/usr/lib/motd" +#define DEFAULT_MOTD_D "/etc/motd.d:/run/motd.d:/usr/lib/motd.d" /* --- session management functions (only) --- */ @@ -48,8 +37,8 @@ pam_sm_close_session (pam_handle_t *pamh UNUSED, int flags UNUSED, return PAM_IGNORE; } -static char default_motd[] = DEFAULT_MOTD; -static char default_motd_dir[] = DEFAULT_MOTD_D; +static const char default_motd[] = DEFAULT_MOTD; +static const char default_motd_dir[] = DEFAULT_MOTD_D; static void try_to_display_fd(pam_handle_t *pamh, int fd) { @@ -75,26 +64,222 @@ static void try_to_display_fd(pam_handle_t *pamh, int fd) _pam_drop(mtmp); } -static void try_to_display_directory(pam_handle_t *pamh, const char *dirname) +/* + * Split a DELIM-separated string ARG into an array. + * Outputs a newly allocated array of strings OUT_ARG_SPLIT + * and the number of strings OUT_NUM_STRS. + * Returns 0 in case of error, 1 in case of success. + */ +static int pam_split_string(const pam_handle_t *pamh, char *arg, char delim, + char ***out_arg_split, unsigned int *out_num_strs) +{ + char *arg_extracted = NULL; + const char *arg_ptr = arg; + char **arg_split = NULL; + char delim_str[2]; + unsigned int i = 0; + unsigned int num_strs = 0; + int retval = 0; + + delim_str[0] = delim; + delim_str[1] = '\0'; + + if (arg == NULL) { + goto out; + } + + while (arg_ptr != NULL) { + num_strs++; + arg_ptr = strchr(arg_ptr + sizeof(const char), delim); + } + + arg_split = calloc(num_strs, sizeof(*arg_split)); + if (arg_split == NULL) { + pam_syslog(pamh, LOG_CRIT, "failed to allocate string array"); + goto out; + } + + arg_extracted = strtok_r(arg, delim_str, &arg); + while (arg_extracted != NULL && i < num_strs) { + arg_split[i++] = arg_extracted; + arg_extracted = strtok_r(NULL, delim_str, &arg); + } + + retval = 1; + + out: + *out_num_strs = num_strs; + *out_arg_split = arg_split; + + return retval; +} + +/* Join A_STR and B_STR, inserting a "/" between them if one is not already trailing + * in A_STR or beginning B_STR. A pointer to a newly allocated string holding the + * joined string is returned in STRP_OUT. + * Returns -1 in case of error, or the number of bytes in the joined string in + * case of success. */ +static int join_dir_strings(char **strp_out, const char *a_str, const char *b_str) +{ + int has_sep = 0; + int retval = -1; + char *join_strp = NULL; + + if (strp_out == NULL || a_str == NULL || b_str == NULL) { + goto out; + } + if (strlen(a_str) == 0) { + goto out; + } + + has_sep = (a_str[strlen(a_str) - 1] == '/') || (b_str[0] == '/'); + + retval = asprintf(&join_strp, "%s%s%s", a_str, + (has_sep == 1) ? "" : "/", b_str); + + if (retval < 0) { + goto out; + } + + *strp_out = join_strp; + + out: + return retval; +} + +static int compare_strings(const void *a, const void *b) +{ + const char *a_str = *(const char * const *)a; + const char *b_str = *(const char * const *)b; + + if (a_str == NULL && b_str == NULL) { + return 0; + } + else if (a_str == NULL) { + return -1; + } + else if (b_str == NULL) { + return 1; + } + else { + return strcmp(a_str, b_str); + } +} + +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) { - DIR *dirp; + struct dirent ***dirscans = NULL; + unsigned int *dirscans_sizes = NULL; + unsigned int dirscans_size_total = 0; + char **dirnames_all = NULL; + unsigned int i; + int i_dirnames = 0; + + if (pamh == NULL || motd_dir_path_split == NULL) { + goto out; + } + if (num_motd_dirs < 1) { + goto out; + } + + if ((dirscans = calloc(num_motd_dirs, sizeof(*dirscans))) == NULL) { + pam_syslog(pamh, LOG_CRIT, "failed to allocate dirent arrays"); + goto out; + } + if ((dirscans_sizes = calloc(num_motd_dirs, sizeof(*dirscans_sizes))) == NULL) { + pam_syslog(pamh, LOG_CRIT, "failed to allocate dirent array sizes"); + goto out; + } + + for (i = 0; i < num_motd_dirs; i++) { + int rv; + rv = scandir(motd_dir_path_split[i], &(dirscans[i]), + filter_dirents, alphasort); + if (rv < 0) { + if (errno != ENOENT || report_missing) { + pam_syslog(pamh, LOG_ERR, "error scanning directory %s: %m", + motd_dir_path_split[i]); + } + } else { + dirscans_sizes[i] = rv; + } + dirscans_size_total += dirscans_sizes[i]; + } + + if (dirscans_size_total == 0) + goto out; + + /* 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"); + goto out; + } + + for (i = 0; i < num_motd_dirs; i++) { + unsigned int j; + + for (j = 0; j < dirscans_sizes[i]; j++) { + dirnames_all[i_dirnames] = dirscans[i][j]->d_name; + i_dirnames++; + } + } + + qsort(dirnames_all, dirscans_size_total, + sizeof(const char *), compare_strings); + + for (i = 0; i < dirscans_size_total; i++) { + unsigned int j; + + if (dirnames_all[i] == NULL) { + continue; + } + + /* Skip duplicate file names. */ + if (i > 0 && strcmp(dirnames_all[i], dirnames_all[i - 1]) == 0) { + continue; + } - dirp = opendir(dirname); + for (j = 0; j < num_motd_dirs; j++) { + char *abs_path = NULL; + int fd; - if (dirp != NULL) { - struct dirent *entry; + if (join_dir_strings(&abs_path, motd_dir_path_split[j], + dirnames_all[i]) < 0 || abs_path == NULL) { + continue; + } - while ((entry = readdir(dirp))) { - int fd = openat(dirfd(dirp), entry->d_name, O_RDONLY); + fd = open(abs_path, O_RDONLY, 0); + _pam_drop(abs_path); if (fd >= 0) { try_to_display_fd(pamh, fd); close(fd); + + /* We displayed a file, skip to the next file name. */ + break; } } + } - closedir(dirp); + out: + _pam_drop(dirnames_all); + if (dirscans_sizes != NULL) { + for (i = 0; i < num_motd_dirs; i++) { + unsigned int j; + + for (j = 0; j < dirscans_sizes[i]; j++) + _pam_drop(dirscans[i][j]); + _pam_drop(dirscans[i]); + } + _pam_drop(dirscans_sizes); } + _pam_drop(dirscans); } int pam_sm_open_session(pam_handle_t *pamh, int flags, @@ -102,16 +287,24 @@ int pam_sm_open_session(pam_handle_t *pamh, int flags, { int retval = PAM_IGNORE; const char *motd_path = NULL; + char *motd_path_copy = NULL; + unsigned int num_motd_paths = 0; + char **motd_path_split = NULL; const char *motd_dir_path = NULL; + char *motd_dir_path_copy = NULL; + unsigned int num_motd_dir_paths = 0; + char **motd_dir_path_split = NULL; + int report_missing; if (flags & PAM_SILENT) { return retval; } for (; argc-- > 0; ++argv) { - if (!strncmp(*argv,"motd=",5)) { + const char *str; + if ((str = pam_str_skip_prefix(*argv, "motd=")) != NULL) { - motd_path = 5 + *argv; + motd_path = str; if (*motd_path != '\0') { D(("set motd path: %s", motd_path)); } else { @@ -120,9 +313,9 @@ int pam_sm_open_session(pam_handle_t *pamh, int flags, "motd= specification missing argument - ignored"); } } - else if (!strncmp(*argv,"motd_dir=",9)) { + else if ((str = pam_str_skip_prefix(*argv, "motd_dir=")) != NULL) { - motd_dir_path = 9 + *argv; + motd_dir_path = str; if (*motd_dir_path != '\0') { D(("set motd.d path: %s", motd_dir_path)); } else { @@ -138,21 +331,62 @@ int pam_sm_open_session(pam_handle_t *pamh, int flags, if (motd_path == NULL && motd_dir_path == NULL) { motd_path = default_motd; motd_dir_path = default_motd_dir; + report_missing = 0; + } else { + report_missing = 1; } if (motd_path != NULL) { - int fd = open(motd_path, O_RDONLY, 0); + motd_path_copy = strdup(motd_path); + } - if (fd >= 0) { - try_to_display_fd(pamh, fd); - close(fd); + if (motd_path_copy != NULL) { + if (pam_split_string(pamh, motd_path_copy, ':', + &motd_path_split, &num_motd_paths) == 0) { + goto out; } } - if (motd_dir_path != NULL) - try_to_display_directory(pamh, motd_dir_path); + if (motd_dir_path != NULL) { + motd_dir_path_copy = strdup(motd_dir_path); + } - return retval; + if (motd_dir_path_copy != NULL) { + if (pam_split_string(pamh, motd_dir_path_copy, ':', + &motd_dir_path_split, &num_motd_dir_paths) == 0) { + goto out; + } + } + + if (motd_path_split != NULL) { + unsigned int i; + + for (i = 0; i < num_motd_paths; i++) { + int fd = open(motd_path_split[i], O_RDONLY, 0); + + if (fd >= 0) { + try_to_display_fd(pamh, fd); + close(fd); + + /* We found and displayed a file, move onto next filename. */ + break; + } + } + } + + if (motd_dir_path_split != NULL) + try_to_display_directories_with_overrides(pamh, motd_dir_path_split, + num_motd_dir_paths, report_missing); + + out: + _pam_drop(motd_path_copy); + _pam_drop(motd_path_split); + _pam_drop(motd_dir_path_copy); + _pam_drop(motd_dir_path_split); + + retval = pam_putenv(pamh, "MOTD_SHOWN=pam"); + + return retval == PAM_SUCCESS ? PAM_IGNORE : retval; } /* end of module definition */ |