github.com/rigado/snapd@v2.42.5-go-mod+incompatible/overlord/snapstate/check_snap_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 snapstate_test 21 22 import ( 23 "errors" 24 "fmt" 25 "io/ioutil" 26 "os" 27 "path/filepath" 28 29 . "gopkg.in/check.v1" 30 31 "github.com/snapcore/snapd/arch" 32 "github.com/snapcore/snapd/cmd" 33 "github.com/snapcore/snapd/dirs" 34 "github.com/snapcore/snapd/osutil" 35 "github.com/snapcore/snapd/overlord/state" 36 "github.com/snapcore/snapd/release" 37 seccomp_compiler "github.com/snapcore/snapd/sandbox/seccomp" 38 "github.com/snapcore/snapd/snap" 39 "github.com/snapcore/snapd/snap/snapdir" 40 "github.com/snapcore/snapd/snap/snaptest" 41 "github.com/snapcore/snapd/testutil" 42 43 "github.com/snapcore/snapd/overlord/snapstate" 44 "github.com/snapcore/snapd/overlord/snapstate/snapstatetest" 45 ) 46 47 type checkSnapSuite struct { 48 testutil.BaseTest 49 st *state.State 50 deviceCtx snapstate.DeviceContext 51 } 52 53 var _ = Suite(&checkSnapSuite{}) 54 55 func (s *checkSnapSuite) SetUpTest(c *C) { 56 s.BaseTest.SetUpTest(c) 57 dirs.SetRootDir(c.MkDir()) 58 s.st = state.New(nil) 59 s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) 60 s.deviceCtx = &snapstatetest.TrivialDeviceContext{DeviceModel: MakeModel(map[string]interface{}{ 61 "kernel": "kernel", 62 "gadget": "gadget", 63 })} 64 } 65 66 func (s *checkSnapSuite) TearDownTest(c *C) { 67 s.BaseTest.TearDownTest(c) 68 dirs.SetRootDir("") 69 } 70 71 func (s *checkSnapSuite) TestCheckSnapErrorOnUnsupportedArchitecture(c *C) { 72 const yaml = `name: hello 73 version: 1.10 74 architectures: 75 - yadayada 76 - blahblah 77 ` 78 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 79 c.Assert(err, IsNil) 80 81 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 82 c.Check(path, Equals, "snap-path") 83 c.Check(si, IsNil) 84 return info, emptyContainer(c), nil 85 } 86 restore := snapstate.MockOpenSnapFile(openSnapFile) 87 defer restore() 88 89 err = snapstate.CheckSnap(s.st, "snap-path", "hello", nil, nil, snapstate.Flags{}, nil) 90 91 errorMsg := fmt.Sprintf(`snap "hello" supported architectures (yadayada, blahblah) are incompatible with this system (%s)`, arch.DpkgArchitecture()) 92 c.Assert(err.Error(), Equals, errorMsg) 93 } 94 95 var assumesTests = []struct { 96 version string 97 assumes string 98 classic bool 99 error string 100 }{{ 101 assumes: "[common-data-dir]", 102 }, { 103 assumes: "[f1, f2]", 104 error: `snap "foo" assumes unsupported features: f1, f2 \(try to refresh the core snap\)`, 105 }, { 106 assumes: "[f1, f2]", 107 classic: true, 108 error: `snap "foo" assumes unsupported features: f1, f2 \(try to update snapd and refresh the core snap\)`, 109 }, { 110 assumes: "[snapd2.15]", 111 version: "unknown", 112 }, { 113 assumes: "[snapdnono]", 114 version: "unknown", 115 error: `.* unsupported features: snapdnono .*`, 116 }, { 117 assumes: "[snapd2.15nono]", 118 version: "unknown", 119 error: `.* unsupported features: snapd2.15nono .*`, 120 }, { 121 assumes: "[snapd2.15]", 122 version: "2.15", 123 }, { 124 assumes: "[snapd2.15]", 125 version: "2.15.1", 126 }, { 127 assumes: "[snapd2.15]", 128 version: "2.15+git", 129 }, { 130 assumes: "[snapd2.15]", 131 version: "2.16", 132 }, { 133 assumes: "[snapd2.15.1]", 134 version: "2.16", 135 }, { 136 assumes: "[snapd2.15.2]", 137 version: "2.16.1", 138 }, { 139 assumes: "[snapd3]", 140 version: "3.1", 141 }, { 142 assumes: "[snapd2.16]", 143 version: "2.15", 144 error: `.* unsupported features: snapd2\.16 .*`, 145 }, { 146 assumes: "[snapd2.15.1]", 147 version: "2.15", 148 error: `.* unsupported features: snapd2\.15\.1 .*`, 149 }, { 150 assumes: "[snapd2.15.1]", 151 version: "2.15.0", 152 error: `.* unsupported features: snapd2\.15\.1 .*`, 153 }, { 154 // Note that this is different from how strconv.VersionCompare 155 // (dpkg version numbering) would behave - it would error here 156 assumes: "[snapd2.15]", 157 version: "2.15~pre1", 158 }, { 159 assumes: "[command-chain]", 160 }} 161 162 func (s *checkSnapSuite) TestCheckSnapAssumes(c *C) { 163 restore := cmd.MockVersion("2.15") 164 defer restore() 165 166 restore = release.MockOnClassic(false) 167 defer restore() 168 169 for _, test := range assumesTests { 170 cmd.Version = test.version 171 if cmd.Version == "" { 172 cmd.Version = "2.15" 173 } 174 release.OnClassic = test.classic 175 176 yaml := fmt.Sprintf("name: foo\nversion: 1.0\nassumes: %s\n", test.assumes) 177 178 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 179 c.Assert(err, IsNil) 180 181 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 182 return info, emptyContainer(c), nil 183 } 184 restore := snapstate.MockOpenSnapFile(openSnapFile) 185 defer restore() 186 err = snapstate.CheckSnap(s.st, "snap-path", "foo", nil, nil, snapstate.Flags{}, nil) 187 if test.error != "" { 188 c.Check(err, ErrorMatches, test.error) 189 } else { 190 c.Assert(err, IsNil) 191 } 192 } 193 } 194 195 func (s *checkSnapSuite) TestCheckSnapCheckCallbackOK(c *C) { 196 const yaml = `name: foo 197 version: 1.0` 198 199 si := &snap.SideInfo{ 200 SnapID: "snap-id", 201 } 202 203 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 204 info := snaptest.MockInfo(c, yaml, si) 205 return info, emptyContainer(c), nil 206 } 207 r1 := snapstate.MockOpenSnapFile(openSnapFile) 208 defer r1() 209 210 checkCbCalled := false 211 checkCb := func(st *state.State, s, cur *snap.Info, flags snapstate.Flags, deviceCtx snapstate.DeviceContext) error { 212 c.Assert(s.InstanceName(), Equals, "foo") 213 c.Assert(s.SnapID, Equals, "snap-id") 214 checkCbCalled = true 215 return nil 216 } 217 r2 := snapstate.MockCheckSnapCallbacks([]snapstate.CheckSnapCallback{checkCb}) 218 defer r2() 219 220 err := snapstate.CheckSnap(s.st, "snap-path", "foo", si, nil, snapstate.Flags{}, nil) 221 c.Check(err, IsNil) 222 223 c.Check(checkCbCalled, Equals, true) 224 } 225 226 func (s *checkSnapSuite) TestCheckSnapCheckCallbackFail(c *C) { 227 const yaml = `name: foo 228 version: 1.0` 229 230 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 231 c.Assert(err, IsNil) 232 233 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 234 return info, emptyContainer(c), nil 235 } 236 restore := snapstate.MockOpenSnapFile(openSnapFile) 237 defer restore() 238 239 fail := errors.New("bad snap") 240 checkCb := func(st *state.State, s, cur *snap.Info, flags snapstate.Flags, deviceCtx snapstate.DeviceContext) error { 241 return fail 242 } 243 r2 := snapstate.MockCheckSnapCallbacks(nil) 244 defer r2() 245 snapstate.AddCheckSnapCallback(checkCb) 246 247 err = snapstate.CheckSnap(s.st, "snap-path", "foo", nil, nil, snapstate.Flags{}, nil) 248 c.Check(err, Equals, fail) 249 } 250 251 func (s *checkSnapSuite) TestCheckSnapGadgetUpdate(c *C) { 252 reset := release.MockOnClassic(false) 253 defer reset() 254 255 st := state.New(nil) 256 st.Lock() 257 defer st.Unlock() 258 259 si := &snap.SideInfo{RealName: "gadget", Revision: snap.R(2), SnapID: "gadget-id"} 260 snaptest.MockSnap(c, ` 261 name: gadget 262 type: gadget 263 version: 1 264 `, si) 265 snapstate.Set(st, "gadget", &snapstate.SnapState{ 266 SnapType: "gadget", 267 Active: true, 268 Sequence: []*snap.SideInfo{si}, 269 Current: si.Revision, 270 }) 271 272 const yaml = `name: gadget 273 type: gadget 274 version: 2 275 ` 276 277 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 278 info.SnapID = "gadget-id" 279 c.Assert(err, IsNil) 280 281 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 282 return info, emptyContainer(c), nil 283 } 284 restore := snapstate.MockOpenSnapFile(openSnapFile) 285 defer restore() 286 287 st.Unlock() 288 err = snapstate.CheckSnap(st, "snap-path", "gadget", nil, nil, snapstate.Flags{}, s.deviceCtx) 289 st.Lock() 290 c.Check(err, IsNil) 291 } 292 293 func (s *checkSnapSuite) TestCheckSnapGadgetUpdateLocal(c *C) { 294 reset := release.MockOnClassic(false) 295 defer reset() 296 297 st := state.New(nil) 298 st.Lock() 299 defer st.Unlock() 300 301 si := &snap.SideInfo{RealName: "gadget", Revision: snap.R(2)} 302 snaptest.MockSnap(c, ` 303 name: gadget 304 type: gadget 305 version: 1 306 `, si) 307 snapstate.Set(st, "gadget", &snapstate.SnapState{ 308 SnapType: "gadget", 309 Active: true, 310 Sequence: []*snap.SideInfo{si}, 311 Current: si.Revision, 312 }) 313 314 const yaml = `name: gadget 315 type: gadget 316 version: 2 317 ` 318 319 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 320 // no SnapID => local! 321 c.Assert(err, IsNil) 322 323 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 324 return info, emptyContainer(c), nil 325 } 326 restore := snapstate.MockOpenSnapFile(openSnapFile) 327 defer restore() 328 329 st.Unlock() 330 err = snapstate.CheckSnap(st, "snap-path", "gadget", nil, nil, snapstate.Flags{}, s.deviceCtx) 331 st.Lock() 332 c.Check(err, IsNil) 333 } 334 335 func (s *checkSnapSuite) TestCheckSnapGadgetUpdateToUnassertedProhibited(c *C) { 336 reset := release.MockOnClassic(false) 337 defer reset() 338 339 st := state.New(nil) 340 st.Lock() 341 defer st.Unlock() 342 343 si := &snap.SideInfo{RealName: "gadget", Revision: snap.R(2), SnapID: "gadget-id"} 344 snaptest.MockSnap(c, ` 345 name: gadget 346 type: gadget 347 version: 1 348 `, si) 349 snapstate.Set(st, "gadget", &snapstate.SnapState{ 350 SnapType: "gadget", 351 Active: true, 352 Sequence: []*snap.SideInfo{si}, 353 Current: si.Revision, 354 }) 355 356 const yaml = `name: gadget 357 type: gadget 358 version: 2 359 ` 360 361 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 362 c.Assert(err, IsNil) 363 364 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 365 return info, emptyContainer(c), nil 366 } 367 restore := snapstate.MockOpenSnapFile(openSnapFile) 368 defer restore() 369 370 st.Unlock() 371 err = snapstate.CheckSnap(st, "snap-path", "gadget", nil, nil, snapstate.Flags{}, s.deviceCtx) 372 st.Lock() 373 c.Check(err, ErrorMatches, `cannot replace signed gadget snap with an unasserted one`) 374 } 375 376 func (s *checkSnapSuite) TestCheckSnapGadgetAdditionProhibited(c *C) { 377 reset := release.MockOnClassic(false) 378 defer reset() 379 380 st := state.New(nil) 381 st.Lock() 382 defer st.Unlock() 383 384 si := &snap.SideInfo{RealName: "gadget", Revision: snap.R(2)} 385 snaptest.MockSnap(c, ` 386 name: gadget 387 type: gadget 388 version: 1 389 `, si) 390 snapstate.Set(st, "gadget", &snapstate.SnapState{ 391 SnapType: "gadget", 392 Active: true, 393 Sequence: []*snap.SideInfo{si}, 394 Current: si.Revision, 395 }) 396 397 const yaml = `name: zgadget 398 type: gadget 399 version: 2 400 ` 401 402 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 403 c.Assert(err, IsNil) 404 405 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 406 return info, emptyContainer(c), nil 407 } 408 restore := snapstate.MockOpenSnapFile(openSnapFile) 409 defer restore() 410 411 st.Unlock() 412 err = snapstate.CheckSnap(st, "snap-path", "gadget", nil, nil, snapstate.Flags{}, s.deviceCtx) 413 st.Lock() 414 c.Check(err, ErrorMatches, "cannot replace gadget snap with a different one") 415 } 416 417 func (s *checkSnapSuite) TestCheckSnapGadgetAdditionProhibitedBySnapID(c *C) { 418 reset := release.MockOnClassic(false) 419 defer reset() 420 421 st := state.New(nil) 422 st.Lock() 423 defer st.Unlock() 424 425 si := &snap.SideInfo{RealName: "gadget", Revision: snap.R(2), SnapID: "gadget-id"} 426 snaptest.MockSnap(c, ` 427 name: gadget 428 type: gadget 429 version: 1 430 `, si) 431 snapstate.Set(st, "gadget", &snapstate.SnapState{ 432 SnapType: "gadget", 433 Active: true, 434 Sequence: []*snap.SideInfo{si}, 435 Current: si.Revision, 436 }) 437 438 const yaml = `name: zgadget 439 type: gadget 440 version: 2 441 ` 442 443 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 444 info.SnapID = "zgadget-id" 445 c.Assert(err, IsNil) 446 447 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 448 return info, emptyContainer(c), nil 449 } 450 restore := snapstate.MockOpenSnapFile(openSnapFile) 451 defer restore() 452 453 st.Unlock() 454 err = snapstate.CheckSnap(st, "snap-path", "gadget", nil, nil, snapstate.Flags{}, s.deviceCtx) 455 st.Lock() 456 c.Check(err, ErrorMatches, "cannot replace gadget snap with a different one") 457 } 458 459 func (s *checkSnapSuite) TestCheckSnapGadgetNoPrior(c *C) { 460 reset := release.MockOnClassic(false) 461 defer reset() 462 463 st := state.New(nil) 464 st.Lock() 465 defer st.Unlock() 466 st.Set("seeded", true) 467 468 const yaml = `name: gadget 469 type: gadget 470 version: 1 471 ` 472 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 473 c.Assert(err, IsNil) 474 475 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 476 return info, emptyContainer(c), nil 477 } 478 restore := snapstate.MockOpenSnapFile(openSnapFile) 479 defer restore() 480 481 st.Unlock() 482 err = snapstate.CheckSnap(st, "snap-path", "gadget", nil, nil, snapstate.Flags{}, nil) 483 st.Lock() 484 c.Check(err, IsNil) 485 } 486 487 func (s *checkSnapSuite) TestCheckSnapErrorOnDevModeDisallowed(c *C) { 488 const yaml = `name: hello 489 version: 1.10 490 confinement: devmode 491 ` 492 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 493 c.Assert(err, IsNil) 494 495 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 496 c.Check(path, Equals, "snap-path") 497 c.Check(si, IsNil) 498 return info, emptyContainer(c), nil 499 } 500 restore := snapstate.MockOpenSnapFile(openSnapFile) 501 defer restore() 502 503 err = snapstate.CheckSnap(s.st, "snap-path", "hello", nil, nil, snapstate.Flags{}, nil) 504 505 c.Assert(err, ErrorMatches, ".* requires devmode or confinement override") 506 } 507 508 func (s *checkSnapSuite) TestCheckSnapErrorOnClassicDisallowed(c *C) { 509 const yaml = `name: hello 510 version: 1.10 511 confinement: classic 512 ` 513 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 514 c.Assert(err, IsNil) 515 516 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 517 c.Check(path, Equals, "snap-path") 518 c.Check(si, IsNil) 519 return info, emptyContainer(c), nil 520 } 521 restore := snapstate.MockOpenSnapFile(openSnapFile) 522 defer restore() 523 524 restore = release.MockOnClassic(true) 525 defer restore() 526 527 err = snapstate.CheckSnap(s.st, "snap-path", "hello", nil, nil, snapstate.Flags{}, nil) 528 529 c.Assert(err, ErrorMatches, ".* requires classic confinement") 530 } 531 532 func (s *checkSnapSuite) TestCheckSnapErrorClassicOnCoreDisallowed(c *C) { 533 const yaml = `name: hello 534 version: 1.10 535 confinement: classic 536 ` 537 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 538 c.Assert(err, IsNil) 539 540 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 541 c.Check(path, Equals, "snap-path") 542 c.Check(si, IsNil) 543 return info, emptyContainer(c), nil 544 } 545 restore := snapstate.MockOpenSnapFile(openSnapFile) 546 defer restore() 547 548 restore = release.MockOnClassic(false) 549 defer restore() 550 551 err = snapstate.CheckSnap(s.st, "snap-path", "hello", nil, nil, snapstate.Flags{Classic: true}, nil) 552 553 c.Assert(err, ErrorMatches, ".* requires classic confinement which is only available on classic systems") 554 } 555 556 func (s *checkSnapSuite) TestCheckSnapErrorClassicModeForStrictOrDevmode(c *C) { 557 const yaml = `name: hello 558 version: 1.10 559 confinement: strict 560 ` 561 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 562 c.Assert(err, IsNil) 563 564 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 565 c.Check(path, Equals, "snap-path") 566 c.Check(si, IsNil) 567 return info, emptyContainer(c), nil 568 } 569 restore := snapstate.MockOpenSnapFile(openSnapFile) 570 defer restore() 571 572 err = snapstate.CheckSnap(s.st, "snap-path", "hello", nil, nil, snapstate.Flags{Classic: true}, nil) 573 574 c.Assert(err, ErrorMatches, `snap "hello" is not a classic confined snap`) 575 } 576 577 func (s *checkSnapSuite) TestCheckSnapKernelUpdate(c *C) { 578 reset := release.MockOnClassic(false) 579 defer reset() 580 581 st := state.New(nil) 582 st.Lock() 583 defer st.Unlock() 584 585 si := &snap.SideInfo{RealName: "kernel", Revision: snap.R(2), SnapID: "kernel-id"} 586 snaptest.MockSnap(c, ` 587 name: kernel 588 type: kernel 589 version: 1 590 `, si) 591 snapstate.Set(st, "kernel", &snapstate.SnapState{ 592 SnapType: "kernel", 593 Active: true, 594 Sequence: []*snap.SideInfo{si}, 595 Current: si.Revision, 596 }) 597 598 const yaml = `name: kernel 599 type: kernel 600 version: 2 601 ` 602 603 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 604 info.SnapID = "kernel-id" 605 c.Assert(err, IsNil) 606 607 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 608 return info, emptyContainer(c), nil 609 } 610 restore := snapstate.MockOpenSnapFile(openSnapFile) 611 defer restore() 612 613 st.Unlock() 614 err = snapstate.CheckSnap(st, "snap-path", "kernel", nil, nil, snapstate.Flags{}, s.deviceCtx) 615 st.Lock() 616 c.Check(err, IsNil) 617 } 618 619 func (s *checkSnapSuite) TestCheckSnapKernelAdditionProhibitedBySnapID(c *C) { 620 reset := release.MockOnClassic(false) 621 defer reset() 622 623 st := state.New(nil) 624 st.Lock() 625 defer st.Unlock() 626 627 si := &snap.SideInfo{RealName: "kernel", Revision: snap.R(2), SnapID: "kernel-id"} 628 snaptest.MockSnap(c, ` 629 name: kernel 630 type: kernel 631 version: 1 632 `, si) 633 snapstate.Set(st, "kernel", &snapstate.SnapState{ 634 SnapType: "kernel", 635 Active: true, 636 Sequence: []*snap.SideInfo{si}, 637 Current: si.Revision, 638 }) 639 640 const yaml = `name: zkernel 641 type: kernel 642 version: 2 643 ` 644 645 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 646 info.SnapID = "zkernel-id" 647 c.Assert(err, IsNil) 648 649 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 650 return info, emptyContainer(c), nil 651 } 652 restore := snapstate.MockOpenSnapFile(openSnapFile) 653 defer restore() 654 655 st.Unlock() 656 err = snapstate.CheckSnap(st, "snap-path", "kernel", nil, nil, snapstate.Flags{}, s.deviceCtx) 657 st.Lock() 658 c.Check(err, ErrorMatches, "cannot replace kernel snap with a different one") 659 } 660 661 func (s *checkSnapSuite) TestCheckSnapBasesErrorsIfMissing(c *C) { 662 st := state.New(nil) 663 st.Lock() 664 defer st.Unlock() 665 666 const yaml = `name: requires-base 667 version: 1 668 base: some-base 669 ` 670 671 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 672 c.Assert(err, IsNil) 673 674 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 675 return info, emptyContainer(c), nil 676 } 677 restore := snapstate.MockOpenSnapFile(openSnapFile) 678 defer restore() 679 680 st.Unlock() 681 err = snapstate.CheckSnap(st, "snap-path", "requires-base", nil, nil, snapstate.Flags{}, nil) 682 st.Lock() 683 c.Check(err, ErrorMatches, "cannot find required base \"some-base\"") 684 } 685 686 func (s *checkSnapSuite) TestCheckSnapBasesNoneHappy(c *C) { 687 st := state.New(nil) 688 st.Lock() 689 defer st.Unlock() 690 691 const yaml = `name: use-base-none 692 version: 1 693 base: none 694 ` 695 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 696 c.Assert(err, IsNil) 697 698 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 699 return info, emptyContainer(c), nil 700 } 701 restore := snapstate.MockOpenSnapFile(openSnapFile) 702 defer restore() 703 704 st.Unlock() 705 err = snapstate.CheckSnap(st, "snap-path", "use-base-none", nil, nil, snapstate.Flags{}, nil) 706 st.Lock() 707 c.Check(err, IsNil) 708 } 709 710 func (s *checkSnapSuite) TestCheckSnapBasesHappy(c *C) { 711 st := state.New(nil) 712 st.Lock() 713 defer st.Unlock() 714 715 si := &snap.SideInfo{RealName: "some-base", Revision: snap.R(1), SnapID: "some-base-id"} 716 snaptest.MockSnap(c, ` 717 name: some-base 718 type: base 719 version: 1 720 `, si) 721 snapstate.Set(st, "some-base", &snapstate.SnapState{ 722 SnapType: "base", 723 Active: true, 724 Sequence: []*snap.SideInfo{si}, 725 Current: si.Revision, 726 }) 727 728 const yaml = `name: requires-base 729 version: 1 730 base: some-base 731 ` 732 733 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 734 c.Assert(err, IsNil) 735 736 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 737 return info, emptyContainer(c), nil 738 } 739 restore := snapstate.MockOpenSnapFile(openSnapFile) 740 defer restore() 741 742 st.Unlock() 743 err = snapstate.CheckSnap(st, "snap-path", "requires-base", nil, nil, snapstate.Flags{}, nil) 744 st.Lock() 745 c.Check(err, IsNil) 746 } 747 748 // emptyContainer returns a minimal container that passes 749 // ValidateContainer: / and /meta exist and are 0755, and 750 // /meta/snap.yaml is a regular world-readable file. 751 func emptyContainer(c *C) *snapdir.SnapDir { 752 d := c.MkDir() 753 c.Assert(os.Chmod(d, 0755), IsNil) 754 c.Assert(os.Mkdir(filepath.Join(d, "meta"), 0755), IsNil) 755 c.Assert(ioutil.WriteFile(filepath.Join(d, "meta", "snap.yaml"), nil, 0444), IsNil) 756 return snapdir.New(d) 757 } 758 759 func (s *checkSnapSuite) TestCheckSnapInstanceName(c *C) { 760 st := state.New(nil) 761 st.Lock() 762 defer st.Unlock() 763 764 si := &snap.SideInfo{RealName: "foo", Revision: snap.R(1), SnapID: "some-base-id"} 765 info := snaptest.MockSnap(c, ` 766 name: foo 767 version: 1 768 `, si) 769 snapstate.Set(st, "foo", &snapstate.SnapState{ 770 Active: true, 771 Sequence: []*snap.SideInfo{si}, 772 Current: si.Revision, 773 }) 774 775 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 776 return info, emptyContainer(c), nil 777 } 778 restore := snapstate.MockOpenSnapFile(openSnapFile) 779 defer restore() 780 781 st.Unlock() 782 err := snapstate.CheckSnap(st, "snap-path", "foo_instance", nil, nil, snapstate.Flags{}, nil) 783 st.Lock() 784 c.Check(err, IsNil) 785 786 st.Unlock() 787 err = snapstate.CheckSnap(st, "snap-path", "bar_instance", nil, nil, snapstate.Flags{}, nil) 788 st.Lock() 789 c.Check(err, ErrorMatches, `cannot install snap "foo" using instance name "bar_instance"`) 790 791 st.Unlock() 792 err = snapstate.CheckSnap(st, "snap-path", "other-name", nil, nil, snapstate.Flags{}, nil) 793 st.Lock() 794 c.Check(err, ErrorMatches, `cannot install snap "foo" using instance name "other-name"`) 795 } 796 797 func (s *checkSnapSuite) TestCheckSnapCheckCallInstanceKeySet(c *C) { 798 const yaml = `name: foo 799 version: 1.0` 800 801 si := &snap.SideInfo{ 802 SnapID: "snap-id", 803 } 804 805 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 806 info := snaptest.MockInfo(c, yaml, si) 807 return info, emptyContainer(c), nil 808 } 809 r1 := snapstate.MockOpenSnapFile(openSnapFile) 810 defer r1() 811 812 checkCbCalled := false 813 checkCb := func(st *state.State, s, cur *snap.Info, flags snapstate.Flags, deviceCtx snapstate.DeviceContext) error { 814 c.Assert(s.InstanceName(), Equals, "foo_instance") 815 c.Assert(s.SnapName(), Equals, "foo") 816 c.Assert(s.SnapID, Equals, "snap-id") 817 checkCbCalled = true 818 return nil 819 } 820 r2 := snapstate.MockCheckSnapCallbacks([]snapstate.CheckSnapCallback{checkCb}) 821 defer r2() 822 823 err := snapstate.CheckSnap(s.st, "snap-path", "foo_instance", si, nil, snapstate.Flags{}, nil) 824 c.Check(err, IsNil) 825 826 c.Check(checkCbCalled, Equals, true) 827 } 828 829 func (s *checkSnapSuite) TestCheckSnapCheckEpochLocal(c *C) { 830 si := &snap.SideInfo{ 831 SnapID: "snap-id", 832 } 833 834 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 835 info := snaptest.MockInfo(c, "{name: foo, version: 1.0, epoch: 13}", si) 836 return info, emptyContainer(c), nil 837 } 838 r1 := snapstate.MockOpenSnapFile(openSnapFile) 839 defer r1() 840 841 err := snapstate.CheckSnap(s.st, "snap-path", "foo", si, &snap.Info{}, snapstate.Flags{}, nil) 842 c.Check(err, ErrorMatches, `cannot refresh "foo" to local snap with epoch 13, because it can't read the current epoch of 0`) 843 } 844 845 func (s *checkSnapSuite) TestCheckSnapCheckEpochNonLocal(c *C) { 846 si := &snap.SideInfo{ 847 SnapID: "snap-id", 848 Revision: snap.R(42), 849 } 850 851 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 852 info := snaptest.MockInfo(c, "{name: foo, version: 1.0, epoch: 13}", si) 853 return info, emptyContainer(c), nil 854 } 855 r1 := snapstate.MockOpenSnapFile(openSnapFile) 856 defer r1() 857 858 err := snapstate.CheckSnap(s.st, "snap-path", "foo", si, &snap.Info{}, snapstate.Flags{}, nil) 859 c.Check(err, ErrorMatches, `cannot refresh "foo" to new revision 42 with epoch 13, because it can't read the current epoch of 0`) 860 } 861 862 func (s *checkSnapSuite) TestCheckSnapBasesCoreCanBeUsedAsCore16(c *C) { 863 st := state.New(nil) 864 st.Lock() 865 defer st.Unlock() 866 867 si := &snap.SideInfo{RealName: "core", Revision: snap.R(1), SnapID: "core-id"} 868 snaptest.MockSnap(c, ` 869 name: core 870 type: os 871 version: 1 872 `, si) 873 snapstate.Set(st, "core", &snapstate.SnapState{ 874 SnapType: "os", 875 Active: true, 876 Sequence: []*snap.SideInfo{si}, 877 Current: si.Revision, 878 }) 879 880 const yaml = `name: requires-core16 881 version: 1 882 base: core16 883 ` 884 885 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 886 c.Assert(err, IsNil) 887 888 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 889 return info, emptyContainer(c), nil 890 } 891 restore := snapstate.MockOpenSnapFile(openSnapFile) 892 defer restore() 893 894 st.Unlock() 895 err = snapstate.CheckSnap(st, "snap-path", "requires-core16", nil, nil, snapstate.Flags{}, nil) 896 st.Lock() 897 c.Check(err, IsNil) 898 } 899 900 func (s *checkSnapSuite) TestCheckSnapdHappy(c *C) { 901 st := state.New(nil) 902 st.Lock() 903 defer st.Unlock() 904 905 for _, t := range []struct { 906 yaml string 907 errStr string 908 }{ 909 {"name: snapd\nversion: 1\ntype: snapd", ""}, 910 {"name: some-snap\nversion: 1\ntype: snapd", `cannot install snap "some-snap" of type "snapd" with a name other than "snapd"`}, 911 {"name: snapd_instance\nversion: 1\ntype: snapd", `cannot install snap "snapd_instance" of type "snapd" with a name other than "snapd"`}, 912 } { 913 info, err := snap.InfoFromSnapYaml([]byte(t.yaml)) 914 c.Assert(err, IsNil) 915 916 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 917 return info, emptyContainer(c), nil 918 } 919 restore := snapstate.MockOpenSnapFile(openSnapFile) 920 defer restore() 921 922 st.Unlock() 923 err = snapstate.CheckSnap(st, "snap-path", "snapd", nil, nil, snapstate.Flags{}, nil) 924 st.Lock() 925 if t.errStr == "" { 926 c.Check(err, IsNil) 927 } else { 928 c.Check(err, ErrorMatches, t.errStr) 929 } 930 } 931 } 932 933 // Note, invalid usernames checked in snap/info_snap_yaml.go 934 var systemUsernamesTests = []struct { 935 sysIDs string 936 classic bool 937 noRangeUser bool 938 noUser bool 939 scVer string 940 error string 941 }{{ 942 sysIDs: "snap_daemon: shared", 943 scVer: "dead 2.4.1 deadbeef bpf-actlog", 944 }, { 945 sysIDs: "snap_daemon:\n scope: shared", 946 scVer: "dead 2.4.1 deadbeef bpf-actlog", 947 }, { 948 sysIDs: "snap_daemon:\n scope: private", 949 scVer: "dead 2.4.1 deadbeef bpf-actlog", 950 error: `snap "foo" requires unsupported user scope "private" for this version of snapd`, 951 }, { 952 sysIDs: "snap_daemon:\n scope: external", 953 scVer: "dead 2.4.1 deadbeef bpf-actlog", 954 error: `snap "foo" requires unsupported user scope "external" for this version of snapd`, 955 }, { 956 sysIDs: "snap_daemon:\n scope: other", 957 scVer: "dead 2.4.1 deadbeef bpf-actlog", 958 error: `snap "foo" requires unsupported user scope "other"`, 959 }, { 960 sysIDs: "snap_daemon: shared", 961 scVer: "dead 2.4.1 deadbeef bpf-actlog", 962 classic: true, 963 }, { 964 sysIDs: "snap_daemon:\n scope: shared", 965 scVer: "dead 2.4.1 deadbeef bpf-actlog", 966 classic: true, 967 }, { 968 sysIDs: "snap_daemon:\n scope: private", 969 scVer: "dead 2.4.1 deadbeef bpf-actlog", 970 classic: true, 971 error: `snap "foo" requires unsupported user scope "private" for this version of snapd`, 972 }, { 973 sysIDs: "snap_daemon:\n scope: external", 974 scVer: "dead 2.4.1 deadbeef bpf-actlog", 975 classic: true, 976 error: `snap "foo" requires unsupported user scope "external" for this version of snapd`, 977 }, { 978 sysIDs: "snap_daemon:\n scope: other", 979 scVer: "dead 2.4.1 deadbeef bpf-actlog", 980 classic: true, 981 error: `snap "foo" requires unsupported user scope "other"`, 982 }, { 983 sysIDs: "snap_daemon: shared\n allowed-not: shared", 984 scVer: "dead 2.4.1 deadbeef bpf-actlog", 985 error: `snap "foo" requires unsupported system username "allowed-not"`, 986 }, { 987 sysIDs: "allowed-not: shared\n snap_daemon: shared", 988 scVer: "dead 2.4.1 deadbeef bpf-actlog", 989 classic: true, 990 error: `snap "foo" requires unsupported system username "allowed-not"`, 991 }, { 992 sysIDs: "snap_daemon: shared", 993 noUser: true, 994 scVer: "dead 2.4.1 deadbeef bpf-actlog", 995 error: `cannot ensure users for snap "foo" required system username "snap_daemon": cannot add user/group "snap_daemon", group exists and user does not`, 996 }, { 997 sysIDs: "snap_daemon: shared", 998 classic: true, 999 noUser: true, 1000 scVer: "dead 2.4.1 deadbeef bpf-actlog", 1001 error: `cannot ensure users for snap "foo" required system username "snap_daemon": cannot add user/group "snap_daemon", group exists and user does not`, 1002 }, { 1003 sysIDs: "snap_daemon: shared", 1004 scVer: "dead 2.3.3 deadbeef bpf-actlog", 1005 error: `snap "foo" system usernames require a snapd built against libseccomp >= 2.4`, 1006 }, { 1007 sysIDs: "snap_daemon: shared", 1008 classic: true, 1009 scVer: "dead 2.3.3 deadbeef bpf-actlog", 1010 error: `snap "foo" system usernames require a snapd built against libseccomp >= 2.4`, 1011 }, { 1012 sysIDs: "snap_daemon: shared", 1013 scVer: "dead 3.0.0 deadbeef bpf-actlog", 1014 }, { 1015 sysIDs: "snap_daemon: shared", 1016 classic: true, 1017 scVer: "dead 3.0.0 deadbeef bpf-actlog", 1018 }, { 1019 sysIDs: "snap_daemon: shared", 1020 scVer: "dead 2.4.1 deadbeef -", 1021 error: `snap "foo" system usernames require a snapd built against golang-seccomp >= 0.9.1`, 1022 }, { 1023 sysIDs: "snap_daemon: shared", 1024 classic: true, 1025 scVer: "dead 2.4.1 deadbeef -", 1026 error: `snap "foo" system usernames require a snapd built against golang-seccomp >= 0.9.1`, 1027 }, { 1028 sysIDs: "snap_daemon: shared", 1029 noRangeUser: true, 1030 scVer: "dead 2.4.1 deadbeef bpf-actlog", 1031 error: `cannot ensure users for snap "foo" required system username "snap_daemon": cannot add user/group "snapd-range-524288-root", group exists and user does not`, 1032 }, { 1033 sysIDs: "snap_daemon: shared", 1034 classic: true, 1035 noRangeUser: true, 1036 scVer: "dead 2.4.1 deadbeef bpf-actlog", 1037 error: `cannot ensure users for snap "foo" required system username "snap_daemon": cannot add user/group "snapd-range-524288-root", group exists and user does not`, 1038 }, { 1039 sysIDs: "snap_daemon: shared\n daemon: shared", 1040 classic: true, 1041 scVer: "dead 2.4.1 deadbeef bpf-actlog", 1042 error: `snap "foo" requires unsupported system username "daemon"`, 1043 }, 1044 } 1045 1046 func (s *checkSnapSuite) TestCheckSnapSystemUsernames(c *C) { 1047 for _, test := range systemUsernamesTests { 1048 restore := seccomp_compiler.MockCompilerVersionInfo(test.scVer) 1049 defer restore() 1050 1051 restore = release.MockOnClassic(test.classic) 1052 defer restore() 1053 1054 var osutilEnsureUserGroupCalls int 1055 if test.noRangeUser { 1056 restore = snapstate.MockOsutilEnsureUserGroup(func(name string, id uint32, extraUsers bool) error { 1057 return fmt.Errorf(`cannot add user/group "%s", group exists and user does not`, name) 1058 }) 1059 } else if test.noUser { 1060 restore = snapstate.MockOsutilEnsureUserGroup(func(name string, id uint32, extraUsers bool) error { 1061 if name == "snapd-range-524288-root" { 1062 return nil 1063 } 1064 return fmt.Errorf(`cannot add user/group "%s", group exists and user does not`, name) 1065 }) 1066 } else { 1067 restore = snapstate.MockOsutilEnsureUserGroup(func(name string, id uint32, extraUsers bool) error { 1068 osutilEnsureUserGroupCalls++ 1069 return nil 1070 }) 1071 } 1072 defer restore() 1073 1074 yaml := fmt.Sprintf("name: foo\nsystem-usernames:\n %s\n", test.sysIDs) 1075 1076 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 1077 c.Assert(err, IsNil) 1078 1079 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 1080 return info, emptyContainer(c), nil 1081 } 1082 restore = snapstate.MockOpenSnapFile(openSnapFile) 1083 defer restore() 1084 err = snapstate.CheckSnap(s.st, "snap-path", "foo", nil, nil, snapstate.Flags{}, nil) 1085 if test.error != "" { 1086 c.Check(err, ErrorMatches, test.error) 1087 c.Check(osutilEnsureUserGroupCalls, Equals, 0) 1088 } else { 1089 c.Assert(err, IsNil) 1090 // one call for the range user, one for the system user 1091 c.Check(osutilEnsureUserGroupCalls, Equals, 2) 1092 } 1093 } 1094 } 1095 1096 func (s *checkSnapSuite) TestCheckSnapSystemUsernamesCalls(c *C) { 1097 falsePath := osutil.LookPathDefault("false", "/bin/false") 1098 for _, classic := range []bool{false, true} { 1099 restore := release.MockOnClassic(classic) 1100 defer restore() 1101 1102 restore = seccomp_compiler.MockCompilerVersionInfo("dead 2.4.1 deadbeef bpf-actlog") 1103 defer restore() 1104 1105 const yaml = `name: foo 1106 version: 1.0 1107 system-usernames: 1108 snap_daemon: shared` 1109 1110 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 1111 c.Assert(err, IsNil) 1112 1113 var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) { 1114 return info, emptyContainer(c), nil 1115 } 1116 restore = snapstate.MockOpenSnapFile(openSnapFile) 1117 defer restore() 1118 1119 mockGroupAdd := testutil.MockCommand(c, "groupadd", "") 1120 defer mockGroupAdd.Restore() 1121 1122 mockUserAdd := testutil.MockCommand(c, "useradd", "") 1123 defer mockUserAdd.Restore() 1124 1125 err = snapstate.CheckSnap(s.st, "snap-path", "foo", nil, nil, snapstate.Flags{}, nil) 1126 c.Assert(err, IsNil) 1127 if classic { 1128 c.Check(mockGroupAdd.Calls(), DeepEquals, [][]string{ 1129 {"groupadd", "--system", "--gid", "524288", "snapd-range-524288-root"}, 1130 {"groupadd", "--system", "--gid", "584788", "snap_daemon"}, 1131 }) 1132 c.Check(mockUserAdd.Calls(), DeepEquals, [][]string{ 1133 {"useradd", "--system", "--home-dir", "/nonexistent", "--no-create-home", "--shell", falsePath, "--gid", "524288", "--no-user-group", "--uid", "524288", "snapd-range-524288-root"}, 1134 {"useradd", "--system", "--home-dir", "/nonexistent", "--no-create-home", "--shell", falsePath, "--gid", "584788", "--no-user-group", "--uid", "584788", "snap_daemon"}, 1135 }) 1136 } else { 1137 c.Check(mockGroupAdd.Calls(), DeepEquals, [][]string{ 1138 {"groupadd", "--system", "--gid", "524288", "--extrausers", "snapd-range-524288-root"}, 1139 {"groupadd", "--system", "--gid", "584788", "--extrausers", "snap_daemon"}, 1140 }) 1141 c.Check(mockUserAdd.Calls(), DeepEquals, [][]string{ 1142 {"useradd", "--system", "--home-dir", "/nonexistent", "--no-create-home", "--shell", falsePath, "--gid", "524288", "--no-user-group", "--uid", "524288", "--extrausers", "snapd-range-524288-root"}, 1143 {"useradd", "--system", "--home-dir", "/nonexistent", "--no-create-home", "--shell", falsePath, "--gid", "584788", "--no-user-group", "--uid", "584788", "--extrausers", "snap_daemon"}, 1144 }) 1145 1146 } 1147 } 1148 }