-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathsbtool-sign-kernel
executable file
·245 lines (202 loc) · 5.9 KB
/
sbtool-sign-kernel
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
#!/bin/bash
set -e
progname="$(basename "$0")"
error() {
echo "$@" >&2
exit 1
}
quiet_message() {
$QUIET || echo "$@"
}
quiet_error() {
quiet_message "$@" >&2
exit 1
}
_usage() {
cat << END
$progname [options] <source> <destination> [signing key]
This script will prepare an unsigned kernel for use on a system with
UEFI Secure Boot enabled. The kernel located at <source> will be
signed using the signing key and written to <destination>. It will then
register the public component of the signing key for enrollment in the
system MOK if it is not already enrolled.
If [signing key] is unspecified and the working directory is a kernel
build directory, the signing key will be pulled from .config.
If an autodetected signing key has been autogenerated by the kernel build
process and Secure Boot is not enabled, enrollment will be skipped.
This script has several exit values:
0 - success
1 - failure
2 - skipped - kernel not copied into place
options:
-q|--quiet: do not report errors for missing dependencies, just exit with
error
-e|--enroll: queue certificate for enrollment in system MOK. If the
certificate is autodetected, sbtool-enroll-key -a will be
used to skip enrollment of kernel-generated signing keys
-f|--force: queue the certificate for enrollment even if it is
kernel-generated and secure boot is disabled
END
}
help() {
_usage
exit 0
}
usage() {
_usage >&2
exit 1
}
check_commands() {
for command in "$@"; do
if ! command -v "$command" > /dev/null; then
for i in /usr/src/linux-obj/$(uname -m)/*/scripts/"$command" ; do
if [ -x "$i" ] ; then
scriptdir="$(dirname "$i")"
quiet_message "Using $command from $scriptdir"
PATH="$PATH:$scriptdir"
continue 2
fi
done
quiet_error "$command is missing"
fi
done
}
cert_subject_hash() {
local cert=$1
openssl x509 -in $cert -noout -subject_hash
}
cert_kernel_generated() {
local cert=$1
# Based on "CN = Build time autogenerated kernel key"
# as defined in linux/certs/Makefile
KERNEL_GENERATED_CERT_HASH=0926ef54
test "$(cert_subject_hash "$cert")" = "$KERNEL_GENERATED_CERT_HASH"
}
secure_boot_enabled() {
mokutil --sb-state | grep -q "enabled"
}
options=$(getopt -o qhef --long quiet,help,enroll,force -- "$@")
eval set -- $options
QUIET=false
ENROLL=false
FORCE=false
while true; do
case "$1" in
-q|--quiet)
QUIET=true
;;
-e|--enroll)
ENROLL=true
;;
-f|--force)
FORCE=true
;;
-h|--help)
help ;;
--)
shift
break ;;
*)
usage ;;
esac
shift
done
arch="$(rpm -E %{_arch})"
case "$arch" in
i?86|x86_64|aarch64|arm*|ia64|riscv64) sign_tools="pesign pk12util certutil" ;;
ppc*|s390*) sign_tools=sign-file ;;
*) echo "Don't know how to sign a kernel on architecture '$arch'."
exit 1
;;
esac
check_commands $sign_tools openssl
UNSIGNED=$1
SIGNED=$2
CERT=$3
test -z "$UNSIGNED" -o -z "$SIGNED" && usage 1
test -f "$UNSIGNED" || error "$UNSIGNED does not exist."
test -d "$(dirname "$SIGNED")" || error "Target directory for $SIGNED does not exist."
read_cert_config() {
sed -n '/^CONFIG_MODULE_SIG_KEY=/s///p' $1 |tr -d '"'
}
DETECTED_KEY=false
FOUND_CONFIG=false
if test -z "$CERT"; then
for path in .config "$(dirname "$UNSIGNED")/.config"; do
if test -e $path; then
FOUND_CONFIG=true
CERT=$(read_cert_config $path)
if test -n "$CERT"; then
DETECTED_KEY=true
break
fi
fi
done
if test -z "$CERT"; then
if $FOUND_CONFIG; then
echo "Module signing not enabled for this kernel. Skipping."
exit 2
else
error "Couldn't autodetect signing key, no config found."
fi
elif ! test -f "$CERT"; then
error "Certificate \"$CERT\" found in config but does not exist."
fi
fi
cleanup() {
test -n "$tmpdir" && rm -rf "$tmpdir"
}
trap cleanup EXIT
tmpdir=$(mktemp -d /tmp/signkernel.XXXXXX)
if ! openssl x509 -in $CERT -ext keyUsage,extendedKeyUsage -noout | \
grep -q "Code Signing"; then
error "Certificate must have Code Signing extended key usage defined for Secure Boot."
fi
# certutil has no facility to import a private key directly, so we have to
# use the pkcs12 interface instead.
certutil_import_key() {
local certdir=$1
local cert=$2
local P12="$tmpdir/cert.p12"
uuidgen > $tmpdir/passwd
openssl pkcs12 -export -password "file:$tmpdir/passwd" -inkey $cert \
-in $cert -name kernel-cert -out $P12
# pk12util has no silent mode
if ! pk12util -w $tmpdir/passwd -d $certdir -i $P12 > $tmpdir/output; then
cat $tmpdir/output
exit 1
fi
rm -f $tmpdir/passwd $P12 $tmpdir/output
}
case "$sign_tools" in
pesign*)
certutil -N -d $tmpdir --empty-password
certutil_import_key $tmpdir $CERT
pesign -n $tmpdir -c kernel-cert -i $UNSIGNED -o $SIGNED -s --force
;;
sign-file)
openssl x509 -in $CERT -outform DER -out "$tmpdir/cert.crt"
sign-file sha256 $CERT $tmpdir/cert.crt $UNSIGNED $SIGNED
;;
esac
echo "Signed $UNSIGNED with $CERT and installed to $SIGNED"
$ENROLL || exit 0
if $QUIET; then
ARGS="-q"
fi
if $DETECTED_KEY && ! $FORCE && \
cert_kernel_generated "$CERT" && ! secure_boot_enabled; then
echo "Skipping enrollment of kernel-generated certificate on" \
"system without Secure Boot enabled."
quiet_message "Override with --force."
echo ""
exit 0
fi
/usr/sbin/sbtool-enroll-key $ARGS $CERT
ret=$?
# Skipping enrollment doesn't mean we've skipped
# signing and copying so return 0.
if test $? -eq 2; then
ret=0
fi
exit $ret