2

I saw this statement at the end of this answer:

PS: I have no idea why rfkill works when run as an unprivileged user. On my Mint, it doesn't have a setuid or setgid bit.

I was curious, and looked on my Arch system. Here's what sudo and rfkill look like on my system. File sizes and dates have been omitted. It looks like there's no setuid bit on rfkill (there is one on sudo, for comparison).

$ /usr/bin/env ls -lah $(which sudo) $(which rfkill)
-rwxr-xr-x 1 root root [OMITTED] /sbin/rfkill
-rwsr-xr-x 1 root root [OMITTED] /sbin/sudo

Interestingly, running rfkill to disable & enable wireless access works as described here, even though I'm running rfkill from my account (i.e., not as root and not with sudo or similar).

How does rfkill not require root, as typically commands that enable/disable hardware need to be ran with root privileges?

3
  • Hint: getfacl /dev/rfkill while at the local keyboard. Commented Feb 19, 2024 at 7:26
  • @A.B I ran that, and one of the lines is # group: rfkill - looking at this Arch Wiki section, it states, "Right to control wireless devices power state (used by rfkill)" for the rfkill group. I'm not very familiar with groups, so I'm not sure how it being part of a group gives it that permission, but... am I on the right track with that? Commented Feb 19, 2024 at 7:34
  • 1
    @A.B Yep, I've accepted it. I totally forgot to earlier, thanks for the ping. Commented Mar 6, 2024 at 14:08

1 Answer 1

6

Modern Linux system ecosystems, probably when systemd based, can change the access rights of /dev entries when physically logged (ie: in front of the keyboard).

One can compare the ownership of /dev/rfkill. Exemple here on a Debian 12 system when looking with user having UID 1000 logged in a local (physical) session:

$ echo $UID
1000
$ getfacl -n /dev/rfkill
getfacl: Removing leading '/' from absolute path names
# file: dev/rfkill
# owner: 0
# group: 106
user::rw-
user:1000:rw-
group::rw-
mask::rw-
other::r--

So here the extra ACL gives owner access rights to the device file:

user:1000:rw-

If not at the part where the user is physically logged in (the sleep 5 gives the time to switch to a console with a login prompt), this ACL gets removed if it was present:

$ sleep 5; getfacl -n /dev/rfkill
getfacl: Removing leading '/' from absolute path names
# file: dev/rfkill
# owner: 0
# group: 106
user::rw-
group::rw-
mask::rw-
other::r--

As long as the linked kernel driver doesn't specifically restrict further access rights or ownership beyond the ACL on the device file, this works for the application using it without additional privileges.

Running (as root would be better but the result doesn't really change):

inotifywait -m -r -e attrib /dev

and switching twice, to a console with a login prompt and back, to get an idea, it's usually also done for:

  • audio-related files (/dev/snd/)
  • video-related files (/dev/dri/)
  • possibly some removable device files (eg: the target of /dev/cdrom ...)
  • KVM for virtualization (/dev/kvm)
  • probably other files I missed.

This might not reflect defaults, but gives an idea.

A quick search found this entry in systemd documentation's in Multi-Seat on Linux:

Note that logind manages ACLs on a number of device classes, to allow user code to access the device nodes attached to a seat as long as the user has an active session on it. This is mostly transparent to applications.

When this is managed using systemd at least two components play a role:

  • udevd tags eligible devices appearing on the system with TAG+="uaccess"

    Empirically the list could be found with something like (systems that don't have /lib symlinked to /usr/lib should also search in /usr/lib/udev/rules.d):

    grep -rw uaccess /etc/udev/rules.d /lib/udev/rules.d
    

    Among other results, one can find a line for rfkill in /lib/udev/rules.d/70-uaccess.rules:

    KERNEL=="rfkill", SUBSYSTEM=="misc", TAG+="uaccess"
    
  • logind tracks user seat changes (of course in addition to creation) and changes ACLs for devices previously tagged with uaccess when the user seat is active

    For the little details: seat_set_active() -> seat_apply_acls() -> devnode_acl_all(), the later checks for the uaccess tag.

1
  • 2
    This is known as uaccess, and isn’t well documented (at least by the projects implementing it). Commented Feb 19, 2024 at 8:55

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.