From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp0.migadu.com ([2001:41d0:303:e224::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms13.migadu.com with LMTPS id 6P1BCmHbFWe8ewAAqHPOHw:P1 (envelope-from ) for ; Mon, 21 Oct 2024 04:41:05 +0000 Received: from aspmx1.migadu.com ([2001:41d0:303:e224::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp0.migadu.com with LMTPS id 6P1BCmHbFWe8ewAAqHPOHw (envelope-from ) for ; Mon, 21 Oct 2024 06:41:05 +0200 X-Envelope-To: larch@yhetil.org Authentication-Results: aspmx1.migadu.com; dkim=fail ("headers rsa verify failed") header.d=debbugs.gnu.org header.s=debbugs-gnu-org header.b=nwPeWmKx; dkim=fail ("headers eddsa verify failed") header.d=russelstein.xyz header.s=ed25519 header.b="sZT/NfV7"; dkim=fail ("headers rsa verify failed") header.d=russelstein.xyz header.s=rsa header.b=Atc+PExF; dmarc=pass (policy=none) header.from=gnu.org; spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org" ARC-Seal: i=1; s=key1; d=yhetil.org; t=1729485664; a=rsa-sha256; cv=none; b=HTzRy91YjqDZlnxHlq1/4pkbA4rG02sHzsryQ8CeeZB7GdM4FzDHJXEyvosZ2HpWB1qmHg koHkK8eJ2OmuYkILKyTlCdy5W7S7LBtGQjZiaiVo2BLuduqCO34x6lWOoG9WIlVtZn2Hd7 gAYUYRmnTbe6vk+2vaMtija+22ygYiUp847Whw+WDVSu81roJQjZfikDwfrFKYW2MqOXFp qPXkEvJqV7Q3xeibCBVstrX/GTIzReH9ViJbyhtWgRNOP2Y+VH97rVT8aZqvSPfcm5//xj s05SHOlnC0SiXNy8QKQyUd1DKPn1RRIpF+9swTkBu23gw0MODghEEZ8D1uTCKw== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=fail ("headers rsa verify failed") header.d=debbugs.gnu.org header.s=debbugs-gnu-org header.b=nwPeWmKx; dkim=fail ("headers eddsa verify failed") header.d=russelstein.xyz header.s=ed25519 header.b="sZT/NfV7"; dkim=fail ("headers rsa verify failed") header.d=russelstein.xyz header.s=rsa header.b=Atc+PExF; dmarc=pass (policy=none) header.from=gnu.org; spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org" ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1729485664; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:mime-version:mime-version: content-type:content-type:resent-cc:resent-from:resent-sender: resent-message-id:list-id:list-help:list-unsubscribe:list-subscribe: list-post:dkim-signature; bh=Jv1Q3ggmq4d6C+AuYMjgVyp8DXl5ypr+PB0KXqv5tQw=; b=SYNghQVfQ89FFSHo0nMRkydPkoDDqJPpXqTSsSplr2rubbsNzKOVPwlQkfmOy2+nwDL8Re cIpAFi07/D7gi+Ktf23fjB697u7rhzJztJGQ8fPV7TKbLhdW0KItF00p5Ld7JOnvoskWBJ wwYbW5/upJi1JZpvZAOO+4IMhf5ppM1zP5m+ZeOKUVTE1gcQRN8A7awlIxk15RD9MPzsDD uEmS6wON19PQwMOAvzU1pTh+PndvbIs6DiQ6e6jBqqgltC4hvVHxhacWuu1Q/lXzBeM7hY FQEj2fEeMnf5F8nksEHwDFUlngX5DjW0HKbn+jMuzDEBF6GkryrXTwelKBnD+A== Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 107F22523 for ; Mon, 21 Oct 2024 06:41:03 +0200 (CEST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1t2kDh-0003FH-9v; Mon, 21 Oct 2024 00:40:41 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1t2kDd-0003EZ-8m for guix-patches@gnu.org; Mon, 21 Oct 2024 00:40:37 -0400 Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1t2kDc-0006pR-W5 for guix-patches@gnu.org; Mon, 21 Oct 2024 00:40:37 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debbugs.gnu.org; s=debbugs-gnu-org; h=MIME-Version:Date:From:To:Subject; bh=Jv1Q3ggmq4d6C+AuYMjgVyp8DXl5ypr+PB0KXqv5tQw=; b=nwPeWmKxX3Dzr90Jiu9OQHJFqtX6pp+KzoKq+0osRZH5lyNWr/7njqz4Sea9aUccEXIsanusE9k9c0Knw8JUfMWSyc5qX+GrjZvBifqM5CEuac+m76OEGZruvwF/D4g9rZVP8vzZwI2fsQ5UJ/gAahy/mpkH6gNzKycz+kWznArQoEvA1mKiQRhmGG95NNquqvFMAU4F3Gr5GolKq3iUa7SsM3szpb52xnpbwo1EtAZ8KYnsTGq1Ae4pS1LAvyBdSwYbpm66Dj0EutsWSDA0op5OmEx01kt5ih0U+S8TEu/0Z+HHiTUmzAVPFZEHvWeUMn7tCmbDwP0kMjhA1t2XYQ==; Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1t2kE2-0001wN-JX for guix-patches@gnu.org; Mon, 21 Oct 2024 00:41:02 -0400 X-Loop: help-debbugs@gnu.org Subject: [bug#73923] Vulnerability allowing takeover of build users Resent-From: Reepca Russelstein Original-Sender: "Debbugs-submit" Resent-CC: guix-patches@gnu.org Resent-Date: Mon, 21 Oct 2024 04:41:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 73923 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: To: 73923@debbugs.gnu.org X-Debbugs-Original-To: guix-patches@gnu.org Received: via spool by submit@debbugs.gnu.org id=B.17294856126991 (code B ref -1); Mon, 21 Oct 2024 04:41:02 +0000 Received: (at submit) by debbugs.gnu.org; 21 Oct 2024 04:40:12 +0000 Received: from localhost ([127.0.0.1]:49654 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1t2kDC-0001oe-AW for submit@debbugs.gnu.org; Mon, 21 Oct 2024 00:40:11 -0400 Received: from lists.gnu.org ([209.51.188.17]:39164) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1t2dOP-00065F-Cj for submit@debbugs.gnu.org; Sun, 20 Oct 2024 17:23:18 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1t2dNz-00029b-3Q for guix-patches@gnu.org; Sun, 20 Oct 2024 17:22:51 -0400 Received: from mailout.russelstein.xyz ([2605:6400:20:11e::1]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1t2dNw-0002Ww-77 for guix-patches@gnu.org; Sun, 20 Oct 2024 17:22:50 -0400 DKIM-Signature: v=1; a=ed25519-sha256; q=dns/txt; c=relaxed/relaxed; d=russelstein.xyz; s=ed25519; h=Content-Type:MIME-Version:Message-ID:Date: Subject:To:From:Sender:Reply-To:Cc:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=Jv1Q3ggmq4d6C+AuYMjgVyp8DXl5ypr+PB0KXqv5tQw=; b=sZT/NfV76GML3ye/LHIRo8LuQE Blwi3GQ5q7CODbGGn8yFtcPXvoOgKl9R7/WP6lqqtAgwQfI+QqWQkY8/1nAA==; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=russelstein.xyz; s=rsa; h=Content-Type:MIME-Version:Message-ID:Date:Subject :To:From:Sender:Reply-To:Cc:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=Jv1Q3ggmq4d6C+AuYMjgVyp8DXl5ypr+PB0KXqv5tQw=; b=Atc+PExF//LZSWoME8n9dxpECu 891MdEZaRvmKtqeX4lPz9ey+cM2Jy+zbPrFfj7GKpJJn6Ka4XKQ23PKp+YvzygjtmlP6mXZq1oREK DwsMNJWxJpR1sntbGqTen+gn2wLkE23DElr8NA7spyMJbWxPNo1ZyYxZhnry87b13+/lNc4NgGlnY kfsbrfROqh0xry6DbF5QgMhTtuYxTfUvrOe5Gb7iPNxjomQMRvCRNIW7gzOYFmsJMVPIanhSRDLn9 L8vkSZYxfUI7FBSW2QY+jIi7B/QR+Y9QVKS5TXmBNn0JwWKS7XQBEWsf3TgVx60wm9nO4Ruh9/Y7Y 1xwMFnlyOiCnR11+BVxOXQnha3FTZG3Cy/VMENorE3arxuZYDrCpW+zQQpHfJdpNRcFBT5C0iG5/g 8EeFXuw4EWPjQNi2wAJdT1ppuFNBMRoJa+zmsi0xMmWGSbyTQtU/nh6TOo0OJjBJ5xo0iMqQwMI8Q olhNEewpqU2VDvzhhP5PdGGGI9pyWNoB2IzLqEjn8gds9cgpITnbwUXdPqKaoKhbf3YonUc4qQ+4P PqDxEcwkFt1aPWDnZCtdSPuB7Hh7iEciruR2YZ3rjXjoxlTUyCwBAuO1OCvMQYw524CFeOiMdcWcV SorzIms/e0Si4M6bA0bOFdDGSPN9aAn5FnHNaFOf4=; Received: by russelstein.xyz with esmtpsa (TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.98) (envelope-from ) id 1t2dNr-000000001Aj-1J4R for guix-patches@gnu.org; Sun, 20 Oct 2024 16:22:44 -0500 Date: Sun, 20 Oct 2024 16:22:00 -0500 Message-ID: <871q0albbb.fsf@russelstein.xyz> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: multipart/signed; boundary="==-=-="; micalg=pgp-sha256; protocol="application/pgp-signature" Received-SPF: pass client-ip=2605:6400:20:11e::1; envelope-from=reepca@russelstein.xyz; helo=mailout.russelstein.xyz X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Mon, 21 Oct 2024 00:40:08 -0400 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-to: Reepca Russelstein X-ACL-Warn: , Reepca Russelstein via Guix-patches From: Reepca Russelstein via Guix-patches via Errors-To: guix-patches-bounces+larch=yhetil.org@gnu.org Sender: guix-patches-bounces+larch=yhetil.org@gnu.org X-Migadu-Flow: FLOW_IN X-Migadu-Country: US X-Migadu-Spam-Score: -1.60 X-Spam-Score: -1.60 X-Migadu-Queue-Id: 107F22523 X-Migadu-Scanner: mx12.migadu.com X-TUID: YW5p816pmlW3 --==-=-= Content-Type: multipart/mixed; boundary="=-=-=" --=-=-= Content-Type: text/plain For a very long time, guix-daemon has helpfully made the outputs of failed derivation builds available at the same location they were at in the build container (see https://git.savannah.gnu.org/cgit/guix.git/tree/nix/libstore/build.cc?id=e951a375a01262dfd470ee343baf7c41dbc6ff58#n1371). This has proven quite useful for debugging of various packages, but unfortunately it is implemented by a plain "rename" of the top-level store items from the chroot's store to the real store. This does not change the permissions or ownership of these files, which allows a setuid / setgid binary created by a malicious build to become exposed to the rest of the users, who can then use it to gain control over that build user. They can exploit this control to overwrite the output of any builds run by that user using /proc/PID/fd and SIGSTOP. Also, there is a window of time for /successful/ build outputs between when they are moved from the chroot and when their permissions are canonicalized, which likewise allows for setuid / setgid binaries to be exposed to other users (see https://git.savannah.gnu.org/cgit/guix.git/tree/nix/libstore/build.cc?id=e951a375a01262dfd470ee343baf7c41dbc6ff58#n2343). The first patch fixes the former, the second patch fixes the latter. We then need to update the guix package to use these new commits, which I leave to whoever applies this to do since my local repository is in a rather unclean state and a fresh work tree may take some time to be ready to run 'make update-guix-package'. --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-nix-build-sanitize-failed-build-outputs-prior-to-exp.patch Content-Transfer-Encoding: quoted-printable From=20e936861263d9bafdfbe395c12526f2dc48ac17d7 Mon Sep 17 00:00:00 2001 Message-ID: From: Reepca Russelstein Date: Sun, 20 Oct 2024 15:36:06 -0500 Subject: [PATCH 1/2] nix: build: sanitize failed build outputs prior to exposing them. The only thing keeping a rogue builder and a local user from collaborating = to usurp control over the builder's user during the build is the fact that whatever files the builder may produce are not accessible to any other users yet. If we're going to make them accessible, we should probably do some sanity checking to ensure that sort of collaborating can't happen. Currently this isn't happening when failed build outputs are moved from the chroot as an aid to debugging. * nix/libstore/build.cc (secureFilePerms): new function. (DerivationGoal::buildDone): use it. Change-Id: I9dce1e3d8813b31cabd87a0e3219bf9830d8be96 =2D-- nix/libstore/build.cc | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc index d23c0944a4..67ebfe2f14 100644 =2D-- a/nix/libstore/build.cc +++ b/nix/libstore/build.cc @@ -1301,6 +1301,34 @@ void replaceValidPath(const Path & storePath, const = Path tmpPath) MakeError(NotDeterministic, BuildError) =20 =20 +/* Recursively make the file permissions of a path safe for exposure to + arbitrary users, but without canonicalising its permissions, timestamp,= and + user. Throw an exception if a file type that isn't explicitly known to= be + safe is found. */ +static void secureFilePerms(Path path) +{ + struct stat st; + if (lstat(path.c_str(), &st)) return; + + switch(st.st_mode & S_IFMT) { + case S_IFLNK: + return; + + case S_IFDIR: + for (auto & i : readDirectory(path)) { + secureFilePerms(path + "/" + i.name); + } + /* FALLTHROUGH */ + + case S_IFREG: + chmod(path.c_str(), (st.st_mode & ~S_IFMT) & ~(S_ISUID | S_ISGID | S_I= WOTH)); + break; + + default: + throw Error(format("file `%1%' has an unsupported type") % path); + } +} + void DerivationGoal::buildDone() { trace("build done"); @@ -1372,9 +1400,15 @@ void DerivationGoal::buildDone() build failures. */ if (useChroot && buildMode =3D=3D bmNormal) foreach (PathSet::iterator, i, missingPaths) =2D if (pathExists(chrootRootDir + *i)) + if (pathExists(chrootRootDir + *i)) { + try { + secureFilePerms(chrootRootDir + *i); rename((chrootRootDir + *i).c_str(), i->c_str()); + } catch(Error & e) { + printMsg(lvlError, e.msg()); + } + } =20 if (diskFull) printMsg(lvlError, "note: build failure may have been caus= ed by lack of free disk space"); =2D-=20 2.45.2 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0002-nix-build-sanitize-successful-build-outputs-prior-to.patch Content-Transfer-Encoding: quoted-printable From=20d096d653cc69118e05f49247ab312d0096b16656 Mon Sep 17 00:00:00 2001 Message-ID: In-Reply-To: References: From: Reepca Russelstein Date: Sun, 20 Oct 2024 15:39:02 -0500 Subject: [PATCH 2/2] nix: build: sanitize successful build outputs prior to exposing them. There is currently a window of time between when the build outputs are expo= sed and when their metadata is canonicalized. * nix/libstore/build.cc (DerivationGoal::registerOutputs): wait until after metadata canonicalization to move successful build outputs to the store. Change-Id: Ia995136f3f965eaf7b0e1d92af964b816f3fb276 =2D-- nix/libstore/build.cc | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc index 67ebfe2f14..43a8a37184 100644 =2D-- a/nix/libstore/build.cc +++ b/nix/libstore/build.cc @@ -2369,15 +2369,6 @@ void DerivationGoal::registerOutputs() Path actualPath =3D path; if (useChroot) { actualPath =3D chrootRootDir + path; =2D if (pathExists(actualPath)) { =2D /* Move output paths from the chroot to the store. */ =2D if (buildMode =3D=3D bmRepair) =2D replaceValidPath(path, actualPath); =2D else =2D if (buildMode !=3D bmCheck && rename(actualPath.c_st= r(), path.c_str()) =3D=3D -1) =2D throw SysError(format("moving build output `%1%'= from the chroot to the store") % path); =2D } =2D if (buildMode !=3D bmCheck) actualPath =3D path; } else { Path redirected =3D redirectedOutputs[path]; if (buildMode =3D=3D bmRepair @@ -2463,6 +2454,20 @@ void DerivationGoal::registerOutputs() canonicalisePathMetaData(actualPath, buildUser.enabled() && !rewritten ? buildUser.getUID() : -1, i= nodesSeen); =20 + if (useChroot) { + if (pathExists(actualPath)) { + /* Now that output paths have been canonicalized (in particular + there are no setuid files left), move them outside of the + chroot and to the store. */ + if (buildMode =3D=3D bmRepair) + replaceValidPath(path, actualPath); + else + if (buildMode !=3D bmCheck && rename(actualPath.c_str(), pat= h.c_str()) =3D=3D -1) + throw SysError(format("moving build output `%1%' from the = chroot to the store") % path); + } + if (buildMode !=3D bmCheck) actualPath =3D path; + } + /* For this output path, find the references to other paths contained in it. Compute the SHA-256 NAR hash at the same time. The hash is stored in the database so that we can =2D-=20 2.45.2 --=-=-=-- --==-=-= Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQFLBAEBCAA1FiEEdNapMPRLm4SepVYGwWaqSV9/GJwFAmcVdHgXHHJlZXBjYUBy dXNzZWxzdGVpbi54eXoACgkQwWaqSV9/GJy4jggAoQ/t8iVxYERZamGGbhmXm4UC o8OHTpUtjxwrQJnDQVhl79xBUykgbTeZoDiXd1RNTEHp3oIySGkaHmjxsHKBgHVb Z9BvOyHPI5OFJJBZ+aGOn2swhLT8Ocph052UDqYBeHgBxpvrtKGNY7i2We5PqYkE XBdxrvUt7by/0DTzl1Ifdt2LCWL5UfdTREdHxVvo5cnmfyadplHWFPDWnT3wyAsG PcSvfo/vntefkAvgy0n4hCKfy/6w5C4hT3dxTJ8GGUoAFk90Z7vSIeDwJf/P4O+w JfOqfg9UFDkomHMsmcNiVer8EzaMCUXVI4QW6knihvohQn5XZ5mGi6azdaiqcg== =dCkH -----END PGP SIGNATURE----- --==-=-=--