diff --git a/Linux-PAM-1.6.1.tar.xz b/Linux-PAM-1.6.1.tar.xz deleted file mode 100644 index 95fe0a8..0000000 --- a/Linux-PAM-1.6.1.tar.xz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f8923c740159052d719dbfc2a2f81942d68dd34fcaf61c706a02c9b80feeef8e -size 1054152 diff --git a/Linux-PAM-1.6.1.tar.xz.asc b/Linux-PAM-1.6.1.tar.xz.asc deleted file mode 100644 index 8cbfcb4..0000000 --- a/Linux-PAM-1.6.1.tar.xz.asc +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQIcBAABCgAGBQJmFWt/AAoJEKgEH6g54W42NCwP/iWl8igdScTreVF6zV79Dqu1 -sl+ZjBr/dL+DOTcotsRnoAZUOy4ug3iktMZr1t0BMpWUorNmUofH4SZuhsX0CgRq -47t5mVqCakwn4JLq8J9cLOciMno6ips5ZT4RbMgzRYd1WcBurCAxQSNLP3aQGgub -RFObkqw5814ksz9Ge6QVhJ4l9P0wUoKfcpkzHj2Vq+cy0EzlBtnBGCHrMDgrz5aT -mXqGVvWTPO+lR2S+7wOLUtPoRv0uvN6h97ZszaoGoJ6wa6yYwOYz12/AiIsVQhet -cnr29ymuwPDqlrYGD1Hb0+ZUQExjVDQY90hdJ/ZntUlK7CY/2SotpDGB9kR8dTYJ -fpIVmR6GEZ+xSjBqa7RaiL8ieZCgT3TIvsMqteiFkqI+2lhlSGHX3g3oNSd3sbqd -PLok6W4L+xWDp89aMyYDDs/ISjBt5sSNK4NOOTZIMK4oeScGJJvrDL3S5DOSk1ku -o3l9N62WStD7fk0LYnyUGZORg/ccK6Yy2fV22zBMm/76PoyA1yHfFxCW+HwwmcqR -0riaFjA8cesZ3Dj79q24U3FRVdW5fTF9gS/5mK/Yj51KMMzTkUmbjksEC/AEBKzB -9laXxPdIeKUwNlGs7Heo/NE87u4OZfyihwpzLaTcOzbpN3zDyH6aH5poDs1FSaQ2 -UoUkHsbCWJU/ksn/9BIQ -=Dbz2 ------END PGP SIGNATURE----- diff --git a/Linux-PAM-1.7.1.tar.xz b/Linux-PAM-1.7.1.tar.xz new file mode 100644 index 0000000..7b3c6a7 --- /dev/null +++ b/Linux-PAM-1.7.1.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21dbcec6e01dd578f14789eac9024a18941e6f2702a05cf91b28c232eeb26ab0 +size 510828 diff --git a/Linux-PAM-1.7.1.tar.xz.asc b/Linux-PAM-1.7.1.tar.xz.asc new file mode 100644 index 0000000..ebcc2cf --- /dev/null +++ b/Linux-PAM-1.7.1.tar.xz.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIcBAABCgAGBQJoUTDGAAoJEKgEH6g54W42RDQQAIKq+ltEn0g/lB0g+xU9SArO +ItMiZDp6RaLDIRgOxbl1hnQyXvXcW5LYBT36u+e5PLKrtMzc8/S3kDtn2FRsS5KW +aZaKmZI6UlEQVErMfX2F8/uPvcMRNmqHL7h3+BW8aIWp+WTBO3TIOZxVqNoDFbxj +L/9G3KYTgcuKjb6XoDlicS68ImcLJC2BPjcaisaoqKRyK504jgYK6Wl6AFo7Fu8r +PS134LM6gUUxMzdYCpISmO5tZh+uOqtCfbdeOY3bwBeupe2J4D6v7uASF7RqEXPX +/imsmUkmqLmOOolvLflGDsiz1HaY05LW7CcJngXOV6WKU+HqBg9E5Xclnr0RyvBD +tmFPeWlgPw+zg+BVUhGAUeLoFCknbtY/7TEB4Jh0Z/Tm+pOUVoQbhrUCI0rAgapN +dA9i5DCUuEBXRul2YvG7EZGuYs77fzpf/J++b9XKB9kH1Bc3vaaZoaO+lbN8g6Ei +CbZCmD0ct0UhUTX+FEUG9SkMTomyd9ihz6kuHcuo4eCVbVuDJpF+vEUjVb7no9Aw +KlZ6/I45GRRjIYYk/vxpgNX05D8xeMxDkXEMcKAHsI/q4oOe7Hsuess47WioiVXL +xNl6AHjJ4VMcz1xLPR8COA8L3uaZNtxuIGhazZFeJbrfJct5gsf9iv04pdAA73/B +NtgHrE6GjGSmw+/xX22z +=RQhR +-----END PGP SIGNATURE----- diff --git a/pam-bsc1194818-cursor-escape.patch b/pam-bsc1194818-cursor-escape.patch deleted file mode 100644 index fbd27de..0000000 --- a/pam-bsc1194818-cursor-escape.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 8ae228fa76ff9ef1d8d6b2199582d9206f1830c6 Mon Sep 17 00:00:00 2001 -From: Stanislav Brabec -Date: Mon, 22 Jul 2024 23:18:16 +0200 -Subject: [PATCH] libpam_misc: Use ECHOCTL in the terminal input - -Use the canonical terminal mode (line mode) and set ECHOCTL to prevent -cursor escape from the login prompt using arrows or escape sequences. - -ICANON is the default in most cases anyway. ECHOCTL is default on tty, but -for example not on pty, allowing cursor to escape. - -Stanislav Brabec ---- - libpam_misc/misc_conv.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/libpam_misc/misc_conv.c b/libpam_misc/misc_conv.c -index 7410e929..6b839b48 100644 ---- a/libpam_misc/misc_conv.c -+++ b/libpam_misc/misc_conv.c -@@ -145,9 +145,10 @@ static int read_string(int echo, const char *prompt, char **retstr) - return -1; - } - memcpy(&term_tmp, &term_before, sizeof(term_tmp)); -- if (!echo) { -+ if (echo) -+ term_tmp.c_lflag |= ICANON | ECHOCTL; -+ else - term_tmp.c_lflag &= ~(ECHO); -- } - have_term = 1; - - /* --- -2.45.2 - diff --git a/pam.changes b/pam.changes index c0863c9..16fe18a 100644 --- a/pam.changes +++ b/pam.changes @@ -1,3 +1,77 @@ +------------------------------------------------------------------- +Mon Sep 15 07:53:29 UTC 2025 - Valentin Lefebvre + +- pam_mkhomedir: building with vendordir option allows fetching skeleton + directory from the vendor directory when creating the user home directory. + [+ pam_mkhomedir-Use-vendordir-when-defined.patch, bsc#1245524] + +------------------------------------------------------------------- +Wed Aug 27 14:20:14 UTC 2025 - Thorsten Kukuk + +- Update to 1.7.1+git (post-v1.7.1.patch) + - disable unix_chkpwd by default, only used as fallback again +- pam_modutil_get-overwrite-password-at-free.patch is included + +------------------------------------------------------------------- +Tue Aug 19 10:12:13 UTC 2025 - Valentin Lefebvre + +- Make sure that the buffer containing encrypted passwords get's erased, + before free. + [pam_modutil_get-overwrite-password-at-free.patch, bsc#1232234, + CVE-2024-10041] + +------------------------------------------------------------------- +Wed Jun 18 12:01:57 UTC 2025 - Thorsten Kukuk + +- hardcode disabling elogind, meson detection is unreliable in OBS + +------------------------------------------------------------------- +Wed Jun 18 05:38:35 UTC 2025 - Thorsten Kukuk + +- Update to version 1.7.1 + - pam_access: do not resolve ttys or display variables as hostnames. + - pam_access: added "nodns" option to disallow resolving of tokens + as hostnames (CVE-2024-10963). + - pam_limits: added support for rttime (RLIMIT_RTTIME). + - pam_namespace: fixed potential privilege escalation (CVE-2025-6020). + - meson: added support of elogind as a logind provider. + - Multiple minor bug fixes, build fixes, portability fixes, + documentation improvements, and translation updates. +- pam_access-rework-resolving-of-tokens-as-hostname.patch got obsoleted + +------------------------------------------------------------------- +Mon Mar 24 17:41:34 UTC 2025 - Thorsten Kukuk + +- Remove unix2_chkpwd, no consumer left + +------------------------------------------------------------------- +Thu Dec 5 12:44:33 UTC 2024 - Valentin Lefebvre + +- pam_access: rework resolving of tokens as hostname + - separate resolving of IP addresses from hostnames. Don't resolve TTYs or + display variables as hostname. + - Add "nodns" option to disallow resolving of tokens as hostname. + - [pam_access-rework-resolving-of-tokens-as-hostname.patch, bsc#1233078, + CVE-2024-10963] + +------------------------------------------------------------------- +Thu Oct 24 11:57:20 UTC 2024 - Thorsten Kukuk + +- Update to version 1.7.0 + - build: changed build system from autotools to meson. + - libpam_misc: use ECHOCTL in the terminal input + - pam_access: support UID and GID in access.conf + - pam_env: install environment file in vendordir if vendordir is enabled + - pam_issue: only count class user if logind support is enabled + - pam_limits: use systemd-logind instead of utmp if logind support is enabled + - pam_unix: compare password hashes in constant time + - Multiple minor bug fixes, build fixes, portability fixes, + documentation improvements, and translation updates. +- Drop upstream patches: + - pam-bsc1194818-cursor-escape.patch + - pam_limits-systemd.patch + - pam_issue-systemd.patch + ------------------------------------------------------------------- Thu Sep 12 07:50:55 UTC 2024 - Thorsten Kukuk @@ -1208,7 +1282,6 @@ Wed Feb 23 12:45:03 UTC 2011 - vcizek@novell.com * correct parsing of "quiet" option ------------------------------------------------------------------- - Wed Feb 23 10:00:22 UTC 2011 - vcizek@novell.com - fix for bnc#673826 (pam_listfile) diff --git a/pam.spec b/pam.spec index ea90976..9f85228 100644 --- a/pam.spec +++ b/pam.spec @@ -36,10 +36,10 @@ %endif %bcond_without selinux -%bcond_with debug %define flavor @BUILD_FLAVOR@%{nil} +# List of config files for migration to /usr/etc %define config_files pam.d/other pam.d/common-account pam.d/common-auth pam.d/common-password pam.d/common-session \\\ security/faillock.conf security/group.conf security/limits.conf security/pam_env.conf security/access.conf \\\ security/namespace.conf security/namespace.init security/sepermit.conf @@ -64,14 +64,13 @@ %define libpamc_so_version 0.82.1 %if ! %{defined _distconfdir} %define _distconfdir %{_sysconfdir} - %define config_noreplace 1 %endif # %{load:%{_sourcedir}/macros.pam} # Name: pam%{name_suffix} # -Version: 1.6.1 +Version: 1.7.1 Release: 0 Summary: A Security Tool that Provides Authentication for Applications License: GPL-2.0-or-later OR BSD-3-Clause @@ -86,8 +85,6 @@ Source5: common-account.pamd Source6: common-password.pamd Source7: common-session.pamd Source9: baselibs.conf -Source10: unix2_chkpwd.c -Source11: unix2_chkpwd.8 Source12: pam-login_defs-check.sh Source13: pam.tmpfiles Source20: common-session-nonlogin.pamd @@ -96,16 +93,14 @@ Source22: postlogin-account.pamd Source23: postlogin-password.pamd Source24: postlogin-session.pamd Patch1: pam-limit-nproc.patch -# https://github.com/linux-pam/linux-pam/pull/816 -Patch2: pam-bsc1194818-cursor-escape.patch -# https://github.com/linux-pam/linux-pam/pull/826 -Patch3: pam_limits-systemd.patch -# https://github.com/linux-pam/linux-pam/pull/825 -Patch4: pam_issue-systemd.patch +# PATCH-FIX-UPSTREAM +Patch2: post-v1.7.1.patch +# PATCH-FIX-UPSTREAM +Patch3: pam_mkhomedir-Use-vendordir-when-defined.patch BuildRequires: audit-devel BuildRequires: bison BuildRequires: flex -BuildRequires: libtool +BuildRequires: meson >= 0.62.0 BuildRequires: xz Requires(post): permissions # All login.defs variables require support from shadow side. @@ -149,9 +144,7 @@ username/password pair against values stored in a Berkeley DB database. %package -n pam-extra Summary: PAM module with extended dependencies Group: System/Libraries -#BuildRequires: pkgconfig(systemd) -# The systemd-mini package does not pass configure checks -BuildRequires: systemd-devel >= 254 +BuildRequires: pkgconfig(libsystemd) >= 254 BuildRequires: pam-devel Provides: pam:%{_sbindir}/pam_timestamp_check Provides: pam:%{_pam_moduledir}/pam_limits.so @@ -217,32 +210,28 @@ cp -a %{SOURCE12} . %build bash ./pam-login_defs-check.sh -export CFLAGS="%{optflags}" -%if !%{with debug} -CFLAGS="$CFLAGS -DNDEBUG" -%endif %if %{livepatchable} CFLAGS="$CFLAGS -fpatchable-function-entry=16,14 -fdump-ipa-clones" %endif -autoreconf -%configure \ - --includedir=%{_includedir}/security \ - --docdir=%{_docdir}/pam \ - --htmldir=%{_docdir}/pam/html \ - --pdfdir=%{_docdir}/pam/pdf \ - --enable-isadir=../..%{_pam_moduledir} \ - --enable-securedir=%{_pam_moduledir} \ - --enable-vendordir=%{_prefix}/etc \ -%if "%{flavor}" == "full" - --enable-logind \ -%endif - --disable-examples \ - --disable-nis \ -%if %{with debug} - --enable-debug -%endif -%make_build +%meson -Dvendordir=%{_distconfdir} \ + -Ddocdir=%{_docdir}/pam \ + -Dhtmldir=%{_docdir}/pam/html \ + -Dpdfdir=%{_docdir}/pam/pdf \ + -Dsecuredir=%{_pam_moduledir} \ + -Dpam_unix-try-getspnam=true \ +%if "%{flavor}" != "full" + -Dlogind=disabled \ + -Dpam_userdb=disabled \ + -Ddocs=disabled \ +%else + -Dlogind=enabled \ +%endif + -Dpwaccess=disabled \ + -Delogind=disabled \ + -Dexamples=false \ + -Dnis=disabled +%meson_build %if %{livepatchable} @@ -270,29 +259,17 @@ cp %{tar_package_name} %{_other} %endif # livepatchable -gcc -fwhole-program -fpie -pie -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE %{optflags} -I%{_builddir}/Linux-PAM-%{version}/libpam/include %{SOURCE10} -o %{_builddir}/unix2_chkpwd -L%{_builddir}/Linux-PAM-%{version}/libpam/.libs -lpam - %if %{build_main} %check -%make_build check +%meson_test %endif %install +%meson_install + mkdir -p %{buildroot}%{_pam_confdir} mkdir -p %{buildroot}%{_pam_vendordir} -mkdir -p %{buildroot}%{_includedir}/security -mkdir -p %{buildroot}%{_pam_moduledir} -mkdir -p %{buildroot}/sbin -mkdir -p -m 755 %{buildroot}%{_libdir} -# For compat reasons -mkdir -p %{buildroot}%{_distconfdir}/pam.d -%make_install -/sbin/ldconfig -n %{buildroot}%{_libdir} -# Install documentation -%make_install -C doc -# install /etc/security/namespace.d used by pam_namespace.so for namespace.conf iscript -install -d %{buildroot}%{_pam_secconfdir}/namespace.d # install other.pamd and common-*.pamd install -m 644 %{SOURCE3} %{buildroot}%{_pam_vendordir}/other install -m 644 %{SOURCE4} %{buildroot}%{_pam_vendordir}/common-auth @@ -304,23 +281,14 @@ install -m 644 %{SOURCE21} %{buildroot}%{_pam_vendordir}/postlogin-auth install -m 644 %{SOURCE22} %{buildroot}%{_pam_vendordir}/postlogin-account install -m 644 %{SOURCE23} %{buildroot}%{_pam_vendordir}/postlogin-password install -m 644 %{SOURCE24} %{buildroot}%{_pam_vendordir}/postlogin-session -mkdir -p %{buildroot}%{_prefix}/lib/motd.d -# -# Remove crap -# -find %{buildroot} -type f -name "*.la" -delete -print # # Install READMEs of PAM modules # DOC=%{buildroot}%{_defaultdocdir}/pam +%if "%{flavor}" == "full" mkdir -p $DOC/modules -pushd modules -for i in pam_*/README; do - cp -fpv "$i" "$DOC/modules/README.${i%/*}" -done -popd -# Install unix2_chkpwd -install -m 755 %{_builddir}/unix2_chkpwd %{buildroot}%{_sbindir} +cp -fpv %{_vpath_builddir}/modules/pam_*/pam_*.txt "$DOC/modules/" +%endif # rpm macros install -D -m 644 %{SOURCE2} %{buildroot}%{_rpmmacrodir}/macros.pam @@ -328,13 +296,11 @@ install -D -m 644 %{SOURCE2} %{buildroot}%{_rpmmacrodir}/macros.pam install -Dm0644 %{SOURCE13} %{buildroot}%{_tmpfilesdir}/pam.conf mkdir -p %{buildroot}%{_pam_secdistconfdir}/{limits.d,namespace.d} -mv %{buildroot}%{_sysconfdir}/environment %{buildroot}%{_distconfdir}/environment # Remove manual pages for main package %if !%{build_doc} rm -rf %{buildroot}%{_mandir}/man?/* %else -install -m 644 %{_sourcedir}/unix2_chkpwd.8 %{buildroot}/%{_mandir}/man8/ # bsc#1188724 echo '.so man8/pam_motd.8' > %{buildroot}%{_mandir}/man5/motd.5 %endif @@ -357,12 +323,10 @@ rm -rf %{buildroot}{%{_pam_moduledir}/pam_limits.so,%{_pam_secdistconfdir}/limi %verifyscript %verify_permissions -e %{_sbindir}/unix_chkpwd -%verify_permissions -e %{_sbindir}/unix2_chkpwd %post /sbin/ldconfig %set_permissions %{_sbindir}/unix_chkpwd -%set_permissions %{_sbindir}/unix2_chkpwd %tmpfiles_create %{_tmpfilesdir}/pam.conf %postun -p /sbin/ldconfig @@ -380,23 +344,13 @@ done %files -f Linux-PAM.lang %doc NEWS %license COPYING -%exclude %{_defaultdocdir}/pam/html -%exclude %{_defaultdocdir}/pam/modules -%exclude %{_defaultdocdir}/pam/pdf -%exclude %{_defaultdocdir}/pam/*.txt %dir %{_pam_confdir} %dir %{_pam_vendordir} %dir %{_pam_secconfdir} %dir %{_pam_secdistconfdir} -%dir %{_prefix}/lib/motd.d -%if %{defined config_noreplace} -%config(noreplace) %{_pam_confdir}/other -%config(noreplace) %{_pam_confdir}/common-* -%else %{_pam_vendordir}/other %{_pam_vendordir}/common-* %{_pam_vendordir}/postlogin-* -%endif %{_distconfdir}/environment %{_pam_secdistconfdir}/access.conf %{_pam_secdistconfdir}/group.conf @@ -466,7 +420,6 @@ done %{_sbindir}/pam_namespace_helper %{_sbindir}/pwhistory_helper %verify(not mode) %attr(4755,root,shadow) %{_sbindir}/unix_chkpwd -%verify(not mode) %attr(4755,root,shadow) %{_sbindir}/unix2_chkpwd %attr(0700,root,root) %{_sbindir}/unix_update %{_unitdir}/pam_namespace.service %{_tmpfilesdir}/pam.conf @@ -570,7 +523,6 @@ done %{_mandir}/man8/pam_wheel.8%{?ext_man} %{_mandir}/man8/pam_xauth.8%{?ext_man} %{_mandir}/man8/pwhistory_helper.8%{?ext_man} -%{_mandir}/man8/unix2_chkpwd.8%{?ext_man} %{_mandir}/man8/unix_chkpwd.8%{?ext_man} %{_mandir}/man8/unix_update.8%{?ext_man} diff --git a/pam_issue-systemd.patch b/pam_issue-systemd.patch deleted file mode 100644 index 40e3bfb..0000000 --- a/pam_issue-systemd.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 8401cef10cd5f62849c5fcfef4c82db92712296c Mon Sep 17 00:00:00 2001 -From: Thorsten Kukuk -Date: Wed, 4 Sep 2024 16:07:56 +0200 -Subject: [PATCH] pam_issue: only count class user - -Since systemd added new types of classes (e.g. manager*), we cannot -use the count of all sessions anymore, but have to check which class -this is. - -This is backward compatible, systemd v209 or newer is required. ---- - modules/pam_issue/pam_issue.c | 20 +++++++++++++++++++- - 1 file changed, 19 insertions(+), 1 deletion(-) - -diff --git a/modules/pam_issue/pam_issue.c b/modules/pam_issue/pam_issue.c -index aade642ec5..e2c555c405 100644 ---- a/modules/pam_issue/pam_issue.c -+++ b/modules/pam_issue/pam_issue.c -@@ -165,13 +165,31 @@ read_issue_quoted(pam_handle_t *pamh, FILE *fp, char **prompt) - { - unsigned int users = 0; - #ifdef USE_LOGIND -- int sessions = sd_get_sessions(NULL); -+ char **sessions_list; -+ int sessions = sd_get_sessions(&sessions_list); - - if (sessions < 0) { - pam_syslog(pamh, LOG_ERR, "logind error: %s", - strerror(-sessions)); - _pam_drop(issue); - return PAM_SERVICE_ERR; -+ } else if (sessions > 0 && sessions_list != NULL) { -+ int i; -+ -+ for (i = 0; i < sessions; i++) { -+ char *class; -+ -+ if (sd_session_get_class(sessions_list[i], &class) < 0 || class == NULL) -+ continue; -+ -+ if (strncmp(class, "user", 4) == 0) // user, user-early, user-incomplete -+ users++; -+ free(class); -+ } -+ -+ for (i = 0; i < sessions; i++) -+ free(sessions_list[i]); -+ free(sessions_list); - } else { - users = sessions; - } diff --git a/pam_limits-systemd.patch b/pam_limits-systemd.patch deleted file mode 100644 index 3934ba9..0000000 --- a/pam_limits-systemd.patch +++ /dev/null @@ -1,157 +0,0 @@ -From 12bb33b56dee6d6b05200d4b776c7e6de3d0df91 Mon Sep 17 00:00:00 2001 -From: Thorsten Kukuk -Date: Fri, 6 Sep 2024 11:55:46 +0200 -Subject: [PATCH] pam_limits: use systemd-logind instead of utmp (#822) - -The utmp database is unreliable for counting logged in users, since -there is no standard which defines who should create an entry at which -time for which reason. And it has a Y2038 problem with glibc/x86-64. -Query systemd-logind for the number of user sessions instead. ---- - modules/pam_limits/Makefile.am | 4 +- - modules/pam_limits/pam_limits.c | 81 +++++++++++++++++++++++++++++++-- - 2 files changed, 80 insertions(+), 5 deletions(-) - -diff --git a/modules/pam_limits/Makefile.am b/modules/pam_limits/Makefile.am -index ab3cf33ed..3f64d79bb 100644 ---- a/modules/pam_limits/Makefile.am -+++ b/modules/pam_limits/Makefile.am -@@ -24,7 +24,7 @@ limits_conf_dir = $(SCONFIGDIR)/limits.d - - AM_CFLAGS = -I$(top_srcdir)/libpam/include \ - -DLIMITS_FILE_DIR=\"$(limits_conf_dir)\" \ -- $(WARN_CFLAGS) -+ $(LOGIND_CFLAGS) $(WARN_CFLAGS) - AM_LDFLAGS = -no-undefined -avoid-version -module - if HAVE_VERSIONING - AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map -@@ -32,7 +32,7 @@ endif - - securelib_LTLIBRARIES = pam_limits.la - pam_limits_la_LIBADD = $(top_builddir)/libpam_internal/libpam_internal.la \ -- $(top_builddir)/libpam/libpam.la -+ $(top_builddir)/libpam/libpam.la $(SYSTEMD_LIBS) - - dist_secureconf_DATA = limits.conf - -diff --git a/modules/pam_limits/pam_limits.c b/modules/pam_limits/pam_limits.c -index 75c584fca..1e4dfa3d0 100644 ---- a/modules/pam_limits/pam_limits.c -+++ b/modules/pam_limits/pam_limits.c -@@ -36,7 +36,12 @@ - #include - #include - #include -+#ifdef USE_LOGIND -+#include -+#else - #include -+#endif -+ - #ifndef UT_USER /* some systems have ut_name instead of ut_user */ - #define UT_USER ut_user - #endif -@@ -240,7 +245,6 @@ static int - check_logins (pam_handle_t *pamh, const char *name, int limit, int ctrl, - struct pam_limit_s *pl) - { -- struct utmp *ut; - int count; - - if (ctrl & PAM_DEBUG_ARG) { -@@ -255,8 +259,6 @@ check_logins (pam_handle_t *pamh, const char *name, int limit, int ctrl, - return LOGIN_ERR; - } - -- setutent(); -- - /* Because there is no definition about when an application - actually adds a utmp entry, some applications bizarrely do the - utmp call before the have PAM authenticate them to the system: -@@ -273,6 +275,78 @@ check_logins (pam_handle_t *pamh, const char *name, int limit, int ctrl, - count = 1; - } - -+#ifdef USE_LOGIND -+ char **sessions_list; -+ int sessions = sd_get_sessions(&sessions_list); -+ -+ /* maxlogins needs to be 2 with systemd-logind because -+ of the systemd --user process started with first login by -+ pam_systemd. -+ Which is also calling pam_limits, but in this very first special -+ case the session does already exist and is counted twice. -+ With start of the second session, session manager is already running -+ and no longer counted. */ -+ if (limit == 1) { -+ pam_syslog(pamh, LOG_WARNING, "Maxlogin limit needs to be 2 or higher with systemd-logind"); -+ return LIMIT_ERR; -+ } -+ -+ if (sessions < 0) { -+ pam_syslog(pamh, LOG_ERR, "logind error getting session list: %s", -+ strerror(-sessions)); -+ return LIMIT_ERR; -+ } else if (sessions > 0 && sessions_list != NULL && !pl->flag_numsyslogins) { -+ int i; -+ -+ for (i = 0; i < sessions; i++) { -+ char *user = NULL; -+ char *class = NULL; -+ -+ if (sd_session_get_class(sessions_list[i], &class) < 0 || class == NULL) -+ continue; -+ -+ if (strncmp(class, "user", 4) != 0) { /* user, user-early, user-incomplete */ -+ free (class); -+ continue; -+ } -+ free (class); -+ -+ if (sd_session_get_username(sessions_list[i], &user) < 0 || user == NULL) { -+ pam_syslog(pamh, LOG_ERR, "logind error getting username: %s", -+ strerror(-sessions)); -+ return LIMIT_ERR; -+ } -+ -+ if (((pl->login_limit_def == LIMITS_DEF_USER) -+ || (pl->login_limit_def == LIMITS_DEF_GROUP) -+ || (pl->login_limit_def == LIMITS_DEF_DEFAULT)) -+ && strcmp(name, user) != 0) { -+ free(user); -+ continue; -+ } -+ if ((pl->login_limit_def == LIMITS_DEF_ALLGROUP) -+ && pl->login_group != NULL -+ && !pam_modutil_user_in_group_nam_nam(pamh, user, pl->login_group)) { -+ free(user); -+ continue; -+ } -+ free(user); -+ -+ if (++count > limit) { -+ break; -+ } -+ } -+ for (i = 0; i < sessions; i++) -+ free(sessions_list[i]); -+ free(sessions_list); -+ } else { -+ count = sessions; -+ } -+#else -+ struct utmp *ut; -+ -+ setutent(); -+ - while((ut = getutent())) { - #ifdef USER_PROCESS - if (ut->ut_type != USER_PROCESS) { -@@ -311,6 +385,7 @@ check_logins (pam_handle_t *pamh, const char *name, int limit, int ctrl, - } - } - endutent(); -+#endif - if (count > limit) { - if (name) { - pam_syslog(pamh, LOG_NOTICE, diff --git a/pam_mkhomedir-Use-vendordir-when-defined.patch b/pam_mkhomedir-Use-vendordir-when-defined.patch new file mode 100644 index 0000000..31e4063 --- /dev/null +++ b/pam_mkhomedir-Use-vendordir-when-defined.patch @@ -0,0 +1,315 @@ +From c4a53492e1b7aebcf7a65a778d9e3a78f196d117 Mon Sep 17 00:00:00 2001 +From: vlefebvre +Date: Tue, 15 Jul 2025 15:42:50 +0200 +Subject: [PATCH] pam_mkhomedir: Use vendordir when defined + +* When the configuration is split between /etc and vendordir, + pam_mkhomedir now takes care of the distribution-provided + skeleton directory. + +* mkhomedir_helper gets a new optional argument: vendordir. + If specified, the home directory will be populated by contents of + the directory specified by skeldir argument, followed by contents + of the directory specified by vendordir argument. + +Signed-off-by: vlefebvre +--- + modules/pam_mkhomedir/mkhomedir_helper.8.xml | 12 ++- + modules/pam_mkhomedir/mkhomedir_helper.c | 91 +++++++++++++------- + modules/pam_mkhomedir/pam_mkhomedir.8.xml | 8 +- + modules/pam_mkhomedir/pam_mkhomedir.c | 14 ++- + 4 files changed, 88 insertions(+), 37 deletions(-) + +diff --git a/modules/pam_mkhomedir/mkhomedir_helper.8.xml b/modules/pam_mkhomedir/mkhomedir_helper.8.xml +index 0f4c4b40..87b83a40 100644 +--- a/modules/pam_mkhomedir/mkhomedir_helper.8.xml ++++ b/modules/pam_mkhomedir/mkhomedir_helper.8.xml +@@ -24,6 +24,9 @@ + path-to-skel + + home_mode ++ ++ path-to-vendor-skel ++ + + + +@@ -48,6 +51,13 @@ + umask. + + ++ ++ path-to-vendor-skel doesn't have default ++ value. When set to a path, home directory will be ++ populated by contents of path-to-skel first, ++ and then by contents of path. ++ ++ + + The helper is separated from the module to not require direct access from + login SELinux domains to the contents of user home directories. The +@@ -77,4 +87,4 @@ + + + +- +\ No newline at end of file ++ +diff --git a/modules/pam_mkhomedir/mkhomedir_helper.c b/modules/pam_mkhomedir/mkhomedir_helper.c +index 0c05ee9c..1d741c5d 100644 +--- a/modules/pam_mkhomedir/mkhomedir_helper.c ++++ b/modules/pam_mkhomedir/mkhomedir_helper.c +@@ -36,7 +36,7 @@ static unsigned long u_mask = 0022; + static const char *skeldir = "/etc/skel"; + + static int create_homedir(struct dir_spec *, const struct passwd *, mode_t, +- const char *, const char *); ++ const char *, const char *, const char *); + + static int + dir_spec_open(struct dir_spec *spec, const char *path) +@@ -88,7 +88,7 @@ dir_spec_close(struct dir_spec *spec) + + static int + copy_entry(struct dir_spec *parent, const struct passwd *pwd, mode_t dir_mode, +- const char *source, struct dirent *dent) ++ const char *source, struct dirent *dent, const char *vendordir) + { + char remark[BUFSIZ]; + int srcfd = -1, destfd = -1; +@@ -96,6 +96,7 @@ copy_entry(struct dir_spec *parent, const struct passwd *pwd, mode_t dir_mode, + int retval = PAM_SESSION_ERR; + struct stat st; + char *newsource; ++ char *newvendordir = NULL; + + /* Determine what kind of file it is. */ + if ((newsource = pam_asprintf("%s/%s", source, dent->d_name)) == NULL) +@@ -114,8 +115,23 @@ copy_entry(struct dir_spec *parent, const struct passwd *pwd, mode_t dir_mode, + /* If it's a directory, recurse. */ + if (S_ISDIR(st.st_mode)) + { ++ if (vendordir != NULL) ++ { ++ if ((newvendordir = pam_asprintf("%s/%s", vendordir, dent->d_name)) == NULL) ++ { ++ pam_syslog(NULL, LOG_CRIT, "asprintf failed for 'newvendordir'"); ++ retval = PAM_BUF_ERR; ++ goto go_out; ++ } ++ if (lstat(newvendordir, &st) != 0) ++ { ++ free(newvendordir); ++ newvendordir = NULL; ++ } ++ } ++ + retval = create_homedir(parent, pwd, dir_mode & (~u_mask), newsource, +- dent->d_name); ++ dent->d_name, newvendordir); + goto go_out; + } + +@@ -259,19 +275,22 @@ copy_entry(struct dir_spec *parent, const struct passwd *pwd, mode_t dir_mode, + close(destfd); + + free(newsource); +- ++ free(newvendordir); + return retval; + } + + /* Do the actual work of creating a home dir */ + static int + create_homedir(struct dir_spec *parent, const struct passwd *pwd, +- mode_t dir_mode, const char *source, const char *dest) ++ mode_t dir_mode, const char *source, const char *dest, ++ const char *vendordir) + { + DIR *d = NULL; + struct dirent *dent; + struct dir_spec base; + int retval = PAM_SESSION_ERR; ++ const char *sourcedirs[] = {source, vendordir}; ++ unsigned int idx = 0; + + /* Create the new directory */ + if (mkdirat(parent->fd, dest, 0700)) +@@ -289,32 +308,35 @@ create_homedir(struct dir_spec *parent, const struct passwd *pwd, + goto go_out; + } + +- /* See if we need to copy the skel dir over. */ +- if ((source == NULL) || (strlen(source) == 0)) +- { +- retval = PAM_SUCCESS; +- goto go_out; +- } +- +- /* Scan the directory */ +- d = opendir(source); +- if (d == NULL) +- { +- pam_syslog(NULL, LOG_DEBUG, "unable to read directory %s: %m", source); +- retval = PAM_PERM_DENIED; +- goto go_out; +- } +- +- for (dent = readdir(d); dent != NULL; dent = readdir(d)) ++ /* Scan source directories */ ++ for (idx = 0; idx < PAM_ARRAY_SIZE(sourcedirs); idx++) + { +- /* Skip some files.. */ +- if (strcmp(dent->d_name,".") == 0 || +- strcmp(dent->d_name,"..") == 0) +- continue; ++ /* See if we need to copy the source skel dir over. */ ++ if ((sourcedirs[idx] == NULL) || strlen(sourcedirs[idx]) == 0) ++ continue; ++ d = opendir(sourcedirs[idx]); ++ if (d == NULL) ++ { ++ pam_syslog(NULL, LOG_DEBUG, "unable to read directory %s: %m", ++ sourcedirs[idx]); ++ retval = PAM_PERM_DENIED; ++ goto go_out; ++ } + +- retval = copy_entry(&base, pwd, dir_mode, source, dent); +- if (retval != PAM_SUCCESS) +- goto go_out; ++ for (dent = readdir(d); dent != NULL; dent = readdir(d)) ++ { ++ /* Skip some files.. */ ++ if (strcmp(dent->d_name,".") == 0 || ++ strcmp(dent->d_name,"..") == 0) ++ continue; ++ ++ retval = copy_entry(&base, pwd, dir_mode, sourcedirs[idx], dent, ++ sourcedirs[idx] == vendordir ? NULL : vendordir); ++ if (retval != PAM_SUCCESS) ++ goto go_out; ++ } ++ closedir(d); ++ d = NULL; + } + + retval = PAM_SUCCESS; +@@ -340,7 +362,8 @@ create_homedir(struct dir_spec *parent, const struct passwd *pwd, + + static int + create_homedir_helper(const struct passwd *_pwd, mode_t home_mode, +- const char *_skeldir, const char *_homedir) ++ const char *_skeldir, const char *_homedir, ++ const char *_vendordir) + { + int retval = PAM_SESSION_ERR; + struct dir_spec base; +@@ -357,7 +380,7 @@ create_homedir_helper(const struct passwd *_pwd, mode_t home_mode, + } + *cp = '/'; + +- retval = create_homedir(&base, _pwd, home_mode, _skeldir, cp + 1); ++ retval = create_homedir(&base, _pwd, home_mode, _skeldir, cp + 1, _vendordir); + + go_out: + dir_spec_close(&base); +@@ -399,6 +422,7 @@ main(int argc, char *argv[]) + struct stat st; + char *eptr; + unsigned long home_mode = 0; ++ const char *vendordir = NULL; + + if (argc < 2) { + fprintf(stderr, "Usage: %s [ [ []]]\n", argv[0]); +@@ -433,6 +457,9 @@ main(int argc, char *argv[]) + } + } + ++ if (argc >= 6) ++ vendordir = argv[5]; ++ + if (home_mode == 0) + home_mode = 0777 & ~u_mask; + +@@ -449,5 +476,5 @@ main(int argc, char *argv[]) + if (make_parent_dirs(pwd->pw_dir, 0) != PAM_SUCCESS) + return PAM_PERM_DENIED; + +- return create_homedir_helper(pwd, home_mode, skeldir, pwd->pw_dir); ++ return create_homedir_helper(pwd, home_mode, skeldir, pwd->pw_dir, vendordir); + } +diff --git a/modules/pam_mkhomedir/pam_mkhomedir.8.xml b/modules/pam_mkhomedir/pam_mkhomedir.8.xml +index ad957248..42f42c58 100644 +--- a/modules/pam_mkhomedir/pam_mkhomedir.8.xml ++++ b/modules/pam_mkhomedir/pam_mkhomedir.8.xml +@@ -46,6 +46,12 @@ + /etc/skel/) is used to copy default files + and also sets a umask for the creation. + ++ ++ ++ %vendordir%/skel will also be used unless ++ skel option is specified. ++ ++ + + The new users home directory will not be removed after logout + of the user. +@@ -213,4 +219,4 @@ + pam_mkhomedir was written by Jason Gunthorpe <jgg@debian.org>. + + +- +\ No newline at end of file ++ +diff --git a/modules/pam_mkhomedir/pam_mkhomedir.c b/modules/pam_mkhomedir/pam_mkhomedir.c +index f090deee..5b08b6fd 100644 +--- a/modules/pam_mkhomedir/pam_mkhomedir.c ++++ b/modules/pam_mkhomedir/pam_mkhomedir.c +@@ -60,6 +60,11 @@ + #define LOGIN_DEFS "/etc/login.defs" + #define UMASK_DEFAULT "0022" + ++#define SKELDIR "/etc/skel" ++#ifdef VENDORDIR ++#define VENDOR_SKELDIR (VENDORDIR "/skel") ++#endif ++ + struct options_t { + int ctrl; + const char *umask; +@@ -73,7 +78,7 @@ _pam_parse (const pam_handle_t *pamh, int flags, int argc, const char **argv, + { + opt->ctrl = 0; + opt->umask = NULL; +- opt->skeldir = "/etc/skel"; ++ opt->skeldir = NULL; + + /* does the application require quiet? */ + if ((flags & PAM_SILENT) == PAM_SILENT) +@@ -154,7 +159,7 @@ create_homedir (pam_handle_t *pamh, options_t *opt, + child = fork(); + if (child == 0) { + static char *envp[] = { NULL }; +- const char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL }; ++ const char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + + if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_PIPE_FD, + PAM_MODUTIL_PIPE_FD, +@@ -165,8 +170,11 @@ create_homedir (pam_handle_t *pamh, options_t *opt, + args[0] = MKHOMEDIR_HELPER; + args[1] = user; + args[2] = opt->umask ? opt->umask : UMASK_DEFAULT; +- args[3] = opt->skeldir; ++ args[3] = opt->skeldir ? opt->skeldir : SKELDIR; + args[4] = login_homemode; ++#ifdef VENDORDIR ++ args[5] = opt->skeldir ? NULL : VENDOR_SKELDIR; ++#endif + + DIAG_PUSH_IGNORE_CAST_QUAL; + execve(MKHOMEDIR_HELPER, (char **)args, envp); +-- +2.51.0 + diff --git a/post-v1.7.1.patch b/post-v1.7.1.patch new file mode 100644 index 0000000..0e1aca8 --- /dev/null +++ b/post-v1.7.1.patch @@ -0,0 +1,1273 @@ +diff --git a/doc/specs/draft-morgan-pam.raw b/doc/specs/draft-morgan-pam.raw +index 8fdb0502..253d7e2c 100644 +--- a/doc/specs/draft-morgan-pam.raw ++++ b/doc/specs/draft-morgan-pam.raw +@@ -442,7 +442,7 @@ available agents on the system is implementation specific. + pamc_start() function returns NULL on failure. Otherwise, the return + value is a pointer to an opaque data type which provides a handle to + the libpamc library. On systems where threading is available, the +-libpamc libraray is thread safe provided a single (pamc_handler_t *) ++libpamc library is thread safe provided a single (pamc_handler_t *) + is used by each thread. + + #$$$$ Client (Applicant) selection of agents +diff --git a/examples/check_user.c b/examples/check_user.c +index 89cc137b..bb100bc5 100644 +--- a/examples/check_user.c ++++ b/examples/check_user.c +@@ -6,8 +6,8 @@ + + You need to add the following (or equivalent) to the /etc/pam.conf file. + # check authorization +- check auth required pam_unix_auth.so +- check account required pam_unix_acct.so ++ check auth required pam_unix.so ++ check account required pam_unix.so + */ + + #include +diff --git a/examples/tty_conv.c b/examples/tty_conv.c +index 59bbb3b3..2219a51e 100644 +--- a/examples/tty_conv.c ++++ b/examples/tty_conv.c +@@ -18,26 +18,26 @@ + static void echoOff(int fd, int off) + { + struct termios tty; +- if (ioctl(fd, TCGETA, &tty) < 0) ++ if (tcgetattr(fd, &tty) < 0) + { +- fprintf(stderr, "TCGETA failed: %s\n", strerror(errno)); ++ fprintf(stderr, "tcgetattr failed: %s\n", strerror(errno)); + return; + } + + if (off) + { + tty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); +- if (ioctl(fd, TCSETAF, &tty) < 0) ++ if (tcsetattr(fd, TCSAFLUSH, &tty) < 0) + { +- fprintf(stderr, "TCSETAF failed: %s\n", strerror(errno)); ++ fprintf(stderr, "tcsetattr(TCSAFLUSH) failed: %s\n", strerror(errno)); + } + } + else + { + tty.c_lflag |= (ECHO | ECHOE | ECHOK | ECHONL); +- if (ioctl(fd, TCSETAW, &tty) < 0) ++ if (tcsetattr(fd, TCSADRAIN, &tty) < 0) + { +- fprintf(stderr, "TCSETAW failed: %s\n", strerror(errno)); ++ fprintf(stderr, "tcsetattr(TCSADRAIN) failed: %s\n", strerror(errno)); + } + } + } +diff --git a/libpam/pam_modutil_cleanup.c b/libpam/pam_modutil_cleanup.c +index 2077cbd7..46233736 100644 +--- a/libpam/pam_modutil_cleanup.c ++++ b/libpam/pam_modutil_cleanup.c +@@ -5,8 +5,12 @@ + */ + + #include "pam_modutil_private.h" ++#include "pam_inline.h" + ++#include ++#include + #include ++#include + + void + pam_modutil_cleanup (pam_handle_t *pamh UNUSED, void *data, +@@ -15,3 +19,39 @@ pam_modutil_cleanup (pam_handle_t *pamh UNUSED, void *data, + /* junk it */ + free(data); + } ++ ++void ++pam_modutil_cleanup_group (pam_handle_t *pamh UNUSED, void *data, ++ int error_status UNUSED) ++{ ++ struct group *gr = data; ++ ++ if (gr && gr->gr_passwd) ++ pam_overwrite_string(gr->gr_passwd); ++ ++ free(data); ++} ++ ++void ++pam_modutil_cleanup_passwd (pam_handle_t *pamh UNUSED, void *data, ++ int error_status UNUSED) ++{ ++ struct passwd *pw = data; ++ ++ if (pw && pw->pw_passwd) ++ pam_overwrite_string(pw->pw_passwd); ++ ++ free(data); ++} ++ ++void ++pam_modutil_cleanup_shadow (pam_handle_t *pamh UNUSED, void *data, ++ int error_status UNUSED) ++{ ++ struct spwd *sp = data; ++ ++ if (sp && sp->sp_pwdp) ++ pam_overwrite_string(sp->sp_pwdp); ++ ++ free(data); ++} +diff --git a/libpam/pam_modutil_getgrgid.c b/libpam/pam_modutil_getgrgid.c +index 6c2bb31b..fa3436c5 100644 +--- a/libpam/pam_modutil_getgrgid.c ++++ b/libpam/pam_modutil_getgrgid.c +@@ -62,7 +62,7 @@ pam_modutil_getgrgid(pam_handle_t *pamh, gid_t gid) + status = PAM_NO_MODULE_DATA; + if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) { + status = pam_set_data(pamh, data_name, +- result, pam_modutil_cleanup); ++ result, pam_modutil_cleanup_group); + } + free(data_name); + if (status == PAM_SUCCESS) { +diff --git a/libpam/pam_modutil_getgrnam.c b/libpam/pam_modutil_getgrnam.c +index 418b9e47..533a8ce6 100644 +--- a/libpam/pam_modutil_getgrnam.c ++++ b/libpam/pam_modutil_getgrnam.c +@@ -62,7 +62,7 @@ pam_modutil_getgrnam(pam_handle_t *pamh, const char *group) + status = PAM_NO_MODULE_DATA; + if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) { + status = pam_set_data(pamh, data_name, +- result, pam_modutil_cleanup); ++ result, pam_modutil_cleanup_group); + } + free(data_name); + if (status == PAM_SUCCESS) { +diff --git a/libpam/pam_modutil_getpwnam.c b/libpam/pam_modutil_getpwnam.c +index 5701ba9c..de654aeb 100644 +--- a/libpam/pam_modutil_getpwnam.c ++++ b/libpam/pam_modutil_getpwnam.c +@@ -62,7 +62,7 @@ pam_modutil_getpwnam(pam_handle_t *pamh, const char *user) + status = PAM_NO_MODULE_DATA; + if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) { + status = pam_set_data(pamh, data_name, +- result, pam_modutil_cleanup); ++ result, pam_modutil_cleanup_passwd); + } + free(data_name); + if (status == PAM_SUCCESS) { +diff --git a/libpam/pam_modutil_getpwuid.c b/libpam/pam_modutil_getpwuid.c +index d3bb7231..6534958c 100644 +--- a/libpam/pam_modutil_getpwuid.c ++++ b/libpam/pam_modutil_getpwuid.c +@@ -62,7 +62,7 @@ pam_modutil_getpwuid(pam_handle_t *pamh, uid_t uid) + status = PAM_NO_MODULE_DATA; + if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) { + status = pam_set_data(pamh, data_name, +- result, pam_modutil_cleanup); ++ result, pam_modutil_cleanup_passwd); + } + free(data_name); + if (status == PAM_SUCCESS) { +diff --git a/libpam/pam_modutil_getspnam.c b/libpam/pam_modutil_getspnam.c +index 9aa6ac9a..9733eda0 100644 +--- a/libpam/pam_modutil_getspnam.c ++++ b/libpam/pam_modutil_getspnam.c +@@ -62,7 +62,7 @@ pam_modutil_getspnam(pam_handle_t *pamh, const char *user) + status = PAM_NO_MODULE_DATA; + if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) { + status = pam_set_data(pamh, data_name, +- result, pam_modutil_cleanup); ++ result, pam_modutil_cleanup_shadow); + } + free(data_name); + if (status == PAM_SUCCESS) { +diff --git a/libpam/pam_modutil_private.h b/libpam/pam_modutil_private.h +index 98a30f68..611c7696 100644 +--- a/libpam/pam_modutil_private.h ++++ b/libpam/pam_modutil_private.h +@@ -20,5 +20,14 @@ + extern void + pam_modutil_cleanup(pam_handle_t *pamh, void *data, + int error_status); ++extern void ++pam_modutil_cleanup_group(pam_handle_t *pamh, void *data, ++ int error_status); ++extern void ++pam_modutil_cleanup_passwd(pam_handle_t *pamh, void *data, ++ int error_status); ++extern void ++pam_modutil_cleanup_shadow(pam_handle_t *pamh, void *data, ++ int error_status); + + #endif /* PAMMODUTIL_PRIVATE_H */ +diff --git a/meson.build b/meson.build +index 0827a53e..af83cd51 100644 +--- a/meson.build ++++ b/meson.build +@@ -53,6 +53,7 @@ cdata.set10('DEFAULT_USERGROUPS_SETTING', get_option('usergroups')) + cdata.set('PAM_USERTYPE_UIDMIN', get_option('uidmin')) + cdata.set('PAM_USERTYPE_OVERFLOW_UID', get_option('kernel-overflow-uid')) + cdata.set('PAM_MISC_CONV_BUFSIZE', get_option('misc-conv-bufsize')) ++cdata.set('PAM_UNIX_TRY_GETSPNAM', get_option('pam_unix-try-getspnam') ? 1 : false) + + cdata.set_quoted('_PAM_ISA', + get_option('isadir') != '' ? get_option('isadir') : '../..' / fs.name(libdir) / 'security') +@@ -150,6 +151,7 @@ add_project_link_arguments( + # --as-needed and --no-undefined are enabled by default + cc.get_supported_link_arguments([ + '-Wl,--fatal-warnings', ++ '-Wl,--no-undefined-version', + '-Wl,-O1', + ]), + language: 'c') +@@ -263,6 +265,10 @@ foreach f: ['crypt_r'] + endif + endforeach + ++libpwaccess = dependency('libpwaccess', required: get_option('pwaccess')) ++if libpwaccess.found() ++ cdata.set('USE_PWACCESS', 1) ++endif + + libeconf = dependency('libeconf', version: '>= 0.5.0', required: get_option('econf')) + if libeconf.found() +diff --git a/meson_options.txt b/meson_options.txt +index a172ceea..35d979b5 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -14,6 +14,8 @@ option('elogind', type: 'feature', value: 'auto', + description: 'logind support in pam_issue, pam_limits, and pam_timestamp via elogind') + option('openssl', type: 'feature', value: 'disabled', + description: 'Use OpenSSL crypto libraries in pam_timestamp') ++option('pwaccess', type: 'feature', value: 'auto', ++ description: 'libpwaccess support in pam_unix') + option('selinux', type: 'feature', value: 'auto', + description: 'SELinux support') + option('nis', type: 'feature', value: 'auto', +@@ -85,7 +87,7 @@ option('xauth', type: 'string', + description: 'Additional path to check for xauth when it is called from pam_xauth') + option('randomdev', type: 'string', + description: 'Random device to use instead of /dev/urandom') +-option('vendordir', type: 'string', ++option('vendordir', type: 'string', value: '/usr/share/pam', + description: 'Distribution provided configuration files directory') + + option('pam_userdb', type: 'feature', value: 'auto', +@@ -99,3 +101,5 @@ option('pam_lastlog', type: 'feature', value: 'disabled', + description: 'pam_lastlog module') + option('pam_unix', type: 'feature', value: 'auto', + description: 'pam_unix module') ++option('pam_unix-try-getspnam', type: 'boolean', value: false, ++ description: 'Let pam_unix try getspnam to obtain shadow password file entry before falling back to invoking the helper') +diff --git a/modules/maps/modules-account-session.map b/modules/maps/modules-account-session.map +new file mode 100644 +index 00000000..da87def8 +--- /dev/null ++++ b/modules/maps/modules-account-session.map +@@ -0,0 +1,7 @@ ++{ ++ global: ++ pam_sm_acct_mgmt; ++ pam_sm_close_session; ++ pam_sm_open_session; ++ local: *; ++}; +diff --git a/modules/maps/modules-account.map b/modules/maps/modules-account.map +new file mode 100644 +index 00000000..2b35aeb7 +--- /dev/null ++++ b/modules/maps/modules-account.map +@@ -0,0 +1,5 @@ ++{ ++ global: ++ pam_sm_acct_mgmt; ++ local: *; ++}; +diff --git a/modules/modules.map b/modules/maps/modules-auth-account-password-session.map +similarity index 100% +rename from modules/modules.map +rename to modules/maps/modules-auth-account-password-session.map +diff --git a/modules/maps/modules-auth-account-password.map b/modules/maps/modules-auth-account-password.map +new file mode 100644 +index 00000000..1f9e00b1 +--- /dev/null ++++ b/modules/maps/modules-auth-account-password.map +@@ -0,0 +1,8 @@ ++{ ++ global: ++ pam_sm_acct_mgmt; ++ pam_sm_authenticate; ++ pam_sm_chauthtok; ++ pam_sm_setcred; ++ local: *; ++}; +diff --git a/modules/maps/modules-auth-account-session.map b/modules/maps/modules-auth-account-session.map +new file mode 100644 +index 00000000..dcc94d87 +--- /dev/null ++++ b/modules/maps/modules-auth-account-session.map +@@ -0,0 +1,9 @@ ++{ ++ global: ++ pam_sm_acct_mgmt; ++ pam_sm_authenticate; ++ pam_sm_close_session; ++ pam_sm_open_session; ++ pam_sm_setcred; ++ local: *; ++}; +diff --git a/modules/maps/modules-auth-account.map b/modules/maps/modules-auth-account.map +new file mode 100644 +index 00000000..71928f8b +--- /dev/null ++++ b/modules/maps/modules-auth-account.map +@@ -0,0 +1,7 @@ ++{ ++ global: ++ pam_sm_acct_mgmt; ++ pam_sm_authenticate; ++ pam_sm_setcred; ++ local: *; ++}; +diff --git a/modules/maps/modules-auth-session.map b/modules/maps/modules-auth-session.map +new file mode 100644 +index 00000000..40ec74d6 +--- /dev/null ++++ b/modules/maps/modules-auth-session.map +@@ -0,0 +1,8 @@ ++{ ++ global: ++ pam_sm_authenticate; ++ pam_sm_close_session; ++ pam_sm_open_session; ++ pam_sm_setcred; ++ local: *; ++}; +diff --git a/modules/maps/modules-auth.map b/modules/maps/modules-auth.map +new file mode 100644 +index 00000000..eb970e07 +--- /dev/null ++++ b/modules/maps/modules-auth.map +@@ -0,0 +1,6 @@ ++{ ++ global: ++ pam_sm_authenticate; ++ pam_sm_setcred; ++ local: *; ++}; +diff --git a/modules/maps/modules-password.map b/modules/maps/modules-password.map +new file mode 100644 +index 00000000..b2538d45 +--- /dev/null ++++ b/modules/maps/modules-password.map +@@ -0,0 +1,5 @@ ++{ ++ global: ++ pam_sm_chauthtok; ++ local: *; ++}; +diff --git a/modules/maps/modules-session.map b/modules/maps/modules-session.map +new file mode 100644 +index 00000000..b413987f +--- /dev/null ++++ b/modules/maps/modules-session.map +@@ -0,0 +1,6 @@ ++{ ++ global: ++ pam_sm_close_session; ++ pam_sm_open_session; ++ local: *; ++}; +diff --git a/modules/meson.build b/modules/meson.build +index 20cebdbb..68a7a63c 100644 +--- a/modules/meson.build ++++ b/modules/meson.build +@@ -1,9 +1,3 @@ +-pam_module_map = 'modules.map' +-pam_module_map_path = meson.current_source_dir() / pam_module_map +- +-pam_module_link_deps = ['..' / pam_module_map] +-pam_module_link_args = ['-Wl,--version-script=' + pam_module_map_path] +- + subdir('pam_access') + subdir('pam_canonicalize_user') + subdir('pam_debug') +diff --git a/modules/module-meson.build b/modules/module-meson.build +index dce38b90..7920dc8b 100644 +--- a/modules/module-meson.build ++++ b/modules/module-meson.build +@@ -128,6 +128,12 @@ if module == 'pam_xauth' + pam_module_deps += [libselinux] + endif + ++pam_module_map = 'module.map' ++pam_module_map_path = meson.current_source_dir() / pam_module_map ++ ++pam_module_link_deps = [pam_module_map] ++pam_module_link_args = ['-Wl,--version-script=' + pam_module_map_path] ++ + pam_module = shared_module( + module, + name_prefix: '', +@@ -425,7 +431,7 @@ if module == 'pam_unix' + ], + c_args: ['-DHELPER_COMPILE="unix_chkpwd"'], + link_args: exe_link_args, +- dependencies: [libpam_internal_dep, libpam_dep, libcrypt, libselinux, libaudit], ++ dependencies: [libpam_internal_dep, libpam_dep, libcrypt, libselinux, libaudit, libpwaccess], + install: true, + install_dir: sbindir, + ) +@@ -441,7 +447,7 @@ if module == 'pam_unix' + ], + c_args: ['-DHELPER_COMPILE="unix_update"'], + link_args: exe_link_args, +- dependencies: [libpam_internal_dep, libpam_dep, libcrypt, libselinux, libaudit], ++ dependencies: [libpam_internal_dep, libpam_dep, libcrypt, libselinux, libaudit, libpwaccess], + install: true, + install_dir: sbindir, + ) +diff --git a/modules/pam_access/module.map b/modules/pam_access/module.map +new file mode 120000 +index 00000000..894551fb +--- /dev/null ++++ b/modules/pam_access/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account-password-session.map +\ No newline at end of file +diff --git a/modules/pam_canonicalize_user/module.map b/modules/pam_canonicalize_user/module.map +new file mode 120000 +index 00000000..190be9a2 +--- /dev/null ++++ b/modules/pam_canonicalize_user/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth.map +\ No newline at end of file +diff --git a/modules/pam_debug/module.map b/modules/pam_debug/module.map +new file mode 120000 +index 00000000..894551fb +--- /dev/null ++++ b/modules/pam_debug/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account-password-session.map +\ No newline at end of file +diff --git a/modules/pam_deny/module.map b/modules/pam_deny/module.map +new file mode 120000 +index 00000000..894551fb +--- /dev/null ++++ b/modules/pam_deny/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account-password-session.map +\ No newline at end of file +diff --git a/modules/pam_echo/module.map b/modules/pam_echo/module.map +new file mode 120000 +index 00000000..894551fb +--- /dev/null ++++ b/modules/pam_echo/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account-password-session.map +\ No newline at end of file +diff --git a/modules/pam_env/module.map b/modules/pam_env/module.map +new file mode 120000 +index 00000000..894551fb +--- /dev/null ++++ b/modules/pam_env/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account-password-session.map +\ No newline at end of file +diff --git a/modules/pam_env/pam_env.8.xml b/modules/pam_env/pam_env.8.xml +index c7889e0f..2bf7d7b2 100644 +--- a/modules/pam_env/pam_env.8.xml ++++ b/modules/pam_env/pam_env.8.xml +@@ -80,8 +80,8 @@ + pairs on separate lines. The path to this file can be specified with the + envfile option. + If this file has not been defined, the settings are read from the +- files /etc/security/environment and +- /etc/security/environment.d/*. ++ files /etc/environment and ++ /etc/environment.d/*. + If the file /etc/environment does not exist, the + settings are read from the files %vendordir%/environment, + %vendordir%/environment.d/* and +diff --git a/modules/pam_env/pam_env.c b/modules/pam_env/pam_env.c +index 496c8943..3158768e 100644 +--- a/modules/pam_env/pam_env.c ++++ b/modules/pam_env/pam_env.c +@@ -627,10 +627,15 @@ _expand_arg(pam_handle_t *pamh, char **value) + if ('\\' == *orig) { + ++orig; + if ('$' != *orig && '@' != *orig && '\\' != *orig) { +- D(("Unrecognized escaped character: <%c> - ignoring", *orig)); +- pam_syslog(pamh, LOG_ERR, +- "Unrecognized escaped character: <%c> - ignoring", +- *orig); ++ if (*orig) { ++ D(("Unrecognized escaped character: <%c> - ignoring", *orig)); ++ pam_syslog(pamh, LOG_ERR, ++ "Unrecognized escaped character: <%c> - ignoring", ++ *orig); ++ } else { ++ D(("Ignoring backslash at end of string")); ++ pam_syslog(pamh, LOG_ERR, "Ignoring backslash at end of string"); ++ } + } else { + /* Note the increment */ + if (_strbuf_add_char(&buf, *orig++)) { +diff --git a/modules/pam_exec/module.map b/modules/pam_exec/module.map +new file mode 120000 +index 00000000..894551fb +--- /dev/null ++++ b/modules/pam_exec/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account-password-session.map +\ No newline at end of file +diff --git a/modules/pam_faildelay/module.map b/modules/pam_faildelay/module.map +new file mode 120000 +index 00000000..190be9a2 +--- /dev/null ++++ b/modules/pam_faildelay/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth.map +\ No newline at end of file +diff --git a/modules/pam_faillock/faillock.conf b/modules/pam_faillock/faillock.conf +index 16d93df7..3e119013 100644 +--- a/modules/pam_faillock/faillock.conf ++++ b/modules/pam_faillock/faillock.conf +@@ -33,7 +33,7 @@ + # + # The length of the interval during which the consecutive + # authentication failures must happen for the user account +-# lock out is n seconds. ++# lock out is n seconds. + # The default is 900 (15 minutes). + # fail_interval = 900 + # +@@ -56,7 +56,7 @@ + # + # If a group name is specified with this option, members + # of the group will be handled by this module the same as +-# the root account (the options `even_deny_root>` and +-# `root_unlock_time` will apply to them. ++# the root account (the options `even_deny_root` and ++# `root_unlock_time` will apply to them). + # By default, the option is not set. + # admin_group = +diff --git a/modules/pam_faillock/module.map b/modules/pam_faillock/module.map +new file mode 120000 +index 00000000..6cc612ef +--- /dev/null ++++ b/modules/pam_faillock/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account.map +\ No newline at end of file +diff --git a/modules/pam_faillock/pam_faillock.8.xml b/modules/pam_faillock/pam_faillock.8.xml +index ce0ae050..42f87e81 100644 +--- a/modules/pam_faillock/pam_faillock.8.xml ++++ b/modules/pam_faillock/pam_faillock.8.xml +@@ -243,6 +243,14 @@ + user accounts allowing the adversary to infer that a particular account + is not existing on a system. + ++ ++ If the stack has not been run prior to the ++ stack, the logic to reset the failed login counter is intentionally skipped. This prevents ++ automated services, such as crond or systemd-user, ++ which might only perform account management tasks, from inadvertently clearing a user's ++ failed attempt records. This ensures the faillock counter is only reset by a service that ++ performs a full, successful authentication. ++ + + + +diff --git a/modules/pam_faillock/pam_faillock.c b/modules/pam_faillock/pam_faillock.c +index 2d847aeb..a8363b6c 100644 +--- a/modules/pam_faillock/pam_faillock.c ++++ b/modules/pam_faillock/pam_faillock.c +@@ -62,6 +62,8 @@ + #define FAILLOCK_ACTION_AUTHSUCC 1 + #define FAILLOCK_ACTION_AUTHFAIL 2 + ++#define FAILLOCK_AUTH_EXECUTED "pam_faillock:auth_executed" ++ + static int + args_parse(pam_handle_t *pamh, int argc, const char **argv, + int flags, struct options *opts) +@@ -478,6 +480,10 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, + goto err; + } + ++ rv = pam_set_data(pamh, FAILLOCK_AUTH_EXECUTED, (void *)1, NULL); ++ if (rv != PAM_SUCCESS) ++ goto err; ++ + if (!(opts.flags & FAILLOCK_FLAG_LOCAL_ONLY) || + check_local_user (pamh, opts.user) != 0) { + switch (opts.action) { +@@ -531,6 +537,7 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, + struct options opts; + int rv, fd = -1; + struct tally_data tallies; ++ const void *auth_flag; + + memset(&tallies, 0, sizeof(tallies)); + +@@ -541,6 +548,12 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, + + opts.action = FAILLOCK_ACTION_AUTHSUCC; + ++ rv = pam_get_data(pamh, FAILLOCK_AUTH_EXECUTED, &auth_flag); ++ if (rv == PAM_NO_MODULE_DATA) { ++ rv = PAM_SUCCESS; ++ goto err; ++ } ++ + if ((rv=get_pam_user(pamh, &opts)) != PAM_SUCCESS) { + goto err; + } +diff --git a/modules/pam_filter/module.map b/modules/pam_filter/module.map +new file mode 120000 +index 00000000..894551fb +--- /dev/null ++++ b/modules/pam_filter/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account-password-session.map +\ No newline at end of file +diff --git a/modules/pam_ftp/module.map b/modules/pam_ftp/module.map +new file mode 120000 +index 00000000..190be9a2 +--- /dev/null ++++ b/modules/pam_ftp/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth.map +\ No newline at end of file +diff --git a/modules/pam_group/module.map b/modules/pam_group/module.map +new file mode 120000 +index 00000000..190be9a2 +--- /dev/null ++++ b/modules/pam_group/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth.map +\ No newline at end of file +diff --git a/modules/pam_issue/module.map b/modules/pam_issue/module.map +new file mode 120000 +index 00000000..190be9a2 +--- /dev/null ++++ b/modules/pam_issue/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth.map +\ No newline at end of file +diff --git a/modules/pam_keyinit/module.map b/modules/pam_keyinit/module.map +new file mode 120000 +index 00000000..96519a4f +--- /dev/null ++++ b/modules/pam_keyinit/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-session.map +\ No newline at end of file +diff --git a/modules/pam_lastlog/module.map b/modules/pam_lastlog/module.map +new file mode 120000 +index 00000000..fa8c55e7 +--- /dev/null ++++ b/modules/pam_lastlog/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account-session.map +\ No newline at end of file +diff --git a/modules/pam_lastlog/pam_lastlog.c b/modules/pam_lastlog/pam_lastlog.c +index 01545a69..c68b5fb0 100644 +--- a/modules/pam_lastlog/pam_lastlog.c ++++ b/modules/pam_lastlog/pam_lastlog.c +@@ -569,7 +569,8 @@ last_login_failed(pam_handle_t *pamh, int announce, const char *user, time_t llt + + while ((retval=pam_modutil_read(fd, (void *)&ut, + sizeof(ut))) == sizeof(ut)) { +- if (ut.ut_tv.tv_sec >= lltime && strncmp(ut.ut_user, user, UT_NAMESIZE) == 0) { ++ if (zero_extend_signed_to_ull(ut.ut_tv.tv_sec) >= zero_extend_signed_to_ull(lltime) ++ && strncmp(ut.ut_user, user, UT_NAMESIZE) == 0) { + memcpy(&utuser, &ut, sizeof(utuser)); + failed++; + } +diff --git a/modules/pam_limits/module.map b/modules/pam_limits/module.map +new file mode 120000 +index 00000000..b90af7a8 +--- /dev/null ++++ b/modules/pam_limits/module.map +@@ -0,0 +1 @@ ++../maps/modules-session.map +\ No newline at end of file +diff --git a/modules/pam_listfile/module.map b/modules/pam_listfile/module.map +new file mode 120000 +index 00000000..894551fb +--- /dev/null ++++ b/modules/pam_listfile/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account-password-session.map +\ No newline at end of file +diff --git a/modules/pam_localuser/module.map b/modules/pam_localuser/module.map +new file mode 120000 +index 00000000..894551fb +--- /dev/null ++++ b/modules/pam_localuser/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account-password-session.map +\ No newline at end of file +diff --git a/modules/pam_loginuid/module.map b/modules/pam_loginuid/module.map +new file mode 120000 +index 00000000..d4486c2b +--- /dev/null ++++ b/modules/pam_loginuid/module.map +@@ -0,0 +1 @@ ++../maps/modules-account-session.map +\ No newline at end of file +diff --git a/modules/pam_mail/module.map b/modules/pam_mail/module.map +new file mode 120000 +index 00000000..96519a4f +--- /dev/null ++++ b/modules/pam_mail/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-session.map +\ No newline at end of file +diff --git a/modules/pam_mkhomedir/module.map b/modules/pam_mkhomedir/module.map +new file mode 120000 +index 00000000..b90af7a8 +--- /dev/null ++++ b/modules/pam_mkhomedir/module.map +@@ -0,0 +1 @@ ++../maps/modules-session.map +\ No newline at end of file +diff --git a/modules/pam_motd/module.map b/modules/pam_motd/module.map +new file mode 120000 +index 00000000..b90af7a8 +--- /dev/null ++++ b/modules/pam_motd/module.map +@@ -0,0 +1 @@ ++../maps/modules-session.map +\ No newline at end of file +diff --git a/modules/pam_namespace/module.map b/modules/pam_namespace/module.map +new file mode 120000 +index 00000000..b90af7a8 +--- /dev/null ++++ b/modules/pam_namespace/module.map +@@ -0,0 +1 @@ ++../maps/modules-session.map +\ No newline at end of file +diff --git a/modules/pam_nologin/module.map b/modules/pam_nologin/module.map +new file mode 120000 +index 00000000..6cc612ef +--- /dev/null ++++ b/modules/pam_nologin/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account.map +\ No newline at end of file +diff --git a/modules/pam_permit/module.map b/modules/pam_permit/module.map +new file mode 120000 +index 00000000..894551fb +--- /dev/null ++++ b/modules/pam_permit/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account-password-session.map +\ No newline at end of file +diff --git a/modules/pam_pwhistory/module.map b/modules/pam_pwhistory/module.map +new file mode 120000 +index 00000000..7df2e1b6 +--- /dev/null ++++ b/modules/pam_pwhistory/module.map +@@ -0,0 +1 @@ ++../maps/modules-password.map +\ No newline at end of file +diff --git a/modules/pam_rhosts/module.map b/modules/pam_rhosts/module.map +new file mode 120000 +index 00000000..190be9a2 +--- /dev/null ++++ b/modules/pam_rhosts/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth.map +\ No newline at end of file +diff --git a/modules/pam_rootok/module.map b/modules/pam_rootok/module.map +new file mode 120000 +index 00000000..175e8b86 +--- /dev/null ++++ b/modules/pam_rootok/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account-password.map +\ No newline at end of file +diff --git a/modules/pam_securetty/module.map b/modules/pam_securetty/module.map +new file mode 120000 +index 00000000..6cc612ef +--- /dev/null ++++ b/modules/pam_securetty/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account.map +\ No newline at end of file +diff --git a/modules/pam_selinux/module.map b/modules/pam_selinux/module.map +new file mode 120000 +index 00000000..96519a4f +--- /dev/null ++++ b/modules/pam_selinux/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-session.map +\ No newline at end of file +diff --git a/modules/pam_sepermit/module.map b/modules/pam_sepermit/module.map +new file mode 120000 +index 00000000..6cc612ef +--- /dev/null ++++ b/modules/pam_sepermit/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account.map +\ No newline at end of file +diff --git a/modules/pam_setquota/module.map b/modules/pam_setquota/module.map +new file mode 120000 +index 00000000..b90af7a8 +--- /dev/null ++++ b/modules/pam_setquota/module.map +@@ -0,0 +1 @@ ++../maps/modules-session.map +\ No newline at end of file +diff --git a/modules/pam_shells/module.map b/modules/pam_shells/module.map +new file mode 120000 +index 00000000..6cc612ef +--- /dev/null ++++ b/modules/pam_shells/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account.map +\ No newline at end of file +diff --git a/modules/pam_stress/module.map b/modules/pam_stress/module.map +new file mode 120000 +index 00000000..894551fb +--- /dev/null ++++ b/modules/pam_stress/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account-password-session.map +\ No newline at end of file +diff --git a/modules/pam_succeed_if/module.map b/modules/pam_succeed_if/module.map +new file mode 120000 +index 00000000..894551fb +--- /dev/null ++++ b/modules/pam_succeed_if/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account-password-session.map +\ No newline at end of file +diff --git a/modules/pam_time/module.map b/modules/pam_time/module.map +new file mode 120000 +index 00000000..1057580e +--- /dev/null ++++ b/modules/pam_time/module.map +@@ -0,0 +1 @@ ++../maps/modules-account.map +\ No newline at end of file +diff --git a/modules/pam_timestamp/module.map b/modules/pam_timestamp/module.map +new file mode 120000 +index 00000000..96519a4f +--- /dev/null ++++ b/modules/pam_timestamp/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-session.map +\ No newline at end of file +diff --git a/modules/pam_timestamp/pam_timestamp.c b/modules/pam_timestamp/pam_timestamp.c +index 0172d1ef..030fa2b8 100644 +--- a/modules/pam_timestamp/pam_timestamp.c ++++ b/modules/pam_timestamp/pam_timestamp.c +@@ -244,7 +244,9 @@ check_login_time( + if (strncmp(ruser, ut->ut_user, sizeof(ut->ut_user)) != 0) { + continue; + } +- if (oldest_login == 0 || oldest_login > ut->ut_tv.tv_sec) { ++ if (oldest_login == 0 || ++ zero_extend_signed_to_ull(oldest_login) ++ > zero_extend_signed_to_ull(ut->ut_tv.tv_sec)) { + oldest_login = ut->ut_tv.tv_sec; + } + } +diff --git a/modules/pam_tty_audit/module.map b/modules/pam_tty_audit/module.map +new file mode 120000 +index 00000000..b90af7a8 +--- /dev/null ++++ b/modules/pam_tty_audit/module.map +@@ -0,0 +1 @@ ++../maps/modules-session.map +\ No newline at end of file +diff --git a/modules/pam_umask/module.map b/modules/pam_umask/module.map +new file mode 120000 +index 00000000..b90af7a8 +--- /dev/null ++++ b/modules/pam_umask/module.map +@@ -0,0 +1 @@ ++../maps/modules-session.map +\ No newline at end of file +diff --git a/modules/pam_unix/module.map b/modules/pam_unix/module.map +new file mode 120000 +index 00000000..894551fb +--- /dev/null ++++ b/modules/pam_unix/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account-password-session.map +\ No newline at end of file +diff --git a/modules/pam_unix/pam_unix_acct.c b/modules/pam_unix/pam_unix_acct.c +index 961b7667..5afcbba9 100644 +--- a/modules/pam_unix/pam_unix_acct.c ++++ b/modules/pam_unix/pam_unix_acct.c +@@ -63,7 +63,7 @@ + #include "passverify.h" + + int _unix_run_verify_binary(pam_handle_t *pamh, unsigned long long ctrl, +- const char *user, int *daysleft) ++ const char *user, long *daysleft) + { + int retval=0, child, fds[2]; + struct sigaction newsa, oldsa; +@@ -156,7 +156,7 @@ int _unix_run_verify_binary(pam_handle_t *pamh, unsigned long long ctrl, + rc = pam_modutil_read(fds[0], buf, sizeof(buf) - 1); + if(rc > 0) { + buf[rc] = '\0'; +- if (sscanf(buf,"%d", daysleft) != 1 ) ++ if (sscanf(buf,"%ld", daysleft) != 1) + retval = PAM_AUTH_ERR; + } + else { +@@ -191,7 +191,8 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) + unsigned long long ctrl; + const void *void_uname; + const char *uname; +- int retval, daysleft = -1; ++ long daysleft = -1; ++ int retval; + char buf[256]; + + D(("called.")); +@@ -263,24 +264,24 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) + case PAM_SUCCESS: + if (daysleft >= 0) { + pam_syslog(pamh, LOG_DEBUG, +- "password for user %s will expire in %d days", ++ "password for user %s will expire in %ld days", + uname, daysleft); + #if defined HAVE_DNGETTEXT && defined ENABLE_NLS + pam_sprintf(buf, + dngettext(PACKAGE, +- "Warning: your password will expire in %d day.", +- "Warning: your password will expire in %d days.", ++ "Warning: your password will expire in %ld day.", ++ "Warning: your password will expire in %ld days.", + daysleft), + daysleft); + #else + if (daysleft == 1) + pam_sprintf(buf, +- _("Warning: your password will expire in %d day."), ++ _("Warning: your password will expire in %ld day."), + daysleft); + else + pam_sprintf(buf, + /* TRANSLATORS: only used if dngettext is not supported */ +- _("Warning: your password will expire in %d days."), ++ _("Warning: your password will expire in %ld days."), + daysleft); + #endif + _make_remark(pamh, ctrl, PAM_TEXT_INFO, buf); +diff --git a/modules/pam_unix/pam_unix_auth.c b/modules/pam_unix/pam_unix_auth.c +index ffb61547..e713afca 100644 +--- a/modules/pam_unix/pam_unix_auth.c ++++ b/modules/pam_unix/pam_unix_auth.c +@@ -2,7 +2,6 @@ + * pam_unix authentication management + * + * Copyright Alexander O. Yuriev, 1996. All rights reserved. +- * NIS+ support by Thorsten Kukuk + * Copyright Jan Rękorajski, 1999. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without +diff --git a/modules/pam_unix/pam_unix_passwd.c b/modules/pam_unix/pam_unix_passwd.c +index cdd253e3..dbc9fb76 100644 +--- a/modules/pam_unix/pam_unix_passwd.c ++++ b/modules/pam_unix/pam_unix_passwd.c +@@ -510,7 +510,7 @@ static int _unix_verify_shadow(pam_handle_t *pamh, const char *user, unsigned lo + { + struct passwd *pwent = NULL; /* Password and shadow password */ + struct spwd *spent = NULL; /* file entries for the user */ +- int daysleft; ++ long daysleft; + int retval; + + retval = get_account_info(pamh, user, &pwent, &spent); +diff --git a/modules/pam_unix/passverify.c b/modules/pam_unix/passverify.c +index 85e7841e..7f758bb4 100644 +--- a/modules/pam_unix/passverify.c ++++ b/modules/pam_unix/passverify.c +@@ -23,6 +23,9 @@ + #ifdef HAVE_CRYPT_H + #include + #endif ++#ifdef USE_PWACCESS ++#include ++#endif + + #include "pam_cc_compat.h" + #include "pam_inline.h" +@@ -200,65 +203,51 @@ PAMH_ARG_DECL(int get_account_info, + *pwd = pam_modutil_getpwnam(pamh, name); /* Get password file entry... */ + *spwdent = NULL; + +- if (*pwd != NULL) { +- if (strcmp((*pwd)->pw_passwd, "*NP*") == 0) +- { /* NIS+ */ +-#ifdef HELPER_COMPILE +- uid_t save_euid, save_uid; +- +- save_euid = geteuid(); +- save_uid = getuid(); +- if (save_uid == (*pwd)->pw_uid) { +- if (setreuid(save_euid, save_uid)) +- return PAM_CRED_INSUFFICIENT; +- } else { +- if (setreuid(0, -1)) +- return PAM_CRED_INSUFFICIENT; +- if (setreuid(-1, (*pwd)->pw_uid)) { +- if (setreuid(-1, 0) +- || setreuid(0, -1) +- || setreuid(-1, (*pwd)->pw_uid)) { +- return PAM_CRED_INSUFFICIENT; +- } +- } +- } +- +- *spwdent = pam_modutil_getspnam(pamh, name); +- if (save_uid == (*pwd)->pw_uid) { +- if (setreuid(save_uid, save_euid)) +- return PAM_CRED_INSUFFICIENT; +- } else { +- if (setreuid(-1, 0) +- || setreuid(save_uid, -1) +- || setreuid(-1, save_euid)) +- return PAM_CRED_INSUFFICIENT; +- } ++ if (*pwd == NULL) { ++ return PAM_USER_UNKNOWN; ++ } + +- if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL) +- return PAM_AUTHINFO_UNAVAIL; +-#else +- /* we must run helper for NIS+ passwords */ +- return PAM_UNIX_RUN_HELPER; ++ if (is_pwd_shadowed(*pwd)) { ++#if defined(HELPER_COMPILE) || defined(PAM_UNIX_TRY_GETSPNAM) ++ /* ++ * shadow password file entry for this user, ++ * if shadowing is enabled ++ */ ++#ifdef USE_PWACCESS ++ int r; ++ bool complete = false; ++ char *error = NULL; ++ ++ r = pwaccess_get_user_record(-1, name, NULL, spwdent, &complete, &error); ++ if (r < 0) { ++ if (!PWACCESS_IS_NOT_RUNNING(r)) ++ pam_syslog(pamh, LOG_ERR, "%s", ++ error ? error : strerror(-r)); ++ free(error); ++ } ++ if (complete) ++ return PAM_SUCCESS; ++ struct_shadow_freep(spwdent); + #endif +- } else if (is_pwd_shadowed(*pwd)) { +-#ifdef HELPER_COMPILE +- /* +- * shadow password file entry for this user, +- * if shadowing is enabled +- */ +- *spwdent = getspnam(name); +- if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL) +- return PAM_AUTHINFO_UNAVAIL; +-#else +- /* +- * The helper has to be invoked to deal with +- * the shadow password file entry. +- */ +- return PAM_UNIX_RUN_HELPER; ++ *spwdent = pam_modutil_getspnam(pamh, name); ++ if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL ++# ifndef HELPER_COMPILE ++ /* synthesized entry from libnss-systemd */ ++ || (strcmp(name, "root") == 0 && ++ strcmp((*spwdent)->sp_pwdp, "!*") == 0) ++# endif ++ ) ++# ifdef HELPER_COMPILE ++ return PAM_AUTHINFO_UNAVAIL; ++# endif ++#endif /* HELPER_COMPILE || PAM_UNIX_TRY_GETSPNAM */ ++#ifndef HELPER_COMPILE ++ /* ++ * The helper has to be invoked to deal with ++ * the shadow password file entry. ++ */ ++ return PAM_UNIX_RUN_HELPER; + #endif +- } +- } else { +- return PAM_USER_UNKNOWN; + } + return PAM_SUCCESS; + } +@@ -284,23 +273,8 @@ PAMH_ARG_DECL(int get_pwd_hash, + return PAM_SUCCESS; + } + +-/* +- * invariant: 0 <= num1 +- * invariant: 0 <= num2 +- */ +-static int +-subtract(long num1, long num2) +-{ +- long value = num1 - num2; +- if (value < INT_MIN) +- return INT_MIN; +- if (value > INT_MAX) +- return INT_MAX; +- return (int)value; +-} +- + PAMH_ARG_DECL(int check_shadow_expiry, +- struct spwd *spent, int *daysleft) ++ struct spwd *spent, long *daysleft) + { + long int curdays, passed; + *daysleft = -1; +@@ -331,7 +305,7 @@ PAMH_ARG_DECL(int check_shadow_expiry, + long inact = spent->sp_max < LONG_MAX - spent->sp_inact ? + spent->sp_max + spent->sp_inact : LONG_MAX; + if (passed >= inact) { +- *daysleft = subtract(inact, passed); ++ *daysleft = inact - passed; + D(("authtok expired")); + return PAM_AUTHTOK_EXPIRED; + } +@@ -344,7 +318,7 @@ PAMH_ARG_DECL(int check_shadow_expiry, + long warn = spent->sp_warn > spent->sp_max ? -1 : + spent->sp_max - spent->sp_warn; + if (passed >= warn) { +- *daysleft = subtract(spent->sp_max, passed); ++ *daysleft = spent->sp_max - passed; + D(("warn before expiry")); + } + } +diff --git a/modules/pam_unix/passverify.h b/modules/pam_unix/passverify.h +index 1636791c..234eb8ca 100644 +--- a/modules/pam_unix/passverify.h ++++ b/modules/pam_unix/passverify.h +@@ -73,7 +73,7 @@ PAMH_ARG_DECL(int get_pwd_hash, + const char *name, struct passwd **pwd, char **hash); + + PAMH_ARG_DECL(int check_shadow_expiry, +- struct spwd *spent, int *daysleft); ++ struct spwd *spent, long *daysleft); + + PAMH_ARG_DECL(int unix_update_passwd, + const char *forwho, const char *towhat); +diff --git a/modules/pam_unix/support.c b/modules/pam_unix/support.c +index b95f95e6..652e35b2 100644 +--- a/modules/pam_unix/support.c ++++ b/modules/pam_unix/support.c +@@ -648,7 +648,7 @@ _unix_blankpasswd (pam_handle_t *pamh, unsigned long long ctrl, const char *name + { + struct passwd *pwd = NULL; + char *salt = NULL; +- int daysleft; ++ long daysleft; + int retval; + int blank = 0; + int execloop; +@@ -863,7 +863,7 @@ int + _unix_verify_user(pam_handle_t *pamh, + unsigned long long ctrl, + const char *name, +- int *daysleft) ++ long *daysleft) + { + int retval; + struct spwd *spent; +diff --git a/modules/pam_unix/support.h b/modules/pam_unix/support.h +index e8f629d7..759b7ba0 100644 +--- a/modules/pam_unix/support.h ++++ b/modules/pam_unix/support.h +@@ -174,9 +174,9 @@ extern int _unix_verify_password(pam_handle_t * pamh, const char *name, + const char *p, unsigned long long ctrl); + + extern int _unix_verify_user(pam_handle_t *pamh, unsigned long long ctrl, +- const char *name, int *daysleft); ++ const char *name, long *daysleft); + + extern int _unix_run_verify_binary(pam_handle_t *pamh, + unsigned long long ctrl, +- const char *user, int *daysleft); ++ const char *user, long *daysleft); + #endif /* _PAM_UNIX_SUPPORT_H */ +diff --git a/modules/pam_unix/unix_chkpwd.c b/modules/pam_unix/unix_chkpwd.c +index 820136d5..dde41a3a 100644 +--- a/modules/pam_unix/unix_chkpwd.c ++++ b/modules/pam_unix/unix_chkpwd.c +@@ -41,7 +41,7 @@ static int _check_expiry(const char *uname) + struct spwd *spent; + struct passwd *pwent; + int retval; +- int daysleft; ++ long daysleft; + + retval = get_account_info(uname, &pwent, &spent); + if (retval != PAM_SUCCESS) { +@@ -56,7 +56,7 @@ static int _check_expiry(const char *uname) + } + + retval = check_shadow_expiry(spent, &daysleft); +- printf("%d\n", daysleft); ++ printf("%ld\n", daysleft); + return retval; + } + +diff --git a/modules/pam_userdb/module.map b/modules/pam_userdb/module.map +new file mode 120000 +index 00000000..6cc612ef +--- /dev/null ++++ b/modules/pam_userdb/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account.map +\ No newline at end of file +diff --git a/modules/pam_usertype/module.map b/modules/pam_usertype/module.map +new file mode 120000 +index 00000000..894551fb +--- /dev/null ++++ b/modules/pam_usertype/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account-password-session.map +\ No newline at end of file +diff --git a/modules/pam_warn/module.map b/modules/pam_warn/module.map +new file mode 120000 +index 00000000..894551fb +--- /dev/null ++++ b/modules/pam_warn/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account-password-session.map +\ No newline at end of file +diff --git a/modules/pam_wheel/module.map b/modules/pam_wheel/module.map +new file mode 120000 +index 00000000..6cc612ef +--- /dev/null ++++ b/modules/pam_wheel/module.map +@@ -0,0 +1 @@ ++../maps/modules-auth-account.map +\ No newline at end of file +diff --git a/modules/pam_xauth/module.map b/modules/pam_xauth/module.map +new file mode 120000 +index 00000000..b90af7a8 +--- /dev/null ++++ b/modules/pam_xauth/module.map +@@ -0,0 +1 @@ ++../maps/modules-session.map +\ No newline at end of file diff --git a/unix2_chkpwd.8 b/unix2_chkpwd.8 deleted file mode 100644 index 5f41cf4..0000000 --- a/unix2_chkpwd.8 +++ /dev/null @@ -1,79 +0,0 @@ -.\" Copyright (C) 2003 International Business Machines Corporation -.\" This file is distributed according to the GNU General Public License. -.\" See the file COPYING in the top level source directory for details. -.\" -.de Sh \" Subsection -.br -.if t .Sp -.ne 5 -.PP -\fB\\$1\fR -.PP -.. -.de Sp \" Vertical space (when we can't use .PP) -.if t .sp .5v -.if n .sp -.. -.de Ip \" List item -.br -.ie \\n(.$>=3 .ne \\$3 -.el .ne 3 -.IP "\\$1" \\$2 -.. -.TH "UNIX2_CHKPWD" 8 "2003-03-21" "Linux-PAM 0.76" "Linux-PAM Manual" -.SH NAME -unix2_chkpwd \- helper binary that verifies the password of the current user -.SH "SYNOPSIS" -.ad l -.hy 0 - -/sbin/unix2_chkpwd \fIservicename\fR \fIusername\fR -.sp -.ad -.hy -.SH "DESCRIPTION" -.PP -\fBunix2_chkpwd\fR is a helper program for applications that verifies -the password of the current user. It is not intended to be run directly from -the command line and logs a security violation if done so. - -It is typically installed setuid root or setgid shadow and called by -applications, which only wishes to do an user authentification and -nothing more. - -.SH "OPTIONS" -.PP -unix2_chkpwd requires the following arguments: -.TP -\fIpam_service\fR -The name of the service using unix2_chkpwd. This is required to be one of -the services in /etc/pam.d -.TP -\fIusername\fR -The name of the user whose password you want to verify. - -.SH "INPUTS" -.PP -unix2_chkpwd expects the password via stdin. - -.SH "RETURN CODES" -.PP -\fBunix2_chkpwd\fR has the following return codes: -.TP -1 -unix2_chkpwd was inappropriately called from the command line or the password is incorrect. - -.TP -0 -The password is correct. - -.SH "HISTORY" -Written by Olaf Kirch loosely based on unix_chkpwd by Andrew Morgan - -.SH "SEE ALSO" - -.PP -\fBpam\fR(8) - -.SH AUTHOR -Emily Ratliff. diff --git a/unix2_chkpwd.c b/unix2_chkpwd.c deleted file mode 100644 index 082fd3d..0000000 --- a/unix2_chkpwd.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Set*id helper program for PAM authentication. - * - * It is supposed to be called from pam_unix2's - * pam_sm_authenticate function if the function notices - * that it's unable to get the password from the shadow file - * because it doesn't have sufficient permissions. - * - * Copyright (C) 2002 SuSE Linux AG - * - * Written by okir@suse.de, loosely based on unix_chkpwd - * by Andrew Morgan. - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BUFLEN 1024 -#ifndef LOGINDEFS -#define LOGINDEFS "/etc/login.defs" -#endif -#define LOGINDEFS_FAIL_DELAY_KEY "FAIL_DELAY" -#define DEFAULT_FAIL_DELAY_S 10 - -#define PASSWD_CRACKER_DELAY_MS 100 - -enum { - UNIX_PASSED = 0, - UNIX_FAILED = 1 -}; - -static char * program_name; -static char pass[64]; -static int npass = -1; - -/* - * Log error messages - */ -static void -_log_err(int err, const char *format,...) -{ - va_list args; - - va_start(args, format); - openlog(program_name, LOG_CONS | LOG_PID, LOG_AUTH); - vsyslog(err, format, args); - va_end(args); - closelog(); -} - -static void -su_sighandler(int sig) -{ - if (sig > 0) { - _log_err(LOG_NOTICE, "caught signal %d.", sig); - exit(sig); - } -} - -/* - * Setup signal handlers - */ -static void -setup_signals(void) -{ - struct sigaction action; - - memset((void *) &action, 0, sizeof(action)); - action.sa_handler = su_sighandler; - action.sa_flags = SA_RESETHAND; - sigaction(SIGILL, &action, NULL); - sigaction(SIGTRAP, &action, NULL); - sigaction(SIGBUS, &action, NULL); - sigaction(SIGSEGV, &action, NULL); - action.sa_handler = SIG_IGN; - action.sa_flags = 0; - sigaction(SIGTERM, &action, NULL); - sigaction(SIGHUP, &action, NULL); - sigaction(SIGINT, &action, NULL); - sigaction(SIGQUIT, &action, NULL); - sigaction(SIGALRM, &action, NULL); -} - -static int -_converse(int num_msg, const struct pam_message **msg, - struct pam_response **resp, void *appdata_ptr) -{ - struct pam_response *reply; - int num; - - if (!(reply = malloc(sizeof(*reply) * num_msg))) - return PAM_CONV_ERR; - - for (num = 0; num < num_msg; num++) { - reply[num].resp_retcode = PAM_SUCCESS; - reply[num].resp = NULL; - switch (msg[num]->msg_style) { - case PAM_PROMPT_ECHO_ON: - return PAM_CONV_ERR; - case PAM_PROMPT_ECHO_OFF: - /* read the password from stdin */ - if (npass < 0) { - npass = read(STDIN_FILENO, pass, sizeof(pass)-1); - if (npass < 0) { - _log_err(LOG_DEBUG, "error reading password"); - return UNIX_FAILED; - } - pass[npass] = '\0'; - } - reply[num].resp = strdup(pass); - break; - case PAM_TEXT_INFO: - case PAM_ERROR_MSG: - /* ignored */ - break; - default: - /* Must be an error of some sort... */ - return PAM_CONV_ERR; - } - } - - *resp = reply; - return PAM_SUCCESS; -} - -static int -_authenticate(const char *service, const char *user) -{ - struct pam_conv conv = { _converse, NULL }; - pam_handle_t *pamh; - int err; - - err = pam_start(service, user, &conv, &pamh); - if (err != PAM_SUCCESS) { - _log_err(LOG_ERR, "pam_start(%s, %s) failed (errno %d)", - service, user, err); - return UNIX_FAILED; - } - - err = pam_authenticate(pamh, 0); - if (err != PAM_SUCCESS) - _log_err(LOG_ERR, "pam_authenticate(%s, %s): %s", - service, user, - pam_strerror(pamh, err)); - - if (err == PAM_SUCCESS) - { - err = pam_acct_mgmt(pamh, 0); - if (err == PAM_SUCCESS) - { - int err2 = pam_setcred(pamh, PAM_REFRESH_CRED); - if (err2 != PAM_SUCCESS) - _log_err(LOG_ERR, "pam_setcred(%s, %s): %s", - service, user, - pam_strerror(pamh, err2)); - /* - * ignore errors on refresh credentials. - * If this did not work we use the old once. - */ - } else { - _log_err(LOG_ERR, "pam_acct_mgmt(%s, %s): %s", - service, user, - pam_strerror(pamh, err)); - } - } - - pam_end(pamh, err); - - if (err != PAM_SUCCESS) - return UNIX_FAILED; - return UNIX_PASSED; -} - -static char * -getuidname(uid_t uid) -{ - struct passwd *pw; - static char username[32]; - - pw = getpwuid(uid); - if (pw == NULL) - return NULL; - - strncpy(username, pw->pw_name, sizeof(username)); - username[sizeof(username) - 1] = '\0'; - - endpwent(); - return username; -} - -static int -sane_pam_service(const char *name) -{ - const char *sp; - char path[128]; - - if (strlen(name) > 32) - return 0; - for (sp = name; *sp; sp++) { - if (!isalnum(*sp) && *sp != '_' && *sp != '-') - return 0; - } - - snprintf(path, sizeof(path), "/etc/pam.d/%s", name); - return access(path, R_OK) == 0; -} - -static int -get_system_fail_delay (void) -{ - FILE *fs; - char buf[BUFLEN]; - long int delay = -1; - char *s; - int l; - - fs = fopen(LOGINDEFS, "r"); - if (NULL == fs) { - goto bail_out; - } - - while ((NULL != fgets(buf, BUFLEN, fs)) && (-1 == delay)) { - if (!strstr(buf, LOGINDEFS_FAIL_DELAY_KEY)) { - continue; - } - s = buf + strspn(buf, " \t"); - l = strcspn(s, " \t"); - if (strncmp(LOGINDEFS_FAIL_DELAY_KEY, s, l)) { - continue; - } - s += l; - s += strspn(s, " \t"); - errno = 0; - delay = strtol(s, NULL, 10); - if (errno) { - delay = -1; - } - break; - } - fclose (fs); -bail_out: - delay = (delay < 0) ? DEFAULT_FAIL_DELAY_S : delay; - return (int)delay; -} - -int -main(int argc, char *argv[]) -{ - const char *program_name; - char *service, *user; - int fd; - int result = UNIX_FAILED; - uid_t uid; - - uid = getuid(); - - /* - * Make sure standard file descriptors are connected. - */ - while ((fd = open("/dev/null", O_RDWR)) <= 2) - ; - close(fd); - - /* - * Get the program name - */ - if (argc == 0) - program_name = "unix2_chkpwd"; - else if ((program_name = strrchr(argv[0], '/')) != NULL) - program_name++; - else - program_name = argv[0]; - - /* - * Catch or ignore as many signal as possible. - */ - setup_signals(); - - /* - * Check argument list - */ - if (argc < 2 || argc > 3) { - _log_err(LOG_NOTICE, "Bad number of arguments (%d)", argc); - return UNIX_FAILED; - } - - /* - * Get the service name and do some sanity checks on it - */ - service = argv[1]; - if (!sane_pam_service(service)) { - _log_err(LOG_ERR, "Illegal service name '%s'", service); - return UNIX_FAILED; - } - - /* - * Discourage users messing around (fat chance) - */ - if (isatty(STDIN_FILENO) && uid != 0) { - _log_err(LOG_NOTICE, - "Inappropriate use of Unix helper binary [UID=%d]", - uid); - fprintf(stderr, - "This binary is not designed for running in this way\n" - "-- the system administrator has been informed\n"); - sleep(10); /* this should discourage/annoy the user */ - return UNIX_FAILED; - } - - /* - * determine the caller's user name - */ - user = getuidname(uid); - if (argc == 3 && strcmp(user, argv[2])) { - user = argv[2]; - } - result = _authenticate(service, user); - /* Discourage use of this program as a - * password cracker */ - usleep(PASSWD_CRACKER_DELAY_MS * 1000); - if (result != UNIX_PASSED && uid != 0) - sleep(get_system_fail_delay()); - return result; -}