Skip to content

fix: preserve special file ownership from packages when cutting - #338

Open
chris-bainbridge wants to merge 1 commit into
canonical:mainfrom
chris-bainbridge:preserve-file-ownership
Open

chris-bainbridge wants to merge 1 commit into
canonical:mainfrom
chris-bainbridge:preserve-file-ownership

Conversation

@chris-bainbridge

Copy link
Copy Markdown
  • Have you signed the CLA?

Problem

Files extracted by chisel cut are always created with the ownership of the running process (root:root in build environments), discarding the uid/gid recorded in the package tarball. This silently breaks any file that relies on non-root ownership for its function.

Most notable victim: the PAM chkpwd helpers /usr/sbin/unix_chkpwd and /usr/sbin/pam_extrausers_chkpwd. These ship setgid shadow (mode 2755) so they can read the root:shadow, mode 0640 shadow files on behalf of an unprivileged PAM caller (the standard Debian/Ubuntu privilege-separation design). Cut with chisel they end up setgid root, which grants no such access, so every password check performed by a non-root caller fails — e.g. screen lock/unlock in a confined desktop session on Ubuntu Core, where the session snap's PAM stack execs the helpers from the base snap. The same flattening affects chage and expiry (shadow), crontab (crontab), plocate (plocate) and ssh-agent (_ssh).

The mode (including the setgid bit) is already preserved; only the uid/gid are dropped, in internal/tarball/extract.go, which builds fsutil.CreateOptions from the tar header without carrying the header's Uid/Gid over — internal/fsutil has no way to express them at all.

Fix

  • internal/fsutil: CreateOptions gains optional Uid/Gid, applied with os.Chown to newly created entries, and to pre-existing ones when OverrideMode is set (mirroring the existing mode semantics). Ownership is never applied to symlinks; hard links inherit it from their target's inode. Unprivileged runs that cannot chown log a single warning and keep the previous behavior.
  • internal/tarball: the tar header's uid/gid is carried through for entries and their parent directories — but only requested when it differs from root:root. Root-owned entries already end up root-owned when cutting as root, and cannot be chowned when cutting as a regular user, so the overwhelmingly common case keeps its previous behavior exactly (no extra syscalls, no behavior change for chiseled container images).

Slice definitions are unchanged; the ownership comes from the packages themselves, so cuts stay deterministic.

Verification

  • Unit tests: new cases in internal/fsutil (ownership applied/withheld per the mode semantics, symlink and hard link handling, CreateWriter, unprivileged EPERM warning) and internal/tarball (a setgid, gid-42 entry modeling unix_chkpwd; asserting preservation as root and the warning fallback as a regular user). Full suite passes both as root and as a regular user (17/17 packages).
  • End-to-end: cutting the real libpam-modules-bin_auth slice from chisel-ubuntu-core (26.04) as root now produces
    -rwxr-sr-x 0:42 usr/sbin/unix_chkpwd
    -rwxr-sr-x 0:42 usr/sbin/pam_extrausers_chkpwd
    
    matching a plain dpkg -x of the same package (previously root:root).

Files extracted from packages were always created with the ownership of
the running process (root:root in build environments), discarding the
uid/gid recorded in the package tarball. This breaks any file relying
on non-root ownership for its function: most notably the PAM
chkpwd helpers (/usr/sbin/unix_chkpwd and
/usr/sbin/pam_extrausers_chkpwd) ship setgid shadow so they can read
the root:shadow, mode 0640 shadow files on behalf of an unprivileged
PAM caller -- cut with chisel they end up setgid root, which grants no
such access, so every password check performed by a non-root caller
(e.g. screen lock/unlock in a confined desktop session) fails. The
same flattening silently affects chage, expiry (shadow), crontab
(crontab), plocate (plocate) and ssh-agent (_ssh).

Extend fsutil.CreateOptions with optional Uid/Gid, applied with
os.Chown to newly created entries (and pre-existing ones when
OverrideMode is set, mirroring the mode semantics). Ownership is never
applied to symlinks, and hard links inherit it from their target's
inode. Unprivileged runs that cannot chown log a single warning and
keep the previous behavior.

tarball.Extract now carries the tar header uid/gid through for entries
and their parent directories, but only requests ownership that differs
from root:root: root-owned entries already end up root-owned when
cutting as root, and cannot be chowned when cutting as a regular user,
so the common case keeps its previous behavior exactly.

Verified cutting the real libpam-modules-bin_auth slice from
chisel-ubuntu-core (26.04): the chkpwd helpers now come out
root:shadow 2755, matching a plain dpkg -x of the same package.
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.

1 participant