github.com/rigado/snapd@v2.42.5-go-mod+incompatible/interfaces/seccomp/backend_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2018 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 seccomp_test 21 22 import ( 23 "bytes" 24 "errors" 25 "io/ioutil" 26 "os" 27 "path/filepath" 28 29 . "gopkg.in/check.v1" 30 31 "github.com/snapcore/snapd/cmd" 32 "github.com/snapcore/snapd/dirs" 33 "github.com/snapcore/snapd/interfaces" 34 "github.com/snapcore/snapd/interfaces/ifacetest" 35 "github.com/snapcore/snapd/interfaces/seccomp" 36 "github.com/snapcore/snapd/osutil" 37 "github.com/snapcore/snapd/release" 38 seccomp_compiler "github.com/snapcore/snapd/sandbox/seccomp" 39 "github.com/snapcore/snapd/snap" 40 "github.com/snapcore/snapd/snap/snaptest" 41 "github.com/snapcore/snapd/testutil" 42 "github.com/snapcore/snapd/timings" 43 ) 44 45 type backendSuite struct { 46 ifacetest.BackendSuite 47 48 snapSeccomp *testutil.MockCmd 49 profileHeader string 50 meas *timings.Span 51 52 restoreReadlink func() 53 } 54 55 var _ = Suite(&backendSuite{}) 56 57 var testedConfinementOpts = []interfaces.ConfinementOptions{ 58 {}, 59 {DevMode: true}, 60 {JailMode: true}, 61 {Classic: true}, 62 } 63 64 func (s *backendSuite) SetUpTest(c *C) { 65 s.Backend = &seccomp.Backend{} 66 s.BackendSuite.SetUpTest(c) 67 c.Assert(s.Repo.AddBackend(s.Backend), IsNil) 68 69 // Prepare a directory for seccomp profiles. 70 // NOTE: Normally this is a part of the OS snap. 71 err := os.MkdirAll(dirs.SnapSeccompDir, 0700) 72 c.Assert(err, IsNil) 73 74 s.restoreReadlink = cmd.MockOsReadlink(func(string) (string, error) { 75 // pretend that snapd is run from distro libexecdir 76 return filepath.Join(dirs.DistroLibExecDir, "snapd"), nil 77 }) 78 snapSeccompPath := filepath.Join(dirs.DistroLibExecDir, "snap-seccomp") 79 err = os.MkdirAll(filepath.Dir(snapSeccompPath), 0755) 80 c.Assert(err, IsNil) 81 s.snapSeccomp = testutil.MockCommand(c, snapSeccompPath, ` 82 if [ "$1" = "version-info" ]; then 83 echo "abcdef 1.2.3 1234abcd -" 84 fi`) 85 86 s.Backend.Initialize() 87 s.profileHeader = `# snap-seccomp version information: 88 # abcdef 1.2.3 1234abcd - 89 ` 90 // make sure initialize called version-info 91 c.Check(s.snapSeccomp.Calls(), DeepEquals, [][]string{ 92 {"snap-seccomp", "version-info"}, 93 }) 94 s.snapSeccomp.ForgetCalls() 95 96 perf := timings.New(nil) 97 s.meas = perf.StartSpan("", "") 98 } 99 100 func (s *backendSuite) TearDownTest(c *C) { 101 s.BackendSuite.TearDownTest(c) 102 103 s.snapSeccomp.Restore() 104 s.restoreReadlink() 105 } 106 107 func (s *backendSuite) TestInitialize(c *C) { 108 err := s.Backend.Initialize() 109 c.Assert(err, IsNil) 110 fname := filepath.Join(dirs.SnapSeccompDir, "global.bin") 111 if seccomp.IsBigEndian() { 112 c.Check(fname, testutil.FileEquals, seccomp.GlobalProfileBE) 113 } else { 114 c.Check(fname, testutil.FileEquals, seccomp.GlobalProfileLE) 115 } 116 } 117 118 // Tests for Setup() and Remove() 119 func (s *backendSuite) TestName(c *C) { 120 c.Check(s.Backend.Name(), Equals, interfaces.SecuritySecComp) 121 } 122 123 func (s *backendSuite) TestInstallingSnapWritesProfiles(c *C) { 124 s.InstallSnap(c, interfaces.ConfinementOptions{}, "", ifacetest.SambaYamlV1, 0) 125 profile := filepath.Join(dirs.SnapSeccompDir, "snap.samba.smbd") 126 // file called "snap.sambda.smbd" was created 127 _, err := os.Stat(profile + ".src") 128 c.Check(err, IsNil) 129 // and got compiled 130 c.Check(s.snapSeccomp.Calls(), DeepEquals, [][]string{ 131 {"snap-seccomp", "compile", profile + ".src", profile + ".bin"}, 132 }) 133 } 134 135 func (s *backendSuite) TestInstallingSnapWritesHookProfiles(c *C) { 136 s.InstallSnap(c, interfaces.ConfinementOptions{}, "", ifacetest.HookYaml, 0) 137 profile := filepath.Join(dirs.SnapSeccompDir, "snap.foo.hook.configure") 138 139 // Verify that profile named "snap.foo.hook.configure" was created. 140 _, err := os.Stat(profile + ".src") 141 c.Check(err, IsNil) 142 // and got compiled 143 c.Check(s.snapSeccomp.Calls(), DeepEquals, [][]string{ 144 {"snap-seccomp", "compile", profile + ".src", profile + ".bin"}, 145 }) 146 } 147 148 func (s *backendSuite) TestInstallingSnapWritesProfilesWithReexec(c *C) { 149 restore := cmd.MockOsReadlink(func(string) (string, error) { 150 // simulate that we run snapd from core 151 return filepath.Join(dirs.SnapMountDir, "core/42/usr/lib/snapd/snapd"), nil 152 }) 153 defer restore() 154 155 // ensure we have a mocked snap-seccomp on core 156 snapSeccompOnCorePath := filepath.Join(dirs.SnapMountDir, "core/42/usr/lib/snapd/snap-seccomp") 157 err := os.MkdirAll(filepath.Dir(snapSeccompOnCorePath), 0755) 158 c.Assert(err, IsNil) 159 snapSeccompOnCore := testutil.MockCommand(c, snapSeccompOnCorePath, `if [ "$1" = "version-info" ]; then 160 echo "2345cdef 2.3.4 2345cdef -" 161 fi`) 162 defer snapSeccompOnCore.Restore() 163 164 // rerun initialization 165 err = s.Backend.Initialize() 166 c.Assert(err, IsNil) 167 168 s.InstallSnap(c, interfaces.ConfinementOptions{}, "", ifacetest.SambaYamlV1, 0) 169 profile := filepath.Join(dirs.SnapSeccompDir, "snap.samba.smbd") 170 // file called "snap.sambda.smbd" was created 171 _, err = os.Stat(profile + ".src") 172 c.Check(err, IsNil) 173 // ensure the snap-seccomp from the regular path was *not* used 174 c.Check(s.snapSeccomp.Calls(), HasLen, 0) 175 // ensure the snap-seccomp from the core snap was used instead 176 c.Check(snapSeccompOnCore.Calls(), DeepEquals, [][]string{ 177 {"snap-seccomp", "version-info"}, 178 {"snap-seccomp", "compile", profile + ".src", profile + ".bin"}, 179 }) 180 raw, err := ioutil.ReadFile(profile + ".src") 181 c.Assert(err, IsNil) 182 c.Assert(bytes.HasPrefix(raw, []byte(`# snap-seccomp version information: 183 # 2345cdef 2.3.4 2345cdef - 184 `)), Equals, true) 185 } 186 187 func (s *backendSuite) TestRemovingSnapRemovesProfiles(c *C) { 188 for _, opts := range testedConfinementOpts { 189 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 190 s.RemoveSnap(c, snapInfo) 191 profile := filepath.Join(dirs.SnapSeccompDir, "snap.samba.smbd") 192 // file called "snap.sambda.smbd" was removed 193 _, err := os.Stat(profile + ".src") 194 c.Check(os.IsNotExist(err), Equals, true) 195 } 196 } 197 198 func (s *backendSuite) TestRemovingSnapRemovesHookProfiles(c *C) { 199 for _, opts := range testedConfinementOpts { 200 snapInfo := s.InstallSnap(c, opts, "", ifacetest.HookYaml, 0) 201 s.RemoveSnap(c, snapInfo) 202 profile := filepath.Join(dirs.SnapSeccompDir, "snap.foo.hook.configure") 203 204 // Verify that profile "snap.foo.hook.configure" was removed. 205 _, err := os.Stat(profile + ".src") 206 c.Check(os.IsNotExist(err), Equals, true) 207 } 208 } 209 210 func (s *backendSuite) TestUpdatingSnapToOneWithMoreApps(c *C) { 211 for _, opts := range testedConfinementOpts { 212 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 213 snapInfo = s.UpdateSnap(c, snapInfo, opts, ifacetest.SambaYamlV1WithNmbd, 0) 214 profile := filepath.Join(dirs.SnapSeccompDir, "snap.samba.nmbd") 215 _, err := os.Stat(profile + ".src") 216 // file called "snap.sambda.nmbd" was created 217 c.Check(err, IsNil) 218 // and got compiled 219 c.Check(s.snapSeccomp.Calls(), testutil.DeepContains, []string{"snap-seccomp", "compile", profile + ".src", profile + ".bin"}) 220 s.snapSeccomp.ForgetCalls() 221 222 s.RemoveSnap(c, snapInfo) 223 } 224 } 225 226 func (s *backendSuite) TestUpdatingSnapToOneWithHooks(c *C) { 227 for _, opts := range testedConfinementOpts { 228 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0) 229 snapInfo = s.UpdateSnap(c, snapInfo, opts, ifacetest.SambaYamlWithHook, 0) 230 profile := filepath.Join(dirs.SnapSeccompDir, "snap.samba.hook.configure") 231 232 _, err := os.Stat(profile + ".src") 233 // Verify that profile "snap.samba.hook.configure" was created. 234 c.Check(err, IsNil) 235 // and got compiled 236 c.Check(s.snapSeccomp.Calls(), testutil.DeepContains, []string{"snap-seccomp", "compile", profile + ".src", profile + ".bin"}) 237 s.snapSeccomp.ForgetCalls() 238 239 s.RemoveSnap(c, snapInfo) 240 } 241 } 242 243 func (s *backendSuite) TestUpdatingSnapToOneWithFewerApps(c *C) { 244 for _, opts := range testedConfinementOpts { 245 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1WithNmbd, 0) 246 snapInfo = s.UpdateSnap(c, snapInfo, opts, ifacetest.SambaYamlV1, 0) 247 profile := filepath.Join(dirs.SnapSeccompDir, "snap.samba.nmbd") 248 // file called "snap.sambda.nmbd" was removed 249 _, err := os.Stat(profile + ".src") 250 c.Check(os.IsNotExist(err), Equals, true) 251 s.RemoveSnap(c, snapInfo) 252 } 253 } 254 255 func (s *backendSuite) TestUpdatingSnapToOneWithNoHooks(c *C) { 256 for _, opts := range testedConfinementOpts { 257 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlWithHook, 0) 258 snapInfo = s.UpdateSnap(c, snapInfo, opts, ifacetest.SambaYamlV1, 0) 259 profile := filepath.Join(dirs.SnapSeccompDir, "snap.samba.hook.configure") 260 261 // Verify that profile snap.samba.hook.configure was removed. 262 _, err := os.Stat(profile + ".src") 263 c.Check(os.IsNotExist(err), Equals, true) 264 s.RemoveSnap(c, snapInfo) 265 } 266 } 267 268 func (s *backendSuite) TestRealDefaultTemplateIsNormallyUsed(c *C) { 269 snapInfo := snaptest.MockInfo(c, ifacetest.SambaYamlV1, nil) 270 // NOTE: we don't call seccomp.MockTemplate() 271 err := s.Backend.Setup(snapInfo, interfaces.ConfinementOptions{}, s.Repo, s.meas) 272 c.Assert(err, IsNil) 273 profile := filepath.Join(dirs.SnapSeccompDir, "snap.samba.smbd") 274 data, err := ioutil.ReadFile(profile + ".src") 275 c.Assert(err, IsNil) 276 for _, line := range []string{ 277 // NOTE: a few randomly picked lines from the real profile. Comments 278 // and empty lines are avoided as those can be discarded in the future. 279 "# - create_module, init_module, finit_module, delete_module (kernel modules)\n", 280 "open\n", 281 "getuid\n", 282 "setresuid\n", // this is not random 283 } { 284 c.Assert(string(data), testutil.Contains, line) 285 } 286 } 287 288 type combineSnippetsScenario struct { 289 opts interfaces.ConfinementOptions 290 snippet string 291 content string 292 } 293 294 var combineSnippetsScenarios = []combineSnippetsScenario{{ 295 opts: interfaces.ConfinementOptions{}, 296 content: "default\n", 297 }, { 298 opts: interfaces.ConfinementOptions{}, 299 snippet: "snippet", 300 content: "default\nsnippet\n", 301 }, { 302 opts: interfaces.ConfinementOptions{DevMode: true}, 303 content: "@complain\ndefault\n", 304 }, { 305 opts: interfaces.ConfinementOptions{DevMode: true}, 306 snippet: "snippet", 307 content: "@complain\ndefault\nsnippet\n", 308 }, { 309 opts: interfaces.ConfinementOptions{Classic: true}, 310 snippet: "snippet", 311 content: "@unrestricted\ndefault\nsnippet\n", 312 }, { 313 opts: interfaces.ConfinementOptions{Classic: true, JailMode: true}, 314 snippet: "snippet", 315 content: "default\nsnippet\n", 316 }} 317 318 func (s *backendSuite) TestCombineSnippets(c *C) { 319 restore := release.MockForcedDevmode(false) 320 defer restore() 321 restore = release.MockSecCompActions([]string{"log"}) 322 defer restore() 323 restore = seccomp.MockRequiresSocketcall(func(string) bool { return false }) 324 defer restore() 325 326 // NOTE: replace the real template with a shorter variant 327 restore = seccomp.MockTemplate([]byte("default\n")) 328 defer restore() 329 for _, scenario := range combineSnippetsScenarios { 330 s.Iface.SecCompPermanentSlotCallback = func(spec *seccomp.Specification, slot *snap.SlotInfo) error { 331 if scenario.snippet != "" { 332 spec.AddSnippet(scenario.snippet) 333 } 334 return nil 335 } 336 337 snapInfo := s.InstallSnap(c, scenario.opts, "", ifacetest.SambaYamlV1, 0) 338 profile := filepath.Join(dirs.SnapSeccompDir, "snap.samba.smbd") 339 c.Check(profile+".src", testutil.FileEquals, s.profileHeader+scenario.content) 340 stat, err := os.Stat(profile + ".src") 341 c.Assert(err, IsNil) 342 c.Check(stat.Mode(), Equals, os.FileMode(0644)) 343 s.RemoveSnap(c, snapInfo) 344 } 345 } 346 347 const snapYaml = ` 348 name: foo 349 version: 1 350 developer: acme 351 apps: 352 foo: 353 slots: [iface, iface2] 354 ` 355 356 // Ensure that combined snippets are sorted 357 func (s *backendSuite) TestCombineSnippetsOrdering(c *C) { 358 restore := release.MockForcedDevmode(false) 359 defer restore() 360 restore = seccomp.MockRequiresSocketcall(func(string) bool { return false }) 361 defer restore() 362 363 // NOTE: replace the real template with a shorter variant 364 restore = seccomp.MockTemplate([]byte("default\n")) 365 defer restore() 366 367 iface2 := &ifacetest.TestInterface{InterfaceName: "iface2"} 368 s.Repo.AddInterface(iface2) 369 370 s.Iface.SecCompPermanentSlotCallback = func(spec *seccomp.Specification, slot *snap.SlotInfo) error { 371 spec.AddSnippet("zzz") 372 return nil 373 } 374 iface2.SecCompPermanentSlotCallback = func(spec *seccomp.Specification, slot *snap.SlotInfo) error { 375 spec.AddSnippet("aaa") 376 return nil 377 } 378 379 s.InstallSnap(c, interfaces.ConfinementOptions{}, "", snapYaml, 0) 380 profile := filepath.Join(dirs.SnapSeccompDir, "snap.foo.foo") 381 c.Check(profile+".src", testutil.FileEquals, s.profileHeader+"default\naaa\nzzz\n") 382 stat, err := os.Stat(profile + ".src") 383 c.Assert(err, IsNil) 384 c.Check(stat.Mode(), Equals, os.FileMode(0644)) 385 } 386 387 func (s *backendSuite) TestBindIsAddedForForcedDevModeSystems(c *C) { 388 restore := release.MockForcedDevmode(true) 389 defer restore() 390 391 snapInfo := snaptest.MockInfo(c, ifacetest.SambaYamlV1, nil) 392 // NOTE: we don't call seccomp.MockTemplate() 393 err := s.Backend.Setup(snapInfo, interfaces.ConfinementOptions{}, s.Repo, s.meas) 394 c.Assert(err, IsNil) 395 profile := filepath.Join(dirs.SnapSeccompDir, "snap.samba.smbd") 396 c.Assert(profile+".src", testutil.FileContains, "\nbind\n") 397 } 398 399 func (s *backendSuite) TestSocketcallIsAddedWhenRequired(c *C) { 400 restore := seccomp.MockRequiresSocketcall(func(string) bool { return true }) 401 defer restore() 402 403 snapInfo := snaptest.MockInfo(c, ifacetest.SambaYamlV1, nil) 404 // NOTE: we don't call seccomp.MockTemplate() 405 err := s.Backend.Setup(snapInfo, interfaces.ConfinementOptions{}, s.Repo, s.meas) 406 c.Assert(err, IsNil) 407 profile := filepath.Join(dirs.SnapSeccompDir, "snap.samba.smbd") 408 c.Assert(profile+".src", testutil.FileContains, "\nsocketcall\n") 409 } 410 411 func (s *backendSuite) TestSocketcallIsNotAddedWhenNotRequired(c *C) { 412 restore := seccomp.MockRequiresSocketcall(func(string) bool { return false }) 413 defer restore() 414 415 snapInfo := snaptest.MockInfo(c, ifacetest.SambaYamlV1, nil) 416 // NOTE: we don't call seccomp.MockTemplate() 417 err := s.Backend.Setup(snapInfo, interfaces.ConfinementOptions{}, s.Repo, s.meas) 418 c.Assert(err, IsNil) 419 profile := filepath.Join(dirs.SnapSeccompDir, "snap.samba.smbd") 420 c.Assert(profile+".src", Not(testutil.FileContains), "\nsocketcall\n") 421 } 422 423 const ClassicYamlV1 = ` 424 name: test-classic 425 version: 1 426 developer: acme 427 confinement: classic 428 apps: 429 sh: 430 ` 431 432 func (s *backendSuite) TestSystemKeyRetLogSupported(c *C) { 433 restore := release.MockSecCompActions([]string{"allow", "errno", "kill", "log", "trace", "trap"}) 434 defer restore() 435 436 snapInfo := s.InstallSnap(c, interfaces.ConfinementOptions{DevMode: true}, "", ifacetest.SambaYamlV1, 0) 437 profile := filepath.Join(dirs.SnapSeccompDir, "snap.samba.smbd") 438 c.Assert(profile+".src", Not(testutil.FileContains), "# complain mode logging unavailable\n") 439 s.RemoveSnap(c, snapInfo) 440 441 snapInfo = s.InstallSnap(c, interfaces.ConfinementOptions{DevMode: false}, "", ifacetest.SambaYamlV1, 0) 442 profile = filepath.Join(dirs.SnapSeccompDir, "snap.samba.smbd") 443 c.Assert(profile+".src", Not(testutil.FileContains), "# complain mode logging unavailable\n") 444 s.RemoveSnap(c, snapInfo) 445 446 snapInfo = s.InstallSnap(c, interfaces.ConfinementOptions{Classic: true}, "", ClassicYamlV1, 0) 447 profile = filepath.Join(dirs.SnapSeccompDir, "snap.test-classic.sh") 448 c.Assert(profile+".src", Not(testutil.FileContains), "# complain mode logging unavailable\n") 449 s.RemoveSnap(c, snapInfo) 450 } 451 452 func (s *backendSuite) TestSystemKeyRetLogUnsupported(c *C) { 453 restore := release.MockSecCompActions([]string{"allow", "errno", "kill", "trace", "trap"}) 454 defer restore() 455 456 snapInfo := s.InstallSnap(c, interfaces.ConfinementOptions{DevMode: true}, "", ifacetest.SambaYamlV1, 0) 457 profile := filepath.Join(dirs.SnapSeccompDir, "snap.samba.smbd") 458 c.Assert(profile+".src", testutil.FileContains, "# complain mode logging unavailable\n") 459 s.RemoveSnap(c, snapInfo) 460 461 snapInfo = s.InstallSnap(c, interfaces.ConfinementOptions{DevMode: false}, "", ifacetest.SambaYamlV1, 0) 462 profile = filepath.Join(dirs.SnapSeccompDir, "snap.samba.smbd") 463 c.Assert(profile+".src", Not(testutil.FileContains), "# complain mode logging unavailable\n") 464 s.RemoveSnap(c, snapInfo) 465 466 snapInfo = s.InstallSnap(c, interfaces.ConfinementOptions{Classic: true}, "", ClassicYamlV1, 0) 467 profile = filepath.Join(dirs.SnapSeccompDir, "snap.test-classic.sh") 468 c.Assert(profile+".src", Not(testutil.FileContains), "# complain mode logging unavailable\n") 469 s.RemoveSnap(c, snapInfo) 470 } 471 472 func (s *backendSuite) TestSandboxFeatures(c *C) { 473 restore := seccomp.MockKernelFeatures(func() []string { return []string{"foo", "bar"} }) 474 defer restore() 475 476 c.Assert(s.Backend.SandboxFeatures(), DeepEquals, []string{"kernel:foo", "kernel:bar", "bpf-argument-filtering"}) 477 478 // change version reported by snap-seccomp 479 snapSeccomp := testutil.MockCommand(c, filepath.Join(dirs.DistroLibExecDir, "snap-seccomp"), ` 480 if [ "$1" = "version-info" ]; then 481 echo "abcdef 1.2.3 1234abcd bpf-actlog" 482 fi`) 483 defer snapSeccomp.Restore() 484 485 // reload cached version info 486 err := s.Backend.Initialize() 487 c.Assert(err, IsNil) 488 c.Assert(s.Backend.SandboxFeatures(), DeepEquals, []string{"kernel:foo", "kernel:bar", "bpf-argument-filtering", "bpf-actlog"}) 489 } 490 491 func (s *backendSuite) TestRequiresSocketcallByNotNeededArch(c *C) { 492 testArchs := []string{"amd64", "armhf", "arm64", "powerpc", "ppc64el", "unknownDefault"} 493 for _, arch := range testArchs { 494 restore := seccomp.MockDpkgKernelArchitecture(func() string { return arch }) 495 defer restore() 496 c.Assert(seccomp.RequiresSocketcall(""), Equals, false) 497 } 498 } 499 500 func (s *backendSuite) TestRequiresSocketcallForceByArch(c *C) { 501 testArchs := []string{"sparc", "sparc64"} 502 for _, arch := range testArchs { 503 restore := seccomp.MockDpkgKernelArchitecture(func() string { return arch }) 504 defer restore() 505 c.Assert(seccomp.RequiresSocketcall(""), Equals, true) 506 } 507 } 508 509 func (s *backendSuite) TestRequiresSocketcallForcedViaUbuntuRelease(c *C) { 510 // specify "core18" with 4.4 kernel so as not to influence the release 511 // check. 512 base := "core18" 513 restore := osutil.MockKernelVersion("4.4") 514 defer restore() 515 516 tests := []struct { 517 distro string 518 arch string 519 release string 520 needsSocketcall bool 521 }{ 522 // with core18 as base and 4.4 kernel, we only require 523 // socketcall on i386/s390 524 {"ubuntu", "i386", "14.04", true}, 525 {"ubuntu", "s390x", "14.04", true}, 526 {"ubuntu", "other", "14.04", false}, 527 528 // releases after 14.04 aren't forced 529 {"ubuntu", "i386", "other", false}, 530 {"ubuntu", "s390x", "other", false}, 531 {"ubuntu", "other", "other", false}, 532 533 // other distros aren't forced 534 {"other", "i386", "14.04", false}, 535 {"other", "s390x", "14.04", false}, 536 {"other", "other", "14.04", false}, 537 {"other", "i386", "other", false}, 538 {"other", "s390x", "other", false}, 539 {"other", "other", "other", false}, 540 } 541 542 for _, t := range tests { 543 restore = seccomp.MockReleaseInfoId(t.distro) 544 defer restore() 545 restore = seccomp.MockDpkgKernelArchitecture(func() string { return t.arch }) 546 defer restore() 547 restore = seccomp.MockReleaseInfoVersionId(t.release) 548 defer restore() 549 550 c.Assert(seccomp.RequiresSocketcall(base), Equals, t.needsSocketcall) 551 } 552 } 553 554 func (s *backendSuite) TestRequiresSocketcallForcedViaKernelVersion(c *C) { 555 // specify "core18" with non-ubuntu so as not to influence the kernel 556 // check. 557 base := "core18" 558 559 restore := seccomp.MockReleaseInfoId("other") 560 defer restore() 561 562 tests := []struct { 563 arch string 564 version string 565 needsSocketcall bool 566 }{ 567 // i386 needs socketcall on <= 4.2 kernels 568 {"i386", "4.2", true}, 569 {"i386", "4.3", false}, 570 {"i386", "4.4", false}, 571 572 // s390x needs socketcall on <= 4.2 kernels 573 {"s390x", "4.2", true}, 574 {"s390x", "4.3", false}, 575 {"s390x", "4.4", false}, 576 577 // other architectures don't require it 578 {"other", "4.2", false}, 579 {"other", "4.3", false}, 580 {"other", "4.4", false}, 581 } 582 583 for _, t := range tests { 584 restore := seccomp.MockDpkgKernelArchitecture(func() string { return t.arch }) 585 defer restore() 586 restore = osutil.MockKernelVersion(t.version) 587 defer restore() 588 589 // specify "core18" here so as not to influence the 590 // kernel check. 591 c.Assert(seccomp.RequiresSocketcall(base), Equals, t.needsSocketcall) 592 } 593 } 594 595 func (s *backendSuite) TestRequiresSocketcallForcedViaBaseSnap(c *C) { 596 // Mock up as non-Ubuntu, i386 with new enough kernel so the base snap 597 // check is reached 598 restore := seccomp.MockReleaseInfoId("other") 599 defer restore() 600 restore = seccomp.MockDpkgKernelArchitecture(func() string { return "i386" }) 601 defer restore() 602 restore = osutil.MockKernelVersion("4.3") 603 defer restore() 604 605 testBases := []string{"", "core", "core16"} 606 for _, baseSnap := range testBases { 607 c.Assert(seccomp.RequiresSocketcall(baseSnap), Equals, true) 608 } 609 } 610 611 func (s *backendSuite) TestRequiresSocketcallNotForcedViaBaseSnap(c *C) { 612 // Mock up as non-Ubuntu, i386 with new enough kernel so the base snap 613 // check is reached 614 restore := seccomp.MockReleaseInfoId("other") 615 defer restore() 616 restore = seccomp.MockDpkgKernelArchitecture(func() string { return "i386" }) 617 defer restore() 618 restore = osutil.MockKernelVersion("4.3") 619 defer restore() 620 621 testBases := []string{"bare", "core18", "fedora-core"} 622 for _, baseSnap := range testBases { 623 c.Assert(seccomp.RequiresSocketcall(baseSnap), Equals, false) 624 } 625 } 626 627 func (s *backendSuite) TestRebuildsWithVersionInfoWhenNeeded(c *C) { 628 restore := release.MockForcedDevmode(false) 629 defer restore() 630 restore = release.MockSecCompActions([]string{"log"}) 631 defer restore() 632 restore = seccomp.MockRequiresSocketcall(func(string) bool { return false }) 633 defer restore() 634 635 // NOTE: replace the real template with a shorter variant 636 restore = seccomp.MockTemplate([]byte("\ndefault\n")) 637 defer restore() 638 639 profile := filepath.Join(dirs.SnapSeccompDir, "snap.samba.smbd") 640 641 snapInfo := snaptest.MockInfo(c, ifacetest.SambaYamlV1, nil) 642 err := s.Backend.Setup(snapInfo, interfaces.ConfinementOptions{}, s.Repo, s.meas) 643 c.Assert(err, IsNil) 644 c.Check(profile+".src", testutil.FileEquals, s.profileHeader+"\ndefault\n") 645 646 c.Check(s.snapSeccomp.Calls(), DeepEquals, [][]string{ 647 {"snap-seccomp", "compile", profile + ".src", profile + ".bin"}, 648 }) 649 650 // unchanged snap-seccomp version will not trigger a rebuild 651 err = s.Backend.Setup(snapInfo, interfaces.ConfinementOptions{}, s.Repo, s.meas) 652 c.Assert(err, IsNil) 653 654 // compilation from this first Setup() 655 c.Check(s.snapSeccomp.Calls(), HasLen, 1) 656 657 // change version reported by snap-seccomp 658 snapSeccomp := testutil.MockCommand(c, filepath.Join(dirs.DistroLibExecDir, "snap-seccomp"), ` 659 if [ "$1" = "version-info" ]; then 660 echo "abcdef 2.3.3 2345abcd -" 661 fi`) 662 defer snapSeccomp.Restore() 663 updatedProfileHeader := `# snap-seccomp version information: 664 # abcdef 2.3.3 2345abcd - 665 ` 666 // reload cached version info 667 err = s.Backend.Initialize() 668 c.Assert(err, IsNil) 669 670 c.Check(s.snapSeccomp.Calls(), HasLen, 2) 671 c.Check(s.snapSeccomp.Calls(), DeepEquals, [][]string{ 672 // compilation from first Setup() 673 {"snap-seccomp", "compile", profile + ".src", profile + ".bin"}, 674 // initialization with new version 675 {"snap-seccomp", "version-info"}, 676 }) 677 678 // the profile should be rebuilt now 679 err = s.Backend.Setup(snapInfo, interfaces.ConfinementOptions{}, s.Repo, s.meas) 680 c.Assert(err, IsNil) 681 c.Check(profile+".src", testutil.FileEquals, updatedProfileHeader+"\ndefault\n") 682 683 c.Check(s.snapSeccomp.Calls(), HasLen, 3) 684 c.Check(s.snapSeccomp.Calls(), DeepEquals, [][]string{ 685 // compilation from first Setup() 686 {"snap-seccomp", "compile", profile + ".src", profile + ".bin"}, 687 // initialization with new version 688 {"snap-seccomp", "version-info"}, 689 // compilation of profiles with new compiler version 690 {"snap-seccomp", "compile", profile + ".src", profile + ".bin"}, 691 }) 692 } 693 694 func (s *backendSuite) TestInitializationDuringBootstrap(c *C) { 695 // undo what was done in test set-up 696 s.snapSeccomp.Restore() 697 os.Remove(s.snapSeccomp.Exe()) 698 699 // during bootstrap, before seeding, snapd/core snap is mounted at some 700 // random location under /tmp 701 tmpDir := c.MkDir() 702 restore := cmd.MockOsReadlink(func(string) (string, error) { 703 return filepath.Join(tmpDir, "usr/lib/snapd/snapd"), nil 704 }) 705 defer restore() 706 707 // ensure we have a mocked snap-seccomp on core 708 snapSeccompInMountedPath := filepath.Join(tmpDir, "usr/lib/snapd/snap-seccomp") 709 err := os.MkdirAll(filepath.Dir(snapSeccompInMountedPath), 0755) 710 c.Assert(err, IsNil) 711 snapSeccompInMounted := testutil.MockCommand(c, snapSeccompInMountedPath, `if [ "$1" = "version-info" ]; then 712 echo "2345cdef 2.3.4 2345cdef -" 713 fi`) 714 defer snapSeccompInMounted.Restore() 715 716 // rerun initialization 717 err = s.Backend.Initialize() 718 c.Assert(err, IsNil) 719 720 // ensure the snap-seccomp from the regular path was *not* used 721 c.Check(s.snapSeccomp.Calls(), HasLen, 0) 722 // the one from mounted snap was used 723 c.Check(snapSeccompInMounted.Calls(), DeepEquals, [][]string{ 724 {"snap-seccomp", "version-info"}, 725 }) 726 727 sb, ok := s.Backend.(*seccomp.Backend) 728 c.Assert(ok, Equals, true) 729 c.Check(sb.VersionInfo(), Equals, seccomp_compiler.VersionInfo("2345cdef 2.3.4 2345cdef -")) 730 } 731 732 func (s *backendSuite) TestCompilerInitUnhappy(c *C) { 733 restore := seccomp.MockSeccompCompilerLookup(func(name string) (string, error) { 734 c.Check(name, Equals, "snap-seccomp") 735 return "", errors.New("failed") 736 }) 737 defer restore() 738 err := s.Backend.Initialize() 739 c.Assert(err, ErrorMatches, "cannot initialize seccomp profile compiler: failed") 740 } 741 742 func (s *backendSuite) TestSystemUsernamesPolicy(c *C) { 743 snapYaml := ` 744 name: app 745 version: 0.1 746 system-usernames: 747 testid: shared 748 testid2: shared 749 apps: 750 cmd: 751 ` 752 snapInfo := snaptest.MockInfo(c, snapYaml, nil) 753 // NOTE: we don't call seccomp.MockTemplate() 754 err := s.Backend.Setup(snapInfo, interfaces.ConfinementOptions{}, s.Repo, s.meas) 755 c.Assert(err, IsNil) 756 // NOTE: we don't call seccomp.MockTemplate() 757 profile := filepath.Join(dirs.SnapSeccompDir, "snap.app.cmd") 758 data, err := ioutil.ReadFile(profile + ".src") 759 c.Assert(err, IsNil) 760 for _, line := range []string{ 761 // NOTE: a few randomly picked lines from the real 762 // profile. Comments and empty lines are avoided as 763 // those can be discarded in the future. 764 "\n# - create_module, init_module, finit_module, delete_module (kernel modules)\n", 765 "\nopen\n", 766 "\ngetuid\n", 767 "\nsetgroups 0 0\n", 768 // and a few randomly picked lines from root syscalls 769 // with extra \n checks to ensure we have the right 770 // "paragraphs" in the generated output 771 "\n\n# allow setresgid to root\n", 772 "\n# allow setresuid to root\n", 773 "\nsetresuid u:root u:root u:root\n", 774 // and a few randomly picked lines from global id syscalls 775 "\n\n# allow setresgid to testid\n", 776 "\n\n# allow setresuid to testid\n", 777 "\nsetresuid -1 u:testid -1\n", 778 // also for the second user 779 "\n\n# allow setresgid to testid2\n", 780 "\n# allow setresuid to testid2\n", 781 "\nsetresuid -1 u:testid2 -1\n", 782 } { 783 c.Assert(string(data), testutil.Contains, line) 784 } 785 786 // make sure the bare syscalls aren't present 787 c.Assert(string(data), Not(testutil.Contains), "setresuid\n") 788 } 789 790 func (s *backendSuite) TestNoSystemUsernamesPolicy(c *C) { 791 snapYaml := ` 792 name: app 793 version: 0.1 794 apps: 795 cmd: 796 ` 797 snapInfo := snaptest.MockInfo(c, snapYaml, nil) 798 // NOTE: we don't call seccomp.MockTemplate() 799 err := s.Backend.Setup(snapInfo, interfaces.ConfinementOptions{}, s.Repo, s.meas) 800 c.Assert(err, IsNil) 801 // NOTE: we don't call seccomp.MockTemplate() 802 profile := filepath.Join(dirs.SnapSeccompDir, "snap.app.cmd") 803 data, err := ioutil.ReadFile(profile + ".src") 804 c.Assert(err, IsNil) 805 for _, line := range []string{ 806 // and a few randomly picked lines from root syscalls 807 "# allow setresgid to root\n", 808 "# allow setresuid to root\n", 809 "setresuid u:root u:root u:root\n", 810 // and a few randomly picked lines from global id syscalls 811 "# allow setresgid to testid\n", 812 "# allow setresuid to testid\n", 813 "setresuid -1 u:testid -1\n", 814 } { 815 c.Assert(string(data), Not(testutil.Contains), line) 816 } 817 818 // make sure the bare syscalls are present 819 c.Assert(string(data), testutil.Contains, "setresuid\n") 820 }