gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/secboot/secboot_sb.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 // +build !nosecboot 3 4 /* 5 * Copyright (C) 2021 Canonical Ltd 6 * 7 * This program is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 3 as 9 * published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 * 19 */ 20 21 package secboot 22 23 import ( 24 "fmt" 25 "path/filepath" 26 27 sb "github.com/snapcore/secboot" 28 "golang.org/x/xerrors" 29 30 "github.com/snapcore/snapd/kernel/fde" 31 "github.com/snapcore/snapd/logger" 32 "github.com/snapcore/snapd/osutil/disks" 33 ) 34 35 var ( 36 sbActivateVolumeWithKey = sb.ActivateVolumeWithKey 37 sbActivateVolumeWithKeyData = sb.ActivateVolumeWithKeyData 38 sbActivateVolumeWithRecoveryKey = sb.ActivateVolumeWithRecoveryKey 39 sbDeactivateVolume = sb.DeactivateVolume 40 ) 41 42 func init() { 43 WithSecbootSupport = true 44 } 45 46 // LockSealedKeys manually locks access to the sealed keys. Meant to be 47 // called in place of passing lockKeysOnFinish as true to 48 // UnlockVolumeUsingSealedKeyIfEncrypted for cases where we don't know if a 49 // given call is the last one to unlock a volume like in degraded recover mode. 50 func LockSealedKeys() error { 51 if fdeHasRevealKey() { 52 return fde.LockSealedKeys() 53 } 54 return lockTPMSealedKeys() 55 } 56 57 // UnlockVolumeUsingSealedKeyIfEncrypted verifies whether an encrypted volume 58 // with the specified name exists and unlocks it using a sealed key in a file 59 // with a corresponding name. The options control activation with the 60 // recovery key will be attempted if a prior activation attempt with 61 // the sealed key fails. 62 // 63 // Note that if the function proceeds to the point where it knows definitely 64 // whether there is an encrypted device or not, IsEncrypted on the return 65 // value will be true, even if error is non-nil. This is so that callers can be 66 // robust and try unlocking using another method for example. 67 func UnlockVolumeUsingSealedKeyIfEncrypted(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *UnlockVolumeUsingSealedKeyOptions) (UnlockResult, error) { 68 res := UnlockResult{} 69 70 // find the encrypted device using the disk we were provided - note that 71 // we do not specify IsDecryptedDevice in opts because here we are 72 // looking for the encrypted device to unlock, later on in the boot 73 // process we will look for the decrypted device to ensure it matches 74 // what we expected 75 partUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(EncryptedPartitionName(name)) 76 if err == nil { 77 res.IsEncrypted = true 78 } else { 79 var errNotFound disks.PartitionNotFoundError 80 if !xerrors.As(err, &errNotFound) { 81 // some other kind of catastrophic error searching 82 return res, fmt.Errorf("error enumerating partitions for disk to find encrypted device %q: %v", name, err) 83 } 84 // otherwise it is an error not found and we should search for the 85 // unencrypted device 86 partUUID, err = disk.FindMatchingPartitionUUIDWithFsLabel(name) 87 if err != nil { 88 return res, fmt.Errorf("error enumerating partitions for disk to find unencrypted device %q: %v", name, err) 89 } 90 } 91 92 partDevice := filepath.Join("/dev/disk/by-partuuid", partUUID) 93 94 if !res.IsEncrypted { 95 // if we didn't find an encrypted device just return, don't try to 96 // unlock it 97 // the filesystem device for the unencrypted case is the same as the 98 // partition device 99 res.PartDevice = partDevice 100 res.FsDevice = res.PartDevice 101 return res, nil 102 } 103 104 mapperName := name + "-" + randutilRandomKernelUUID() 105 sourceDevice := partDevice 106 targetDevice := filepath.Join("/dev/mapper", mapperName) 107 108 if fdeHasRevealKey() { 109 return unlockVolumeUsingSealedKeyFDERevealKey(name, sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName, opts) 110 } else { 111 return unlockVolumeUsingSealedKeyTPM(name, sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName, opts) 112 } 113 } 114 115 // UnlockEncryptedVolumeUsingKey unlocks an existing volume using the provided key. 116 func UnlockEncryptedVolumeUsingKey(disk disks.Disk, name string, key []byte) (UnlockResult, error) { 117 unlockRes := UnlockResult{ 118 UnlockMethod: NotUnlocked, 119 } 120 // find the encrypted device using the disk we were provided - note that 121 // we do not specify IsDecryptedDevice in opts because here we are 122 // looking for the encrypted device to unlock, later on in the boot 123 // process we will look for the decrypted device to ensure it matches 124 // what we expected 125 partUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(EncryptedPartitionName(name)) 126 if err != nil { 127 return unlockRes, err 128 } 129 unlockRes.IsEncrypted = true 130 // we have a device 131 encdev := filepath.Join("/dev/disk/by-partuuid", partUUID) 132 unlockRes.PartDevice = encdev 133 // make up a new name for the mapped device 134 mapperName := name + "-" + randutilRandomKernelUUID() 135 if err := unlockEncryptedPartitionWithKey(mapperName, encdev, key); err != nil { 136 return unlockRes, err 137 } 138 139 unlockRes.FsDevice = filepath.Join("/dev/mapper/", mapperName) 140 unlockRes.UnlockMethod = UnlockedWithKey 141 return unlockRes, nil 142 } 143 144 // unlockEncryptedPartitionWithKey unlocks encrypted partition with the provided 145 // key. 146 func unlockEncryptedPartitionWithKey(name, device string, key []byte) error { 147 // no special options set 148 options := sb.ActivateVolumeOptions{} 149 err := sbActivateVolumeWithKey(name, device, key, &options) 150 if err == nil { 151 logger.Noticef("successfully activated encrypted device %v using a key", device) 152 } 153 return err 154 } 155 156 // UnlockEncryptedVolumeWithRecoveryKey prompts for the recovery key and uses it 157 // to open an encrypted device. 158 func UnlockEncryptedVolumeWithRecoveryKey(name, device string) error { 159 options := sb.ActivateVolumeOptions{ 160 RecoveryKeyTries: 3, 161 KeyringPrefix: keyringPrefix, 162 } 163 164 if err := sbActivateVolumeWithRecoveryKey(name, device, nil, &options); err != nil { 165 return fmt.Errorf("cannot unlock encrypted device %q: %v", device, err) 166 } 167 168 return nil 169 }