forked from osresearch/safeboot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsafeboot-tpm-unseal
executable file
·203 lines (171 loc) · 5.26 KB
/
safeboot-tpm-unseal
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#!/bin/sh
# This is run in the initramfs context, not in the normal user space.
# The boot mode should have been extended in the start of the initramfs.
#
# It attempts to unseal the key from the TPM based on the PCRS passed
# on the command line using direct access since there is no resource
# manager.
#
# If successful, PCR14 will be extended to prevent later stages from
# retrieving the decryption key. The key is stored in a kernel key
# ring, so it should not be accessible to even a root user.
#
# If the unsealing fails, fall back to asking for the user's recovery key.
#
# turn off "echo flags are undefined" and external shell scripts
# shellcheck disable=SC2039 disable=SC1091
PCRS=0
BOOTMODE_PCR=14
MODE=unknown
[ -r "/etc/safeboot/safeboot.conf" ] && . "/etc/safeboot/safeboot.conf"
[ -r "/etc/safeboot/local.conf" ] && . "/etc/safeboot/local.conf"
# Direct access to the kernel resource manager
# since there is no standalone TPM resource manager in the initrd
export TPM2TOOLS_TCTI="device:/dev/tpmrm0"
sha256() { echo -n "$@" | sha256sum | cut -d' ' -f1 ; }
log() { echo >&2 "$@" ; }
die() {
echo >&2 "$@"
/usr/sbin/tpm2 pcrextend >&2 "$BOOTMODE_PCR:sha256=$(sha256 bootfail)"
touch "/tmp/unseal-failed"
exit 1
}
# shellcheck disable=SC2013
for arg in $(cat /proc/cmdline)
do
case "$arg" in
safeboot.mode=*)
MODE=${arg#safeboot.mode=}
;;
*)
;;
esac
done
log "TPM mode=$MODE pcrs=$PCRS $BOOTMODE_PCR"
/usr/sbin/tpm2 pcrread >&2 \
'sha256:0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16'
if [ "$MODE" = "recovery" ] \
|| [ -r "/tmp/unseal-failed" ] \
; then
log "Falling back to user pass phrase"
# retrieve a tpmtotp attestation so that the user knows
# that the firmware is unmodified and that it is safe to
# enter their credentials.
totp="$(/usr/sbin/tpm2-totp --time calculate || echo TPM TOTP FAILED)"
msg="$totp $MODE
Enter recovery key for $CRYPTTAB_SOURCE ($CRYPTTAB_NAME): "
/lib/cryptsetup/askpass "$msg"
exit $?
fi
#
# This is not a recovery boot, try to unseal the secret using the TPM
# and optional user PIN.
#
tpm2 flushcontext --transient-object
tpm2 flushcontext --loaded-session
log "TPM: Starting auth session"
tpm2 startauthsession \
--policy-session \
--session "/tmp/session.ctx" \
>> /tmp/tpm.log \
|| die "Unable to start policy session"
tpm2 policypcr \
--session "/tmp/session.ctx" \
--pcr-list "sha256:$PCRS,$BOOTMODE_PCR" \
--policy "/tmp/pcr.policy" \
>> /tmp/tpm.log \
|| die "Unable to create PCR policy"
if [ "$SEAL_PIN" = "1" ]; then
# Add an auth value, which will require the PIN for unsealing
tpm2 policyauthvalue \
--session "/tmp/session.ctx" \
--policy "/tmp/pcr.policy" \
>> /tmp/tpm.log \
|| die "Unable to create auth value policy"
fi
tpm2 nvread "$TPM_NV_VERSION" \
| tpm2 policynv \
--session "/tmp/session.ctx" \
"$TPM_NV_VERSION" eq \
--input "-" \
--policy "/tmp/pcr.policy" \
>> /tmp/tpm.log \
|| die "Unable to load TPM version counter"
log "TPM: Loading RSA public key"
tpm2 loadexternal \
--key-algorithm rsa \
--hierarchy owner \
--public "/etc/safeboot/cert.pub" \
--key-context "/tmp/key.ctx" \
--name "/tmp/key.name" \
>> /tmp/tpm.log \
|| die "Unable to load platform key"
# Extract the signature from the UEFI variable, skipping
# the four-byte UEFI variable header.
if ! mount | grep -q /sys/firmware/efi/efivars ; then
mount -t efivarfs efivarfs /sys/firmware/efi/efivars \
|| die "Unable to mount EFI var filesystem"
fi
cat "/sys/firmware/efi/efivars/$PCR_SIGNATURE" > /tmp/efivar.bin \
|| die "Unable to read PCR signature from $PCR_SIGNATURE"
tail \
-c +5 \
< "/tmp/efivar.bin" \
> "/tmp/pcr.policy.sig" \
|| die "Unable to read PCR signature from $PCR_SIGNATURE"
tpm2 verifysignature \
--hash-algorithm sha256 \
--scheme rsassa \
--key-context "/tmp/key.ctx" \
--message "/tmp/pcr.policy" \
--signature "/tmp/pcr.policy.sig" \
--ticket "/tmp/pcr.policy.tkt" \
>> /tmp/tpm.log \
|| die "Unable to verify PCR signature"
tpm2 policyauthorize \
--session "/tmp/session.ctx" \
--name "/tmp/key.name" \
--input "/tmp/pcr.policy" \
--ticket "/tmp/pcr.policy.tkt" \
>> /tmp/tpm.log \
|| die "Unable to authorize ticket"
tpm2 flushcontext --transient-object
tpm2_unseal()
{
PIN="$1"
tpm2 unseal \
--auth "session:/tmp/session.ctx$PIN" \
--object-context "$TPM_SEALED_HANDLE" \
> /tmp/secret.bin \
|| return $?
# Successfully unsealed, extend the bootmode PCR
log "TPM disk key unsealed"
/usr/sbin/tpm2 pcrextend >&2 "$BOOTMODE_PCR:sha256=$(sha256 postboot)"
cat /tmp/secret.bin
rm /tmp/secret.bin
exit 0
}
if [ "$SEAL_PIN" != "1" ]; then
tpm2_unseal ""
else
for tries in 1 2 3; do
while true; do
# Use the askpass program to try to get a pin
# retrieve a tpmtotp attestation so that the user knows
# that the firmware is unmodified and that it is safe to
# enter their credentials.
totp="$(/usr/sbin/tpm2-totp --time calculate || echo TPM TOTP FAILED)"
msg="$totp $MODE (Try $tries)
Enter unseal PIN for $CRYPTTAB_SOURCE ($CRYPTTAB_NAME): "
PIN="$(/lib/cryptsetup/askpass "$msg")"
if [ "$PIN" != "" ]; then
break
fi
done
# try to unseal with the provided PIN
tpm2_unseal "+$PIN"
done
fi
# if we ended up here, things are bad.
# The system will re-run the script to try to use the recovery key
die "UNSEALING FAILED"