diff --git a/modules/pam_motd/pam_motd.8.xml b/modules/pam_motd/pam_motd.8.xml index 906c4ed..4e2110c 100644 --- a/modules/pam_motd/pam_motd.8.xml +++ b/modules/pam_motd/pam_motd.8.xml @@ -21,6 +21,9 @@ motd=/path/filename + + motd_dir=/path/dirname.d + @@ -31,10 +34,49 @@ pam_motd is a PAM module that can be used to display arbitrary motd (message of the day) files after a successful - login. By default the /etc/motd file is - shown. The message size is limited to 64KB. + login. By default, pam_motd shows files in the + following locations: + + + + /etc/motd + /run/motd + /usr/lib/motd + /etc/motd.d/ + /run/motd.d/ + /usr/lib/motd.d/ + + + + Each message size is limited to 64KB. + + + If /etc/motd does not exist, + then /run/motd is shown. If + /run/motd does not exist, then + /usr/lib/motd is shown. + + + Similar overriding behavior applies to the directories. + Files in /etc/motd.d/ override files + with the same name in /run/motd.d/ and + /usr/lib/motd.d/. Files in /run/motd.d/ + override files with the same name in /usr/lib/motd.d/. + + + Files the in the directories listed above are displayed in + lexicographic order by name. + + + To silence a message, + a symbolic link with target /dev/null + may be placed in /etc/motd.d with + the same filename as the message to be silenced. Example: + Creating a symbolic link as follows silences /usr/lib/motd.d/my_motd. + + + ln -s /dev/null /etc/motd.d/my_motd - @@ -47,8 +89,10 @@ - The /path/filename file is displayed - as message of the day. + The /path/filename file is displayed + as message of the day. Multiple paths to try can be + specified as a colon-separated list. By default this option + is set to /etc/motd:/run/motd:/usr/lib/motd. @@ -59,16 +103,17 @@ The /path/dirname.d directory is scanned - and each file contained inside of it is displayed. + and each file contained inside of it is displayed. Multiple + directories to scan can be specified as a colon-separated list. + By default this option is set to /etc/motd.d:/run/motd.d:/usr/lib/motd.d. - When no options are given, the default is to display both - /etc/motd and the contents of - /etc/motd.d. Specifying either option (or both) - will disable this default behavior. + When no options are given, the default behavior applies for both + options. Specifying either option (or both) will disable the + default behavior for both options. diff --git a/modules/pam_motd/pam_motd.c b/modules/pam_motd/pam_motd.c index cc828d7..ec3ebd5 100644 --- a/modules/pam_motd/pam_motd.c +++ b/modules/pam_motd/pam_motd.c @@ -33,8 +33,8 @@ */ #define PAM_SM_SESSION -#define DEFAULT_MOTD "/etc/motd" -#define DEFAULT_MOTD_D "/etc/motd.d" +#define DEFAULT_MOTD "/etc/motd:/run/motd:/usr/lib/motd" +#define DEFAULT_MOTD_D "/etc/motd.d:/run/motd.d:/usr/lib/motd.d" #include #include @@ -97,12 +97,234 @@ 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, uint *out_num_strs) +{ + char *arg_extracted = NULL; + const char *arg_ptr = arg; + char **arg_split = NULL; + char delim_str[2]; + int i = 0; + uint 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 = (char **)calloc(num_strs, sizeof(char *)); + if (arg_split == NULL) { + pam_syslog(pamh, LOG_CRIT, "pam_motd: 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 = *(char **)a; + const char *b_str = *(char **)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, int num_motd_dirs) +{ + struct dirent ***dirscans = NULL; + int *dirscans_sizes = NULL; + int dirscans_size_total = 0; + char **dirnames_all = NULL; + 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 = (struct dirent ***)calloc(num_motd_dirs, + sizeof(struct dirent **))) == NULL) { + pam_syslog(pamh, LOG_CRIT, "pam_motd: failed to allocate dirent arrays"); + goto out; + } + if ((dirscans_sizes = (int *)calloc(num_motd_dirs, sizeof(int))) == NULL) { + pam_syslog(pamh, LOG_CRIT, "pam_motd: failed to allocate dirent array sizes"); + goto out; + } + + for (i = 0; i < num_motd_dirs; i++) { + dirscans_sizes[i] = scandir(motd_dir_path_split[i], &(dirscans[i]), + filter_dirents, alphasort); + if (dirscans_sizes[i] < 0) { + pam_syslog(pamh, LOG_ERR, "pam_motd: error scanning directory %s", motd_dir_path_split[i]); + dirscans_sizes[i] = 0; + } + dirscans_size_total += dirscans_sizes[i]; + } + + /* Allocate space for all file names found in the directories, including duplicates. */ + if ((dirnames_all = (char **)calloc(dirscans_size_total, + sizeof(char *))) == NULL) { + pam_syslog(pamh, LOG_CRIT, "pam_motd: failed to allocate dirname array"); + goto out; + } + + for (i = 0; i < dirscans_size_total; i++) { + dirnames_all[i] = NULL; + } + + for (i = 0; i < num_motd_dirs; i++) { + 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++) { + 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; + } + + for (j = 0; j < num_motd_dirs; j++) { + char *abs_path = NULL; + + if (join_dir_strings(&abs_path, motd_dir_path_split[j], + dirnames_all[i]) < 0) { + continue; + } + + if (abs_path != NULL) { + int fd = open(abs_path, O_RDONLY, 0); + if (fd >= 0) { + try_to_display_fd(pamh, fd); + close(fd); + + /* We displayed a file, skip to the next file name. */ + break; + } + } + _pam_drop(abs_path); + } + } + + out: + _pam_drop(dirnames_all); + for (i = 0; i < num_motd_dirs; i++) { + 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); + + return; +} + int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) { int retval = PAM_IGNORE; const char *motd_path = NULL; + char *motd_path_copy = NULL; + int num_motd_paths = 0; + char **motd_path_split = NULL; const char *motd_dir_path = NULL; + char *motd_dir_path_copy = NULL; + int num_motd_dir_paths = 0; + char **motd_dir_path_split = NULL; if (flags & PAM_SILENT) { return retval; @@ -141,16 +363,52 @@ int pam_sm_open_session(pam_handle_t *pamh, int flags, } if (motd_path != NULL) { - int fd = open(motd_path, O_RDONLY, 0); + motd_path_copy = strdup(motd_path); + } + + 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) { + motd_dir_path_copy = strdup(motd_dir_path); + } - if (fd >= 0) { - try_to_display_fd(pamh, fd); - close(fd); + 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_dir_path != NULL) - try_to_display_directory(pamh, motd_dir_path); + if (motd_path_split != NULL) { + 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); + + out: + _pam_drop(motd_path_copy); + _pam_drop(motd_path_split); + _pam_drop(motd_dir_path_copy); + _pam_drop(motd_dir_path_split); return retval; } diff --git a/xtests/Makefile.am b/xtests/Makefile.am index a6d6f8d..4d5aba3 100644 --- a/xtests/Makefile.am +++ b/xtests/Makefile.am @@ -32,7 +32,10 @@ EXTRA_DIST = run-xtests.sh tst-pam_dispatch1.pamd tst-pam_dispatch2.pamd \ tst-pam_substack5.pamd tst-pam_substack5a.pamd tst-pam_substack5.sh \ tst-pam_assemble_line1.pamd tst-pam_assemble_line1.sh \ tst-pam_pwhistory1.pamd tst-pam_pwhistory1.sh \ - tst-pam_time1.pamd time.conf + tst-pam_time1.pamd time.conf \ + tst-pam_motd.sh tst-pam_motd1.sh tst-pam_motd2.sh \ + tst-pam_motd3.sh tst-pam_motd4.sh tst-pam_motd1.pamd \ + tst-pam_motd2.pamd tst-pam_motd3.pamd tst-pam_motd4.pamd XTESTS = tst-pam_dispatch1 tst-pam_dispatch2 tst-pam_dispatch3 \ tst-pam_dispatch4 tst-pam_dispatch5 \ @@ -41,7 +44,7 @@ XTESTS = tst-pam_dispatch1 tst-pam_dispatch2 tst-pam_dispatch3 \ tst-pam_access1 tst-pam_access2 tst-pam_access3 \ tst-pam_access4 tst-pam_limits1 tst-pam_succeed_if1 \ tst-pam_group1 tst-pam_authfail tst-pam_authsucceed \ - tst-pam_pwhistory1 tst-pam_time1 + tst-pam_pwhistory1 tst-pam_time1 tst-pam_motd NOSRCTESTS = tst-pam_substack1 tst-pam_substack2 tst-pam_substack3 \ tst-pam_substack4 tst-pam_substack5 tst-pam_assemble_line1 diff --git a/xtests/tst-pam_motd.c b/xtests/tst-pam_motd.c new file mode 100644 index 0000000..bba2f9d --- /dev/null +++ b/xtests/tst-pam_motd.c @@ -0,0 +1,69 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +static struct pam_conv conv = { + misc_conv, + NULL +}; + +int main(int argc, char *argv[]) +{ + pam_handle_t *pamh=NULL; + char *tst_arg = NULL; + int retval; + + if (argc > 1) + tst_arg = argv[1]; + + retval = pam_start(tst_arg, NULL, &conv, &pamh); + + retval = pam_open_session(pamh, 0); + + retval = pam_close_session(pamh, 0); + + if (pam_end(pamh,retval) != PAM_SUCCESS) { /* close Linux-PAM */ + pamh = NULL; + exit(1); + } + + return ( retval == PAM_SUCCESS ? 0:1 ); /* indicate success */ +} diff --git a/xtests/tst-pam_motd.sh b/xtests/tst-pam_motd.sh new file mode 100755 index 0000000..9080128 --- /dev/null +++ b/xtests/tst-pam_motd.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -e + +./tst-pam_motd1.sh +./tst-pam_motd2.sh +./tst-pam_motd3.sh +./tst-pam_motd4.sh diff --git a/xtests/tst-pam_motd1.pamd b/xtests/tst-pam_motd1.pamd new file mode 100644 index 0000000..ddea82c --- /dev/null +++ b/xtests/tst-pam_motd1.pamd @@ -0,0 +1,3 @@ +#%PAM-1.0 +session required pam_permit.so +session optional pam_motd.so motd=tst-pam_motd1.d/etc/motd motd_dir=tst-pam_motd1.d/etc/motd.d diff --git a/xtests/tst-pam_motd1.sh b/xtests/tst-pam_motd1.sh new file mode 100755 index 0000000..cc88854 --- /dev/null +++ b/xtests/tst-pam_motd1.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +TST_DIR="tst-pam_motd1.d" + +function tst_cleanup() { + rm -rf "${TST_DIR}" + rm -f tst-pam_motd1.out +} + +mkdir -p ${TST_DIR} +mkdir -p ${TST_DIR}/etc/motd.d + +# Verify the case of single motd and motd.d directory works +echo "motd: /etc/motd" > ${TST_DIR}/etc/motd +echo "motd: /etc/motd.d/test" > ${TST_DIR}/etc/motd.d/test + +./tst-pam_motd tst-pam_motd1 > tst-pam_motd1.out + +RET=$? + +motd_to_show_output=$(cat tst-pam_motd1.out | grep "motd: /etc/motd") +if [ -z "${motd_to_show_output}" ]; +then + tst_cleanup + exit 1 +fi + +motd_dir_to_show_output=$(cat tst-pam_motd1.out | grep "motd: /etc/motd.d/test") +if [ -z "${motd_dir_to_show_output}" ]; +then + tst_cleanup + exit 1 +fi + +tst_cleanup +exit $RET diff --git a/xtests/tst-pam_motd2.pamd b/xtests/tst-pam_motd2.pamd new file mode 100644 index 0000000..8200191 --- /dev/null +++ b/xtests/tst-pam_motd2.pamd @@ -0,0 +1,3 @@ +#%PAM-1.0 +session required pam_permit.so +session optional pam_motd.so motd=tst-pam_motd2.d/etc/motd:tst-pam_motd2.d/run/motd:tst-pam_motd2.d/usr/lib/motd motd_dir=tst-pam_motd2.d/etc/motd.d:tst-pam_motd2.d/run/motd.d:tst-pam_motd2.d/usr/lib/motd.d diff --git a/xtests/tst-pam_motd2.sh b/xtests/tst-pam_motd2.sh new file mode 100755 index 0000000..d26ea92 --- /dev/null +++ b/xtests/tst-pam_motd2.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +TST_DIR="tst-pam_motd2.d" + +function tst_cleanup() { + rm -rf "${TST_DIR}" + rm -f tst-pam_motd2.out +} + +mkdir -p ${TST_DIR} +mkdir -p ${TST_DIR}/etc/motd.d +mkdir -p ${TST_DIR}/run/motd.d +mkdir -p ${TST_DIR}/usr/lib/motd.d + +echo "motd: /etc/motd" > ${TST_DIR}/etc/motd +echo "motd: /run/motd" > ${TST_DIR}/run/motd +echo "motd: /usr/lib/motd" > ${TST_DIR}/usr/lib/motd + +# Drop a motd file in test directories such that every overriding +# condition (for 3 directories in this case) will be seen. +echo "motd: e0r0u1 in usr/lib - will show" > ${TST_DIR}/usr/lib/motd.d/e0r0u1.motd +echo "motd: e0r1u0 in run - will show" > ${TST_DIR}/run/motd.d/e0r1u0.motd +echo "motd: e0r1u1 in usr/lib - not show" > ${TST_DIR}/usr/lib/motd.d/e0r1u1.motd +echo "motd: e0r1u1 in run - will show" > ${TST_DIR}/run/motd.d/e0r1u1.motd +echo "motd: e1r0u0 in etc - will show" > ${TST_DIR}/etc/motd.d/e1r0u0.motd +echo "motd: e1r0u1 in usr/lib - not show" > ${TST_DIR}/usr/lib/motd.d/e1r0u1.motd +echo "motd: e1r0u1 in etc - will show" > ${TST_DIR}/etc/motd.d/e1r0u1.motd +echo "motd: e1r1u0 in run - not show" > ${TST_DIR}/run/motd.d/e1r1u0.motd +echo "motd: e1r1u0 in etc - will show" > ${TST_DIR}/etc/motd.d/e1r1u0.motd +echo "motd: e1r1u1 in usr/lib - not show" > ${TST_DIR}/usr/lib/motd.d/e1r1u1.motd +echo "motd: e1r1u1 in run - not show" > ${TST_DIR}/run/motd.d/e1r1u1.motd +echo "motd: e1r1u1 in etc - will show" > ${TST_DIR}/etc/motd.d/e1r1u1.motd + +./tst-pam_motd tst-pam_motd2 > tst-pam_motd2.out + +RET=$? + +motd_to_show_output=$(cat tst-pam_motd2.out | grep "motd: /etc/motd") +if [ -z "${motd_to_show_output}" ]; +then + tst_cleanup + exit 1 +fi + +motd_dir_not_show_output=$(cat tst-pam_motd2.out | grep "not show") +if [ -n "${motd_dir_not_show_output}" ]; +then + tst_cleanup + exit 1 +fi + +tst_cleanup +exit $RET diff --git a/xtests/tst-pam_motd3.pamd b/xtests/tst-pam_motd3.pamd new file mode 100644 index 0000000..a8b8cbf --- /dev/null +++ b/xtests/tst-pam_motd3.pamd @@ -0,0 +1,3 @@ +#%PAM-1.0 +session required pam_permit.so +session optional pam_motd.so motd=tst-pam_motd3.d/etc/motd:tst-pam_motd3.d/run/motd:tst-pam_motd3.d/usr/lib/motd motd_dir=tst-pam_motd3.d/etc/motd.d:tst-pam_motd3.d/run/motd.d:tst-pam_motd3.d/usr/lib/motd.d diff --git a/xtests/tst-pam_motd3.sh b/xtests/tst-pam_motd3.sh new file mode 100755 index 0000000..e18856b --- /dev/null +++ b/xtests/tst-pam_motd3.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +TST_DIR="tst-pam_motd3.d" + +function tst_cleanup() { + rm -rf "${TST_DIR}" + rm -f tst-pam_motd3.out +} + +mkdir -p ${TST_DIR} +mkdir -p ${TST_DIR}/etc/motd.d +mkdir -p ${TST_DIR}/run/motd.d +mkdir -p ${TST_DIR}/usr/lib/motd.d + +# Verify motd is still displayed when not overridden +echo "motd: test-show in run - show" > ${TST_DIR}/run/motd.d/test-show.motd + +# Test overridden by a symlink to a file that isn't /dev/null; symlink target should show +echo "motd: hidden-by-symlink in usr/lib - not show" > ${TST_DIR}/usr/lib/motd.d/hidden-by-symlink.motd +echo "motd: test-from-symlink - show" > ${TST_DIR}/test-from-symlink.motd +ln -sr ${TST_DIR}/test-from-symlink.motd ${TST_DIR}/run/motd.d/hidden-by-symlink.motd + +# Test hidden by a null symlink +echo "motd: hidden-by-null-symlink in run - not show" > ${TST_DIR}/run/motd.d/hidden-by-null-symlink.motd +ln -s /dev/null ${TST_DIR}/etc/motd.d/hidden-by-null-symlink.motd + +./tst-pam_motd tst-pam_motd3 > tst-pam_motd3.out + +RET=$? + +motd_dir_not_show_output=$(cat tst-pam_motd3.out | grep "not show") +if [ -n "${motd_dir_not_show_output}" ]; +then + tst_cleanup + exit 1 +fi + +motd_test_show_output=$(cat tst-pam_motd3.out | grep "test-show.*- show") +if [ -z "${motd_test_show_output}" ]; +then + tst_cleanup + exit 1 +fi + +motd_general_symlink_show_output=$(cat tst-pam_motd3.out | grep "test-from-symlink.*- show") +if [ -z "${motd_general_symlink_show_output}" ]; +then + tst_cleanup + exit 1 +fi + +tst_cleanup +exit $RET diff --git a/xtests/tst-pam_motd4.pamd b/xtests/tst-pam_motd4.pamd new file mode 100644 index 0000000..9dc311a --- /dev/null +++ b/xtests/tst-pam_motd4.pamd @@ -0,0 +1,3 @@ +#%PAM-1.0 +session required pam_permit.so +session optional pam_motd.so motd=tst-pam_motd4.d/etc/motd diff --git a/xtests/tst-pam_motd4.sh b/xtests/tst-pam_motd4.sh new file mode 100755 index 0000000..6022177 --- /dev/null +++ b/xtests/tst-pam_motd4.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +TST_DIR="tst-pam_motd4.d" + +function tst_cleanup() { + rm -rf "${TST_DIR}" + rm -f tst-pam_motd4.out +} + +mkdir -p ${TST_DIR}/etc + +# Verify the case of single motd with no motd_dir given in tst-pam_motd4.pamd +echo "motd: /etc/motd" > ${TST_DIR}/etc/motd + +./tst-pam_motd tst-pam_motd4 > tst-pam_motd4.out + +RET=$? + +motd_to_show_output=$(cat tst-pam_motd4.out | grep "motd: /etc/motd") +if [ -z "${motd_to_show_output}" ]; +then + tst_cleanup + exit 1 +fi + +tst_cleanup +exit $RET