gitee.com/mysnapcore/mysnapd@v0.1.0/secboot/encrypt_sb_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 //go:build !nosecboot 3 // +build !nosecboot 4 5 /* 6 * Copyright (C) 2022 Canonical Ltd 7 * 8 * This program is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 3 as 10 * published by the Free Software Foundation. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. 19 * 20 */ 21 22 package secboot_test 23 24 import ( 25 "bytes" 26 "encoding/json" 27 "errors" 28 "fmt" 29 "path/filepath" 30 31 sb "gitee.com/mysnapcore/mysecboot" 32 . "gopkg.in/check.v1" 33 34 "gitee.com/mysnapcore/mysnapd/dirs" 35 "gitee.com/mysnapcore/mysnapd/osutil" 36 "gitee.com/mysnapcore/mysnapd/secboot" 37 "gitee.com/mysnapcore/mysnapd/secboot/keys" 38 "gitee.com/mysnapcore/mysnapd/snap/snaptest" 39 "gitee.com/mysnapcore/mysnapd/snapdtool" 40 "gitee.com/mysnapcore/mysnapd/testutil" 41 ) 42 43 func (s *encryptSuite) TestFormatEncryptedDevice(c *C) { 44 for _, tc := range []struct { 45 initErr error 46 err string 47 }{ 48 {initErr: nil, err: ""}, 49 {initErr: errors.New("some error"), err: "some error"}, 50 } { 51 // create empty key to prevent blocking on lack of system entropy 52 myKey := keys.EncryptionKey{} 53 for i := range myKey { 54 myKey[i] = byte(i) 55 } 56 57 calls := 0 58 restore := secboot.MockSbInitializeLUKS2Container(func(devicePath, label string, key []byte, 59 opts *sb.InitializeLUKS2ContainerOptions) error { 60 calls++ 61 c.Assert(devicePath, Equals, "/dev/node") 62 c.Assert(label, Equals, "my label") 63 c.Assert(key, DeepEquals, []byte(myKey)) 64 c.Assert(opts, DeepEquals, &sb.InitializeLUKS2ContainerOptions{ 65 MetadataKiBSize: 2048, 66 KeyslotsAreaKiBSize: 2560, 67 KDFOptions: &sb.KDFOptions{ 68 MemoryKiB: 32, 69 ForceIterations: 4, 70 }, 71 }) 72 return tc.initErr 73 }) 74 defer restore() 75 76 err := secboot.FormatEncryptedDevice(myKey, "my label", "/dev/node") 77 c.Assert(calls, Equals, 1) 78 if tc.err == "" { 79 c.Assert(err, IsNil) 80 } else { 81 c.Assert(err, ErrorMatches, tc.err) 82 } 83 } 84 } 85 86 type keymgrSuite struct { 87 testutil.BaseTest 88 89 d string 90 keymgrCmd *testutil.MockCmd 91 udevadmCmd *testutil.MockCmd 92 systemdRunCmd *testutil.MockCmd 93 } 94 95 var _ = Suite(&keymgrSuite{}) 96 97 func (s *keymgrSuite) SetUpTest(c *C) { 98 s.BaseTest.SetUpTest(c) 99 100 s.d = c.MkDir() 101 s.systemdRunCmd = testutil.MockCommand(c, "systemd-run", ` 102 while true; do 103 case "$1" in 104 --*) 105 shift 106 ;; 107 *) 108 exec "$@" 109 ;; 110 esac 111 done 112 `) 113 s.AddCleanup(s.systemdRunCmd.Restore) 114 s.keymgrCmd = testutil.MockCommand(c, "snap-fde-keymgr", fmt.Sprintf(` 115 set -e 116 if [ "$1" = "change-encryption-key" ]; then 117 cat > %s/input 118 exit 0 119 fi 120 if [ "$1" = "add-recovery-key" ]; then 121 while true; do 122 case "$1" in 123 --key-file) 124 shift 125 printf "recovery11111111" > "$1" 126 exit 0 127 ;; 128 *) shift ;; 129 esac 130 done 131 fi 132 if [ "$1" = "remove-recovery-key" ]; then 133 while [ "$#" -gt 1 ]; do 134 case "$1" in 135 --key-file) 136 shift 137 rm -f "$1" 138 ;; 139 *) shift ;; 140 esac 141 done 142 fi 143 144 `, s.d)) 145 s.AddCleanup(s.keymgrCmd.Restore) 146 147 s.udevadmCmd = testutil.MockCommand(c, "udevadm", ` 148 echo "ID_PART_ENTRY_UUID=something" 149 `) 150 s.AddCleanup(s.udevadmCmd.Restore) 151 152 restore := snapdtool.MockOsReadlink(func(string) (string, error) { 153 return filepath.Join(filepath.Dir(s.keymgrCmd.Exe()), "snapd"), nil 154 }) 155 s.AddCleanup(restore) 156 } 157 158 var ( 159 key = keys.EncryptionKey{'e', 'n', 'c', 'r', 'y', 'p', 't', 1, 1, 1, 1} 160 ) 161 162 func (s *keymgrSuite) TestStageEncryptionKeyHappy(c *C) { 163 err := secboot.StageEncryptionKeyChange("/dev/foo/bar", key) 164 c.Assert(err, IsNil) 165 c.Check(s.udevadmCmd.Calls(), DeepEquals, [][]string{ 166 {"udevadm", "info", "--query", "property", "--name", "/dev/foo/bar"}, 167 }) 168 c.Check(s.systemdRunCmd.Calls(), DeepEquals, [][]string{ 169 { 170 "systemd-run", 171 "--wait", "--pipe", "--collect", "--service-type=exec", "--quiet", 172 "--property=KeyringMode=inherit", "--", 173 s.keymgrCmd.Exe(), "change-encryption-key", "--device", "/dev/disk/by-partuuid/something", 174 "--stage", 175 }, 176 }) 177 c.Check(s.keymgrCmd.Calls(), DeepEquals, [][]string{ 178 {"snap-fde-keymgr", "change-encryption-key", "--device", "/dev/disk/by-partuuid/something", "--stage"}, 179 }) 180 var b bytes.Buffer 181 json.NewEncoder(&b).Encode(struct { 182 Key []byte `json:"key"` 183 }{ 184 Key: key, 185 }) 186 c.Check(filepath.Join(s.d, "input"), testutil.FileEquals, b.String()) 187 } 188 189 func (s *keymgrSuite) TestStageEncryptionKeyBadUdev(c *C) { 190 udevadmCmd := testutil.MockCommand(c, "udevadm", ` 191 echo "unhappy udev" 192 `) 193 defer udevadmCmd.Restore() 194 err := secboot.StageEncryptionKeyChange("/dev/foo/bar", key) 195 c.Assert(err, ErrorMatches, "cannot get UUID of partition /dev/foo/bar: cannot get required udev partition UUID property") 196 c.Check(udevadmCmd.Calls(), DeepEquals, [][]string{ 197 {"udevadm", "info", "--query", "property", "--name", "/dev/foo/bar"}, 198 }) 199 c.Check(s.systemdRunCmd.Calls(), HasLen, 0) 200 c.Check(s.keymgrCmd.Calls(), HasLen, 0) 201 } 202 203 func (s *keymgrSuite) TestStageTransitionEncryptionKeyBadKeymgr(c *C) { 204 keymgrCmd := testutil.MockCommand(c, "snap-fde-keymgr", `echo keymgr very unhappy; exit 1`) 205 defer keymgrCmd.Restore() 206 // update where /proc/self/exe resolves to 207 restore := snapdtool.MockOsReadlink(func(string) (string, error) { 208 return filepath.Join(filepath.Dir(keymgrCmd.Exe()), "snapd"), nil 209 }) 210 defer restore() 211 212 err := secboot.StageEncryptionKeyChange("/dev/foo/bar", key) 213 c.Assert(err, ErrorMatches, "cannot run FDE key manager tool: cannot run .*: keymgr very unhappy") 214 215 c.Check(s.systemdRunCmd.Calls(), DeepEquals, [][]string{ 216 { 217 "systemd-run", 218 "--wait", "--pipe", "--collect", "--service-type=exec", "--quiet", 219 "--property=KeyringMode=inherit", "--", 220 keymgrCmd.Exe(), "change-encryption-key", "--device", "/dev/disk/by-partuuid/something", 221 "--stage", 222 }, 223 }) 224 c.Check(keymgrCmd.Calls(), DeepEquals, [][]string{ 225 {"snap-fde-keymgr", "change-encryption-key", "--device", "/dev/disk/by-partuuid/something", "--stage"}, 226 }) 227 228 s.systemdRunCmd.ForgetCalls() 229 keymgrCmd.ForgetCalls() 230 231 s.mocksForDeviceMounts(c) 232 err = secboot.TransitionEncryptionKeyChange("/foo", key) 233 c.Assert(err, ErrorMatches, "cannot run FDE key manager tool: cannot run .*: keymgr very unhappy") 234 235 c.Check(s.systemdRunCmd.Calls(), DeepEquals, [][]string{ 236 { 237 "systemd-run", 238 "--wait", "--pipe", "--collect", "--service-type=exec", "--quiet", 239 "--property=KeyringMode=inherit", "--", 240 keymgrCmd.Exe(), "change-encryption-key", "--device", "/dev/disk/by-partuuid/foo-uuid", 241 "--transition", 242 }, 243 }) 244 c.Check(keymgrCmd.Calls(), DeepEquals, [][]string{ 245 {"snap-fde-keymgr", "change-encryption-key", "--device", "/dev/disk/by-partuuid/foo-uuid", "--transition"}, 246 }) 247 } 248 249 func (s *keymgrSuite) TestTransitionEncryptionKeyNoMountDev(c *C) { 250 restore := osutil.MockMountInfo(` 251 27 27 600:3 / /foo rw,relatime shared:7 - vfat /dev/mapper/foo rw 252 `[1:]) 253 s.AddCleanup(restore) 254 255 udevadmCmd := testutil.MockCommand(c, "udevadm", `echo nope; exit 1`) 256 defer udevadmCmd.Restore() 257 258 err := secboot.TransitionEncryptionKeyChange("/foo", key) 259 c.Assert(err, ErrorMatches, "cannot find matching device: cannot partition for mount /foo: cannot process udev properties of /dev/mapper/foo: nope") 260 } 261 262 func (s *keymgrSuite) TestTransitionEncryptionKeyHappy(c *C) { 263 udevadmCmd := s.mocksForDeviceMounts(c) 264 265 err := secboot.TransitionEncryptionKeyChange("/foo", key) 266 c.Assert(err, IsNil) 267 c.Check(udevadmCmd.Calls(), DeepEquals, [][]string{ 268 {"udevadm", "info", "--query", "property", "--name", "/dev/mapper/foo"}, 269 {"udevadm", "info", "--query", "property", "--name", "/dev/disk/by-uuid/5a522809-c87e-4dfa-81a8-8dc5667d1304"}, 270 }) 271 c.Check(s.systemdRunCmd.Calls(), DeepEquals, [][]string{ 272 { 273 "systemd-run", 274 "--wait", "--pipe", "--collect", "--service-type=exec", "--quiet", 275 "--property=KeyringMode=inherit", "--", 276 s.keymgrCmd.Exe(), "change-encryption-key", "--device", "/dev/disk/by-partuuid/foo-uuid", 277 "--transition", 278 }, 279 }) 280 c.Check(s.keymgrCmd.Calls(), DeepEquals, [][]string{ 281 {"snap-fde-keymgr", "change-encryption-key", "--device", "/dev/disk/by-partuuid/foo-uuid", "--transition"}, 282 }) 283 var b bytes.Buffer 284 json.NewEncoder(&b).Encode(struct { 285 Key []byte `json:"key"` 286 }{ 287 Key: key, 288 }) 289 c.Check(filepath.Join(s.d, "input"), testutil.FileEquals, b.String()) 290 } 291 292 func (s *keymgrSuite) mocksForDeviceMounts(c *C) (udevadmCmd *testutil.MockCmd) { 293 restore := osutil.MockMountInfo(` 294 27 27 600:3 / /foo rw,relatime shared:7 - vfat /dev/mapper/foo rw 295 27 27 600:4 / /bar rw,relatime shared:7 - vfat /dev/mapper/bar rw 296 `[1:]) 297 s.AddCleanup(restore) 298 299 udevadmCmd = testutil.MockCommand(c, "udevadm", ` 300 while [ "$#" -gt 1 ]; do 301 case "$1" in 302 --name) 303 shift 304 case "$1" in 305 /dev/mapper/foo) 306 echo "DEVTYPE=disk" 307 echo "MAJOR=600" 308 echo "MINOR=3" 309 ;; 310 /dev/mapper/bar) 311 echo "DEVTYPE=disk" 312 echo "MAJOR=600" 313 echo "MINOR=4" 314 ;; 315 /dev/disk/by-uuid/5a522809-c87e-4dfa-81a8-8dc5667d1304) 316 echo "ID_PART_ENTRY_UUID=foo-uuid" 317 ;; 318 /dev/disk/by-uuid/5a522809-c87e-4dfa-81a8-8dc5667d1305) 319 echo "ID_PART_ENTRY_UUID=bar-uuid" 320 ;; 321 esac 322 ;; 323 *) 324 shift 325 ;; 326 esac 327 done 328 `) 329 s.AddCleanup(udevadmCmd.Restore) 330 331 s.AddCleanup(func() { dirs.SetRootDir(dirs.GlobalRootDir) }) 332 dirs.SetRootDir(s.d) 333 334 snaptest.PopulateDir(s.d, [][]string{ 335 {"/sys/dev/block/600:3/dm/uuid", "CRYPT-LUKS2-5a522809c87e4dfa81a88dc5667d1304-foo"}, 336 {"/sys/dev/block/600:3/dm/name", "foo"}, 337 {"/sys/dev/block/600:4/dm/uuid", "CRYPT-LUKS2-5a522809c87e4dfa81a88dc5667d1305-bar"}, 338 {"/sys/dev/block/600:4/dm/name", "bar"}, 339 }) 340 return udevadmCmd 341 } 342 343 func (s *keymgrSuite) TestEnsureRecoveryKey(c *C) { 344 udevadmCmd := s.mocksForDeviceMounts(c) 345 346 rkey, err := secboot.EnsureRecoveryKey(filepath.Join(s.d, "recovery.key"), []secboot.RecoveryKeyDevice{ 347 {Mountpoint: "/foo"}, 348 {Mountpoint: "/bar", AuthorizingKeyFile: "/authz/key.file"}, 349 }) 350 c.Assert(err, IsNil) 351 c.Check(udevadmCmd.Calls(), DeepEquals, [][]string{ 352 {"udevadm", "info", "--query", "property", "--name", "/dev/mapper/foo"}, 353 {"udevadm", "info", "--query", "property", "--name", "/dev/disk/by-uuid/5a522809-c87e-4dfa-81a8-8dc5667d1304"}, 354 {"udevadm", "info", "--query", "property", "--name", "/dev/mapper/bar"}, 355 {"udevadm", "info", "--query", "property", "--name", "/dev/disk/by-uuid/5a522809-c87e-4dfa-81a8-8dc5667d1305"}, 356 }) 357 c.Check(s.systemdRunCmd.Calls(), DeepEquals, [][]string{ 358 { 359 "systemd-run", 360 "--wait", "--pipe", "--collect", "--service-type=exec", "--quiet", 361 "--property=KeyringMode=inherit", "--", 362 s.keymgrCmd.Exe(), "add-recovery-key", 363 "--key-file", filepath.Join(s.d, "recovery.key"), 364 "--devices", "/dev/disk/by-partuuid/foo-uuid", 365 "--authorizations", "keyring", 366 "--devices", "/dev/disk/by-partuuid/bar-uuid", 367 "--authorizations", "file:/authz/key.file", 368 }, 369 }) 370 c.Check(s.keymgrCmd.Calls(), DeepEquals, [][]string{ 371 { 372 "snap-fde-keymgr", "add-recovery-key", 373 "--key-file", filepath.Join(s.d, "recovery.key"), 374 "--devices", "/dev/disk/by-partuuid/foo-uuid", "--authorizations", "keyring", 375 "--devices", "/dev/disk/by-partuuid/bar-uuid", "--authorizations", "file:/authz/key.file", 376 }, 377 }) 378 c.Check(rkey, DeepEquals, keys.RecoveryKey{'r', 'e', 'c', 'o', 'v', 'e', 'r', 'y', '1', '1', '1', '1', '1', '1', '1', '1'}) 379 } 380 381 func (s *keymgrSuite) TestRemoveRecoveryKey(c *C) { 382 udevadmCmd := s.mocksForDeviceMounts(c) 383 384 snaptest.PopulateDir(s.d, [][]string{ 385 {"recovery.key", "foobar"}, 386 }) 387 // only one of the key files exists 388 err := secboot.RemoveRecoveryKeys(map[secboot.RecoveryKeyDevice]string{ 389 {Mountpoint: "/foo"}: filepath.Join(s.d, "recovery.key"), 390 {Mountpoint: "/bar", AuthorizingKeyFile: "/authz/key.file"}: filepath.Join(s.d, "missing-recovery.key"), 391 }) 392 c.Assert(err, IsNil) 393 394 expectedUdevCalls := [][]string{ 395 // order can change depending on map iteration 396 {"udevadm", "info", "--query", "property", "--name", "/dev/mapper/foo"}, 397 {"udevadm", "info", "--query", "property", "--name", "/dev/disk/by-uuid/5a522809-c87e-4dfa-81a8-8dc5667d1304"}, 398 {"udevadm", "info", "--query", "property", "--name", "/dev/mapper/bar"}, 399 {"udevadm", "info", "--query", "property", "--name", "/dev/disk/by-uuid/5a522809-c87e-4dfa-81a8-8dc5667d1305"}, 400 } 401 expectedSystemdRunCalls := [][]string{ 402 { 403 "systemd-run", 404 "--wait", "--pipe", "--collect", "--service-type=exec", "--quiet", 405 "--property=KeyringMode=inherit", "--", 406 s.keymgrCmd.Exe(), "remove-recovery-key", 407 // order can change depending on map iteration 408 "--devices", "/dev/disk/by-partuuid/foo-uuid", "--authorizations", "keyring", 409 "--key-files", filepath.Join(s.d, "recovery.key"), 410 "--devices", "/dev/disk/by-partuuid/bar-uuid", "--authorizations", "file:/authz/key.file", 411 "--key-files", filepath.Join(s.d, "missing-recovery.key"), 412 }, 413 } 414 expectedKeymgrCalls := [][]string{ 415 { 416 "snap-fde-keymgr", "remove-recovery-key", 417 // order can change depending on map iteration 418 "--devices", "/dev/disk/by-partuuid/foo-uuid", "--authorizations", "keyring", 419 "--key-files", filepath.Join(s.d, "recovery.key"), 420 "--devices", "/dev/disk/by-partuuid/bar-uuid", "--authorizations", "file:/authz/key.file", 421 "--key-files", filepath.Join(s.d, "missing-recovery.key"), 422 }, 423 } 424 425 udevCalls := udevadmCmd.Calls() 426 c.Assert(udevCalls, HasLen, len(expectedUdevCalls)) 427 // iteration order can be different though 428 c.Assert(udevCalls[0], HasLen, 6) 429 firstFoo := udevCalls[0][5] == "/dev/mapper/foo" 430 431 if !firstFoo { 432 // flip the order of foo and bar 433 expectedUdevCalls = append(expectedUdevCalls[2:], expectedUdevCalls[0:2]...) 434 expectedSystemdRunCalls[0] = append(expectedSystemdRunCalls[0][0:10], 435 append(expectedSystemdRunCalls[0][16:], expectedSystemdRunCalls[0][10:16]...)...) 436 expectedKeymgrCalls[0] = append(expectedKeymgrCalls[0][0:2], 437 append(expectedKeymgrCalls[0][8:], expectedKeymgrCalls[0][2:8]...)...) 438 } 439 440 c.Check(s.systemdRunCmd.Calls(), DeepEquals, expectedSystemdRunCalls) 441 c.Check(s.keymgrCmd.Calls(), DeepEquals, expectedKeymgrCalls) 442 }