Linux automatic user ACL management

You may have not noticed, because it just works in Linux. Every time user logs in, Access Control Lists on a few important devices nodes are set for him. And every time his session becomes inactive, ACL are revoked. It looks like this:

# getfacl /dev/video0
getfacl: Removing leading '/' from absolute path names
# file: dev/video0
# owner: root
# group: video

Works like this:

Audio device access for user “zdzichu” is only granted when this user has an active session. If no one is logged in, or user “tomek” has an active session, then “zdzichu” access is revoked.

And works internally like this...

Which devices are affected?

In theory, the devices belonging to the seat used by user. In addition to peripherals, access to a few subsystems is granted. Best practice is to classify your device — some classes have the privilege of being ACL-managed. The logic lies in 70-uaccess.rules in udev configuration. Devices of the following classes are made accessible: ID_GPHOTO2, ID_HPLIP, ID_CDROM, ID_FFADO, ID_SMARTCARD_READER, ID_PDA, ID_REMOTE_CONTROL, ID_INPUT_JOYSTICK, ID_MEDIA_PLAYER. Names are self-explanatory.

In future, this classification will let administator decide which classes are granted to local user.

Some subsystems are treated specially, access is allowed without further classification. Those subsystems are: sound, video4linux, dvb, drm.

We need to go deeper

Classification from previous paragraph is really an abstraction layer. The ACL granting is handled at the lowest level in file /usr/lib/udev/rules.d/73-seat-late.rules by this line:

TAG=="uaccess", ENV{MAJOR}!="", RUN+="/usr/lib/systemd/systemd-uaccess $env{DEVNAME} $env{ID_SEAT}"

The systemd-uaccess helper is run for each device with uaccess tag (tagging itself happens in 70-uaccess.rules). On my system, there are a few devices affected:

# udevadm info --export-db | grep -cE "TAG.*uaccess"

Example: USB-to-RS232 converter

There is no proper class for this gizmo. We can either invent one and send the patch to upstream, or short-circuit logic by tagging device directly. For simplicity, let’s take the second route, keeping in mind it’s discouraged :)

After plugging in our device, use udevadm info to see what udev knows about it and what makes it special. It may be a serial number, specific values of vendor id or product id, type, model or any other property. For my device, I will differentiate on model. Create a simple rule:

# cat /etc/udev/rules.d/92-usbserial-for-user.rules 
SUBSYSTEM=="tty", ENV{ID_MODEL}=="USB_Serial_Converter", TAG+="uaccess"
From now on, every time this converter is plugged in, an active user will gain access to /dev/ttyUSB0 thanks to ACLs.

This leaves us with important question:

Who is an active user?

This information is managed by logind. CLI interaction is provided by loginctl:

# loginctl 
   SESSION        UID USER             SEAT            
        36        502 tomek            seat0           
         2        500 zdzichu          seat0    

# loginctl show-session -p Active 36

# loginctl show-session -p Active 2

Thus, ACLs for currently active user “zdzichu” are applied. Start of new session is signalled by pam_systemd module, which should be included in PAM stack. logind by itself watches for active VT console. In other implementations, like for example kmscon, explicit calls on console switch are needed. Calls to logind, of course.

(This section probably needs to be expanded)


What to do if you have daemon requiring access to device nodes? Should it start its own session and keep it active? The answer is no. Daemons should be added to the appropriate group. For example, CCTV daemon should be in group video, which has access to /dev/video*.

Group ownership of device nodes is of course managed by udev:

# grep video /usr/lib/udev/rules.d/*
/usr/lib/udev/rules.d/50-udev-default.rules:# video4linux
/usr/lib/udev/rules.d/50-udev-default.rules:SUBSYSTEM=="video4linux", GROUP="video"

Yes, that’s an UNIX way.

By the way, you don’t want users permanently added to groups like audio or video. Such user would be able to ssh into the machine while you are using it and spy on you using webcam, microphone etc. Access to such critical peripherals should only be granted for active user.

What could possibly go wrong?

Everything. There’s a lot of moving parts. Hopefully, most of them are very simple. And with this document, you’ll know what parts to check.

One quite complicated aspect is finding the link between current graphical session and Xorg socket in /tmp. This could go wrong, and will go wrong if you have per-user /tmp dirs¹. In this case current user will have no sound, no accelerated GUI etc.

Standard installations require pam_systemd to be included in PAM stack. pam_systemd(8) man page contains example snippet.

¹ — this could be fixed by looking for X socket in abstract namespace first. Nobody needed it done, yet.

Look into the past

Information here applies to the last couple of Fedora releases.

Some time ago, ACL modifications were done by udev working with ConsoleKit database. The rules were different. Devices had to be TAGed with TAG+="udev-acl" or variables like ENV{ACL_MANAGE}="1" were used. This no longer applies.


Comments powered by Disqus