Classic confinement review request for fwup

I’d like to request classic confinement for the fwup snap.

Reasons:

  • One of fwup's main operations is to write to MicroSD cards (and similar) at the block level. I.e., it opens and writes to device files like /dev/sdc.
  • It also has convenience methods that find and unmount the MicroSD cards

I looked through the various plugs, and it doesn’t look like anything will work. I’m not too surprised since writing directly to a device requires a degree of trust from users.

The snap can be found here: https://snapcraft.io/fwup

Thanks for any help,
Frank

I think that many MicroSD cards typically show up on the USB bus, even when they are hardwired. I suspect that the upcoming dynamic slots work would help you avoid classic.

@niemeyer - can you comment on the status of that work and if this should be considered for classic in the meantime?

@jdstrand Would the dynamic slots also provide the ability to access the block device for an sdcard? Even though it’s on the USB bus I would expect that the writing software probably cannot write to it unless it uses the /dev/sdx or similar block device nodes.

It’s mostly SDCards found at /dev/sdX (USB reader) or /dev/mmcblkX (built-in reader) that would be seen. I’ve seen people use fwup to initialize small SATA drives that are used in embedded x86 systems, but that’s less common. It still would be nice to have that use case work, but not a big deal.

1 Like

I made a mistake while publishing the fwup snap last week. Is there an easy way to unpublish it while the details on classic confinement or otherwise get figured out? The published version runs into permission errors. A few people already found it and are trying it out. Sorry, I expected it to go unnoticed until it was announced. I’m not sure why I’m having trouble finding this in the docs.

@niemeyer - ping re hotplugging and use of classic for now.

Ping again regarding this snap and classic.

@niemeyer - ping regarding this snap and classic.

@jdstrand It sounds like something could be made along the lines of the udisks2 interface for this snap. Meanwhile, if we want to get going quickly, would the raw-usb interface be enough of a start? It offers access to /dev/ttyUSB*.

Thanks for reviving this request.

fwup needs write access to /dev/sdc, /dev/mmcblk0, etc. or where ever the MicroSD card/removable drive shows up. I don’t think USB access will help. It looks like udisks2 is for detecting and managing mounts. I can’t tell that it helps in gaining write access to the raw disk.

The udisks2 interface grants access to those. It also grants much more, which is undue, but it might make sense to have something like a block-devices interface specifically for that.

1 Like

… said interface might even have a default behavior that excludes known disks mounted in the system, outside of /media. That would be quite nice, as it would offer snaps access to those external devices, without compromising the basic system data.

An advance +1 to granting auto-connection said interface to fwup by default, excluding the system devices.

cc @zyga-snapd

1 Like

If a disk is not mounted in the system and is then granted by the new interface would mounting that filesystem result in revoking said permission?

It’s okay if the answer is simplistic on the first implementation. Even if the logic is basic it would already prevent access into the root filesystem, which is better than the alternative of granting access into everything when that interface is connected.

1 Like

Please consider lvm, raid, etc when looking for ‘mounted in the system’.

That said, it’s probably worth looking to see if udev has tags that we can depend on, kinda like how it has ID_INPUT_JOYSTICK, but for removable media. We might be able to concoct something similar to joystick and the new device-buttons interface there.

1 Like

@zyga-snapd - can you comment on your investigation?

I looked at the udev database on my systems searching for anything that would indicate that a block device is mounted but found nothing of the sort. I then investigated systemd’s device units with similar outcome. Using systemctl status dev-sda2.device I could detect the actual kernel device being followed by that unit. On my openSUSE VM this was sys-devices-pci0000:00-0000:00:10.0-host2-target2:0:0-2:0:0:0-block-sda-sda2.device. The unit with the longer name was active. Since I had a /dev/sda1 FAT partition as well I decided to unmount that to see what happens then. Unfortunately there was no difference with the /dev/sda1 partition mounted or unmounted.

Having found nothing useful here I started looking at /proc/self/mountinfo and /proc/mounts. I was looking for events that we could monitor for to detect that something is mounted or unmounted. According to proc(5) we can open /proc/mounts for reading and obtain a priority event when the mount table changes. I decided to test that quickly with the following program:

zyga@yantra:~/mount-watch> cat main.c 
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <sys/types.h>

int main(int argc, char **argv)
{
	int epoll_fd, mounts_fd, n;
	struct epoll_event event;

	epoll_fd = epoll_create1(EPOLL_CLOEXEC);
	if (epoll_fd < 0) {
		perror("cannot create epoll object");
		return 1;
	}

	mounts_fd = open("/proc/mounts", O_RDONLY|O_CLOEXEC);
	if (mounts_fd < 0) {
		perror("cannot open /proc/mounts");
		return 2;
	}

	memset(&event, 0, sizeof event);
	event.events |= EPOLLPRI;
	event.data.u32 = 0xCAFE;
	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, mounts_fd, &event) < 0) {
		perror("cannot add /proc/mounts file descriptor to epoll");
		return 3;
	}

	for (;;) {
		memset(&event, 0, sizeof event);
		n = epoll_wait(epoll_fd, &event, 1, 0); 
		if (n < 0) {
			perror("cannot wait for epoll event");
			return 4;
		}
		if (n == 1) {
			if (event.data.u32 == 0xCAFE) {
				printf("priority event on /proc/mounts\n");
			}
		}
	}

	return 0;
}

To compile the program and invoke it as non-root user. The program correctly detects changes to the mount table.

With this knowledge we could do as follows:

  • Teach snapd to monitor mount events and track the set of mounted block devices.
  • Create an interface that allows interaction with block devices that are not mounted.
  • React to mount changes and modify device control groups accordingly.

Some questions and comments:

  • Assume that a device was previously unmounted and “available” though the interface. When said device is mounted how do we know if it was mounted by a snap or by the rest of the system. If the device was mounted by a snap we should keep access as-is. If a device was mounted by the system we should revoke access.
  • We could manipulate control groups directly or we could attach a PBF program and a shared table that contains whitelisted devices (or blacklisted devices) and manipulate the map from snapd. I haven’t checked this option but it is interesting since it doesn’t involve as many racy operations - just react to mount events and update the BPF map. The whole process is atomic across all the snaps. Should

I’d prefer not to add an opaque backend for the security policy for some devices and not others. IMO, it is bad enough that we are updating a device cgroup rather than an inode label that AppArmor can look at (ie, what we want to move to long term); I’d rather not add more complexity in this area. It should be fine to update the udev rules file and call trigger on block devices.

@zyga-snapd - what is the status of this?

I’m not actively working on this. @pedronis, could we have this in the backlog / TODO list - specifically improved management of block devices for unmounted filesystems.