]> git.ipfire.org Git - thirdparty/dracut.git/blob - modules.d/90crypt/crypt-lib.sh
Fix boot with `fips` without a value
[thirdparty/dracut.git] / modules.d / 90crypt / crypt-lib.sh
1 #!/bin/sh
2
3 command -v getarg >/dev/null || . /lib/dracut-lib.sh
4
5 # check if the crypttab contains an entry for a LUKS UUID
6 crypttab_contains() {
7 local luks="$1"
8 local dev="$2"
9 local l d rest
10 if [ -f /etc/crypttab ]; then
11 while read l d rest || [ -n "$l" ]; do
12 strstr "${l##luks-}" "${luks##luks-}" && return 0
13 strstr "$d" "${luks##luks-}" && return 0
14 if [ -n "$dev" ]; then
15 for _dev in $(devnames $d); do
16 [ "$dev" -ef "$_dev" ] && return 0
17 done
18 fi
19 if [ -e /etc/block_uuid.map ]; then
20 # search for line starting with $d
21 _line=$(sed -n "\,^$d .*$,{p}" /etc/block_uuid.map)
22 [ -z "$_line" ] && continue
23 # get second column with uuid
24 _uuid="$(echo $_line | sed 's,^.* \(.*$\),\1,')"
25 strstr "$_uuid" "${luks##luks-}" && return 0
26 fi
27 done < /etc/crypttab
28 fi
29 return 1
30 }
31
32 # ask_for_password
33 #
34 # Wraps around plymouth ask-for-password and adds fallback to tty password ask
35 # if plymouth is not present.
36 #
37 # --cmd command
38 # Command to execute. Required.
39 # --prompt prompt
40 # Password prompt. Note that function already adds ':' at the end.
41 # Recommended.
42 # --tries n
43 # How many times repeat command on its failure. Default is 3.
44 # --ply-[cmd|prompt|tries]
45 # Command/prompt/tries specific for plymouth password ask only.
46 # --tty-[cmd|prompt|tries]
47 # Command/prompt/tries specific for tty password ask only.
48 # --tty-echo-off
49 # Turn off input echo before tty command is executed and turn on after.
50 # It's useful when password is read from stdin.
51 ask_for_password() {
52 local cmd; local prompt; local tries=3
53 local ply_cmd; local ply_prompt; local ply_tries=3
54 local tty_cmd; local tty_prompt; local tty_tries=3
55 local ret
56
57 while [ $# -gt 0 ]; do
58 case "$1" in
59 --cmd) ply_cmd="$2"; tty_cmd="$2"; shift;;
60 --ply-cmd) ply_cmd="$2"; shift;;
61 --tty-cmd) tty_cmd="$2"; shift;;
62 --prompt) ply_prompt="$2"; tty_prompt="$2"; shift;;
63 --ply-prompt) ply_prompt="$2"; shift;;
64 --tty-prompt) tty_prompt="$2"; shift;;
65 --tries) ply_tries="$2"; tty_tries="$2"; shift;;
66 --ply-tries) ply_tries="$2"; shift;;
67 --tty-tries) tty_tries="$2"; shift;;
68 --tty-echo-off) tty_echo_off=yes;;
69 esac
70 shift
71 done
72
73 { flock -s 9;
74 # Prompt for password with plymouth, if installed and running.
75 if type plymouth >/dev/null 2>&1 && plymouth --ping 2>/dev/null; then
76 plymouth ask-for-password \
77 --prompt "$ply_prompt" --number-of-tries=$ply_tries \
78 --command="$ply_cmd"
79 ret=$?
80 else
81 if [ "$tty_echo_off" = yes ]; then
82 stty_orig="$(stty -g)"
83 stty -echo
84 fi
85
86 local i=1
87 while [ $i -le $tty_tries ]; do
88 [ -n "$tty_prompt" ] && \
89 printf "$tty_prompt [$i/$tty_tries]:" >&2
90 eval "$tty_cmd" && ret=0 && break
91 ret=$?
92 i=$(($i+1))
93 [ -n "$tty_prompt" ] && printf '\n' >&2
94 done
95
96 [ "$tty_echo_off" = yes ] && stty $stty_orig
97 fi
98 } 9>/.console_lock
99
100 [ $ret -ne 0 ] && echo "Wrong password" >&2
101 return $ret
102 }
103
104 # Try to mount specified device (by path, by UUID or by label) and check
105 # the path with 'test'.
106 #
107 # example:
108 # test_dev -f LABEL="nice label" /some/file1
109 test_dev() {
110 local test_op=$1; local dev="$2"; local f="$3"
111 local ret=1; local mount_point=$(mkuniqdir /mnt testdev)
112 local path
113
114 [ -n "$dev" -a -n "$*" ] || return 1
115 [ -d "$mount_point" ] || die 'Mount point does not exist!'
116
117 if mount -r "$dev" "$mount_point" >/dev/null 2>&1; then
118 test $test_op "${mount_point}/${f}"
119 ret=$?
120 umount "$mount_point"
121 fi
122
123 rmdir "$mount_point"
124
125 return $ret
126 }
127
128 # match_dev devpattern dev
129 #
130 # Returns true if 'dev' matches 'devpattern'. Both 'devpattern' and 'dev' are
131 # expanded to kernel names and then compared. If name of 'dev' is on list of
132 # names of devices matching 'devpattern', the test is positive. 'dev' and
133 # 'devpattern' may be anything which function 'devnames' recognizes.
134 #
135 # If 'devpattern' is empty or '*' then function just returns true.
136 #
137 # Example:
138 # match_dev UUID=123 /dev/dm-1
139 # Returns true if /dev/dm-1 UUID starts with "123".
140 match_dev() {
141 [ -z "$1" -o "$1" = '*' ] && return 0
142 local devlist; local dev
143
144 devlist="$(devnames "$1")" || return 255
145 dev="$(devnames "$2")" || return 255
146
147 strstr "
148 $devlist
149 " "
150 $dev
151 "
152 }
153
154 # getkey keysfile for_dev
155 #
156 # Reads file <keysfile> produced by probe-keydev and looks for first line to
157 # which device <for_dev> matches. The successful result is printed in format
158 # "<keydev>:<keypath>". When nothing found, just false is returned.
159 #
160 # Example:
161 # getkey /tmp/luks.keys /dev/sdb1
162 # May print:
163 # /dev/sdc1:/keys/some.key
164 getkey() {
165 local keys_file="$1"; local for_dev="$2"
166 local luks_dev; local key_dev; local key_path
167
168 [ -z "$keys_file" -o -z "$for_dev" ] && die 'getkey: wrong usage!'
169 [ -f "$keys_file" ] || return 1
170
171 local IFS=:
172 while read luks_dev key_dev key_path || [ -n "$luks_dev" ]; do
173 if match_dev "$luks_dev" "$for_dev"; then
174 echo "${key_dev}:${key_path}"
175 return 0
176 fi
177 done < "$keys_file"
178
179 return 1
180 }
181
182 # readkey keypath keydev device
183 #
184 # Mounts <keydev>, reads key from file <keypath>, optionally processes it (e.g.
185 # if encrypted with GPG) and prints to standard output which is supposed to be
186 # read by cryptsetup. <device> is just passed to helper function for
187 # informational purpose.
188 readkey() {
189 local keypath="$1"
190 local keydev="$2"
191 local device="$3"
192
193 # No mounting needed if the keyfile resides inside the initrd
194 if [ "/" = "$keydev" ]; then
195 local mntp=/
196 else
197 # This creates a unique single mountpoint for *, or several for explicitly
198 # given LUKS devices. It accomplishes unlocking multiple LUKS devices with
199 # a single password entry.
200 local mntp="/mnt/$(str_replace "keydev-$keydev-$keypath" '/' '-')"
201
202 if [ ! -d "$mntp" ]; then
203 mkdir "$mntp"
204 mount -r "$keydev" "$mntp" || die 'Mounting rem. dev. failed!'
205 fi
206 fi
207
208 case "${keypath##*.}" in
209 gpg)
210 if [ -f /lib/dracut-crypt-gpg-lib.sh ]; then
211 . /lib/dracut-crypt-gpg-lib.sh
212 gpg_decrypt "$mntp" "$keypath" "$keydev" "$device"
213 else
214 die "No GPG support to decrypt '$keypath' on '$keydev'."
215 fi
216 ;;
217 img)
218 if [ -f /lib/dracut-crypt-loop-lib.sh ]; then
219 . /lib/dracut-crypt-loop-lib.sh
220 loop_decrypt "$mntp" "$keypath" "$keydev" "$device"
221 printf "%s\n" "umount \"$mntp\"; rmdir \"$mntp\";" > ${hookdir}/cleanup/"crypt-loop-cleanup-99-${mntp##*/}".sh
222 return 0
223 else
224 die "No loop file support to decrypt '$keypath' on '$keydev'."
225 fi
226 ;;
227 *) cat "$mntp/$keypath" ;;
228 esac
229
230 # No unmounting if the keyfile resides inside the initrd
231 if [ "/" != "$keydev" ]; then
232 # General unmounting mechanism, modules doing custom cleanup should return earlier
233 # and install a pre-pivot cleanup hook
234 umount "$mntp"
235 rmdir "$mntp"
236 fi
237 }