-
-
Notifications
You must be signed in to change notification settings - Fork 189
/
Copy pathoem-factory-reset
executable file
·521 lines (461 loc) · 15.6 KB
/
oem-factory-reset
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
#!/bin/sh
# Automated setup of TPM, GPG keys, and disk
set -o pipefail
# use TERM to exit on error
trap "exit 1" TERM
export TOP_PID=$$
## Static local variables
CLEAR="--clear"
CONTINUE="--yes-button Continue"
CANCEL="--no-button Cancel"
HEIGHT="30"
WIDTH="90"
USER_PIN_DEF=123456
ADMIN_PIN_DEF=12345678
TPM_PASS_DEF=12345678
CUSTOM_PASS=""
RSA_KEY_LENGTH=3072
GPG_USER_NAME="OEM Key"
GPG_KEY_NAME=`date +%Y%m%d%H%M%S`
GPG_USER_MAIL="oem-${GPG_KEY_NAME}@example.com"
GPG_USER_COMMENT="OEM-generated key"
SKIP_BOOT="n"
## External files sourced
. /etc/functions
. /tmp/config
## functions
die() {
local msg=$1
if [ -n "$msg" ]; then
echo -e "\n$msg"
fi
kill -s TERM $TOP_PID
exit 1
}
whiptail_error()
{
local msg=$1
if [ "$msg" = "" ]; then
die "whiptail error: An error msg is required"
fi
whiptail $BG_COLOR_ERROR --msgbox "${msg}\n\n" $HEIGHT $WIDTH $BG_COLOR_ERROR --title "Error"
}
whiptail_error_die()
{
whiptail_error "$@"
die
}
gpg_key_reset()
{
# Factory reset GPG card
{
echo admin
echo factory-reset
echo y
echo yes
} | gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --card-edit \
> /tmp/gpg_card_edit_output 2>/dev/null
if [ $? -ne 0 ]; then
ERROR=`cat /tmp/gpg_card_edit_output`
whiptail_error_die "GPG Key factory reset failed!\n\n$ERROR"
fi
# If Nitrokey Storage is inserted, reset AES keys as well
if lsusb | grep -q "20a0:4109" && [ -x /bin/hotp_verification ] ; then
/bin/hotp_verification regenerate ${ADMIN_PIN_DEF}
fi
# Set RSA key length
{
echo admin
echo key-attr
echo 1 # RSA
echo ${RSA_KEY_LENGTH} #Signing key size set to RSA_KEY_LENGTH
echo ${ADMIN_PIN_DEF}
echo 1 # RSA
echo ${RSA_KEY_LENGTH} #Encryption key size set to RSA_KEY_LENGTH
echo ${ADMIN_PIN_DEF}
echo 1 # RSA
echo ${RSA_KEY_LENGTH} #Authentication key size set to RSA_KEY_LENGTH
echo ${ADMIN_PIN_DEF}
} | gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --card-edit \
> /tmp/gpg_card_edit_output 2>/dev/null
if [ $? -ne 0 ]; then
ERROR=`cat /tmp/gpg_card_edit_output`
whiptail_error_die "Setting key attributed to RSA ${RSA_KEY_LENGTH} bits in USB security dongle failed."
fi
# Generate OEM GPG keys
{
echo admin
echo generate
echo n
echo ${ADMIN_PIN_DEF}
echo ${USER_PIN_DEF}
echo 0
echo y
echo ${GPG_USER_NAME}
echo ${GPG_USER_MAIL}
echo ${GPG_USER_COMMENT}
} | gpg --command-fd=0 --status-fd=2 --pinentry-mode=loopback --card-edit \
> /tmp/gpg_card_edit_output 2>/dev/null
if [ $? -ne 0 ]; then
ERROR=`cat /tmp/gpg_card_edit_output`
whiptail_error_die "GPG Key automatic keygen failed!\n\n$ERROR"
fi
}
gpg_key_change_pin()
{
# 1 = user PIN, 3 = admin PIN
PIN_TYPE=$1
PIN_ORIG=$2
PIN_NEW=$3
# Change PIN
{
echo admin
echo passwd
echo ${PIN_TYPE}
echo ${PIN_ORIG}
echo ${PIN_NEW}
echo ${PIN_NEW}
echo q
echo q
} | gpg --command-fd=0 --status-fd=2 --pinentry-mode=loopback --card-edit \
> /tmp/gpg_card_edit_output 2>/dev/null
if [ $? -ne 0 ]; then
ERROR=`cat /tmp/gpg_card_edit_output | fold -s`
whiptail_error_die "GPG Key PIN change failed!\n\n$ERROR"
fi
}
generate_checksums()
{
# ensure /boot mounted
if ! grep -q /boot /proc/mounts ; then
mount -o rw /boot || whiptail_error_die "Unable to mount /boot"
else
mount -o remount,rw /boot || whiptail_error_die "Unable to mount /boot"
fi
# clear any existing checksums/signatures
rm /boot/kexec* 2>/dev/null
# create Heads TPM counter
if [ "$CONFIG_TPM" = "y" ]; then
tpm counter_create \
-pwdo "$TPM_PASS_DEF" \
-pwdc '' \
-la -3135106223 \
| tee /tmp/counter \
|| whiptail_error_die "Unable to create TPM counter"
TPM_COUNTER=`cut -d: -f1 < /tmp/counter`
# increment TPM counter
increment_tpm_counter $TPM_COUNTER >/dev/null 2>&1 \
|| whiptail_error_die "Unable to increment tpm counter"
# create rollback file
sha256sum /tmp/counter-$TPM_COUNTER > /boot/kexec_rollback.txt 2>/dev/null \
|| whiptail_error_die "Unable to create rollback file"
else
## needs to exist for initial call to unseal-hotp
echo "0" > /boot/kexec_hotp_counter
fi
# set default boot option
set_default_boot_option
# generate hashes
find /boot -type f ! -name '*kexec*' -print0 \
| xargs -0 sha256sum > /boot/kexec_hashes.txt 2>/dev/null \
|| whiptail_error_die "Error generating kexec hashes"
param_files=`find /boot/kexec*.txt`
[ -z "$param_files" ] \
&& whiptail_error_die "No kexec parameter files to sign"
# sign kexec boot files
if sha256sum $param_files 2>/dev/null | gpg \
--pinentry-mode loopback \
--passphrase "$USER_PIN_DEF" \
--digest-algo SHA256 \
--detach-sign \
-a \
> /boot/kexec.sig 2>/tmp/error; then
# successful - update the validated params
if ! check_config /boot >/dev/null 2>/tmp/error ; then
cat /tmp/error
ret=1
else
ret=0
fi
else
cat /tmp/error
ret=1
fi
# done writing to /boot, switch back to RO
mount -o ro,remount /boot
if [ $ret = 1 ] ; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Error signing kexec boot files:\n\n$ERROR"
fi
}
set_default_boot_option()
{
option_file="/tmp/kexec_options.txt"
tmp_menu_file="/tmp/kexec/kexec_menu.txt"
hash_file="/boot/kexec_default_hashes.txt"
mkdir -p /tmp/kexec/
rm $option_file 2>/dev/null
# parse boot options from grub.cfg
for i in `find /boot -name "grub.cfg"`; do
kexec-parse-boot "/boot" "$i" >> $option_file
done
# FC29/30+ may use BLS format grub config files
# https://fedoraproject.org/wiki/Changes/BootLoaderSpecByDefault
# only parse these if $option_file is still empty
if [ ! -s $option_file ] && [ -d "/boot/loader/entries" ]; then
for i in `find /boot -name "grub.cfg"`; do
kexec-parse-bls "/boot" "$i" "/boot/loader/entries" >> $option_file
done
fi
[ ! -s $option_file ] \
&& whiptail_error_die "Failed to parse any boot options"
# sort boot options
sort -r $option_file | uniq > $tmp_menu_file
## save first option as default
entry=`head -n 1 $tmp_menu_file | tail -1`
# clear existing default configs
rm "/boot/kexec_default.*.txt" 2>/dev/null
# get correct index for entry
index=$(grep -n "$entry" $option_file | cut -f1 -d ':')
# write new config
echo "$entry" > /boot/kexec_default.$index.txt
# validate boot option
( cd /boot && /bin/kexec-boot -b "/boot" -e "$entry" -f \
| xargs sha256sum > $hash_file 2>/dev/null ) \
|| whiptail_error_die "Failed to create hashes of boot files"
}
## main script start
# check for args
if [ "$1" != "" ]; then
title_text=$1
else
title_text="OEM Factory Reset"
fi
if [ "$2" != "" ]; then
bg_color=$2
else
bg_color=""
fi
# show warning prompt
if [ "$CONFIG_TPM" = "y" ]; then
TPM_STR=" * ERASE the TPM and reset it with a default password\n"
else
TPM_STR=""
fi
if ! whiptail --yesno "
This operation will automatically:\n\n
$TPM_STR
* ERASE any keys or passwords on the GPG smart card,\n
reset it to a factory state, and generate new keys\n
* Add the new GPG key to the firmware and reflash it\n
* Sign all of the files in /boot with the new GPG key\n\n
It requires that you already have an OS installed on a\n
dedicated /boot partition. Do you wish to continue?\n" \
$HEIGHT $WIDTH $CONTINUE $CANCEL $CLEAR $bg_color --title "$title_text" ; then
exit 1
fi
# Prompt to change default passwords
echo -e -n "Would you like to set a custom password? [y/N]: "
read -n 1 prompt_output
echo
if [ "$prompt_output" == "y" \
-o "$prompt_output" == "Y" ] \
; then
echo -e "\nThe custom password will be used for the
TPM admin and GPG user/admin passwords.
It must be at least 8 characters in length.\n"
CUSTOM_PASS=""
echo
while [[ ${#CUSTOM_PASS} -lt 8 ]] ; do
echo -e -n "Enter the custom password: "
read CUSTOM_PASS
done
echo
TPM_PASS_DEF=$CUSTOM_PASS
fi
# Prompt to change default GnuPG key information
echo -e -n "Would you like to set custom user information for the GnuPG key? [y/N]: "
read -n 1 prompt_output
echo
if [ "$prompt_output" == "y" \
-o "$prompt_output" == "Y" ] \
; then
echo -e "\n\n"
echo -e "We will generate a GnuPG (PGP) keypair identifiable with the following text form:"
echo -e "Real Name (Comment) [email protected]"
echo -e "\nEnter your Real Name (At least 5 characters long):"
read -r GPG_USER_NAME
while [[ ${#GPG_USER_NAME} -lt 5 ]]; do
{
echo -e "\nEnter your Real Name (At least 5 characters long):"
read -r GPG_USER_NAME
};done
echo -e "\nEnter your [email protected]:"
read -r GPG_USER_MAIL
while ! $(expr "$GPG_USER_MAIL" : '.*@' >/dev/null); do
{
echo -e "\nEnter your [email protected]:"
read -r GPG_USER_MAIL
};done
echo -e "\nEnter Comment (Optional, to distinguish this key from others with same previous attributes. Must be smaller then 60 characters):"
read -r GPG_USER_COMMENT
while [[ ${#GPG_USER_COMMENT} -gt 60 ]]; do
{
echo -e "\nEnter Comment (Optional, to distinguish this key from others with same previous attributes. Must be smaller then 60 characters):"
read -r GPG_USER_COMMENT
};done
fi
## sanity check the USB, GPG key, and boot device before proceeding further
# Prompt to insert USB drive if desired
echo -e -n "Would you like to export your public key to an USB drive? [y/N]: "
read -n 1 prompt_output
echo
if [ "$prompt_output" == "y" \
-o "$prompt_output" == "Y" ] \
; then
GPG_EXPORT=1
# mount USB, then remount rw
echo -e "\nPlease insert an USB drive and hit enter.\n"
read
echo -e "\nChecking for USB media...\n"
# ensure /media not mounted
umount /media 2>/dev/null
# mount-usb will detect and prompt if no USB inserted
if ! mount-usb rw 2>/tmp/error; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Unable to mount USB on /media:\n\n${ERROR}"
fi
else
GPG_EXPORT=0
# needed for USB Security dongle below and is ensured via mount-usb in case of GPG_EXPORT=1
enable_usb
fi
# ensure USB Security Dongle connected
echo -e "\nChecking for USB Security Dongle...\n"
# USB kernel modules already loaded via mount-usb
if ! gpg --card-status >/dev/null 2>&1 ; then
whiptail_error "Can't access USB Security Dongle; \nPlease remove and reinsert, then press Enter."
if ! gpg --card-status >/dev/null 2>/tmp/error ; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Unable to detect USB Security Dongle:\n\n${ERROR}"
fi
fi
# detect and set /boot device
echo -e "\nDetecting and setting boot device...\n"
if ! detect_boot_device ; then
SKIP_BOOT="y"
else
echo -e "Boot device set to $CONFIG_BOOT_DEV\n"
fi
# update configs
if [[ "$SKIP_BOOT" == "n" ]]; then
replace_config /etc/config.user "CONFIG_BOOT_DEV" "$CONFIG_BOOT_DEV"
combine_configs
fi
## reset TPM and set default password
if [ "$CONFIG_TPM" = "y" ]; then
echo -e "\nResetting TPM...\n"
{
echo $TPM_PASS_DEF
echo $TPM_PASS_DEF
} | /bin/tpm-reset >/dev/null 2>/tmp/error
if [ $? -ne 0 ]; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Error resetting TPM:\n\n${ERROR}"
fi
fi
# clear local keyring
rm /.gnupg/*.gpg 2>/dev/null
rm /.gnupg/*.kbx 2>/dev/null
gpg --list-keys >/dev/null 2>&1
## reset the GPG Key
echo -e "\nResetting GPG Key...\n(this will take around 3 minutes...)\n"
gpg_key_reset
# parse name of generated key
GPG_GEN_KEY=`grep -A1 pub /tmp/gpg_card_edit_output | tail -n1 | sed -nr 's/^([ ])*//p'`
PUBKEY="/tmp/${GPG_GEN_KEY}.asc"
if [ "$CUSTOM_PASS" != "" ]; then
echo -e "\nChanging default GPG Admin PIN\n"
gpg_key_change_pin "3" "$ADMIN_PIN_DEF" "$CUSTOM_PASS"
echo -e "\nChanging default GPG User PIN\n"
gpg_key_change_pin "1" "$USER_PIN_DEF" "$CUSTOM_PASS"
USER_PIN_DEF=$CUSTOM_PASS
ADMIN_PIN_DEF=$CUSTOM_PASS
fi
# export pubkey to file
if ! gpg --export --armor $GPG_GEN_KEY > "${PUBKEY}" 2>/tmp/error ; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "GPG Key gpg export to file failed!\n\n$ERROR"
fi
## export pubkey to USB
if [ $GPG_EXPORT -ne 0 ]; then
echo -e "\nExporting generated key to USB...\n"
# copy to USB
if ! cp "${PUBKEY}" "/media/${GPG_GEN_KEY}.asc" 2>/tmp/error ; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Key export error: unable to copy ${GPG_GEN_KEY}.asc to /media:\n\n$ERROR"
fi
umount /media 2>/dev/null
fi
## flash generated key to ROM
echo -e "\nReading current firmware...\n(this will take a minute or two)\n"
/bin/flash.sh -r /tmp/oem-setup.rom >/dev/null 2>/tmp/error
if [ ! -s /tmp/oem-setup.rom ]; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Error reading current firmware:\n\n$ERROR"
fi
# ensure key imported locally
if ! cat "$PUBKEY" | gpg --import >/dev/null 2>/tmp/error ; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Error importing GPG key:\n\n$ERROR"
fi
# update /.gnupg/trustdb.gpg to ultimately trust all user provided public keys
if ! gpg --list-keys --fingerprint --with-colons 2>/dev/null \
| sed -E -n -e 's/^fpr:::::::::([0-9A-F]+):$/\1:6:/p' \
| gpg --import-ownertrust >/dev/null 2>/tmp/error ; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Error importing GPG ownertrust:\n\n$ERROR"
fi
if ! gpg --update-trust >/dev/null 2>/tmp/error ; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Error updating GPG ownertrust:\n\n$ERROR"
fi
# clear any existing heads/gpg files from current firmware
for i in `cbfs -o /tmp/oem-setup.rom -l | grep -e "heads/"`; do
cbfs -o /tmp/oem-setup.rom -d $i
done
# add heads/gpg files to current firmware
if [ -e /.gnupg/pubring.kbx ];then
cbfs -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/pubring.kbx" -f /.gnupg/pubring.kbx
if [ -e /.gnupg/pubring.gpg ];then
rm /.gnupg/pubring.gpg
fi
elif [ -e /.gnupg/pubring.gpg ];then
cbfs -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/pubring.gpg" -f /.gnupg/pubring.gpg
fi
if [ -e /.gnupg/trustdb.gpg ]; then
cbfs -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/trustdb.gpg" -f /.gnupg/trustdb.gpg
fi
# persist user config changes (boot device)
if [ -e /etc/config.user ]; then
cbfs -o /tmp/oem-setup.rom -a "heads/initrd/etc/config.user" -f /etc/config.user
fi
# flash updated firmware image
echo -e "\nAdding generated key to current firmware and re-flashing...\n"
if ! /bin/flash.sh /tmp/oem-setup.rom >/dev/null 2>/tmp/error ; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Error flashing updated firmware image:\n\n$ERROR"
fi
## sign files in /boot and generate checksums
if [[ "$SKIP_BOOT" == "n" ]]; then
echo -e "\nSigning boot files and generating checksums...\n"
generate_checksums
fi
## all done -- reboot
whiptail --msgbox "
The OEM Factory Reset has completed successfully\n\n
After rebooting, you will need to generate new TOTP/HOTP secrets\n
when prompted in order to complete the setup process.\n\n
Press Enter to reboot.\n" \
$HEIGHT $WIDTH --title "OEM Factory Reset Complete"
reboot