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  }