Join GitHub today
GitHub is home to over 20 million developers working together to host and review code, manage projects, and build software together.
interfaces/mir: allow Wayland socket and non-root sockets #4365
Conversation
| @@ -49,6 +49,9 @@ capability sys_tty_config, | ||
| /{dev,run}/shm/\#* mrw, | ||
| /run/mir_socket rw, | ||
| +/run/user/[0-9]*/mir_socket rw, |
jdstrand
Dec 6, 2017
Contributor
I'm surprised we didn't notice this access before since we allow it in mirConnectedPlugAppArmor.
| @@ -49,6 +49,9 @@ capability sys_tty_config, | ||
| /{dev,run}/shm/\#* mrw, | ||
| /run/mir_socket rw, | ||
| +/run/user/[0-9]*/mir_socket rw, | ||
| +/run/wayland* rw, | ||
| +/run/user/[0-9]*/wayland* rw, |
jdstrand
Dec 6, 2017
Contributor
I think the wayland interface should be updated to be implicit on classic and providable as a snap on non-classic (similar to how avahi-observe was recently converted) with these rules in the waylandPermanentSlotAppArmor policy. Then the mir snap would slots both mir and wayland.
gerboland
Dec 6, 2017
Yep I initially tried the Mir snap providing the wayland interface, but it failed as it was not the core snap. I agree your proposal is much clearer, I'll do that then
jdstrand
requested changes
Dec 7, 2017
This is a great start, thanks! A few questions which I think will lead to code changes inline.
| - core | ||
| + deny-auto-connection: true | ||
| + deny-connection: | ||
| + on-classic: false |
jdstrand
Dec 7, 2017
Contributor
You want this for auto-connection:
deny-auto-connection:
on-classic: false
| +capability sys_tty_config, | ||
| +/dev/tty[0-9]* rw, | ||
| + | ||
| +/{dev,run}/shm/\#* mrw, |
jdstrand
Dec 7, 2017
Contributor
Is this access actually needed by wayland? I recall it was a mir-specific implementation detail.
| +/run/user/[0-9]*/wayland-[0-9]* rw, | ||
| + | ||
| +# XWayland requires access to this | ||
| +/run/xwayland-shared-* rw, |
jdstrand
Dec 7, 2017
Contributor
mutter doesn't seem to expose this on my system. Is this mir-specific? Something else?
| +# Needed by server upon client connect | ||
| +accept | ||
| +accept4 | ||
| +shmctl |
| + | ||
| +const waylandConnectedSlotAppArmor = ` | ||
| +# Description: Permit clients to use Wayland | ||
| +unix (receive, send) type=seqpacket addr=none peer=(label=###PLUG_SECURITY_TAGS###), |
jdstrand
Dec 7, 2017
Contributor
This was also a mir-specific anonymous socket. Is it required by wayland?
| ` | ||
| const waylandConnectedPlugAppArmor = ` | ||
| -# Description: Can access compositors supporting the wayland protocol | ||
| +# Description: Permit clients to connect to compositors supporting the Wayland protocol | ||
| +unix (receive, send) type=seqpacket addr=none peer=(label=###SLOT_SECURITY_TAGS###), |
| + } else { | ||
| + new = slotAppLabelExpr(slot) | ||
| + } | ||
| + snippet := strings.Replace(waylandConnectedPlugAppArmor, old, new, -1) |
| + new := plugAppLabelExpr(plug) | ||
| + snippet := strings.Replace(waylandConnectedSlotAppArmor, old, new, -1) | ||
| + spec.AddSnippet(snippet) | ||
| + } |
gerboland
commented
Dec 8, 2017
|
Ok, iteration 2 ready. I've whipped up a quick Weston snap to test it with: |
| - core | ||
| + deny-auto-connection: | ||
| + on-classic: false |
jdstrand
Dec 8, 2017
Contributor
This wasn't fixed in the manner needed. Please adjust to be:
wayland:
allow-installation:
slot-snap-type:
- app
- core
deny-connection:
on-classic: false
deny-auto-connection:
on-classic: false
| +capability sys_tty_config, | ||
| +/dev/tty[0-9]* rw, | ||
| + | ||
| +/{dev,run}/shm/\#* mrw, |
jdstrand
Dec 7, 2017
Contributor
Is this access actually needed by wayland? I recall it was a mir-specific implementation detail.
jdstrand
requested changes
Dec 8, 2017
Thanks for the updates! This is really coming along. Can you also update wayland_test.go and address the inline comments?
| +/dev/tty[0-9]* rw, | ||
| + | ||
| +# Create the Wayland socket and lock file | ||
| +/run/user/[0-9]*/wayland-[0-9]* rw, |
jdstrand
Dec 8, 2017
Contributor
I think you will need:
/run/user/[0-9]*/wayland-shared-* rw,
/run/user/[0-9]*/wayland-cursor-shared-* rw,
gerboland
Dec 14, 2017
I've been tracking these down, these are files for shared memory created by wayland (another one being xwayland-shared), to pass data between client and compositor, for example pass pixel buffers, cursor images.
The tricky thing is that clients will create them in their XDG_RUNTIME_DIR. For a snapped app, this directory is per-snap. Non-snapped apps use the normal /run/user/id -u
It is correct that the Wayland socket is in a shared place, so multiple clients can connect to it. But these shared memory files I think can be confined inside the snap's XDG_RUNTIME_DIR, as I don't believe they are ever shared between clients, they are purely for client-server communication.
For snapped apps, I think in addition to your proposal we should add
/run/user/[0-9]*/snap.*/wayland-shared-* rw,
/run/user/[0-9]*/snap.*/wayland-cursor-shared-* rw,
/run/user/[0-9]*/snap.*/xwayland-shared-* rw,
(XWayland clients create the later). WDYT?
jdstrand
Dec 14, 2017
Contributor
It would be fine for the server to do this, but note that @jhenstridge is working on user mounts. This will allow the snap's XDG_RUNTIME_DIR to be /run/user/id -u on the inside, but bind mounted on top of a snap-specific directory, then snapd/snap-confine/snap-update-ns would bind mount the wayland socket into the snap-specific dir. This would mean we could get rid of the desktop helper bits for the compat symlink (this also more closely follows the upstream designs).
How these intersect with the above accesses... I'm not sure. With blackbox testing, it seems to be initiated on the client side (they do an open() then unlink()) in the client's XDG_RUNTIME_DIR and I think the client must pass the fd over to the server. Ie, if I add an appropriate 'audit deny' rule, I can see denials like this:
apparmor="DENIED" operation="mknod" profile="snap.gedit.gedit" name="/run/user/1000/snap.gedit/wayland-cursor-shared-Hcwp8Q" pid=4896 comm="gedit" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
as such, what you should do is add waylandConnectedSlotAppArmor policy, like so:
const waylandConnectedSlotAppArmor = `
# Allow access to client Wayland sockets for connected snaps
owner /run/user/[0-9]*/###PLUG_SECURITY_TAGS###/wayland-shared-* rw,
owner /run/user/[0-9]*/###PLUG_SECURITY_TAGS###/wayland-cursor-shared-* rw,
owner /run/user/[0-9]*/###PLUG_SECURITY_TAGS###/xwayland-shared-* rw,
This will allow the server to access these sockets for connected snaps. If my blackbox testing analysis is correct, this should continue to work after user mounts are in effect, because the slot side will continue to see these in /run/user/id -u/snap.*.
jdstrand
Dec 14, 2017
Contributor
In addition to the above, the waylandPermanentSlotAppArmor policy would need this to handle unconfined applications:
owner /run/user/[0-9]*/wayland-shared-* rw,
owner /run/user/[0-9]*/wayland-cursor-shared-* rw,
owner /run/user/[0-9]*/xwayland-shared-* rw,
| +/run/user/[0-9]*/wayland-[0-9]* rw, | ||
| + | ||
| +# Allow write access to create XDG_RUNTIME_DIR (until lp:1656340 is fixed) | ||
| +/run/user/[0-9]*/ w, |
jdstrand
Dec 8, 2017
Contributor
LP: #1656340 was about not creating /run/user/<uid>/snap.$SNAP, not /run/user/<uid>. The session manager is supposed to create this directory, not the snap. Why did you add this rule?
gerboland
Dec 11, 2017
Mir is running as root, and I'm seeing /run/user/0 does not exist. Well with
[ -n "$XDG_RUNTIME_DIR" ] && mkdir -p $XDG_RUNTIME_DIR -m 700
I get
audit[6690]: AVC apparmor="DENIED" operation="mkdir" profile="snap.mir-kiosk.mir-kiosk" name="/run/user/0/" pid=6690 comm="mkdir" requested_mask="c" denied_mask="c" fsuid=0 ouid=0
mir-kiosk.mir-kiosk[6286]: mkdir: cannot create directory ‘/run/user/0’: Permission denied
Session Manager bug?
jdstrand
Dec 11, 2017
Contributor
In this case I assume you mean that you are starting mir as a daemon. In this case, there is no user session and IMO it would be snappy's responsibility to create this directory. We had a previous PR for this in snap-confine, but it was never committed for various reasons. @zyga - can you comment? IIRC, the issue was the parent directories of /run/user/<uid> but I think if we just create /run/user/<uid> as 0700 as per the specification and log (perhaps not fail) if /run/user does not exist, we should be fine.
gerboland
Dec 11, 2017
Correct assumption. Daemonized Mir just needs an accessible XDG_RUNTIME_DIR somehow.
| +bind | ||
| +listen | ||
| +# Needed by server upon client connect | ||
| +accept4 |
jdstrand
Dec 8, 2017
Contributor
Pease add accept back for the architectures that use it instead of accept4.
| +` | ||
| + | ||
| +const waylandConnectedPlugAppArmor = ` | ||
| +# Allow access to the Wayland compositor server socket | ||
| owner /run/user/*/wayland-[0-9]* rw, |
jdstrand
Dec 8, 2017
Contributor
This should be:
owner /run/user/[0-9]*/wayland-[0-9]* rw,
I think you will also need:
owner /run/user/[0-9]*/wayland-shared-* rw,
owner /run/user/[0-9]*/wayland-cursor-shared-* rw,
| owner /run/user/*/wayland-[0-9]* rw, | ||
| +# XWayland needs access to this | ||
| +/run/xwayland-shared-* rw, |
jdstrand
Dec 8, 2017
Contributor
You removed this from waylandPermanentSlotAppArmor, but left it here. This should be removed too, no?
gerboland
Dec 11, 2017
I'm building my Chromium snap to incorporate XWayland, which will create that socket on startup. I was thinking it handiest to add support for any XWayland client to the Wayland interface. But in retrospect that's not very secure.
I'll iterate this a bit more.
| + spec.TagDevice(`KERNEL=="mice"`) | ||
| + spec.TagDevice(`KERNEL=="mouse[0-9]*"`) | ||
| + spec.TagDevice(`KERNEL=="event[0-9]*"`) | ||
| + spec.TagDevice(`KERNEL=="ts[0-9]*"`) |
jdstrand
Dec 11, 2017
Contributor
Yes. Note that this is the UDevPermanentSlot policy-- ie, what to tag for the slot provider so that the slot provider has these devices added to its device cgroup so it may function. When OnClassic, weston, mutter, etc are not running under snappy and are implicitly provided by the classic distro, and therefore there is no 'slot provider snap' where we need to tag these devices.
gerboland
changed the title from
interfaces/mir: allow Wayland socket and non-root sockets
to
WIP: interfaces/mir: allow Wayland socket and non-root sockets
Dec 13, 2017
codecov-io
commented
Dec 13, 2017
•
Codecov Report
@@ Coverage Diff @@
## master #4365 +/- ##
==========================================
+ Coverage 78.02% 78.04% +0.01%
==========================================
Files 452 452
Lines 31278 31306 +28
==========================================
+ Hits 24404 24432 +28
Misses 4830 4830
Partials 2044 2044
Continue to review full report at Codecov.
|
gerboland
added some commits
Dec 6, 2017
gerboland
changed the title from
WIP: interfaces/mir: allow Wayland socket and non-root sockets
to
interfaces/mir: allow Wayland socket and non-root sockets
Jan 2, 2018
gerboland
commented
Jan 2, 2018
|
Assuming tests pass on CI, this should be good for another review pass. Thanks |
jdstrand
requested changes
Jan 3, 2018
Thanks for all the updates! This is looking really good. Just a few small comments inline.
| + | ||
| +# Create the Wayland socket and lock file | ||
| +owner /run/user/[0-9]*/wayland-[0-9]* rw, | ||
| +# Allow access to common client Wayland sockets |
| ` | ||
| const waylandConnectedPlugAppArmor = ` | ||
| -# Description: Can access compositors supporting the wayland protocol | ||
| +# Allow access to the Wayland compositor server socket | ||
| +/run/user/[0-9]*/wayland-[0-9]* rw, |
jdstrand
Jan 3, 2018
Contributor
Shouldn't this be 'owner' match? I expected it and the server side has owner match....
gerboland
Jan 4, 2018
•
This ends up in the client's AppArmor policy, yeah? I didn't add owner as I was leaving open the possibility that the wayland socket may have different uid/gid than the client. But then you're right in that I should remove the "owner" from the client sockets. I think I'll do what you recommend
jdstrand
Jan 4, 2018
Contributor
It is in the client's apparmor policy, yes. AIUI, part of the design of wayland was to not need for it to run as root (like X required for a long time) and indeed, mutter, plasma/wayland and sway all run their compositors under the uid of the user, which is why I thought this could use 'owner' match. What is the use case for a client running under one uid to use a wayland socket created/used by a compositor running as a different user?
| +# Allow client create common Wayland sockets to share with compositor | ||
| +owner /run/user/[0-9]*/wayland-shared-* rw, | ||
| +owner /run/user/[0-9]*/wayland-cursor-shared-* rw, | ||
| +owner /run/user/[0-9]*/xwayland-shared-* rw, |
jdstrand
Jan 3, 2018
Contributor
You mentioned before that the client creates them in XDG_RUNTIME_DIR for the client, so it makes sense that ConnectedSlotAppArmor policy uses this:
owner /run/user/[0-9]*/###PLUG_SECURITY_TAGS###/wayland-shared-* rw,
owner /run/user/[0-9]*/###PLUG_SECURITY_TAGS###/wayland-cursor-shared-* rw,
owner /run/user/[0-9]*/###PLUG_SECURITY_TAGS###/xwayland-shared-* rw,
XDG_RUNTIME_DIR is (currently) expected to be set to /run/user/<uid>/snap.$SNAP and the default policy already allows:
owner /run/user/[0-9]*/snap.@{SNAP_NAME}/** mrwklix,
so it seems to me that these three rules for wayland-shared, wayland-cursor-shared and xwayland-shared should not be needed in ConnectedPlugAppArmor. Once we bind mount the snap-specific XDG_RUNTIME_DIR onto /run/user/<uid>, then you should have these rules, but today, we aren't doing that. Feel free to add a comment for this.
I realize I said I thought you might need these paths, but that was before your investigation that these are created client side.
| + app1: | ||
| + command: foo | ||
| + slots: [wayland] | ||
| +` |
| + c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "owner /run/user/[0-9]*/wayland-shared-* rw,") | ||
| + c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "owner /run/user/[0-9]*/wayland-cursor-shared-* rw,") | ||
| + c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "owner /run/user/[0-9]*/xwayland-shared-* rw,") | ||
| + c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "/etc/drirc r,") |
jdstrand
Jan 3, 2018
•
Contributor
Typically you only need to test for one rule that is not shared with the other snippets, eg, here the drirc rule would work. Sometimes it is nice to also test other types of rules (eg, the security label on unix sockets, snap-specific file paths, etc). This applies to the ConnectedSlot and PermanentSlot policy below too. Not a blocker.
jdstrand
approved these changes
Jan 4, 2018
Thanks for making all these changes! I see that you added 'owner' match but there is an open question on it. I'm going to approve this as is for now since it is taking my recommendation of being more strict.
| @@ -139,10 +120,6 @@ func (s *WaylandInterfaceSuite) TestAppArmorSpecOnClassic(c *C) { | ||
| c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.classicSlot), IsNil) | ||
| c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"}) | ||
| c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "/run/user/[0-9]*/wayland-[0-9]* rw,") |
jdstrand
Jan 4, 2018
Contributor
Since you used 'owner' above now, it would be nice to adjust this for owner so that if we change the above rule to remove it, this would fail.
gerboland
commented
Jan 5, 2018
|
CI failing on fedora 26 setup... |
gerboland commentedDec 6, 2017
Mir 0.28 and greater implements the Wayland protocol, and needs to create its socket.
Mir running as non-root will create those sockets in $XDG_RUNTIME_DIR which is typically /run/users/*, so allow that too.