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