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: add ofono interface #2058
Conversation
jdstrand
requested changes
Oct 3, 2016
Thanks for the PR. See inline comments on a number of changes that need to be done and reviewed by the security team.
| @@ -44,6 +44,7 @@ var allInterfaces = []interfaces.Interface{ | ||
| &PulseAudioInterface{}, | ||
| &UDisks2Interface{}, | ||
| &FwupdInterface{}, | ||
| + &OfonoInterface{}, |
| + "github.com/snapcore/snapd/release" | ||
| +) | ||
| + | ||
| +var ofonoPermanentSlotAppArmor = []byte(` |
jdstrand
Oct 3, 2016
•
Contributor
Can you use const ofonoPermanentSlotAppArmor = ... instead and convert to []byte when you need a byte slice? I realize not all interfaces do this yet, but it is a requirement for new interfaces.
| +capability net_admin, | ||
| + | ||
| +# To check present devices | ||
| +/run/udev/data/* r, |
jdstrand
Oct 3, 2016
Contributor
Can you limit this to the devices you need based on https://www.kernel.org/doc/Documentation/devices.txt? See interfaces/builtin/opengl.go and browser_support.go for example rules.
| +/sys/bus/usb/devices/ r, | ||
| +# FIXME snapd should be querying udev and adding the /sys and /run/udev accesses | ||
| +# that are assigned to the snap, but we are not there yet. | ||
| +/sys/bus/usb/devices/** r, |
| +/sys/bus/usb/devices/** r, | ||
| + | ||
| +# To get current seat preferences | ||
| +/run/systemd/seats{,/*} r, |
jdstrand
Oct 3, 2016
Contributor
This rule should be: /run/systemd/seats/{,*} r
But why does ofono care about the seat?
alfonsosanchezbeato
Oct 4, 2016
Contributor
Changed. In some cases there are preferences that depend on the user in the seat, like the preferred SIM for voice/data in dual SIM devices.
jdstrand
Oct 4, 2016
Contributor
Ok, can you then say something along the lines of # To get current seat preferences for choosing SIM based on user or similar?
| +# FIXME snapd should be more dynamic to avoid conflicts between snaps trying to | ||
| +# access same ports. | ||
| +/dev/tty[^0-9]* rw, | ||
| +/dev/cdc-* rw, |
jdstrand
Oct 3, 2016
Contributor
Another hrm, but ok. Note that we do have the serial-port interface that makes things better than they used to be, but until the core snap adds implicit serial-port slots and the core snap supports dynamic plugging, I don't think we can do better than this.
morphis
Oct 4, 2016
Contributor
@jdstrand Right, that was our assumption. Once we have better support from snapd for this we will remove those rules and switch to the serial-port interface.
| + | ||
| +network netlink, | ||
| +network netlink raw, | ||
| +network netlink dgram, |
jdstrand
Oct 3, 2016
Contributor
network netlink covers the other two. Can you remove network netlink?
| +dbus (receive, send) | ||
| + bus=system | ||
| + path=/{,**} | ||
| + peer=(label=###SLOT_SECURITY_TAGS###), |
| +# Description: Allow operating as the ofono service. Reserved because this | ||
| +# gives privileged access to the system. | ||
| +# Usage: reserved | ||
| +# TODO: add ioctl argument filters when seccomp arg filtering is implemented |
jdstrand
Oct 3, 2016
Contributor
seccomp argument filter is implemented. What are you wanting to filter on?
jdstrand
Oct 3, 2016
Contributor
Can you also add a comment as to what these are for? dbus? networking? etc?
alfonsosanchezbeato
Oct 4, 2016
Contributor
Comment was a left over, removed. Added a comment to clarify what are the calls for.
| +socket | ||
| +`) | ||
| + | ||
| +var ofonoPermanentSlotDBus = []byte(` |
jdstrand
Oct 3, 2016
Contributor
Can you add an xml comment here on the provenance of this dbus bus policy?
| +</policy> | ||
| + | ||
| +<policy context="default"> | ||
| + <deny send_destination="org.ofono"/> |
jdstrand
Oct 3, 2016
Contributor
Can you also add to the default bus policy:
<deny own="org.ofono"/>
| +## Note that ofono uses this for very few modems and that in most cases it finds | ||
| +## modems by checking directly in code udev events, so changes here will be rare | ||
| + | ||
| +## 97-ofono.rules |
jdstrand
Oct 3, 2016
Contributor
Can you provide a comment as to the provenance of these udev rules?
| +func (iface *OfonoInterface) ConnectedPlugSnippet(plug *interfaces.Plug, slot *interfaces.Slot, securitySystem interfaces.SecuritySystem) ([]byte, error) { | ||
| + switch securitySystem { | ||
| + case interfaces.SecurityDBus: | ||
| + return nil, nil |
| + if release.OnClassic { | ||
| + // If we're running on classic Ofono will be part | ||
| + // of the OS snap and will run unconfined. | ||
| + new = []byte("unconfined") |
jdstrand
Oct 3, 2016
•
Contributor
This assumes that the ofono snap will not be installable on classic. Is that the case?
Also, on classic this means that a connected plug will have this policy:
# Allow all access to ofono services
dbus (receive, send)
bus=system
path=/{,**}
peer=(label=unconfined),
This is a security hole because this rule grants access to all dbus system services, which is wrong. I suggest instead you:
- add an
interfacecomponent to the plugs rule - create a separate variable to hold classic policy and
if release.OnClassicappend it to the policy
If you do this, you fix the security hole and make the interface work on all snaps, classic and classic with ofono snap.
alfonsosanchezbeato
Oct 4, 2016
Contributor
Interface added to apparmor rule. But not sure about what to put there as classic policy, as the ofono package has no apparmor rules.
jdstrand
Oct 4, 2016
Contributor
Thank you for fixing the interface omission.
As for classic, look at mpris.go. Notice how it creates both mprisConnectedSlotAppArmor and mprisConnectedSlotAppArmorClassic. Then look in PermanentSlotSnippet how it creates snippet and then appends to snippet when on classic. I suggest you:
- keep ofonoConnectedPlugAppArmor as is
- add ofonoConnectedPlugAppArmorClassic with:
# Allow all access to ofono services on classic
dbus (receive, send)
bus=system
path=/{,**}
interface=org.ofono.*
peer=(label=unconfined),
- adjust ConnectedPlugSnippet to do:
old := []byte("###SLOT_SECURITY_TAGS###")
new := []byte("")
snippet := bytes.Replace([]byte(ofonoConnectedPlugAppArmor), old, new, -1)
if release.OnClassic {
snippet = append(snippet, ofonoConnectedPlugAppArmorClassic...)
}
return snippet, nil
| + snippet, err := s.iface.ConnectedPlugSnippet(s.plug, slot, interfaces.SecurityAppArmor) | ||
| + c.Assert(err, IsNil) | ||
| + c.Assert(string(snippet), testutil.Contains, "peer=(label=unconfined),") | ||
| +} |
jdstrand
Oct 3, 2016
Contributor
Please add tests for:
- seccomp on plugs side
- seccomp on slots side
- apparmor on the slots side
alfonsosanchezbeato
Oct 4, 2016
Contributor
That is what TestUsedSecuritySystems() was doing, so I it is not clear to me how tests should be. Which ones could I use as reference?
jdstrand
Oct 4, 2016
Contributor
I mean for each of these, add a test that when connected you verify a rule is present. Eg, add TestConnectedPlugSnippetAppArmor():
plug := &interfaces.Plug{}
release.OnClassic = false
snippet, err := s.iface.ConnectedPlugSnippet(s.plug, slot, interfaces.SecurityAppArmor)
c.Assert(err, IsNil)
// verify apparmor connected
c.Assert(string(snippet), testutil.Contains, "#include <abstractions/dbus-strict>")
// verify classic didn't connect
c.Assert(string(snippet), Not(testutil.Contains), "peer=(label=unconfined),")
Now do the same for the others.
| + snippet, err = s.iface.PermanentSlotSnippet(s.slot, interfaces.SecurityUDev) | ||
| + c.Assert(err, IsNil) | ||
| + c.Assert(snippet, Not(IsNil)) | ||
| +} |
jdstrand
Oct 3, 2016
Contributor
You can drop TestUsedSecuritySystems() - they aren't required any more.
| @@ -70,6 +70,7 @@ var implicitClassicSlots = []string{ | ||
| "unity7", | ||
| "upower-observe", | ||
| "x11", | ||
| + "ofono", |
|
PR refreshed for another round |
jdstrand
requested changes
Oct 4, 2016
Thanks for the updates, this is close. Please see my inline comments.
| +/run/udev/data/+usb:* r, | ||
| +/run/udev/data/+pci:* r, | ||
| +/run/udev/data/c* r, | ||
| +/run/udev/data/n* r, |
jdstrand
Oct 4, 2016
Contributor
These aren't quite as fine-grained as I was hoping. It seems from the udev rules that only a handful of devices are known by ofono-- is that true? If so, can you determine the more fine-grained access? If you need help, simply remove these, run in devmode and paste the /run/udev/data denials and we can get more fine-grained.
alfonsosanchezbeato
Oct 5, 2016
Contributor
No, ofono knows many more devices (see comment in ofonoPermanentSlotUdev). It uses libudev to get currently connected devices and processes udev events when devices are hot-plugged. When doing this libudev accesses to files in /run/udev/data, I have seen it accessing:
c2:*
c3:*
c188:*
c189:*
n*
+usb:*
+pci:*
An option to narrow this down is to modify ofono udev filter so it is more strict. Another one is to ignore some of the denials as some do not affect apparently the outcome. For instance, of the previous ones only reading char devices 188 and 189 seems critical (those are for /dev/ttyUSB* devices). However, I am a bit concerned about being too restrictive, as modem vendors might use non standard devices with unexpected major numbers.
jdstrand
Oct 7, 2016
Contributor
Since this is the ofono service, it is less of a concern to let it have access to /run/udev/data. If you you feel the current accesses are sufficient, +1. It will be nice when we have the dynamic resolution code in place.
alfonsosanchezbeato
Oct 7, 2016
Contributor
Great, then I think we can leave this in its current shape until that happens.
| + if release.OnClassic { | ||
| + // If we're running on classic Ofono will be part | ||
| + // of the OS snap and will run unconfined. | ||
| + new = []byte("unconfined") |
jdstrand
Oct 3, 2016
•
Contributor
This assumes that the ofono snap will not be installable on classic. Is that the case?
Also, on classic this means that a connected plug will have this policy:
# Allow all access to ofono services
dbus (receive, send)
bus=system
path=/{,**}
peer=(label=unconfined),
This is a security hole because this rule grants access to all dbus system services, which is wrong. I suggest instead you:
- add an
interfacecomponent to the plugs rule - create a separate variable to hold classic policy and
if release.OnClassicappend it to the policy
If you do this, you fix the security hole and make the interface work on all snaps, classic and classic with ofono snap.
alfonsosanchezbeato
Oct 4, 2016
Contributor
Interface added to apparmor rule. But not sure about what to put there as classic policy, as the ofono package has no apparmor rules.
jdstrand
Oct 4, 2016
Contributor
Thank you for fixing the interface omission.
As for classic, look at mpris.go. Notice how it creates both mprisConnectedSlotAppArmor and mprisConnectedSlotAppArmorClassic. Then look in PermanentSlotSnippet how it creates snippet and then appends to snippet when on classic. I suggest you:
- keep ofonoConnectedPlugAppArmor as is
- add ofonoConnectedPlugAppArmorClassic with:
# Allow all access to ofono services on classic
dbus (receive, send)
bus=system
path=/{,**}
interface=org.ofono.*
peer=(label=unconfined),
- adjust ConnectedPlugSnippet to do:
old := []byte("###SLOT_SECURITY_TAGS###")
new := []byte("")
snippet := bytes.Replace([]byte(ofonoConnectedPlugAppArmor), old, new, -1)
if release.OnClassic {
snippet = append(snippet, ofonoConnectedPlugAppArmorClassic...)
}
return snippet, nil
| + c.Assert(string(snippet), testutil.Contains, `peer=(label="snap.ofono.app"),`) | ||
| +} | ||
| + | ||
| +func (s *OfonoInterfaceSuite) TestConnectedPlugSnippedUsesUnconfinedLabelOnClassic(c *C) { |
| + slot := &interfaces.Slot{} | ||
| + release.OnClassic = true | ||
| + snippet, err := s.iface.ConnectedPlugSnippet(s.plug, slot, interfaces.SecurityAppArmor) | ||
| + c.Assert(err, IsNil) |
jdstrand
Oct 4, 2016
Contributor
Please also test for c.Assert(string(snippet), testutil.Contains, "#include <abstractions/dbus-strict>") here.
| + snippet, err := s.iface.ConnectedPlugSnippet(s.plug, slot, interfaces.SecurityAppArmor) | ||
| + c.Assert(err, IsNil) | ||
| + c.Assert(string(snippet), testutil.Contains, "peer=(label=unconfined),") | ||
| +} |
jdstrand
Oct 3, 2016
Contributor
Please add tests for:
- seccomp on plugs side
- seccomp on slots side
- apparmor on the slots side
alfonsosanchezbeato
Oct 4, 2016
Contributor
That is what TestUsedSecuritySystems() was doing, so I it is not clear to me how tests should be. Which ones could I use as reference?
jdstrand
Oct 4, 2016
Contributor
I mean for each of these, add a test that when connected you verify a rule is present. Eg, add TestConnectedPlugSnippetAppArmor():
plug := &interfaces.Plug{}
release.OnClassic = false
snippet, err := s.iface.ConnectedPlugSnippet(s.plug, slot, interfaces.SecurityAppArmor)
c.Assert(err, IsNil)
// verify apparmor connected
c.Assert(string(snippet), testutil.Contains, "#include <abstractions/dbus-strict>")
// verify classic didn't connect
c.Assert(string(snippet), Not(testutil.Contains), "peer=(label=unconfined),")
Now do the same for the others.
|
Thanks for the review. PR refreshed addressing issues. Only remaining point should be permissions for /run/udev/data/ (see comment on that). |
|
Branch refreshed adding permissions for unconfined apps in permanent slot. |
jdstrand
requested changes
Oct 7, 2016
Thanks for the changes. A few more things remain. Please see inline comments.
| +dbus (receive, send) | ||
| + bus=system | ||
| + path=/{,**} | ||
| + interface=org.ofono.*, |
jdstrand
Oct 7, 2016
•
Contributor
This will allow everything to connect to ofono. Please use this instead:
# Allow traffic to/from our path and interface with any method for unconfined
# clients to talk to our ofono services.
dbus (receive, send)
bus=system
path=/{,**}
interface=org.ofono.*
peer=(label=unconfined),
| +` | ||
| + | ||
| +const ofonoConnectedPlugAppArmorClassic = ` | ||
| +# Allow all access to ofono services on classic |
jdstrand
Oct 7, 2016
Contributor
Please adjust this comment to be:
# Allow access to the unconfined ofono services on classic.
| +setsockopt | ||
| +shutdown | ||
| +socketpair | ||
| +socket |
jdstrand
Oct 7, 2016
Contributor
Please remove: connect, getpeername, getsockname, setsockopt, socketpair and socket. They are all included in the default template.
| +send | ||
| +sendto | ||
| +sendmsg | ||
| +socket |
jdstrand
Oct 7, 2016
Contributor
Please remove: connect, getsockname and socket. They are all included in the default template.
| + c.Assert(err, IsNil) | ||
| + c.Assert(snippet, Not(IsNil)) | ||
| + | ||
| + c.Check(string(snippet), testutil.Contains, "getsockname\n") |
jdstrand
Oct 7, 2016
Contributor
Please use this instead: c.Check(string(snippet), testutil.Contains, "send\n") (getsockname is already in the default template).
| + snippet, err = s.iface.PermanentSlotSnippet(s.slot, interfaces.SecurityUDev) | ||
| + c.Assert(err, IsNil) | ||
| + c.Assert(snippet, Not(IsNil)) | ||
| +} |
jdstrand
Oct 3, 2016
Contributor
You can drop TestUsedSecuritySystems() - they aren't required any more.
| + c.Assert(err, IsNil) | ||
| + c.Assert(snippet, Not(IsNil)) | ||
| + | ||
| + c.Check(string(snippet), testutil.Contains, "interface=org.ofono.*\n") |
jdstrand
Oct 7, 2016
•
Contributor
Please use this instead: c.Check(string(snippet), testutil.Contains, "peer=(label=snap (interface=org.ofono.* is in PermanentSlotAppArmor).
| + c.Assert(err, IsNil) | ||
| + c.Assert(snippet, Not(IsNil)) | ||
| + | ||
| + c.Check(string(snippet), testutil.Contains, "setsockopt\n") |
jdstrand
Oct 7, 2016
Contributor
Please add: c.Check(string(snippet), testutil.Contains, "listen\n") (setsockopt is in default policy.
|
Thanks for the review. PR refreshed after addressing comments. |
| +# FIXME snapd should be more dynamic to avoid conflicts between snaps trying to | ||
| +# access same ports. | ||
| +/dev/tty[^0-9]* rw, | ||
| +/dev/cdc-* rw, |
tonyespy
Oct 10, 2016
Contributor
A quick scan of the code also shows /dev/dsp being used by tools/huawei-audio, and /dev/chnlat11 being used by unit/test-caif.c. Might be worth adding these now.
alfonsosanchezbeato
Oct 11, 2016
Contributor
Good point. Added, plus a couple of additional ones I found.
jdstrand
reviewed
Oct 14, 2016
+1 regarding security policy provided you fix the comment I mentioned needing fixing just now.
| + new := slotAppLabelExpr(slot) | ||
| + snippet := bytes.Replace([]byte(ofonoConnectedPlugAppArmor), old, new, -1) | ||
| + if release.OnClassic { | ||
| + // Let unconfined apps access ofono on classic |
jdstrand
Oct 14, 2016
Contributor
This comment is wrong. It should be // Let confined apps access unconfined ofono on classic
| @@ -327,6 +327,8 @@ slots: | ||
| slot-snap-type: | ||
| - core | ||
| deny-auto-connection: true | ||
| + ofono: | ||
| + deny-auto-connection: true |
jdstrand
Nov 9, 2016
Contributor
Now that the base declaration is more finalized (also see #2269), please adjust this to be:
ofono:
allow-installation:
slot-snap-type:
- app
- core
deny-auto-connection: true
deny-connection:
on-classic: false
This says that this may be supplied as an app snap or via core (ie, on classic) and if supplied via an app snap, then a snap declaration is required to connect to the the slot implementation.
| @@ -353,6 +353,7 @@ var ( | ||
| "location-control": unconstrained, | ||
| "location-observe": unconstrained, | ||
| "modem-manager": unconstrained, | ||
| + "ofono": unconstrained, |
jdstrand
Nov 9, 2016
Contributor
Please move this down below and use:
"ofono": []string{"app", "core"}
| +const ofonoPermanentSlotAppArmor = ` | ||
| +# Description: Allow operating as the ofono service. Reserved because this | ||
| +# gives privileged access to the system. | ||
| +# Usage: reserved |
jdstrand
Nov 9, 2016
Contributor
You can drop 'Usage' now that we have the base declaration. Also, please adjust the whitespace before 'gives'.
| +# Usage: reserved | ||
| + | ||
| +# ofono puts ppp on top of the tun device | ||
| +/dev/net/tun rw, |
jdstrand
Nov 9, 2016
Contributor
For readability, can you move this rule and comment down below with the other /dev rules?
| + | ||
| +<policy at_console="true"> | ||
| + <allow send_destination="org.ofono"/> | ||
| +</policy> |
jdstrand
Nov 9, 2016
Contributor
It looks like 'at_console' is deprecated: https://lists.fedoraproject.org/pipermail/devel/2014-May/199004.html. Considering the lack of policykit on Ubuntu Core it seems like removing it and relying on the root and default policy would be wise. Can you comment on why 'at_console' is here?
alfonsosanchezbeato
Nov 10, 2016
Contributor
I copied this over from ofono default policy file. Removed this in the refreshed branch, as it is not really needed and does not make much sense anyway.
| + if release.OnClassic { | ||
| + // Let confined apps access unconfined ofono on classic | ||
| + snippet = append(snippet, ofonoConnectedPlugAppArmorClassic...) | ||
| + } |
jdstrand
Nov 9, 2016
Contributor
I've rethought this section since previous reviews. With the proposed implementation, classic systems with connected plugs get to talk to unconfined ofono as well as the security label of the ofono snap, but this snap won't exist on a classic system where the deb would be used instead. Can you confirm this?
If that is true, instead of using ofonoConnectedPlugAppArmorClassic, instead conditionally set new to unconfined when OnClassic and to slotAppLabelExpr(slot) when not and drop ofonoConnectedPlugAppArmorClassic altogether.
alfonsosanchezbeato
Nov 10, 2016
Contributor
Well, I can see both things happening (ofono client using the snap or using the deb in a classic system). Maybe we should not be too restrictive and allow both things, if not an issue security-wise. Same should apply to the modem-manager interface changes: I have changed it back to the way you mention here, but maybe I should leave the change I just made. We can discuss on irc.
jdstrand
Nov 10, 2016
Contributor
Re "ofono client using the snap or using the deb in a classic system"> if that is the case, then you can leave this as is.
| + snippet, err := s.iface.ConnectedSlotSnippet(s.plug, s.slot, interfaces.SecuritySecComp) | ||
| + c.Assert(err, IsNil) | ||
| + c.Assert(snippet, IsNil) | ||
| +} |
jdstrand
Nov 9, 2016
Contributor
You can drop this test-- you aren't adding seccomp policy for slots.
| + snippet, err := s.iface.PermanentPlugSnippet(s.plug, interfaces.SecurityAppArmor) | ||
| + c.Assert(err, IsNil) | ||
| + c.Assert(snippet, IsNil) | ||
| +} |
| + snippet, err := s.iface.PermanentPlugSnippet(s.plug, interfaces.SecuritySecComp) | ||
| + c.Assert(err, IsNil) | ||
| + c.Assert(snippet, IsNil) | ||
| +} |
|
Branch refreshed after addressing comments. |
|
Thanks for the changes! |
|
@jdstrand, thanks for the review. No more changes on my side, as I have left the classic parts as they are now. |
alfonsosanchezbeato
added some commits
Sep 30, 2016
|
@jdstrand FYI I have added a couple of additional apparmor permissions: +/run/udev/data/+usb-serial:* r, |
|
Those new accesses are fine. |
alfonsosanchezbeato commentedOct 3, 2016
Add interface for ofono telephony daemon.