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
|
#!/bin/sh
PREREQ=""
prereqs()
{
echo "$PREREQ"
}
case "$1" in
prereqs)
prereqs
exit 0
;;
esac
. /usr/share/initramfs-tools/hook-functions
. /lib/cryptsetup/functions
TABFILE="/etc/crypttab"
# crypttab_find_and_print_entry($target)
# Find the crypttab(5) entry for the given (unmangled) $target and
# print it - preserving the mangling - to FD nr. 3; but only if the
# target has not already been processed during an earlier function
# call. (Processed target names are stored in
# $DESTDIR/cryptroot/targets.)
# Return 0 on success, 1 on error.
crypttab_find_and_print_entry() {
local target="$1"
local _CRYPTTAB_NAME _CRYPTTAB_SOURCE _CRYPTTAB_KEY _CRYPTTAB_OPTIONS
if ! grep -Fxqz -e "$target" -- "$DESTDIR/cryptroot/targets"; then
printf '%s\0' "$target" >>"$DESTDIR/cryptroot/targets"
crypttab_find_entry "$target" || return 1
crypttab_parse_options --missing-path=warn || return 1
crypttab_print_entry
fi
}
# crypttab_print_entry()
# Print an unmangled crypttab(5) entry to FD nr. 3, using CRYPTTAB_*
# and _CRYPTTAB_* values.
# _CRYPTTAB_SOURCE is replaced with /dev/mapper/$sourcename for mapped
# sources, otherwise by UUID=<uuid> if possible (eg, for LUKS). If
# the entry uses the 'decrypt_derived' keyscript, the other
# crypttab(5) entries it depends on are (recursively) printed before
# hand.
# Various checks are performed on the key and crypttab options, but no
# parsing is done so it's the responsibility of the caller to call
# crypttab_parse_options().
# Return 0 on success, 1 on error.
crypttab_print_entry() {
local DEV MAJ MIN sourcename uuid keyfile
if _resolve_device "$CRYPTTAB_SOURCE"; then
if [ "$(dmsetup info -c --noheadings -o devnos_used -- "$CRYPTTAB_NAME" 2>/dev/null)" != "$MAJ:$MIN" ]; then
cryptsetup_message "ERROR: $CRYPTTAB_NAME: Source mismatch"
elif sourcename="$(dmsetup info -c --noheadings -o mangled_name -j "$MAJ" -m "$MIN" 2>/dev/null)" &&
[ -b "/dev/mapper/$sourcename" ]; then
# for mapped sources, use the dm name as the lvm
# local-block script doesn't work with UUIDs (#902943)
_CRYPTTAB_SOURCE="/dev/mapper/$sourcename"
elif uuid="$(_device_uuid "$DEV")"; then
# otherwise, use its UUID if possible (eg. for LUKS)
_CRYPTTAB_SOURCE="UUID=$uuid"
fi
# on failure _resolve_device() prints a warning and we try our
# luck with the unchanged _CRYPTTAB_SOURCE value
fi
# if keyscript is set, the "key" is just an argument to the script
if [ -z "${CRYPTTAB_OPTION_keyscript+x}" ] && [ "$CRYPTTAB_KEY" != "none" ]; then
crypttab_key_check || return 1
case "$CRYPTTAB_KEY" in
$KEYFILE_PATTERN)
mkdir -pm0700 -- "$DESTDIR/cryptroot/keyfiles"
# $CRYPTTAB_NAME can't contain '/' (even after unmangling)
keyfile="/cryptroot/keyfiles/$CRYPTTAB_NAME.key"
if [ ! -f "$DESTDIR$keyfile" ] && ! copy_file keyfile "$CRYPTTAB_KEY" "$keyfile"; then
cryptsetup_message "WARNING: couldn't copy keyfile $CRYPTTAB_KEY"
fi
_CRYPTTAB_KEY="/cryptroot/keyfiles/$_CRYPTTAB_NAME.key" # preserve mangled name
;;
*)
if [ "$usage" = rootfs ]; then
cryptsetup_message "WARNING: Skipping root target $CRYPTTAB_NAME: uses a key file"
return 1
elif [ "$usage" = resume ]; then
cryptsetup_message "WARNING: Resume target $CRYPTTAB_NAME uses a key file"
fi
if [ -L "$CRYPTTAB_KEY" ] && keyfile="$(readlink -- "$CRYPTTAB_KEY")" &&
[ "${keyfile#/}" != "$keyfile" ]; then
cryptsetup_message "WARNING: Skipping target $CRYPTTAB_NAME: key file is a symlink with absolute target"
return 1
elif [ -f "$CRYPTTAB_KEY" ] && [ "$(stat -L -c"%m" -- "$CRYPTTAB_KEY" 2>/dev/null)" != "/" ]; then
cryptsetup_message "WARNING: Skipping target $CRYPTTAB_NAME: key file is not on the root FS"
return 1
fi
if [ ! -e "$CRYPTTAB_KEY" ]; then
cryptsetup_message "WARNING: Target $CRYPTTAB_NAME has a non-existing key file $CRYPTTAB_KEY"
else
_CRYPTTAB_KEY="/FIXME-initramfs-rootmnt$_CRYPTTAB_KEY" # preserve mangled name
fi
esac
fi
if [ -n "${CRYPTTAB_OPTION_keyscript+x}" ]; then
copy_exec "$CRYPTTAB_OPTION_keyscript"
fi
if [ "${CRYPTTAB_OPTION_keyscript-}" = "/lib/cryptsetup/scripts/decrypt_derived" ]; then
# (recursively) list first the device to derive the key from (so
# the boot scripts unlock it first); since _CRYPTTAB_* are local
# to crypttab_find_and_print_entry() the new value won't
# override the new ones
crypttab_find_and_print_entry "$CRYPTTAB_KEY"
fi
printf '%s %s %s %s\n' \
"$_CRYPTTAB_NAME" "$_CRYPTTAB_SOURCE" "$_CRYPTTAB_KEY" "$_CRYPTTAB_OPTIONS" >&3
}
# get_resume_devno()
# Return the device ID(s) used for system suspend/hibernate.
get_resume_devno() {
local dev filename
# uswsusp
for filename in /etc/uswsusp.conf /etc/suspend.conf; do
[ -e "$filename" ] || continue
dev="$(sed -nr '/^resume device\s*[:=]\s*/ {s///p;q}' "$filename")"
if [ -n "$dev" ] && [ "$dev" != "<path_to_resume_device_file>" ]; then
# trim quotes
dev="$(printf '%s' "$dev" | sed -re 's/^"(.*)"\s*$/\1/' -e "s/^'(.*)'\\s*$/\\1/")"
_print_devno "$(printf '%b' "$dev")" # unmangle
fi
done
# regular swsusp
dev="$(sed -nr 's,^(.*\s)?resume=(\S+)(\s.*)?$,\2,p' /proc/cmdline)"
dev="$(printf '%b' "$dev")" # unmangle
_print_devno "$(printf '%b' "$dev")" # unmangle
# initramfs-tools >=0.129
dev="${RESUME:-auto}"
if [ "$dev" != none ]; then
if [ "$dev" = auto ]; then
# next line from /usr/share/initramfs-tools/hooks/resume
dev="$(grep ^/dev/ /proc/swaps | sort -rnk3 | head -n 1 | cut -d " " -f 1)"
fi
_print_devno "$(printf '%b' "$dev")" # unmangle
fi
}
_print_devno() {
local DEV MAJ MIN # locally scope the 3 variables _resolve_device() sets
if [ -n "$1" ] && _resolve_device "$1"; then
printf '%d:%d\n' "$MAJ" "$MIN"
fi
}
# crypttab_print_initramfs_entry()
# Print a crypttab(5) entry - unless it was already processed - if it
# has the 'initramfs' option set.
crypttab_print_initramfs_entry() {
local usage=
if ! grep -Fxqz -e "$CRYPTTAB_NAME" -- "$DESTDIR/cryptroot/targets" &&
crypttab_parse_options --quiet &&
[ "${CRYPTTAB_OPTION_initramfs-no}" = "yes" ]; then
printf '%s\0' "$CRYPTTAB_NAME" >>"$DESTDIR/cryptroot/targets"
crypttab_print_entry
fi
}
# generate_initrd_crypttab()
# Generate the crypttab(5) snippet that is relevant at initramfs
# stage. (Devices that aren't required at initramfs stage are
# ignored.)
generate_initrd_crypttab() {
local devnos usage IFS="$(printf '\t\n ')"
mkdir -- "$DESTDIR/cryptroot"
true >"$DESTDIR/cryptroot/targets"
{
if devnos="$(get_mnt_devno /)"; then
usage=rootfs foreach_cryptdev crypttab_find_and_print_entry $devnos
else
cryptsetup_message "WARNING: Couldn't determine root device"
fi
if devnos="$(get_resume_devno)"; then
usage=resume foreach_cryptdev crypttab_find_and_print_entry $devnos
fi
if devnos="$(get_mnt_devno /usr)"; then
usage="" foreach_cryptdev crypttab_find_and_print_entry $devnos
fi
# add crypttab entries with the 'initramfs' option set
crypttab_foreach_entry crypttab_print_initramfs_entry
} 3>"$DESTDIR/cryptroot/crypttab"
rm -f "$DESTDIR/cryptroot/targets"
}
# populate_CRYPTO_MODULES()
# Find out which crypto modules are required for a crypttab(5) entry,
# and append them to the CRYPTO_MODULES variable.
populate_CRYPTO_MODULES() {
local cipher iv
# cf. dmsetup(8) and https://gitlab.com/cryptsetup/cryptsetup/wikis/DMCrypt
cipher="$(dmsetup table --target crypt -- "$CRYPTTAB_NAME" | cut -d' ' -f4)"
if [ -z "$cipher" ]; then
cryptsetup_message "WARNING: Couldn't determine cipher modules to load for $CRYPTTAB_NAME"
elif [ "${cipher#capi:}" = "$cipher" ]; then
# direct specification "cipher[:keycount]-chainmode-ivmode[:ivopts]"
CRYPTO_MODULES="${CRYPTO_MODULES:+$CRYPTO_MODULES }${cipher%%[-:]*}" # cipher
cipher="${cipher#"${cipher%%-*}-"}" # chainmode-ivmode[:ivopts]"
CRYPTO_MODULES="${CRYPTO_MODULES:+$CRYPTO_MODULES }${cipher%-*}" # chainmode
iv="${cipher##*-}" # ivmode[:ivopts]"
CRYPTO_MODULES="${CRYPTO_MODULES:+$CRYPTO_MODULES }${iv%%:*}" # ivmode
if [ "${iv#*:}" != "$iv" ]; then
CRYPTO_MODULES="${CRYPTO_MODULES:+$CRYPTO_MODULES }${iv#*:}" # ivopts
fi
else
# kernel crypto API format "capi:cipher_api_spec-ivmode[:ivopts]", since linux 4.12
cipher="${cipher#capi:}"
cryptsetup_message "WARNING: Couldn't determine cipher modules to load for $CRYPTTAB_NAME" \
"(kernel crypto API format isn't supported yet)"
fi
}
# add_modules($glob, $moduledir, [$moduledir ..])
# Add modules matching under the given $moduledir(s), the name of
# which matching $glob.
# Return 0 if any module was found found, 1 if not.
add_modules() {
local glob="$1" found=n
shift
for mod in $(find -H "$@" -name "$glob.ko" -type f -printf '%f\n'); do
manual_add_modules "${mod%.ko}"
found=y
done
[ "$found" = y ] && return 0 || return 1
}
# add_crypto_modules($name, [$name ..])
# Determine kernel module name and add to initramfs.
add_crypto_modules() {
local mod
for mod in "$@"; do
# We have several potential sources of modules (in order of preference):
#
# a) /lib/modules/$VERSION/kernel/arch/$ARCH/crypto/$mod-$specific.ko
# b) /lib/modules/$VERSION/kernel/crypto/$mod_generic.ko
# c) /lib/modules/$VERSION/kernel/crypto/$mod.ko
#
# and (currently ignored):
#
# d) /lib/modules/$VERSION/kernel/drivers/crypto/$specific-$mod.ko
add_modules "$mod-*" "$MODULESDIR"/kernel/arch/*/crypto || true
add_modules "${mod}_generic" "$MODULESDIR/kernel/crypto" \
|| add_modules "$mod" "$MODULESDIR/kernel/crypto" \
|| true
done
}
#######################################################################
# Begin real processing
unset -v KEYFILE_PATTERN
KEYFILE_PATTERN=
# Load the hook config
if [ -f "/etc/cryptsetup-initramfs/conf-hook" ]; then
. /etc/cryptsetup-initramfs/conf-hook
fi
if [ -n "$KEYFILE_PATTERN" ]; then
case "${UMASK:-$(umask)}" in
0[0-7]77) ;;
*) cryptsetup_message "WARNING: Permissive UMASK (${UMASK:-$(umask)})." \
"Private key material within the initrd might be left unprotected."
;;
esac
fi
CRYPTO_MODULES=
if [ -r "$TABFILE" ]; then
generate_initrd_crypttab
TABFILE="$DESTDIR/cryptroot/crypttab"
crypttab_foreach_entry populate_CRYPTO_MODULES
fi
# add required components
manual_add_modules dm_mod
manual_add_modules dm_crypt
copy_exec /sbin/cryptsetup
copy_exec /sbin/dmsetup
copy_exec /lib/cryptsetup/askpass
# We need sed. Either via busybox or as standalone binary.
if [ "$BUSYBOX" = n ] || [ ! -e "$BUSYBOXDIR/busybox" ]; then
copy_exec /bin/sed
fi
# detect whether the host CPU has AES-NI support
if grep -Eq '^flags\s*:(.*\s)?aes(\s.*)?$' /proc/cpuinfo; then
CRYPTO_MODULES="${CRYPTO_MODULES:+$CRYPTO_MODULES }aesni"
else
# workaround for #883595/#901884 (xts depends on ecb)
CRYPTO_MODULES="${CRYPTO_MODULES:+$CRYPTO_MODULES }ecb"
fi
# add userspace crypto module (only required for opening LUKS2 devices
# we add the module unconditionally as it's the default format)
CRYPTO_MODULES="${CRYPTO_MODULES:+$CRYPTO_MODULES }algif_skcipher"
if [ "$MODULES" = most ]; then
for d in "$MODULESDIR"/kernel/arch/*/crypto; do
copy_modules_dir "${d#"$MODULESDIR/"}"
done
copy_modules_dir "kernel/crypto"
else
if [ "$MODULES" != "dep" ]; then
# with large initramfs, we always add a basic subset of modules
add_crypto_modules aes cbc chainiv cryptomgr krng sha256 xts
fi
add_crypto_modules $(printf '%s' "${CRYPTO_MODULES-}" | tr ' ' '\n' | sort -u)
fi
copy_file library /lib/cryptsetup/functions /lib/cryptsetup/functions
|