Skip to content

fix(pam): pass profile names to pam-auth-update --remove#376

Open
maybebyte wants to merge 1 commit into
Kicksecure:masterfrom
maybebyte:fix/pam-auth-update-remove-profiles
Open

fix(pam): pass profile names to pam-auth-update --remove#376
maybebyte wants to merge 1 commit into
Kicksecure:masterfrom
maybebyte:fix/pam-auth-update-remove-profiles

Conversation

@maybebyte

Copy link
Copy Markdown
Contributor

Summary

The prerm/postrm scripts try to disable this package's PAM profiles when the
package is removed, but they pass the package name to pam-auth-update --remove,
which matches profile filenames — so the removal was silently a no-op. This
restores the intended early-disable and hardens the removal path across dpkg states.

Changes

  • Root cause: pam-auth-update --remove matches the config filenames in
    /usr/share/pam-configs/ (readdir basenames), never the package name. Both
    scripts passed $DPKG_MAINTSCRIPT_PACKAGE (security-misc-shared), matching no
    profile, so nothing was removed. An unknown target is not validated and never
    errors, so it failed silently. The prerm early-disable was therefore dead:
    on removal, /etc/pam.d/common-* kept referencing helper scripts under
    /usr/libexec/security-misc/ that dpkg then deleted — and block-unsafe-logins
    is a Priority 1100 requisite, so a dangling reference fails all logins.
  • prerm: pass the 8 actual profile filenames to --remove, guarded by
    command -v pam-auth-update. This is the man-page-documented purpose of
    --remove: strip profiles before the modules they reference leave disk.
  • postrm: replace the inert --remove <pkg> with a plain
    pam-auth-update --package reconciliation scoped to remove|purge|disappear,
    so upgrade/abort-* never strip live hardening mid-transaction.

Testing

  • Built a real security-misc-shared .deb (the actual profiles, the referenced
    helper scripts, and these maintainer scripts) and ran install → dpkg -r
    dpkg -P in debian:stable-slim11/11 checks passed. Crux: same tree,
    only the --remove argument differs — --remove security-misc-shared left the
    profile in common-auth (old no-op reproduced), while --remove <8 filenames>
    removed it with the files still on disk; dpkg -r/-P exited 0 with
    common-* cleaned before the helper was deleted.
  • Guard behavior confirmed: postrm invoked pam-auth-update only for
    remove/purge/disappear (not upgrade/abort-upgrade/abort-install/
    failed-upgrade/deconfigure), and postrm purge exited 0 with
    pam-auth-update absent from PATH.
  • bash -n passes on both scripts; shellcheck is clean on the changed blocks.

Notes for reviewers

  • On a normal remove/purge the observable end state is unchanged today —
    reconciliation already drops the profiles once their files are gone. This fix
    restores the prerm early-disable that closes the window where common-auth
    references a just-deleted module; it is a correctness/robustness fix, not a
    fix for a failure users currently hit.
  • Decision: postrm keeps a plain --package (rather than dropping the call)
    as a safety net for profile-list drift and the disappear state, where prerm
    never runs. A real --remove <profiles> there was deliberately avoided — it
    would strip live hardening during upgrade/abort-*.
  • Caveat: the 8-name list in prerm must stay in sync with
    debian/security-misc-shared.install and usr/share/pam-configs/; a forgotten
    profile is still reconciled away by the postrm net within the same dpkg run.

pam-auth-update --remove matches pam-config filenames under
/usr/share/pam-configs/, but prerm and postrm passed
$DPKG_MAINTSCRIPT_PACKAGE -- the package name security-misc-shared --
which matches no profile and is silently ignored. The prerm
early-disable was therefore dead: on removal, /etc/pam.d/common-* kept
referencing helper scripts under /usr/libexec/security-misc/ that dpkg
then deleted, risking failed authentication (block-unsafe-logins is a
Priority 1100 requisite entry).

prerm now passes the eight profile filenames explicitly, guarded by
command -v so a bulk purge cannot abort under set -e. postrm drops the
ineffective --remove for a plain reconciliation scoped to
remove|purge|disappear, so it never strips live hardening during an
upgrade or abort.
@adrelanos

Copy link
Copy Markdown
Contributor

Have you been able to reproduce this bug - what bug exactly? And was this PR tested and known to fix the issue?

@ArrayBolt3

ArrayBolt3 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Looking at the pam-update-auth documentation, I think this change is probably good, though I need to make sure that the arguments being checked for in prerm make sense.

@adrelanos The bug is:

  • If security-misc-shared's prerm succeeds,
  • but then the postrm fails before pam-auth-update --package is called,
  • the configuration files under /etc/pam.d will still contain PAM configuration from security-misc, but the scripts that configuration depends on will have been removed from disk. PAM will therefore break entirely.
  • There may be other edge cases where a PAM module is left around when the files it depends on are removed by an upgrade, which would come with similar consequences.

The solution is to remove PAM configuration from /etc/pam.d before security-misc's files are removed from disk. This is documented in pam-auth-update's manual page:

pam-auth-update --remove should be used to remove profiles from the configuration before the modules they reference are removed from disk, to ensure that PAM is in a consistent and usable state at all times during package upgrades or removals.

An easy way to reproduce the issue is to make /usr/libexec/helper-scripts/pre.bsh improperly terminate the postrm script, and only the postrm script, by adding the following code to the file immediately after the shebang:

if [ "${DPKG_MAINTSCRIPT_PACKAGE}" = 'security-misc-shared' ] \
   && [ "${DPKG_MAINTSCRIPT_NAME}" = 'postrm' ] \
   && [[ "${1}" =~ ^(remove|purge) ]]; then
   exit 1
fi

Then run sudo apt remove security-misc-shared. The removal will fail (installed security-misc-shared package post-removal script subprocess returned error exit status 1), and if you attempt to run sudo -i thereafter, it will break:

[sysmaint ~]% sudo -i
/usr/libexec/security-misc/block-unsafe-logins failed: exit code 2
sudo: PAM authentication error: Unknown error -1
sudo: a password is required
zsh: exit 1     sudo -i

So if this PR doesn't break anything and does fix the above, it's good.

## config files are still present and '--remove' would strip live hardening
## mid-transaction, so those states are deliberately not matched here.
case "$1" in
remove|purge|disappear)

@ArrayBolt3 ArrayBolt3 Jul 20, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we match failed-upgrade, abort-install, and abort-upgrade too? (See https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html#summary-of-ways-maintainer-scripts-are-called, specifically the subsection about how the postrm script may be called.) I don't think this will be problematic for any of these (or for upgrade, for that matter), and it might not be redundant in any of those situations. (It is redundant for upgrade though, since the postinst will have already called pam-auth-update.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants