Signed-off-by: Roland Hieber <r...@pengutronix.de>
---
PATCH v3:
 - adapt to cs_get_uri_impl from previous patch. cs_get_uri_unchecked
   didn't really need to check for pkg_name anyway as it's only called
   by cs_append_ca_from_uri in the code provider setup path, and the
   code provider should always be able to get its own URI, so we can use
   cs_get_uri_impl here too.
 - cs_check_whitelist: handle the case correctly when the code signing
   provider is not set up yet (i.e. when the BSP is built from scratch)
 - small doc fixes
 - use fixed whitelist filename, remove CODE_SIGNING_WHITELIST_FILENAME
   (feedback by Michael Olbrich)
 - remove superfluous backslash-newline escapes in multi-line defines
   (feedback by Michael Olbrich)
 - add early error handling for openssl | spki-hash in cs_append_ca_*
   (feedback by Michael Olbrich)
 - remove factually untrue comment in cs_append_ca_from_uri (feedback by
   Michael Olbrich)
 - use $(error) instead of $(ptx/error) (feedback by Michael Olbrich)

PATCH v2: 
https://lore.ptxdist.org/ptxdist/20210809080608.23475-5-...@pengutronix.de
 - cs_check_whitelisted: make "needle" a local variable (feedback by
   Michael Olbrich)
 - cs_check_whitelisted: error out with ERROR_KEY_NOT_WHITELISTED also
   if whitelist does not exist yet (Michael Olbrich)
 - rename cs_get_uri to cs_get_uri_unchecked and let cs_get_uri wrap it
   instead of setting cs_no_whitelist_check=1 (Michael Olbrich)
 - docs: simplify introductory example (Michael Olbrich)
 - docs: add short paragraph on how to determine fingerprints of certs

PATCH v1: 
https://lore.ptxdist.org/ptxdist/20210804142330.32739-5-...@pengutronix.de
---
 doc/daily_work.inc                        |  1 +
 doc/dev_code_signing.rst                  | 77 +++++++++++++++++++++++
 platforms/code-signing.in                 | 10 +++
 rules/pre/030-code-signing-consumers.make |  6 ++
 scripts/lib/ptxd_lib_code_signing.sh      | 56 ++++++++++++++++-
 5 files changed, 149 insertions(+), 1 deletion(-)

diff --git a/doc/daily_work.inc b/doc/daily_work.inc
index 37bb9bc48180..a5b32dc5461c 100644
--- a/doc/daily_work.inc
+++ b/doc/daily_work.inc
@@ -180,6 +180,7 @@ options during the `kernel.compile` and `kernel.install` 
stages:
   PTXdist supplies the URI from the ``kernel-modules`` role of the configured
   code signing provider.
   (The code signing provider should use :ref:`cs_set_uri` to set the URI.)
+  The value of this kconfig option from your kernel config file is overridden.
 
 However, additional settings must also be enabled in the kernel config:
 
diff --git a/doc/dev_code_signing.rst b/doc/dev_code_signing.rst
index 413f694980eb..b0b8c9b4a3b8 100644
--- a/doc/dev_code_signing.rst
+++ b/doc/dev_code_signing.rst
@@ -172,3 +172,80 @@ also via an environment variable.
   (``=``, not ``:=``).
   Otherwise the variable is expanded before a code signing provider can perform
   its setup.
+
+Key Whitelisting
+~~~~~~~~~~~~~~~~
+
+In some use cases, it may be feasible to do additional checks to make sure that
+a package uses the correct key material.
+For example, suppose you have a "release" code signing provider which you use
+to sign bootloaders for production,
+and a "development" code signing provider which you use to sign bootloaders
+with an extended feature set, e.g. to allow booting arbitrary kernels and
+userlands for debugging purposes.
+Your production boards are locked down in hardware so the ROM code only
+executes bootloaders signed with the "release" key.
+Now you don't want any bootloader with debugging features to be signed with a
+release key, otherwise someone could boot them on a locked-down production
+device, and use the additional debugging features to get extended access to the
+production device.
+In this case, key whitelisting can help to prevent signing bootloader packages
+with the wrong key.
+
+If the ``CODE_SIGNING_REQUIRE_WHITELIST`` kconfig symbol is enabled,
+the consumer functions :ref:`ptx/cs-get-ca` and :ref:`ptx/cs-get-uri`
+look up the triplet of package name, role name, and the pubkey's SHA256
+fingerprint in ``configs/platform-<name>/code-signing-whitelist``.
+If a key or a CA is not whitelisted for the package in which it is to be used,
+the functions will exit with an error message on the terminal::
+
+   $ ptxdist -v print KERNEL_MAKE_OPT
+   ptxdist: error: SPKI whitelist record 'kernel kernel-modules
+   69C9BBB8BB4DFAE74AB21D06DFB5F2C67066373AE545453276847340822CDF04' not found 
in
+   distrokit/configs/platform-v7a/code-signing-whitelist
+
+   …/ptxdist/rules/kernel.make:196: *** cs-get-uri: whitelist check failed, 
see errors above.  Stop.
+
+If ``CODE_SIGNING_REQUIRE_WHITELIST`` is disabled (the default),
+all keys and CAs are provided to all packages without further checks.
+
+The format of the code signing whitelist consists of one triplet per line, in
+which the elements of the triplet are separated by whitespace.
+If a CA is to be checked, the role name is prefixed with a literal ``ca:``,
+and the fingerprint refers to the public key of the certificate.
+All other unmatched lines in the file are ignored, but we suggest to use ``#``
+to start a line comment so as not to add a whitelist record accidentally.
+
+For example, here is a whitelist for use with the *devel* code provider which
+allows all provided keys to be used by their respective consumers::
+
+   # format: package-name role-name sha256-pubkey-fingerprint
+   kernel      kernel-modules   
69C9BBB8BB4DFAE74AB21D06DFB5F2C67066373AE545453276847340822CDF04
+   image-rauc  update           
0034F8FE5ADC3B0DFE642407275D144DE2398C68CC9A86DD6703D7151116B44E
+   image-rauc  ca:update        
0034F8FE5ADC3B0DFE642407275D144DE2398C68CC9A86DD6703D7151116B44E
+   rauc        ca:update        
0034F8FE5ADC3B0DFE642407275D144DE2398C68CC9A86DD6703D7151116B44E
+
+.. note:: The match is case-sensitive, and the fingerprints are expected
+   in uppercase.
+
+   If a CA consists of more than one certificate, all of their fingerprints
+   must be whitelisted.
+
+You can determine the key fingerprints by copying it from the error message,
+or with the `spki-hash`__ tool from the ``host-extract-cert`` package,
+or with openssl::
+
+   $ openssl pkey -in keyfile.pem -pubout -outform der \
+     | openssl sha256 \
+     | tr 'a-z' 'A-Z'
+   (STDIN)= 69C9BBB8BB4DFAE74AB21D06DFB5F2C67066373AE545453276847340822CDF04
+
+or, in the case of certificates::
+
+   $ openssl x509 -noout -in cert.pem -pubkey \
+     | openssl pkey -pubin -inform pem -pubout -outform der \
+     | openssl sha256 \
+     | tr 'a-z' 'A-Z'
+   (STDIN)= 0034F8FE5ADC3B0DFE642407275D144DE2398C68CC9A86DD6703D7151116B44E
+
+__ https://git.pengutronix.de/cgit/extract-cert/tree/spki-hash.c
diff --git a/platforms/code-signing.in b/platforms/code-signing.in
index 81f9ef6f3c9e..7fa46e644df7 100644
--- a/platforms/code-signing.in
+++ b/platforms/code-signing.in
@@ -20,4 +20,14 @@ source "generated/code_signing_provider.in"
 
 endchoice
 
+config CODE_SIGNING_REQUIRE_WHITELISTED_KEYS
+       bool
+       prompt "require whitelisted keys"
+       help
+         Every time a key from the code provider is used, check if the consumer
+         is allowed to use it.
+
+         Code signing consumers can depend on this option if they want to force
+         the key whitelist check.
+
 endif
diff --git a/rules/pre/030-code-signing-consumers.make 
b/rules/pre/030-code-signing-consumers.make
index e2c6c868e0ee..24bfa1c9c815 100644
--- a/rules/pre/030-code-signing-consumers.make
+++ b/rules/pre/030-code-signing-consumers.make
@@ -21,6 +21,9 @@ $(strip
                $(call ptx/cs-consumer-env, $(1))
                        cs_get_uri '$(strip $(2))'
        )
+       $(if $(filter-out 0,$(.SHELLSTATUS)),
+               $(error cs-get-uri: whitelist check failed – see errors above)
+       )
 )
 endef
 
@@ -33,5 +36,8 @@ $(strip
                $(call ptx/cs-consumer-env, $(1))
                        cs_get_ca '$(strip $(2))'
        )
+       $(if $(filter-out 0,$(.SHELLSTATUS)),
+               $(error cs-get-ca: whitelist check failed – see errors above)
+       )
 )
 endef
diff --git a/scripts/lib/ptxd_lib_code_signing.sh 
b/scripts/lib/ptxd_lib_code_signing.sh
index 8f35c276855f..fe819bcb07ae 100644
--- a/scripts/lib/ptxd_lib_code_signing.sh
+++ b/scripts/lib/ptxd_lib_code_signing.sh
@@ -1,6 +1,7 @@
 #!/bin/bash
 #
 # Copyright (C) 2019 Sascha Hauer <s.ha...@pengutronix.de>
+# Copyright (C) 2020 Jan Luebbe <j.lue...@pengutronix.de>
 # Copyright (C) 2021 Roland Hieber, Pengutronix <r...@pengutronix.de>
 #
 # For further information about the PTXdist project and license conditions
@@ -145,6 +146,47 @@ cs_group_get_roles() {
 }
 export -f cs_group_get_roles
 
+#
+# cs_check_whitelisted <role> <uri/pem or fingerprint prefixed with "sha256:">
+#
+# Checks if the SPKI (Subject Public Key Info) Hash is in the whitelist
+#
+cs_check_whitelisted() {
+    local role="${1:-ERROR_ROLE_IS_EMPTY}"
+    local src="${2}"
+    cs_init_variables
+
+    if [ "${src}" = "ERROR_URI_NOT_YET_SET" ]; then
+       # skip check until the code signing provider has been set up
+       return;
+    fi
+
+    if [ "$(ptxd_get_ptxconf PTXCONF_CODE_SIGNING_REQUIRE_WHITELISTED_KEYS)" 
!= "y" ]; then
+       return
+    fi
+
+    if ! ptxd_in_path PTXDIST_PATH_PLATFORMCONFIGDIR "code-signing-whitelist"; 
then
+       echo ERROR_KEY_NOT_WHITELISTED
+       ptxd_bailout "${pkg_name}: SPKI hash whitelist check for role '${role}' 
(${src}) is required, but configs/<platform>/code-signing-whitelist is missing."
+    fi
+    local whitelist_path="${ptxd_reply}"
+
+    local hash
+    if [ "${src#sha256:}" != "${src}" ]; then
+       hash="${src#sha256:}"
+    else
+       hash=$(ptxd_exec_silent_stderr spki-hash "${src}")
+    fi
+    hash=${hash:-ERROR_HASH_IS_EMPTY}
+    local needle="${pkg_name}\s\+${role}\s\+${hash}"
+
+    if ! grep -q --line-regexp "${needle}" "${whitelist_path}"; then
+       echo ERROR_KEY_NOT_WHITELISTED
+       ptxd_bailout "SPKI whitelist record '${pkg_name} ${role} ${hash}' not 
found in $(ptxd_print_path "${whitelist_path}")"
+    fi
+}
+export -f cs_check_whitelisted
+
 #
 # cs_set_uri <role> <uri>
 #
@@ -191,7 +233,12 @@ cs_get_uri() {
                'Use $(call ptx/cs-get-uri, <PKG>, <role>) instead.'
     fi
 
-    cs_get_uri_impl "$@"
+    local role="${1}"
+    local uri=$(cs_get_uri_impl "${role}")
+
+    if cs_check_whitelisted "${role}" "${uri}"; then
+       echo "${uri}"
+    fi
 }
 export -f cs_get_uri
 
@@ -318,6 +365,9 @@ cs_get_ca() {
     fi
 
     if [ -e "${ca}" ]; then
+       while read fp; do
+           cs_check_whitelisted "ca:${role}" "sha256:${fp}"
+       done < "${keydir}/${role}/ca.fingerprints"
        echo "${ca}"
     fi
 }
@@ -333,6 +383,10 @@ cs_append_ca_from_pem() {
     local pem="${2}"
     cs_init_variables
 
+    openssl x509 -in "${pem}" -inform pem -noout -pubkey | \
+       spki-hash /dev/stdin >> "${keydir}/${role}/ca.fingerprints"
+    check_pipe_status || ptxd_bailout "Extracting SPKI hash from CA '${pem}' 
failed"
+
     cat "${pem}" >> "${keydir}/${role}/ca.pem"
     # add new line in case ${pem} does not end with an EOL
     echo >> "${keydir}/${role}/ca.pem"
-- 
2.30.2


_______________________________________________
ptxdist mailing list
ptxdist@pengutronix.de
To unsubscribe, send a mail with subject "unsubscribe" to 
ptxdist-requ...@pengutronix.de

Reply via email to