gitee.com/mysnapcore/mysnapd@v0.1.0/secboot/secboot_hooks.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 //go:build !nosecboot 3 // +build !nosecboot 4 5 /* 6 * Copyright (C) 2021 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 23 24 import ( 25 "bytes" 26 "crypto" 27 "encoding/json" 28 "fmt" 29 "io" 30 "io/ioutil" 31 "os" 32 33 sb "gitee.com/mysnapcore/mysecboot" 34 "golang.org/x/xerrors" 35 36 "gitee.com/mysnapcore/mysnapd/kernel/fde" 37 "gitee.com/mysnapcore/mysnapd/logger" 38 "gitee.com/mysnapcore/mysnapd/osutil" 39 ) 40 41 var fdeHasRevealKey = fde.HasRevealKey 42 43 const fdeHooksPlatformName = "fde-hook-v2" 44 45 func init() { 46 handler := &fdeHookV2DataHandler{} 47 sb.RegisterPlatformKeyDataHandler(fdeHooksPlatformName, handler) 48 } 49 50 // SealKeysWithFDESetupHook protects the given keys through using the 51 // fde-setup hook and saves each protected key to the KeyFile 52 // indicated in the key SealKeyRequest. 53 func SealKeysWithFDESetupHook(runHook fde.RunSetupHookFunc, keys []SealKeyRequest, params *SealKeysWithFDESetupHookParams) error { 54 auxKey := params.AuxKey[:] 55 for _, skr := range keys { 56 payload := sb.MarshalKeys([]byte(skr.Key), auxKey) 57 keyParams := &fde.InitialSetupParams{ 58 Key: payload, 59 KeyName: skr.KeyName, 60 } 61 res, err := fde.InitialSetup(runHook, keyParams) 62 if err != nil { 63 return err 64 } 65 if err := writeKeyData(skr.KeyFile, res, auxKey, params.Model); err != nil { 66 return fmt.Errorf("cannot store key: %v", err) 67 } 68 } 69 if params.AuxKeyFile != "" { 70 if err := osutil.AtomicWriteFile(params.AuxKeyFile, auxKey, 0600, 0); err != nil { 71 return fmt.Errorf("cannot write the aux key file: %v", err) 72 } 73 } 74 75 return nil 76 } 77 78 func writeKeyData(path string, keySetup *fde.InitialSetupResult, auxKey []byte, model sb.SnapModel) error { 79 var handle []byte 80 if keySetup.Handle == nil { 81 // this will reach fde-reveal-key as null but should be ok 82 handle = []byte("null") 83 } else { 84 handle = *keySetup.Handle 85 } 86 kd, err := sb.NewKeyData(&sb.KeyCreationData{ 87 PlatformKeyData: sb.PlatformKeyData{ 88 EncryptedPayload: keySetup.EncryptedKey, 89 Handle: handle, 90 }, 91 PlatformName: fdeHooksPlatformName, 92 AuxiliaryKey: auxKey, 93 SnapModelAuthHash: crypto.SHA256, 94 }) 95 if err != nil { 96 return fmt.Errorf("cannot create key data: %v", err) 97 } 98 if err := kd.SetAuthorizedSnapModels(auxKey, model); err != nil { 99 return fmt.Errorf("cannot set model %s/%s as authorized: %v", model.BrandID(), model.Model(), err) 100 } 101 f := sb.NewFileKeyDataWriter(path) 102 if err := kd.WriteAtomic(f); err != nil { 103 return fmt.Errorf("cannot write key data: %v", err) 104 } 105 return nil 106 } 107 108 func isV1EncryptedKeyFile(p string) bool { 109 // XXX move some of this to kernel/fde 110 var v1KeyPrefix = []byte("USK$") 111 112 f, err := os.Open(p) 113 if err != nil { 114 return false 115 } 116 defer f.Close() 117 118 buf := make([]byte, len(v1KeyPrefix)) 119 if _, err := io.ReadFull(f, buf); err != nil { 120 return false 121 } 122 return bytes.HasPrefix(buf, v1KeyPrefix) 123 } 124 125 // We have to deal with the following cases: 126 // 1. Key created with v1 data-format on disk (raw encrypted key), v1 hook reads the data 127 // 2. Key created with v2 data-format on disk (json), v1 hook created the data (no handle) and reads the data (hook output not json but raw binary data) 128 // 3. Key created with v1 data-format on disk (raw), v2 hook 129 // 4. Key created with v2 data-format on disk (json), v2 hook [easy] 130 func unlockVolumeUsingSealedKeyFDERevealKey(sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName string, opts *UnlockVolumeUsingSealedKeyOptions) (UnlockResult, error) { 131 // deal with v1 keys 132 if isV1EncryptedKeyFile(sealedEncryptionKeyFile) { 133 return unlockVolumeUsingSealedKeyFDERevealKeyV1(sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName) 134 } 135 return unlockVolumeUsingSealedKeyFDERevealKeyV2(sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName, opts) 136 } 137 138 func unlockVolumeUsingSealedKeyFDERevealKeyV1(sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName string) (UnlockResult, error) { 139 res := UnlockResult{IsEncrypted: true, PartDevice: sourceDevice} 140 141 sealedKey, err := ioutil.ReadFile(sealedEncryptionKeyFile) 142 if err != nil { 143 return res, fmt.Errorf("cannot read sealed key file: %v", err) 144 } 145 146 p := fde.RevealParams{ 147 SealedKey: sealedKey, 148 } 149 output, err := fde.Reveal(&p) 150 if err != nil { 151 return res, err 152 } 153 154 // the output of fde-reveal-key is the unsealed key 155 unsealedKey := output 156 if err := unlockEncryptedPartitionWithKey(mapperName, sourceDevice, unsealedKey); err != nil { 157 return res, fmt.Errorf("cannot unlock encrypted partition: %v", err) 158 } 159 res.FsDevice = targetDevice 160 res.UnlockMethod = UnlockedWithSealedKey 161 return res, nil 162 } 163 164 func unlockVolumeUsingSealedKeyFDERevealKeyV2(sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName string, opts *UnlockVolumeUsingSealedKeyOptions) (res UnlockResult, err error) { 165 res = UnlockResult{IsEncrypted: true, PartDevice: sourceDevice} 166 167 f, err := sb.NewFileKeyDataReader(sealedEncryptionKeyFile) 168 if err != nil { 169 return res, err 170 } 171 keyData, err := sb.ReadKeyData(f) 172 if err != nil { 173 fmt := "cannot read key data: %w" 174 return res, xerrors.Errorf(fmt, err) 175 } 176 177 // the output of fde-reveal-key is the unsealed key 178 options := activateVolOpts(opts.AllowRecoveryKey) 179 modChecker, err := sbActivateVolumeWithKeyData(mapperName, sourceDevice, keyData, options) 180 if err == sb.ErrRecoveryKeyUsed { 181 logger.Noticef("successfully activated encrypted device %q using a fallback activation method", sourceDevice) 182 res.FsDevice = targetDevice 183 res.UnlockMethod = UnlockedWithRecoveryKey 184 return res, nil 185 } 186 if err != nil { 187 return res, fmt.Errorf("cannot unlock encrypted partition: %v", err) 188 } 189 // ensure we close the open volume under any error condition 190 defer func() { 191 if err != nil { 192 if err := sbDeactivateVolume(mapperName); err != nil { 193 logger.Noticef("cannot deactivate volume %q: %v", mapperName, err) 194 } 195 } 196 }() 197 // ensure that the model is authorized to open the volume 198 model, err := opts.WhichModel() 199 if err != nil { 200 return res, fmt.Errorf("cannot retrieve which model to unlock for: %v", err) 201 } 202 ok, err := modChecker.IsModelAuthorized(model) 203 if err != nil { 204 return res, fmt.Errorf("cannot check if model is authorized to unlock disk: %v", err) 205 } 206 if !ok { 207 return res, fmt.Errorf("cannot unlock volume: model %s/%s not authorized", model.BrandID(), model.Model()) 208 } 209 210 logger.Noticef("successfully activated encrypted device %q using FDE kernel hooks", sourceDevice) 211 res.FsDevice = targetDevice 212 res.UnlockMethod = UnlockedWithSealedKey 213 return res, nil 214 } 215 216 type fdeHookV2DataHandler struct{} 217 218 func (fh *fdeHookV2DataHandler) RecoverKeys(data *sb.PlatformKeyData) (sb.KeyPayload, error) { 219 var handle *json.RawMessage 220 if len(data.Handle) != 0 { 221 rawHandle := json.RawMessage(data.Handle) 222 handle = &rawHandle 223 } 224 p := fde.RevealParams{ 225 SealedKey: data.EncryptedPayload, 226 Handle: handle, 227 V2Payload: true, 228 } 229 return fde.Reveal(&p) 230 }