Source: shadow Version: 1:4.11.1+dfsg1-2 Severity: normal Tags: patch User: debian-d...@lists.debian.org Usertags: dpkg-root-support X-Debbugs-Cc: jo...@debian.org
Hi, when creating chroots for new architectures that are in the process of being bootstrapped without yet having emulation support from qemu, it is not possible to run maintainer scripts inside the foreign architecture chroot because foreign architecture ELF binaries cannot be executed. The solution to that problem is to run maintainer scripts from outside the chroot and use the DPKG_ROOT environment variable to instruct the maintainer script on which chroot to operate. By default, for normal installations, that environment variable is set, but empty. Apart from init-system-helpers and pam, all packages in the Essential:yes set have support for DPKG_ROOT already. To start building packages we also need to install build-essential. In debootstrap, the buildd variant includes the Essential:yes packages, Priority:required packages and build-essential. Strictly speaking passwd is not necessary to start building packages as it only gets installed in the buildd variant of debootstrap because it is Priority:required. The postinst of apt also indirectly depends on passwd via adduser to create the _apt user, but since apt is able to operate without the _apt user, this also doesn't make passwd required for the early native bootstrap phase. The patch at the end of this mail proposes two ways to add support for DPKG_ROOT to the shadowconfig script as it is called by the passwd postinst. The first method, which is disabled by a "if false" uses the --root parameter to pwck, grpck, pwconv and grpconv to let these tools chroot into the directory stored in DPKG_ROOT and then operate on that directory instead of /. Since the DPKG_ROOT variable is empty for normal installations, that codepath would also transparently work for those without any conditionals. The second method that would solve this situation is shown in the second branch of the if-statement. The disadvantage of the first method is, that we still need to call chroot(). Currently, all other packages in the Essential:yes, Priority:required and build-essential set can be installed without any call to chroot(). The passwd postinst via shadowconfig would be the only part that requires the chroot() call when using the --root parameter. It would be nice if no component would require doing the chroot() call because that would allow creating a DPKG_ROOT chroot simply inside fakeroot. Thus, the second branch of the conditional implements a method that works without chroot() and creates a bit-by-bit identical result compared to a normal installation. While those 10 lines are definitely complex and prone to breaking, please consider using that method anyway because 1) the code-path is never executed during a normal installation because the DPKG_ROOT variable is empty 2) we regularly test this method in our CI system and would send patches if it should break in the future 3) if it breaks it would only break DPKG_ROOT support and not normal installations What do you think? Thanks! cheers, josch diff -Nru shadow-4.11.1+dfsg1/debian/shadowconfig shadow-4.11.1+dfsg1/debian/shadowconfig --- shadow-4.11.1+dfsg1/debian/shadowconfig 2022-03-03 20:41:41.000000000 +0100 +++ shadow-4.11.1+dfsg1/debian/shadowconfig 2022-03-14 14:18:52.000000000 +0100 @@ -5,14 +5,40 @@ shadowon () { set -e - pwck -q -r - grpck -r - pwconv - grpconv - chown root:root /etc/passwd /etc/group - chmod 644 /etc/passwd /etc/group - chown root:shadow /etc/shadow /etc/gshadow - chmod 640 /etc/shadow /etc/gshadow + + if false; then + pwck -q -r --root "${DPKG_ROOT}/" + grpck -r --root "${DPKG_ROOT}/" + pwconv --root "${DPKG_ROOT}/" + grpconv --root "${DPKG_ROOT}/" + elif [ -n "$DPKG_ROOT" ] \ + && cmp "${DPKG_ROOT}/etc/passwd" "${DPKG_ROOT}/usr/share/base-passwd/passwd.master" 2>/dev/null \ + && cmp "${DPKG_ROOT}/etc/group" "${DPKG_ROOT}/usr/share/base-passwd/group.master" 2>/dev/null; then + # If dpkg is run with --force-script-chrootless and if /etc/passwd + # and /etc/group are unchanged, we avoid the chroot() call by manually + # processing the files. This produces bit-by-bit identical results + # compared to the normal case as shown by the CI setup at + # https://salsa.debian.org/helmutg/dpkg-root-demo/-/jobs + for f in passwd group; do + cp -a "${DPKG_ROOT}/etc/$f" "${DPKG_ROOT}/etc/$f-" + done + chmod 600 "${DPKG_ROOT}/etc/passwd-" + sed -i 's/^\([^:]\+\):\*:/\1:x:/' "${DPKG_ROOT}/etc/group" "${DPKG_ROOT}/etc/passwd" + [ -n "$SOURCE_DATE_EPOCH" ] && epoch=$SOURCE_DATE_EPOCH || epoch=$(date +%s) + sed "s/^\([^:]\+\):.*/\1:*:$((epoch/60/60/24)):0:99999:7:::/" "${DPKG_ROOT}/etc/passwd" > "${DPKG_ROOT}/etc/shadow" + sed "s/^\([^:]\+\):.*/\1:*::/" "${DPKG_ROOT}/etc/group" > "${DPKG_ROOT}/etc/gshadow" + touch "${DPKG_ROOT}/etc/.pwd.lock" + chmod 600 "${DPKG_ROOT}/etc/.pwd.lock" + else + pwck -q -r + grpck -r + pwconv + grpconv + fi + chown root:root "${DPKG_ROOT}/etc/passwd" "${DPKG_ROOT}/etc/group" + chmod 644 "${DPKG_ROOT}/etc/passwd" "${DPKG_ROOT}/etc/group" + chown root:shadow "${DPKG_ROOT}/etc/shadow" "${DPKG_ROOT}/etc/gshadow" + chmod 640 "${DPKG_ROOT}/etc/shadow" "${DPKG_ROOT}/etc/gshadow" } shadowoff () {