Openat returning EPERM for /dev/sg0


#1

I’ve been working on MakeMKV and, thanks to @jdstrand’s semaphore work that I’ve added to snapcraft-preload, I have managed to get the UI to come up. However, I am struggling with getting access to /dev/sg0. I have manually added both r and w rules to the AppArmor profile and reloaded it but still, the access to /dev/sg0 is returning EPERM.

Here is a log:

$ snap run --strace makemkv.makemkv info disk:0 2>&1 | grep sg0                
[pid 12577] access("/snap/makemkv/x18/dev/sg0", F_OK) = -1 ENOENT (No such file or directo[pid 12577] openat(AT_FDCWD, "/dev/sg0", O_RDWR|O_NONBLOCK) = -1 EPERM (Operation not perm[pid 12577] access("/snap/makemkv/x18/dev/sg0", F_OK) = -1 ENOENT (No such file or directory)
[pid 12577] openat(AT_FDCWD, "/dev/sg0", O_RDONLY|O_NONBLOCK) = -1 EPERM (Operation not permitted)
[pid 12577] access("/snap/makemkv/x18/dev/sg0", F_OK) = -1 ENOENT (No such file or directory)
[pid 12577] openat(AT_FDCWD, "/dev/sg0", O_RDONLY|O_NONBLOCK) = -1 EPERM (Operation not permitted)

I added these rules to /var/lib/snapd/apparmor/profiles/snap.makemkv.makemkv:

/dev/sg0[0-9]* r,
/dev/sg0[0-9]* w,

There are zero log messages about /dev/sg0 being denied by AppArmor and I have even seen messages indicating the access is granted with my rules in-place. I’m wondering if seccomp is refusing to honour the openat syscalls, possibly due to the O_NONBLOCK flag? @jdstrand, do you have any insight?


#2

What plugs do you have? You are likely running into the device cgroup being enabled if you are plugging an interface that uses the udev backend. You will probably need to write a new interface that adds the /dev/sg0 to the device cgroup using the udev backend, where whenever the device is added with udev rules. See an example for dev/dvb devices with the dvb interface here: https://github.com/snapcore/snapd/blob/master/interfaces/builtin/dvb.go#L39


#3

also … staing the obvious… does the user you run the app with have permissions to open the device ?


#4

I’m not doing anything with udev. I wrote my own apparmor rule (in the original post) to grant the read and write to /dev/sg0 because it currently isn’t covered by any interfaces.

Yes, I’m in the cdrom group and:

$ ls -l /dev/sg0
crw-rw----+ 1 root cdrom 21, 0 Feb 20 20:37 /dev/sg0

#5

How can I verify whether I need to do this, and can I do it manually to check whether it is curative?


#6

Firstly, there’s the question of what the device /dev/sg0 actually is used for and does. If the device fits in with something already existing interface, then the apparmor rule you have and an appropriate udev rule is probably necessary to add to that interface. I’m not familiar with what this specific device is used for, so I can’t really answer whether it should go into a pre-existing interface or if it needs it’s own interface.

Secondly, to test whether adding the device to the device cgroup is necessary (and thus curative for local development) you need to find out if your snap is currently inside a device cgroup. While not a general rule, most interfaces that provide access to something in /dev with AppArmor will also enable a device cgroup and use udev rules to add the device into the device cgroup when the device appears. The device cgroup blocks access to /dev/ independently of AppArmor, so to access something in /dev you need both AppArmor rules and a udev rule to add it to the device cgroup. Not all snaps are put into a device cgroup because not all snaps plug interfaces that allow access to /dev. Side-note: if you are using a devmode or classic snap, there will be no device cgroup as it’s turned off. So if the snap you are developing plugs an interface that turns on the device cgroup, you will need to add the device into the snap’s device cgroup in order to allow the snap to access the device. This is why I was asking which plugs your snap has.

There are a few ways to tell if your snap is currently being put inside a device cgroup:

  1. does the directory /sys/fs/cgroup/devices/snap.$YOUR_SNAP/ exist?
  2. does the file /etc/udev/rules.d/70-snap.$YOUR_SNAP.rules exist
  3. Does your snap plug an interface which uses the udev backend?

#7

Yes, yes, and yes :slight_smile:

The /dev/sg0 is a so-called “SCSI Generic” device, which in this instance is assigned to my Bluray drive. MakeMKV uses it presumably to send raw SCSI commands to access the drive at a lower level than the marshalled /dev/sr0 might allow, being that it needs to get the drive to send the video data even though “copying” is supposedly restricted by DRM (legal grey area - this isn’t the place to discuss copyright, methinks :-p).

My intention is to eventually update the optical-drive interface with these learnings once I’ve figured out the right enchantment to bless the computer with, sacrificed a goat, and spun-around on the spot three times reciting pi.


#8

I’m not sure if we want to provide such a “low-level” access to all scsi generic devices inside the optical-device since there are probably things that aren’t optical drives that this access would allow. I’ll defer to @jdstrand on that decision.

However, for hacking purposes, in the optical-drive interface in snapd here: https://github.com/snapcore/snapd/blob/master/interfaces/builtin/optical_drive.go#L53
add another element to the list with

`KERNEL=="sg[0-9]*"`

and recompile snapd and reconnect the interface and see if it works. If on the other hand you just want to test your AppArmor and Seccomp code locally modified you have is enough, if you start a shell with sudo snap run --shell $YOUR_SNAP.$YOUR_APP and then in another shell do:

sudo rm -f /sys/fs/cgroup/devices/snap.$YOUR_SNAP

and then run your snap from the shell you opened you should be able to test it.
(the reason you have to have the shell is so that you disable the device cgroup against an already running process, if you disable it and try to run a new process, snap-confine should recreate the cgroup)


#9

No, because /dev/sg* can give more access than that (I considered it for optical-drive before). But there is no reason why a generic-scsi-control interface can’t be created.


#10

Actually, just to see if it fixes your problem, go to /etc/udev/rules.d/snap.<your snap>.rules then add KERNEL=="sg[0-9]*", TAG+="snap_<snap>_<command>" then run sudo udevadm trigger. When you next start your application, you should have /dev/sg0 added to /sys/fs/cgroup/devices/snap.<snap>.<command>/devices.list


#11

Still getting EPERM on /dev/sg0 even with that change. This is why closed source is a pain - I can’t work out which things it’s doing are important in the strace and which aren’t.


#12

My build can be replicated with the snapcraft.yaml at https://paste.ubuntu.com/p/FnZNpRPWXr/. Maybe someone else can figure out what’s amiss - the UI reports that it can’t find any optical drives upon startup when in strict mode (report is in the log pane at the bottom of the window).


#13

Note, my suggested changes to the udev rules assumed you kept the apparmor rules changes. Assuming you made the changes to apparmor, loaded them into the kernel, made the udev changes and called sudo udevadm trigger, then after your snap runs, what:

  1. is in /sys/fs/cgroup/devices/snap.<snap>.<command>/devices.list?
  2. is the output of ls -l /dev/sg0?
  3. is your user in the ‘cdrom’ group?

#14

I built your snap and tried to run it, but it then occurred to me I don’t have an optical drive to read from at all, so I can’t reproduce this unfortunately.
I personally prefer to just hack directly on snapd (and disable re-exec) because I am constantly forgetting and overwriting my edits to the policies and so it’s easier to have it in snapd where it can’t change cause it’s in the binary


#15

SUCCESS!

Sorry about the time it took me to get back to this. @jdstrand, adding the apparmor rules AND the udev rule allows the app to detect the drive. Now we need to figure out the best place to put this in terms of snapd interfaces.


#16

Specifically, what is currently working:

Additional Apparmor rules:

/sys/bus/scsi/** r,
/dev/sg[0-9]* r,
/dev/sg[0-9]* w,

Additional udev rules:

KERNEL=="sg[0-9]*", TAG+="snap_makemkv_makemkv"

#17

For other following along, please also see Daniel’s https://github.com/snapcore/snapd/pull/6876