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