summaryrefslogtreecommitdiffstats
path: root/testing/source/PAM/a/pam/fedora-patches/pam-1.3.1-motd-multiple-paths.patch
diff options
context:
space:
mode:
author Patrick J Volkerding <volkerdi@slackware.com>2020-02-12 05:05:50 +0000
committer Eric Hameleers <alien@slackware.com>2020-02-12 17:59:48 +0100
commit6a63f41b3b49b8f8f86b40166b782d907ccb2538 (patch)
tree7d2c77a55463f5586ec453e13602ff2889030f84 /testing/source/PAM/a/pam/fedora-patches/pam-1.3.1-motd-multiple-paths.patch
parente1ab44f93f91f5c967ac1b7143c7a8ac287d1a52 (diff)
downloadcurrent-97940c50aff41995ad351e5f6532bcdccd47648b.tar.gz
current-97940c50aff41995ad351e5f6532bcdccd47648b.tar.xz
Wed Feb 12 05:05:50 UTC 202020200212050550
Hey folks! PAM has finally landed in /testing. Some here wanted it to go right into the main tree immediately, and in a more normal development cycle I'd have been inclined to agree (it is -current, after all). But it's probably better for it to appear in /testing first, to make sure we didn't miss any bugs and also to serve as a warning shot that we'll be shaking up the tree pretty good over the next few weeks. I'd like to see this merged into the main tree in a day or two, so any testing is greatly appreciated. Switching to the PAM packages (or reverting from them) is as easy as installing all of them with upgradepkg --install-new, and if reverting then remove the three leftover _pam packages. After reverting, a bit of residue will remain in /etc/pam.d/ and /etc/security/ which can either be manually deleted or simply ignored. While there are many more features available in PAM compared with plain shadow, out of the box about the only noticable change is the use of cracklib and libpwquality to check the quality of a user-supplied password. Hopefully having PAM and krb5 will get us on track to having proper Active Directory integration as well as using code paths that are likely better audited these days. The attack surface *might* be bigger, but it's also a lot better scrutinized. Thanks to Robby Workman and Vincent Batts who did most of the initial heavy lifting on the core PAM packages as a side project for many years. Thanks also to Phantom X whose PAM related SlackBuilds were a valuable reference. And thanks as well to ivandi - I learned a lot from the SlackMATE build scripts and was even occasionally thankful for the amusing ways you would kick my ass on LQ. ;-) You're more than welcome to let us know where we've messed up this time. The binutils and glibc packages in /testing were removed and are off the table for now. I'm not seeing much upside to heading down that rabbit hole at the moment. Next we need to be looking at Xfce 4.14 and Plasma 5.18 LTS and some other things that have been held back since KDE4 couldn't use them. Cheers! :-) a/kernel-generic-5.4.19-x86_64-1.txz: Upgraded. a/kernel-huge-5.4.19-x86_64-1.txz: Upgraded. a/kernel-modules-5.4.19-x86_64-1.txz: Upgraded. a/lvm2-2.03.08-x86_64-1.txz: Upgraded. a/shadow-4.8.1-x86_64-2.txz: Rebuilt. Automatically backup /etc/login.defs and install the new version if incompatible PAM options are detected. d/kernel-headers-5.4.19-x86-1.txz: Upgraded. k/kernel-source-5.4.19-noarch-1.txz: Upgraded. VALIDATE_FS_PARSER y -> n xap/mozilla-thunderbird-68.5.0-x86_64-1.txz: Upgraded. This release contains security fixes and improvements. For more information, see: https://www.mozilla.org/en-US/thunderbird/68.5.0/releasenotes/ https://www.mozilla.org/en-US/security/advisories/mfsa2020-07/ https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-6793 https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-6794 https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-6795 https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-6797 https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-6798 https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-6792 https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-6800 (* Security fix *) isolinux/initrd.img: Rebuilt. kernels/*: Upgraded. testing/packages/PAM/ConsoleKit2-1.2.1-x86_64-1_pam.txz: Added. testing/packages/PAM/at-3.2.1-x86_64-1_pam.txz: Added. testing/packages/PAM/cifs-utils-6.10-x86_64-2_pam.txz: Added. testing/packages/PAM/cracklib-2.9.7-x86_64-1_pam.txz: Added. testing/packages/PAM/cups-2.3.1-x86_64-1_pam.txz: Added. testing/packages/PAM/cyrus-sasl-2.1.27-x86_64-2_pam.txz: Added. testing/packages/PAM/dovecot-2.3.9.2-x86_64-1_pam.txz: Added. testing/packages/PAM/gnome-keyring-3.34.0-x86_64-1_pam.txz: Added. testing/packages/PAM/hplip-3.19.12-x86_64-2_pam.txz: Added. testing/packages/PAM/kde-workspace-4.11.22-x86_64-6_pam.txz: Added. testing/packages/PAM/libcap-2.31-x86_64-1_pam.txz: Added. testing/packages/PAM/libcgroup-0.41-x86_64-5_pam.txz: Added. testing/packages/PAM/libpwquality-1.4.2-x86_64-1_pam.txz: Added. testing/packages/PAM/mariadb-10.4.12-x86_64-1_pam.txz: Added. testing/packages/PAM/netatalk-3.1.12-x86_64-2_pam.txz: Added. testing/packages/PAM/netkit-rsh-0.17-x86_64-2_pam.txz: Added. testing/packages/PAM/openssh-8.1p1-x86_64-1_pam.txz: Added. testing/packages/PAM/openvpn-2.4.8-x86_64-1_pam.txz: Added. testing/packages/PAM/pam-1.3.1-x86_64-1_pam.txz: Added. testing/packages/PAM/polkit-0.116-x86_64-1_pam.txz: Added. testing/packages/PAM/popa3d-1.0.3-x86_64-3_pam.txz: Added. testing/packages/PAM/ppp-2.4.7-x86_64-3_pam.txz: Added. testing/packages/PAM/proftpd-1.3.6b-x86_64-1_pam.txz: Added. testing/packages/PAM/samba-4.11.6-x86_64-1_pam.txz: Added. testing/packages/PAM/screen-4.8.0-x86_64-1_pam.txz: Added. testing/packages/PAM/shadow-4.8.1-x86_64-2_pam.txz: Added. testing/packages/PAM/sudo-1.8.31-x86_64-1_pam.txz: Added. testing/packages/PAM/system-config-printer-1.5.12-x86_64-2_pam.txz: Added. testing/packages/PAM/util-linux-2.35.1-x86_64-1_pam.txz: Added. testing/packages/PAM/vsftpd-3.0.3-x86_64-5_pam.txz: Added. testing/packages/PAM/xdm-1.1.11-x86_64-9_pam.txz: Added. testing/packages/PAM/xlockmore-5.62-x86_64-1_pam.txz: Added. testing/packages/PAM/xscreensaver-5.43-x86_64-1_pam.txz: Added. testing/packages/binutils-2.34-x86_64-1.txz: Removed. testing/packages/glibc-2.31-x86_64-1.txz: Removed. testing/packages/glibc-i18n-2.31-x86_64-1.txz: Removed. testing/packages/glibc-profile-2.31-x86_64-1.txz: Removed. testing/packages/glibc-solibs-2.31-x86_64-1.txz: Removed. usb-and-pxe-installers/usbboot.img: Rebuilt.
Diffstat (limited to 'testing/source/PAM/a/pam/fedora-patches/pam-1.3.1-motd-multiple-paths.patch')
-rw-r--r--testing/source/PAM/a/pam/fedora-patches/pam-1.3.1-motd-multiple-paths.patch755
1 files changed, 755 insertions, 0 deletions
diff --git a/testing/source/PAM/a/pam/fedora-patches/pam-1.3.1-motd-multiple-paths.patch b/testing/source/PAM/a/pam/fedora-patches/pam-1.3.1-motd-multiple-paths.patch
new file mode 100644
index 000000000..a0b069815
--- /dev/null
+++ b/testing/source/PAM/a/pam/fedora-patches/pam-1.3.1-motd-multiple-paths.patch
@@ -0,0 +1,755 @@
+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 @@
+ <arg choice="opt">
+ motd=<replaceable>/path/filename</replaceable>
+ </arg>
++ <arg choice="opt">
++ motd_dir=<replaceable>/path/dirname.d</replaceable>
++ </arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+@@ -31,10 +34,49 @@
+ <para>
+ 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 <filename>/etc/motd</filename> file is
+- shown. The message size is limited to 64KB.
++ login. By default, pam_motd shows files in the
++ following locations:
++ </para>
++ <para>
++ <simplelist type='vert'>
++ <member><filename>/etc/motd</filename></member>
++ <member><filename>/run/motd</filename></member>
++ <member><filename>/usr/lib/motd</filename></member>
++ <member><filename>/etc/motd.d/</filename></member>
++ <member><filename>/run/motd.d/</filename></member>
++ <member><filename>/usr/lib/motd.d/</filename></member>
++ </simplelist>
++ </para>
++ <para>
++ Each message size is limited to 64KB.
++ </para>
++ <para>
++ If <filename>/etc/motd</filename> does not exist,
++ then <filename>/run/motd</filename> is shown. If
++ <filename>/run/motd</filename> does not exist, then
++ <filename>/usr/lib/motd</filename> is shown.
++ </para>
++ <para>
++ Similar overriding behavior applies to the directories.
++ Files in <filename>/etc/motd.d/</filename> override files
++ with the same name in <filename>/run/motd.d/</filename> and
++ <filename>/usr/lib/motd.d/</filename>. Files in <filename>/run/motd.d/</filename>
++ override files with the same name in <filename>/usr/lib/motd.d/</filename>.
++ </para>
++ <para>
++ Files the in the directories listed above are displayed in
++ lexicographic order by name.
++ </para>
++ <para>
++ To silence a message,
++ a symbolic link with target <filename>/dev/null</filename>
++ may be placed in <filename>/etc/motd.d</filename> with
++ the same filename as the message to be silenced. Example:
++ Creating a symbolic link as follows silences <filename>/usr/lib/motd.d/my_motd</filename>.
++ </para>
++ <para>
++ <command>ln -s /dev/null /etc/motd.d/my_motd</command>
+ </para>
+-
+ </refsect1>
+
+ <refsect1 id="pam_motd-options">
+@@ -47,8 +89,10 @@
+ </term>
+ <listitem>
+ <para>
+- The <filename>/path/filename</filename> file is displayed
+- as message of the day.
++ The <filename>/path/filename</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 <filename>/etc/motd:/run/motd:/usr/lib/motd</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+@@ -59,16 +103,17 @@
+ <listitem>
+ <para>
+ The <filename>/path/dirname.d</filename> 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 <filename>/etc/motd.d:/run/motd.d:/usr/lib/motd.d</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ <para>
+- When no options are given, the default is to display both
+- <filename>/etc/motd</filename> and the contents of
+- <filename>/etc/motd.d</filename>. 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.
+ </para>
+ </refsect1>
+
+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 <security/pam_modules.h>
+ #include <security/pam_modutil.h>
+@@ -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 <config.h>
++#endif
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <security/pam_appl.h>
++#include <security/pam_misc.h>
++
++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