github.com/rigado/snapd@v2.42.5-go-mod+incompatible/interfaces/dbus/backend_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 dbus_test 21 22 import ( 23 "fmt" 24 "io/ioutil" 25 "os" 26 "path/filepath" 27 28 . "gopkg.in/check.v1" 29 30 "github.com/snapcore/snapd/dirs" 31 "github.com/snapcore/snapd/interfaces" 32 "github.com/snapcore/snapd/interfaces/dbus" 33 "github.com/snapcore/snapd/interfaces/ifacetest" 34 "github.com/snapcore/snapd/snap" 35 "github.com/snapcore/snapd/snap/snaptest" 36 "github.com/snapcore/snapd/testutil" 37 ) 38 39 type backendSuite struct { 40 ifacetest.BackendSuite 41 } 42 43 var _ = Suite(&backendSuite{}) 44 45 var testedConfinementOpts = []interfaces.ConfinementOptions{ 46 {}, 47 {DevMode: true}, 48 {JailMode: true}, 49 {Classic: true}, 50 } 51 52 func (s *backendSuite) SetUpTest(c *C) { 53 s.Backend = &dbus.Backend{} 54 s.BackendSuite.SetUpTest(c) 55 c.Assert(s.Repo.AddBackend(s.Backend), IsNil) 56 57 // Prepare a directory for DBus configuration files. 58 // NOTE: Normally this is a part of the OS snap. 59 err := os.MkdirAll(dirs.SnapBusPolicyDir, 0700) 60 c.Assert(err, IsNil) 61 } 62 63 func (s *backendSuite) TearDownTest(c *C) { 64 s.BackendSuite.TearDownTest(c) 65 } 66 67 // Tests for Setup() and Remove() 68 func (s *backendSuite) TestName(c *C) { 69 c.Check(s.Backend.Name(), Equals, interfaces.SecurityDBus) 70 } 71 72 func (s *backendSuite) TestInstallingSnapWritesConfigFiles(c *C) { 73 // NOTE: Hand out a permanent snippet so that .conf file is generated. 74 s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error { 75 spec.AddSnippet("<policy/>") 76 return nil 77 } 78 for _, opts := range testedConfinementOpts { 79 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 80 profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.smbd.conf") 81 // file called "snap.sambda.smbd.conf" was created 82 _, err := os.Stat(profile) 83 c.Check(err, IsNil) 84 s.RemoveSnap(c, snapInfo) 85 } 86 } 87 88 func (s *backendSuite) TestInstallingSnapWithHookWritesConfigFiles(c *C) { 89 // NOTE: Hand out a permanent snippet so that .conf file is generated. 90 s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error { 91 spec.AddSnippet("<policy/>") 92 return nil 93 } 94 s.Iface.DBusPermanentPlugCallback = func(spec *dbus.Specification, plug *snap.PlugInfo) error { 95 spec.AddSnippet("<policy/>") 96 return nil 97 } 98 for _, opts := range testedConfinementOpts { 99 snapInfo := s.InstallSnap(c, opts, "", ifacetest.HookYaml, 0) 100 profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.foo.hook.configure.conf") 101 102 // Verify that "snap.foo.hook.configure.conf" was created 103 _, err := os.Stat(profile) 104 c.Check(err, IsNil) 105 s.RemoveSnap(c, snapInfo) 106 } 107 } 108 109 func (s *backendSuite) TestRemovingSnapRemovesConfigFiles(c *C) { 110 // NOTE: Hand out a permanent snippet so that .conf file is generated. 111 s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error { 112 spec.AddSnippet("<policy/>") 113 return nil 114 } 115 for _, opts := range testedConfinementOpts { 116 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 117 s.RemoveSnap(c, snapInfo) 118 profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.smbd.conf") 119 // file called "snap.sambda.smbd.conf" was removed 120 _, err := os.Stat(profile) 121 c.Check(os.IsNotExist(err), Equals, true) 122 } 123 } 124 125 func (s *backendSuite) TestRemovingSnapWithHookRemovesConfigFiles(c *C) { 126 // NOTE: Hand out a permanent snippet so that .conf file is generated. 127 s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error { 128 spec.AddSnippet("<policy/>") 129 return nil 130 } 131 s.Iface.DBusPermanentPlugCallback = func(spec *dbus.Specification, plug *snap.PlugInfo) error { 132 spec.AddSnippet("<policy/>") 133 return nil 134 } 135 for _, opts := range testedConfinementOpts { 136 snapInfo := s.InstallSnap(c, opts, "", ifacetest.HookYaml, 0) 137 s.RemoveSnap(c, snapInfo) 138 profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.foo.hook.configure.conf") 139 140 // Verify that "snap.foo.hook.configure.conf" was removed 141 _, err := os.Stat(profile) 142 c.Check(os.IsNotExist(err), Equals, true) 143 } 144 } 145 146 func (s *backendSuite) TestUpdatingSnapToOneWithMoreApps(c *C) { 147 // NOTE: Hand out a permanent snippet so that .conf file is generated. 148 s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error { 149 spec.AddSnippet("<policy/>") 150 return nil 151 } 152 for _, opts := range testedConfinementOpts { 153 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 154 snapInfo = s.UpdateSnap(c, snapInfo, opts, ifacetest.SambaYamlV1WithNmbd, 0) 155 profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.nmbd.conf") 156 // file called "snap.sambda.nmbd.conf" was created 157 _, err := os.Stat(profile) 158 c.Check(err, IsNil) 159 s.RemoveSnap(c, snapInfo) 160 } 161 } 162 163 func (s *backendSuite) TestUpdatingSnapToOneWithMoreHooks(c *C) { 164 // NOTE: Hand out a permanent snippet so that .conf file is generated. 165 s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error { 166 spec.AddSnippet("<policy/>") 167 return nil 168 } 169 s.Iface.DBusPermanentPlugCallback = func(spec *dbus.Specification, plug *snap.PlugInfo) error { 170 spec.AddSnippet("<policy/>") 171 return nil 172 } 173 for _, opts := range testedConfinementOpts { 174 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 175 snapInfo = s.UpdateSnap(c, snapInfo, opts, ifacetest.SambaYamlWithHook, 0) 176 profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.hook.configure.conf") 177 178 // Verify that "snap.samba.hook.configure.conf" was created 179 _, err := os.Stat(profile) 180 c.Check(err, IsNil) 181 s.RemoveSnap(c, snapInfo) 182 } 183 } 184 185 func (s *backendSuite) TestUpdatingSnapToOneWithFewerApps(c *C) { 186 // NOTE: Hand out a permanent snippet so that .conf file is generated. 187 s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error { 188 spec.AddSnippet("<policy/>") 189 return nil 190 } 191 for _, opts := range testedConfinementOpts { 192 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1WithNmbd, 0) 193 snapInfo = s.UpdateSnap(c, snapInfo, opts, ifacetest.SambaYamlV1, 0) 194 profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.nmbd.conf") 195 // file called "snap.sambda.nmbd.conf" was removed 196 _, err := os.Stat(profile) 197 c.Check(os.IsNotExist(err), Equals, true) 198 s.RemoveSnap(c, snapInfo) 199 } 200 } 201 202 func (s *backendSuite) TestUpdatingSnapToOneWithFewerHooks(c *C) { 203 // NOTE: Hand out a permanent snippet so that .conf file is generated. 204 s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error { 205 spec.AddSnippet("<policy/>") 206 return nil 207 } 208 s.Iface.DBusPermanentPlugCallback = func(spec *dbus.Specification, plug *snap.PlugInfo) error { 209 spec.AddSnippet("<policy/>") 210 return nil 211 } 212 for _, opts := range testedConfinementOpts { 213 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlWithHook, 0) 214 snapInfo = s.UpdateSnap(c, snapInfo, opts, ifacetest.SambaYamlV1, 0) 215 profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.hook.configure.conf") 216 217 // Verify that "snap.samba.hook.configure.conf" was removed 218 _, err := os.Stat(profile) 219 c.Check(os.IsNotExist(err), Equals, true) 220 s.RemoveSnap(c, snapInfo) 221 } 222 } 223 224 func (s *backendSuite) TestCombineSnippetsWithActualSnippets(c *C) { 225 // NOTE: replace the real template with a shorter variant 226 restore := dbus.MockXMLEnvelope([]byte("<?xml>\n"), []byte("</xml>")) 227 defer restore() 228 s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error { 229 spec.AddSnippet("<policy>...</policy>") 230 return nil 231 } 232 for _, opts := range testedConfinementOpts { 233 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 234 profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.smbd.conf") 235 c.Check(profile, testutil.FileEquals, "<?xml>\n<policy>...</policy>\n</xml>") 236 stat, err := os.Stat(profile) 237 c.Assert(err, IsNil) 238 c.Check(stat.Mode(), Equals, os.FileMode(0644)) 239 s.RemoveSnap(c, snapInfo) 240 } 241 } 242 243 func (s *backendSuite) TestCombineSnippetsWithoutAnySnippets(c *C) { 244 for _, opts := range testedConfinementOpts { 245 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 246 profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.smbd.conf") 247 _, err := os.Stat(profile) 248 // Without any snippets, there the .conf file is not created. 249 c.Check(os.IsNotExist(err), Equals, true) 250 s.RemoveSnap(c, snapInfo) 251 } 252 } 253 254 const sambaYamlWithIfaceBoundToNmbd = ` 255 name: samba 256 version: 1 257 developer: acme 258 apps: 259 smbd: 260 nmbd: 261 slots: [iface] 262 ` 263 264 func (s *backendSuite) TestAppBoundIfaces(c *C) { 265 // NOTE: Hand out a permanent snippet so that .conf file is generated. 266 s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error { 267 spec.AddSnippet("<policy/>") 268 return nil 269 } 270 // Install a snap with two apps, only one of which needs a .conf file 271 // because the interface is app-bound. 272 snapInfo := s.InstallSnap(c, interfaces.ConfinementOptions{}, "", sambaYamlWithIfaceBoundToNmbd, 0) 273 defer s.RemoveSnap(c, snapInfo) 274 // Check that only one of the .conf files is actually created 275 _, err := os.Stat(filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.smbd.conf")) 276 c.Check(os.IsNotExist(err), Equals, true) 277 _, err = os.Stat(filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.nmbd.conf")) 278 c.Check(err, IsNil) 279 } 280 281 func (s *backendSuite) TestSandboxFeatures(c *C) { 282 c.Assert(s.Backend.SandboxFeatures(), DeepEquals, []string{"mediated-bus-access"}) 283 } 284 285 func makeFakeDbusUserdServiceFiles(c *C, coreOrSnapdSnap *snap.Info) { 286 err := os.MkdirAll(filepath.Join(dirs.GlobalRootDir, "/usr/share/dbus-1/services"), 0755) 287 c.Assert(err, IsNil) 288 289 servicesPath := filepath.Join(coreOrSnapdSnap.MountDir(), "/usr/share/dbus-1/services") 290 err = os.MkdirAll(servicesPath, 0755) 291 c.Assert(err, IsNil) 292 293 for _, fn := range []string{ 294 "io.snapcraft.Launcher.service", 295 "io.snapcraft.Settings.service", 296 } { 297 content := fmt.Sprintf("content of %s for snap %s", fn, coreOrSnapdSnap.InstanceName()) 298 err = ioutil.WriteFile(filepath.Join(servicesPath, fn), []byte(content), 0644) 299 c.Assert(err, IsNil) 300 } 301 } 302 303 func (s *backendSuite) testSetupWritesUsedFilesForCoreOrSnapd(c *C, coreOrSnapdYaml string) { 304 coreOrSnapdInfo := snaptest.MockInfo(c, coreOrSnapdYaml, &snap.SideInfo{Revision: snap.R(2)}) 305 makeFakeDbusUserdServiceFiles(c, coreOrSnapdInfo) 306 307 err := s.Backend.Setup(coreOrSnapdInfo, interfaces.ConfinementOptions{}, s.Repo, nil) 308 c.Assert(err, IsNil) 309 310 for _, fn := range []string{ 311 "io.snapcraft.Launcher.service", 312 "io.snapcraft.Settings.service", 313 } { 314 c.Assert(filepath.Join(dirs.GlobalRootDir, "/usr/share/dbus-1/services/"+fn), testutil.FilePresent) 315 } 316 } 317 318 var ( 319 coreYaml string = "name: core\nversion: 1\ntype: os" 320 snapdYaml string = "name: snapd\nversion: 1\ntype: snapd" 321 ) 322 323 func (s *backendSuite) TestSetupWritesUsedFilesForCore(c *C) { 324 s.testSetupWritesUsedFilesForCoreOrSnapd(c, coreYaml) 325 } 326 327 func (s *backendSuite) TestSetupWritesUsedFilesForSnapd(c *C) { 328 s.testSetupWritesUsedFilesForCoreOrSnapd(c, snapdYaml) 329 } 330 331 func (s *backendSuite) TestSetupWritesUsedFilesBothSnapdAndCoreInstalled(c *C) { 332 err := os.MkdirAll(filepath.Join(dirs.SnapMountDir, "snapd/current"), 0755) 333 c.Assert(err, IsNil) 334 335 coreInfo := snaptest.MockInfo(c, coreYaml, &snap.SideInfo{Revision: snap.R(2)}) 336 makeFakeDbusUserdServiceFiles(c, coreInfo) 337 snapdInfo := snaptest.MockInfo(c, snapdYaml, &snap.SideInfo{Revision: snap.R(3)}) 338 makeFakeDbusUserdServiceFiles(c, snapdInfo) 339 340 // first setup snapd which writes the files 341 err = s.Backend.Setup(snapdInfo, interfaces.ConfinementOptions{}, s.Repo, nil) 342 c.Assert(err, IsNil) 343 344 // then setup core - if both are installed snapd should win 345 err = s.Backend.Setup(coreInfo, interfaces.ConfinementOptions{}, s.Repo, nil) 346 c.Assert(err, IsNil) 347 348 for _, fn := range []string{ 349 "io.snapcraft.Launcher.service", 350 "io.snapcraft.Settings.service", 351 } { 352 c.Assert(filepath.Join(dirs.GlobalRootDir, "/usr/share/dbus-1/services/"+fn), testutil.FileEquals, fmt.Sprintf("content of %s for snap snapd", fn)) 353 } 354 }