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