github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/boot/boot_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2020 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 boot_test 21 22 import ( 23 "errors" 24 "fmt" 25 "io/ioutil" 26 "os" 27 "path/filepath" 28 "testing" 29 30 . "gopkg.in/check.v1" 31 32 "github.com/snapcore/snapd/asserts" 33 "github.com/snapcore/snapd/boot" 34 "github.com/snapcore/snapd/boot/boottest" 35 "github.com/snapcore/snapd/bootloader" 36 "github.com/snapcore/snapd/bootloader/bootloadertest" 37 "github.com/snapcore/snapd/dirs" 38 "github.com/snapcore/snapd/osutil" 39 "github.com/snapcore/snapd/secboot" 40 "github.com/snapcore/snapd/seed" 41 "github.com/snapcore/snapd/snap" 42 "github.com/snapcore/snapd/snap/snaptest" 43 "github.com/snapcore/snapd/testutil" 44 "github.com/snapcore/snapd/timings" 45 ) 46 47 func TestBoot(t *testing.T) { TestingT(t) } 48 49 type baseBootenvSuite struct { 50 testutil.BaseTest 51 52 rootdir string 53 bootdir string 54 cmdlineFile string 55 } 56 57 func (s *baseBootenvSuite) SetUpTest(c *C) { 58 s.BaseTest.SetUpTest(c) 59 60 s.rootdir = c.MkDir() 61 dirs.SetRootDir(s.rootdir) 62 s.AddCleanup(func() { dirs.SetRootDir("") }) 63 restore := snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}) 64 s.AddCleanup(restore) 65 66 s.bootdir = filepath.Join(s.rootdir, "boot") 67 68 s.cmdlineFile = filepath.Join(c.MkDir(), "cmdline") 69 restore = osutil.MockProcCmdline(s.cmdlineFile) 70 s.AddCleanup(restore) 71 } 72 73 func (s *baseBootenvSuite) forceBootloader(bloader bootloader.Bootloader) { 74 bootloader.Force(bloader) 75 s.AddCleanup(func() { bootloader.Force(nil) }) 76 } 77 78 func (s *baseBootenvSuite) stampSealedKeys(c *C, rootdir string) { 79 stamp := filepath.Join(dirs.SnapFDEDirUnder(rootdir), "sealed-keys") 80 c.Assert(os.MkdirAll(filepath.Dir(stamp), 0755), IsNil) 81 err := ioutil.WriteFile(stamp, nil, 0644) 82 c.Assert(err, IsNil) 83 } 84 85 func (s *baseBootenvSuite) mockCmdline(c *C, cmdline string) { 86 c.Assert(ioutil.WriteFile(s.cmdlineFile, []byte(cmdline), 0644), IsNil) 87 } 88 89 // mockAssetsCache mocks the listed assets in the boot assets cache by creating 90 // an empty file for each. 91 func mockAssetsCache(c *C, rootdir, bootloaderName string, cachedAssets []string) { 92 p := filepath.Join(dirs.SnapBootAssetsDirUnder(rootdir), bootloaderName) 93 err := os.MkdirAll(p, 0755) 94 c.Assert(err, IsNil) 95 for _, cachedAsset := range cachedAssets { 96 err = ioutil.WriteFile(filepath.Join(p, cachedAsset), nil, 0644) 97 c.Assert(err, IsNil) 98 } 99 } 100 101 type bootenvSuite struct { 102 baseBootenvSuite 103 104 bootloader *bootloadertest.MockBootloader 105 } 106 107 var _ = Suite(&bootenvSuite{}) 108 109 func (s *bootenvSuite) SetUpTest(c *C) { 110 s.baseBootenvSuite.SetUpTest(c) 111 112 s.bootloader = bootloadertest.Mock("mock", c.MkDir()) 113 s.forceBootloader(s.bootloader) 114 } 115 116 type baseBootenv20Suite struct { 117 baseBootenvSuite 118 119 kern1 snap.PlaceInfo 120 kern2 snap.PlaceInfo 121 ukern1 snap.PlaceInfo 122 ukern2 snap.PlaceInfo 123 base1 snap.PlaceInfo 124 base2 snap.PlaceInfo 125 126 normalDefaultState *bootenv20Setup 127 normalTryingKernelState *bootenv20Setup 128 } 129 130 func (s *baseBootenv20Suite) SetUpTest(c *C) { 131 s.baseBootenvSuite.SetUpTest(c) 132 133 var err error 134 s.kern1, err = snap.ParsePlaceInfoFromSnapFileName("pc-kernel_1.snap") 135 c.Assert(err, IsNil) 136 s.kern2, err = snap.ParsePlaceInfoFromSnapFileName("pc-kernel_2.snap") 137 c.Assert(err, IsNil) 138 139 s.ukern1, err = snap.ParsePlaceInfoFromSnapFileName("pc-kernel_x1.snap") 140 c.Assert(err, IsNil) 141 s.ukern2, err = snap.ParsePlaceInfoFromSnapFileName("pc-kernel_x2.snap") 142 c.Assert(err, IsNil) 143 144 s.base1, err = snap.ParsePlaceInfoFromSnapFileName("core20_1.snap") 145 c.Assert(err, IsNil) 146 s.base2, err = snap.ParsePlaceInfoFromSnapFileName("core20_2.snap") 147 c.Assert(err, IsNil) 148 149 // default boot state for robustness tests, etc. 150 s.normalDefaultState = &bootenv20Setup{ 151 modeenv: &boot.Modeenv{ 152 // base is base1 153 Base: s.base1.Filename(), 154 // no try base 155 TryBase: "", 156 // base status is default 157 BaseStatus: boot.DefaultStatus, 158 // current kernels is just kern1 159 CurrentKernels: []string{s.kern1.Filename()}, 160 // operating mode is run 161 Mode: "run", 162 // RecoverySystem is unset, as it should be during run mode 163 RecoverySystem: "", 164 }, 165 // enabled kernel is kern1 166 kern: s.kern1, 167 // no try kernel enabled 168 tryKern: nil, 169 // kernel status is default 170 kernStatus: boot.DefaultStatus, 171 } 172 173 // state for after trying a new kernel for robustness tests, etc. 174 s.normalTryingKernelState = &bootenv20Setup{ 175 modeenv: &boot.Modeenv{ 176 // operating mode is run 177 Mode: "run", 178 // base is base1 179 Base: s.base1.Filename(), 180 // no try base 181 TryBase: "", 182 // base status is default 183 BaseStatus: boot.DefaultStatus, 184 // current kernels is kern1 + kern2 185 CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()}, 186 }, 187 // enabled kernel is kern1 188 kern: s.kern1, 189 // try kernel is kern2 190 tryKern: s.kern2, 191 // kernel status is trying 192 kernStatus: boot.TryingStatus, 193 } 194 195 s.mockCmdline(c, "snapd_recovery_mode=run") 196 } 197 198 type bootenv20Suite struct { 199 baseBootenv20Suite 200 201 bootloader *bootloadertest.MockExtractedRunKernelImageBootloader 202 } 203 204 type bootenv20EnvRefKernelSuite struct { 205 baseBootenv20Suite 206 207 bootloader *bootloadertest.MockBootloader 208 } 209 210 var defaultUC20BootEnv = map[string]string{"kernel_status": boot.DefaultStatus} 211 212 var _ = Suite(&bootenv20Suite{}) 213 var _ = Suite(&bootenv20EnvRefKernelSuite{}) 214 215 func (s *bootenv20Suite) SetUpTest(c *C) { 216 s.baseBootenv20Suite.SetUpTest(c) 217 218 s.bootloader = bootloadertest.Mock("mock", c.MkDir()).WithExtractedRunKernelImage() 219 s.forceBootloader(s.bootloader) 220 } 221 222 func (s *bootenv20EnvRefKernelSuite) SetUpTest(c *C) { 223 s.baseBootenv20Suite.SetUpTest(c) 224 225 s.bootloader = bootloadertest.Mock("mock", c.MkDir()) 226 s.forceBootloader(s.bootloader) 227 } 228 229 type bootenv20Setup struct { 230 modeenv *boot.Modeenv 231 kern snap.PlaceInfo 232 tryKern snap.PlaceInfo 233 kernStatus string 234 } 235 236 func setupUC20Bootenv(c *C, bl bootloader.Bootloader, opts *bootenv20Setup) (restore func()) { 237 var cleanups []func() 238 239 // write the modeenv 240 if opts.modeenv != nil { 241 c.Assert(opts.modeenv.WriteTo(""), IsNil) 242 // this isn't strictly necessary since the modeenv will be written to 243 // the test's private dir anyways, but it's nice to have so we can write 244 // multiple modeenvs from a single test and just call the restore 245 // function in between the parts of the test that use different modeenvs 246 r := func() { 247 defaultModeenv := &boot.Modeenv{Mode: "run"} 248 c.Assert(defaultModeenv.WriteTo(""), IsNil) 249 } 250 cleanups = append(cleanups, r) 251 } 252 253 // set the status 254 origEnv, err := bl.GetBootVars("kernel_status") 255 c.Assert(err, IsNil) 256 257 err = bl.SetBootVars(map[string]string{"kernel_status": opts.kernStatus}) 258 c.Assert(err, IsNil) 259 cleanups = append(cleanups, func() { 260 err := bl.SetBootVars(origEnv) 261 c.Assert(err, IsNil) 262 }) 263 264 // check what kind of real mock bootloader we have to use different methods 265 // to set the kernel snaps are if they're non-nil 266 switch vbl := bl.(type) { 267 case *bootloadertest.MockExtractedRunKernelImageBootloader: 268 // then we can use the advanced methods on it 269 if opts.kern != nil { 270 r := vbl.SetEnabledKernel(opts.kern) 271 cleanups = append(cleanups, r) 272 } 273 274 if opts.tryKern != nil { 275 r := vbl.SetEnabledTryKernel(opts.tryKern) 276 cleanups = append(cleanups, r) 277 } 278 279 // don't count any calls to SetBootVars made thus far 280 vbl.SetBootVarsCalls = 0 281 282 case *bootloadertest.MockBootloader: 283 // then we need to use the bootenv to set the current kernels 284 origEnv, err := vbl.GetBootVars("snap_kernel", "snap_try_kernel") 285 c.Assert(err, IsNil) 286 m := make(map[string]string, 2) 287 if opts.kern != nil { 288 m["snap_kernel"] = opts.kern.Filename() 289 } else { 290 m["snap_kernel"] = "" 291 } 292 293 if opts.tryKern != nil { 294 m["snap_try_kernel"] = opts.tryKern.Filename() 295 } else { 296 m["snap_try_kernel"] = "" 297 } 298 299 err = vbl.SetBootVars(m) 300 c.Assert(err, IsNil) 301 302 // don't count any calls to SetBootVars made thus far 303 vbl.SetBootVarsCalls = 0 304 305 cleanups = append(cleanups, func() { 306 err := bl.SetBootVars(origEnv) 307 c.Assert(err, IsNil) 308 }) 309 default: 310 c.Fatalf("unsupported bootloader %T", bl) 311 } 312 313 return func() { 314 for _, r := range cleanups { 315 r() 316 } 317 } 318 } 319 320 func (s *bootenvSuite) TestInUseClassic(c *C) { 321 classicDev := boottest.MockDevice("") 322 323 // make bootloader.Find fail but shouldn't matter 324 bootloader.ForceError(errors.New("broken bootloader")) 325 326 inUse, err := boot.InUse(snap.TypeBase, classicDev) 327 c.Assert(err, IsNil) 328 c.Check(inUse("core18", snap.R(41)), Equals, false) 329 } 330 331 func (s *bootenvSuite) TestInUseIrrelevantTypes(c *C) { 332 coreDev := boottest.MockDevice("some-snap") 333 334 // make bootloader.Find fail but shouldn't matter 335 bootloader.ForceError(errors.New("broken bootloader")) 336 337 inUse, err := boot.InUse(snap.TypeGadget, coreDev) 338 c.Assert(err, IsNil) 339 c.Check(inUse("gadget", snap.R(41)), Equals, false) 340 } 341 342 func (s *bootenvSuite) TestInUse(c *C) { 343 coreDev := boottest.MockDevice("some-snap") 344 345 for _, t := range []struct { 346 bootVarKey string 347 bootVarValue string 348 349 snapName string 350 snapRev snap.Revision 351 352 inUse bool 353 }{ 354 // in use 355 {"snap_kernel", "kernel_41.snap", "kernel", snap.R(41), true}, 356 {"snap_try_kernel", "kernel_82.snap", "kernel", snap.R(82), true}, 357 {"snap_core", "core_21.snap", "core", snap.R(21), true}, 358 {"snap_try_core", "core_42.snap", "core", snap.R(42), true}, 359 // not in use 360 {"snap_core", "core_111.snap", "core", snap.R(21), false}, 361 {"snap_try_core", "core_111.snap", "core", snap.R(21), false}, 362 {"snap_kernel", "kernel_111.snap", "kernel", snap.R(1), false}, 363 {"snap_try_kernel", "kernel_111.snap", "kernel", snap.R(1), false}, 364 } { 365 typ := snap.TypeBase 366 if t.snapName == "kernel" { 367 typ = snap.TypeKernel 368 } 369 s.bootloader.BootVars[t.bootVarKey] = t.bootVarValue 370 inUse, err := boot.InUse(typ, coreDev) 371 c.Assert(err, IsNil) 372 c.Assert(inUse(t.snapName, t.snapRev), Equals, t.inUse, Commentf("unexpected result: %s %s %v", t.snapName, t.snapRev, t.inUse)) 373 } 374 } 375 376 func (s *bootenvSuite) TestInUseEphemeral(c *C) { 377 coreDev := boottest.MockDevice("some-snap@install") 378 379 // make bootloader.Find fail but shouldn't matter 380 bootloader.ForceError(errors.New("broken bootloader")) 381 382 inUse, err := boot.InUse(snap.TypeBase, coreDev) 383 c.Assert(err, IsNil) 384 c.Check(inUse("whatever", snap.R(0)), Equals, true) 385 } 386 387 func (s *bootenvSuite) TestInUseUnhappy(c *C) { 388 coreDev := boottest.MockDevice("some-snap") 389 390 // make GetVars fail 391 s.bootloader.GetErr = errors.New("zap") 392 _, err := boot.InUse(snap.TypeKernel, coreDev) 393 c.Check(err, ErrorMatches, `cannot get boot variables: zap`) 394 395 // make bootloader.Find fail 396 bootloader.ForceError(errors.New("broken bootloader")) 397 _, err = boot.InUse(snap.TypeKernel, coreDev) 398 c.Check(err, ErrorMatches, `cannot get boot settings: broken bootloader`) 399 } 400 401 func (s *bootenvSuite) TestCurrentBootNameAndRevision(c *C) { 402 coreDev := boottest.MockDevice("some-snap") 403 404 s.bootloader.BootVars["snap_core"] = "core_2.snap" 405 s.bootloader.BootVars["snap_kernel"] = "canonical-pc-linux_2.snap" 406 407 current, err := boot.GetCurrentBoot(snap.TypeOS, coreDev) 408 c.Check(err, IsNil) 409 c.Check(current.SnapName(), Equals, "core") 410 c.Check(current.SnapRevision(), Equals, snap.R(2)) 411 412 current, err = boot.GetCurrentBoot(snap.TypeKernel, coreDev) 413 c.Check(err, IsNil) 414 c.Check(current.SnapName(), Equals, "canonical-pc-linux") 415 c.Check(current.SnapRevision(), Equals, snap.R(2)) 416 417 s.bootloader.BootVars["snap_mode"] = boot.TryingStatus 418 _, err = boot.GetCurrentBoot(snap.TypeKernel, coreDev) 419 c.Check(err, Equals, boot.ErrBootNameAndRevisionNotReady) 420 } 421 422 func (s *bootenv20Suite) TestCurrentBoot20NameAndRevision(c *C) { 423 coreDev := boottest.MockUC20Device("", nil) 424 c.Assert(coreDev.HasModeenv(), Equals, true) 425 426 r := setupUC20Bootenv( 427 c, 428 s.bootloader, 429 s.normalDefaultState, 430 ) 431 defer r() 432 433 current, err := boot.GetCurrentBoot(snap.TypeBase, coreDev) 434 c.Check(err, IsNil) 435 c.Check(current.SnapName(), Equals, s.base1.SnapName()) 436 c.Check(current.SnapRevision(), Equals, snap.R(1)) 437 438 current, err = boot.GetCurrentBoot(snap.TypeKernel, coreDev) 439 c.Check(err, IsNil) 440 c.Check(current.SnapName(), Equals, s.kern1.SnapName()) 441 c.Check(current.SnapRevision(), Equals, snap.R(1)) 442 443 s.bootloader.BootVars["kernel_status"] = boot.TryingStatus 444 _, err = boot.GetCurrentBoot(snap.TypeKernel, coreDev) 445 c.Check(err, Equals, boot.ErrBootNameAndRevisionNotReady) 446 } 447 448 // only difference between this test and TestCurrentBoot20NameAndRevision is the 449 // base bootloader which doesn't support ExtractedRunKernelImageBootloader. 450 func (s *bootenv20EnvRefKernelSuite) TestCurrentBoot20NameAndRevision(c *C) { 451 coreDev := boottest.MockUC20Device("", nil) 452 c.Assert(coreDev.HasModeenv(), Equals, true) 453 454 r := setupUC20Bootenv( 455 c, 456 s.bootloader, 457 s.normalDefaultState, 458 ) 459 defer r() 460 461 current, err := boot.GetCurrentBoot(snap.TypeKernel, coreDev) 462 c.Assert(err, IsNil) 463 c.Assert(current.SnapName(), Equals, s.kern1.SnapName()) 464 c.Assert(current.SnapRevision(), Equals, snap.R(1)) 465 } 466 467 func (s *bootenvSuite) TestCurrentBootNameAndRevisionUnhappy(c *C) { 468 coreDev := boottest.MockDevice("some-snap") 469 470 _, err := boot.GetCurrentBoot(snap.TypeKernel, coreDev) 471 c.Check(err, ErrorMatches, `cannot get name and revision of kernel \(snap_kernel\): boot variable unset`) 472 473 _, err = boot.GetCurrentBoot(snap.TypeOS, coreDev) 474 c.Check(err, ErrorMatches, `cannot get name and revision of boot base \(snap_core\): boot variable unset`) 475 476 _, err = boot.GetCurrentBoot(snap.TypeBase, coreDev) 477 c.Check(err, ErrorMatches, `cannot get name and revision of boot base \(snap_core\): boot variable unset`) 478 479 _, err = boot.GetCurrentBoot(snap.TypeApp, coreDev) 480 c.Check(err, ErrorMatches, `internal error: no boot state handling for snap type "app"`) 481 482 // sanity check 483 s.bootloader.BootVars["snap_kernel"] = "kernel_41.snap" 484 current, err := boot.GetCurrentBoot(snap.TypeKernel, coreDev) 485 c.Check(err, IsNil) 486 c.Check(current.SnapName(), Equals, "kernel") 487 c.Check(current.SnapRevision(), Equals, snap.R(41)) 488 489 // make GetVars fail 490 s.bootloader.GetErr = errors.New("zap") 491 _, err = boot.GetCurrentBoot(snap.TypeKernel, coreDev) 492 c.Check(err, ErrorMatches, "cannot get boot variables: zap") 493 s.bootloader.GetErr = nil 494 495 // make bootloader.Find fail 496 bootloader.ForceError(errors.New("broken bootloader")) 497 _, err = boot.GetCurrentBoot(snap.TypeKernel, coreDev) 498 c.Check(err, ErrorMatches, "cannot get boot settings: broken bootloader") 499 } 500 501 func (s *bootenvSuite) TestParticipant(c *C) { 502 info := &snap.Info{} 503 info.RealName = "some-snap" 504 505 coreDev := boottest.MockDevice("some-snap") 506 classicDev := boottest.MockDevice("") 507 508 bp := boot.Participant(info, snap.TypeApp, coreDev) 509 c.Check(bp.IsTrivial(), Equals, true) 510 511 for _, typ := range []snap.Type{ 512 snap.TypeKernel, 513 snap.TypeOS, 514 snap.TypeBase, 515 } { 516 bp = boot.Participant(info, typ, classicDev) 517 c.Check(bp.IsTrivial(), Equals, true) 518 519 bp = boot.Participant(info, typ, coreDev) 520 c.Check(bp.IsTrivial(), Equals, false) 521 522 c.Check(bp, DeepEquals, boot.NewCoreBootParticipant(info, typ, coreDev)) 523 } 524 } 525 526 func (s *bootenvSuite) TestParticipantBaseWithModel(c *C) { 527 core := &snap.Info{SideInfo: snap.SideInfo{RealName: "core"}, SnapType: snap.TypeOS} 528 core18 := &snap.Info{SideInfo: snap.SideInfo{RealName: "core18"}, SnapType: snap.TypeBase} 529 530 type tableT struct { 531 with *snap.Info 532 model string 533 nop bool 534 } 535 536 table := []tableT{ 537 { 538 with: core, 539 model: "", 540 nop: true, 541 }, { 542 with: core, 543 model: "core", 544 nop: false, 545 }, { 546 with: core, 547 model: "core18", 548 nop: true, 549 }, 550 { 551 with: core18, 552 model: "", 553 nop: true, 554 }, 555 { 556 with: core18, 557 model: "core", 558 nop: true, 559 }, 560 { 561 with: core18, 562 model: "core18", 563 nop: false, 564 }, 565 { 566 with: core18, 567 model: "core18@install", 568 nop: true, 569 }, 570 { 571 with: core, 572 model: "core@install", 573 nop: true, 574 }, 575 } 576 577 for i, t := range table { 578 dev := boottest.MockDevice(t.model) 579 bp := boot.Participant(t.with, t.with.Type(), dev) 580 c.Check(bp.IsTrivial(), Equals, t.nop, Commentf("%d", i)) 581 if !t.nop { 582 c.Check(bp, DeepEquals, boot.NewCoreBootParticipant(t.with, t.with.Type(), dev)) 583 } 584 } 585 } 586 587 func (s *bootenvSuite) TestKernelWithModel(c *C) { 588 info := &snap.Info{} 589 info.RealName = "kernel" 590 591 type tableT struct { 592 model string 593 nop bool 594 krn boot.BootKernel 595 } 596 597 table := []tableT{ 598 { 599 model: "other-kernel", 600 nop: true, 601 krn: boot.Trivial{}, 602 }, { 603 model: "kernel", 604 nop: false, 605 krn: boot.NewCoreKernel(info, boottest.MockDevice("kernel")), 606 }, { 607 model: "", 608 nop: true, 609 krn: boot.Trivial{}, 610 }, { 611 model: "kernel@install", 612 nop: true, 613 krn: boot.Trivial{}, 614 }, 615 } 616 617 for _, t := range table { 618 dev := boottest.MockDevice(t.model) 619 krn := boot.Kernel(info, snap.TypeKernel, dev) 620 c.Check(krn.IsTrivial(), Equals, t.nop) 621 c.Check(krn, DeepEquals, t.krn) 622 } 623 } 624 625 func (s *bootenvSuite) TestMarkBootSuccessfulKernelStatusTryingNoTryKernelSnapCleansUp(c *C) { 626 coreDev := boottest.MockDevice("some-snap") 627 628 // set all the same vars as if we were doing trying, except don't set a try 629 // kernel 630 631 err := s.bootloader.SetBootVars(map[string]string{ 632 "snap_kernel": "kernel_41.snap", 633 "snap_mode": boot.TryingStatus, 634 }) 635 c.Assert(err, IsNil) 636 637 // mark successful 638 err = boot.MarkBootSuccessful(coreDev) 639 c.Assert(err, IsNil) 640 641 // check that the bootloader variables were cleaned 642 expected := map[string]string{ 643 "snap_mode": boot.DefaultStatus, 644 "snap_kernel": "kernel_41.snap", 645 "snap_try_kernel": "", 646 } 647 m, err := s.bootloader.GetBootVars("snap_mode", "snap_try_kernel", "snap_kernel") 648 c.Assert(err, IsNil) 649 c.Assert(m, DeepEquals, expected) 650 651 // do it again, verify it's still okay 652 err = boot.MarkBootSuccessful(coreDev) 653 c.Assert(err, IsNil) 654 m2, err := s.bootloader.GetBootVars("snap_mode", "snap_try_kernel", "snap_kernel") 655 c.Assert(err, IsNil) 656 c.Assert(m2, DeepEquals, expected) 657 } 658 659 func (s *bootenvSuite) TestMarkBootSuccessfulTryKernelKernelStatusDefaultCleansUp(c *C) { 660 coreDev := boottest.MockDevice("some-snap") 661 662 // set an errant snap_try_kernel 663 err := s.bootloader.SetBootVars(map[string]string{ 664 "snap_kernel": "kernel_41.snap", 665 "snap_try_kernel": "kernel_42.snap", 666 "snap_mode": boot.DefaultStatus, 667 }) 668 c.Assert(err, IsNil) 669 670 // mark successful 671 err = boot.MarkBootSuccessful(coreDev) 672 c.Assert(err, IsNil) 673 674 // check that the bootloader variables were cleaned 675 expected := map[string]string{ 676 "snap_mode": boot.DefaultStatus, 677 "snap_kernel": "kernel_41.snap", 678 "snap_try_kernel": "", 679 } 680 m, err := s.bootloader.GetBootVars("snap_mode", "snap_try_kernel", "snap_kernel") 681 c.Assert(err, IsNil) 682 c.Assert(m, DeepEquals, expected) 683 684 // do it again, verify it's still okay 685 err = boot.MarkBootSuccessful(coreDev) 686 c.Assert(err, IsNil) 687 m2, err := s.bootloader.GetBootVars("snap_mode", "snap_try_kernel", "snap_kernel") 688 c.Assert(err, IsNil) 689 c.Assert(m2, DeepEquals, expected) 690 } 691 692 func (s *bootenv20Suite) TestCoreKernel20(c *C) { 693 coreDev := boottest.MockUC20Device("", nil) 694 c.Assert(coreDev.HasModeenv(), Equals, true) 695 696 r := setupUC20Bootenv( 697 c, 698 s.bootloader, 699 s.normalDefaultState, 700 ) 701 defer r() 702 703 // get the boot kernel from our kernel snap 704 bootKern := boot.Kernel(s.kern1, snap.TypeKernel, coreDev) 705 // can't use FitsTypeOf with coreKernel here, cause that causes an import 706 // loop as boottest imports boot and coreKernel is unexported 707 c.Assert(bootKern.IsTrivial(), Equals, false) 708 709 // extract the kernel assets from the coreKernel 710 // the container here doesn't really matter since it's just being passed 711 // to the mock bootloader method anyways 712 kernelContainer := snaptest.MockContainer(c, nil) 713 err := bootKern.ExtractKernelAssets(kernelContainer) 714 c.Assert(err, IsNil) 715 716 // make sure that the bootloader was told to extract some assets 717 c.Assert(s.bootloader.ExtractKernelAssetsCalls, DeepEquals, []snap.PlaceInfo{s.kern1}) 718 719 // now remove the kernel assets and ensure that we get those calls 720 err = bootKern.RemoveKernelAssets() 721 c.Assert(err, IsNil) 722 723 // make sure that the bootloader was told to remove assets 724 c.Assert(s.bootloader.RemoveKernelAssetsCalls, DeepEquals, []snap.PlaceInfo{s.kern1}) 725 } 726 727 func (s *bootenv20Suite) TestCoreParticipant20SetNextSameKernelSnap(c *C) { 728 coreDev := boottest.MockUC20Device("", nil) 729 c.Assert(coreDev.HasModeenv(), Equals, true) 730 731 r := setupUC20Bootenv( 732 c, 733 s.bootloader, 734 s.normalDefaultState, 735 ) 736 defer r() 737 738 // get the boot kernel participant from our kernel snap 739 bootKern := boot.Participant(s.kern1, snap.TypeKernel, coreDev) 740 741 // make sure it's not a trivial boot participant 742 c.Assert(bootKern.IsTrivial(), Equals, false) 743 744 // make the kernel used on next boot 745 rebootRequired, err := bootKern.SetNextBoot() 746 c.Assert(err, IsNil) 747 c.Assert(rebootRequired, Equals, false) 748 749 // make sure that the bootloader was asked for the current kernel 750 _, nKernelCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("Kernel") 751 c.Assert(nKernelCalls, Equals, 1) 752 753 // ensure that kernel_status is still empty 754 c.Assert(s.bootloader.BootVars["kernel_status"], Equals, boot.DefaultStatus) 755 756 // there was no attempt to enable a kernel 757 _, enableKernelCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableTryKernel") 758 c.Assert(enableKernelCalls, Equals, 0) 759 760 // the modeenv is still the same as well 761 m2, err := boot.ReadModeenv("") 762 c.Assert(err, IsNil) 763 c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename()}) 764 765 // finally we didn't call SetBootVars on the bootloader because nothing 766 // changed 767 c.Assert(s.bootloader.SetBootVarsCalls, Equals, 0) 768 } 769 770 func (s *bootenv20EnvRefKernelSuite) TestCoreParticipant20SetNextSameKernelSnap(c *C) { 771 coreDev := boottest.MockUC20Device("", nil) 772 c.Assert(coreDev.HasModeenv(), Equals, true) 773 774 r := setupUC20Bootenv( 775 c, 776 s.bootloader, 777 s.normalDefaultState, 778 ) 779 defer r() 780 781 // get the boot kernel participant from our kernel snap 782 bootKern := boot.Participant(s.kern1, snap.TypeKernel, coreDev) 783 784 // make sure it's not a trivial boot participant 785 c.Assert(bootKern.IsTrivial(), Equals, false) 786 787 // make the kernel used on next boot 788 rebootRequired, err := bootKern.SetNextBoot() 789 c.Assert(err, IsNil) 790 c.Assert(rebootRequired, Equals, false) 791 792 // ensure that bootenv is unchanged 793 m, err := s.bootloader.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel") 794 c.Assert(err, IsNil) 795 c.Assert(m, DeepEquals, map[string]string{ 796 "kernel_status": boot.DefaultStatus, 797 "snap_kernel": s.kern1.Filename(), 798 "snap_try_kernel": "", 799 }) 800 801 // the modeenv is still the same as well 802 m2, err := boot.ReadModeenv("") 803 c.Assert(err, IsNil) 804 c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename()}) 805 806 // finally we didn't call SetBootVars on the bootloader because nothing 807 // changed 808 c.Assert(s.bootloader.SetBootVarsCalls, Equals, 0) 809 } 810 811 func (s *bootenv20Suite) TestCoreParticipant20SetNextNewKernelSnap(c *C) { 812 coreDev := boottest.MockUC20Device("", nil) 813 c.Assert(coreDev.HasModeenv(), Equals, true) 814 815 r := setupUC20Bootenv( 816 c, 817 s.bootloader, 818 s.normalDefaultState, 819 ) 820 defer r() 821 822 // get the boot kernel participant from our new kernel snap 823 bootKern := boot.Participant(s.kern2, snap.TypeKernel, coreDev) 824 // make sure it's not a trivial boot participant 825 c.Assert(bootKern.IsTrivial(), Equals, false) 826 827 // make the kernel used on next boot 828 rebootRequired, err := bootKern.SetNextBoot() 829 c.Assert(err, IsNil) 830 c.Assert(rebootRequired, Equals, true) 831 832 // make sure that the bootloader was asked for the current kernel 833 _, nKernelCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("Kernel") 834 c.Assert(nKernelCalls, Equals, 1) 835 836 // ensure that kernel_status is now try 837 c.Assert(s.bootloader.BootVars["kernel_status"], Equals, boot.TryStatus) 838 839 // and we were asked to enable kernel2 as the try kernel 840 actual, _ := s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableTryKernel") 841 c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2}) 842 843 // and that the modeenv now has this kernel listed 844 m2, err := boot.ReadModeenv("") 845 c.Assert(err, IsNil) 846 c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename(), s.kern2.Filename()}) 847 } 848 849 func (s *bootenv20Suite) TestCoreParticipant20SetNextNewKernelSnapWithReseal(c *C) { 850 // checked by resealKeyToModeenv 851 s.stampSealedKeys(c, dirs.GlobalRootDir) 852 853 tab := s.bootloaderWithTrustedAssets(c, []string{"asset"}) 854 855 data := []byte("foobar") 856 // SHA3-384 857 dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8" 858 859 c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil) 860 c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil) 861 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil) 862 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil) 863 864 // mock the files in cache 865 mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{ 866 "asset-" + dataHash, 867 }) 868 869 assetBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("asset-%s", dataHash)), bootloader.RoleRunMode) 870 runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode) 871 872 tab.BootChainList = []bootloader.BootFile{ 873 bootloader.NewBootFile("", "asset", bootloader.RoleRunMode), 874 // TODO:UC20: fix mocked trusted assets bootloader to actually 875 // geenerate kernel boot files 876 runKernelBf, 877 } 878 879 coreDev := boottest.MockUC20Device("", nil) 880 c.Assert(coreDev.HasModeenv(), Equals, true) 881 882 m := &boot.Modeenv{ 883 Mode: "run", 884 Base: s.base1.Filename(), 885 CurrentKernels: []string{s.kern1.Filename()}, 886 CurrentTrustedBootAssets: boot.BootAssetsMap{ 887 "asset": {dataHash}, 888 }, 889 } 890 891 r := setupUC20Bootenv( 892 c, 893 tab.MockBootloader, 894 &bootenv20Setup{ 895 modeenv: m, 896 kern: s.kern1, 897 kernStatus: boot.DefaultStatus, 898 }, 899 ) 900 defer r() 901 902 resealCalls := 0 903 restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { 904 resealCalls++ 905 906 c.Assert(params.ModelParams, HasLen, 1) 907 mp := params.ModelParams[0] 908 c.Check(mp.Model, DeepEquals, coreDev.Model()) 909 for _, ch := range mp.EFILoadChains { 910 printChain(c, ch, "-") 911 } 912 c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{ 913 secboot.NewLoadChain(assetBf, 914 secboot.NewLoadChain(runKernelBf)), 915 secboot.NewLoadChain(assetBf, 916 // TODO:UC20: once mock trusted assets 917 // bootloader can generated boot files for the 918 // kernel this will use candidate kernel 919 secboot.NewLoadChain(runKernelBf)), 920 }) 921 // actual paths are seen only here 922 c.Check(tab.BootChainKernelPath, DeepEquals, []string{ 923 s.kern1.MountFile(), 924 s.kern2.MountFile(), 925 }) 926 return nil 927 }) 928 defer restore() 929 930 // get the boot kernel participant from our new kernel snap 931 bootKern := boot.Participant(s.kern2, snap.TypeKernel, coreDev) 932 // make sure it's not a trivial boot participant 933 c.Assert(bootKern.IsTrivial(), Equals, false) 934 935 // make the kernel used on next boot 936 rebootRequired, err := bootKern.SetNextBoot() 937 c.Assert(err, IsNil) 938 c.Assert(rebootRequired, Equals, true) 939 940 // make sure the env was updated 941 bvars, err := tab.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel") 942 c.Assert(err, IsNil) 943 c.Assert(bvars, DeepEquals, map[string]string{ 944 "kernel_status": boot.TryStatus, 945 "snap_kernel": s.kern1.Filename(), 946 "snap_try_kernel": s.kern2.Filename(), 947 }) 948 949 // and that the modeenv now has this kernel listed 950 m2, err := boot.ReadModeenv("") 951 c.Assert(err, IsNil) 952 c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename(), s.kern2.Filename()}) 953 954 c.Check(resealCalls, Equals, 1) 955 } 956 957 func (s *bootenv20Suite) TestCoreParticipant20SetNextNewUnassertedKernelSnapWithReseal(c *C) { 958 // checked by resealKeyToModeenv 959 s.stampSealedKeys(c, dirs.GlobalRootDir) 960 961 tab := s.bootloaderWithTrustedAssets(c, []string{"asset"}) 962 963 data := []byte("foobar") 964 // SHA3-384 965 dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8" 966 967 c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil) 968 c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil) 969 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil) 970 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil) 971 972 // mock the files in cache 973 mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{ 974 "asset-" + dataHash, 975 }) 976 977 assetBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("asset-%s", dataHash)), bootloader.RoleRunMode) 978 runKernelBf := bootloader.NewBootFile(filepath.Join(s.ukern1.Filename()), "kernel.efi", bootloader.RoleRunMode) 979 980 tab.BootChainList = []bootloader.BootFile{ 981 bootloader.NewBootFile("", "asset", bootloader.RoleRunMode), 982 // TODO:UC20: fix mocked trusted assets bootloader to actually 983 // geenerate kernel boot files 984 runKernelBf, 985 } 986 987 uc20Model := boottest.MakeMockUC20Model() 988 coreDev := boottest.MockUC20Device("", uc20Model) 989 c.Assert(coreDev.HasModeenv(), Equals, true) 990 991 m := &boot.Modeenv{ 992 Mode: "run", 993 Base: s.base1.Filename(), 994 CurrentKernels: []string{s.ukern1.Filename()}, 995 CurrentTrustedBootAssets: boot.BootAssetsMap{ 996 "asset": {dataHash}, 997 }, 998 } 999 1000 r := setupUC20Bootenv( 1001 c, 1002 tab.MockBootloader, 1003 &bootenv20Setup{ 1004 modeenv: m, 1005 kern: s.ukern1, 1006 kernStatus: boot.DefaultStatus, 1007 }, 1008 ) 1009 defer r() 1010 1011 resealCalls := 0 1012 restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { 1013 resealCalls++ 1014 1015 c.Assert(params.ModelParams, HasLen, 1) 1016 mp := params.ModelParams[0] 1017 c.Check(mp.Model, DeepEquals, uc20Model) 1018 for _, ch := range mp.EFILoadChains { 1019 printChain(c, ch, "-") 1020 } 1021 c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{ 1022 secboot.NewLoadChain(assetBf, 1023 secboot.NewLoadChain(runKernelBf)), 1024 secboot.NewLoadChain(assetBf, 1025 // TODO:UC20: once mock trusted assets 1026 // bootloader can generated boot files for the 1027 // kernel this will use candidate kernel 1028 secboot.NewLoadChain(runKernelBf)), 1029 }) 1030 // actual paths are seen only here 1031 c.Check(tab.BootChainKernelPath, DeepEquals, []string{ 1032 s.ukern1.MountFile(), 1033 s.ukern2.MountFile(), 1034 }) 1035 return nil 1036 }) 1037 defer restore() 1038 1039 // get the boot kernel participant from our new kernel snap 1040 bootKern := boot.Participant(s.ukern2, snap.TypeKernel, coreDev) 1041 // make sure it's not a trivial boot participant 1042 c.Assert(bootKern.IsTrivial(), Equals, false) 1043 1044 // make the kernel used on next boot 1045 rebootRequired, err := bootKern.SetNextBoot() 1046 c.Assert(err, IsNil) 1047 c.Assert(rebootRequired, Equals, true) 1048 1049 bvars, err := tab.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel") 1050 c.Assert(err, IsNil) 1051 c.Assert(bvars, DeepEquals, map[string]string{ 1052 "kernel_status": boot.TryStatus, 1053 "snap_kernel": s.ukern1.Filename(), 1054 "snap_try_kernel": s.ukern2.Filename(), 1055 }) 1056 1057 // and that the modeenv now has this kernel listed 1058 m2, err := boot.ReadModeenv("") 1059 c.Assert(err, IsNil) 1060 c.Assert(m2.CurrentKernels, DeepEquals, []string{s.ukern1.Filename(), s.ukern2.Filename()}) 1061 1062 c.Check(resealCalls, Equals, 1) 1063 } 1064 1065 func (s *bootenv20Suite) TestCoreParticipant20SetNextSameKernelSnapNoReseal(c *C) { 1066 // checked by resealKeyToModeenv 1067 s.stampSealedKeys(c, dirs.GlobalRootDir) 1068 1069 tab := s.bootloaderWithTrustedAssets(c, []string{"asset"}) 1070 1071 data := []byte("foobar") 1072 // SHA3-384 1073 dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8" 1074 1075 c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil) 1076 c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil) 1077 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil) 1078 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil) 1079 1080 // mock the files in cache 1081 mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{ 1082 "asset-" + dataHash, 1083 }) 1084 1085 runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode) 1086 1087 tab.BootChainList = []bootloader.BootFile{ 1088 bootloader.NewBootFile("", "asset", bootloader.RoleRunMode), 1089 runKernelBf, 1090 } 1091 1092 uc20Model := boottest.MakeMockUC20Model() 1093 coreDev := boottest.MockUC20Device("", uc20Model) 1094 c.Assert(coreDev.HasModeenv(), Equals, true) 1095 1096 m := &boot.Modeenv{ 1097 Mode: "run", 1098 Base: s.base1.Filename(), 1099 CurrentKernels: []string{s.kern1.Filename()}, 1100 CurrentTrustedBootAssets: boot.BootAssetsMap{ 1101 "asset": {dataHash}, 1102 }, 1103 CurrentKernelCommandLines: boot.BootCommandLines{"snapd_recovery_mode=run"}, 1104 } 1105 1106 r := setupUC20Bootenv( 1107 c, 1108 tab.MockBootloader, 1109 &bootenv20Setup{ 1110 modeenv: m, 1111 kern: s.kern1, 1112 kernStatus: boot.DefaultStatus, 1113 }, 1114 ) 1115 defer r() 1116 1117 resealCalls := 0 1118 restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { 1119 resealCalls++ 1120 return fmt.Errorf("unexpected call") 1121 }) 1122 defer restore() 1123 1124 // get the boot kernel participant from our kernel snap 1125 bootKern := boot.Participant(s.kern1, snap.TypeKernel, coreDev) 1126 // make sure it's not a trivial boot participant 1127 c.Assert(bootKern.IsTrivial(), Equals, false) 1128 1129 // write boot-chains for current state that will stay unchanged 1130 bootChains := []boot.BootChain{{ 1131 BrandID: "my-brand", 1132 Model: "my-model-uc20", 1133 Grade: "dangerous", 1134 ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij", 1135 AssetChain: []boot.BootAsset{ 1136 { 1137 Role: bootloader.RoleRunMode, 1138 Name: "asset", 1139 Hashes: []string{ 1140 "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8", 1141 }, 1142 }, 1143 }, 1144 Kernel: "pc-kernel", 1145 KernelRevision: "1", 1146 KernelCmdlines: []string{"snapd_recovery_mode=run"}, 1147 }} 1148 err := boot.WriteBootChains(bootChains, filepath.Join(dirs.SnapFDEDir, "boot-chains"), 0) 1149 c.Assert(err, IsNil) 1150 1151 // make the kernel used on next boot 1152 rebootRequired, err := bootKern.SetNextBoot() 1153 c.Assert(err, IsNil) 1154 c.Assert(rebootRequired, Equals, false) 1155 1156 // make sure the env is as expected 1157 bvars, err := tab.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel") 1158 c.Assert(err, IsNil) 1159 c.Assert(bvars, DeepEquals, map[string]string{ 1160 "kernel_status": boot.DefaultStatus, 1161 "snap_kernel": s.kern1.Filename(), 1162 "snap_try_kernel": "", 1163 }) 1164 1165 // and that the modeenv now has the one kernel listed 1166 m2, err := boot.ReadModeenv("") 1167 c.Assert(err, IsNil) 1168 c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename()}) 1169 1170 // boot chains were built 1171 c.Check(tab.BootChainKernelPath, DeepEquals, []string{ 1172 s.kern1.MountFile(), 1173 }) 1174 // no actual reseal 1175 c.Check(resealCalls, Equals, 0) 1176 } 1177 1178 func (s *bootenv20Suite) TestCoreParticipant20SetNextSameUnassertedKernelSnapNoReseal(c *C) { 1179 // checked by resealKeyToModeenv 1180 s.stampSealedKeys(c, dirs.GlobalRootDir) 1181 1182 tab := s.bootloaderWithTrustedAssets(c, []string{"asset"}) 1183 1184 data := []byte("foobar") 1185 // SHA3-384 1186 dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8" 1187 1188 c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil) 1189 c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil) 1190 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil) 1191 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil) 1192 1193 // mock the files in cache 1194 mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{ 1195 "asset-" + dataHash, 1196 }) 1197 1198 runKernelBf := bootloader.NewBootFile(filepath.Join(s.ukern1.Filename()), "kernel.efi", bootloader.RoleRunMode) 1199 1200 tab.BootChainList = []bootloader.BootFile{ 1201 bootloader.NewBootFile("", "asset", bootloader.RoleRunMode), 1202 runKernelBf, 1203 } 1204 1205 uc20Model := boottest.MakeMockUC20Model() 1206 coreDev := boottest.MockUC20Device("", uc20Model) 1207 c.Assert(coreDev.HasModeenv(), Equals, true) 1208 1209 m := &boot.Modeenv{ 1210 Mode: "run", 1211 Base: s.base1.Filename(), 1212 CurrentKernels: []string{s.ukern1.Filename()}, 1213 CurrentTrustedBootAssets: boot.BootAssetsMap{ 1214 "asset": {dataHash}, 1215 }, 1216 CurrentKernelCommandLines: boot.BootCommandLines{"snapd_recovery_mode=run"}, 1217 } 1218 1219 r := setupUC20Bootenv( 1220 c, 1221 tab.MockBootloader, 1222 &bootenv20Setup{ 1223 modeenv: m, 1224 kern: s.ukern1, 1225 kernStatus: boot.DefaultStatus, 1226 }, 1227 ) 1228 defer r() 1229 1230 resealCalls := 0 1231 restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { 1232 resealCalls++ 1233 return fmt.Errorf("unexpected call") 1234 }) 1235 defer restore() 1236 1237 // get the boot kernel participant from our kernel snap 1238 bootKern := boot.Participant(s.ukern1, snap.TypeKernel, coreDev) 1239 // make sure it's not a trivial boot participant 1240 c.Assert(bootKern.IsTrivial(), Equals, false) 1241 1242 // write boot-chains for current state that will stay unchanged 1243 bootChains := []boot.BootChain{{ 1244 BrandID: "my-brand", 1245 Model: "my-model-uc20", 1246 Grade: "dangerous", 1247 ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij", 1248 AssetChain: []boot.BootAsset{ 1249 { 1250 Role: bootloader.RoleRunMode, 1251 Name: "asset", 1252 Hashes: []string{ 1253 "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8", 1254 }, 1255 }, 1256 }, 1257 Kernel: "pc-kernel", 1258 KernelRevision: "", 1259 KernelCmdlines: []string{"snapd_recovery_mode=run"}, 1260 }} 1261 err := boot.WriteBootChains(bootChains, filepath.Join(dirs.SnapFDEDir, "boot-chains"), 0) 1262 c.Assert(err, IsNil) 1263 1264 // make the kernel used on next boot 1265 rebootRequired, err := bootKern.SetNextBoot() 1266 c.Assert(err, IsNil) 1267 c.Assert(rebootRequired, Equals, false) 1268 1269 // make sure the env is as expected 1270 bvars, err := tab.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel") 1271 c.Assert(err, IsNil) 1272 c.Assert(bvars, DeepEquals, map[string]string{ 1273 "kernel_status": boot.DefaultStatus, 1274 "snap_kernel": s.ukern1.Filename(), 1275 "snap_try_kernel": "", 1276 }) 1277 1278 // and that the modeenv now has the one kernel listed 1279 m2, err := boot.ReadModeenv("") 1280 c.Assert(err, IsNil) 1281 c.Assert(m2.CurrentKernels, DeepEquals, []string{s.ukern1.Filename()}) 1282 1283 // boot chains were built 1284 c.Check(tab.BootChainKernelPath, DeepEquals, []string{ 1285 s.ukern1.MountFile(), 1286 }) 1287 // no actual reseal 1288 c.Check(resealCalls, Equals, 0) 1289 } 1290 1291 func (s *bootenv20EnvRefKernelSuite) TestCoreParticipant20SetNextNewKernelSnap(c *C) { 1292 coreDev := boottest.MockUC20Device("", nil) 1293 c.Assert(coreDev.HasModeenv(), Equals, true) 1294 1295 r := setupUC20Bootenv( 1296 c, 1297 s.bootloader, 1298 s.normalDefaultState, 1299 ) 1300 defer r() 1301 1302 // get the boot kernel participant from our new kernel snap 1303 bootKern := boot.Participant(s.kern2, snap.TypeKernel, coreDev) 1304 // make sure it's not a trivial boot participant 1305 c.Assert(bootKern.IsTrivial(), Equals, false) 1306 1307 // make the kernel used on next boot 1308 rebootRequired, err := bootKern.SetNextBoot() 1309 c.Assert(err, IsNil) 1310 c.Assert(rebootRequired, Equals, true) 1311 1312 // make sure the env was updated 1313 m := s.bootloader.BootVars 1314 c.Assert(m, DeepEquals, map[string]string{ 1315 "kernel_status": boot.TryStatus, 1316 "snap_kernel": s.kern1.Filename(), 1317 "snap_try_kernel": s.kern2.Filename(), 1318 }) 1319 1320 // and that the modeenv now has this kernel listed 1321 m2, err := boot.ReadModeenv("") 1322 c.Assert(err, IsNil) 1323 c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename(), s.kern2.Filename()}) 1324 } 1325 1326 func (s *bootenv20Suite) TestMarkBootSuccessful20KernelStatusTryingNoKernelSnapCleansUp(c *C) { 1327 coreDev := boottest.MockUC20Device("", nil) 1328 c.Assert(coreDev.HasModeenv(), Equals, true) 1329 1330 // set all the same vars as if we were doing trying, except don't set a try 1331 // kernel 1332 r := setupUC20Bootenv( 1333 c, 1334 s.bootloader, 1335 &bootenv20Setup{ 1336 modeenv: &boot.Modeenv{ 1337 Mode: "run", 1338 Base: s.base1.Filename(), 1339 CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()}, 1340 }, 1341 kern: s.kern1, 1342 // no try-kernel 1343 kernStatus: boot.TryingStatus, 1344 }, 1345 ) 1346 defer r() 1347 1348 // mark successful 1349 err := boot.MarkBootSuccessful(coreDev) 1350 c.Assert(err, IsNil) 1351 1352 // check that the bootloader variable was cleaned 1353 expected := map[string]string{"kernel_status": boot.DefaultStatus} 1354 c.Assert(s.bootloader.BootVars, DeepEquals, expected) 1355 1356 // check that MarkBootSuccessful didn't enable a kernel (since there was no 1357 // try kernel) 1358 _, nEnableCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel") 1359 c.Assert(nEnableCalls, Equals, 0) 1360 1361 // we will always end up disabling a try-kernel though as cleanup 1362 _, nDisableTryCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel") 1363 c.Assert(nDisableTryCalls, Equals, 1) 1364 1365 // do it again, verify it's still okay 1366 err = boot.MarkBootSuccessful(coreDev) 1367 c.Assert(err, IsNil) 1368 c.Assert(s.bootloader.BootVars, DeepEquals, expected) 1369 1370 // no new enabled kernels 1371 _, nEnableCalls = s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel") 1372 c.Assert(nEnableCalls, Equals, 0) 1373 1374 // again we will try to cleanup any leftover try-kernels 1375 _, nDisableTryCalls = s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel") 1376 c.Assert(nDisableTryCalls, Equals, 2) 1377 1378 // check that the modeenv re-wrote the CurrentKernels 1379 m2, err := boot.ReadModeenv("") 1380 c.Assert(err, IsNil) 1381 c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename()}) 1382 } 1383 1384 func (s *bootenv20EnvRefKernelSuite) TestMarkBootSuccessful20KernelStatusTryingNoKernelSnapCleansUp(c *C) { 1385 coreDev := boottest.MockUC20Device("", nil) 1386 c.Assert(coreDev.HasModeenv(), Equals, true) 1387 1388 // set all the same vars as if we were doing trying, except don't set a try 1389 // kernel 1390 r := setupUC20Bootenv( 1391 c, 1392 s.bootloader, 1393 &bootenv20Setup{ 1394 modeenv: &boot.Modeenv{ 1395 Mode: "run", 1396 Base: s.base1.Filename(), 1397 CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()}, 1398 }, 1399 kern: s.kern1, 1400 // no try-kernel 1401 kernStatus: boot.TryingStatus, 1402 }, 1403 ) 1404 defer r() 1405 1406 // mark successful 1407 err := boot.MarkBootSuccessful(coreDev) 1408 c.Assert(err, IsNil) 1409 1410 // make sure the env was updated 1411 expected := map[string]string{ 1412 "kernel_status": boot.DefaultStatus, 1413 "snap_kernel": s.kern1.Filename(), 1414 "snap_try_kernel": "", 1415 } 1416 c.Assert(s.bootloader.BootVars, DeepEquals, expected) 1417 1418 // do it again, verify it's still okay 1419 err = boot.MarkBootSuccessful(coreDev) 1420 c.Assert(err, IsNil) 1421 1422 c.Assert(s.bootloader.BootVars, DeepEquals, expected) 1423 1424 // check that the modeenv re-wrote the CurrentKernels 1425 m2, err := boot.ReadModeenv("") 1426 c.Assert(err, IsNil) 1427 c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename()}) 1428 } 1429 1430 func (s *bootenv20Suite) TestMarkBootSuccessful20BaseStatusTryingNoTryBaseSnapCleansUp(c *C) { 1431 m := &boot.Modeenv{ 1432 Mode: "run", 1433 Base: s.base1.Filename(), 1434 // no TryBase set 1435 BaseStatus: boot.TryingStatus, 1436 } 1437 r := setupUC20Bootenv( 1438 c, 1439 s.bootloader, 1440 &bootenv20Setup{ 1441 modeenv: m, 1442 // no kernel setup necessary 1443 }, 1444 ) 1445 defer r() 1446 1447 coreDev := boottest.MockUC20Device("", nil) 1448 c.Assert(coreDev.HasModeenv(), Equals, true) 1449 1450 // mark successful 1451 err := boot.MarkBootSuccessful(coreDev) 1452 c.Assert(err, IsNil) 1453 1454 // check that the modeenv base_status was re-written to default 1455 m2, err := boot.ReadModeenv("") 1456 c.Assert(err, IsNil) 1457 c.Assert(m2.BaseStatus, Equals, boot.DefaultStatus) 1458 c.Assert(m2.Base, Equals, m.Base) 1459 c.Assert(m2.TryBase, Equals, m.TryBase) 1460 1461 // do it again, verify it's still okay 1462 err = boot.MarkBootSuccessful(coreDev) 1463 c.Assert(err, IsNil) 1464 1465 m3, err := boot.ReadModeenv("") 1466 c.Assert(err, IsNil) 1467 c.Assert(m3.BaseStatus, Equals, boot.DefaultStatus) 1468 c.Assert(m3.Base, Equals, m.Base) 1469 c.Assert(m3.TryBase, Equals, m.TryBase) 1470 } 1471 1472 func (s *bootenv20Suite) TestCoreParticipant20SetNextSameBaseSnap(c *C) { 1473 coreDev := boottest.MockUC20Device("", nil) 1474 c.Assert(coreDev.HasModeenv(), Equals, true) 1475 1476 m := &boot.Modeenv{ 1477 Mode: "run", 1478 Base: s.base1.Filename(), 1479 } 1480 r := setupUC20Bootenv( 1481 c, 1482 s.bootloader, 1483 &bootenv20Setup{ 1484 modeenv: m, 1485 // no kernel setup necessary 1486 }, 1487 ) 1488 defer r() 1489 1490 // get the boot base participant from our base snap 1491 bootBase := boot.Participant(s.base1, snap.TypeBase, coreDev) 1492 // make sure it's not a trivial boot participant 1493 c.Assert(bootBase.IsTrivial(), Equals, false) 1494 1495 // make the base used on next boot 1496 rebootRequired, err := bootBase.SetNextBoot() 1497 c.Assert(err, IsNil) 1498 1499 // we don't need to reboot because it's the same base snap 1500 c.Assert(rebootRequired, Equals, false) 1501 1502 // make sure the modeenv wasn't changed 1503 m2, err := boot.ReadModeenv("") 1504 c.Assert(err, IsNil) 1505 c.Assert(m2.Base, Equals, m.Base) 1506 c.Assert(m2.BaseStatus, Equals, m.BaseStatus) 1507 c.Assert(m2.TryBase, Equals, m.TryBase) 1508 } 1509 1510 func (s *bootenv20Suite) TestCoreParticipant20SetNextNewBaseSnap(c *C) { 1511 coreDev := boottest.MockUC20Device("", nil) 1512 c.Assert(coreDev.HasModeenv(), Equals, true) 1513 1514 // default state 1515 m := &boot.Modeenv{ 1516 Mode: "run", 1517 Base: s.base1.Filename(), 1518 } 1519 r := setupUC20Bootenv( 1520 c, 1521 s.bootloader, 1522 &bootenv20Setup{ 1523 modeenv: m, 1524 // no kernel setup necessary 1525 }, 1526 ) 1527 defer r() 1528 1529 // get the boot base participant from our new base snap 1530 bootBase := boot.Participant(s.base2, snap.TypeBase, coreDev) 1531 // make sure it's not a trivial boot participant 1532 c.Assert(bootBase.IsTrivial(), Equals, false) 1533 1534 // make the base used on next boot 1535 rebootRequired, err := bootBase.SetNextBoot() 1536 c.Assert(err, IsNil) 1537 c.Assert(rebootRequired, Equals, true) 1538 1539 // make sure the modeenv was updated 1540 m2, err := boot.ReadModeenv("") 1541 c.Assert(err, IsNil) 1542 c.Assert(m2.Base, Equals, m.Base) 1543 c.Assert(m2.BaseStatus, Equals, boot.TryStatus) 1544 c.Assert(m2.TryBase, Equals, s.base2.Filename()) 1545 } 1546 1547 func (s *bootenv20Suite) TestCoreParticipant20SetNextNewBaseSnapNoReseal(c *C) { 1548 // checked by resealKeyToModeenv 1549 s.stampSealedKeys(c, dirs.GlobalRootDir) 1550 1551 tab := s.bootloaderWithTrustedAssets(c, []string{"asset"}) 1552 1553 coreDev := boottest.MockUC20Device("", nil) 1554 c.Assert(coreDev.HasModeenv(), Equals, true) 1555 1556 resealCalls := 0 1557 restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { 1558 resealCalls++ 1559 return nil 1560 }) 1561 defer restore() 1562 1563 // we should not even need to build boot chains 1564 tab.BootChainErr = errors.New("boom") 1565 1566 // default state 1567 m := &boot.Modeenv{ 1568 Mode: "run", 1569 Base: s.base1.Filename(), 1570 } 1571 r := setupUC20Bootenv( 1572 c, 1573 tab.MockBootloader, 1574 &bootenv20Setup{ 1575 modeenv: m, 1576 // no kernel setup necessary 1577 }, 1578 ) 1579 defer r() 1580 1581 // get the boot base participant from our new base snap 1582 bootBase := boot.Participant(s.base2, snap.TypeBase, coreDev) 1583 // make sure it's not a trivial boot participant 1584 c.Assert(bootBase.IsTrivial(), Equals, false) 1585 1586 // make the base used on next boot 1587 rebootRequired, err := bootBase.SetNextBoot() 1588 c.Assert(err, IsNil) 1589 c.Assert(rebootRequired, Equals, true) 1590 1591 // make sure the modeenv was updated 1592 m2, err := boot.ReadModeenv("") 1593 c.Assert(err, IsNil) 1594 c.Assert(m2.Base, Equals, m.Base) 1595 c.Assert(m2.BaseStatus, Equals, boot.TryStatus) 1596 c.Assert(m2.TryBase, Equals, s.base2.Filename()) 1597 1598 // no reseal 1599 c.Check(resealCalls, Equals, 0) 1600 } 1601 1602 func (s *bootenvSuite) TestMarkBootSuccessfulAllSnap(c *C) { 1603 coreDev := boottest.MockDevice("some-snap") 1604 1605 s.bootloader.BootVars["snap_mode"] = boot.TryingStatus 1606 s.bootloader.BootVars["snap_try_core"] = "os1" 1607 s.bootloader.BootVars["snap_try_kernel"] = "k1" 1608 err := boot.MarkBootSuccessful(coreDev) 1609 c.Assert(err, IsNil) 1610 1611 expected := map[string]string{ 1612 // cleared 1613 "snap_mode": boot.DefaultStatus, 1614 "snap_try_kernel": "", 1615 "snap_try_core": "", 1616 // updated 1617 "snap_kernel": "k1", 1618 "snap_core": "os1", 1619 } 1620 c.Assert(s.bootloader.BootVars, DeepEquals, expected) 1621 1622 // do it again, verify its still valid 1623 err = boot.MarkBootSuccessful(coreDev) 1624 c.Assert(err, IsNil) 1625 c.Assert(s.bootloader.BootVars, DeepEquals, expected) 1626 } 1627 1628 func (s *bootenv20Suite) TestMarkBootSuccessful20AllSnap(c *C) { 1629 coreDev := boottest.MockUC20Device("", nil) 1630 c.Assert(coreDev.HasModeenv(), Equals, true) 1631 1632 // bonus points: we were trying both a base snap and a kernel snap 1633 m := &boot.Modeenv{ 1634 Mode: "run", 1635 Base: s.base1.Filename(), 1636 TryBase: s.base2.Filename(), 1637 BaseStatus: boot.TryingStatus, 1638 CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()}, 1639 } 1640 r := setupUC20Bootenv( 1641 c, 1642 s.bootloader, 1643 &bootenv20Setup{ 1644 modeenv: m, 1645 kern: s.kern1, 1646 tryKern: s.kern2, 1647 kernStatus: boot.TryingStatus, 1648 }, 1649 ) 1650 defer r() 1651 1652 err := boot.MarkBootSuccessful(coreDev) 1653 c.Assert(err, IsNil) 1654 1655 // check the bootloader variables 1656 expected := map[string]string{ 1657 // cleared 1658 "kernel_status": boot.DefaultStatus, 1659 } 1660 c.Assert(s.bootloader.BootVars, DeepEquals, expected) 1661 1662 // check that we called EnableKernel() on the try-kernel 1663 actual, _ := s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel") 1664 c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2}) 1665 1666 // and that we disabled a try kernel 1667 _, nDisableTryCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel") 1668 c.Assert(nDisableTryCalls, Equals, 1) 1669 1670 // also check that the modeenv was updated 1671 m2, err := boot.ReadModeenv("") 1672 c.Assert(err, IsNil) 1673 c.Assert(m2.Base, Equals, s.base2.Filename()) 1674 c.Assert(m2.TryBase, Equals, "") 1675 c.Assert(m2.BaseStatus, Equals, boot.DefaultStatus) 1676 c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()}) 1677 1678 // do it again, verify its still valid 1679 err = boot.MarkBootSuccessful(coreDev) 1680 c.Assert(err, IsNil) 1681 c.Assert(s.bootloader.BootVars, DeepEquals, expected) 1682 1683 // no new enabled kernels 1684 actual, _ = s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel") 1685 c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2}) 1686 // we always disable the try kernel as a cleanup operation, so there's one 1687 // more call here 1688 _, nDisableTryCalls = s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel") 1689 c.Assert(nDisableTryCalls, Equals, 2) 1690 } 1691 1692 func (s *bootenv20EnvRefKernelSuite) TestMarkBootSuccessful20AllSnap(c *C) { 1693 coreDev := boottest.MockUC20Device("", nil) 1694 c.Assert(coreDev.HasModeenv(), Equals, true) 1695 1696 // bonus points: we were trying both a base snap and a kernel snap 1697 m := &boot.Modeenv{ 1698 Mode: "run", 1699 Base: s.base1.Filename(), 1700 TryBase: s.base2.Filename(), 1701 BaseStatus: boot.TryingStatus, 1702 CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()}, 1703 } 1704 r := setupUC20Bootenv( 1705 c, 1706 s.bootloader, 1707 &bootenv20Setup{ 1708 modeenv: m, 1709 kern: s.kern1, 1710 tryKern: s.kern2, 1711 kernStatus: boot.TryingStatus, 1712 }, 1713 ) 1714 defer r() 1715 1716 err := boot.MarkBootSuccessful(coreDev) 1717 c.Assert(err, IsNil) 1718 1719 // check the bootloader variables 1720 expected := map[string]string{ 1721 // cleared 1722 "kernel_status": boot.DefaultStatus, 1723 "snap_try_kernel": "", 1724 // enabled new kernel 1725 "snap_kernel": s.kern2.Filename(), 1726 } 1727 c.Assert(s.bootloader.BootVars, DeepEquals, expected) 1728 1729 // also check that the modeenv was updated 1730 m2, err := boot.ReadModeenv("") 1731 c.Assert(err, IsNil) 1732 c.Assert(m2.Base, Equals, s.base2.Filename()) 1733 c.Assert(m2.TryBase, Equals, "") 1734 c.Assert(m2.BaseStatus, Equals, boot.DefaultStatus) 1735 c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()}) 1736 1737 // do it again, verify its still valid 1738 err = boot.MarkBootSuccessful(coreDev) 1739 c.Assert(err, IsNil) 1740 c.Assert(s.bootloader.BootVars, DeepEquals, expected) 1741 } 1742 1743 func (s *bootenvSuite) TestMarkBootSuccessfulKernelUpdate(c *C) { 1744 coreDev := boottest.MockDevice("some-snap") 1745 1746 s.bootloader.BootVars["snap_mode"] = boot.TryingStatus 1747 s.bootloader.BootVars["snap_core"] = "os1" 1748 s.bootloader.BootVars["snap_kernel"] = "k1" 1749 s.bootloader.BootVars["snap_try_core"] = "" 1750 s.bootloader.BootVars["snap_try_kernel"] = "k2" 1751 err := boot.MarkBootSuccessful(coreDev) 1752 c.Assert(err, IsNil) 1753 c.Assert(s.bootloader.BootVars, DeepEquals, map[string]string{ 1754 // cleared 1755 "snap_mode": boot.DefaultStatus, 1756 "snap_try_kernel": "", 1757 "snap_try_core": "", 1758 // unchanged 1759 "snap_core": "os1", 1760 // updated 1761 "snap_kernel": "k2", 1762 }) 1763 } 1764 1765 func (s *bootenvSuite) TestMarkBootSuccessfulBaseUpdate(c *C) { 1766 coreDev := boottest.MockDevice("some-snap") 1767 1768 s.bootloader.BootVars["snap_mode"] = boot.TryingStatus 1769 s.bootloader.BootVars["snap_core"] = "os1" 1770 s.bootloader.BootVars["snap_kernel"] = "k1" 1771 s.bootloader.BootVars["snap_try_core"] = "os2" 1772 s.bootloader.BootVars["snap_try_kernel"] = "" 1773 err := boot.MarkBootSuccessful(coreDev) 1774 c.Assert(err, IsNil) 1775 c.Assert(s.bootloader.BootVars, DeepEquals, map[string]string{ 1776 // cleared 1777 "snap_mode": boot.DefaultStatus, 1778 "snap_try_core": "", 1779 // unchanged 1780 "snap_kernel": "k1", 1781 "snap_try_kernel": "", 1782 // updated 1783 "snap_core": "os2", 1784 }) 1785 } 1786 1787 func (s *bootenv20Suite) TestMarkBootSuccessful20KernelUpdate(c *C) { 1788 // trying a kernel snap 1789 m := &boot.Modeenv{ 1790 Mode: "run", 1791 Base: s.base1.Filename(), 1792 CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()}, 1793 } 1794 r := setupUC20Bootenv( 1795 c, 1796 s.bootloader, 1797 &bootenv20Setup{ 1798 modeenv: m, 1799 kern: s.kern1, 1800 tryKern: s.kern2, 1801 kernStatus: boot.TryingStatus, 1802 }, 1803 ) 1804 defer r() 1805 1806 coreDev := boottest.MockUC20Device("", nil) 1807 c.Assert(coreDev.HasModeenv(), Equals, true) 1808 1809 // mark successful 1810 err := boot.MarkBootSuccessful(coreDev) 1811 c.Assert(err, IsNil) 1812 1813 // check the bootloader variables 1814 expected := map[string]string{"kernel_status": boot.DefaultStatus} 1815 c.Assert(s.bootloader.BootVars, DeepEquals, expected) 1816 1817 // check that MarkBootSuccessful enabled the try kernel 1818 actual, _ := s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel") 1819 c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2}) 1820 1821 // and that we disabled a try kernel 1822 _, nDisableTryCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel") 1823 c.Assert(nDisableTryCalls, Equals, 1) 1824 1825 // check that the new kernel is the only one in modeenv 1826 m2, err := boot.ReadModeenv("") 1827 c.Assert(err, IsNil) 1828 c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()}) 1829 1830 // do it again, verify its still valid 1831 err = boot.MarkBootSuccessful(coreDev) 1832 c.Assert(err, IsNil) 1833 c.Assert(s.bootloader.BootVars, DeepEquals, expected) 1834 1835 // no new bootloader calls 1836 actual, _ = s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel") 1837 c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2}) 1838 1839 // we did disable the kernel again because we always do this to cleanup in 1840 // case there were leftovers 1841 _, nDisableTryCalls = s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel") 1842 c.Assert(nDisableTryCalls, Equals, 2) 1843 } 1844 1845 func (s *bootenv20Suite) TestMarkBootSuccessful20KernelUpdateWithReseal(c *C) { 1846 // checked by resealKeyToModeenv 1847 s.stampSealedKeys(c, dirs.GlobalRootDir) 1848 1849 tab := s.bootloaderWithTrustedAssets(c, []string{"asset"}) 1850 1851 data := []byte("foobar") 1852 // SHA3-384 1853 dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8" 1854 1855 c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil) 1856 c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil) 1857 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil) 1858 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil) 1859 1860 // mock the files in cache 1861 mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{ 1862 "asset-" + dataHash, 1863 }) 1864 1865 assetBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("asset-%s", dataHash)), bootloader.RoleRunMode) 1866 runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode) 1867 1868 tab.BootChainList = []bootloader.BootFile{ 1869 bootloader.NewBootFile("", "asset", bootloader.RoleRunMode), 1870 runKernelBf, 1871 } 1872 1873 // trying a kernel snap 1874 m := &boot.Modeenv{ 1875 Mode: "run", 1876 Base: s.base1.Filename(), 1877 CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()}, 1878 CurrentTrustedBootAssets: boot.BootAssetsMap{ 1879 "asset": {dataHash}, 1880 }, 1881 } 1882 r := setupUC20Bootenv( 1883 c, 1884 tab.MockBootloader, 1885 &bootenv20Setup{ 1886 modeenv: m, 1887 kern: s.kern1, 1888 tryKern: s.kern2, 1889 kernStatus: boot.TryingStatus, 1890 }, 1891 ) 1892 defer r() 1893 1894 coreDev := boottest.MockUC20Device("", nil) 1895 c.Assert(coreDev.HasModeenv(), Equals, true) 1896 1897 resealCalls := 0 1898 restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { 1899 resealCalls++ 1900 1901 c.Assert(params.ModelParams, HasLen, 1) 1902 mp := params.ModelParams[0] 1903 c.Check(mp.Model, DeepEquals, coreDev.Model()) 1904 for _, ch := range mp.EFILoadChains { 1905 printChain(c, ch, "-") 1906 } 1907 c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{ 1908 secboot.NewLoadChain(assetBf, 1909 secboot.NewLoadChain(runKernelBf)), 1910 }) 1911 return nil 1912 }) 1913 defer restore() 1914 1915 // mark successful 1916 err := boot.MarkBootSuccessful(coreDev) 1917 c.Assert(err, IsNil) 1918 1919 // check the bootloader variables 1920 expected := map[string]string{ 1921 "kernel_status": boot.DefaultStatus, 1922 "snap_kernel": s.kern2.Filename(), 1923 "snap_try_kernel": boot.DefaultStatus, 1924 } 1925 c.Assert(tab.BootVars, DeepEquals, expected) 1926 1927 // check that the new kernel is the only one in modeenv 1928 m2, err := boot.ReadModeenv("") 1929 c.Assert(err, IsNil) 1930 c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()}) 1931 1932 c.Check(resealCalls, Equals, 1) 1933 } 1934 1935 func (s *bootenv20EnvRefKernelSuite) TestMarkBootSuccessful20KernelUpdate(c *C) { 1936 // trying a kernel snap 1937 m := &boot.Modeenv{ 1938 Mode: "run", 1939 Base: s.base1.Filename(), 1940 CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()}, 1941 } 1942 r := setupUC20Bootenv( 1943 c, 1944 s.bootloader, 1945 &bootenv20Setup{ 1946 modeenv: m, 1947 kern: s.kern1, 1948 tryKern: s.kern2, 1949 kernStatus: boot.TryingStatus, 1950 }, 1951 ) 1952 defer r() 1953 1954 coreDev := boottest.MockUC20Device("", nil) 1955 c.Assert(coreDev.HasModeenv(), Equals, true) 1956 1957 // mark successful 1958 err := boot.MarkBootSuccessful(coreDev) 1959 c.Assert(err, IsNil) 1960 1961 // check the bootloader variables 1962 expected := map[string]string{ 1963 "kernel_status": boot.DefaultStatus, 1964 "snap_kernel": s.kern2.Filename(), 1965 "snap_try_kernel": "", 1966 } 1967 c.Assert(s.bootloader.BootVars, DeepEquals, expected) 1968 1969 // check that the new kernel is the only one in modeenv 1970 m2, err := boot.ReadModeenv("") 1971 c.Assert(err, IsNil) 1972 c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()}) 1973 1974 // do it again, verify its still valid 1975 err = boot.MarkBootSuccessful(coreDev) 1976 c.Assert(err, IsNil) 1977 c.Assert(s.bootloader.BootVars, DeepEquals, expected) 1978 c.Assert(s.bootloader.BootVars, DeepEquals, expected) 1979 } 1980 1981 func (s *bootenv20Suite) TestMarkBootSuccessful20BaseUpdate(c *C) { 1982 // we were trying a base snap 1983 m := &boot.Modeenv{ 1984 Mode: "run", 1985 Base: s.base1.Filename(), 1986 TryBase: s.base2.Filename(), 1987 BaseStatus: boot.TryingStatus, 1988 CurrentKernels: []string{s.kern1.Filename()}, 1989 } 1990 r := setupUC20Bootenv( 1991 c, 1992 s.bootloader, 1993 &bootenv20Setup{ 1994 modeenv: m, 1995 kern: s.kern1, 1996 kernStatus: boot.DefaultStatus, 1997 }, 1998 ) 1999 defer r() 2000 2001 coreDev := boottest.MockUC20Device("", nil) 2002 c.Assert(coreDev.HasModeenv(), Equals, true) 2003 2004 // mark successful 2005 err := boot.MarkBootSuccessful(coreDev) 2006 c.Assert(err, IsNil) 2007 2008 // check the modeenv 2009 m2, err := boot.ReadModeenv("") 2010 c.Assert(err, IsNil) 2011 c.Assert(m2.Base, Equals, s.base2.Filename()) 2012 c.Assert(m2.TryBase, Equals, "") 2013 c.Assert(m2.BaseStatus, Equals, "") 2014 2015 // do it again, verify its still valid 2016 err = boot.MarkBootSuccessful(coreDev) 2017 c.Assert(err, IsNil) 2018 2019 // check the modeenv again 2020 m3, err := boot.ReadModeenv("") 2021 c.Assert(err, IsNil) 2022 c.Assert(m3.Base, Equals, s.base2.Filename()) 2023 c.Assert(m3.TryBase, Equals, "") 2024 c.Assert(m3.BaseStatus, Equals, "") 2025 } 2026 2027 func (s *bootenv20Suite) bootloaderWithTrustedAssets(c *C, trustedAssets []string) *bootloadertest.MockTrustedAssetsBootloader { 2028 // TODO:UC20: this should be an ExtractedRecoveryKernelImageBootloader 2029 // because that would reflect our main currently supported 2030 // trusted assets bootloader (grub) 2031 tab := bootloadertest.Mock("trusted", "").WithTrustedAssets() 2032 bootloader.Force(tab) 2033 tab.TrustedAssetsList = trustedAssets 2034 s.AddCleanup(func() { bootloader.Force(nil) }) 2035 return tab 2036 } 2037 2038 func (s *bootenv20Suite) TestMarkBootSuccessful20BootAssetsUpdateHappy(c *C) { 2039 // checked by resealKeyToModeenv 2040 s.stampSealedKeys(c, dirs.GlobalRootDir) 2041 2042 tab := s.bootloaderWithTrustedAssets(c, []string{"asset", "shim"}) 2043 2044 data := []byte("foobar") 2045 // SHA3-384 2046 dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8" 2047 shim := []byte("shim") 2048 shimHash := "dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b" 2049 2050 c.Assert(os.MkdirAll(boot.InitramfsUbuntuBootDir, 0755), IsNil) 2051 c.Assert(os.MkdirAll(boot.InitramfsUbuntuSeedDir, 0755), IsNil) 2052 // only asset for ubuntu 2053 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil) 2054 // shim and asset for seed 2055 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil) 2056 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "shim"), shim, 0644), IsNil) 2057 2058 // mock the files in cache 2059 mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{ 2060 "shim-recoveryshimhash", 2061 "shim-" + shimHash, 2062 "asset-assethash", 2063 "asset-recoveryassethash", 2064 "asset-" + dataHash, 2065 }) 2066 2067 shimBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("shim-%s", shimHash)), bootloader.RoleRecovery) 2068 assetBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("asset-%s", dataHash)), bootloader.RoleRecovery) 2069 runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode) 2070 recoveryKernelBf := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery) 2071 2072 tab.BootChainList = []bootloader.BootFile{ 2073 bootloader.NewBootFile("", "shim", bootloader.RoleRecovery), 2074 bootloader.NewBootFile("", "asset", bootloader.RoleRecovery), 2075 runKernelBf, 2076 } 2077 tab.RecoveryBootChainList = []bootloader.BootFile{ 2078 bootloader.NewBootFile("", "shim", bootloader.RoleRecovery), 2079 bootloader.NewBootFile("", "asset", bootloader.RoleRecovery), 2080 recoveryKernelBf, 2081 } 2082 2083 uc20Model := boottest.MakeMockUC20Model() 2084 2085 restore := boot.MockSeedReadSystemEssential(func(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*seed.Snap, error) { 2086 kernelSnap := &seed.Snap{ 2087 Path: "/var/lib/snapd/seed/snaps/pc-linux_1.snap", 2088 SideInfo: &snap.SideInfo{ 2089 Revision: snap.Revision{N: 1}, 2090 RealName: "pc-linux", 2091 }, 2092 } 2093 return uc20Model, []*seed.Snap{kernelSnap}, nil 2094 }) 2095 defer restore() 2096 2097 // we were trying an update of boot assets 2098 m := &boot.Modeenv{ 2099 Mode: "run", 2100 Base: s.base1.Filename(), 2101 CurrentKernels: []string{s.kern1.Filename()}, 2102 CurrentTrustedBootAssets: boot.BootAssetsMap{ 2103 "asset": {"assethash", dataHash}, 2104 }, 2105 CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{ 2106 "asset": {"recoveryassethash", dataHash}, 2107 "shim": {"recoveryshimhash", shimHash}, 2108 }, 2109 CurrentRecoverySystems: []string{"system"}, 2110 } 2111 r := setupUC20Bootenv( 2112 c, 2113 tab.MockBootloader, 2114 &bootenv20Setup{ 2115 modeenv: m, 2116 kern: s.kern1, 2117 kernStatus: boot.DefaultStatus, 2118 }, 2119 ) 2120 defer r() 2121 2122 coreDev := boottest.MockUC20Device("", uc20Model) 2123 c.Assert(coreDev.HasModeenv(), Equals, true) 2124 2125 resealCalls := 0 2126 restore = boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { 2127 resealCalls++ 2128 2129 c.Assert(params.ModelParams, HasLen, 1) 2130 mp := params.ModelParams[0] 2131 c.Check(mp.Model, DeepEquals, uc20Model) 2132 for _, ch := range mp.EFILoadChains { 2133 printChain(c, ch, "-") 2134 } 2135 switch resealCalls { 2136 case 1: 2137 c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{ 2138 secboot.NewLoadChain(shimBf, 2139 secboot.NewLoadChain(assetBf, 2140 secboot.NewLoadChain(runKernelBf))), 2141 secboot.NewLoadChain(shimBf, 2142 secboot.NewLoadChain(assetBf, 2143 secboot.NewLoadChain(recoveryKernelBf))), 2144 }) 2145 case 2: 2146 c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{ 2147 secboot.NewLoadChain(shimBf, 2148 secboot.NewLoadChain(assetBf, 2149 secboot.NewLoadChain(recoveryKernelBf))), 2150 }) 2151 default: 2152 c.Errorf("unexpected additional call to secboot.ResealKey (call # %d)", resealCalls) 2153 } 2154 return nil 2155 }) 2156 defer restore() 2157 2158 // mark successful 2159 err := boot.MarkBootSuccessful(coreDev) 2160 c.Assert(err, IsNil) 2161 2162 // check the modeenv 2163 m2, err := boot.ReadModeenv("") 2164 c.Assert(err, IsNil) 2165 // update assets are in the list 2166 c.Check(m2.CurrentTrustedBootAssets, DeepEquals, boot.BootAssetsMap{ 2167 "asset": {dataHash}, 2168 }) 2169 c.Check(m2.CurrentTrustedRecoveryBootAssets, DeepEquals, boot.BootAssetsMap{ 2170 "asset": {dataHash}, 2171 "shim": {shimHash}, 2172 }) 2173 // unused files were dropped from cache 2174 checkContentGlob(c, filepath.Join(dirs.SnapBootAssetsDir, "trusted", "*"), []string{ 2175 filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-"+dataHash), 2176 filepath.Join(dirs.SnapBootAssetsDir, "trusted", "shim-"+shimHash), 2177 }) 2178 c.Check(resealCalls, Equals, 2) 2179 } 2180 2181 func (s *bootenv20Suite) TestMarkBootSuccessful20BootAssetsStableStateHappy(c *C) { 2182 // checked by resealKeyToModeenv 2183 s.stampSealedKeys(c, dirs.GlobalRootDir) 2184 2185 tab := s.bootloaderWithTrustedAssets(c, []string{"nested/asset", "shim"}) 2186 2187 data := []byte("foobar") 2188 // SHA3-384 2189 dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8" 2190 shim := []byte("shim") 2191 shimHash := "dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b" 2192 2193 c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir, "nested"), 0755), IsNil) 2194 c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir, "nested"), 0755), IsNil) 2195 // only asset for ubuntu-boot 2196 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "nested/asset"), data, 0644), IsNil) 2197 // shim and asset for ubuntu-seed 2198 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "nested/asset"), data, 0644), IsNil) 2199 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "shim"), shim, 0644), IsNil) 2200 2201 // mock the files in cache 2202 mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{ 2203 "shim-" + shimHash, 2204 "asset-" + dataHash, 2205 }) 2206 2207 runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode) 2208 recoveryKernelBf := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery) 2209 2210 tab.BootChainList = []bootloader.BootFile{ 2211 bootloader.NewBootFile("", "shim", bootloader.RoleRecovery), 2212 bootloader.NewBootFile("", "asset", bootloader.RoleRecovery), 2213 runKernelBf, 2214 } 2215 tab.RecoveryBootChainList = []bootloader.BootFile{ 2216 bootloader.NewBootFile("", "shim", bootloader.RoleRecovery), 2217 bootloader.NewBootFile("", "asset", bootloader.RoleRecovery), 2218 recoveryKernelBf, 2219 } 2220 2221 uc20Model := boottest.MakeMockUC20Model() 2222 2223 restore := boot.MockSeedReadSystemEssential(func(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*seed.Snap, error) { 2224 kernelSnap := &seed.Snap{ 2225 Path: "/var/lib/snapd/seed/snaps/pc-kernel-recovery_1.snap", 2226 SideInfo: &snap.SideInfo{ 2227 Revision: snap.Revision{N: 1}, 2228 RealName: "pc-kernel-recovery", 2229 }, 2230 } 2231 return uc20Model, []*seed.Snap{kernelSnap}, nil 2232 }) 2233 defer restore() 2234 2235 // we were trying an update of boot assets 2236 m := &boot.Modeenv{ 2237 Mode: "run", 2238 Base: s.base1.Filename(), 2239 CurrentKernels: []string{s.kern1.Filename()}, 2240 CurrentTrustedBootAssets: boot.BootAssetsMap{ 2241 "asset": {dataHash}, 2242 }, 2243 CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{ 2244 "asset": {dataHash}, 2245 "shim": {shimHash}, 2246 }, 2247 CurrentRecoverySystems: []string{"system"}, 2248 CurrentKernelCommandLines: boot.BootCommandLines{"snapd_recovery_mode=run"}, 2249 } 2250 r := setupUC20Bootenv( 2251 c, 2252 tab.MockBootloader, 2253 &bootenv20Setup{ 2254 modeenv: m, 2255 kern: s.kern1, 2256 kernStatus: boot.DefaultStatus, 2257 }, 2258 ) 2259 defer r() 2260 2261 coreDev := boottest.MockUC20Device("", uc20Model) 2262 c.Assert(coreDev.HasModeenv(), Equals, true) 2263 2264 resealCalls := 0 2265 restore = boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { 2266 resealCalls++ 2267 return nil 2268 }) 2269 defer restore() 2270 2271 // write boot-chains for current state that will stay unchanged 2272 bootChains := []boot.BootChain{{ 2273 BrandID: "my-brand", 2274 Model: "my-model-uc20", 2275 Grade: "dangerous", 2276 ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij", 2277 AssetChain: []boot.BootAsset{{ 2278 Role: bootloader.RoleRecovery, Name: "shim", 2279 Hashes: []string{ 2280 "dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b", 2281 }, 2282 }, { 2283 Role: bootloader.RoleRecovery, Name: "asset", Hashes: []string{ 2284 "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8", 2285 }, 2286 }}, 2287 Kernel: "pc-kernel", 2288 KernelRevision: "1", 2289 KernelCmdlines: []string{"snapd_recovery_mode=run"}, 2290 }, { 2291 BrandID: "my-brand", 2292 Model: "my-model-uc20", 2293 Grade: "dangerous", 2294 ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij", 2295 AssetChain: []boot.BootAsset{{ 2296 Role: bootloader.RoleRecovery, Name: "shim", 2297 Hashes: []string{ 2298 "dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b", 2299 }, 2300 }, { 2301 Role: bootloader.RoleRecovery, Name: "asset", Hashes: []string{ 2302 "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8", 2303 }, 2304 }}, 2305 Kernel: "pc-kernel-recovery", 2306 KernelRevision: "1", 2307 KernelCmdlines: []string{"snapd_recovery_mode=recover snapd_recovery_system=system"}, 2308 }} 2309 2310 recoveryBootChains := []boot.BootChain{bootChains[1]} 2311 2312 err := boot.WriteBootChains(boot.ToPredictableBootChains(bootChains), filepath.Join(dirs.SnapFDEDir, "boot-chains"), 0) 2313 c.Assert(err, IsNil) 2314 2315 err = boot.WriteBootChains(boot.ToPredictableBootChains(recoveryBootChains), filepath.Join(dirs.SnapFDEDir, "recovery-boot-chains"), 0) 2316 c.Assert(err, IsNil) 2317 2318 // mark successful 2319 err = boot.MarkBootSuccessful(coreDev) 2320 c.Assert(err, IsNil) 2321 2322 // modeenv is unchanged 2323 m2, err := boot.ReadModeenv("") 2324 c.Assert(err, IsNil) 2325 c.Check(m2.CurrentTrustedBootAssets, DeepEquals, m.CurrentTrustedBootAssets) 2326 c.Check(m2.CurrentTrustedRecoveryBootAssets, DeepEquals, m.CurrentTrustedRecoveryBootAssets) 2327 // files are still in cache 2328 checkContentGlob(c, filepath.Join(dirs.SnapBootAssetsDir, "trusted", "*"), []string{ 2329 filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-"+dataHash), 2330 filepath.Join(dirs.SnapBootAssetsDir, "trusted", "shim-"+shimHash), 2331 }) 2332 2333 // boot chains were built 2334 c.Check(tab.BootChainKernelPath, DeepEquals, []string{ 2335 s.kern1.MountFile(), 2336 }) 2337 // no actual reseal 2338 c.Check(resealCalls, Equals, 0) 2339 } 2340 2341 func (s *bootenv20Suite) TestMarkBootSuccessful20BootUnassertedKernelAssetsStableStateHappy(c *C) { 2342 // checked by resealKeyToModeenv 2343 s.stampSealedKeys(c, dirs.GlobalRootDir) 2344 2345 tab := s.bootloaderWithTrustedAssets(c, []string{"nested/asset", "shim"}) 2346 2347 data := []byte("foobar") 2348 // SHA3-384 2349 dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8" 2350 shim := []byte("shim") 2351 shimHash := "dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b" 2352 2353 c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir, "nested"), 0755), IsNil) 2354 c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir, "nested"), 0755), IsNil) 2355 // only asset for ubuntu-boot 2356 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "nested/asset"), data, 0644), IsNil) 2357 // shim and asset for ubuntu-seed 2358 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "nested/asset"), data, 0644), IsNil) 2359 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "shim"), shim, 0644), IsNil) 2360 2361 // mock the files in cache 2362 mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{ 2363 "shim-" + shimHash, 2364 "asset-" + dataHash, 2365 }) 2366 2367 runKernelBf := bootloader.NewBootFile(filepath.Join(s.ukern1.Filename()), "kernel.efi", bootloader.RoleRunMode) 2368 recoveryKernelBf := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery) 2369 2370 tab.BootChainList = []bootloader.BootFile{ 2371 bootloader.NewBootFile("", "shim", bootloader.RoleRecovery), 2372 bootloader.NewBootFile("", "asset", bootloader.RoleRecovery), 2373 runKernelBf, 2374 } 2375 tab.RecoveryBootChainList = []bootloader.BootFile{ 2376 bootloader.NewBootFile("", "shim", bootloader.RoleRecovery), 2377 bootloader.NewBootFile("", "asset", bootloader.RoleRecovery), 2378 recoveryKernelBf, 2379 } 2380 2381 uc20Model := boottest.MakeMockUC20Model() 2382 2383 restore := boot.MockSeedReadSystemEssential(func(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*seed.Snap, error) { 2384 kernelSnap := &seed.Snap{ 2385 Path: "/var/lib/snapd/seed/snaps/pc-kernel-recovery_1.snap", 2386 SideInfo: &snap.SideInfo{ 2387 Revision: snap.Revision{N: 1}, 2388 RealName: "pc-kernel-recovery", 2389 }, 2390 } 2391 return uc20Model, []*seed.Snap{kernelSnap}, nil 2392 }) 2393 defer restore() 2394 2395 // we were trying an update of boot assets 2396 m := &boot.Modeenv{ 2397 Mode: "run", 2398 Base: s.base1.Filename(), 2399 CurrentKernels: []string{s.ukern1.Filename()}, 2400 CurrentTrustedBootAssets: boot.BootAssetsMap{ 2401 "asset": {dataHash}, 2402 }, 2403 CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{ 2404 "asset": {dataHash}, 2405 "shim": {shimHash}, 2406 }, 2407 CurrentRecoverySystems: []string{"system"}, 2408 GoodRecoverySystems: []string{"system"}, 2409 CurrentKernelCommandLines: boot.BootCommandLines{"snapd_recovery_mode=run"}, 2410 } 2411 r := setupUC20Bootenv( 2412 c, 2413 tab.MockBootloader, 2414 &bootenv20Setup{ 2415 modeenv: m, 2416 kern: s.ukern1, 2417 kernStatus: boot.DefaultStatus, 2418 }, 2419 ) 2420 defer r() 2421 2422 coreDev := boottest.MockUC20Device("", uc20Model) 2423 c.Assert(coreDev.HasModeenv(), Equals, true) 2424 2425 resealCalls := 0 2426 restore = boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { 2427 resealCalls++ 2428 return nil 2429 }) 2430 defer restore() 2431 2432 // write boot-chains for current state that will stay unchanged 2433 bootChains := []boot.BootChain{{ 2434 BrandID: "my-brand", 2435 Model: "my-model-uc20", 2436 Grade: "dangerous", 2437 ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij", 2438 AssetChain: []boot.BootAsset{{ 2439 Role: bootloader.RoleRecovery, Name: "shim", 2440 Hashes: []string{ 2441 "dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b", 2442 }, 2443 }, { 2444 Role: bootloader.RoleRecovery, Name: "asset", Hashes: []string{ 2445 "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8", 2446 }, 2447 }}, 2448 Kernel: "pc-kernel", 2449 // unasserted kernel snap 2450 KernelRevision: "", 2451 KernelCmdlines: []string{"snapd_recovery_mode=run"}, 2452 }, { 2453 BrandID: "my-brand", 2454 Model: "my-model-uc20", 2455 Grade: "dangerous", 2456 ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij", 2457 AssetChain: []boot.BootAsset{{ 2458 Role: bootloader.RoleRecovery, Name: "shim", 2459 Hashes: []string{ 2460 "dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b", 2461 }, 2462 }, { 2463 Role: bootloader.RoleRecovery, Name: "asset", Hashes: []string{ 2464 "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8", 2465 }, 2466 }}, 2467 Kernel: "pc-kernel-recovery", 2468 KernelRevision: "1", 2469 KernelCmdlines: []string{"snapd_recovery_mode=recover snapd_recovery_system=system"}, 2470 }} 2471 2472 recoveryBootChains := []boot.BootChain{bootChains[1]} 2473 2474 err := boot.WriteBootChains(boot.ToPredictableBootChains(bootChains), filepath.Join(dirs.SnapFDEDir, "boot-chains"), 0) 2475 c.Assert(err, IsNil) 2476 2477 err = boot.WriteBootChains(boot.ToPredictableBootChains(recoveryBootChains), filepath.Join(dirs.SnapFDEDir, "recovery-boot-chains"), 0) 2478 c.Assert(err, IsNil) 2479 2480 // mark successful 2481 err = boot.MarkBootSuccessful(coreDev) 2482 c.Assert(err, IsNil) 2483 2484 // modeenv is unchanged 2485 m2, err := boot.ReadModeenv("") 2486 c.Assert(err, IsNil) 2487 c.Check(m2.CurrentTrustedBootAssets, DeepEquals, m.CurrentTrustedBootAssets) 2488 c.Check(m2.CurrentTrustedRecoveryBootAssets, DeepEquals, m.CurrentTrustedRecoveryBootAssets) 2489 // files are still in cache 2490 checkContentGlob(c, filepath.Join(dirs.SnapBootAssetsDir, "trusted", "*"), []string{ 2491 filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-"+dataHash), 2492 filepath.Join(dirs.SnapBootAssetsDir, "trusted", "shim-"+shimHash), 2493 }) 2494 2495 // boot chains were built 2496 c.Check(tab.BootChainKernelPath, DeepEquals, []string{ 2497 s.ukern1.MountFile(), 2498 }) 2499 // no actual reseal 2500 c.Check(resealCalls, Equals, 0) 2501 } 2502 2503 func (s *bootenv20Suite) TestMarkBootSuccessful20BootAssetsUpdateUnexpectedAsset(c *C) { 2504 tab := s.bootloaderWithTrustedAssets(c, []string{"EFI/asset"}) 2505 2506 data := []byte("foobar") 2507 // SHA3-384 2508 dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8" 2509 2510 c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir, "EFI"), 0755), IsNil) 2511 c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI"), 0755), IsNil) 2512 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "EFI/asset"), data, 0644), IsNil) 2513 c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI/asset"), data, 0644), IsNil) 2514 // mock some state in the cache 2515 mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{ 2516 "asset-one", 2517 "asset-two", 2518 }) 2519 2520 // we were trying an update of boot assets 2521 m := &boot.Modeenv{ 2522 Mode: "run", 2523 Base: s.base1.Filename(), 2524 CurrentKernels: []string{s.kern1.Filename()}, 2525 CurrentTrustedBootAssets: boot.BootAssetsMap{ 2526 // hash will not match 2527 "asset": {"one", "two"}, 2528 }, 2529 CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{ 2530 "asset": {"one", "two"}, 2531 }, 2532 } 2533 r := setupUC20Bootenv( 2534 c, 2535 tab.MockBootloader, 2536 &bootenv20Setup{ 2537 modeenv: m, 2538 kern: s.kern1, 2539 kernStatus: boot.DefaultStatus, 2540 }, 2541 ) 2542 defer r() 2543 2544 coreDev := boottest.MockUC20Device("", nil) 2545 c.Assert(coreDev.HasModeenv(), Equals, true) 2546 2547 // mark successful 2548 err := boot.MarkBootSuccessful(coreDev) 2549 c.Assert(err, ErrorMatches, fmt.Sprintf(`cannot mark boot successful: cannot mark successful boot assets: system booted with unexpected run mode bootloader asset "EFI/asset" hash %s`, dataHash)) 2550 2551 // check the modeenv 2552 m2, err := boot.ReadModeenv("") 2553 c.Assert(err, IsNil) 2554 // modeenv is unchaged 2555 c.Check(m2.CurrentTrustedBootAssets, DeepEquals, m.CurrentTrustedBootAssets) 2556 c.Check(m2.CurrentTrustedRecoveryBootAssets, DeepEquals, m.CurrentTrustedRecoveryBootAssets) 2557 // nothing was removed from cache 2558 checkContentGlob(c, filepath.Join(dirs.SnapBootAssetsDir, "trusted", "*"), []string{ 2559 filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-one"), 2560 filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-two"), 2561 }) 2562 } 2563 2564 func (s *bootenv20Suite) setupMarkBootSuccessful20CommandLine(c *C, mode string, cmdlines boot.BootCommandLines) *boot.Modeenv { 2565 // mock some state in the cache 2566 mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{ 2567 "asset-one", 2568 }) 2569 // a pending kernel command line change 2570 m := &boot.Modeenv{ 2571 Mode: mode, 2572 Base: s.base1.Filename(), 2573 CurrentKernels: []string{s.kern1.Filename()}, 2574 CurrentTrustedBootAssets: boot.BootAssetsMap{ 2575 "asset": {"one"}, 2576 }, 2577 CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{ 2578 "asset": {"one"}, 2579 }, 2580 CurrentKernelCommandLines: cmdlines, 2581 } 2582 return m 2583 } 2584 2585 func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedHappy(c *C) { 2586 s.mockCmdline(c, "snapd_recovery_mode=run candidate panic=-1") 2587 tab := s.bootloaderWithTrustedAssets(c, []string{"asset"}) 2588 m := s.setupMarkBootSuccessful20CommandLine(c, "run", boot.BootCommandLines{ 2589 "snapd_recovery_mode=run panic=-1", 2590 "snapd_recovery_mode=run candidate panic=-1", 2591 }) 2592 2593 r := setupUC20Bootenv( 2594 c, 2595 tab.MockBootloader, 2596 &bootenv20Setup{ 2597 modeenv: m, 2598 kern: s.kern1, 2599 kernStatus: boot.DefaultStatus, 2600 }, 2601 ) 2602 defer r() 2603 2604 coreDev := boottest.MockUC20Device("", nil) 2605 c.Assert(coreDev.HasModeenv(), Equals, true) 2606 // mark successful 2607 err := boot.MarkBootSuccessful(coreDev) 2608 c.Assert(err, IsNil) 2609 2610 // check the modeenv 2611 m2, err := boot.ReadModeenv("") 2612 c.Assert(err, IsNil) 2613 // modeenv is unchaged 2614 c.Check(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{ 2615 "snapd_recovery_mode=run candidate panic=-1", 2616 }) 2617 } 2618 2619 func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedOld(c *C) { 2620 s.mockCmdline(c, "snapd_recovery_mode=run panic=-1") 2621 tab := s.bootloaderWithTrustedAssets(c, []string{"asset"}) 2622 m := s.setupMarkBootSuccessful20CommandLine(c, "run", boot.BootCommandLines{ 2623 "snapd_recovery_mode=run panic=-1", 2624 "snapd_recovery_mode=run candidate panic=-1", 2625 }) 2626 r := setupUC20Bootenv( 2627 c, 2628 tab.MockBootloader, 2629 &bootenv20Setup{ 2630 modeenv: m, 2631 kern: s.kern1, 2632 kernStatus: boot.DefaultStatus, 2633 }, 2634 ) 2635 defer r() 2636 2637 coreDev := boottest.MockUC20Device("", nil) 2638 c.Assert(coreDev.HasModeenv(), Equals, true) 2639 // mark successful 2640 err := boot.MarkBootSuccessful(coreDev) 2641 c.Assert(err, IsNil) 2642 2643 // check the modeenv 2644 m2, err := boot.ReadModeenv("") 2645 c.Assert(err, IsNil) 2646 // modeenv is unchaged 2647 c.Check(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{ 2648 "snapd_recovery_mode=run panic=-1", 2649 }) 2650 } 2651 2652 func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedMismatch(c *C) { 2653 s.mockCmdline(c, "snapd_recovery_mode=run different") 2654 tab := s.bootloaderWithTrustedAssets(c, []string{"asset"}) 2655 m := s.setupMarkBootSuccessful20CommandLine(c, "run", boot.BootCommandLines{ 2656 "snapd_recovery_mode=run", 2657 "snapd_recovery_mode=run candidate", 2658 }) 2659 r := setupUC20Bootenv( 2660 c, 2661 tab.MockBootloader, 2662 &bootenv20Setup{ 2663 modeenv: m, 2664 kern: s.kern1, 2665 kernStatus: boot.DefaultStatus, 2666 }, 2667 ) 2668 defer r() 2669 2670 coreDev := boottest.MockUC20Device("", nil) 2671 c.Assert(coreDev.HasModeenv(), Equals, true) 2672 // mark successful 2673 err := boot.MarkBootSuccessful(coreDev) 2674 c.Assert(err, ErrorMatches, `cannot mark boot successful: cannot mark successful boot command line: current command line content "snapd_recovery_mode=run different" not matching any expected entry`) 2675 } 2676 2677 func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedFallbackOnBootSuccessful(c *C) { 2678 s.mockCmdline(c, "snapd_recovery_mode=run panic=-1") 2679 tab := s.bootloaderWithTrustedAssets(c, []string{"asset"}) 2680 tab.StaticCommandLine = "panic=-1" 2681 m := s.setupMarkBootSuccessful20CommandLine(c, "run", nil) 2682 r := setupUC20Bootenv( 2683 c, 2684 tab.MockBootloader, 2685 &bootenv20Setup{ 2686 modeenv: m, 2687 kern: s.kern1, 2688 kernStatus: boot.DefaultStatus, 2689 }, 2690 ) 2691 defer r() 2692 2693 coreDev := boottest.MockUC20Device("", nil) 2694 c.Assert(coreDev.HasModeenv(), Equals, true) 2695 // mark successful 2696 err := boot.MarkBootSuccessful(coreDev) 2697 c.Assert(err, IsNil) 2698 2699 // check the modeenv 2700 m2, err := boot.ReadModeenv("") 2701 c.Assert(err, IsNil) 2702 // modeenv is unchaged 2703 c.Check(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{ 2704 "snapd_recovery_mode=run panic=-1", 2705 }) 2706 } 2707 2708 func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedFallbackOnBootMismatch(c *C) { 2709 s.mockCmdline(c, "snapd_recovery_mode=run panic=-1 unexpected") 2710 tab := s.bootloaderWithTrustedAssets(c, []string{"asset"}) 2711 tab.StaticCommandLine = "panic=-1" 2712 m := s.setupMarkBootSuccessful20CommandLine(c, "run", nil) 2713 r := setupUC20Bootenv( 2714 c, 2715 tab.MockBootloader, 2716 &bootenv20Setup{ 2717 modeenv: m, 2718 kern: s.kern1, 2719 kernStatus: boot.DefaultStatus, 2720 }, 2721 ) 2722 defer r() 2723 2724 coreDev := boottest.MockUC20Device("", nil) 2725 c.Assert(coreDev.HasModeenv(), Equals, true) 2726 // mark successful 2727 err := boot.MarkBootSuccessful(coreDev) 2728 c.Assert(err, ErrorMatches, `cannot mark boot successful: cannot mark successful boot command line: unexpected current command line: "snapd_recovery_mode=run panic=-1 unexpected"`) 2729 } 2730 2731 func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineNonRunMode(c *C) { 2732 // recover mode 2733 s.mockCmdline(c, "snapd_recovery_mode=recover snapd_recovery_system=1234 panic=-1") 2734 tab := s.bootloaderWithTrustedAssets(c, []string{"asset"}) 2735 tab.StaticCommandLine = "panic=-1" 2736 // current command line does not match any of the run mode command lines 2737 m := s.setupMarkBootSuccessful20CommandLine(c, "recover", boot.BootCommandLines{ 2738 "snapd_recovery_mode=run panic=-1", 2739 "snapd_recovery_mode=run candidate panic=-1", 2740 }) 2741 r := setupUC20Bootenv( 2742 c, 2743 tab.MockBootloader, 2744 &bootenv20Setup{ 2745 modeenv: m, 2746 kern: s.kern1, 2747 kernStatus: boot.DefaultStatus, 2748 }, 2749 ) 2750 defer r() 2751 2752 coreDev := boottest.MockUC20Device("", nil) 2753 c.Assert(coreDev.HasModeenv(), Equals, true) 2754 // mark successful 2755 err := boot.MarkBootSuccessful(coreDev) 2756 c.Assert(err, IsNil) 2757 2758 // check the modeenv 2759 m2, err := boot.ReadModeenv("") 2760 c.Assert(err, IsNil) 2761 // modeenv is unchaged 2762 c.Check(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{ 2763 "snapd_recovery_mode=run panic=-1", 2764 "snapd_recovery_mode=run candidate panic=-1", 2765 }) 2766 } 2767 2768 func (s *bootenv20Suite) TestMarkBootSuccessful20SystemsCompat(c *C) { 2769 b := bootloadertest.Mock("mock", s.bootdir) 2770 s.forceBootloader(b) 2771 2772 m := &boot.Modeenv{ 2773 Mode: "run", 2774 Base: s.base1.Filename(), 2775 CurrentKernels: []string{s.kern1.Filename()}, 2776 CurrentRecoverySystems: []string{"1234"}, 2777 } 2778 2779 r := setupUC20Bootenv( 2780 c, 2781 b, 2782 &bootenv20Setup{ 2783 modeenv: m, 2784 kern: s.kern1, 2785 kernStatus: boot.DefaultStatus, 2786 }, 2787 ) 2788 defer r() 2789 2790 coreDev := boottest.MockUC20Device("", nil) 2791 c.Assert(coreDev.HasModeenv(), Equals, true) 2792 // mark successful 2793 err := boot.MarkBootSuccessful(coreDev) 2794 c.Assert(err, IsNil) 2795 2796 // check the modeenv 2797 m2, err := boot.ReadModeenv("") 2798 c.Assert(err, IsNil) 2799 // the list of good recovery systems has not been modified 2800 c.Check(m2.GoodRecoverySystems, DeepEquals, []string{"1234"}) 2801 c.Check(m2.CurrentRecoverySystems, DeepEquals, []string{"1234"}) 2802 } 2803 2804 func (s *bootenv20Suite) TestMarkBootSuccessful20SystemsPopulated(c *C) { 2805 b := bootloadertest.Mock("mock", s.bootdir) 2806 s.forceBootloader(b) 2807 2808 m := &boot.Modeenv{ 2809 Mode: "run", 2810 Base: s.base1.Filename(), 2811 CurrentKernels: []string{s.kern1.Filename()}, 2812 CurrentRecoverySystems: []string{"1234", "9999"}, 2813 GoodRecoverySystems: []string{"1234"}, 2814 } 2815 2816 r := setupUC20Bootenv( 2817 c, 2818 b, 2819 &bootenv20Setup{ 2820 modeenv: m, 2821 kern: s.kern1, 2822 kernStatus: boot.DefaultStatus, 2823 }, 2824 ) 2825 defer r() 2826 2827 coreDev := boottest.MockUC20Device("", nil) 2828 c.Assert(coreDev.HasModeenv(), Equals, true) 2829 // mark successful 2830 err := boot.MarkBootSuccessful(coreDev) 2831 c.Assert(err, IsNil) 2832 2833 // check the modeenv 2834 m2, err := boot.ReadModeenv("") 2835 c.Assert(err, IsNil) 2836 // good recovery systems has been populated 2837 c.Check(m2.GoodRecoverySystems, DeepEquals, []string{"1234"}) 2838 c.Check(m2.CurrentRecoverySystems, DeepEquals, []string{"1234", "9999"}) 2839 } 2840 2841 type recoveryBootenv20Suite struct { 2842 baseBootenvSuite 2843 2844 bootloader *bootloadertest.MockBootloader 2845 2846 dev boot.Device 2847 } 2848 2849 var _ = Suite(&recoveryBootenv20Suite{}) 2850 2851 func (s *recoveryBootenv20Suite) SetUpTest(c *C) { 2852 s.baseBootenvSuite.SetUpTest(c) 2853 2854 s.bootloader = bootloadertest.Mock("mock", c.MkDir()) 2855 s.forceBootloader(s.bootloader) 2856 2857 s.dev = boottest.MockUC20Device("", nil) 2858 } 2859 2860 func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeHappy(c *C) { 2861 err := boot.SetRecoveryBootSystemAndMode(s.dev, "1234", "install") 2862 c.Assert(err, IsNil) 2863 c.Check(s.bootloader.BootVars, DeepEquals, map[string]string{ 2864 "snapd_recovery_system": "1234", 2865 "snapd_recovery_mode": "install", 2866 }) 2867 } 2868 2869 func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeSetErr(c *C) { 2870 s.bootloader.SetErr = errors.New("no can do") 2871 err := boot.SetRecoveryBootSystemAndMode(s.dev, "1234", "install") 2872 c.Assert(err, ErrorMatches, `no can do`) 2873 } 2874 2875 func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeNonUC20(c *C) { 2876 non20Dev := boottest.MockDevice("some-snap") 2877 err := boot.SetRecoveryBootSystemAndMode(non20Dev, "1234", "install") 2878 c.Assert(err, Equals, boot.ErrUnsupportedSystemMode) 2879 } 2880 2881 func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeErrClumsy(c *C) { 2882 err := boot.SetRecoveryBootSystemAndMode(s.dev, "", "install") 2883 c.Assert(err, ErrorMatches, "internal error: system label is unset") 2884 err = boot.SetRecoveryBootSystemAndMode(s.dev, "1234", "") 2885 c.Assert(err, ErrorMatches, "internal error: system mode is unset") 2886 } 2887 2888 func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeRealHappy(c *C) { 2889 bootloader.Force(nil) 2890 2891 mockSeedGrubDir := filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI", "ubuntu") 2892 err := os.MkdirAll(mockSeedGrubDir, 0755) 2893 c.Assert(err, IsNil) 2894 err = ioutil.WriteFile(filepath.Join(mockSeedGrubDir, "grub.cfg"), nil, 0644) 2895 c.Assert(err, IsNil) 2896 2897 err = boot.SetRecoveryBootSystemAndMode(s.dev, "1234", "install") 2898 c.Assert(err, IsNil) 2899 2900 bl, err := bootloader.Find(boot.InitramfsUbuntuSeedDir, &bootloader.Options{Role: bootloader.RoleRecovery}) 2901 c.Assert(err, IsNil) 2902 2903 blvars, err := bl.GetBootVars("snapd_recovery_mode", "snapd_recovery_system") 2904 c.Assert(err, IsNil) 2905 c.Check(blvars, DeepEquals, map[string]string{ 2906 "snapd_recovery_system": "1234", 2907 "snapd_recovery_mode": "install", 2908 }) 2909 } 2910 2911 type bootConfigSuite struct { 2912 baseBootenvSuite 2913 2914 bootloader *bootloadertest.MockTrustedAssetsBootloader 2915 } 2916 2917 var _ = Suite(&bootConfigSuite{}) 2918 2919 func (s *bootConfigSuite) SetUpTest(c *C) { 2920 s.baseBootenvSuite.SetUpTest(c) 2921 2922 s.bootloader = bootloadertest.Mock("trusted", c.MkDir()).WithTrustedAssets() 2923 s.bootloader.StaticCommandLine = "this is mocked panic=-1" 2924 s.bootloader.CandidateStaticCommandLine = "mocked candidate panic=-1" 2925 s.forceBootloader(s.bootloader) 2926 2927 s.mockCmdline(c, "snapd_recovery_mode=run this is mocked panic=-1") 2928 } 2929 2930 func (s *bootConfigSuite) mockCmdline(c *C, cmdline string) { 2931 c.Assert(ioutil.WriteFile(s.cmdlineFile, []byte(cmdline), 0644), IsNil) 2932 } 2933 2934 func (s *bootConfigSuite) TestBootConfigUpdateHappyNoKeysNoReseal(c *C) { 2935 coreDev := boottest.MockUC20Device("", nil) 2936 c.Assert(coreDev.HasModeenv(), Equals, true) 2937 2938 m := &boot.Modeenv{ 2939 Mode: "run", 2940 CurrentKernelCommandLines: boot.BootCommandLines{ 2941 "snapd_recovery_mode=run this is mocked panic=-1", 2942 }, 2943 } 2944 c.Assert(m.WriteTo(""), IsNil) 2945 2946 resealCalls := 0 2947 restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { 2948 resealCalls++ 2949 return nil 2950 }) 2951 defer restore() 2952 2953 updated, err := boot.UpdateManagedBootConfigs(coreDev) 2954 c.Assert(err, IsNil) 2955 c.Check(updated, Equals, false) 2956 c.Check(s.bootloader.UpdateCalls, Equals, 1) 2957 c.Check(resealCalls, Equals, 0) 2958 } 2959 2960 func (s *bootConfigSuite) TestBootConfigUpdateHappyWithReseal(c *C) { 2961 s.stampSealedKeys(c, dirs.GlobalRootDir) 2962 2963 coreDev := boottest.MockUC20Device("", nil) 2964 c.Assert(coreDev.HasModeenv(), Equals, true) 2965 2966 runKernelBf := bootloader.NewBootFile("/var/lib/snapd/snap/pc-kernel_600.snap", "kernel.efi", bootloader.RoleRunMode) 2967 recoveryKernelBf := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery) 2968 mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{ 2969 "asset-hash-1", 2970 }) 2971 2972 s.bootloader.TrustedAssetsList = []string{"asset"} 2973 s.bootloader.BootChainList = []bootloader.BootFile{ 2974 bootloader.NewBootFile("", "asset", bootloader.RoleRunMode), 2975 runKernelBf, 2976 } 2977 s.bootloader.RecoveryBootChainList = []bootloader.BootFile{ 2978 bootloader.NewBootFile("", "asset", bootloader.RoleRecovery), 2979 recoveryKernelBf, 2980 } 2981 m := &boot.Modeenv{ 2982 Mode: "run", 2983 CurrentKernels: []string{"pc-kernel_500.snap"}, 2984 CurrentKernelCommandLines: boot.BootCommandLines{ 2985 "snapd_recovery_mode=run this is mocked panic=-1", 2986 }, 2987 CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{ 2988 "asset": []string{"hash-1"}, 2989 }, 2990 CurrentTrustedBootAssets: boot.BootAssetsMap{ 2991 "asset": []string{"hash-1"}, 2992 }, 2993 } 2994 c.Assert(m.WriteTo(""), IsNil) 2995 2996 resealCalls := 0 2997 restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { 2998 resealCalls++ 2999 c.Assert(params, NotNil) 3000 c.Assert(params.ModelParams, HasLen, 1) 3001 c.Check(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{ 3002 "snapd_recovery_mode=run mocked candidate panic=-1", 3003 "snapd_recovery_mode=run this is mocked panic=-1", 3004 }) 3005 return nil 3006 }) 3007 defer restore() 3008 3009 updated, err := boot.UpdateManagedBootConfigs(coreDev) 3010 c.Assert(err, IsNil) 3011 c.Check(updated, Equals, false) 3012 c.Check(s.bootloader.UpdateCalls, Equals, 1) 3013 c.Check(resealCalls, Equals, 1) 3014 3015 m2, err := boot.ReadModeenv("") 3016 c.Assert(err, IsNil) 3017 c.Assert(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{ 3018 "snapd_recovery_mode=run this is mocked panic=-1", 3019 "snapd_recovery_mode=run mocked candidate panic=-1", 3020 }) 3021 } 3022 3023 func (s *bootConfigSuite) TestBootConfigUpdateHappyNoChange(c *C) { 3024 s.stampSealedKeys(c, dirs.GlobalRootDir) 3025 3026 coreDev := boottest.MockUC20Device("", nil) 3027 c.Assert(coreDev.HasModeenv(), Equals, true) 3028 3029 s.bootloader.StaticCommandLine = "mocked unchanged panic=-1" 3030 s.bootloader.CandidateStaticCommandLine = "mocked unchanged panic=-1" 3031 s.mockCmdline(c, "snapd_recovery_mode=run mocked unchanged panic=-1") 3032 3033 m := &boot.Modeenv{ 3034 Mode: "run", 3035 CurrentKernelCommandLines: boot.BootCommandLines{ 3036 "snapd_recovery_mode=run mocked unchanged panic=-1", 3037 }, 3038 } 3039 c.Assert(m.WriteTo(""), IsNil) 3040 3041 resealCalls := 0 3042 restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { 3043 resealCalls++ 3044 return nil 3045 }) 3046 defer restore() 3047 3048 updated, err := boot.UpdateManagedBootConfigs(coreDev) 3049 c.Assert(err, IsNil) 3050 c.Check(updated, Equals, false) 3051 c.Check(s.bootloader.UpdateCalls, Equals, 1) 3052 c.Check(resealCalls, Equals, 0) 3053 3054 m2, err := boot.ReadModeenv("") 3055 c.Assert(err, IsNil) 3056 c.Assert(m2.CurrentKernelCommandLines, HasLen, 1) 3057 } 3058 3059 func (s *bootConfigSuite) TestBootConfigUpdateNonUC20DoesNothing(c *C) { 3060 nonUC20coreDev := boottest.MockDevice("pc-kernel") 3061 c.Assert(nonUC20coreDev.HasModeenv(), Equals, false) 3062 updated, err := boot.UpdateManagedBootConfigs(nonUC20coreDev) 3063 c.Assert(err, IsNil) 3064 c.Check(updated, Equals, false) 3065 c.Check(s.bootloader.UpdateCalls, Equals, 0) 3066 } 3067 3068 func (s *bootConfigSuite) TestBootConfigUpdateBadModeErr(c *C) { 3069 uc20Dev := boottest.MockUC20Device("recover", nil) 3070 c.Assert(uc20Dev.HasModeenv(), Equals, true) 3071 updated, err := boot.UpdateManagedBootConfigs(uc20Dev) 3072 c.Assert(err, ErrorMatches, "internal error: boot config can only be updated in run mode") 3073 c.Check(updated, Equals, false) 3074 c.Check(s.bootloader.UpdateCalls, Equals, 0) 3075 } 3076 3077 func (s *bootConfigSuite) TestBootConfigUpdateFailErr(c *C) { 3078 coreDev := boottest.MockUC20Device("", nil) 3079 c.Assert(coreDev.HasModeenv(), Equals, true) 3080 3081 m := &boot.Modeenv{ 3082 Mode: "run", 3083 CurrentKernelCommandLines: boot.BootCommandLines{ 3084 "snapd_recovery_mode=run this is mocked panic=-1", 3085 }, 3086 } 3087 c.Assert(m.WriteTo(""), IsNil) 3088 3089 s.bootloader.UpdateErr = errors.New("update fail") 3090 3091 updated, err := boot.UpdateManagedBootConfigs(coreDev) 3092 c.Assert(err, ErrorMatches, "update fail") 3093 c.Check(updated, Equals, false) 3094 c.Check(s.bootloader.UpdateCalls, Equals, 1) 3095 } 3096 3097 func (s *bootConfigSuite) TestBootConfigUpdateCmdlineMismatchErr(c *C) { 3098 coreDev := boottest.MockUC20Device("", nil) 3099 c.Assert(coreDev.HasModeenv(), Equals, true) 3100 3101 m := &boot.Modeenv{ 3102 Mode: "run", 3103 } 3104 c.Assert(m.WriteTo(""), IsNil) 3105 3106 s.mockCmdline(c, "snapd_recovery_mode=run unexpected cmdline") 3107 3108 updated, err := boot.UpdateManagedBootConfigs(coreDev) 3109 c.Assert(err, ErrorMatches, `internal error: current kernel command lines is unset`) 3110 c.Check(updated, Equals, false) 3111 c.Check(s.bootloader.UpdateCalls, Equals, 0) 3112 } 3113 3114 func (s *bootConfigSuite) TestBootConfigUpdateNotManagedErr(c *C) { 3115 coreDev := boottest.MockUC20Device("", nil) 3116 c.Assert(coreDev.HasModeenv(), Equals, true) 3117 3118 bl := bootloadertest.Mock("not-managed", c.MkDir()) 3119 bootloader.Force(bl) 3120 defer bootloader.Force(nil) 3121 3122 m := &boot.Modeenv{ 3123 Mode: "run", 3124 } 3125 c.Assert(m.WriteTo(""), IsNil) 3126 3127 updated, err := boot.UpdateManagedBootConfigs(coreDev) 3128 c.Assert(err, IsNil) 3129 c.Check(updated, Equals, false) 3130 c.Check(s.bootloader.UpdateCalls, Equals, 0) 3131 } 3132 3133 func (s *bootConfigSuite) TestBootConfigUpdateBootloaderFindErr(c *C) { 3134 coreDev := boottest.MockUC20Device("", nil) 3135 c.Assert(coreDev.HasModeenv(), Equals, true) 3136 3137 bootloader.ForceError(errors.New("mocked find error")) 3138 defer bootloader.ForceError(nil) 3139 3140 m := &boot.Modeenv{ 3141 Mode: "run", 3142 } 3143 c.Assert(m.WriteTo(""), IsNil) 3144 3145 updated, err := boot.UpdateManagedBootConfigs(coreDev) 3146 c.Assert(err, ErrorMatches, "internal error: cannot find trusted assets bootloader under .*: mocked find error") 3147 c.Check(updated, Equals, false) 3148 c.Check(s.bootloader.UpdateCalls, Equals, 0) 3149 }