github.com/freetocompute/snapd@v0.0.0-20210618182524-2fb355d72fd9/boot/seal_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 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 "strconv" 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/kernel/fde" 39 "github.com/snapcore/snapd/osutil" 40 "github.com/snapcore/snapd/secboot" 41 "github.com/snapcore/snapd/seed" 42 "github.com/snapcore/snapd/snap" 43 "github.com/snapcore/snapd/snap/snaptest" 44 "github.com/snapcore/snapd/testutil" 45 "github.com/snapcore/snapd/timings" 46 ) 47 48 type sealSuite struct { 49 testutil.BaseTest 50 } 51 52 var _ = Suite(&sealSuite{}) 53 54 func (s *sealSuite) SetUpTest(c *C) { 55 s.BaseTest.SetUpTest(c) 56 57 rootdir := c.MkDir() 58 dirs.SetRootDir(rootdir) 59 s.AddCleanup(func() { dirs.SetRootDir("/") }) 60 } 61 62 func mockKernelSeedSnap(c *C, rev snap.Revision) *seed.Snap { 63 return mockNamedKernelSeedSnap(c, rev, "pc-kernel") 64 } 65 66 func mockNamedKernelSeedSnap(c *C, rev snap.Revision, name string) *seed.Snap { 67 revAsString := rev.String() 68 if rev.Unset() { 69 revAsString = "unset" 70 } 71 return &seed.Snap{ 72 Path: fmt.Sprintf("/var/lib/snapd/seed/snaps/%v_%v.snap", name, revAsString), 73 SideInfo: &snap.SideInfo{ 74 RealName: name, 75 Revision: rev, 76 }, 77 EssentialType: snap.TypeKernel, 78 } 79 } 80 81 func mockGadgetSeedSnap(c *C, files [][]string) *seed.Snap { 82 gadgetSnapFile := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, files) 83 return &seed.Snap{ 84 Path: gadgetSnapFile, 85 SideInfo: &snap.SideInfo{ 86 RealName: "gadget", 87 Revision: snap.R(1), 88 }, 89 EssentialType: snap.TypeGadget, 90 } 91 } 92 93 func (s *sealSuite) TestSealKeyToModeenv(c *C) { 94 for _, tc := range []struct { 95 sealErr error 96 err string 97 }{ 98 {sealErr: nil, err: ""}, 99 {sealErr: errors.New("seal error"), err: "cannot seal the encryption keys: seal error"}, 100 } { 101 rootdir := c.MkDir() 102 dirs.SetRootDir(rootdir) 103 defer dirs.SetRootDir("") 104 105 err := createMockGrubCfg(filepath.Join(rootdir, "run/mnt/ubuntu-seed")) 106 c.Assert(err, IsNil) 107 108 err = createMockGrubCfg(filepath.Join(rootdir, "run/mnt/ubuntu-boot")) 109 c.Assert(err, IsNil) 110 111 model := boottest.MakeMockUC20Model() 112 113 modeenv := &boot.Modeenv{ 114 RecoverySystem: "20200825", 115 CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{ 116 "grubx64.efi": []string{"grub-hash-1"}, 117 "bootx64.efi": []string{"shim-hash-1"}, 118 }, 119 120 CurrentTrustedBootAssets: boot.BootAssetsMap{ 121 "grubx64.efi": []string{"run-grub-hash-1"}, 122 }, 123 124 CurrentKernels: []string{"pc-kernel_500.snap"}, 125 126 CurrentKernelCommandLines: boot.BootCommandLines{ 127 "snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1", 128 }, 129 Model: model.Model(), 130 BrandID: model.BrandID(), 131 Grade: string(model.Grade()), 132 ModelSignKeyID: model.SignKeyID(), 133 } 134 135 // mock asset cache 136 mockAssetsCache(c, rootdir, "grub", []string{ 137 "bootx64.efi-shim-hash-1", 138 "grubx64.efi-grub-hash-1", 139 "grubx64.efi-run-grub-hash-1", 140 }) 141 142 // set encryption key 143 myKey := secboot.EncryptionKey{} 144 myKey2 := secboot.EncryptionKey{} 145 for i := range myKey { 146 myKey[i] = byte(i) 147 myKey2[i] = byte(128 + i) 148 } 149 150 // set a mock recovery kernel 151 readSystemEssentialCalls := 0 152 restore := boot.MockSeedReadSystemEssential(func(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*seed.Snap, error) { 153 readSystemEssentialCalls++ 154 return model, []*seed.Snap{mockKernelSeedSnap(c, snap.R(1)), mockGadgetSeedSnap(c, nil)}, nil 155 }) 156 defer restore() 157 158 // set mock key sealing 159 sealKeysCalls := 0 160 restore = boot.MockSecbootSealKeys(func(keys []secboot.SealKeyRequest, params *secboot.SealKeysParams) error { 161 sealKeysCalls++ 162 switch sealKeysCalls { 163 case 1: 164 // the run object seals only the ubuntu-data key 165 c.Check(params.TPMPolicyAuthKeyFile, Equals, filepath.Join(boot.InstallHostFDESaveDir, "tpm-policy-auth-key")) 166 c.Check(params.TPMLockoutAuthFile, Equals, filepath.Join(boot.InstallHostFDESaveDir, "tpm-lockout-auth")) 167 168 dataKeyFile := filepath.Join(rootdir, "/run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key") 169 c.Check(keys, DeepEquals, []secboot.SealKeyRequest{{Key: myKey, KeyName: "ubuntu-data", KeyFile: dataKeyFile}}) 170 case 2: 171 // the fallback object seals the ubuntu-data and the ubuntu-save keys 172 c.Check(params.TPMPolicyAuthKeyFile, Equals, "") 173 c.Check(params.TPMLockoutAuthFile, Equals, "") 174 175 dataKeyFile := filepath.Join(rootdir, "/run/mnt/ubuntu-seed/device/fde/ubuntu-data.recovery.sealed-key") 176 saveKeyFile := filepath.Join(rootdir, "/run/mnt/ubuntu-seed/device/fde/ubuntu-save.recovery.sealed-key") 177 c.Check(keys, DeepEquals, []secboot.SealKeyRequest{{Key: myKey, KeyName: "ubuntu-data", KeyFile: dataKeyFile}, {Key: myKey2, KeyName: "ubuntu-save", KeyFile: saveKeyFile}}) 178 default: 179 c.Errorf("unexpected additional call to secboot.SealKeys (call # %d)", sealKeysCalls) 180 } 181 c.Assert(params.ModelParams, HasLen, 1) 182 for _, d := range []string{boot.InitramfsSeedEncryptionKeyDir, boot.InstallHostFDEDataDir} { 183 ex, isdir, _ := osutil.DirExists(d) 184 c.Check(ex && isdir, Equals, true, Commentf("location %q does not exist or is not a directory", d)) 185 } 186 187 shim := bootloader.NewBootFile("", filepath.Join(rootdir, "var/lib/snapd/boot-assets/grub/bootx64.efi-shim-hash-1"), bootloader.RoleRecovery) 188 grub := bootloader.NewBootFile("", filepath.Join(rootdir, "var/lib/snapd/boot-assets/grub/grubx64.efi-grub-hash-1"), bootloader.RoleRecovery) 189 runGrub := bootloader.NewBootFile("", filepath.Join(rootdir, "var/lib/snapd/boot-assets/grub/grubx64.efi-run-grub-hash-1"), bootloader.RoleRunMode) 190 kernel := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery) 191 runKernel := bootloader.NewBootFile(filepath.Join(rootdir, "var/lib/snapd/snaps/pc-kernel_500.snap"), "kernel.efi", bootloader.RoleRunMode) 192 193 switch sealKeysCalls { 194 case 1: 195 c.Assert(params.ModelParams[0].EFILoadChains, DeepEquals, []*secboot.LoadChain{ 196 secboot.NewLoadChain(shim, 197 secboot.NewLoadChain(grub, 198 secboot.NewLoadChain(kernel))), 199 secboot.NewLoadChain(shim, 200 secboot.NewLoadChain(grub, 201 secboot.NewLoadChain(runGrub, 202 secboot.NewLoadChain(runKernel)))), 203 }) 204 c.Assert(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{ 205 "snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", 206 "snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1", 207 }) 208 case 2: 209 c.Assert(params.ModelParams[0].EFILoadChains, DeepEquals, []*secboot.LoadChain{ 210 secboot.NewLoadChain(shim, 211 secboot.NewLoadChain(grub, 212 secboot.NewLoadChain(kernel))), 213 }) 214 c.Assert(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{ 215 "snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", 216 }) 217 default: 218 c.Errorf("unexpected additional call to secboot.SealKeys (call # %d)", sealKeysCalls) 219 } 220 c.Assert(params.ModelParams[0].Model.Model(), Equals, "my-model-uc20") 221 222 return tc.sealErr 223 }) 224 defer restore() 225 226 err = boot.SealKeyToModeenv(myKey, myKey2, model, modeenv) 227 if tc.sealErr != nil { 228 c.Assert(sealKeysCalls, Equals, 1) 229 } else { 230 c.Assert(sealKeysCalls, Equals, 2) 231 } 232 if tc.err == "" { 233 c.Assert(err, IsNil) 234 } else { 235 c.Assert(err, ErrorMatches, tc.err) 236 continue 237 } 238 239 // verify the boot chains data file 240 pbc, cnt, err := boot.ReadBootChains(filepath.Join(dirs.SnapFDEDirUnder(boot.InstallHostWritableDir), "boot-chains")) 241 c.Assert(err, IsNil) 242 c.Check(cnt, Equals, 0) 243 c.Check(pbc, DeepEquals, boot.PredictableBootChains{ 244 boot.BootChain{ 245 BrandID: "my-brand", 246 Model: "my-model-uc20", 247 Grade: "dangerous", 248 ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij", 249 AssetChain: []boot.BootAsset{ 250 { 251 Role: "recovery", 252 Name: "bootx64.efi", 253 Hashes: []string{"shim-hash-1"}, 254 }, 255 { 256 Role: "recovery", 257 Name: "grubx64.efi", 258 Hashes: []string{"grub-hash-1"}, 259 }, 260 }, 261 Kernel: "pc-kernel", 262 KernelRevision: "1", 263 KernelCmdlines: []string{ 264 "snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", 265 }, 266 }, 267 boot.BootChain{ 268 BrandID: "my-brand", 269 Model: "my-model-uc20", 270 Grade: "dangerous", 271 ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij", 272 AssetChain: []boot.BootAsset{ 273 { 274 Role: "recovery", 275 Name: "bootx64.efi", 276 Hashes: []string{"shim-hash-1"}, 277 }, 278 { 279 Role: "recovery", 280 Name: "grubx64.efi", 281 Hashes: []string{"grub-hash-1"}, 282 }, 283 { 284 Role: "run-mode", 285 Name: "grubx64.efi", 286 Hashes: []string{"run-grub-hash-1"}, 287 }, 288 }, 289 Kernel: "pc-kernel", 290 KernelRevision: "500", 291 KernelCmdlines: []string{ 292 "snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1", 293 }, 294 }, 295 }) 296 297 // verify the recovery boot chains 298 pbc, cnt, err = boot.ReadBootChains(filepath.Join(dirs.SnapFDEDirUnder(boot.InstallHostWritableDir), "recovery-boot-chains")) 299 c.Assert(err, IsNil) 300 c.Check(cnt, Equals, 0) 301 c.Check(pbc, DeepEquals, boot.PredictableBootChains{ 302 boot.BootChain{ 303 BrandID: "my-brand", 304 Model: "my-model-uc20", 305 Grade: "dangerous", 306 ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij", 307 AssetChain: []boot.BootAsset{ 308 { 309 Role: "recovery", 310 Name: "bootx64.efi", 311 Hashes: []string{"shim-hash-1"}, 312 }, 313 { 314 Role: "recovery", 315 Name: "grubx64.efi", 316 Hashes: []string{"grub-hash-1"}, 317 }, 318 }, 319 Kernel: "pc-kernel", 320 KernelRevision: "1", 321 KernelCmdlines: []string{ 322 "snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", 323 }, 324 }, 325 }) 326 327 // marker 328 marker := filepath.Join(dirs.SnapFDEDirUnder(boot.InstallHostWritableDir), "sealed-keys") 329 c.Check(marker, testutil.FileEquals, "tpm") 330 } 331 } 332 333 // TODO:UC20: also test fallback reseal 334 func (s *sealSuite) TestResealKeyToModeenvWithSystemFallback(c *C) { 335 var prevPbc boot.PredictableBootChains 336 var prevRecoveryPbc boot.PredictableBootChains 337 338 for idx, tc := range []struct { 339 sealedKeys bool 340 reuseRunPbc bool 341 reuseRecoveryPbc bool 342 resealErr error 343 err string 344 }{ 345 {sealedKeys: false, resealErr: nil, err: ""}, 346 {sealedKeys: true, resealErr: nil, err: ""}, 347 {sealedKeys: true, resealErr: errors.New("reseal error"), err: "cannot reseal the encryption key: reseal error"}, 348 {reuseRunPbc: true, reuseRecoveryPbc: true, sealedKeys: true, resealErr: nil, err: ""}, 349 // recovery boot chain is unchanged 350 {reuseRunPbc: false, reuseRecoveryPbc: true, sealedKeys: true, resealErr: nil, err: ""}, 351 // run boot chain is unchanged 352 {reuseRunPbc: true, reuseRecoveryPbc: false, sealedKeys: true, resealErr: nil, err: ""}, 353 } { 354 c.Logf("tc: %v", idx) 355 rootdir := c.MkDir() 356 dirs.SetRootDir(rootdir) 357 defer dirs.SetRootDir("") 358 359 if tc.sealedKeys { 360 c.Assert(os.MkdirAll(dirs.SnapFDEDir, 0755), IsNil) 361 err := ioutil.WriteFile(filepath.Join(dirs.SnapFDEDir, "sealed-keys"), nil, 0644) 362 c.Assert(err, IsNil) 363 364 } 365 366 err := createMockGrubCfg(filepath.Join(rootdir, "run/mnt/ubuntu-seed")) 367 c.Assert(err, IsNil) 368 369 err = createMockGrubCfg(filepath.Join(rootdir, "run/mnt/ubuntu-boot")) 370 c.Assert(err, IsNil) 371 372 model := boottest.MakeMockUC20Model() 373 374 modeenv := &boot.Modeenv{ 375 CurrentRecoverySystems: []string{"20200825"}, 376 CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{ 377 "grubx64.efi": []string{"grub-hash-1"}, 378 "bootx64.efi": []string{"shim-hash-1", "shim-hash-2"}, 379 }, 380 381 CurrentTrustedBootAssets: boot.BootAssetsMap{ 382 "grubx64.efi": []string{"run-grub-hash-1", "run-grub-hash-2"}, 383 }, 384 385 CurrentKernels: []string{"pc-kernel_500.snap", "pc-kernel_600.snap"}, 386 387 CurrentKernelCommandLines: boot.BootCommandLines{ 388 "snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1", 389 }, 390 Model: model.Model(), 391 BrandID: model.BrandID(), 392 Grade: string(model.Grade()), 393 ModelSignKeyID: model.SignKeyID(), 394 } 395 396 if tc.reuseRunPbc { 397 err := boot.WriteBootChains(prevPbc, filepath.Join(dirs.SnapFDEDir, "boot-chains"), 9) 398 c.Assert(err, IsNil) 399 } 400 if tc.reuseRecoveryPbc { 401 err = boot.WriteBootChains(prevRecoveryPbc, filepath.Join(dirs.SnapFDEDir, "recovery-boot-chains"), 9) 402 c.Assert(err, IsNil) 403 } 404 405 // mock asset cache 406 mockAssetsCache(c, rootdir, "grub", []string{ 407 "bootx64.efi-shim-hash-1", 408 "bootx64.efi-shim-hash-2", 409 "grubx64.efi-grub-hash-1", 410 "grubx64.efi-run-grub-hash-1", 411 "grubx64.efi-run-grub-hash-2", 412 }) 413 414 // set a mock recovery kernel 415 readSystemEssentialCalls := 0 416 restore := boot.MockSeedReadSystemEssential(func(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*seed.Snap, error) { 417 readSystemEssentialCalls++ 418 return model, []*seed.Snap{mockKernelSeedSnap(c, snap.R(1)), mockGadgetSeedSnap(c, nil)}, nil 419 }) 420 defer restore() 421 422 // set mock key resealing 423 resealKeysCalls := 0 424 restore = boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { 425 c.Check(params.TPMPolicyAuthKeyFile, Equals, filepath.Join(dirs.SnapSaveDir, "device/fde", "tpm-policy-auth-key")) 426 427 resealKeysCalls++ 428 c.Assert(params.ModelParams, HasLen, 1) 429 430 // shared parameters 431 c.Assert(params.ModelParams[0].Model.Model(), Equals, "my-model-uc20") 432 433 // recovery parameters 434 shim := bootloader.NewBootFile("", filepath.Join(rootdir, "var/lib/snapd/boot-assets/grub/bootx64.efi-shim-hash-1"), bootloader.RoleRecovery) 435 shim2 := bootloader.NewBootFile("", filepath.Join(rootdir, "var/lib/snapd/boot-assets/grub/bootx64.efi-shim-hash-2"), bootloader.RoleRecovery) 436 grub := bootloader.NewBootFile("", filepath.Join(rootdir, "var/lib/snapd/boot-assets/grub/grubx64.efi-grub-hash-1"), bootloader.RoleRecovery) 437 kernel := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery) 438 // run mode parameters 439 runGrub := bootloader.NewBootFile("", filepath.Join(rootdir, "var/lib/snapd/boot-assets/grub/grubx64.efi-run-grub-hash-1"), bootloader.RoleRunMode) 440 runGrub2 := bootloader.NewBootFile("", filepath.Join(rootdir, "var/lib/snapd/boot-assets/grub/grubx64.efi-run-grub-hash-2"), bootloader.RoleRunMode) 441 runKernel := bootloader.NewBootFile(filepath.Join(rootdir, "var/lib/snapd/snaps/pc-kernel_500.snap"), "kernel.efi", bootloader.RoleRunMode) 442 runKernel2 := bootloader.NewBootFile(filepath.Join(rootdir, "var/lib/snapd/snaps/pc-kernel_600.snap"), "kernel.efi", bootloader.RoleRunMode) 443 444 checkRunParams := func() { 445 c.Check(params.KeyFiles, DeepEquals, []string{ 446 filepath.Join(boot.InitramfsBootEncryptionKeyDir, "ubuntu-data.sealed-key"), 447 }) 448 c.Check(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{ 449 "snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", 450 "snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1", 451 }) 452 // load chains 453 c.Assert(params.ModelParams[0].EFILoadChains, HasLen, 6) 454 // recovery load chains 455 c.Assert(params.ModelParams[0].EFILoadChains[:2], DeepEquals, []*secboot.LoadChain{ 456 secboot.NewLoadChain(shim, 457 secboot.NewLoadChain(grub, 458 secboot.NewLoadChain(kernel))), 459 secboot.NewLoadChain(shim2, 460 secboot.NewLoadChain(grub, 461 secboot.NewLoadChain(kernel))), 462 }) 463 // run load chains 464 c.Assert(params.ModelParams[0].EFILoadChains[2:4], DeepEquals, []*secboot.LoadChain{ 465 secboot.NewLoadChain(shim, 466 secboot.NewLoadChain(grub, 467 secboot.NewLoadChain(runGrub, 468 secboot.NewLoadChain(runKernel)), 469 secboot.NewLoadChain(runGrub2, 470 secboot.NewLoadChain(runKernel)), 471 )), 472 secboot.NewLoadChain(shim2, 473 secboot.NewLoadChain(grub, 474 secboot.NewLoadChain(runGrub, 475 secboot.NewLoadChain(runKernel)), 476 secboot.NewLoadChain(runGrub2, 477 secboot.NewLoadChain(runKernel)), 478 )), 479 }) 480 481 c.Assert(params.ModelParams[0].EFILoadChains[4:], DeepEquals, []*secboot.LoadChain{ 482 secboot.NewLoadChain(shim, 483 secboot.NewLoadChain(grub, 484 secboot.NewLoadChain(runGrub, 485 secboot.NewLoadChain(runKernel2)), 486 secboot.NewLoadChain(runGrub2, 487 secboot.NewLoadChain(runKernel2)), 488 )), 489 secboot.NewLoadChain(shim2, 490 secboot.NewLoadChain(grub, 491 secboot.NewLoadChain(runGrub, 492 secboot.NewLoadChain(runKernel2)), 493 secboot.NewLoadChain(runGrub2, 494 secboot.NewLoadChain(runKernel2)), 495 )), 496 }) 497 } 498 499 checkRecoveryParams := func() { 500 c.Check(params.KeyFiles, DeepEquals, []string{ 501 filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-data.recovery.sealed-key"), 502 filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-save.recovery.sealed-key"), 503 }) 504 c.Check(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{ 505 "snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", 506 }) 507 // load chains 508 c.Assert(params.ModelParams[0].EFILoadChains, HasLen, 2) 509 // recovery load chains 510 c.Assert(params.ModelParams[0].EFILoadChains[:2], DeepEquals, []*secboot.LoadChain{ 511 secboot.NewLoadChain(shim, 512 secboot.NewLoadChain(grub, 513 secboot.NewLoadChain(kernel))), 514 secboot.NewLoadChain(shim2, 515 secboot.NewLoadChain(grub, 516 secboot.NewLoadChain(kernel))), 517 }) 518 } 519 520 switch resealKeysCalls { 521 case 1: 522 if !tc.reuseRunPbc { 523 checkRunParams() 524 } else if !tc.reuseRecoveryPbc { 525 checkRecoveryParams() 526 } else { 527 c.Errorf("unexpected call to secboot.ResealKeys (call # %d)", resealKeysCalls) 528 } 529 case 2: 530 if !tc.reuseRecoveryPbc { 531 checkRecoveryParams() 532 } else { 533 c.Errorf("unexpected call to secboot.ResealKeys (call # %d)", resealKeysCalls) 534 } 535 default: 536 c.Errorf("unexpected additional call to secboot.ResealKeys (call # %d)", resealKeysCalls) 537 } 538 539 return tc.resealErr 540 }) 541 defer restore() 542 543 // here we don't have unasserted kernels so just set 544 // expectReseal to false as it doesn't matter; 545 // the behavior with unasserted kernel is tested in 546 // boot_test.go specific tests 547 const expectReseal = false 548 err = boot.ResealKeyToModeenv(rootdir, model, modeenv, expectReseal) 549 if !tc.sealedKeys || (tc.reuseRunPbc && tc.reuseRecoveryPbc) { 550 // did nothing 551 c.Assert(err, IsNil) 552 c.Assert(resealKeysCalls, Equals, 0) 553 continue 554 } 555 if tc.err == "" { 556 c.Assert(err, IsNil) 557 } else { 558 c.Assert(err, ErrorMatches, tc.err) 559 } 560 if tc.resealErr != nil { 561 // mocked error is returned on first reseal 562 c.Assert(resealKeysCalls, Equals, 1) 563 } else if !tc.reuseRecoveryPbc && !tc.reuseRunPbc { 564 // none of the boot chains is reused, so 2 reseals are 565 // observed 566 c.Assert(resealKeysCalls, Equals, 2) 567 } else { 568 // one of the boot chains is reused, only one reseal 569 c.Assert(resealKeysCalls, Equals, 1) 570 } 571 if tc.err != "" { 572 continue 573 } 574 575 // verify the boot chains data file 576 pbc, cnt, err := boot.ReadBootChains(filepath.Join(dirs.SnapFDEDir, "boot-chains")) 577 c.Assert(err, IsNil) 578 if tc.reuseRunPbc { 579 c.Assert(cnt, Equals, 9) 580 } else { 581 c.Assert(cnt, Equals, 1) 582 } 583 c.Check(pbc, DeepEquals, boot.PredictableBootChains{ 584 boot.BootChain{ 585 BrandID: "my-brand", 586 Model: "my-model-uc20", 587 Grade: "dangerous", 588 ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij", 589 AssetChain: []boot.BootAsset{ 590 { 591 Role: "recovery", 592 Name: "bootx64.efi", 593 Hashes: []string{"shim-hash-1", "shim-hash-2"}, 594 }, 595 { 596 Role: "recovery", 597 Name: "grubx64.efi", 598 Hashes: []string{"grub-hash-1"}, 599 }, 600 }, 601 Kernel: "pc-kernel", 602 KernelRevision: "1", 603 KernelCmdlines: []string{ 604 "snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", 605 }, 606 }, 607 boot.BootChain{ 608 BrandID: "my-brand", 609 Model: "my-model-uc20", 610 Grade: "dangerous", 611 ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij", 612 AssetChain: []boot.BootAsset{ 613 { 614 Role: "recovery", 615 Name: "bootx64.efi", 616 Hashes: []string{"shim-hash-1", "shim-hash-2"}, 617 }, 618 { 619 Role: "recovery", 620 Name: "grubx64.efi", 621 Hashes: []string{"grub-hash-1"}, 622 }, 623 { 624 Role: "run-mode", 625 Name: "grubx64.efi", 626 Hashes: []string{"run-grub-hash-1", "run-grub-hash-2"}, 627 }, 628 }, 629 Kernel: "pc-kernel", 630 KernelRevision: "500", 631 KernelCmdlines: []string{ 632 "snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1", 633 }, 634 }, 635 boot.BootChain{ 636 BrandID: "my-brand", 637 Model: "my-model-uc20", 638 Grade: "dangerous", 639 ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij", 640 AssetChain: []boot.BootAsset{ 641 { 642 Role: "recovery", 643 Name: "bootx64.efi", 644 Hashes: []string{"shim-hash-1", "shim-hash-2"}, 645 }, 646 { 647 Role: "recovery", 648 Name: "grubx64.efi", 649 Hashes: []string{"grub-hash-1"}, 650 }, 651 { 652 Role: "run-mode", 653 Name: "grubx64.efi", 654 Hashes: []string{"run-grub-hash-1", "run-grub-hash-2"}, 655 }, 656 }, 657 Kernel: "pc-kernel", 658 KernelRevision: "600", 659 KernelCmdlines: []string{ 660 "snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1", 661 }, 662 }, 663 }) 664 prevPbc = pbc 665 recoveryPbc, cnt, err := boot.ReadBootChains(filepath.Join(dirs.SnapFDEDir, "recovery-boot-chains")) 666 c.Assert(err, IsNil) 667 if tc.reuseRecoveryPbc { 668 c.Check(cnt, Equals, 9) 669 } else { 670 c.Check(cnt, Equals, 1) 671 } 672 prevRecoveryPbc = recoveryPbc 673 } 674 } 675 676 func (s *sealSuite) TestResealKeyToModeenvRecoveryKeysForGoodSystemsOnly(c *C) { 677 rootdir := c.MkDir() 678 dirs.SetRootDir(rootdir) 679 defer dirs.SetRootDir("") 680 681 c.Assert(os.MkdirAll(dirs.SnapFDEDir, 0755), IsNil) 682 err := ioutil.WriteFile(filepath.Join(dirs.SnapFDEDir, "sealed-keys"), nil, 0644) 683 c.Assert(err, IsNil) 684 685 err = createMockGrubCfg(filepath.Join(rootdir, "run/mnt/ubuntu-seed")) 686 c.Assert(err, IsNil) 687 688 err = createMockGrubCfg(filepath.Join(rootdir, "run/mnt/ubuntu-boot")) 689 c.Assert(err, IsNil) 690 691 model := boottest.MakeMockUC20Model() 692 693 modeenv := &boot.Modeenv{ 694 // where 1234 is being tried 695 CurrentRecoverySystems: []string{"20200825", "1234"}, 696 // 20200825 has known to be good 697 GoodRecoverySystems: []string{"20200825"}, 698 CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{ 699 "grubx64.efi": []string{"grub-hash"}, 700 "bootx64.efi": []string{"shim-hash"}, 701 }, 702 703 CurrentTrustedBootAssets: boot.BootAssetsMap{ 704 "grubx64.efi": []string{"run-grub-hash"}, 705 }, 706 707 CurrentKernels: []string{"pc-kernel_500.snap"}, 708 709 CurrentKernelCommandLines: boot.BootCommandLines{ 710 "snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1", 711 }, 712 Model: model.Model(), 713 BrandID: model.BrandID(), 714 Grade: string(model.Grade()), 715 ModelSignKeyID: model.SignKeyID(), 716 } 717 718 // mock asset cache 719 mockAssetsCache(c, rootdir, "grub", []string{ 720 "bootx64.efi-shim-hash", 721 "grubx64.efi-grub-hash", 722 "grubx64.efi-run-grub-hash", 723 }) 724 725 // set a mock recovery kernel 726 readSystemEssentialCalls := 0 727 restore := boot.MockSeedReadSystemEssential(func(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*seed.Snap, error) { 728 readSystemEssentialCalls++ 729 kernelRev := 1 730 if label == "1234" { 731 kernelRev = 999 732 } 733 return model, []*seed.Snap{mockKernelSeedSnap(c, snap.R(kernelRev)), mockGadgetSeedSnap(c, nil)}, nil 734 }) 735 defer restore() 736 737 // set mock key resealing 738 resealKeysCalls := 0 739 restore = boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { 740 c.Check(params.TPMPolicyAuthKeyFile, Equals, filepath.Join(dirs.SnapSaveDir, "device/fde", "tpm-policy-auth-key")) 741 742 resealKeysCalls++ 743 c.Assert(params.ModelParams, HasLen, 1) 744 745 // shared parameters 746 c.Assert(params.ModelParams[0].Model.Model(), Equals, "my-model-uc20") 747 c.Logf("got:") 748 for _, ch := range params.ModelParams[0].EFILoadChains { 749 printChain(c, ch, "-") 750 } 751 switch resealKeysCalls { 752 case 1: // run key 753 c.Assert(params.KeyFiles, DeepEquals, []string{ 754 filepath.Join(boot.InitramfsBootEncryptionKeyDir, "ubuntu-data.sealed-key"), 755 }) 756 c.Assert(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{ 757 "snapd_recovery_mode=recover snapd_recovery_system=1234 console=ttyS0 console=tty1 panic=-1", 758 "snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", 759 "snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1", 760 }) 761 // load chains 762 c.Assert(params.ModelParams[0].EFILoadChains, HasLen, 3) 763 case 2: // recovery keys 764 c.Assert(params.KeyFiles, DeepEquals, []string{ 765 filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-data.recovery.sealed-key"), 766 filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-save.recovery.sealed-key"), 767 }) 768 c.Assert(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{ 769 "snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", 770 }) 771 // load chains 772 c.Assert(params.ModelParams[0].EFILoadChains, HasLen, 1) 773 default: 774 c.Errorf("unexpected additional call to secboot.ResealKeys (call # %d)", resealKeysCalls) 775 } 776 777 // recovery parameters 778 shim := bootloader.NewBootFile("", filepath.Join(rootdir, "var/lib/snapd/boot-assets/grub/bootx64.efi-shim-hash"), bootloader.RoleRecovery) 779 grub := bootloader.NewBootFile("", filepath.Join(rootdir, "var/lib/snapd/boot-assets/grub/grubx64.efi-grub-hash"), bootloader.RoleRecovery) 780 kernelGoodRecovery := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery) 781 // kernel from a tried recovery system 782 kernelTriedRecovery := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_999.snap", "kernel.efi", bootloader.RoleRecovery) 783 // run mode parameters 784 runGrub := bootloader.NewBootFile("", filepath.Join(rootdir, "var/lib/snapd/boot-assets/grub/grubx64.efi-run-grub-hash"), bootloader.RoleRunMode) 785 runKernel := bootloader.NewBootFile(filepath.Join(rootdir, "var/lib/snapd/snaps/pc-kernel_500.snap"), "kernel.efi", bootloader.RoleRunMode) 786 787 switch resealKeysCalls { 788 case 1: // run load chain 789 c.Assert(params.ModelParams[0].EFILoadChains, DeepEquals, []*secboot.LoadChain{ 790 secboot.NewLoadChain(shim, 791 secboot.NewLoadChain(grub, 792 secboot.NewLoadChain(kernelGoodRecovery), 793 )), 794 secboot.NewLoadChain(shim, 795 secboot.NewLoadChain(grub, 796 secboot.NewLoadChain(kernelTriedRecovery), 797 )), 798 secboot.NewLoadChain(shim, 799 secboot.NewLoadChain(grub, 800 secboot.NewLoadChain(runGrub, 801 secboot.NewLoadChain(runKernel)), 802 )), 803 }) 804 case 2: // recovery load chains 805 c.Assert(params.ModelParams[0].EFILoadChains, DeepEquals, []*secboot.LoadChain{ 806 secboot.NewLoadChain(shim, 807 secboot.NewLoadChain(grub, 808 secboot.NewLoadChain(kernelGoodRecovery), 809 )), 810 }) 811 } 812 813 return nil 814 }) 815 defer restore() 816 817 // here we don't have unasserted kernels so just set 818 // expectReseal to false as it doesn't matter; 819 // the behavior with unasserted kernel is tested in 820 // boot_test.go specific tests 821 const expectReseal = false 822 err = boot.ResealKeyToModeenv(rootdir, model, modeenv, expectReseal) 823 c.Assert(err, IsNil) 824 c.Assert(resealKeysCalls, Equals, 2) 825 826 // verify the boot chains data file for run key 827 runPbc, cnt, err := boot.ReadBootChains(filepath.Join(dirs.SnapFDEDir, "boot-chains")) 828 c.Assert(err, IsNil) 829 c.Assert(cnt, Equals, 1) 830 c.Check(runPbc, DeepEquals, boot.PredictableBootChains{ 831 boot.BootChain{ 832 BrandID: "my-brand", 833 Model: "my-model-uc20", 834 Grade: "dangerous", 835 ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij", 836 AssetChain: []boot.BootAsset{ 837 { 838 Role: "recovery", 839 Name: "bootx64.efi", 840 Hashes: []string{"shim-hash"}, 841 }, 842 { 843 Role: "recovery", 844 Name: "grubx64.efi", 845 Hashes: []string{"grub-hash"}, 846 }, 847 }, 848 Kernel: "pc-kernel", 849 KernelRevision: "1", 850 KernelCmdlines: []string{ 851 "snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", 852 }, 853 }, 854 // includes the tried system 855 boot.BootChain{ 856 BrandID: "my-brand", 857 Model: "my-model-uc20", 858 Grade: "dangerous", 859 ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij", 860 AssetChain: []boot.BootAsset{ 861 { 862 Role: "recovery", 863 Name: "bootx64.efi", 864 Hashes: []string{"shim-hash"}, 865 }, 866 { 867 Role: "recovery", 868 Name: "grubx64.efi", 869 Hashes: []string{"grub-hash"}, 870 }, 871 }, 872 Kernel: "pc-kernel", 873 KernelRevision: "999", 874 KernelCmdlines: []string{ 875 "snapd_recovery_mode=recover snapd_recovery_system=1234 console=ttyS0 console=tty1 panic=-1", 876 }, 877 }, 878 boot.BootChain{ 879 BrandID: "my-brand", 880 Model: "my-model-uc20", 881 Grade: "dangerous", 882 ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij", 883 AssetChain: []boot.BootAsset{ 884 { 885 Role: "recovery", 886 Name: "bootx64.efi", 887 Hashes: []string{"shim-hash"}, 888 }, 889 { 890 Role: "recovery", 891 Name: "grubx64.efi", 892 Hashes: []string{"grub-hash"}, 893 }, 894 { 895 Role: "run-mode", 896 Name: "grubx64.efi", 897 Hashes: []string{"run-grub-hash"}, 898 }, 899 }, 900 Kernel: "pc-kernel", 901 KernelRevision: "500", 902 KernelCmdlines: []string{ 903 "snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1", 904 }, 905 }, 906 }) 907 // recovery boot chains 908 recoveryPbc, cnt, err := boot.ReadBootChains(filepath.Join(dirs.SnapFDEDir, "recovery-boot-chains")) 909 c.Assert(err, IsNil) 910 c.Assert(cnt, Equals, 1) 911 c.Check(recoveryPbc, DeepEquals, boot.PredictableBootChains{ 912 // only one entry for a recovery system that is known to be good 913 boot.BootChain{ 914 BrandID: "my-brand", 915 Model: "my-model-uc20", 916 Grade: "dangerous", 917 ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij", 918 AssetChain: []boot.BootAsset{ 919 { 920 Role: "recovery", 921 Name: "bootx64.efi", 922 Hashes: []string{"shim-hash"}, 923 }, 924 { 925 Role: "recovery", 926 Name: "grubx64.efi", 927 Hashes: []string{"grub-hash"}, 928 }, 929 }, 930 Kernel: "pc-kernel", 931 KernelRevision: "1", 932 KernelCmdlines: []string{ 933 "snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", 934 }, 935 }, 936 }) 937 } 938 939 func (s *sealSuite) TestResealKeyToModeenvFallbackCmdline(c *C) { 940 rootdir := c.MkDir() 941 dirs.SetRootDir(rootdir) 942 defer dirs.SetRootDir("") 943 944 model := boottest.MakeMockUC20Model() 945 946 c.Assert(os.MkdirAll(dirs.SnapFDEDir, 0755), IsNil) 947 err := ioutil.WriteFile(filepath.Join(dirs.SnapFDEDir, "sealed-keys"), nil, 0644) 948 c.Assert(err, IsNil) 949 950 modeenv := &boot.Modeenv{ 951 CurrentRecoverySystems: []string{"20200825"}, 952 CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{ 953 "asset": []string{"asset-hash-1"}, 954 }, 955 956 CurrentTrustedBootAssets: boot.BootAssetsMap{ 957 "asset": []string{"asset-hash-1"}, 958 }, 959 960 CurrentKernels: []string{"pc-kernel_500.snap"}, 961 962 // as if it is unset yet 963 CurrentKernelCommandLines: nil, 964 965 Model: model.Model(), 966 BrandID: model.BrandID(), 967 Grade: string(model.Grade()), 968 ModelSignKeyID: model.SignKeyID(), 969 } 970 971 err = boot.WriteBootChains(nil, filepath.Join(dirs.SnapFDEDir, "boot-chains"), 9) 972 c.Assert(err, IsNil) 973 // mock asset cache 974 mockAssetsCache(c, rootdir, "trusted", []string{ 975 "asset-asset-hash-1", 976 }) 977 978 // match one of current kernels 979 runKernelBf := bootloader.NewBootFile("/var/lib/snapd/snap/pc-kernel_500.snap", "kernel.efi", bootloader.RoleRunMode) 980 // match the seed kernel 981 recoveryKernelBf := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery) 982 983 bootdir := c.MkDir() 984 mtbl := bootloadertest.Mock("trusted", bootdir).WithTrustedAssets() 985 mtbl.TrustedAssetsList = []string{"asset-1"} 986 mtbl.StaticCommandLine = "static cmdline" 987 mtbl.BootChainList = []bootloader.BootFile{ 988 bootloader.NewBootFile("", "asset", bootloader.RoleRunMode), 989 runKernelBf, 990 } 991 mtbl.RecoveryBootChainList = []bootloader.BootFile{ 992 bootloader.NewBootFile("", "asset", bootloader.RoleRecovery), 993 recoveryKernelBf, 994 } 995 bootloader.Force(mtbl) 996 defer bootloader.Force(nil) 997 998 // set a mock recovery kernel 999 readSystemEssentialCalls := 0 1000 restore := boot.MockSeedReadSystemEssential(func(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*seed.Snap, error) { 1001 readSystemEssentialCalls++ 1002 return model, []*seed.Snap{mockKernelSeedSnap(c, snap.R(1)), mockGadgetSeedSnap(c, nil)}, nil 1003 }) 1004 defer restore() 1005 1006 // set mock key resealing 1007 resealKeysCalls := 0 1008 restore = boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { 1009 resealKeysCalls++ 1010 c.Assert(params.ModelParams, HasLen, 1) 1011 c.Logf("reseal: %+v", params) 1012 switch resealKeysCalls { 1013 case 1: 1014 c.Assert(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{ 1015 "snapd_recovery_mode=recover snapd_recovery_system=20200825 static cmdline", 1016 "snapd_recovery_mode=run static cmdline", 1017 }) 1018 case 2: 1019 c.Assert(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{ 1020 "snapd_recovery_mode=recover snapd_recovery_system=20200825 static cmdline", 1021 }) 1022 default: 1023 c.Fatalf("unexpected number of reseal calls, %v", params) 1024 } 1025 return nil 1026 }) 1027 defer restore() 1028 1029 const expectReseal = false 1030 err = boot.ResealKeyToModeenv(rootdir, model, modeenv, expectReseal) 1031 c.Assert(err, IsNil) 1032 c.Assert(resealKeysCalls, Equals, 2) 1033 1034 // verify the boot chains data file 1035 pbc, cnt, err := boot.ReadBootChains(filepath.Join(dirs.SnapFDEDir, "boot-chains")) 1036 c.Assert(err, IsNil) 1037 c.Assert(cnt, Equals, 10) 1038 c.Check(pbc, DeepEquals, boot.PredictableBootChains{ 1039 boot.BootChain{ 1040 BrandID: "my-brand", 1041 Model: "my-model-uc20", 1042 Grade: "dangerous", 1043 ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij", 1044 AssetChain: []boot.BootAsset{ 1045 { 1046 Role: "recovery", 1047 Name: "asset", 1048 Hashes: []string{"asset-hash-1"}, 1049 }, 1050 }, 1051 Kernel: "pc-kernel", 1052 KernelRevision: "1", 1053 KernelCmdlines: []string{ 1054 "snapd_recovery_mode=recover snapd_recovery_system=20200825 static cmdline", 1055 }, 1056 }, 1057 boot.BootChain{ 1058 BrandID: "my-brand", 1059 Model: "my-model-uc20", 1060 Grade: "dangerous", 1061 ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij", 1062 AssetChain: []boot.BootAsset{ 1063 { 1064 Role: "run-mode", 1065 Name: "asset", 1066 Hashes: []string{"asset-hash-1"}, 1067 }, 1068 }, 1069 Kernel: "pc-kernel", 1070 KernelRevision: "500", 1071 KernelCmdlines: []string{ 1072 "snapd_recovery_mode=run static cmdline", 1073 }, 1074 }, 1075 }) 1076 } 1077 1078 func (s *sealSuite) TestRecoveryBootChainsForSystems(c *C) { 1079 for _, tc := range []struct { 1080 desc string 1081 assetsMap boot.BootAssetsMap 1082 recoverySystems []string 1083 undefinedKernel bool 1084 gadgetFilesForSystem map[string][][]string 1085 expectedAssets []boot.BootAsset 1086 expectedKernelRevs []int 1087 // in the order of boot chains 1088 expectedCmdlines [][]string 1089 err string 1090 }{ 1091 { 1092 desc: "transition sequences", 1093 recoverySystems: []string{"20200825"}, 1094 assetsMap: boot.BootAssetsMap{ 1095 "grubx64.efi": []string{"grub-hash-1", "grub-hash-2"}, 1096 "bootx64.efi": []string{"shim-hash-1"}, 1097 }, 1098 expectedAssets: []boot.BootAsset{ 1099 {Role: bootloader.RoleRecovery, Name: "bootx64.efi", Hashes: []string{"shim-hash-1"}}, 1100 {Role: bootloader.RoleRecovery, Name: "grubx64.efi", Hashes: []string{"grub-hash-1", "grub-hash-2"}}, 1101 }, 1102 expectedKernelRevs: []int{1}, 1103 expectedCmdlines: [][]string{ 1104 {"snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1"}, 1105 }, 1106 }, 1107 { 1108 desc: "two systems", 1109 recoverySystems: []string{"20200825", "20200831"}, 1110 assetsMap: boot.BootAssetsMap{ 1111 "grubx64.efi": []string{"grub-hash-1", "grub-hash-2"}, 1112 "bootx64.efi": []string{"shim-hash-1"}, 1113 }, 1114 expectedAssets: []boot.BootAsset{ 1115 {Role: bootloader.RoleRecovery, Name: "bootx64.efi", Hashes: []string{"shim-hash-1"}}, 1116 {Role: bootloader.RoleRecovery, Name: "grubx64.efi", Hashes: []string{"grub-hash-1", "grub-hash-2"}}, 1117 }, 1118 expectedKernelRevs: []int{1, 3}, 1119 expectedCmdlines: [][]string{ 1120 {"snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1"}, 1121 {"snapd_recovery_mode=recover snapd_recovery_system=20200831 console=ttyS0 console=tty1 panic=-1"}, 1122 }, 1123 }, 1124 { 1125 desc: "non transition sequence", 1126 recoverySystems: []string{"20200825"}, 1127 assetsMap: boot.BootAssetsMap{ 1128 "grubx64.efi": []string{"grub-hash-1"}, 1129 "bootx64.efi": []string{"shim-hash-1"}, 1130 }, 1131 expectedAssets: []boot.BootAsset{ 1132 {Role: bootloader.RoleRecovery, Name: "bootx64.efi", Hashes: []string{"shim-hash-1"}}, 1133 {Role: bootloader.RoleRecovery, Name: "grubx64.efi", Hashes: []string{"grub-hash-1"}}, 1134 }, 1135 expectedKernelRevs: []int{1}, 1136 expectedCmdlines: [][]string{ 1137 {"snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1"}, 1138 }, 1139 }, 1140 { 1141 desc: "two systems with command lines", 1142 recoverySystems: []string{"20200825", "20200831"}, 1143 assetsMap: boot.BootAssetsMap{ 1144 "grubx64.efi": []string{"grub-hash-1", "grub-hash-2"}, 1145 "bootx64.efi": []string{"shim-hash-1"}, 1146 }, 1147 expectedAssets: []boot.BootAsset{ 1148 {Role: bootloader.RoleRecovery, Name: "bootx64.efi", Hashes: []string{"shim-hash-1"}}, 1149 {Role: bootloader.RoleRecovery, Name: "grubx64.efi", Hashes: []string{"grub-hash-1", "grub-hash-2"}}, 1150 }, 1151 gadgetFilesForSystem: map[string][][]string{ 1152 "20200825": { 1153 {"cmdline.extra", "extra for 20200825"}, 1154 }, 1155 "20200831": { 1156 // TODO: make it a cmdline.full 1157 {"cmdline.extra", "some-extra-for-20200831"}, 1158 }, 1159 }, 1160 expectedKernelRevs: []int{1, 3}, 1161 expectedCmdlines: [][]string{ 1162 {"snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1 extra for 20200825"}, 1163 {"snapd_recovery_mode=recover snapd_recovery_system=20200831 console=ttyS0 console=tty1 panic=-1 some-extra-for-20200831"}, 1164 }, 1165 }, 1166 { 1167 desc: "invalid recovery system label", 1168 recoverySystems: []string{"0"}, 1169 err: `cannot read system "0" seed: invalid system seed`, 1170 }, 1171 } { 1172 c.Logf("tc: %q", tc.desc) 1173 rootdir := c.MkDir() 1174 dirs.SetRootDir(rootdir) 1175 defer dirs.SetRootDir("") 1176 1177 // set recovery kernel 1178 restore := boot.MockSeedReadSystemEssential(func(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*seed.Snap, error) { 1179 if label != "20200825" && label != "20200831" { 1180 return nil, nil, fmt.Errorf("invalid system seed") 1181 } 1182 kernelRev := 1 1183 if label == "20200831" { 1184 kernelRev = 3 1185 } 1186 return nil, []*seed.Snap{mockKernelSeedSnap(c, snap.R(kernelRev)), mockGadgetSeedSnap(c, tc.gadgetFilesForSystem[label])}, nil 1187 }) 1188 defer restore() 1189 1190 grubDir := filepath.Join(rootdir, "run/mnt/ubuntu-seed") 1191 err := createMockGrubCfg(grubDir) 1192 c.Assert(err, IsNil) 1193 1194 bl, err := bootloader.Find(grubDir, &bootloader.Options{Role: bootloader.RoleRecovery}) 1195 c.Assert(err, IsNil) 1196 tbl, ok := bl.(bootloader.TrustedAssetsBootloader) 1197 c.Assert(ok, Equals, true) 1198 1199 model := boottest.MakeMockUC20Model() 1200 1201 modeenv := &boot.Modeenv{ 1202 CurrentTrustedRecoveryBootAssets: tc.assetsMap, 1203 1204 BrandID: model.BrandID(), 1205 Model: model.Model(), 1206 ModelSignKeyID: model.SignKeyID(), 1207 Grade: string(model.Grade()), 1208 } 1209 1210 bc, err := boot.RecoveryBootChainsForSystems(tc.recoverySystems, tbl, modeenv) 1211 if tc.err == "" { 1212 c.Assert(err, IsNil) 1213 c.Assert(bc, HasLen, len(tc.recoverySystems)) 1214 c.Assert(tc.expectedCmdlines, HasLen, len(bc), Commentf("broken test, expected command lines must be of the same length as recovery systems and recovery boot chains")) 1215 for i, chain := range bc { 1216 c.Assert(chain.AssetChain, DeepEquals, tc.expectedAssets) 1217 c.Assert(chain.Kernel, Equals, "pc-kernel") 1218 expectedKernelRev := tc.expectedKernelRevs[i] 1219 c.Assert(chain.KernelRevision, Equals, fmt.Sprintf("%d", expectedKernelRev)) 1220 c.Assert(chain.KernelBootFile(), DeepEquals, bootloader.BootFile{ 1221 Snap: fmt.Sprintf("/var/lib/snapd/seed/snaps/pc-kernel_%d.snap", expectedKernelRev), 1222 Path: "kernel.efi", 1223 Role: bootloader.RoleRecovery, 1224 }) 1225 c.Assert(chain.KernelCmdlines, DeepEquals, tc.expectedCmdlines[i]) 1226 } 1227 } else { 1228 c.Assert(err, ErrorMatches, tc.err) 1229 } 1230 1231 } 1232 1233 } 1234 1235 func createMockGrubCfg(baseDir string) error { 1236 cfg := filepath.Join(baseDir, "EFI/ubuntu/grub.cfg") 1237 if err := os.MkdirAll(filepath.Dir(cfg), 0755); err != nil { 1238 return err 1239 } 1240 return ioutil.WriteFile(cfg, []byte("# Snapd-Boot-Config-Edition: 1\n"), 0644) 1241 } 1242 1243 func (s *sealSuite) TestSealKeyModelParams(c *C) { 1244 rootdir := c.MkDir() 1245 dirs.SetRootDir(rootdir) 1246 defer dirs.SetRootDir("") 1247 1248 model := boottest.MakeMockUC20Model() 1249 1250 roleToBlName := map[bootloader.Role]string{ 1251 bootloader.RoleRecovery: "grub", 1252 bootloader.RoleRunMode: "grub", 1253 } 1254 // mock asset cache 1255 mockAssetsCache(c, rootdir, "grub", []string{ 1256 "shim-shim-hash", 1257 "loader-loader-hash1", 1258 "loader-loader-hash2", 1259 }) 1260 1261 oldmodel := boottest.MakeMockUC20Model(map[string]interface{}{ 1262 "model": "old-model-uc20", 1263 "timestamp": "2019-10-01T08:00:00+00:00", 1264 }) 1265 1266 // old recovery 1267 oldrc := boot.BootChain{ 1268 BrandID: oldmodel.BrandID(), 1269 Model: oldmodel.Model(), 1270 Grade: oldmodel.Grade(), 1271 ModelSignKeyID: oldmodel.SignKeyID(), 1272 AssetChain: []boot.BootAsset{ 1273 {Name: "shim", Role: bootloader.RoleRecovery, Hashes: []string{"shim-hash"}}, 1274 {Name: "loader", Role: bootloader.RoleRecovery, Hashes: []string{"loader-hash1"}}, 1275 }, 1276 KernelCmdlines: []string{"panic=1", "oldrc"}, 1277 } 1278 oldkbf := bootloader.BootFile{Snap: "pc-kernel_1.snap"} 1279 oldrc.SetKernelBootFile(oldkbf) 1280 1281 // recovery 1282 rc1 := boot.BootChain{ 1283 BrandID: model.BrandID(), 1284 Model: model.Model(), 1285 Grade: model.Grade(), 1286 ModelSignKeyID: model.SignKeyID(), 1287 AssetChain: []boot.BootAsset{ 1288 {Name: "shim", Role: bootloader.RoleRecovery, Hashes: []string{"shim-hash"}}, 1289 {Name: "loader", Role: bootloader.RoleRecovery, Hashes: []string{"loader-hash1"}}, 1290 }, 1291 KernelCmdlines: []string{"panic=1", "rc1"}, 1292 } 1293 rc1kbf := bootloader.BootFile{Snap: "pc-kernel_10.snap"} 1294 rc1.SetKernelBootFile(rc1kbf) 1295 1296 // run system 1297 runc1 := boot.BootChain{ 1298 BrandID: model.BrandID(), 1299 Model: model.Model(), 1300 Grade: model.Grade(), 1301 ModelSignKeyID: model.SignKeyID(), 1302 AssetChain: []boot.BootAsset{ 1303 {Name: "shim", Role: bootloader.RoleRecovery, Hashes: []string{"shim-hash"}}, 1304 {Name: "loader", Role: bootloader.RoleRecovery, Hashes: []string{"loader-hash1"}}, 1305 {Name: "loader", Role: bootloader.RoleRunMode, Hashes: []string{"loader-hash2"}}, 1306 }, 1307 KernelCmdlines: []string{"panic=1", "runc1"}, 1308 } 1309 runc1kbf := bootloader.BootFile{Snap: "pc-kernel_50.snap"} 1310 runc1.SetKernelBootFile(runc1kbf) 1311 1312 pbc := boot.ToPredictableBootChains([]boot.BootChain{rc1, runc1, oldrc}) 1313 1314 shim := bootloader.NewBootFile("", filepath.Join(rootdir, "var/lib/snapd/boot-assets/grub/shim-shim-hash"), bootloader.RoleRecovery) 1315 loader1 := bootloader.NewBootFile("", filepath.Join(rootdir, "var/lib/snapd/boot-assets/grub/loader-loader-hash1"), bootloader.RoleRecovery) 1316 loader2 := bootloader.NewBootFile("", filepath.Join(rootdir, "var/lib/snapd/boot-assets/grub/loader-loader-hash2"), bootloader.RoleRunMode) 1317 1318 params, err := boot.SealKeyModelParams(pbc, roleToBlName) 1319 c.Assert(err, IsNil) 1320 c.Check(params, HasLen, 2) 1321 c.Check(params[0].Model.Model(), Equals, model.Model()) 1322 // NB: merging of lists makes panic=1 appear once 1323 c.Check(params[0].KernelCmdlines, DeepEquals, []string{"panic=1", "rc1", "runc1"}) 1324 1325 c.Check(params[0].EFILoadChains, DeepEquals, []*secboot.LoadChain{ 1326 secboot.NewLoadChain(shim, 1327 secboot.NewLoadChain(loader1, 1328 secboot.NewLoadChain(rc1kbf))), 1329 secboot.NewLoadChain(shim, 1330 secboot.NewLoadChain(loader1, 1331 secboot.NewLoadChain(loader2, 1332 secboot.NewLoadChain(runc1kbf)))), 1333 }) 1334 1335 c.Check(params[1].Model.Model(), Equals, oldmodel.Model()) 1336 c.Check(params[1].KernelCmdlines, DeepEquals, []string{"oldrc", "panic=1"}) 1337 c.Check(params[1].EFILoadChains, DeepEquals, []*secboot.LoadChain{ 1338 secboot.NewLoadChain(shim, 1339 secboot.NewLoadChain(loader1, 1340 secboot.NewLoadChain(oldkbf))), 1341 }) 1342 } 1343 1344 func (s *sealSuite) TestIsResealNeeded(c *C) { 1345 if os.Geteuid() == 0 { 1346 c.Skip("the test cannot be run by the root user") 1347 } 1348 1349 chains := []boot.BootChain{ 1350 { 1351 BrandID: "mybrand", 1352 Model: "foo", 1353 Grade: "signed", 1354 ModelSignKeyID: "my-key-id", 1355 AssetChain: []boot.BootAsset{ 1356 // hashes will be sorted 1357 {Role: bootloader.RoleRecovery, Name: "shim", Hashes: []string{"x", "y"}}, 1358 {Role: bootloader.RoleRecovery, Name: "loader", Hashes: []string{"c", "d"}}, 1359 {Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"z", "x"}}, 1360 }, 1361 Kernel: "pc-kernel-other", 1362 KernelRevision: "2345", 1363 KernelCmdlines: []string{`snapd_recovery_mode=run foo`}, 1364 }, { 1365 BrandID: "mybrand", 1366 Model: "foo", 1367 Grade: "dangerous", 1368 ModelSignKeyID: "my-key-id", 1369 AssetChain: []boot.BootAsset{ 1370 // hashes will be sorted 1371 {Role: bootloader.RoleRecovery, Name: "shim", Hashes: []string{"y", "x"}}, 1372 {Role: bootloader.RoleRecovery, Name: "loader", Hashes: []string{"c", "d"}}, 1373 }, 1374 Kernel: "pc-kernel-recovery", 1375 KernelRevision: "1234", 1376 KernelCmdlines: []string{`snapd_recovery_mode=recover foo`}, 1377 }, 1378 } 1379 1380 pbc := boot.ToPredictableBootChains(chains) 1381 1382 rootdir := c.MkDir() 1383 err := boot.WriteBootChains(pbc, filepath.Join(dirs.SnapFDEDirUnder(rootdir), "boot-chains"), 2) 1384 c.Assert(err, IsNil) 1385 1386 needed, _, err := boot.IsResealNeeded(pbc, filepath.Join(dirs.SnapFDEDirUnder(rootdir), "boot-chains"), false) 1387 c.Assert(err, IsNil) 1388 c.Check(needed, Equals, false) 1389 1390 otherchain := []boot.BootChain{pbc[0]} 1391 needed, cnt, err := boot.IsResealNeeded(otherchain, filepath.Join(dirs.SnapFDEDirUnder(rootdir), "boot-chains"), false) 1392 c.Assert(err, IsNil) 1393 // chains are different 1394 c.Check(needed, Equals, true) 1395 c.Check(cnt, Equals, 3) 1396 1397 // boot-chains does not exist, we cannot compare so advise to reseal 1398 otherRootdir := c.MkDir() 1399 needed, cnt, err = boot.IsResealNeeded(otherchain, filepath.Join(dirs.SnapFDEDirUnder(otherRootdir), "boot-chains"), false) 1400 c.Assert(err, IsNil) 1401 c.Check(needed, Equals, true) 1402 c.Check(cnt, Equals, 1) 1403 1404 // exists but cannot be read 1405 c.Assert(os.Chmod(filepath.Join(dirs.SnapFDEDirUnder(rootdir), "boot-chains"), 0000), IsNil) 1406 defer os.Chmod(filepath.Join(dirs.SnapFDEDirUnder(rootdir), "boot-chains"), 0755) 1407 needed, _, err = boot.IsResealNeeded(otherchain, filepath.Join(dirs.SnapFDEDirUnder(rootdir), "boot-chains"), false) 1408 c.Assert(err, ErrorMatches, "cannot open existing boot chains data file: open .*/boot-chains: permission denied") 1409 c.Check(needed, Equals, false) 1410 1411 // unrevisioned kernel chain 1412 unrevchain := []boot.BootChain{pbc[0], pbc[1]} 1413 unrevchain[1].KernelRevision = "" 1414 // write on disk 1415 bootChainsFile := filepath.Join(dirs.SnapFDEDirUnder(rootdir), "boot-chains") 1416 err = boot.WriteBootChains(unrevchain, bootChainsFile, 2) 1417 c.Assert(err, IsNil) 1418 1419 needed, cnt, err = boot.IsResealNeeded(pbc, bootChainsFile, false) 1420 c.Assert(err, IsNil) 1421 c.Check(needed, Equals, true) 1422 c.Check(cnt, Equals, 3) 1423 1424 // cases falling back to expectReseal 1425 needed, _, err = boot.IsResealNeeded(unrevchain, bootChainsFile, false) 1426 c.Assert(err, IsNil) 1427 c.Check(needed, Equals, false) 1428 1429 needed, cnt, err = boot.IsResealNeeded(unrevchain, bootChainsFile, true) 1430 c.Assert(err, IsNil) 1431 c.Check(needed, Equals, true) 1432 c.Check(cnt, Equals, 3) 1433 } 1434 1435 func (s *sealSuite) TestSealToModeenvWithFdeHookHappy(c *C) { 1436 rootdir := c.MkDir() 1437 dirs.SetRootDir(rootdir) 1438 defer dirs.SetRootDir("") 1439 1440 restore := boot.MockHasFDESetupHook(func() (bool, error) { 1441 return true, nil 1442 }) 1443 defer restore() 1444 1445 model := boottest.MakeMockUC20Model() 1446 1447 n := 0 1448 var runFDESetupHookReqs []*fde.SetupRequest 1449 restore = boot.MockRunFDESetupHook(func(req *fde.SetupRequest) ([]byte, error) { 1450 n++ 1451 runFDESetupHookReqs = append(runFDESetupHookReqs, req) 1452 1453 key := []byte(fmt.Sprintf("key-%v", strconv.Itoa(n))) 1454 return key, nil 1455 }) 1456 defer restore() 1457 keyToSave := make(map[string][]byte) 1458 restore = boot.MockSecbootSealKeysWithFDESetupHook(func(runHook fde.RunSetupHookFunc, skrs []secboot.SealKeyRequest, params *secboot.SealKeysWithFDESetupHookParams) error { 1459 c.Check(params.Model.Model(), Equals, model.Model()) 1460 c.Check(params.AuxKeyFile, Equals, filepath.Join(boot.InstallHostFDESaveDir, "aux-key")) 1461 for _, skr := range skrs { 1462 out, err := runHook(&fde.SetupRequest{ 1463 Key: skr.Key, 1464 KeyName: skr.KeyName, 1465 }) 1466 c.Assert(err, IsNil) 1467 keyToSave[skr.KeyFile] = out 1468 } 1469 return nil 1470 }) 1471 defer restore() 1472 1473 modeenv := &boot.Modeenv{ 1474 RecoverySystem: "20200825", 1475 Model: model.Model(), 1476 BrandID: model.BrandID(), 1477 Grade: string(model.Grade()), 1478 ModelSignKeyID: model.SignKeyID(), 1479 } 1480 key := secboot.EncryptionKey{1, 2, 3, 4} 1481 saveKey := secboot.EncryptionKey{5, 6, 7, 8} 1482 1483 err := boot.SealKeyToModeenv(key, saveKey, model, modeenv) 1484 c.Assert(err, IsNil) 1485 // check that runFDESetupHook was called the expected way 1486 c.Check(runFDESetupHookReqs, DeepEquals, []*fde.SetupRequest{ 1487 {Key: key, KeyName: "ubuntu-data"}, 1488 {Key: key, KeyName: "ubuntu-data"}, 1489 {Key: saveKey, KeyName: "ubuntu-save"}, 1490 }) 1491 // check that the sealed keys got written to the expected places 1492 for i, p := range []string{ 1493 filepath.Join(boot.InitramfsBootEncryptionKeyDir, "ubuntu-data.sealed-key"), 1494 filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-data.recovery.sealed-key"), 1495 filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-save.recovery.sealed-key"), 1496 } { 1497 // Check for a valid platform handle, encrypted payload (base64) 1498 mockedSealedKey := []byte(fmt.Sprintf("key-%v", strconv.Itoa(i+1))) 1499 c.Check(keyToSave[p], DeepEquals, mockedSealedKey) 1500 } 1501 1502 marker := filepath.Join(dirs.SnapFDEDirUnder(boot.InstallHostWritableDir), "sealed-keys") 1503 c.Check(marker, testutil.FileEquals, "fde-setup-hook") 1504 } 1505 1506 func (s *sealSuite) TestSealToModeenvWithFdeHookSad(c *C) { 1507 rootdir := c.MkDir() 1508 dirs.SetRootDir(rootdir) 1509 defer dirs.SetRootDir("") 1510 1511 restore := boot.MockHasFDESetupHook(func() (bool, error) { 1512 return true, nil 1513 }) 1514 defer restore() 1515 1516 restore = boot.MockSecbootSealKeysWithFDESetupHook(func(fde.RunSetupHookFunc, []secboot.SealKeyRequest, *secboot.SealKeysWithFDESetupHookParams) error { 1517 return fmt.Errorf("hook failed") 1518 }) 1519 defer restore() 1520 1521 modeenv := &boot.Modeenv{ 1522 RecoverySystem: "20200825", 1523 } 1524 key := secboot.EncryptionKey{1, 2, 3, 4} 1525 saveKey := secboot.EncryptionKey{5, 6, 7, 8} 1526 1527 model := boottest.MakeMockUC20Model() 1528 err := boot.SealKeyToModeenv(key, saveKey, model, modeenv) 1529 c.Assert(err, ErrorMatches, "hook failed") 1530 marker := filepath.Join(dirs.SnapFDEDirUnder(boot.InstallHostWritableDir), "sealed-keys") 1531 c.Check(marker, testutil.FileAbsent) 1532 } 1533 1534 func (s *sealSuite) TestResealKeyToModeenvWithFdeHookCalled(c *C) { 1535 rootdir := c.MkDir() 1536 dirs.SetRootDir(rootdir) 1537 defer dirs.SetRootDir("") 1538 1539 resealKeyToModeenvUsingFDESetupHookCalled := 0 1540 restore := boot.MockResealKeyToModeenvUsingFDESetupHook(func(string, *boot.Modeenv, bool) error { 1541 resealKeyToModeenvUsingFDESetupHookCalled++ 1542 return nil 1543 }) 1544 defer restore() 1545 1546 // TODO: this simulates that the hook is not available yet 1547 // because of e.g. seeding. Longer term there will be 1548 // more, see TODO in resealKeyToModeenvUsingFDESetupHookImpl 1549 restore = boot.MockHasFDESetupHook(func() (bool, error) { 1550 return false, fmt.Errorf("hook not available yet because e.g. seeding") 1551 }) 1552 defer restore() 1553 1554 marker := filepath.Join(dirs.SnapFDEDirUnder(rootdir), "sealed-keys") 1555 err := os.MkdirAll(filepath.Dir(marker), 0755) 1556 c.Assert(err, IsNil) 1557 err = ioutil.WriteFile(marker, []byte("fde-setup-hook"), 0644) 1558 c.Assert(err, IsNil) 1559 1560 modeenv := &boot.Modeenv{ 1561 RecoverySystem: "20200825", 1562 } 1563 1564 model := boottest.MakeMockUC20Model() 1565 expectReseal := false 1566 err = boot.ResealKeyToModeenv(rootdir, model, modeenv, expectReseal) 1567 c.Assert(err, IsNil) 1568 c.Check(resealKeyToModeenvUsingFDESetupHookCalled, Equals, 1) 1569 } 1570 1571 func (s *sealSuite) TestResealKeyToModeenvWithFdeHookVerySad(c *C) { 1572 rootdir := c.MkDir() 1573 dirs.SetRootDir(rootdir) 1574 defer dirs.SetRootDir("") 1575 1576 resealKeyToModeenvUsingFDESetupHookCalled := 0 1577 restore := boot.MockResealKeyToModeenvUsingFDESetupHook(func(string, *boot.Modeenv, bool) error { 1578 resealKeyToModeenvUsingFDESetupHookCalled++ 1579 return fmt.Errorf("fde setup hook failed") 1580 }) 1581 defer restore() 1582 1583 marker := filepath.Join(dirs.SnapFDEDirUnder(rootdir), "sealed-keys") 1584 err := os.MkdirAll(filepath.Dir(marker), 0755) 1585 c.Assert(err, IsNil) 1586 err = ioutil.WriteFile(marker, []byte("fde-setup-hook"), 0644) 1587 c.Assert(err, IsNil) 1588 1589 modeenv := &boot.Modeenv{ 1590 RecoverySystem: "20200825", 1591 } 1592 1593 model := boottest.MakeMockUC20Model() 1594 expectReseal := false 1595 err = boot.ResealKeyToModeenv(rootdir, model, modeenv, expectReseal) 1596 c.Assert(err, ErrorMatches, "fde setup hook failed") 1597 c.Check(resealKeyToModeenvUsingFDESetupHookCalled, Equals, 1) 1598 }