mirror of
https://github.com/token2/snapd.git
synced 2026-03-13 11:15:47 -07:00
186 lines
6.5 KiB
Go
186 lines
6.5 KiB
Go
// -*- Mode: Go; indent-tabs-mode: t -*-
|
|
//go:build !nosecboot
|
|
|
|
/*
|
|
* Copyright (C) 2021 Canonical Ltd
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 3 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
package secboot
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
|
|
sb "github.com/snapcore/secboot"
|
|
"golang.org/x/xerrors"
|
|
|
|
"github.com/snapcore/snapd/kernel/fde"
|
|
"github.com/snapcore/snapd/logger"
|
|
"github.com/snapcore/snapd/osutil/disks"
|
|
)
|
|
|
|
var (
|
|
sbActivateVolumeWithKey = sb.ActivateVolumeWithKey
|
|
sbActivateVolumeWithKeyData = sb.ActivateVolumeWithKeyData
|
|
sbActivateVolumeWithRecoveryKey = sb.ActivateVolumeWithRecoveryKey
|
|
sbDeactivateVolume = sb.DeactivateVolume
|
|
)
|
|
|
|
func init() {
|
|
WithSecbootSupport = true
|
|
}
|
|
|
|
// LockSealedKeys manually locks access to the sealed keys. Meant to be
|
|
// called in place of passing lockKeysOnFinish as true to
|
|
// UnlockVolumeUsingSealedKeyIfEncrypted for cases where we don't know if a
|
|
// given call is the last one to unlock a volume like in degraded recover mode.
|
|
func LockSealedKeys() error {
|
|
if fdeHasRevealKey() {
|
|
return fde.LockSealedKeys()
|
|
}
|
|
return lockTPMSealedKeys()
|
|
}
|
|
|
|
// UnlockVolumeUsingSealedKeyIfEncrypted verifies whether an encrypted volume
|
|
// with the specified name exists and unlocks it using a sealed key in a file
|
|
// with a corresponding name. The options control activation with the
|
|
// recovery key will be attempted if a prior activation attempt with
|
|
// the sealed key fails.
|
|
//
|
|
// Note that if the function proceeds to the point where it knows definitely
|
|
// whether there is an encrypted device or not, IsEncrypted on the return
|
|
// value will be true, even if error is non-nil. This is so that callers can be
|
|
// robust and try unlocking using another method for example.
|
|
func UnlockVolumeUsingSealedKeyIfEncrypted(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *UnlockVolumeUsingSealedKeyOptions) (UnlockResult, error) {
|
|
res := UnlockResult{}
|
|
|
|
// find the encrypted device using the disk we were provided - note that
|
|
// we do not specify IsDecryptedDevice in opts because here we are
|
|
// looking for the encrypted device to unlock, later on in the boot
|
|
// process we will look for the decrypted device to ensure it matches
|
|
// what we expected
|
|
partUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(EncryptedPartitionName(name))
|
|
if err == nil {
|
|
res.IsEncrypted = true
|
|
} else {
|
|
var errNotFound disks.PartitionNotFoundError
|
|
if !xerrors.As(err, &errNotFound) {
|
|
// some other kind of catastrophic error searching
|
|
return res, fmt.Errorf("error enumerating partitions for disk to find encrypted device %q: %v", name, err)
|
|
}
|
|
// otherwise it is an error not found and we should search for the
|
|
// unencrypted device
|
|
partUUID, err = disk.FindMatchingPartitionUUIDWithFsLabel(name)
|
|
if err != nil {
|
|
return res, fmt.Errorf("error enumerating partitions for disk to find unencrypted device %q: %v", name, err)
|
|
}
|
|
}
|
|
|
|
partDevice := filepath.Join("/dev/disk/by-partuuid", partUUID)
|
|
|
|
if !res.IsEncrypted {
|
|
// if we didn't find an encrypted device just return, don't try to
|
|
// unlock it the filesystem device for the unencrypted case is the
|
|
// same as the partition device
|
|
res.PartDevice = partDevice
|
|
res.FsDevice = res.PartDevice
|
|
return res, nil
|
|
}
|
|
|
|
uuid, err := randutilRandomKernelUUID()
|
|
if err != nil {
|
|
// We failed before we could generate the filsystem device path for
|
|
// the encrypted partition device, so we return FsDevice empty.
|
|
res.PartDevice = partDevice
|
|
return res, err
|
|
}
|
|
|
|
// make up a new name for the mapped device
|
|
mapperName := name + "-" + uuid
|
|
sourceDevice := partDevice
|
|
targetDevice := filepath.Join("/dev/mapper", mapperName)
|
|
|
|
if fdeHasRevealKey() {
|
|
return unlockVolumeUsingSealedKeyFDERevealKey(sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName, opts)
|
|
} else {
|
|
return unlockVolumeUsingSealedKeyTPM(name, sealedEncryptionKeyFile, sourceDevice, targetDevice, mapperName, opts)
|
|
}
|
|
}
|
|
|
|
// UnlockEncryptedVolumeUsingKey unlocks an existing volume using the provided key.
|
|
func UnlockEncryptedVolumeUsingKey(disk disks.Disk, name string, key []byte) (UnlockResult, error) {
|
|
unlockRes := UnlockResult{
|
|
UnlockMethod: NotUnlocked,
|
|
}
|
|
// find the encrypted device using the disk we were provided - note that
|
|
// we do not specify IsDecryptedDevice in opts because here we are
|
|
// looking for the encrypted device to unlock, later on in the boot
|
|
// process we will look for the decrypted device to ensure it matches
|
|
// what we expected
|
|
partUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(EncryptedPartitionName(name))
|
|
if err != nil {
|
|
return unlockRes, err
|
|
}
|
|
unlockRes.IsEncrypted = true
|
|
// we have a device
|
|
encdev := filepath.Join("/dev/disk/by-partuuid", partUUID)
|
|
unlockRes.PartDevice = encdev
|
|
|
|
uuid, err := randutilRandomKernelUUID()
|
|
if err != nil {
|
|
// We failed before we could generate the filsystem device path for
|
|
// the encrypted partition device, so we return FsDevice empty.
|
|
return unlockRes, err
|
|
}
|
|
|
|
// make up a new name for the mapped device
|
|
mapperName := name + "-" + uuid
|
|
if err := unlockEncryptedPartitionWithKey(mapperName, encdev, key); err != nil {
|
|
return unlockRes, err
|
|
}
|
|
|
|
unlockRes.FsDevice = filepath.Join("/dev/mapper/", mapperName)
|
|
unlockRes.UnlockMethod = UnlockedWithKey
|
|
return unlockRes, nil
|
|
}
|
|
|
|
// unlockEncryptedPartitionWithKey unlocks encrypted partition with the provided
|
|
// key.
|
|
func unlockEncryptedPartitionWithKey(name, device string, key []byte) error {
|
|
// no special options set
|
|
options := sb.ActivateVolumeOptions{}
|
|
err := sbActivateVolumeWithKey(name, device, key, &options)
|
|
if err == nil {
|
|
logger.Noticef("successfully activated encrypted device %v using a key", device)
|
|
}
|
|
return err
|
|
}
|
|
|
|
// UnlockEncryptedVolumeWithRecoveryKey prompts for the recovery key and uses it
|
|
// to open an encrypted device.
|
|
func UnlockEncryptedVolumeWithRecoveryKey(name, device string) error {
|
|
options := sb.ActivateVolumeOptions{
|
|
RecoveryKeyTries: 3,
|
|
KeyringPrefix: keyringPrefix,
|
|
}
|
|
|
|
if err := sbActivateVolumeWithRecoveryKey(name, device, nil, &options); err != nil {
|
|
return fmt.Errorf("cannot unlock encrypted device %q: %v", device, err)
|
|
}
|
|
|
|
return nil
|
|
}
|