Skip to content

Commit

Permalink
cbuild: stricten handling of suid files and security xattrs
Browse files Browse the repository at this point in the history
The suid_files field has been removed, instead every package that
installs suid files must explicitly declare the mode via file_modes
to verify that the final owner and permissions are correct.

Additionally, every package that sets an xattr in the security
namespace must likewise declare its mode.

Executable mode sanitization (which sets 755 mode on executables)
will not take place on files that have a mode explicitly declared.

Additionally, this fixes a bug when executables with security
capabilities would get an invalid owner (nobody/nogroup) because
of weird treatment of these by fakeroot. Forcing a chown after
will not mess up the xattrs in the fakeroot environment, so
reverse the order to get correct behavior.
  • Loading branch information
q66 committed Mar 11, 2024
1 parent 09bf7ac commit 37ef5a5
Show file tree
Hide file tree
Showing 26 changed files with 124 additions and 82 deletions.
18 changes: 10 additions & 8 deletions Packaging.md
Original file line number Diff line number Diff line change
Expand Up @@ -802,10 +802,13 @@ Keep in mind that default values may be overridden by build styles.
and optionally the recursive flag (`True` or `False`). The third field
is a regular permissions integer, e.g. `0o755`. This can be used when
the package creates a new group or user and needs to have files that
are owned by that. Keep in mind that the `suid` checks and so on still
happen, so if you make the permissions `suid`, you also need to declare
the file in `suid_files`. The permissions are applied in the order the
fields are added in the dictionary.
are owned by that. The permissions are applied in the order the
fields are added in the dictionary. Note that all setuid/setgid files
as well as files with xattrs in the security namespace must have an
explicit mode set here, otherwise they will not be allowed. That means
any suid file installed by a package without the template re-declaring
its mode is forbidden; the primary purpose is to make sure the packager
knows what kind of mode it needs to have.
* `file_xattrs` *(dict)* A dictionary of strings to dictionaries, where
the string keys are file paths (relative to the package, e.g. `usr/foo`)
and the dicts contain mappings of extended attribute names to values.
Expand All @@ -819,6 +822,8 @@ Keep in mind that default values may be overridden by build styles.
not use `setfattr` but `setcap` instead. For extended attributes to work
here, you need to have the right host programs (`setfattr` or `setcap`)
installed in the package build environment via `hostmakedepends`.
If setting the security namespace, `file_modes` entry must also be
declared, see above.
* `hardening` *(list)* Hardening options to be enabled or disabled for the
template. Refer to the hardening section for more information. This is
a simple list of strings that works similarly to `options`, with `!`
Expand Down Expand Up @@ -960,9 +965,6 @@ Keep in mind that default values may be overridden by build styles.
string or `.` implies default behavior. Effectively all sources that have
a path that is not the default will be extracted separately and then moved
into place.
* `suid_files` *(list)* A list of glob patterns (strings). The system will
reject any `setuid` and `setgid` files that do not match at least one
pattern in this list.
* `tools` *(dict)* This can be used to override default tools. Refer to the
section about tools for more information.
* `tool_flags` *(dict)* This can be used to override things such as `CFLAGS`
Expand Down Expand Up @@ -1373,9 +1375,9 @@ those are explicitly marked.
* `nostrip_files`
* `hardening`
* `nopie_files`
* `file_modes`
* `shlib_provides`
* `shlib_requires`
* `suid_files`
* `triggers`

The `hardening` option does not actually do anything (since subpackages do
Expand Down
6 changes: 3 additions & 3 deletions contrib/chromium/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,9 @@
"-Wno-deprecated-declarations",
],
}
suid_files = [
"usr/lib/chromium/chrome-sandbox",
]
file_modes = {
"usr/lib/chromium/chrome-sandbox": ("root", "root", 0o4755),
}
hardening = ["!scp"]
# lol
options = ["!cross", "!check", "!scanshlibs"]
Expand Down
18 changes: 13 additions & 5 deletions contrib/enlightenment/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,19 @@
url = "https://enlightenment.org"
source = f"http://download.enlightenment.org/rel/apps/{pkgname}/{pkgname}-{pkgver}.tar.xz"
sha256 = "11b6ef0671be5fead688bf554c30a2a1c683493ad10c5fe3115ffb4655424e84"
suid_files = [
"usr/lib/enlightenment/utils/enlightenment_ckpasswd",
"usr/lib/enlightenment/utils/enlightenment_system",
"usr/lib/enlightenment/utils/enlightenment_sys",
]
file_modes = {
"usr/lib/enlightenment/utils/enlightenment_ckpasswd": (
"root",
"root",
0o4755,
),
"usr/lib/enlightenment/utils/enlightenment_system": (
"root",
"root",
0o4755,
),
"usr/lib/enlightenment/utils/enlightenment_sys": ("root", "root", 0o4755),
}
# FIXME int: janky codebase
hardening = ["!int"]

Expand Down
6 changes: 5 additions & 1 deletion contrib/extrace/template.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pkgname = "extrace"
pkgver = "0.9"
pkgrel = 0
pkgrel = 1
build_style = "makefile"
hostmakedepends = ["libcap-progs"]
makedepends = ["linux-headers"]
Expand All @@ -10,6 +10,10 @@
url = "https://github.com/leahneukirchen/extrace"
source = f"{url}/archive/v{pkgver}.tar.gz"
sha256 = "e488db1126bd941e5a094e6024c3975f70abfa7ad51a3451191d1518c0b35ced"
file_modes = {
"usr/bin/extrace": ("root", "root", 0o755),
"usr/bin/pwait": ("root", "root", 0o755),
}
file_xattrs = {
"usr/bin/extrace": {
"security.capability": "cap_net_admin+ep",
Expand Down
3 changes: 3 additions & 0 deletions contrib/gamescope/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@
"b4eca5edca75355ea1443ad96fd59b0a407f6a2ce17ef5a8f9849c05fc10155f",
"165726ad21fbfc221c0363e40b597834068a416a11a1204ae2ac6d13ec161035",
]
file_modes = {
"usr/bin/gamescope": ("root", "root", 0o755),
}
file_xattrs = {
"usr/bin/gamescope": {
"security.capability": "cap_sys_nice+ep",
Expand Down
5 changes: 4 additions & 1 deletion contrib/mtr/template.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pkgname = "mtr"
pkgver = "0.95"
pkgrel = 1
pkgrel = 2
build_style = "gnu_configure"
configure_args = ["--without-gtk"]
configure_gen = ["./bootstrap.sh"]
Expand All @@ -14,6 +14,9 @@
f"https://github.com/traviscross/mtr/archive/refs/tags/v{pkgver}.tar.gz"
)
sha256 = "12490fb660ba5fb34df8c06a0f62b4f9cbd11a584fc3f6eceda0a99124e8596f"
file_modes = {
"usr/bin/mtr-packet": ("root", "root", 0o755),
}
file_xattrs = {
"usr/bin/mtr-packet": {
"security.capability": "cap_net_raw+ep",
Expand Down
1 change: 0 additions & 1 deletion contrib/qemu/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@
url = "https://qemu.org"
source = f"https://download.qemu.org/qemu-{pkgver}.tar.xz"
sha256 = "847346c1b82c1a54b2c38f6edbd85549edeb17430b7d4d3da12620e2962bc4f3"
suid_files = ["usr/libexec/qemu-bridge-helper"]
file_modes = {
"etc/qemu/bridge.conf": ("root", "_qemu", 0o640),
"usr/libexec/qemu-bridge-helper": ("root", "_qemu", 0o4710),
Expand Down
5 changes: 4 additions & 1 deletion contrib/spice-gtk/template.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pkgname = "spice-gtk"
pkgver = "0.42"
pkgrel = 0
pkgrel = 1
build_style = "meson"
configure_args = [
"-Dbuiltin-mjpeg=false",
Expand Down Expand Up @@ -60,6 +60,9 @@
url = "https://gitlab.freedesktop.org/spice/spice-gtk"
source = f"https://www.spice-space.org/download/gtk/spice-gtk-{pkgver}.tar.xz"
sha256 = "9380117f1811ad1faa1812cb6602479b6290d4a0d8cc442d44427f7f6c0e7a58"
file_modes = {
"usr/libexec/spice-client-glib-usb-acl-helper": ("root", "root", 0o755),
}
file_xattrs = {
"usr/libexec/spice-client-glib-usb-acl-helper": {
"security.capability": "cap_fowner+ep",
Expand Down
2 changes: 1 addition & 1 deletion contrib/xserver-xorg-core/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"CFLAGS": ["-D_GNU_SOURCE", "-D__uid_t=uid_t", "-D__gid_t=gid_t"],
"LDFLAGS": ["-Wl,-z,lazy"], # must be set for modules to work
}
suid_files = ["usr/libexec/Xorg.wrap"]
file_modes = {"usr/libexec/Xorg.wrap": ("root", "root", 0o4755)}
# FIXME int
hardening = ["!int"]
# test times out
Expand Down
1 change: 0 additions & 1 deletion main/dbus/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
f"https://dbus.freedesktop.org/releases/{pkgname}/{pkgname}-{pkgver}.tar.xz"
)
sha256 = "ba1f21d2bd9d339da2d4aa8780c09df32fea87998b73da24f49ab9df1e36a50f"
suid_files = ["usr/libexec/dbus-daemon-launch-helper"]
file_modes = {"usr/libexec/dbus-daemon-launch-helper": ("root", "dbus", 0o4750)}
# FIXME cfi
hardening = ["vis", "!cfi"]
Expand Down
3 changes: 1 addition & 2 deletions main/fuse/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
url = "https://github.com/libfuse/libfuse"
source = f"{url}/releases/download/{pkgname}-{pkgver}/{pkgname}-{pkgver}.tar.gz"
sha256 = "f797055d9296b275e981f5f62d4e32e089614fc253d1ef2985851025b8a0ce87"
suid_files = ["usr/bin/fusermount3"]
file_modes = {"usr/bin/fusermount3": ("root", "root", 0o4755)}
# ld: error: default version symbol fuse_loop_mt@@FUSE_3.2 must be defined
# tests need examples and are useless in chroot
options = ["!lto", "!check"]
Expand All @@ -23,7 +23,6 @@ def do_check(self):


def post_install(self):
self.chmod(self.destdir / "usr/bin/fusermount3", 0o4755)
self.rm(self.destdir / "etc/init.d/fuse3")
# compat links
self.install_link("fusermount3", "usr/bin/fusermount")
Expand Down
3 changes: 3 additions & 0 deletions main/gstreamer/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
url = "https://gstreamer.freedesktop.org"
source = f"{url}/src/{pkgname}/{pkgname}-{pkgver}.tar.xz"
sha256 = "969aaef396f252ce925132a4be2aa004e0320f5c1baf0acaaae09c544a6759f4"
file_modes = {
"usr/libexec/gstreamer-1.0/gst-ptp-helper": ("root", "root", 0o755),
}
file_xattrs = {
"usr/libexec/gstreamer-1.0/gst-ptp-helper": {
"security.capability": "cap_net_bind_service,cap_net_admin+ep",
Expand Down
2 changes: 1 addition & 1 deletion main/heimdal/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ def _kdc(self):
@subpackage("heimdal-clients")
def _client(self):
self.pkgdesc = f"{pkgdesc} (clients)"
self.suid_files = ["usr/bin/ksu"]
self.file_modes = {"usr/bin/ksu": ("root", "root", 0o4755)}

def _install():
self.take("usr/libexec/kdigest")
Expand Down
6 changes: 5 additions & 1 deletion main/iputils/template.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pkgname = "iputils"
pkgver = "20240117"
pkgrel = 0
pkgrel = 1
build_style = "meson"
configure_args = [
"-DNO_SETCAP_OR_SUID=true",
Expand All @@ -21,6 +21,10 @@
url = "https://github.com/iputils/iputils"
source = f"{url}/archive/{pkgver}.tar.gz"
sha256 = "a5d66e2997945b2541b8f780a7f5a5ec895d53a517ae1dc4f3ab762573edea9a"
file_modes = {
"usr/bin/clockdiff": ("root", "root", 0o755),
"usr/bin/ping": ("root", "root", 0o755),
}
file_xattrs = {
"usr/bin/clockdiff": {
"security.capability": "cap_net_raw,cap_sys_nice+ep",
Expand Down
6 changes: 3 additions & 3 deletions main/libgtop/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
url = "https://gitlab.gnome.org/GNOME/libgtop"
source = f"$(GNOME_SITE)/{pkgname}/{pkgver[:-2]}/{pkgname}-{pkgver}.tar.xz"
sha256 = "775676df958e2ea2452f7568f28b2ea581063d312773dd5c0b7624c1b9b2da8c"
suid_files = [
"usr/libexec/libgtop_server2",
]
file_modes = {
"usr/libexec/libgtop_server2": ("root", "root", 0o4755),
}


@subpackage("libgtop-devel")
Expand Down
2 changes: 1 addition & 1 deletion main/linux-pam/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
url = "https://github.com/linux-pam/linux-pam"
source = f"{url}/releases/download/v{pkgver}/Linux-PAM-{pkgver}.tar.xz"
sha256 = "fff4a34e5bbee77e2e8f1992f27631e2329bcbf8a0563ddeb5c3389b4e3169ad"
suid_files = ["usr/bin/unix_chkpwd"]
file_modes = {"usr/bin/unix_chkpwd": ("root", "root", 0o4755)}


def post_install(self):
Expand Down
2 changes: 1 addition & 1 deletion main/opendoas/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
url = "https://github.com/Duncaen/OpenDoas"
source = f"{url}/archive/v{pkgver}.tar.gz"
sha256 = "6da058a0e70b7543bc60624389b0b00b686189ec933828c522bf8b2600495a67"
suid_files = ["usr/bin/doas"]
file_modes = {"usr/bin/doas": ("root", "root", 0o4755)}
hardening = ["vis", "cfi"]
# no test suite
options = ["!check"]
Expand Down
2 changes: 1 addition & 1 deletion main/openssh/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
url = "https://www.openssh.com"
source = f"https://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/{pkgname}-{pkgver}.tar.gz"
sha256 = "910211c07255a8c5ad654391b40ee59800710dd8119dd5362de09385aa7a777c"
suid_files = ["usr/libexec/ssh-keysign"]
file_modes = {"usr/libexec/ssh-keysign": ("root", "root", 0o4755)}
# FIXME cfi (does not work); maybe make testsuite work first
hardening = ["vis", "!cfi"]
# portable openssh is not very portable
Expand Down
8 changes: 4 additions & 4 deletions main/polkit/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@
url = "https://www.freedesktop.org/wiki/Software/polkit"
source = f"$(FREEDESKTOP_SITE)/{pkgname}/releases/{pkgname}-{pkgver}.tar.gz"
sha256 = "9dc7ae341a797c994a5a36da21963f0c5c8e3e5a1780ccc2a5f52e7be01affaa"
suid_files = [
"usr/lib/polkit-1/polkit-agent-helper-1",
"usr/bin/pkexec",
]
file_modes = {
"usr/lib/polkit-1/polkit-agent-helper-1": ("root", "root", 0o4755),
"usr/bin/pkexec": ("root", "root", 0o4755),
}
# tests are broken on musl
options = ["!check"]

Expand Down
26 changes: 13 additions & 13 deletions main/shadow/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,19 @@
url = "https://github.com/shadow-maint/shadow"
source = f"{url}/releases/download/{pkgver}/shadow-{pkgver}.tar.xz"
sha256 = "17bdd568e7683e5df4e2de6cf69b0905d8a3a028c1689eb189005fea268e0ad6"
suid_files = [
"usr/bin/chage",
"usr/bin/chfn",
"usr/bin/chsh",
"usr/bin/expiry",
"usr/bin/gpasswd",
"usr/bin/newgidmap",
"usr/bin/newuidmap",
"usr/bin/newgrp",
"usr/bin/passwd",
"usr/bin/sg",
"usr/bin/su",
]
file_modes = {
"usr/bin/chage": ("root", "root", 0o4755),
"usr/bin/chfn": ("root", "root", 0o4755),
"usr/bin/chsh": ("root", "root", 0o4755),
"usr/bin/expiry": ("root", "root", 0o4755),
"usr/bin/gpasswd": ("root", "root", 0o4755),
"usr/bin/newgidmap": ("root", "root", 0o4755),
"usr/bin/newuidmap": ("root", "root", 0o4755),
"usr/bin/newgrp": ("root", "root", 0o4755),
"usr/bin/passwd": ("root", "root", 0o4755),
"usr/bin/sg": ("root", "root", 0o4755),
"usr/bin/su": ("root", "root", 0o4755),
}
hardening = ["!cfi"] # TODO
# messes with filesystem
options = ["!check"]
Expand Down
8 changes: 4 additions & 4 deletions main/util-linux/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,10 @@ def _dmesg(self):
def _mnt(self):
self.pkgdesc = "The mount(8) program and related utilities"
self.depends = [f"util-linux-common={pkgver}-r{pkgrel}"]
self.suid_files = [
"usr/bin/mount",
"usr/bin/umount",
]
self.file_modes = {
"usr/bin/mount": ("root", "root", 0o4755),
"usr/bin/umount": ("root", "root", 0o4755),
}

return [
"usr/bin/blkid",
Expand Down
2 changes: 0 additions & 2 deletions src/cbuild/core/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,6 @@ def find(self, path, pattern, files=False):
("nostrip_files", [], list, False, True, False),
("hardening", [], list, False, True, False),
("nopie_files", [], list, False, True, False),
("suid_files", [], list, False, True, False),
("tools", {}, dict, False, False, False),
("tool_flags", {}, dict, False, False, False),
("env", {}, dict, False, False, False),
Expand Down Expand Up @@ -545,7 +544,6 @@ def find(self, path, pattern, files=False):
("protected_paths", True),
("nostrip_files", True),
("nopie_files", True),
("suid_files", True),
("file_modes", True),
("file_xattrs", True),
("broken_symlinks", True),
Expand Down
Loading

0 comments on commit 37ef5a5

Please sign in to comment.