This patchset extends the previous version[1] by adding support for
checking against a blacklist of binary hashes.
The IMA subsystem supports custom, built-in, arch-specific policies to
define the files to be measured and appraised. These policies are honored
based on priority, where arch-specific policy is the highest and custom
is the lowest.
PowerNV system uses a Linux-based bootloader to kexec the OS. The
bootloader kernel relies on IMA for signature verification of the OS
kernel before doing the kexec. This patchset adds support for powerpc
arch-specific IMA policies that are conditionally defined based on a
system's secure boot and trusted boot states. The OS secure boot and
trusted boot states are determined via device-tree properties.
The verification needs to be performed only for binaries that are not
blacklisted. The kernel currently only checks against the blacklist of
keys. However, doing so results in blacklisting all the binaries that
are signed by the same key. In order to prevent just one particular
binary from being loaded, it must be checked against a blacklist of
binary hashes. This patchset also adds support to IMA for checking
against a hash blacklist for files. signed by appended signature.
[1] http://patchwork.ozlabs.org/cover/1149262/
Changelog:
v9:
* Includes feedbacks from Michael
* fix the missing of_node_put()
* Includes Mimi's feedbacks
* fix the policy show() function to display check_blacklist
* fix the other comment related and patch description
* add the example of blacklist in the Patch 7/8
Note: Patch 7/8 is giving errors when checkpatch.pl is run because
of the format of showing measurement record as part of the example. I am
not very sure if that can be fixed as we need to represent the
measurements as is.
v8:
* Updates the Patch Description as per Michael's and Mimi's feedback
* Includes feedbacks from Michael for the device tree and policies
* removes the arch-policy hack by defining three arrays.
* fixes related to device-tree calls
* other code specific feedbacks
* Includes feedbacks from Mimi on the blacklist
* generic blacklist function is modified than previous version
* other coding fixes
v7:
* Removes patch related to dt-bindings as per input from Rob Herring.
* fixes Patch 1/8 to use new device-tree updates as per Oliver
feedback to device-tree documentation in skiboot mailing list.
(https://lists.ozlabs.org/pipermail/skiboot/2019-September/015329.html)
* Includes feedbacks from Mimi, Thiago
* moves function get_powerpc_fw_sb_node() from Patch 1 to Patch 3
* fixes Patch 2/8 to use CONFIG_MODULE_SIG_FORCE.
* updates Patch description in Patch 5/8
* adds a new patch to add wrapper is_binary_blacklisted()
* removes the patch that deprecated permit_directio
v6:
* includes feedbacks from Michael Ellerman on the patchset v5
* removed email ids from comments
* add the doc for the device-tree
* renames the secboot.c to secure_boot.c and secboot.h to secure_boot.h
* other code specific fixes
* split the patches to differentiate between secureboot and trustedboot
state of the system
* adds the patches to support the blacklisting of the binary hash.
v5:
* secureboot state is now read via device tree entry rather than OPAL
secure variables
* ima arch policies are updated to use policy based template for
measurement rules
v4:
* Fixed the build issue as reported by Satheesh Rajendran.
v3:
* OPAL APIs in Patch 1 are updated to provide generic interface based on
key/keylen. This patchset updates kernel OPAL APIs to be compatible with
generic interface.
* Patch 2 is cleaned up to use new OPAL APIs.
* Since OPAL can support different types of backend which can vary in the
variable interpretation, the Patch 2 is updated to add a check for the
backend version
* OPAL API now expects consumer to first check the supported backend version
before calling other secvar OPAL APIs. This check is now added in patch 2.
* IMA policies in Patch 3 is updated to specify appended signature and
per policy template.
* The patches now are free of any EFIisms.
v2:
* Removed Patch 1: powerpc/include: Override unneeded early ioremap
functions
* Updated Subject line and patch description of the Patch 1 of this series
* Removed dependency of OPAL_SECVAR on EFI, CPU_BIG_ENDIAN and UCS2_STRING
* Changed OPAL APIs from static to non-static. Added opal-secvar.h for the
same
* Removed EFI hooks from opal_secvar.c
* Removed opal_secvar_get_next(), opal_secvar_enqueue() and
opal_query_variable_info() function
* get_powerpc_sb_mode() in secboot.c now directly calls OPAL Runtime API
rather than via EFI hooks.
* Fixed log messages in get_powerpc_sb_mode() function.
* Added dependency for PPC_SECURE_BOOT on configs PPC64 and OPAL_SECVAR
* Replaced obj-$(CONFIG_IMA) with obj-$(CONFIG_PPC_SECURE_BOOT) in
arch/powerpc/kernel/Makefile
Nayna Jain (8):
powerpc: detect the secure boot mode of the system
powerpc/ima: add support to initialize ima policy rules
powerpc: detect the trusted boot state of the system
powerpc/ima: define trusted boot policy
ima: make process_buffer_measurement() generic
certs: add wrapper function to check blacklisted binary hash
ima: check against blacklisted hashes for files with modsig
powerpc/ima: update ima arch policy to check for blacklist
Documentation/ABI/testing/ima_policy | 4 ++
arch/powerpc/Kconfig | 11 ++++
arch/powerpc/include/asm/secure_boot.h | 29 ++++++++++
arch/powerpc/kernel/Makefile | 2 +
arch/powerpc/kernel/ima_arch.c | 74 ++++++++++++++++++++++++++
arch/powerpc/kernel/secure_boot.c | 58 ++++++++++++++++++++
certs/blacklist.c | 9 ++++
include/keys/system_keyring.h | 6 +++
include/linux/ima.h | 3 +-
security/integrity/ima/ima.h | 11 ++++
security/integrity/ima/ima_appraise.c | 33 ++++++++++++
security/integrity/ima/ima_main.c | 63 ++++++++++++++--------
security/integrity/ima/ima_policy.c | 12 ++++-
security/integrity/integrity.h | 1 +
14 files changed, 291 insertions(+), 25 deletions(-)
create mode 100644 arch/powerpc/include/asm/secure_boot.h
create mode 100644 arch/powerpc/kernel/ima_arch.c
create mode 100644 arch/powerpc/kernel/secure_boot.c
--
2.20.1
This patch defines a function to detect the secure boot state of a
PowerNV system.
The PPC_SECURE_BOOT config represents the base enablement of secure boot
for powerpc.
Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
---
arch/powerpc/Kconfig | 10 ++++++++
arch/powerpc/include/asm/secure_boot.h | 23 ++++++++++++++++++
arch/powerpc/kernel/Makefile | 2 ++
arch/powerpc/kernel/secure_boot.c | 32 ++++++++++++++++++++++++++
4 files changed, 67 insertions(+)
create mode 100644 arch/powerpc/include/asm/secure_boot.h
create mode 100644 arch/powerpc/kernel/secure_boot.c
PowerNV system use a Linux-based bootloader, which relies on the IMA
subsystem to enforce different secure boot modes. Since the verification
policy may differ based on the secure boot mode of the system, the
policies must be defined at runtime.
This patch implements arch-specific support to define IMA policy
rules based on the runtime secure boot mode of the system.
This patch provides arch-specific IMA policies if PPC_SECURE_BOOT
config is enabled.
Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
---
arch/powerpc/Kconfig | 1 +
arch/powerpc/kernel/Makefile | 2 +-
arch/powerpc/kernel/ima_arch.c | 43 ++++++++++++++++++++++++++++++++++
include/linux/ima.h | 3 ++-
4 files changed, 47 insertions(+), 2 deletions(-)
create mode 100644 arch/powerpc/kernel/ima_arch.c
While secure boot permits only properly verified signed kernels to be
booted, trusted boot calculates the file hash of the kernel image and
stores the measurement prior to boot, that can be subsequently compared
against good known values via attestation services.
This patch reads the trusted boot state of a PowerNV system. The state
is used to conditionally enable additional measurement rules in the IMA
arch-specific policies.
Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
---
arch/powerpc/include/asm/secure_boot.h | 6 ++++++
arch/powerpc/kernel/secure_boot.c | 26 ++++++++++++++++++++++++++
2 files changed, 32 insertions(+)
process_buffer_measurement() is limited to measuring the kexec boot
command line. This patch makes process_buffer_measurement() more
generic, allowing it to measure other types of buffer data (e.g.
blacklisted binary hashes or key hashes).
process_buffer_measurement() may be called directly from an IMA
hook or as an auxiliary measurement record. In both cases the buffer
measurement is based on policy. This patch modifies the function to
conditionally retrieve the policy defined PCR and template for the IMA
hook case.
Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
---
security/integrity/ima/ima.h | 3 ++
security/integrity/ima/ima_main.c | 51 ++++++++++++++++++++-----------
2 files changed, 36 insertions(+), 18 deletions(-)
The -EKEYREJECTED error returned by existing is_hash_blacklisted() is
misleading when called for checking against blacklisted hash of a
binary.
This patch adds a wrapper function is_binary_blacklisted() to return
-EPERM error if binary is blacklisted.
Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
---
certs/blacklist.c | 9 +++++++++
include/keys/system_keyring.h | 6 ++++++
2 files changed, 15 insertions(+)
Asymmetric private keys are used to sign multiple files. The kernel
currently support checking against blacklisted keys. However, if the
public key is blacklisted, any file signed by the blacklisted key will
automatically fail signature verification. We might not want to blacklist
all the files signed by a particular key, but just a single file.
Blacklisting the public key is not fine enough granularity.
This patch adds support for checking against the blacklisted hash of the
file based on the IMA policy. The blacklisted hash is the file hash
without the appended signature. Defined is a new policy option
"appraise_flag=check_blacklist".
In addition to the blacklisted binary hashes stored in the firmware "dbx"
variable, the Linux kernel may be configured to load blacklisted binary
hashes onto the .blacklist keyring as well. The following example shows
how to blacklist a kernel module.
$ sha256sum kernel/kheaders.ko
77fa889b35a05338ec52e51591c1b89d4c8d1c99a21251d7c22b1a8642a6bad3 kern
el/kheaders.ko
$ grep BLACKLIST .config
# CONFIG_IMA_BLACKLIST_KEYRING is not set
CONFIG_SYSTEM_BLACKLIST_KEYRING=y
CONFIG_SYSTEM_BLACKLIST_HASH_LIST="blacklist-hash-list"
$ cat certs/blacklist-hash-list
"bin:77fa889b35a05338ec52e51591c1b89d4c8d1c99a21251d7c22b1a8642a6bad3"
Update the IMA custom measurement and appraisal policy rules
(/etc/ima-policy):
measure func=MODULE_CHECK template=ima-modsig
appraise func=MODULE_CHECK appraise_flag=check_blacklist
appraise_type=imasig|modsig
After building, installing, and rebooting the kernel:
# keyctl show %keyring:.blacklist | grep 77fa889b35a05
545660333 ---lswrv 0 0 \_ blacklist:
bin:77fa889b35a05338ec52e51591c1b89d4c8d1c99a21251d7c22b1a8642a6bad3
# cat /sys/kernel/security/ima/policy | grep MODULE_CHECK
measure func=MODULE_CHECK template=ima-modsig
appraise func=MODULE_CHECK appraise_flag=check_blacklist
appraise_type=imasig|modsig
# modprobe kheaders
modprobe: ERROR: could not insert 'kheaders': Permission denied
# cat /sys/kernel/security/ima/ascii_runtime_measurements
10 0c9834db5a0182c1fb0cdc5d3adcf11a11fd83dd ima-sig
sha256:3bc6ed4f0b4d6e31bc1dbc9ef844605abc7afdc6d81a57d77a1ec9407997c40
2 /usr/lib/modules/5.4.0-rc3+/kernel/kernel/kheaders.ko
10 82aad2bcc3fa8ed94762356b5c14838f3bcfa6a0 ima-modsig
sha256:3bc6ed4f0b4d6e31bc1dbc9ef844605abc7afdc6d81a57d77a1ec9407997c40
2 /usr/lib/modules/5.4.0rc3+/kernel/kernel/kheaders.ko sha256:77fa889b3
5a05338ec52e51591c1b89d4c8d1c99a21251d7c22b1a8642a6bad3
3082029a06092a864886f70d010702a082028b30820287020101310d300b0609608648
016503040201300b06092a864886f70d01070131820264....
10 25b72217cc1152b44b134ce2cd68f12dfb71acb3 ima-buf
sha256:8b58427fedcf8f4b20bc8dc007f2e232bf7285d7b93a66476321f9c2a3aa132
b blacklisted-hash
77fa889b35a05338ec52e51591c1b89d4c8d1c99a21251d7c22b1a8642a6bad3
Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
---
Documentation/ABI/testing/ima_policy | 4 ++++
security/integrity/ima/ima.h | 8 +++++++
security/integrity/ima/ima_appraise.c | 33 +++++++++++++++++++++++++++
security/integrity/ima/ima_main.c | 12 ++++++----
security/integrity/ima/ima_policy.c | 12 ++++++++--
security/integrity/integrity.h | 1 +
6 files changed, 64 insertions(+), 6 deletions(-)
@@ -38,6 +39,9 @@ Description: fowner:= decimal value lsm: are LSM specific option: appraise_type:= [imasig] [imasig|modsig]+ appraise_flag:= [check_blacklist]+ Currently, blacklist check is only for files signed with appended+ signature. template:= name of a defined IMA template type (eg, ima-ng). Only valid when action is "measure". pcr:= decimal value
This patch updates the arch-specific policies for PowerNV system to make
sure that the binary hash is not blacklisted.
Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
---
arch/powerpc/kernel/ima_arch.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
In your change set process_buffer_measurement is called with NONE for
the parameter func. So ima_get_action (the above if block) will not be
executed.
Wouldn't it better to update ima_get_action (and related functions) to
handle the ima policy (func param)?
thanks,
-lakshmi
Related to "goto out;" above:
Would of_find_compatible_node return NULL if the given node is not found?
If of_device_is_available returns false (say, because node is NULL or it
does not find the specified node) would it be correct to call of_node_put?
+/*
+ * The "secure_rules" are enabled only on "secureboot" enabled systems.
+ * These rules verify the file signatures against known good values.
+ * The "appraise_type=imasig|modsig" option allows the known good signature
+ * to be stored as an xattr or as an appended signature.
+ *
+ * To avoid duplicate signature verification as much as possible, the IMA
+ * policy rule for module appraisal is added only if CONFIG_MODULE_SIG_FORCE
+ * is not enabled.
+ */
+static const char *const secure_rules[] = {
+ "appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig|modsig",
+#ifndef CONFIG_MODULE_SIG_FORCE
+ "appraise func=MODULE_CHECK appraise_type=imasig|modsig",
+#endif
+ NULL
+};
Is there any way to not use conditional compilation in the above array
definition? Maybe define different functions to get "secure_rules" for
when CONFIG_MODULE_SIG_FORCE is defined and when it is not defined.
Just a suggestion.
-lakshmi
+/*
+ * ima_check_blacklist - determine if the binary is blacklisted.
+ *
+ * Add the hash of the blacklisted binary to the measurement list, based
+ * on policy.
+ *
+ * Returns -EPERM if the hash is blacklisted.
+ */
+int ima_check_blacklist(struct integrity_iint_cache *iint,
+ const struct modsig *modsig, int pcr)
+{
+ enum hash_algo hash_algo;
+ const u8 *digest = NULL;
+ u32 digestsize = 0;
+ int rc = 0;
+
+ if (!(iint->flags & IMA_CHECK_BLACKLIST))
+ return 0;
+
+ if (iint->flags & IMA_MODSIG_ALLOWED && modsig) {
+ ima_get_modsig_digest(modsig, &hash_algo, &digest, &digestsize);
+
+ rc = is_binary_blacklisted(digest, digestsize);
+ if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
+ process_buffer_measurement(digest, digestsize,
+ "blacklisted-hash", NONE,
+ pcr);
+ }
The enum value "NONE" is being passed to process_buffer_measurement to
indicate that the check for required action based on ima policy is
already done by ima_check_blacklist. Not sure, but this can cause
confusion in the future when someone updates process_buffer_measurement.
Would it instead be better to add another parameter to
process_buffer_measurement to indicate the above condition?
-lakshmi
Related to "goto out;" above:
Would of_find_compatible_node return NULL if the given node is not found?
If of_device_is_available returns false (say, because node is NULL or
it does not find the specified node) would it be correct to call
of_node_put?
On 10/24/19 12:35 PM, Lakshmi Ramasubramanian wrote:
On 10/23/2019 8:47 PM, Nayna Jain wrote:
quoted
+/*
+ * The "secure_rules" are enabled only on "secureboot" enabled systems.
+ * These rules verify the file signatures against known good values.
+ * The "appraise_type=imasig|modsig" option allows the known good
signature
+ * to be stored as an xattr or as an appended signature.
+ *
+ * To avoid duplicate signature verification as much as possible,
the IMA
+ * policy rule for module appraisal is added only if
CONFIG_MODULE_SIG_FORCE
+ * is not enabled.
+ */
+static const char *const secure_rules[] = {
+ "appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig|modsig",
+#ifndef CONFIG_MODULE_SIG_FORCE
+ "appraise func=MODULE_CHECK appraise_type=imasig|modsig",
+#endif
+ NULL
+};
Is there any way to not use conditional compilation in the above array
definition? Maybe define different functions to get "secure_rules" for
when CONFIG_MODULE_SIG_FORCE is defined and when it is not defined.
How will you decide which function to be called ?
Thanks & Regards,
- Nayna
In your change set process_buffer_measurement is called with NONE for
the parameter func. So ima_get_action (the above if block) will not be
executed.
Wouldn't it better to update ima_get_action (and related functions) to
handle the ima policy (func param)?
The idea is to use ima-buf template for the auxiliary measurement
record. The auxiliary measurement record is an additional record to the
one already created based on the existing policy. When func is passed as
NONE, it represents it is an additional record. I am not sure what you
mean by updating ima_get_action, it is already handling the ima policy.
Thanks & Regards,
- Nayna
In your change set process_buffer_measurement is called with NONE for
the parameter func. So ima_get_action (the above if block) will not be
executed.
Wouldn't it better to update ima_get_action (and related functions) to
handle the ima policy (func param)?
The idea is to use ima-buf template for the auxiliary measurement
record. The auxiliary measurement record is an additional record to the
one already created based on the existing policy. When func is passed as
NONE, it represents it is an additional record. I am not sure what you
mean by updating ima_get_action, it is already handling the ima policy.
I was referring to using "func" in process_buffer_measurement to
determine ima action. In my opinion, process_buffer_measurement should
be generic.
ima_get_action() should instead determine the required ima action,
template, pcr, etc. based on "func" passed to it.
thanks,
-lakshmi
On 10/24/19 12:48 PM, Lakshmi Ramasubramanian wrote:
On 10/23/2019 8:47 PM, Nayna Jain wrote:
quoted
+/*
+ * ima_check_blacklist - determine if the binary is blacklisted.
+ *
+ * Add the hash of the blacklisted binary to the measurement list,
based
+ * on policy.
+ *
+ * Returns -EPERM if the hash is blacklisted.
+ */
+int ima_check_blacklist(struct integrity_iint_cache *iint,
+ const struct modsig *modsig, int pcr)
+{
+ enum hash_algo hash_algo;
+ const u8 *digest = NULL;
+ u32 digestsize = 0;
+ int rc = 0;
+
+ if (!(iint->flags & IMA_CHECK_BLACKLIST))
+ return 0;
+
+ if (iint->flags & IMA_MODSIG_ALLOWED && modsig) {
+ ima_get_modsig_digest(modsig, &hash_algo, &digest,
&digestsize);
+
+ rc = is_binary_blacklisted(digest, digestsize);
+ if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
+ process_buffer_measurement(digest, digestsize,
+ "blacklisted-hash", NONE,
+ pcr);
+ }
The enum value "NONE" is being passed to process_buffer_measurement to
indicate that the check for required action based on ima policy is
already done by ima_check_blacklist. Not sure, but this can cause
confusion in the future when someone updates process_buffer_measurement.
As I explained in the response to other patch, the purpose is to
indicate that it is an auxiliary measurement record. By passing func as
NONE, it implies there is no explicit policy to be queried for the
template as it is an additional record for an existing policy and is to
use ima-buf template.
What type of confusion do you mean ?
Thanks & Regards,
- Nayna
On 10/25/2019 10:02 AM, Nayna Jain wrote:
>> Is there any way to not use conditional compilation in
>> the above array definition? Maybe define different functions to get
>> "secure_rules" for when CONFIG_MODULE_SIG_FORCE is defined and when
>> it is not defined.
>
> How will you decide which function to be called ?
Define the array in the C file:
const char *const secure_rules_kernel_check[] = {
"appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig|modsig",
NULL
};
const char *const secure_rules_kernel_module_check[] = {
"appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig|modsig",
"appraise func=MODULE_CHECK appraise_type=imasig|modsig",
NULL
};
And, in the header file :
extern const char *const secure_rules_kernel_check;
extern const char *const secure_rules_kernel_module_check;
#ifdef CONFIG_MODULE_SIG_FORCE
const char *secure_rules() { return secure_rules_kernel_check; }
#else
const char *secure_rules() { return secure_rules_kernel_module_check;}
#endif // #ifdef CONFIG_MODULE_SIG_FORCE
If you want to avoid duplication, secure_rules_kernel_check and
secure_rules_kernel_module_check could be defined in separate C files
and conditionally compiled (in Makefile).
I was just trying to suggest the guidelines given in
"Section 21) Conditional Compilation" in coding-style.rst.
It says:
Whenever possible don't use preprocessor conditionals (#ifdef, #if) in
.c files;...
Feel free to do what you think is appropriate.
thanks,
-lakshmi
On Fri, 2019-10-25 at 12:02 -0500, Nayna Jain wrote:
On 10/24/19 12:35 PM, Lakshmi Ramasubramanian wrote:
quoted
On 10/23/2019 8:47 PM, Nayna Jain wrote:
quoted
+/*
+ * The "secure_rules" are enabled only on "secureboot" enabled systems.
+ * These rules verify the file signatures against known good values.
+ * The "appraise_type=imasig|modsig" option allows the known good
signature
+ * to be stored as an xattr or as an appended signature.
+ *
+ * To avoid duplicate signature verification as much as possible,
the IMA
+ * policy rule for module appraisal is added only if
CONFIG_MODULE_SIG_FORCE
+ * is not enabled.
+ */
+static const char *const secure_rules[] = {
+ "appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig|modsig",
+#ifndef CONFIG_MODULE_SIG_FORCE
+ "appraise func=MODULE_CHECK appraise_type=imasig|modsig",
+#endif
+ NULL
+};
Is there any way to not use conditional compilation in the above array
definition? Maybe define different functions to get "secure_rules" for
when CONFIG_MODULE_SIG_FORCE is defined and when it is not defined.
In your change set process_buffer_measurement is called with NONE for
the parameter func. So ima_get_action (the above if block) will not be
executed.
Wouldn't it better to update ima_get_action (and related functions) to
handle the ima policy (func param)?
The idea is to use ima-buf template for the auxiliary measurement
record. The auxiliary measurement record is an additional record to the
one already created based on the existing policy. When func is passed as
NONE, it represents it is an additional record. I am not sure what you
mean by updating ima_get_action, it is already handling the ima policy.
I was referring to using "func" in process_buffer_measurement to
determine ima action. In my opinion, process_buffer_measurement should
be generic.
ima_get_action() should instead determine the required ima action,
template, pcr, etc. based on "func" passed to it.
Nayna's original patch moved ima_get_action() into the caller, but
that resulted in code duplication in each of the callers. This
solution differentiates between the initial, which requires calling
ima_get_action(), and auxiliary buffer measurement records.
Mimi
On Sat, 2019-10-26 at 19:52 -0400, Mimi Zohar wrote:
On Fri, 2019-10-25 at 12:02 -0500, Nayna Jain wrote:
quoted
On 10/24/19 12:35 PM, Lakshmi Ramasubramanian wrote:
quoted
On 10/23/2019 8:47 PM, Nayna Jain wrote:
quoted
+/*
+ * The "secure_rules" are enabled only on "secureboot" enabled systems.
+ * These rules verify the file signatures against known good values.
+ * The "appraise_type=imasig|modsig" option allows the known good
signature
+ * to be stored as an xattr or as an appended signature.
+ *
+ * To avoid duplicate signature verification as much as possible,
the IMA
+ * policy rule for module appraisal is added only if
CONFIG_MODULE_SIG_FORCE
+ * is not enabled.
+ */
+static const char *const secure_rules[] = {
+ "appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig|modsig",
+#ifndef CONFIG_MODULE_SIG_FORCE
+ "appraise func=MODULE_CHECK appraise_type=imasig|modsig",
+#endif
+ NULL
+};
Is there any way to not use conditional compilation in the above array
definition? Maybe define different functions to get "secure_rules" for
when CONFIG_MODULE_SIG_FORCE is defined and when it is not defined.
How will you decide which function to be called ?
You could call "is_module_sig_enforced()".
Calling is_module_sig_enforce() would prevent verifying the same
kernel module appended signature twice, when CONFIG_MODULE_SIG is
enabled, but not CONFIG_MODULE_SIG_FORCE. This comes at the expense
of having to define additional policies.
Unlike for the kernel image, there is no coordination between lockdown
and IMA for kernel modules signature verification. I suggest
deferring defining additional policies to when the lockdown/IMA
coordination is addressed.
Mimi
On Wed, 2019-10-23 at 22:47 -0500, Nayna Jain wrote:
This patchset extends the previous version[1] by adding support for
checking against a blacklist of binary hashes.
The IMA subsystem supports custom, built-in, arch-specific policies to
define the files to be measured and appraised. These policies are honored
based on priority, where arch-specific policy is the highest and custom
is the lowest.
PowerNV system uses a Linux-based bootloader to kexec the OS. The
bootloader kernel relies on IMA for signature verification of the OS
kernel before doing the kexec. This patchset adds support for powerpc
arch-specific IMA policies that are conditionally defined based on a
system's secure boot and trusted boot states. The OS secure boot and
trusted boot states are determined via device-tree properties.
The verification needs to be performed only for binaries that are not
blacklisted. The kernel currently only checks against the blacklist of
keys. However, doing so results in blacklisting all the binaries that
are signed by the same key. In order to prevent just one particular
binary from being loaded, it must be checked against a blacklist of
binary hashes. This patchset also adds support to IMA for checking
against a hash blacklist for files. signed by appended signature.
[1] http://patchwork.ozlabs.org/cover/1149262/
Thanks, Nayna.
Please feel free to add my Signed-off-by tag on patches (2, 4, 5, 7 &
8).
thanks,
Mimi
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2019-10-28 23:42:40
Hi Lakshmi,
Lakshmi Ramasubramanian [off-list ref] writes:
On 10/25/2019 10:02 AM, Nayna Jain wrote:
>> Is there any way to not use conditional compilation in
>> the above array definition? Maybe define different functions to get
>> "secure_rules" for when CONFIG_MODULE_SIG_FORCE is defined and when
>> it is not defined.
>
> How will you decide which function to be called ?
Define the array in the C file:
const char *const secure_rules_kernel_check[] = {
"appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig|modsig",
NULL
};
const char *const secure_rules_kernel_module_check[] = {
"appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig|modsig",
"appraise func=MODULE_CHECK appraise_type=imasig|modsig",
NULL
};
And, in the header file :
But there's no reason for any of this to be in a header, it's all
contained in one file.
Moving things into a header purely to avoid a single #ifdef in a C file
is a backward step.
extern const char *const secure_rules_kernel_check;
extern const char *const secure_rules_kernel_module_check;
#ifdef CONFIG_MODULE_SIG_FORCE
const char *secure_rules() { return secure_rules_kernel_check; }
#else
const char *secure_rules() { return secure_rules_kernel_module_check;}
#endif // #ifdef CONFIG_MODULE_SIG_FORCE
If you want to avoid duplication, secure_rules_kernel_check and
secure_rules_kernel_module_check could be defined in separate C files
and conditionally compiled (in Makefile).
Again that's just lots of added complication for no real benefit.
I was just trying to suggest the guidelines given in
"Section 21) Conditional Compilation" in coding-style.rst.
It says:
Whenever possible don't use preprocessor conditionals (#ifdef, #if) in
.c files;...
The key phrase being "guideline" :)
That suggestion is aimed at avoiding code with lots of ifdefs sprinkled
through the body of functions. Code written in that way can be very hard
to read because you have to mentally pre-process it first, and then read
the C-level logic. See below for an example.
Moving the pre-processing out of line into helpers means when you're
reading the function you can just reason about the C control flow.
The reference to ".c files" is really talking about moving logic that is
#ifdef'ed into static inline helpers. Those typically go in headers, but
they don't have to if there's no other reason for them to be in a
header.
So where the code is all in one C file it would be completely fine to
have an #ifdef in the C file around a static inline helper.
But in this case where the #ifdef is just in an array I think it's
entirely fine to just keep the #ifdef. Its presence there doesn't
complicate the logic in anyway.
cheers
This is a "good" (bad) example of what we're trying to avoid:
static long ppc_set_hwdebug(struct task_struct *child,
struct ppc_hw_breakpoint *bp_info)
{
#ifdef CONFIG_HAVE_HW_BREAKPOINT
int len = 0;
struct thread_struct *thread = &(child->thread);
struct perf_event *bp;
struct perf_event_attr attr;
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
#ifndef CONFIG_PPC_ADV_DEBUG_REGS
struct arch_hw_breakpoint brk;
#endif
if (bp_info->version != 1)
return -ENOTSUPP;
#ifdef CONFIG_PPC_ADV_DEBUG_REGS
/*
* Check for invalid flags and combinations
*/
if ((bp_info->trigger_type == 0) ||
(bp_info->trigger_type & ~(PPC_BREAKPOINT_TRIGGER_EXECUTE |
PPC_BREAKPOINT_TRIGGER_RW)) ||
(bp_info->addr_mode & ~PPC_BREAKPOINT_MODE_MASK) ||
(bp_info->condition_mode &
~(PPC_BREAKPOINT_CONDITION_MODE |
PPC_BREAKPOINT_CONDITION_BE_ALL)))
return -EINVAL;
#if CONFIG_PPC_ADV_DEBUG_DVCS == 0
if (bp_info->condition_mode != PPC_BREAKPOINT_CONDITION_NONE)
return -EINVAL;
#endif
if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_EXECUTE) {
if ((bp_info->trigger_type != PPC_BREAKPOINT_TRIGGER_EXECUTE) ||
(bp_info->condition_mode != PPC_BREAKPOINT_CONDITION_NONE))
return -EINVAL;
return set_instruction_bp(child, bp_info);
}
if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_EXACT)
return set_dac(child, bp_info);
#ifdef CONFIG_PPC_ADV_DEBUG_DAC_RANGE
return set_dac_range(child, bp_info);
#else
return -EINVAL;
#endif
#else /* !CONFIG_PPC_ADV_DEBUG_DVCS */
/*
* We only support one data breakpoint
*/
if ((bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_RW) == 0 ||
(bp_info->trigger_type & ~PPC_BREAKPOINT_TRIGGER_RW) != 0 ||
bp_info->condition_mode != PPC_BREAKPOINT_CONDITION_NONE)
return -EINVAL;
if ((unsigned long)bp_info->addr >= TASK_SIZE)
return -EIO;
brk.address = bp_info->addr & ~7UL;
brk.type = HW_BRK_TYPE_TRANSLATE;
brk.len = 8;
if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_READ)
brk.type |= HW_BRK_TYPE_READ;
if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE)
brk.type |= HW_BRK_TYPE_WRITE;
#ifdef CONFIG_HAVE_HW_BREAKPOINT
/*
* Check if the request is for 'range' breakpoints. We can
* support it if range < 8 bytes.
*/
if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE)
len = bp_info->addr2 - bp_info->addr;
else if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_EXACT)
len = 1;
else
return -EINVAL;
bp = thread->ptrace_bps[0];
if (bp)
return -ENOSPC;
/* Create a new breakpoint request if one doesn't exist already */
hw_breakpoint_init(&attr);
attr.bp_addr = (unsigned long)bp_info->addr & ~HW_BREAKPOINT_ALIGN;
attr.bp_len = len;
arch_bp_generic_fields(brk.type, &attr.bp_type);
thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr,
ptrace_triggered, NULL, child);
if (IS_ERR(bp)) {
thread->ptrace_bps[0] = NULL;
return PTR_ERR(bp);
}
return 1;
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
if (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT)
return -EINVAL;
if (child->thread.hw_brk.address)
return -ENOSPC;
if (!ppc_breakpoint_available())
return -ENODEV;
child->thread.hw_brk = brk;
return 1;
#endif /* !CONFIG_PPC_ADV_DEBUG_DVCS */
}
process_buffer_measurement() is limited to measuring the kexec boot
command line. This patch makes process_buffer_measurement() more
generic, allowing it to measure other types of buffer data (e.g.
blacklisted binary hashes or key hashes).
Now that process_buffer_measurement() is being made generic to measure
any buffer, it would be good to add a tag to indicate what type of
buffer is being measured.
For example, if the buffer is kexec command line the log could look like:
"kexec_cmdline: <command line data>"
Similarly, if the buffer is blacklisted binary hash:
"blacklist hash: <data>".
If the buffer is key hash:
"<name of the keyring>: key data".
This would greatly help the consumer of the IMA log to know the type of
data represented in each IMA log entry.
thanks,
-lakshmi
On Wed, 2019-10-30 at 08:22 -0700, Lakshmi Ramasubramanian wrote:
On 10/23/19 8:47 PM, Nayna Jain wrote:
Hi Nayna,
quoted
process_buffer_measurement() is limited to measuring the kexec boot
command line. This patch makes process_buffer_measurement() more
generic, allowing it to measure other types of buffer data (e.g.
blacklisted binary hashes or key hashes).
Now that process_buffer_measurement() is being made generic to measure
any buffer, it would be good to add a tag to indicate what type of
buffer is being measured.
For example, if the buffer is kexec command line the log could look like:
"kexec_cmdline: <command line data>"
Similarly, if the buffer is blacklisted binary hash:
"blacklist hash: <data>".
If the buffer is key hash:
"<name of the keyring>: key data".
This would greatly help the consumer of the IMA log to know the type of
data represented in each IMA log entry.
Both the existing kexec command line and the new blacklist buffer
measurement pass that information in the eventname. The [PATCH 7/8]
"ima: check against blacklisted hashes for files with modsig" patch
description includes an example.
Mimi