mirror of
https://github.com/token2/snapd.git
synced 2026-03-13 11:15:47 -07:00
* gadget/device: introduce package which provides helpers for locations of things Various helpers for getting location of things were spread out or doubled in the code base. This package aggregates them in one place. Signed-off-by: Maciej Borzecki <maciej.zenon.borzecki@canonical.com> * boot,secboot: reset DA lockout counter after successful boot The TPM DictionaryAttack lockout counter is incremented after each unclean shutdown according to Chris Coulson in LP:1979185. This means that eventually enough unclean shutdowns the system fails to boot and asks for the recovery key. To fix this snapd needs to clean the DA lockout counter after each successful run. * device: add missing doc string for TpmLockoutAuthUnder * secboot: add missing tests around MarkSuccessful * tests: add nested core20-da-lockout test This commits adds a nested core20 test that triggers a bunch of unclean shutdowns in a nested VM. On a system that does not have the new `secboot.MarkSuccessful()` code this will eventually trigger a recovery key prompt as each unclean reboot increases the DA lockout counter by one. ``` google-nested:ubuntu-20.04-64 /tmp/work-dir/logs# tail -n1 /tmp/work-dir/logs/serial.log Please enter the recovery key for disk /dev/disk/by-partuuid/3a1bacae-5d46-ce4b-960a-4074d18a8c05: (press TAB for no echo) ``` * devicestate: fix go fmt error * tests: set nested/manual/core20-da-lockout to manual and improve comment/description * secboot: improve docstrings * secboot: shuffle code around so that tests with `-tags nosecboot` work correctly * boot,device: extract StampSealedKeys,SealedKeysMethod from boot to device * secboot: use new device.SealedKeysMethod() * tests: make core20-da-lockout faster by reading the da lockout counter directly (thanks to Chris Coulson) * tests: add new 'test.nested vm unclean-reboot` and use in da lockout test * tests: retry on lockout counter reading The lockout counter is cleared during snapd startup so we need to retry for a bit to see it cleared. * tests: fix whitespace * secboot: improve test around testMarkSuccessfulEncrypted and also check authValue * secboot: fix silly typo (thanks to Samuele) Co-authored-by: Maciej Borzecki <maciej.zenon.borzecki@canonical.com>
224 lines
7.4 KiB
Go
224 lines
7.4 KiB
Go
// -*- Mode: Go; indent-tabs-mode: t -*-
|
|
|
|
/*
|
|
* 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
|
|
|
|
// This file must not have a build-constraint and must not import
|
|
// the github.com/snapcore/secboot repository. That will ensure
|
|
// it can be build as part of the debian build without secboot.
|
|
// Debian does run "go list" without any support for passing -tags.
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
|
|
"github.com/snapcore/snapd/asserts"
|
|
"github.com/snapcore/snapd/bootloader"
|
|
"github.com/snapcore/snapd/dirs"
|
|
"github.com/snapcore/snapd/gadget/device"
|
|
"github.com/snapcore/snapd/secboot/keys"
|
|
)
|
|
|
|
const (
|
|
// Handles are in the block reserved for TPM owner objects (0x01800000 - 0x01bfffff).
|
|
//
|
|
// Handles are rotated during factory reset, depending on the PCR handle
|
|
// thet was used when sealing key objects during installation (or a
|
|
// previous factory reset).
|
|
RunObjectPCRPolicyCounterHandle = uint32(0x01880001)
|
|
FallbackObjectPCRPolicyCounterHandle = uint32(0x01880002)
|
|
AltRunObjectPCRPolicyCounterHandle = uint32(0x01880003)
|
|
AltFallbackObjectPCRPolicyCounterHandle = uint32(0x01880004)
|
|
)
|
|
|
|
// WithSecbootSupport is true if this package was built with githbu.com/snapcore/secboot.
|
|
var WithSecbootSupport = false
|
|
|
|
type LoadChain struct {
|
|
*bootloader.BootFile
|
|
// Next is a list of alternative chains that can be loaded
|
|
// following the boot file.
|
|
Next []*LoadChain
|
|
}
|
|
|
|
// NewLoadChain returns a LoadChain corresponding to loading the given
|
|
// BootFile before any of the given next chains.
|
|
func NewLoadChain(bf bootloader.BootFile, next ...*LoadChain) *LoadChain {
|
|
return &LoadChain{
|
|
BootFile: &bf,
|
|
Next: next,
|
|
}
|
|
}
|
|
|
|
type SealKeyRequest struct {
|
|
// The key to seal
|
|
Key keys.EncryptionKey
|
|
// The key name; identical keys should have identical names
|
|
KeyName string
|
|
// The path to store the sealed key file. The same Key/KeyName
|
|
// can be stored under multiple KeyFile names for safety.
|
|
KeyFile string
|
|
}
|
|
|
|
// ModelForSealing provides information about the model for use in the context
|
|
// of (re)sealing the encryption keys.
|
|
type ModelForSealing interface {
|
|
Series() string
|
|
BrandID() string
|
|
Model() string
|
|
Grade() asserts.ModelGrade
|
|
SignKeyID() string
|
|
}
|
|
|
|
type SealKeyModelParams struct {
|
|
// The snap model
|
|
Model ModelForSealing
|
|
// The set of EFI binary load chains for the current device
|
|
// configuration
|
|
EFILoadChains []*LoadChain
|
|
// The kernel command line
|
|
KernelCmdlines []string
|
|
}
|
|
|
|
type TPMProvisionMode int
|
|
|
|
const (
|
|
TPMProvisionNone TPMProvisionMode = iota
|
|
// TPMProvisionFull indicates a full provisioning of the TPM
|
|
TPMProvisionFull
|
|
// TPMPartialReprovision indicates a partial reprovisioning of the TPM
|
|
// which was previously already provisioned by secboot. Existing lockout
|
|
// authorization data from TPMLockoutAuthFile will be used to authorize
|
|
// provisioning and will get overwritten in the process.
|
|
TPMPartialReprovision
|
|
)
|
|
|
|
type SealKeysParams struct {
|
|
// The parameters we're sealing the key to
|
|
ModelParams []*SealKeyModelParams
|
|
// The authorization policy update key file (only relevant for TPM)
|
|
TPMPolicyAuthKey *ecdsa.PrivateKey
|
|
// The path to the authorization policy update key file (only relevant for TPM,
|
|
// if empty the key will not be saved)
|
|
TPMPolicyAuthKeyFile string
|
|
// The handle at which to create a NV index for dynamic authorization policy revocation support
|
|
PCRPolicyCounterHandle uint32
|
|
}
|
|
|
|
type SealKeysWithFDESetupHookParams struct {
|
|
// Initial model to bind sealed keys to.
|
|
Model ModelForSealing
|
|
// AuxKey is the auxiliary key used to bind models.
|
|
AuxKey keys.AuxKey
|
|
// The path to the aux key file (if empty the key will not be
|
|
// saved)
|
|
AuxKeyFile string
|
|
}
|
|
|
|
type ResealKeysParams struct {
|
|
// The snap model parameters
|
|
ModelParams []*SealKeyModelParams
|
|
// The path to the sealed key files
|
|
KeyFiles []string
|
|
// The path to the authorization policy update key file (only relevant for TPM)
|
|
TPMPolicyAuthKeyFile string
|
|
}
|
|
|
|
// UnlockVolumeUsingSealedKeyOptions contains options for unlocking encrypted
|
|
// volumes using keys sealed to the TPM.
|
|
type UnlockVolumeUsingSealedKeyOptions struct {
|
|
// AllowRecoveryKey when true indicates activation with the recovery key
|
|
// will be attempted if activation with the sealed key failed.
|
|
AllowRecoveryKey bool
|
|
// WhichModel if invoked should return the device model
|
|
// assertion for which the disk is being unlocked.
|
|
WhichModel func() (*asserts.Model, error)
|
|
}
|
|
|
|
// UnlockMethod is the method that was used to unlock a volume.
|
|
type UnlockMethod int
|
|
|
|
const (
|
|
// NotUnlocked indicates that the device was either not unlocked or is not
|
|
// an encrypted device.
|
|
NotUnlocked UnlockMethod = iota
|
|
// UnlockedWithSealedKey indicates that the device was unlocked with the
|
|
// provided sealed key object.
|
|
UnlockedWithSealedKey
|
|
// UnlockedWithRecoveryKey indicates that the device was unlocked by the
|
|
// user providing the recovery key at the prompt.
|
|
UnlockedWithRecoveryKey
|
|
// UnlockedWithKey indicates that the device was unlocked with the provided
|
|
// key, which is not sealed.
|
|
UnlockedWithKey
|
|
// UnlockStatusUnknown indicates that the unlock status of the device is not clear.
|
|
UnlockStatusUnknown
|
|
)
|
|
|
|
// UnlockResult is the result of trying to unlock a volume.
|
|
type UnlockResult struct {
|
|
// FsDevice is the device with filesystem ready to mount.
|
|
// It is the activated device if encrypted or just
|
|
// the underlying device (same as PartDevice) if non-encrypted.
|
|
// FsDevice can be empty when none was found.
|
|
FsDevice string
|
|
// PartDevice is the underlying partition device.
|
|
// PartDevice can be empty when no device was found.
|
|
PartDevice string
|
|
// IsEncrypted indicates that PartDevice is encrypted.
|
|
IsEncrypted bool
|
|
// UnlockMethod is the method used to unlock the device. Valid values are
|
|
// - NotUnlocked
|
|
// - UnlockedWithRecoveryKey
|
|
// - UnlockedWithSealedKey
|
|
// - UnlockedWithKey
|
|
UnlockMethod UnlockMethod
|
|
}
|
|
|
|
// EncryptedPartitionName returns the name/label used by an encrypted partition
|
|
// corresponding to a given name.
|
|
func EncryptedPartitionName(name string) string {
|
|
return name + "-enc"
|
|
}
|
|
|
|
// MarkSuccessful marks the secure boot parts of the boot as
|
|
// successful.
|
|
//
|
|
//This means that the dictionary attack (DA) lockout counter is reset.
|
|
func MarkSuccessful() error {
|
|
sealingMethod, err := device.SealedKeysMethod(dirs.GlobalRootDir)
|
|
if err != nil && err != device.ErrNoSealedKeys {
|
|
return err
|
|
}
|
|
if sealingMethod == device.SealingMethodTPM {
|
|
lockoutAuthFile := device.TpmLockoutAuthUnder(dirs.SnapFDEDirUnderSave(dirs.SnapSaveDir))
|
|
// each unclean shtutdown will increase the DA lockout
|
|
// counter. So on a successful boot we need to clear
|
|
// this counter to avoid eventually hitting the
|
|
// snapcore/secboot:tpm2/provisioning.go limit of
|
|
// maxTries=32. Note that on a clean shtudown linux
|
|
// will call TPM2_Shutdown which ensure no DA lockout
|
|
// is increased.
|
|
if err := resetLockoutCounter(lockoutAuthFile); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|