Skip to content

Commit eca718c

Browse files
committed
Added TPM 1.2 support for initramfs-tools
Signed-off-by: Oldřich Jedlička <[email protected]>
1 parent 6fb4cd9 commit eca718c

File tree

5 files changed

+281
-102
lines changed

5 files changed

+281
-102
lines changed

src/initramfs-tools/hooks/clevis.in

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,29 @@ find_binary() {
4747
echo "$resolved"
4848
}
4949

50+
find_library() {
51+
lib_name="$1"
52+
for lib_path in \
53+
{/usr,}/libexec/${lib_name} \
54+
{/usr,}/lib64/${lib_name} \
55+
{/usr,}/lib/${lib_name} \
56+
/usr/lib/`uname -m`-linux-gnu*/${lib_name} \
57+
; do
58+
if [ -e "$lib_path" ]; then
59+
echo "$lib_path"
60+
return
61+
fi
62+
done
63+
die 1 "Unable to find library ${lib_name}"
64+
}
65+
5066
if [ -n "${FORCE_CLEVIS}" ] && [ "${FORCE_CLEVIS}" != "n" ]; then
5167
for f in /sbin/cryptsetup /sbin/dmsetup /lib/cryptsetup/askpass; do
5268
if [ ! -e "${DESTDIR}${f}" ]; then
5369
die 2 "cryptsetup utility '$f' wasn't found in the generated ramdisk image. "
5470
fi
5571
done
56-
fi
72+
fi
5773

5874

5975
copy_exec @bindir@/clevis-decrypt-tang || die 1 "@bindir@/clevis-decrypt-tang not found"
@@ -80,6 +96,53 @@ if [ -x @bindir@/clevis-decrypt-tpm2 ]; then
8096
manual_add_modules tpm_crb
8197
manual_add_modules tpm_tis
8298
fi
99+
if [ -x @bindir@/clevis-decrypt-tpm1 ]; then
100+
copy_exec @bindir@/clevis-decrypt-tpm1 || die 1 "@bindir@/clevis-decrypt-tpm1 not found"
101+
copy_exec @libexecdir@/clevis-luks-tpm1-functions || die 1 "@libexecdir@/clevis-luks-tpm1-functions not found"
102+
103+
tcsd_bin=$(find_binary "tcsd")
104+
tpm_version_bin=$(find_binary "tpm_version")
105+
tpm_unsealdata_bin=$(find_binary "tpm_unsealdata")
106+
stdbuf_bin=$(find_binary "stdbuf")
107+
libstdbuf_bin=$(find_library "coreutils/libstdbuf.so*")
108+
109+
copy_exec "${tpm_version_bin}" || die 1 "Unable to copy ${tpm_version_bin}"
110+
copy_exec "${tpm_unsealdata_bin}" || die 1 "Unable to copy ${tpm_unsealdata_bin}"
111+
112+
copy_exec "${tcsd_bin}" || die 1 "Unable to copy ${tcsd_bin}"
113+
copy_file config /etc/tcsd.conf || dia 2 "Unable to copy /etc/tcsd.conf"
114+
115+
copy_exec "${stdbuf_bin}" || die 1 "Unable to copy ${stdbuf_bin}"
116+
copy_exec "${libstdbuf_bin}" || die 1 "Unable to copy ${libstdbuf_bin}"
117+
118+
mkdir -p "${DESTDIR}/var/lib/tpm" || die 2 "Unable to create /var/lib/tpm"
119+
cp /var/lib/tpm/* "${DESTDIR}/var/lib/tpm/" || die 2 "Unable to copy /var/lib/tpm"
120+
121+
mkdir -p "${DESTDIR}/lib/udev/rules.d" || die 2 "Unable to create /lib/udev/rules.d"
122+
# shellcheck disable=SC2043
123+
for rule in 60-tpm-udev.rules; do
124+
if [ -e /etc/udev/rules.d/$rule ]; then
125+
copy_file udev_rule /etc/udev/rules.d/$rule "/lib/udev/rules.d" || die 2 "Unable to copy $rule"
126+
elif [ -e /lib/udev/rules.d/$rule ]; then
127+
copy_file udev_rule /lib/udev/rules.d/$rule "/lib/udev/rules.d" || die 2 "Unable to copy $rule"
128+
fi
129+
done
130+
131+
echo "root:x:0:0:root:/root:/bin/bash" >> "${DESTDIR}/etc/passwd"
132+
echo "root:x:0:" >> "${DESTDIR}/etc/group"
133+
134+
group_id=`id -G tss` || die 2 "Unable to get tss group ID"
135+
user_id=`id -u tss` || die 2 "Unable to get tss user ID"
136+
echo "tss:x:$user_id:$group_id::/var/lib/tpm:/bin/false" >> "${DESTDIR}/etc/passwd"
137+
echo "tss:x:$group_id:" >> "${DESTDIR}/etc/group"
138+
139+
echo "127.0.0.1 localhost" >> "${DESTDIR}/etc/hosts"
140+
echo "::1 localhost ip6-localhost ip6-loopback" >> "${DESTDIR}/etc/hosts"
141+
echo "ff02::1 ip6-allnodes" >> "${DESTDIR}/etc/hosts"
142+
echo "ff02::2 ip6-allrouters" >> "${DESTDIR}/etc/hosts"
143+
144+
manual_add_modules tpm_tis
145+
fi
83146

84147

85148
luksmeta_bin=$(find_binary "luksmeta")

src/initramfs-tools/scripts/local-bottom/clevis.in

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,15 @@ esac
3333

3434
[ -s /run/clevis.pid ] || exit 0
3535

36+
if [ -f @libexecdir@/clevis-luks-tpm1-functions ]; then
37+
. @libexecdir@/clevis-luks-tpm1-functions
38+
stop_tcsd
39+
fi
40+
3641
pid=$(cat /run/clevis.pid)
3742
child_pids=$(ps -o pid,ppid | awk -v pid="$pid" '$2==pid { print $1 }')
3843
for kill_pid in $pid $child_pids; do
39-
kill "$kill_pid"
44+
kill "$kill_pid"
4045
done
4146

4247
# Not really worried about downing extra interfaces: they will come up

src/initramfs-tools/scripts/local-top/clevis.in

Lines changed: 112 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -28,87 +28,73 @@ prereqs) exit 0 ;;
2828
esac
2929

3030
# Return fifo path or nothing if not found
31-
get_fifo_path() {
31+
get_pid_fifo_path() {
3232
local pid="$1"
3333
for fd in /proc/$pid/fd/*; do
3434
if [ -e "$fd" ]; then
3535
if [[ $(readlink -f "${fd}") == *"/cryptsetup/passfifo" ]]; then
3636
readlink -f "${fd}"
37+
return 0
3738
fi
3839
fi
3940
done
41+
return 1
4042
}
4143

42-
# Print the PID of the askpass process and fifo path with a file descriptor opened to
43-
get_askpass_pid() {
44-
psinfo=$(ps) # Doing this so I don't end up matching myself
45-
echo "$psinfo" | awk "/$cryptkeyscript/ { print \$1 }" | while read -r pid; do
46-
pf=$(get_fifo_path "${pid}")
47-
if [[ $pf != "" ]]; then
48-
echo "${pid} ${pf}"
49-
break
50-
fi
51-
done
52-
}
53-
54-
luks1_decrypt() {
55-
local CRYPTTAB_SOURCE=$1
56-
local PASSFIFO=$2
57-
UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e
58-
luksmeta show -d "$CRYPTTAB_SOURCE" | while read -r slot state uuid; do
59-
[ "$state" == "active" ] || continue
60-
[ "$uuid" == "$UUID" ] || continue
44+
# Gets the luks device to be unlocked and used pins
45+
get_pid_device_pins() {
46+
local pid="$1"
47+
local CRYPTTAB_SOURCE=$(cat /proc/${pid}/environ 2> /dev/null | \
48+
tr '\0' '\n' | grep '^CRYPTTAB_SOURCE=' | cut -d= -f2)
6149

62-
lml=$(luksmeta load -d "${CRYPTTAB_SOURCE}" -s "${slot}" -u "${UUID}")
63-
[ $? -eq 0 ] || continue
50+
# Wrong process, no CRYPTTAB_SOURCE, return error
51+
[ -n "$CRYPTTAB_SOURCE" ] || return 1
6452

65-
decrypted=$(echo -n "${lml}" | clevis decrypt 2>/dev/null)
66-
[ $? -eq 0 ] || continue
53+
[ -b "$CRYPTTAB_SOURCE" ] || return 0
6754

68-
# Fail safe
69-
[ "$decrypted" != "" ] || continue
55+
local cache="/var/cache/clevis-disks/${CRYPTTAB_SOURCE//\//_}"
56+
if [ ! -f "$cache" ]; then
57+
local pins=$(clevis_luks_used_pins "$CRYPTTAB_SOURCE")
58+
echo "${CRYPTTAB_SOURCE}:${pins}" > "$cache"
59+
fi
7060

71-
echo -n "${decrypted}" >"$PASSFIFO"
72-
return 0
73-
done
61+
cat "$cache"
62+
return 0
63+
}
7464

75-
return 1
65+
# Print colon-separated password-asking info like device, pins and fifo
66+
# path for unlocking with password
67+
get_askpass_info() {
68+
local cryptkeyscript=$1
69+
local psinfo pf dev_pins
70+
psinfo=$(ps -A) # Doing this so I don't end up matching myself
71+
echo "$psinfo" | awk "/$cryptkeyscript/ { print \$1 }" | {
72+
while read -r pid; do
73+
if pf=$(get_pid_fifo_path "${pid}") && dev_pins=$(get_pid_device_pins "${pid}"); then
74+
if [[ $pf != "" && $dev_pins != "" ]]; then
75+
# Output only in case of clevis device
76+
echo "${dev_pins}:${pf}"
77+
fi
78+
# Return that we found valid process
79+
return 0
80+
fi
81+
done
82+
return 1
83+
}
7684
}
7785

78-
luks2_decrypt() {
86+
# Try to decrypt the password to fifo file
87+
luks_decrypt() {
7988
local CRYPTTAB_SOURCE=$1
8089
local PASSFIFO=$2
81-
cryptsetup luksDump "$CRYPTTAB_SOURCE" | sed -rn 's|^\s+([0-9]+): clevis|\1|p' | while read -r id; do
82-
# jose jwe fmt -c outputs extra \n, so clean it up
83-
cte=$(cryptsetup token export --token-id "$id" "$CRYPTTAB_SOURCE")
84-
[ $? -eq 0 ] || continue
85-
86-
josefmt=$(echo "${cte}" | jose fmt -j- -Og jwe -o-)
87-
[ $? -eq 0 ] || continue
88-
89-
josejwe=$(echo "${josefmt}" | jose jwe fmt -i- -c)
90-
[ $? -eq 0 ] || continue
91-
92-
jwe=$(echo "${josejwe}" | tr -d '\n')
93-
[ $? -eq 0 ] || continue
90+
local pt
9491

95-
decrypted=$(echo -n "${jwe}" | clevis decrypt 2>/dev/null)
96-
[ $? -eq 0 ] || continue
97-
98-
# Fail safe
99-
[ "$decrypted" != "" ] || continue
100-
101-
echo -n "${decrypted}" >"$PASSFIFO"
92+
if pt=$(clevis_luks_unlock_device "${CRYPTTAB_SOURCE}"); then
93+
echo -n "${pt}" >"${PASSFIFO}"
10294
return 0
103-
done
104-
105-
return 1
106-
}
107-
108-
has_tang_pin() {
109-
local dev="$1"
110-
111-
clevis luks list -d "${dev}" | grep -q tang
95+
else
96+
return 1
97+
fi
11298
}
11399

114100
# Wait for askpass, and then try and decrypt immediately. Just in case
@@ -118,71 +104,69 @@ clevisloop() {
118104
# Set the path how we want it (Probably not all needed)
119105
PATH="/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin"
120106

107+
local cryptkeyscript
108+
local askpass_info
109+
local sleep_time
110+
local OLD_CRYPTTAB_SOURCE=""
111+
local netcfg_attempted=0
112+
local tpm1cfg_attempted=0
113+
121114
if [ -x /bin/plymouth ] && plymouth --ping; then
122115
cryptkeyscript='plymouth ask-for-password'
123116
else
124117
# This has to be escaped for awk
125118
cryptkeyscript='\/lib\/cryptsetup\/askpass'
126119
fi
127120

128-
OLD_CRYPTTAB_SOURCE=""
129-
local netcfg_attempted=0
130-
131121
while true; do
132-
133122
# Re-get the askpass PID in case there are multiple encrypted devices
134-
pid=""
135-
until [ "$pid" ] && [ -p "$PASSFIFO" ]; do
136-
sleep .1
137-
pid_fifo=$(get_askpass_pid)
138-
pid=$(echo "${pid_fifo}" | cut -d' ' -f1)
139-
PASSFIFO=$(echo "${pid_fifo}" | cut -d' ' -f2-)
123+
CRYPTTAB_SOURCE=""
124+
sleep_time=.1
125+
until [ -n "$CRYPTTAB_SOURCE" ] && [ -p "$PASSFIFO" ]; do
126+
sleep $sleep_time
127+
if askpass_info=$(get_askpass_info "$cryptkeyscript"); then
128+
# Workaround for initramfs-tools checking the script as sh-compatible
129+
IFS=':' read -r CRYPTTAB_SOURCE pins PASSFIFO <<EOM
130+
${askpass_info}
131+
EOM
132+
# Ask process is running, variables might be empty for non-clevis device
133+
sleep_time=.5
134+
else
135+
# No valid process found
136+
sleep_time=.1
137+
fi
140138
done
141139

142-
# Import CRYPTTAB_SOURCE from the askpass process.
143-
local CRYPTTAB_SOURCE="$(cat /proc/${pid}/environ 2> /dev/null | \
144-
tr '\0' '\n' | grep '^CRYPTTAB_SOURCE=' | cut -d= -f2)"
145-
[ -n "$CRYPTTAB_SOURCE" ] || continue
146-
147-
# Make sure that CRYPTTAB_SOURCE is actually a block device
148-
[ ! -b "$CRYPTTAB_SOURCE" ] && continue
149-
150-
sleep .1
151140
# Make the source has changed if needed
152141
[ "$CRYPTTAB_SOURCE" = "$OLD_CRYPTTAB_SOURCE" ] && continue
153142
OLD_CRYPTTAB_SOURCE="$CRYPTTAB_SOURCE"
154143

155-
if [ $netcfg_attempted -eq 0 ] && has_tang_pin ${CRYPTTAB_SOURCE}; then
144+
if [[ " $pins " == *" tang "* ]] && [ $netcfg_attempted -eq 0 ]; then
156145
netcfg_attempted=1
157146
do_configure_networking
158147
fi
148+
if [[ " $pins " == *" tpm1 "* ]] && [ $tpm1cfg_attempted -eq 0 ]; then
149+
tpm1cfg_attempted=1
150+
do_configure_tpm1
151+
fi
159152

160-
if cryptsetup isLuks --type luks1 "$CRYPTTAB_SOURCE"; then
161-
# If the device is not initialized, sliently skip it.
162-
luksmeta test -d "$CRYPTTAB_SOURCE" || continue
153+
if luks_decrypt "${CRYPTTAB_SOURCE}" "${PASSFIFO}"; then
154+
echo "Unlocked ${CRYPTTAB_SOURCE} with clevis"
163155

164-
if luks1_decrypt "${CRYPTTAB_SOURCE}" "${PASSFIFO}"; then
165-
echo "Unlocked ${CRYPTTAB_SOURCE} with clevis"
166-
else
167-
OLD_CRYPTTAB_SOURCE=""
168-
sleep 5
169-
fi
170-
elif cryptsetup isLuks --type luks2 "$CRYPTTAB_SOURCE"; then
171-
if luks2_decrypt "${CRYPTTAB_SOURCE}" "${PASSFIFO}"; then
172-
echo "Unlocked ${CRYPTTAB_SOURCE} with clevis"
173-
else
174-
OLD_CRYPTTAB_SOURCE=""
175-
sleep 5
176-
fi
156+
# Now that the current device has its password, let's sleep a
157+
# bit. This gives cryptsetup time to actually decrypt the
158+
# device and prompt for the next password if needed.
159+
sleep .5
160+
else
161+
# Unable to unlock now, retry later
162+
OLD_CRYPTTAB_SOURCE=""
163+
sleep 5
177164
fi
178-
# Now that the current device has its password, let's sleep a
179-
# bit. This gives cryptsetup time to actually decrypt the
180-
# device and prompt for the next password if needed.
181-
sleep .5
182165
done
183166
}
184167

185168
. /scripts/functions
169+
. clevis-luks-common-functions
186170

187171
# This is a copy of 'all_netbootable_devices/all_non_enslaved_devices' for
188172
# platforms that might not provide it.
@@ -283,5 +267,33 @@ do_configure_networking() {
283267
fi
284268
}
285269

270+
do_configure_tpm1() {
271+
local tcsd_output=
272+
local tcsd_result
273+
274+
[ -x @bindir@/clevis-decrypt-tpm1 ] && [ -f @libexecdir@/clevis-luks-tpm1-functions ] || return
275+
276+
. @libexecdir@/clevis-luks-tpm1-functions
277+
278+
log_begin_msg "clevis: Starting TCSD daemon"
279+
280+
wait_for_udev 10
281+
282+
if tcsd_output=$(start_tcsd 2>&1); then
283+
log_success_msg "clevis: TCSD up and running"
284+
else
285+
if [ -n "$tcsd_output" ]; then
286+
log_failure_msg "clevis: Unable to start TCSD: $tcsd_output"
287+
else
288+
log_failure_msg "clevis: Unable to start TCSD"
289+
fi
290+
fi
291+
292+
log_end_msg
293+
}
294+
295+
mkdir -p -m 0755 /var/cache
296+
mkdir -p -m 0700 /var/cache/clevis-disks
297+
286298
clevisloop &
287299
echo $! >/run/clevis.pid

0 commit comments

Comments
 (0)