sudo make install fails to set suid bits
$ sudo make PREFIX=${HOME}/.local install
install -D -t /home/nero/.local/bin -o root -g root -m 04111 brightness
install -D -t /home/nero/.local/share/man/man1 brightness.1
$ stat /home/nero/.local/bin/brightness
File: /home/nero/.local/bin/brightness
Size: 19000 Blocks: 40 IO Block: 4096 regular file
Device: fd01h/64769d Inode: 4849710 Links: 1
Access: (0111/---x--x--x) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2023-09-21 16:12:04.397232551 +0000
Modify: 2023-09-21 16:12:04.397232551 +0000
Change: 2023-09-21 16:12:04.397232551 +0000
The suid bit is lost. This is a problem with the busybox coreutils/install.c:
strace:
chmod("/home/nero/.local/bin/brightness", 04111) = 0
lchown("/home/nero/.local/bin/brightness", 0, 0) = 0
From chown(2):
When the owner or group of an executable file is changed by an unprivileged user, the S_ISUID and S_ISGID mode bits are cleared. POSIX does not specify whether this also should happen when root does the chown(); the Linux behavior depends on the kernel version, and since Linux 2.2.13, root is treated like other users.
For install
to work correctly in this case, it needs to chown first and chmod second.
coreutils doing things in the right order:
fchownat(3, "brightness", 0, 0, AT_SYMLINK_NOFOLLOW) = 0
fchmodat(3, "brightness", 04111) = 0
Patch proposal for busybox:
diff --git a/coreutils/install.c b/coreutils/install.c
index c0f1c538a..a81a5a1ef 100644
--- a/coreutils/install.c
+++ b/coreutils/install.c
@@ -244,6 +244,14 @@ int install_main(int argc, char **argv)
}
}
+ /* Set the user and group id */
+ if ((opts & (OPT_OWNER|OPT_GROUP))
+ && lchown(dest, uid, gid) == -1
+ ) {
+ bb_perror_msg("can't change %s of %s", "ownership", dest);
+ ret = EXIT_FAILURE;
+ }
+
/* Set the file mode (always, not only with -m).
* GNU coreutils 6.10 is not affected by umask. */
if (chmod(dest, mode) == -1) {
@@ -254,13 +262,6 @@ int install_main(int argc, char **argv)
if (use_default_selinux_context)
setdefaultfilecon(dest);
#endif
- /* Set the user and group id */
- if ((opts & (OPT_OWNER|OPT_GROUP))
- && lchown(dest, uid, gid) == -1
- ) {
- bb_perror_msg("can't change %s of %s", "ownership", dest);
- ret = EXIT_FAILURE;
- }
next:
if (ENABLE_FEATURE_CLEAN_UP && isdir)
free(dest);