blob: 7361465ee2d39d50b8d8d1a2eb52d8c28a983279 (
plain)
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
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
|
# Copyright 1999-2025 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
# @ECLASS: secureboot.eclass
# @MAINTAINER:
# Nowa Ammerlaan <nowa@gentoo.org>
# @AUTHOR:
# Author: Nowa Ammerlaan <nowa@gentoo.org>
# @SUPPORTED_EAPIS: 7 8
# @BLURB: A small eclass to sign efi files for Secure Boot
# @DESCRIPTION:
# Eclass for packages that install .efi files. A use flag and two user
# variables allow signing these .efi files for use on systems with Secure Boot
# enabled.
#
# Signing the files during emerge ensures that any tooling that actually
# installs the bootloaders and kernels to ESP always uses a signed version.
# This prevents Secure Boot from accidentally breaking when upgrading the
# kernel or the bootloader.
#
# Example use
# @CODE
# src_install() {
# default
# secureboot_sign_efi_file in.efi out.efi.signed
# }
# @CODE
#
# Or
# @CODE
# src_install() {
# default
# secureboot_auto_sign
# }
# @CODE
#
# Some tools will automatically detect and use EFI executables with the .signed
# suffix. For tools that do not do this the --in-place argument for
# secureboot_auto_sign can be used to ensure that the signed version is used.
case ${EAPI} in
7|8) ;;
*) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
esac
IUSE="secureboot"
BDEPEND="
secureboot? (
app-crypt/sbsigntools
dev-libs/openssl
)
"
# @ECLASS_VARIABLE: SECUREBOOT_SIGN_KEY
# @USER_VARIABLE
# @DEFAULT_UNSET
# @DESCRIPTION:
# Used with USE=secureboot. Should be set to the path of the private
# key in PEM format to use, or a PKCS#11 URI.
# If unspecified the following locations are tried in order:
# - /etc/portage/secureboot.pem
# - /var/lib/sbctl/keys/db/db.{key,pem} (from app-crypt/sbctl)
# - the MODULES_SIGN_KEY (and MODULES_SIGN_CERT if set)
# - the contents of CONFIG_MODULE_SIG_KEY in the current kernel
# If none of these exist, a new key will be generated at
# /etc/portage/secureboot.pem.
# @ECLASS_VARIABLE: SECUREBOOT_SIGN_CERT
# @USER_VARIABLE
# @DEFAULT_UNSET
# @DESCRIPTION:
# Used with USE=secureboot. Should be set to the path of the public
# key certificate in PEM format to use.
# If unspecified the SECUREBOOT_SIGN_KEY is assumed to also contain the
# certificate belonging to it.
if [[ -z ${_SECUREBOOT_ECLASS} ]]; then
_SECUREBOOT_ECLASS=1
inherit linux-info
# @FUNCTION: secureboot_pkg_setup
# @DESCRIPTION:
# Checks if required user variables are set before starting the build
secureboot_pkg_setup() {
debug-print-function ${FUNCNAME} "$@"
use secureboot || return
# If we are merging a binary then the files in this binary
# are already signed, no need to check the variables.
if [[ ${MERGE_TYPE} != binary ]]; then
if [[ -z ${SECUREBOOT_SIGN_KEY} ]]; then
# No key specified, try some usual suspects
linux-info_pkg_setup
local module_sig_key=
if linux_config_exists MODULE_SIG_KEY; then
: "$(linux_chkconfig_string MODULE_SIG_KEY)"
module_sig_key=${_//\"}
# Convert to absolute path if required
if [[ ${module_sig_key} != pkcs11:* &&
${module_sig_key} != /* ]]
then
module_sig_key=${KV_OUT_DIR}/${module_sig_key}
fi
fi
# Check both the SYSROOT and ROOT, like linux-info.eclass
ewarn "No Secure Boot signing key specified."
if [[ -r ${SYSROOT}/etc/portage/secureboot.pem ]]; then
ewarn "Using ${SYSROOT}/etc/portage/secureboot.pem as signing key"
export SECUREBOOT_SIGN_KEY=${SYSROOT}/etc/portage/secureboot.pem
export SECUREBOOT_SIGN_CERT=${SYSROOT}/etc/portage/secureboot.pem
elif [[ -r ${ROOT}/etc/portage/secureboot.pem ]]; then
ewarn "Using ${ROOT}/etc/portage/secureboot.pem as signing key"
export SECUREBOOT_SIGN_KEY=${ROOT}/etc/portage/secureboot.pem
export SECUREBOOT_SIGN_CERT=${ROOT}/etc/portage/secureboot.pem
elif [[ -r ${SYSROOT}/var/lib/sbctl/keys/db/db.key &&
-r ${SYSROOT}/var/lib/sbctl/keys/db/db.pem ]]
then
ewarn "Using keys maintained by app-crypt/sbctl"
export SECUREBOOT_SIGN_KEY=${SYSROOT}/var/lib/sbctl/keys/db/db.key
export SECUREBOOT_SIGN_CERT=${SYSROOT}/var/lib/sbctl/keys/db/db.pem
elif [[ -r ${ROOT}/var/lib/sbctl/keys/db/db.key &&
-r ${ROOT}/var/lib/sbctl/keys/db/db.pem ]]
then
ewarn "Using keys maintained by app-crypt/sbctl"
export SECUREBOOT_SIGN_KEY=${ROOT}/var/lib/sbctl/keys/db/db.key
export SECUREBOOT_SIGN_CERT=${ROOT}/var/lib/sbctl/keys/db/db.pem
elif [[ -r ${MODULES_SIGN_KEY} ]]; then
ewarn "Using the kernel module signing key"
export SECUREBOOT_SIGN_KEY=${MODULES_SIGN_KEY}
if [[ -r ${MODULES_SIGN_CERT} ]]; then
export SECUREBOOT_SIGN_CERT=${MODULES_SIGN_CERT}
else
export SECUREBOOT_SIGN_CERT=${MODULES_SIGN_KEY}
fi
elif [[ -r ${KV_OUT_DIR}/certs/signing_key.x509 ]] &&
[[ -r ${module_sig_key} || ${module_sig_key} == pkcs11:* ]]
then
ewarn "Using keys maintained by the kernel"
openssl x509 \
-in "${KV_OUT_DIR}/certs/signing_key.x509" -inform DER \
-out "${T}/secureboot.pem" -outform PEM ||
die "Failed to convert kernel certificate to PEM format"
export SECUREBOOT_SIGN_KEY=${module_sig_key}
export SECUREBOOT_SIGN_CERT=${T}/secureboot.pem
else
ewarn "No candidate keys found, generating a new key"
local openssl_gen_args=(
req -new -batch -nodes -utf8 -sha256 -days 36500 -x509
-outform PEM -out "${SYSROOT}/etc/portage/secureboot.pem"
-keyform PEM -keyout "${SYSROOT}/etc/portage/secureboot.pem"
)
if [[ -r ${KV_OUT_DIR}/certs/x509.genkey ]]; then
openssl_gen_args+=(
-config "${KV_OUT_DIR}/certs/x509.genkey"
)
elif [[ -r ${KV_OUT_DIR}/certs/default_x509.genkey ]]; then
openssl_gen_args+=(
-config "${KV_OUT_DIR}/certs/default_x509.genkey"
)
else
openssl_gen_args+=(
-subj '/CN=Build time autogenerated kernel key'
)
fi
(
umask 066
openssl "${openssl_gen_args[@]}" ||
die "Failed to generate new signing key"
# Generate DER format key as well for easy inclusion in
# either the UEFI dB or MOK list.
openssl x509 \
-in "${SYSROOT}/etc/portage/secureboot.pem" -inform PEM \
-out "${ROOT}/etc/portage/secureboot.x509" -outform DER ||
die "Failed to convert signing certificate to DER format"
)
export SECUREBOOT_SIGN_KEY=${SYSROOT}/etc/portage/secureboot.pem
export SECUREBOOT_SIGN_CERT=${SYSROOT}/etc/portage/secureboot.pem
fi
elif [[ -z ${SECUREBOOT_SIGN_CERT} ]]; then
ewarn "A SECUREBOOT_SIGN_KEY was specified but no SECUREBOOT_SIGN_CERT"
ewarn "was set. Assuming the certificate is in the same file as the key."
export SECUREBOOT_SIGN_CERT=${SECUREBOOT_SIGN_KEY}
fi
# Sanity check: fail early if key/cert in DER format or does not exist
local openssl_args=(
-inform PEM -in "${SECUREBOOT_SIGN_CERT}"
-noout -nocert
)
if [[ ${SECUREBOOT_SIGN_KEY} == pkcs11:* ]]; then
openssl_args+=( -engine pkcs11 -keyform ENGINE -key "${SECUREBOOT_SIGN_KEY}" )
else
openssl_args+=( -keyform PEM -key "${SECUREBOOT_SIGN_KEY}" )
fi
openssl x509 "${openssl_args[@]}" ||
die "Secure Boot signing certificate or key not found or not PEM format."
fi
}
# @FUNCTION: secureboot_sign_efi_file
# @USAGE: <input file> [<output file>]
# @DESCRIPTION:
# Sign a file using sbsign and the requested key/certificate.
# If the file is already signed with our key then the file is skipped.
# If no output file is specified the output file will be the same
# as the input file, i.e. the file will be overwritten.
secureboot_sign_efi_file() {
debug-print-function ${FUNCNAME} "$@"
use secureboot || return
local input_file=${1}
local output_file=${2:-${1}}
ebegin "Signing ${input_file}"
local return=1
if sbverify "${input_file}" --cert "${SECUREBOOT_SIGN_CERT}" &> /dev/null; then
ewarn "${input_file} already signed, skipping"
return=0
else
local args=(
"--key=${SECUREBOOT_SIGN_KEY}"
"--cert=${SECUREBOOT_SIGN_CERT}"
)
if [[ ${SECUREBOOT_SIGN_KEY} == pkcs11:* ]]; then
args+=( --engine=pkcs11 )
fi
sbsign "${args[@]}" "${input_file}" --output "${output_file}"
return=${?}
fi
eend ${return} || die "Signing ${input_file} failed"
}
# @FUNCTION: secureboot_auto_sign
# @USAGE: [--in-place]
# @DESCRIPTION:
# Automatically discover and sign efi files in the image directory.
#
# By default signed files gain the .signed suffix. If the --in-place
# argument is given the efi files are replaced with a signed version in place.
secureboot_auto_sign() {
debug-print-function ${FUNCNAME} "$@"
use secureboot || return
[[ ${EBUILD_PHASE} == install ]] ||
die "${FUNCNAME[0]} can only be called in the src_install phase"
local -a efi_execs
mapfile -td '' efi_execs < <(
find "${ED}" -type f \
\( -iname '*.efi' -o -iname '*.efi32' -o -iname '*.efi64' \) \
-print0 || die
)
(( ${#efi_execs[@]} )) ||
die "${FUNCNAME[0]} was called but no efi executables were found"
local suffix
if [[ ${1} == --in-place ]]; then
suffix=""
elif [[ -n ${1} ]]; then
die "Invalid argument ${1}"
else
suffix=".signed"
fi
for efi_exec in "${efi_execs[@]}"; do
secureboot_sign_efi_file "${efi_exec}" "${efi_exec}${suffix}"
done
}
fi
EXPORT_FUNCTIONS pkg_setup
|