-
Notifications
You must be signed in to change notification settings - Fork 18
/
scencrypt-hook
210 lines (190 loc) · 6.95 KB
/
scencrypt-hook
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
#!/usr/bin/ash
card_status() {
/usr/bin/gpg --homedir "/etc/initcpio/gpg" --card-status --no-tty \
>/dev/null 2>&1
return $?
}
decrypt_file() {
local input="$1"
local output="${2:--}"
local status_file="/etc/initcpio/status"
local again=1
while [ $again -eq 1 ]; do
/usr/bin/gpg --homedir "/etc/initcpio/gpg" \
--status-file "$status_file" \
-o "${output}" --decrypt "${input}" \
0</dev/console 1>/dev/console 2>/dev/console
local status=$?
if [ "$status" -eq 0 ]; then
again=0
else
grep "^\[GNUPG:\] ERROR pkdecrypt_failed 100663383$" "$status_file"
if [ $? -eq 0 ]; then
again=1
else
again=0
fi
fi
echo -n "" > "$status_file"
done
return $status
}
retry() {
local timeout="$1"; shift
local message="$1"; shift
local command="$@"
local status=1
local i=0
eval $command
status=$?
if [ "$status" -gt 0 ]; then
echo "$message" >/dev/console
echo -n "Timeout is $timeout seconds:" >/dev/console
while true; do
eval $command
status=$?
[ $status -eq 0 ] && break
[ $i -eq $timeout ] && break
i=$(($i + 1))
echo -n "." >/dev/console
read -t 1 -s 0</dev/console && break
done
echo "" >/dev/console
fi
return $status
}
run_hook() {
modprobe -a -q dm-crypt >/dev/null 2>&1
[ "${quiet}" = "y" ] && CSQUIET=">/dev/null"
# Get plain key file if specified, example: cryptkey=/dev/sda:2048:4096, cryptkey=rootfs:/keyfile.bin
ckeyfile=
if [ -n "$cryptkey" ]; then
IFS=: read ckdev ckarg1 ckarg2 <<EOF
$cryptkey
EOF
if [ "$ckdev" = "rootfs" ]; then
ckeyfile=$ckarg1
elif resolved=$(resolve_device "${ckdev}" ${rootdelay}); then
case ${ckarg1} in
*[!0-9]*)
# Use a file on the device
# ckarg1 is not numeric: ckarg1=filesystem, ckarg2=path
mkdir /ckey
mount -r -t "$ckarg1" "$resolved" /ckey
dd if="/ckey/$ckarg2" of="$ckeyfile" >/dev/null 2>&1
umount /ckey
;;
*)
# Read raw data from the block device
# ckarg1 is numeric: ckarg1=offset, ckarg2=length
dd if="$resolved" of="$ckeyfile" bs=1 skip="$ckarg1" count="$ckarg2" >/dev/null 2>&1
;;
esac
fi
fi
sed -re 's;#.*$;;g' -e '/^[ ]*$/ d' -i /etc/crypttab
IFS_BACKUP="$IFS"
IFS=$'\n'
for line in $(cat /etc/crypttab); do
# parse fields in the crypttab line
IFS="$IFS_BACKUP" read mapped_name device_path key_spec options <<EOF
$line
EOF
IFS=: read key_file keyarg1 keyarg2 <<EOF
$key_spec
EOF
# handle case of no key file
if [ "$key_file" = "-" -o "$key_file" = "none" ]; then
key_file=
elif [ -r "${ckeyfile}" ]; then
# plain key file via cmdline cryptkey=
echo "Using plain key file specified in cryptkey="
key_file="${ckeyfile}"
elif [ -c "${key_file}" ]; then
# key file is a character device
length=${keyarg1:-32}
dd if=$key_file of=/keyfile.bin bs=1 count=$length >/dev/null 2>&1
key_file=/keyfile.bin
elif [ -b "${key_file}" ]; then
echo "ERROR: Key files on block devices are not supported yet."
key_file=
elif [ -r "${key_file}" -a "${key_file%.gpg}" != "${key_file}" ]; then
# /.gnupg is where the scdaemon socket lives
test -d /.gnupg || mkdir -p /.gnupg
chmod -R go-rwx /.gnupg /etc/initcpio/gpg
# store the key at a known path. this allows the same key to be
# used for multiple disks and only have to decrypt once.
key_dest_path=/etc/initcpio/gpg/key_${key_file//\//S}
# only attempt decryption if the keypath doesn't exist.
if [ -r "${key_dest_path}" ]; then
key_file="${key_dest_path}"
else
# we need to decrypt.
# test communication with card - this is also needed for
# decryption to work at all
retry 60 "Waiting for the smartcard to be inserted
(or press enter to falling back to passphrase)" card_status
# now attempt to decrypt
if decrypt_file "${key_file}" "${key_dest_path}"; then
# we got it!
key_file="${key_dest_path}"
else
# if decryption fails, still prompt for a passphrase
echo "Failed to decrypt key file with GPG."
echo "Falling back to passphrase."
key_file=
fi
fi
elif [ -r "${key_file}" ]; then
cp "${key_file}" /keyfile.bin
key_file=/keyfile.bin
fi
## end key retrieval
## start device setup
# parse options
luksoptions=""
for option in ${options//,/ }; do
case "$option" in
discard)
luksoptions="$luksoptions --allow-discards"
;;
*)
echo "Warning: ignoring unknown crypttab option: $option"
esac
done
# resolve block device
if resolved=$(resolve_device "${device_path}" "${rootdelay}"); then
if cryptsetup isLuks "${device_path}"; then
# LUKS devices
# open device with key file
if [ -n "$key_file" ]; then
if ! eval cryptsetup luksOpen --key-file="${key_file}" \
$luksoptions "${resolved}" "${mapped_name}"; then
echo "WARNING: Failed to luksOpen crypto device" \
${device_path}
key_file=
fi
fi
# open device with passphrase
if [ ! -n "$key_file" ]; then
if ! eval cryptsetup luksOpen $luksoptions "${resolved}" \
"${mapped_name}"; then
echo "WARNING: Failed to luksOpen crypto device" \
${device_path}
fi
fi
else
# non-LUKS
echo "ERROR: ${device_path} is not a LUKS volume."
echo "Plain dm-crypt is not supported by the scencrypt hook."
fi
else
echo "WARNING: Failed to resolve crypto device ${device_path}"
fi
done
IFS="$IFS_BACKUP"
/usr/bin/gpg-connect-agent --homedir "/etc/initcpio/gpg" KILLAGENT /bye \
>/dev/null 2>&1
rm -rf /etc/initcpio/gpg
}
# vim: set ft=sh ts=4 sw=4 et: