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
|
#!/bin/sh
PREREQ="cryptroot-prepare"
#
# Standard initramfs preamble
#
prereqs()
{
# Make sure that cryptroot is run last in local-top
local req
for req in "${0%/*}"/*; do
script="${req##*/}"
if [ "$script" != "${0##*/}" ]; then
printf '%s\n' "$script"
fi
done
}
case $1 in
prereqs)
prereqs
exit 0
;;
esac
. /scripts/functions
[ -f /lib/cryptsetup/functions ] || return 0
. /lib/cryptsetup/functions
# wait_for_source()
# Wait for encrypted $CRYPTTAB_SOURCE . Set $CRYPTTAB_SOURCE
# to its normalized device name when it shows up;
# return 1 if timeout.
wait_for_source() {
wait_for_udev 10
if crypttab_resolve_source; then
# the device is here already, no need to loop
return 0
fi
# If the source device hasn't shown up yet, give it a little while
# to allow for asynchronous device discovery (e.g. USB).
#
# We also need to take into account RAID or other devices that may
# only be available on local-block stage. So, wait 5 seconds upfront,
# in local-top; if that fails, end execution relying on local-block
# invocations. Allow $ROOTDELAY/4 invocations with 1s sleep times (with
# a minimum of 20 invocations), and if after that we still fail, then it's
# really time to give-up. Variable $initrd_cnt tracks the re-invocations.
#
# Part of the lines below has been taken from initramfs-tools
# scripts/local's local_device_setup(), as suggested per
# https://launchpad.net/bugs/164044 .
local slumber=5
if [ "${CRYPTROOT_STAGE-}" = "local-block" ]; then
slumber=1
fi
cryptsetup_message "Waiting for encrypted source device $CRYPTTAB_SOURCE..."
while [ $slumber -gt 0 ]; do
sleep 1
if [ -x /scripts/local-block/lvm2 ]; then
# activate any VG that might hold $CRYPTTAB_SOURCE
/scripts/local-block/lvm2 "$CRYPTTAB_SOURCE"
fi
if crypttab_resolve_source; then
wait_for_udev 10
return 0
fi
slumber=$(( $slumber - 1 ))
done
return 1
}
# setup_mapping()
# Set up a crypttab(5) mapping defined by $CRYPTTAB_NAME,
# $CRYPTTAB_SOURCE, $CRYPTTAB_KEY, $CRYPTTAB_OPTIONS.
setup_mapping() {
local dev initrd_cnt
# We control here the number of re-invocations of this script from
# local-block - the heuristic is $ROOTDELAY/4, with a minimum of 20.
if [ -f "$CRYPTROOT_COUNT_FILE" ]; then
initrd_cnt="$(cat <"$CRYPTROOT_COUNT_FILE")"
else
initrd_cnt="${ROOTDELAY:-180}"
initrd_cnt=$(( initrd_cnt/4 ))
if [ $initrd_cnt -lt 20 ]; then
initrd_cnt=20
fi
echo "$initrd_cnt" >"$CRYPTROOT_COUNT_FILE"
fi
# The same target can be specified multiple times
# e.g. root and resume lvs-on-lvm-on-crypto
if dm_blkdevname "$CRYPTTAB_NAME" >/dev/null; then
return 0
fi
crypttab_parse_options --export --missing-path=fail || return 1
if ! wait_for_source; then
if [ $initrd_cnt -eq 0 ]; then
# we've given up
if [ -n "$panic" ]; then
panic "ALERT! encrypted source device $CRYPTTAB_SOURCE does not exist, can't unlock $CRYPTTAB_NAME."
else
# let the user fix matters if they can
echo " ALERT! encrypted source device $CRYPTTAB_SOURCE does not exist, can't unlock $CRYPTTAB_NAME."
echo " Check cryptopts=source= bootarg: cat /proc/cmdline"
echo " or missing modules, devices: cat /proc/modules; ls /dev"
panic "Dropping to a shell."
fi
return 1 # can't continue because environment is lost
else
initrd_cnt=$(( initrd_cnt - 1 ))
echo "$initrd_cnt" >"$CRYPTROOT_COUNT_FILE"
return 0 # allow some attempts on local-block stage
fi
fi
# our `cryptroot-unlock` script searches for cryptsetup processes
# with a given CRYPTTAB_NAME it their environment
export CRYPTTAB_NAME
if [ -z "${CRYPTTAB_OPTION_keyscript+x}" ]; then
# no keyscript: interactive unlocking, or key file
if [ "${CRYPTTAB_KEY#/FIXME-initramfs-rootmnt/}" != "$CRYPTTAB_KEY" ]; then
# skip the mapping for now if the root FS is not mounted yet
sed -rn 's/^\s*[^#[:blank:]]\S*\s+(\S+)\s.*/\1/p' /proc/mounts | grep -Fxq -- "$rootmnt" || return 1
# substitute the "/FIXME-initramfs-rootmnt/" prefix by the real root FS mountpoint otherwise
CRYPTTAB_KEY="$rootmnt/${CRYPTTAB_KEY#/FIXME-initramfs-rootmnt/}"
fi
if [ "$CRYPTTAB_KEY" != "none" ]; then
if [ ! -e "$CRYPTTAB_KEY" ]; then
cryptsetup_message "ERROR: Skipping target $CRYPTTAB_NAME: non-existing key file $CRYPTTAB_KEY"
return 1
fi
# try only once if we have a key file
CRYPTTAB_OPTION_tries=1
fi
fi
local count=0 maxtries="${CRYPTTAB_OPTION_tries:-3}" fstype vg rv
while [ $maxtries -le 0 ] || [ $count -lt $maxtries ]; do
if [ -z "${CRYPTTAB_OPTION_keyscript+x}" ] && [ "$CRYPTTAB_KEY" != "none" ]; then
# unlock via keyfile
unlock_mapping "$CRYPTTAB_KEY"
else
# unlock interactively or via keyscript
run_keyscript "$CRYPTTAB_KEY" "$count" | unlock_mapping
fi
rv=$?
count=$(( $count + 1 ))
if [ $rv -ne 0 ]; then
cryptsetup_message "ERROR: $CRYPTTAB_NAME: cryptsetup failed, bad password or options?"
sleep 1
continue
elif ! dev="$(dm_blkdevname "$CRYPTTAB_NAME")"; then
cryptsetup_message "ERROR: $CRYPTTAB_NAME: unknown error setting up device mapping"
return 1
fi
if ! fstype="$(get_fstype "$dev")" || [ "$fstype" = "unknown" ]; then
if [ "$CRYPTTAB_TYPE" != "luks" ]; then
# bad password for plain dm-crypt device? or mkfs not run yet?
cryptsetup_message "ERROR: $CRYPTTAB_NAME: unknown fstype, bad password or options?"
wait_for_udev 10
/sbin/cryptsetup remove -- "$CRYPTTAB_NAME"
sleep 1
continue
fi
elif [ "$fstype" = lvm2 ]; then
if [ ! -x /sbin/lvm ]; then
cryptsetup_message "WARNING: $CRYPTTAB_NAME: lvm is not available"
return 1
elif vg="$(lvm pvs --noheadings -o vg_name --config 'log{prefix=""}' -- "$dev")"; then
# activate the VG held by the PV we just unlocked
lvm lvchange -a y --sysinit -- "$vg"
fi
fi
cryptsetup_message "$CRYPTTAB_NAME: set up successfully"
wait_for_udev 10
return 0
done
cryptsetup_message "ERROR: $CRYPTTAB_NAME: maximum number of tries exceeded"
exit 1
}
#######################################################################
# Begin real processing
mkdir -p /cryptroot # might not exist yet if the main system has no crypttab(5)
# Do we have any kernel boot arguments?
if ! grep -qE '^(.*\s)?cryptopts=' /proc/cmdline; then
# ensure $TABFILE exists and has a mtime greater than the boot time
# (existing $TABFILE is preserved)
touch -- "$TABFILE"
else
# let the read builtin unescape the '\' as GRUB substitutes '\' by '\\' in the cmdline
tr ' ' '\n' </proc/cmdline | sed -n 's/^cryptopts=//p' | while IFS= read cryptopts; do
# skip empty values (which can be used to disable the initramfs
# scripts for a particular boot, cf. #873840)
[ -n "$cryptopts" ] || continue
unset -v target source key options
IFS=","
for x in $cryptopts; do
case "$x" in
target=*) target="${x#target=}";;
source=*) source="${x#source=}";;
key=*) key="${x#key=}";;
*) options="${options+$options,}$x";;
esac
done
if [ -z "${source:+x}" ]; then
cryptsetup_message "ERROR: Missing source= value in kernel parameter cryptopts=$cryptopts"
else
# preserve mangling
printf '%s %s %s %s\n' "${target:-cryptroot}" "$source" "${key:-none}" "${options-}"
fi
done >"$TABFILE"
fi
# Do we have any settings from the $TABFILE?
if [ -s "$TABFILE" ]; then
# Create locking directory before invoking cryptsetup(8) to avoid warnings
mkdir -pm0700 /run/cryptsetup
modprobe -q dm_crypt
crypttab_foreach_entry setup_mapping
fi
exit 0
|