github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/interfaces/builtin/fwupd_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package builtin_test 21 22 import ( 23 "os" 24 "path/filepath" 25 26 . "gopkg.in/check.v1" 27 28 "github.com/snapcore/snapd/dirs" 29 "github.com/snapcore/snapd/interfaces" 30 "github.com/snapcore/snapd/interfaces/apparmor" 31 "github.com/snapcore/snapd/interfaces/builtin" 32 "github.com/snapcore/snapd/interfaces/dbus" 33 "github.com/snapcore/snapd/interfaces/mount" 34 "github.com/snapcore/snapd/interfaces/seccomp" 35 "github.com/snapcore/snapd/snap" 36 "github.com/snapcore/snapd/testutil" 37 ) 38 39 type FwupdInterfaceSuite struct { 40 iface interfaces.Interface 41 appSlotInfo *snap.SlotInfo 42 appSlot *interfaces.ConnectedSlot 43 coreSlotInfo *snap.SlotInfo 44 coreSlot *interfaces.ConnectedSlot 45 plugInfo *snap.PlugInfo 46 plug *interfaces.ConnectedPlug 47 } 48 49 const mockPlugSnapInfoYaml = `name: uefi-fw-tools 50 version: 1.0 51 apps: 52 app: 53 command: foo 54 plugs: [fwupd] 55 ` 56 57 const mockAppSlotSnapInfoYaml = `name: uefi-fw-tools 58 version: 1.0 59 apps: 60 app2: 61 command: foo 62 slots: [fwupd] 63 ` 64 65 const mockCoreSlotSnapInfoYaml = `name: core 66 type: os 67 version: 1.0 68 slots: 69 fwupd: 70 ` 71 72 var _ = Suite(&FwupdInterfaceSuite{ 73 iface: builtin.MustInterface("fwupd"), 74 }) 75 76 func (s *FwupdInterfaceSuite) SetUpTest(c *C) { 77 s.plug, s.plugInfo = MockConnectedPlug(c, mockPlugSnapInfoYaml, nil, "fwupd") 78 s.appSlot, s.appSlotInfo = MockConnectedSlot(c, mockAppSlotSnapInfoYaml, nil, "fwupd") 79 s.coreSlot, s.coreSlotInfo = MockConnectedSlot(c, mockCoreSlotSnapInfoYaml, nil, "fwupd") 80 } 81 82 func (s *FwupdInterfaceSuite) TestName(c *C) { 83 c.Assert(s.iface.Name(), Equals, "fwupd") 84 } 85 86 // The label glob when all apps are bound to the fwupd slot 87 func (s *FwupdInterfaceSuite) TestConnectedPlugSnippetUsesSlotLabelAll(c *C) { 88 app1 := &snap.AppInfo{Name: "app1"} 89 app2 := &snap.AppInfo{Name: "app2"} 90 slot := &snap.SlotInfo{ 91 Snap: &snap.Info{ 92 SuggestedName: "uefi-fw-tools", 93 Apps: map[string]*snap.AppInfo{"app1": app1, "app2": app2}, 94 }, 95 Name: "fwupd", 96 Interface: "fwupd", 97 Apps: map[string]*snap.AppInfo{"app1": app1, "app2": app2}, 98 } 99 100 // connected plugs have a non-nil security snippet for apparmor 101 apparmorSpec := &apparmor.Specification{} 102 err := apparmorSpec.AddConnectedPlug(s.iface, s.plug, interfaces.NewConnectedSlot(slot, nil, nil)) 103 c.Assert(err, IsNil) 104 c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.uefi-fw-tools.app"}) 105 c.Assert(apparmorSpec.SnippetForTag("snap.uefi-fw-tools.app"), testutil.Contains, `peer=(label="snap.uefi-fw-tools.*"),`) 106 } 107 108 // The label uses alternation when some, but not all, apps is bound to the fwupd slot 109 func (s *FwupdInterfaceSuite) TestConnectedPlugSnippetUsesSlotLabelSome(c *C) { 110 app1 := &snap.AppInfo{Name: "app1"} 111 app2 := &snap.AppInfo{Name: "app2"} 112 app3 := &snap.AppInfo{Name: "app3"} 113 slot := &snap.SlotInfo{ 114 Snap: &snap.Info{ 115 SuggestedName: "uefi-fw-tools", 116 Apps: map[string]*snap.AppInfo{"app1": app1, "app2": app2, "app3": app3}, 117 }, 118 Name: "fwupd", 119 Interface: "fwupd", 120 Apps: map[string]*snap.AppInfo{"app1": app1, "app2": app2}, 121 } 122 123 apparmorSpec := &apparmor.Specification{} 124 err := apparmorSpec.AddConnectedPlug(s.iface, s.plug, interfaces.NewConnectedSlot(slot, nil, nil)) 125 c.Assert(err, IsNil) 126 c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.uefi-fw-tools.app"}) 127 c.Assert(apparmorSpec.SnippetForTag("snap.uefi-fw-tools.app"), testutil.Contains, `peer=(label="snap.uefi-fw-tools.{app1,app2}"),`) 128 } 129 130 // The label uses short form when exactly one app is bound to the fwupd slot 131 func (s *FwupdInterfaceSuite) TestConnectedPlugSnippetUsesSlotLabelOne(c *C) { 132 apparmorSpec := &apparmor.Specification{} 133 err := apparmorSpec.AddConnectedPlug(s.iface, s.plug, s.appSlot) 134 c.Assert(err, IsNil) 135 c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.uefi-fw-tools.app"}) 136 c.Assert(apparmorSpec.SnippetForTag("snap.uefi-fw-tools.app"), testutil.Contains, `peer=(label="snap.uefi-fw-tools.app2"),`) 137 } 138 139 func (s *FwupdInterfaceSuite) TestConnectedPlugSnippetToImplicitSlot(c *C) { 140 apparmorSpec := &apparmor.Specification{} 141 err := apparmorSpec.AddConnectedPlug(s.iface, s.plug, s.coreSlot) 142 c.Assert(err, IsNil) 143 c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.uefi-fw-tools.app"}) 144 c.Assert(apparmorSpec.SnippetForTag("snap.uefi-fw-tools.app"), testutil.Contains, `peer=(label=unconfined),`) 145 } 146 147 func (s *FwupdInterfaceSuite) TestUsedSecuritySystems(c *C) { 148 // connected plugs have a non-nil security snippet for apparmor 149 apparmorSpec := &apparmor.Specification{} 150 err := apparmorSpec.AddConnectedPlug(s.iface, s.plug, s.appSlot) 151 c.Assert(err, IsNil) 152 err = apparmorSpec.AddConnectedSlot(s.iface, s.plug, s.appSlot) 153 c.Assert(err, IsNil) 154 err = apparmorSpec.AddPermanentSlot(s.iface, s.appSlotInfo) 155 c.Assert(err, IsNil) 156 c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.uefi-fw-tools.app", "snap.uefi-fw-tools.app2"}) 157 158 dbusSpec := &dbus.Specification{} 159 err = dbusSpec.AddPermanentSlot(s.iface, s.appSlotInfo) 160 c.Assert(err, IsNil) 161 c.Assert(dbusSpec.SecurityTags(), HasLen, 1) 162 163 // UpdateNS rules are only applied if the permanent slot is provided by 164 // an app snap 165 updateNS := apparmorSpec.UpdateNS() 166 c.Check(updateNS, testutil.Contains, " # Read-write access to /boot\n") 167 168 // When connecting to the implicit slot on Classic systems, we 169 // don't generate slot-side AppArmor rules. 170 apparmorSpec = &apparmor.Specification{} 171 err = apparmorSpec.AddConnectedPlug(s.iface, s.plug, s.coreSlot) 172 c.Assert(err, IsNil) 173 c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.uefi-fw-tools.app"}) 174 apparmorSpec = &apparmor.Specification{} 175 err = apparmorSpec.AddConnectedSlot(s.iface, s.plug, s.coreSlot) 176 c.Assert(err, IsNil) 177 err = apparmorSpec.AddPermanentSlot(s.iface, s.coreSlotInfo) 178 c.Assert(err, IsNil) 179 c.Assert(apparmorSpec.SecurityTags(), HasLen, 0) 180 181 // The same is true for D-Bus rules 182 dbusSpec = &dbus.Specification{} 183 err = dbusSpec.AddPermanentSlot(s.iface, s.coreSlotInfo) 184 c.Assert(err, IsNil) 185 c.Assert(dbusSpec.SecurityTags(), HasLen, 0) 186 } 187 188 func (s *FwupdInterfaceSuite) TestMountPermanentSlot(c *C) { 189 tmpdir := c.MkDir() 190 dirs.SetRootDir(tmpdir) 191 192 c.Assert(os.MkdirAll(filepath.Join(tmpdir, "/boot"), 0777), IsNil) 193 194 // If the permanent slot is provided by an app snap, the boot partition 195 // should be bind mounted from the host system if they exist. 196 mountSpec := &mount.Specification{} 197 c.Assert(mountSpec.AddPermanentSlot(s.iface, s.appSlotInfo), IsNil) 198 199 entries := mountSpec.MountEntries() 200 c.Assert(entries, HasLen, 1) 201 202 const hostfs = "/var/lib/snapd/hostfs" 203 c.Check(entries[0].Name, Equals, filepath.Join(hostfs, dirs.GlobalRootDir, "/boot")) 204 c.Check(entries[0].Dir, Equals, "/boot") 205 c.Check(entries[0].Options, DeepEquals, []string{"rbind", "rw"}) 206 } 207 208 func (s *FwupdInterfaceSuite) TestPermanentSlotSnippetSecComp(c *C) { 209 seccompSpec := &seccomp.Specification{} 210 err := seccompSpec.AddPermanentSlot(s.iface, s.appSlotInfo) 211 c.Assert(err, IsNil) 212 c.Assert(seccompSpec.SecurityTags(), DeepEquals, []string{"snap.uefi-fw-tools.app2"}) 213 c.Check(seccompSpec.SnippetForTag("snap.uefi-fw-tools.app2"), testutil.Contains, "bind\n") 214 215 // On classic systems, fwupd is an implicit slot 216 seccompSpec = &seccomp.Specification{} 217 err = seccompSpec.AddPermanentSlot(s.iface, s.coreSlotInfo) 218 c.Assert(err, IsNil) 219 c.Assert(seccompSpec.SecurityTags(), HasLen, 0) 220 } 221 222 func (s *FwupdInterfaceSuite) TestPermanentSlotDBus(c *C) { 223 dbusSpec := &dbus.Specification{} 224 err := dbusSpec.AddPermanentSlot(s.iface, s.appSlotInfo) 225 c.Assert(err, IsNil) 226 c.Assert(dbusSpec.SecurityTags(), DeepEquals, []string{"snap.uefi-fw-tools.app2"}) 227 c.Assert(dbusSpec.SnippetForTag("snap.uefi-fw-tools.app2"), testutil.Contains, `<allow own="org.freedesktop.fwupd"/>`) 228 229 // The implicit slot found on classic systems does not generate any rules 230 dbusSpec = &dbus.Specification{} 231 err = dbusSpec.AddPermanentSlot(s.iface, s.coreSlotInfo) 232 c.Assert(err, IsNil) 233 c.Assert(dbusSpec.SecurityTags(), HasLen, 0) 234 } 235 236 func (s *FwupdInterfaceSuite) TestConnectedPlugSnippetSecComp(c *C) { 237 seccompSpec := &seccomp.Specification{} 238 err := seccompSpec.AddConnectedPlug(s.iface, s.plug, s.appSlot) 239 c.Assert(err, IsNil) 240 c.Assert(seccompSpec.SecurityTags(), DeepEquals, []string{"snap.uefi-fw-tools.app"}) 241 c.Check(seccompSpec.SnippetForTag("snap.uefi-fw-tools.app"), testutil.Contains, "bind\n") 242 } 243 244 func (s *FwupdInterfaceSuite) TestInterfaces(c *C) { 245 c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface) 246 }