interfaces/builtin: improve the bluez interface #1078

Merged
merged 19 commits into from Apr 29, 2016
Commits
Jump to file or symbol
Failed to load files and symbols.
+158 −8
Split
@@ -20,6 +20,8 @@
package builtin
import (
+ "bytes"
+
"github.com/ubuntu-core/snappy/interfaces"
)
@@ -64,6 +66,11 @@ var bluezPermanentSlotAppArmor = []byte(`
bus=system
name="org.bluez",
+ # Allow binding the service to the requested connection name
+ dbus (bind)
+ bus=system
+ name="org.bluez.obex",
+
# Allow traffic to/from our path and interface with any method
dbus (receive, send)
bus=system
@@ -91,7 +98,7 @@ var bluezConnectedPlugAppArmor = []byte(`
# Allow all access to bluez service
dbus (receive, send)
bus=system
- peer=(label=bluez5_bluez_*),
+ peer=(label=###SLOT_SECURITY_TAGS###),
dbus (send)
bus=system
@@ -157,7 +164,7 @@ sendmsg
socket
`)
-var bluezConnectedPlugDBus = []byte(`
+var bluezPermanentSlotDBus = []byte(`
<policy user="root">
<allow own="org.bluez"/>
<allow own="org.bluez.obex"/>
@@ -196,13 +203,14 @@ func (iface *BluezInterface) PermanentPlugSnippet(plug *interfaces.Plug, securit
func (iface *BluezInterface) ConnectedPlugSnippet(plug *interfaces.Plug, slot *interfaces.Slot, securitySystem interfaces.SecuritySystem) ([]byte, error) {
switch securitySystem {
- case interfaces.SecurityDBus:
- return bluezConnectedPlugDBus, nil
case interfaces.SecurityAppArmor:
- return bluezConnectedPlugAppArmor, nil
+ old := []byte("###SLOT_SECURITY_TAGS###")
+ new := slotAppLabelExpr(slot)
+ snippet := bytes.Replace(bluezConnectedPlugAppArmor, old, new, -1)
+ return snippet, nil
case interfaces.SecuritySecComp:
return bluezConnectedPlugSecComp, nil
- case interfaces.SecurityUDev:
+ case interfaces.SecurityUDev, interfaces.SecurityDBus:
return nil, nil
default:
return nil, interfaces.ErrUnknownSecurity
@@ -215,7 +223,9 @@ func (iface *BluezInterface) PermanentSlotSnippet(slot *interfaces.Slot, securit
return bluezPermanentSlotAppArmor, nil
case interfaces.SecuritySecComp:
return bluezPermanentSlotSecComp, nil
- case interfaces.SecurityUDev, interfaces.SecurityDBus:
+ case interfaces.SecurityDBus:
+ return bluezPermanentSlotDBus, nil
+ case interfaces.SecurityUDev:
return nil, nil
default:
return nil, interfaces.ErrUnknownSecurity
@@ -24,6 +24,8 @@ import (
"github.com/ubuntu-core/snappy/interfaces"
"github.com/ubuntu-core/snappy/interfaces/builtin"
+ "github.com/ubuntu-core/snappy/snap"
+ "github.com/ubuntu-core/snappy/testutil"
)
type BluezInterfaceSuite struct {
@@ -34,12 +36,86 @@ type BluezInterfaceSuite struct {
var _ = Suite(&BluezInterfaceSuite{
iface: &builtin.BluezInterface{},
+ slot: &interfaces.Slot{
+ SlotInfo: &snap.SlotInfo{
+ Snap: &snap.Info{SuggestedName: "bluez"},
+ Name: "bluez",
+ Interface: "bluez",
+ },
+ },
+ plug: &interfaces.Plug{
+ PlugInfo: &snap.PlugInfo{
+ Snap: &snap.Info{SuggestedName: "bluez"},
+ Name: "bluezctl",
+ Interface: "bluez",
+ },
+ },
})
func (s *BluezInterfaceSuite) TestName(c *C) {
c.Assert(s.iface.Name(), Equals, "bluez")
}
+// The label glob when all apps are bound to the bluez slot
+func (s *BluezInterfaceSuite) TestConnectedPlugSnippetUsesSlotLabelAll(c *C) {
+ app1 := &snap.AppInfo{Name: "app1"}
+ app2 := &snap.AppInfo{Name: "app2"}
+ slot := &interfaces.Slot{
+ SlotInfo: &snap.SlotInfo{
+ Snap: &snap.Info{
+ SuggestedName: "bluez",
+ Apps: map[string]*snap.AppInfo{"app1": app1, "app2": app2},
+ },
+ Name: "bluez",
+ Interface: "bluez",
+ Apps: map[string]*snap.AppInfo{"app1": app1, "app2": app2},
+ },
+ }
+ snippet, err := s.iface.ConnectedPlugSnippet(s.plug, slot, interfaces.SecurityAppArmor)
+ c.Assert(err, IsNil)
+ c.Assert(string(snippet), testutil.Contains, "peer=(label=snap.bluez.*),")
+}
+
+// The label uses alternation when some, but not all, apps is bound to the bluez slot
+func (s *BluezInterfaceSuite) TestConnectedPlugSnippetUsesSlotLabelSome(c *C) {
+ app1 := &snap.AppInfo{Name: "app1"}
+ app2 := &snap.AppInfo{Name: "app2"}
+ app3 := &snap.AppInfo{Name: "app3"}
+ slot := &interfaces.Slot{
+ SlotInfo: &snap.SlotInfo{
+ Snap: &snap.Info{
+ SuggestedName: "bluez",
+ Apps: map[string]*snap.AppInfo{"app1": app1, "app2": app2, "app3": app3},
+ },
+ Name: "bluez",
+ Interface: "bluez",
+ Apps: map[string]*snap.AppInfo{"app1": app1, "app2": app2},
+ },
+ }
+ snippet, err := s.iface.ConnectedPlugSnippet(s.plug, slot, interfaces.SecurityAppArmor)
+ c.Assert(err, IsNil)
+ c.Assert(string(snippet), testutil.Contains, "peer=(label=snap.bluez.{app1,app2}),")
+}
+
+// The label uses short form when exactly one app is bound to the bluez slot
+func (s *BluezInterfaceSuite) TestConnectedPlugSnippetUsesSlotLabelOne(c *C) {
+ app := &snap.AppInfo{Name: "app"}
+ slot := &interfaces.Slot{
+ SlotInfo: &snap.SlotInfo{
+ Snap: &snap.Info{
+ SuggestedName: "bluez",
+ Apps: map[string]*snap.AppInfo{"app": app},
+ },
+ Name: "bluez",
+ Interface: "bluez",
+ Apps: map[string]*snap.AppInfo{"app": app},
+ },
+ }
+ snippet, err := s.iface.ConnectedPlugSnippet(s.plug, slot, interfaces.SecurityAppArmor)
+ c.Assert(err, IsNil)
+ c.Assert(string(snippet), testutil.Contains, "peer=(label=snap.bluez.app),")
+}
+
func (s *BluezInterfaceSuite) TestUnusedSecuritySystems(c *C) {
systems := [...]interfaces.SecuritySystem{interfaces.SecurityAppArmor,
interfaces.SecuritySecComp, interfaces.SecurityDBus,
@@ -58,7 +134,7 @@ func (s *BluezInterfaceSuite) TestUnusedSecuritySystems(c *C) {
snippet, err = s.iface.PermanentSlotSnippet(s.slot, interfaces.SecurityUDev)
c.Assert(err, IsNil)
c.Assert(snippet, IsNil)
- snippet, err = s.iface.PermanentSlotSnippet(s.slot, interfaces.SecurityDBus)
+ snippet, err = s.iface.ConnectedPlugSnippet(s.plug, s.slot, interfaces.SecurityDBus)
c.Assert(err, IsNil)
c.Assert(snippet, IsNil)
}
@@ -76,6 +152,9 @@ func (s *BluezInterfaceSuite) TestUsedSecuritySystems(c *C) {
}
snippet, err := s.iface.ConnectedPlugSnippet(s.plug, s.slot, interfaces.SecurityDBus)
c.Assert(err, IsNil)
+ c.Assert(snippet, IsNil)
+ snippet, err = s.iface.PermanentSlotSnippet(s.slot, interfaces.SecurityDBus)
+ c.Assert(err, IsNil)
c.Assert(snippet, Not(IsNil))
}
@@ -0,0 +1,61 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2016 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package builtin
+
+import (
+ "bytes"
+ "fmt"
+ "sort"
+
+ "github.com/ubuntu-core/snappy/interfaces"
+)
+
+// slotAppLabelExpr returns the specification of the apparmor label describing
+// all the apps bound to a given slot. The result has one of three forms,
+// depending on how apps are bound to the slot:
+//
+// - "snap.$snap.$app" if there is exactly one app bound
+// - "snap.$snap.{$app1,...$appN}" if there are some, but not all, apps bound
+// - "snap.$snap.*" if all apps are bound to the slot
+func slotAppLabelExpr(slot *interfaces.Slot) []byte {
@niemeyer

niemeyer Apr 29, 2016

Contributor

Nice!!

+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "snap.%s.", slot.Snap.Name())
+ if len(slot.Apps) == 1 {
+ for appName := range slot.Apps {
+ buf.WriteString(appName)
+ }
+ } else if len(slot.Apps) == len(slot.Snap.Apps) {
+ buf.WriteByte('*')
+ } else {
+ appNames := make([]string, 0, len(slot.Apps))
+ for appName := range slot.Apps {
+ appNames = append(appNames, appName)
+ }
+ sort.Strings(appNames)
+ buf.WriteByte('{')
+ for _, appName := range appNames {
+ buf.WriteString(appName)
+ buf.WriteByte(',')
+ }
+ buf.Truncate(buf.Len() - 1)
+ buf.WriteByte('}')
+ }
+ return buf.Bytes()
+}