-
Notifications
You must be signed in to change notification settings - Fork 601
/
Copy pathsecboot_sb.go
185 lines (162 loc) · 6.55 KB
/
secboot_sb.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// -*- 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
}