2018-11-17 06:22:06

by Thiago Jung Bauermann

[permalink] [raw]
Subject: [PATCH v8 00/14] Appended signatures support for IMA appraisal

Hello,

v8 is just a rebase on top of today's linux-integrity/next-integrity.
There aren't any noteworthy changes since v7, which is why I'm keeping its
description below:

The main difference in v7 is the addition of the last patch, which ensures
that there will always be a measurement entry containing the appended
modsig if one was used to appraise the file. The patch description and
comments in the code should explain in which circumstances the patch proved
necessary.

Apart from that, there was some small cleaning up of the code, and merging
and splitting of patches. The changelog below has the details.

Original cover letter:

On the OpenPOWER platform, secure boot and trusted boot are being
implemented using IMA for taking measurements and verifying signatures.
Since the kernel image on Power servers is an ELF binary, kernels are
signed using the scripts/sign-file tool and thus use the same signature
format as signed kernel modules.

This patch series adds support in IMA for verifying those signatures.
It adds flexibility to OpenPOWER secure boot, because it allows it to boot
kernels with the signature appended to them as well as kernels where the
signature is stored in the IMA extended attribute.

Changes since v7:
- Patch "MODSIGN: Export module signature definitions"
- Added module name parameter to validate_module_sig() so that it can be
shown in error messages.

- Patch "integrity: Introduce struct evm_xattr"
- Dropped use of struct evm_xattr in evm_update_evmxattr() and
evm_verify_hmac(). It's not needed there anymore because of changes
to support portable EVM signatures.

Changes since v6:

- Patch "PKCS#7: Introduce pkcs7_get_message_sig() and verify_pkcs7_message_sig()"
- Retitled to "PKCS#7: Refactor verify_pkcs7_signature() and
add pkcs7_get_message_sig()"
- Reworded description to clarify why the refactoring is needed.
The code is unchanged. (Suggested by Mimi Zohar)
- Added Mimi Zohar's Reviewed-by.

- Patch "PKCS#7: Introduce pkcs7_get_digest()"
- Added Mimi Zohar's Reviewed-by.

- Patch "integrity: Introduce integrity_keyring_from_id"
- Added Mimi Zohar's Signed-off-by.

- Patch "integrity: Introduce asymmetric_sig_has_known_key()"
- Added Mimi Zohar's Signed-off-by.

- Patch "integrity: Select CONFIG_KEYS instead of depending on it"
- Added Mimi Zohar's Signed-off-by.

- Patch "ima: Introduce is_ima_sig()"
- Renamed function to is_signed() (suggested by Mimi Zohar).

- Patch "ima: Add functions to read and verify a modsig signature"
- Changed stubs for the !CONFIG_IMA_APPRAISE_MODSIG to return -EOPNOTSUPP
instead of -ENOTSUPP, since the latter isn't defined in uapi headers.
- Moved functions to the patches which use them and dropped this patch
(suggested by Mimi Zohar).

- Patch "ima: Implement support for module-style appended signatures"
- Prevent reading and writing of IMA_MODSIG xattr in ima_read_xattr()
and ima_inode_setxattr().
- Simplify code in process_measurement() which decides whether to try
reading a modsig (suggested by Mimi Zohar).
- Moved some functions from patch "ima: Add functions to read and verify
a modsig signature" into this patch.

- Patch "ima: Add new "d-sig" template field"
- New patch containing code from patch "ima: Write modsig to the measurement list"
(Suggested by Mimi Zohar).

- Patch "ima: Write modsig to the measurement list"
- Moved some functions from patch "ima: Add functions to read and verify
a modsig signature" into this patch.
- Moved code related to d-sig support to new patch.

- Patch "ima: Store the measurement again when appraising a modsig"
- New patch.

Changes since v5:
- Patch "ima: Remove some superfluous parentheses"
- Dropped.

- Patch "evm, ima: Remove superfluous parentheses"
- Dropped.

- Patch "evm, ima: Remove more superfluous parentheses"
- Dropped.

- Patch "ima: Don't pass xattr value to EVM xattr verification."
- Dropped.

- Patch "ima: Store measurement after appraisal"
- Dropped.

- Patch "MODSIGN: Export module signature definitions"
- Reduced changes to the code that was moved into validate_module_sig()
to the minimum necessary (suggested by Mimi Zohar).
- Added SPDX license identifier.

- Patch "PKCS#7: Introduce pkcs7_get_message_sig() and verify_pkcs7_message_sig()"
- In the hypothetical case that there's more than one sinfo, changed
pkcs7_get_message_sig() to return NULL instead of the first sinfo's sig.
- Dropped Mimi's Reviewed-by because of the code change above.

- Patch "PKCS#7: Introduce pkcs7_get_digest()"
- New patch.

- Patch "integrity: Introduce integrity_keyring_from_id"
- Add stub in case CONFIG_INTEGRITY_SIGNATURE isn't set.

- Patch "integrity: Introduce asymmetric_sig_has_known_key()"
- New patch.

- Patch "ima: Introduce is_ima_sig"
- New patch, with code from "ima: Improvements in ima_appraise_measurement"

- Patch "ima: Add modsig appraise_type option for module-style appended signatures"
- Changed appraise_type to accept "imasig|modsig" instead of
"modsig|imasig" to reflect the fact that now IMA only looks for
the modsig after failing to find a suitable imasig stored in the xattr.
- Added SPDX license identifier.

- Patch "ima: Add functions to read and verify a modsig signature"
- Changed ima_read_modsig() to abort loading the modsig if it uses a key
which isn't known to IMA.
- Changed ima_get_modsig_hash() to use pkcs7_get_digest().

- Patch "ima: Implement support for module-style appended signatures"
- Added ima_xattr_sig_known_key() auxiliary function.
- Call ima_read_modsig() directly from process_measurement() instead of
from ima_appraise_measurement(), and only if there's no xattr signature
or if the xattr signature uses a key which isn't known to IMA.
- hash_algo in process_measurement() is always obtained from the xattr
signature, never from the modsig.
- Changes to ima_appraise_measurement() are a lot simpler now, and don't
involve going back to the main switch statement a second time.
- Pass xattr_value to evm_verifyxattr() unless xattr_value is a modsig.

- Patch "ima: Write modsig to the measurement list"
- Since now we determine whether we'll use an xattr sig or a modsig
at the time they are read, there's no need to store a measurement
again in the modsig case. Thus, this patch doesn't need to change
ima_store_measurement() nor process_measurement() anymore.
- Define new "d-sig" template field which holds the digest that is
expected to match the one contained in the modsig.
- Moved addition of ima_modsig_serialize_data() to patch "ima: Add
functions to read and verify a modsig signature".
- Increase MAX_TEMPLATE_NAME_LEN to 24.

Changes since v4:
- Patch "ima: Remove redundant conditional operator"
- New patch.

- Patch "ima: Remove some superfluous parentheses"
- New patch.

- Patch "evm, ima: Remove superfluous parentheses"
- New patch.

- Patch "evm, ima: Remove more superfluous parentheses"
- New patch.

- Patch "ima: Simplify ima_eventsig_init"
- New patch.

- Patch "ima: Improvements in ima_appraise_measurement"
- New patch.

- Patch "ima: Don't pass xattr value to EVM xattr verification."
- New patch.

- Patch "ima: Export func_tokens"
- Split from patch "ima: Support module-style appended signatures for
appraisal".

- Patch "ima: Add modsig appraise_type option for module-style appended
signatures"
- Split from patch "ima: Support module-style appended signatures for
appraisal".
- Mention modsig option in Documentation/ABI/testing/ima_policy
(suggested by Mimi Zohar).

- Patch "ima: Add functions to read and verify a modsig signature"
- Split from patch "ima: Support module-style appended signatures for
appraisal".

- Patch "ima: Implement support for module-style appended signatures"
- Split from patch "ima: Support module-style appended signatures for
appraisal".
- In ima_appraise_measurement, change the logic of dealing with xattr
errors in case the modsig verification fails. With this,
process_xattr_error isn't needed anymore.

- Patch "ima: Write modsig to the measurement list"
- Split from patch "ima: Support module-style appended signatures for
appraisal".
- Added ima_current_template_has_sig function.
- Removed hdr parameter from ima_modsig_serialize_data.
- In ima_store_measurement, continue processing even if the given PCR
is already measured if it's for a modsig.
- In process_measurement, add exception to store measurement even if
IMA_MEASURE is not set when appraising a modsig (suggested by
Mimi Zohar).
- Call is_ima_sig in ima_eventsig_init.

Thiago Jung Bauermann (14):
MODSIGN: Export module signature definitions
PKCS#7: Refactor verify_pkcs7_signature() and add
pkcs7_get_message_sig()
PKCS#7: Introduce pkcs7_get_digest()
integrity: Introduce struct evm_xattr
integrity: Introduce integrity_keyring_from_id()
integrity: Introduce asymmetric_sig_has_known_key()
integrity: Select CONFIG_KEYS instead of depending on it
ima: Introduce is_signed()
ima: Export func_tokens
ima: Add modsig appraise_type option for module-style appended
signatures
ima: Implement support for module-style appended signatures
ima: Add new "d-sig" template field
ima: Write modsig to the measurement list
ima: Store the measurement again when appraising a modsig

Documentation/ABI/testing/ima_policy | 6 +-
Documentation/security/IMA-templates.rst | 5 +
certs/system_keyring.c | 61 +++++--
crypto/asymmetric_keys/pkcs7_parser.c | 16 ++
crypto/asymmetric_keys/pkcs7_verify.c | 25 +++
include/crypto/pkcs7.h | 5 +
include/linux/module.h | 3 -
include/linux/module_signature.h | 47 +++++
include/linux/verification.h | 10 +
init/Kconfig | 6 +-
kernel/Makefile | 2 +-
kernel/module.c | 1 +
kernel/module_signing.c | 82 ++++-----
security/integrity/Kconfig | 2 +-
security/integrity/digsig.c | 28 ++-
security/integrity/digsig_asymmetric.c | 44 +++--
security/integrity/evm/evm_main.c | 8 +-
security/integrity/ima/Kconfig | 13 ++
security/integrity/ima/Makefile | 1 +
security/integrity/ima/ima.h | 67 +++++++
security/integrity/ima/ima_api.c | 8 +-
security/integrity/ima/ima_appraise.c | 79 ++++++--
security/integrity/ima/ima_main.c | 33 +++-
security/integrity/ima/ima_modsig.c | 212 ++++++++++++++++++++++
security/integrity/ima/ima_policy.c | 81 +++++++--
security/integrity/ima/ima_template.c | 31 +++-
security/integrity/ima/ima_template_lib.c | 49 ++++-
security/integrity/ima/ima_template_lib.h | 2 +
security/integrity/integrity.h | 30 ++-
29 files changed, 829 insertions(+), 128 deletions(-)
create mode 100644 include/linux/module_signature.h
create mode 100644 security/integrity/ima/ima_modsig.c


2018-11-17 06:23:22

by Thiago Jung Bauermann

[permalink] [raw]
Subject: [PATCH v8 14/14] ima: Store the measurement again when appraising a modsig

If the IMA template contains the 'sig' field, then the modsig should be
added to the measurement list when the file is appraised, and that is what
normally happens.

But If a measurement rule caused a file containing a modsig to be measured
before a different rule causes it to be appraised, the resulting
measurement entry will not contain the modsig because it is only fetched
during appraisal. When the appraisal rule triggers, it won't store a new
measurement containing the modsig because the file was already measured.

We need to detect that situation and store an additional measurement with
the modsig. This is done by defining the appraise subaction flag
IMA_READ_MEASURE and testing for it in process_measurement().

Suggested-by: Mimi Zohar <[email protected]>
Signed-off-by: Thiago Jung Bauermann <[email protected]>
---
security/integrity/ima/ima.h | 1 +
security/integrity/ima/ima_api.c | 8 +++-
security/integrity/ima/ima_main.c | 16 +++++++-
security/integrity/ima/ima_policy.c | 59 ++++++++++++++++++++++++---
security/integrity/ima/ima_template.c | 27 ++++++++++++
security/integrity/integrity.h | 9 ++--
6 files changed, 110 insertions(+), 10 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 8e1b1ddbe14f..c39bed55f6d2 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -147,6 +147,7 @@ int ima_init_crypto(void);
void ima_putc(struct seq_file *m, void *data, int datalen);
void ima_print_digest(struct seq_file *m, u8 *digest, u32 size);
struct ima_template_desc *ima_template_desc_current(void);
+bool ima_current_template_has_sig(void);
int ima_restore_measurement_entry(struct ima_template_entry *entry);
int ima_restore_measurement_list(loff_t bufsize, void *buf);
int ima_measurements_show(struct seq_file *m, void *v);
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 99dd1d53fc35..a7af114bc6b4 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -289,7 +289,13 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
xattr_len, NULL};
int violation = 0;

- if (iint->measured_pcrs & (0x1 << pcr))
+ /*
+ * We still need to store the measurement in the case of MODSIG because
+ * we only have its contents to put in the list at the time of
+ * appraisal. See comment in store_measurement_again() for more details.
+ */
+ if (iint->measured_pcrs & (0x1 << pcr) &&
+ (!xattr_value || xattr_value->type != IMA_MODSIG))
return;

result = ima_alloc_init_template(&event_data, &entry);
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index d5abd22d502a..807b9b77b813 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -166,6 +166,20 @@ void ima_file_free(struct file *file)
ima_check_last_writer(iint, inode, file);
}

+/*
+ * A file measurement might already exist in the measurement list. Based on
+ * policy, include an additional file measurement containing the appended
+ * signature and file hash, without the appended signature (i.e., the 'd-sig'
+ * field).
+ */
+static bool store_measurement_again(struct integrity_iint_cache *iint,
+ struct evm_ima_xattr_data *xattr_value)
+{
+ return iint->flags & IMA_READ_MEASURE && xattr_value &&
+ xattr_value->type == IMA_MODSIG &&
+ ima_current_template_has_sig();
+}
+
static int process_measurement(struct file *file, const struct cred *cred,
u32 secid, char *buf, loff_t size, int mask,
enum ima_hooks func)
@@ -299,7 +313,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */
pathname = ima_d_path(&file->f_path, &pathbuf, filename);

- if (action & IMA_MEASURE)
+ if (action & IMA_MEASURE || store_measurement_again(iint, xattr_value))
ima_store_measurement(iint, file, pathname,
xattr_value, xattr_len, pcr);
if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 3e5a64053aa8..c0b39802e988 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -10,6 +10,9 @@
* - initialize default measure policy rules
*
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/list.h>
#include <linux/fs.h>
@@ -367,7 +370,8 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
* In addition to knowing that we need to appraise the file in general,
* we need to differentiate between calling hooks, for hook specific rules.
*/
-static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
+static int get_appraise_subaction(struct ima_rule_entry *rule,
+ enum ima_hooks func)
{
if (!(rule->flags & IMA_FUNC))
return IMA_FILE_APPRAISE;
@@ -388,6 +392,15 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
}
}

+static int get_measure_subaction(struct ima_rule_entry *rule,
+ enum ima_hooks func)
+{
+ if (rule->flags & IMA_FUNC && ima_hook_supports_modsig(func))
+ return IMA_READ_MEASURE;
+ else
+ return 0;
+}
+
/**
* ima_match_policy - decision based on LSM and other conditions
* @inode: pointer to an inode for which the policy decision is being made
@@ -424,11 +437,12 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,

action |= entry->action & IMA_DO_MASK;
if (entry->action & IMA_APPRAISE) {
- action |= get_subaction(entry, func);
+ action |= get_appraise_subaction(entry, func);
action &= ~IMA_HASH;
if (ima_fail_unverifiable_sigs)
action |= IMA_FAIL_UNVERIFIABLE_SIGS;
- }
+ } else if (entry->action & IMA_MEASURE)
+ action |= get_measure_subaction(entry, func);

if (entry->action & IMA_DO_MASK)
actmask &= ~(entry->action | entry->action << 1);
@@ -756,6 +770,40 @@ static void ima_log_string(struct audit_buffer *ab, char *key, char *value)
ima_log_string_op(ab, key, value, NULL);
}

+/*
+ * To validate the appended signature included in the measurement list requires
+ * the file hash, without the appended signature (i.e., the 'd-sig' field).
+ * Therefore, notify the user if they have the 'sig' field but not the 'd-sig'
+ * field in the template.
+ */
+static void check_current_template_modsig(void)
+{
+#define MSG "template with 'sig' field also needs 'd-sig' field when modsig is allowed\n"
+ struct ima_template_desc *template;
+ bool has_sig, has_dsig;
+ static bool checked;
+ int i;
+
+ /* We only need to notify the user once. */
+ if (checked)
+ return;
+
+ has_sig = has_dsig = false;
+ template = ima_template_desc_current();
+ for (i = 0; i < template->num_fields; i++) {
+ if (!strcmp(template->fields[i]->field_id, "sig"))
+ has_sig = true;
+ else if (!strcmp(template->fields[i]->field_id, "d-sig"))
+ has_dsig = true;
+ }
+
+ if (has_sig && !has_dsig)
+ pr_notice(MSG);
+
+ checked = true;
+#undef MSG
+}
+
static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
{
struct audit_buffer *ab;
@@ -1035,10 +1083,11 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
if ((strcmp(args[0].from, "imasig")) == 0)
entry->flags |= IMA_DIGSIG_REQUIRED;
else if (ima_hook_supports_modsig(entry->func) &&
- strcmp(args[0].from, "imasig|modsig") == 0)
+ strcmp(args[0].from, "imasig|modsig") == 0) {
entry->flags |= IMA_DIGSIG_REQUIRED
| IMA_MODSIG_ALLOWED;
- else
+ check_current_template_modsig();
+ } else
result = -EINVAL;
break;
case Opt_permit_directio:
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index 045ad508cbb8..a58b55e7c1c6 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -231,6 +231,33 @@ struct ima_template_desc *ima_template_desc_current(void)
return ima_template;
}

+/*
+ * Tells whether the current template has fields which reference a file's
+ * signature.
+ */
+bool ima_current_template_has_sig(void)
+{
+ static int ima_template_has_sig = -1;
+
+ if (ima_template_has_sig < 0) {
+ struct ima_template_desc *template;
+ int i;
+
+ template = ima_template_desc_current();
+ for (i = 0; i < template->num_fields; i++)
+ if (!strcmp(template->fields[i]->field_id, "sig") ||
+ !strcmp(template->fields[i]->field_id, "d-sig")) {
+ ima_template_has_sig = 1;
+ break;
+ }
+
+ if (ima_template_has_sig < 0)
+ ima_template_has_sig = 0;
+ }
+
+ return ima_template_has_sig;
+}
+
int __init ima_init_template(void)
{
struct ima_template_desc *template = ima_template_desc_current();
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index e8c379211a96..9d7cefa5c999 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -39,12 +39,13 @@
#define IMA_MODSIG_ALLOWED 0x20000000

#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
- IMA_HASH | IMA_APPRAISE_SUBMASK)
+ IMA_HASH | IMA_APPRAISE_SUBMASK | \
+ IMA_READ_MEASURE)
#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED | \
IMA_HASHED | IMA_COLLECTED | \
- IMA_APPRAISED_SUBMASK)
+ IMA_APPRAISED_SUBMASK | IMA_READ_MEASURED)

-/* iint subaction appraise cache flags */
+/* iint subaction appraise and measure cache flags */
#define IMA_FILE_APPRAISE 0x00001000
#define IMA_FILE_APPRAISED 0x00002000
#define IMA_MMAP_APPRAISE 0x00004000
@@ -55,6 +56,8 @@
#define IMA_READ_APPRAISED 0x00080000
#define IMA_CREDS_APPRAISE 0x00100000
#define IMA_CREDS_APPRAISED 0x00200000
+#define IMA_READ_MEASURE 0x00400000
+#define IMA_READ_MEASURED 0x00800000
#define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
IMA_BPRM_APPRAISE | IMA_READ_APPRAISE | \
IMA_CREDS_APPRAISE)

2018-11-17 06:22:16

by Thiago Jung Bauermann

[permalink] [raw]
Subject: [PATCH v8 02/14] PKCS#7: Refactor verify_pkcs7_signature() and add pkcs7_get_message_sig()

IMA will need to verify a PKCS#7 which has already been parsed. For this
reason, factor out the code which does that from verify_pkcs7_signature()
into a new function which takes a struct pkcs7_message instead of a data
buffer.

In addition, IMA will need to know the key that signed a given PKCS#7
message, so add pkcs7_get_message_sig().

Signed-off-by: Thiago Jung Bauermann <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
Cc: David Howells <[email protected]>
Cc: David Woodhouse <[email protected]>
Cc: Herbert Xu <[email protected]>
Cc: "David S. Miller" <[email protected]>
---
certs/system_keyring.c | 61 ++++++++++++++++++++-------
crypto/asymmetric_keys/pkcs7_parser.c | 16 +++++++
include/crypto/pkcs7.h | 2 +
include/linux/verification.h | 10 +++++
4 files changed, 73 insertions(+), 16 deletions(-)

diff --git a/certs/system_keyring.c b/certs/system_keyring.c
index 81728717523d..dd8c5ef941ce 100644
--- a/certs/system_keyring.c
+++ b/certs/system_keyring.c
@@ -191,33 +191,27 @@ late_initcall(load_system_certificate_list);
#ifdef CONFIG_SYSTEM_DATA_VERIFICATION

/**
- * verify_pkcs7_signature - Verify a PKCS#7-based signature on system data.
+ * verify_pkcs7_message_sig - Verify a PKCS#7-based signature on system data.
* @data: The data to be verified (NULL if expecting internal data).
* @len: Size of @data.
- * @raw_pkcs7: The PKCS#7 message that is the signature.
- * @pkcs7_len: The size of @raw_pkcs7.
+ * @pkcs7: The PKCS#7 message that is the signature.
* @trusted_keys: Trusted keys to use (NULL for builtin trusted keys only,
* (void *)1UL for all trusted keys).
* @usage: The use to which the key is being put.
* @view_content: Callback to gain access to content.
* @ctx: Context for callback.
*/
-int verify_pkcs7_signature(const void *data, size_t len,
- const void *raw_pkcs7, size_t pkcs7_len,
- struct key *trusted_keys,
- enum key_being_used_for usage,
- int (*view_content)(void *ctx,
- const void *data, size_t len,
- size_t asn1hdrlen),
- void *ctx)
+int verify_pkcs7_message_sig(const void *data, size_t len,
+ struct pkcs7_message *pkcs7,
+ struct key *trusted_keys,
+ enum key_being_used_for usage,
+ int (*view_content)(void *ctx,
+ const void *data, size_t len,
+ size_t asn1hdrlen),
+ void *ctx)
{
- struct pkcs7_message *pkcs7;
int ret;

- pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len);
- if (IS_ERR(pkcs7))
- return PTR_ERR(pkcs7);
-
/* The data should be detached - so we need to supply it. */
if (data && pkcs7_supply_detached_data(pkcs7, data, len) < 0) {
pr_err("PKCS#7 signature with non-detached data\n");
@@ -259,6 +253,41 @@ int verify_pkcs7_signature(const void *data, size_t len,
}

error:
+ pr_devel("<==%s() = %d\n", __func__, ret);
+ return ret;
+}
+
+/**
+ * verify_pkcs7_signature - Verify a PKCS#7-based signature on system data.
+ * @data: The data to be verified (NULL if expecting internal data).
+ * @len: Size of @data.
+ * @raw_pkcs7: The PKCS#7 message that is the signature.
+ * @pkcs7_len: The size of @raw_pkcs7.
+ * @trusted_keys: Trusted keys to use (NULL for builtin trusted keys only,
+ * (void *)1UL for all trusted keys).
+ * @usage: The use to which the key is being put.
+ * @view_content: Callback to gain access to content.
+ * @ctx: Context for callback.
+ */
+int verify_pkcs7_signature(const void *data, size_t len,
+ const void *raw_pkcs7, size_t pkcs7_len,
+ struct key *trusted_keys,
+ enum key_being_used_for usage,
+ int (*view_content)(void *ctx,
+ const void *data, size_t len,
+ size_t asn1hdrlen),
+ void *ctx)
+{
+ struct pkcs7_message *pkcs7;
+ int ret;
+
+ pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len);
+ if (IS_ERR(pkcs7))
+ return PTR_ERR(pkcs7);
+
+ ret = verify_pkcs7_message_sig(data, len, pkcs7, trusted_keys, usage,
+ view_content, ctx);
+
pkcs7_free_message(pkcs7);
pr_devel("<==%s() = %d\n", __func__, ret);
return ret;
diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c
index f0d56e1a8b7e..8df9693f659f 100644
--- a/crypto/asymmetric_keys/pkcs7_parser.c
+++ b/crypto/asymmetric_keys/pkcs7_parser.c
@@ -684,3 +684,19 @@ int pkcs7_note_signed_info(void *context, size_t hdrlen,
return -ENOMEM;
return 0;
}
+
+/**
+ * pkcs7_get_message_sig - get signature in @pkcs7
+ */
+const struct public_key_signature *pkcs7_get_message_sig(
+ const struct pkcs7_message *pkcs7)
+{
+ /*
+ * This function doesn't support messages with more than one signature,
+ * so don't return anything in that case.
+ */
+ if (pkcs7->signed_infos == NULL || pkcs7->signed_infos->next != NULL)
+ return NULL;
+
+ return pkcs7->signed_infos->sig;
+}
diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h
index 583f199400a3..6f51d0cb6d12 100644
--- a/include/crypto/pkcs7.h
+++ b/include/crypto/pkcs7.h
@@ -28,6 +28,8 @@ extern void pkcs7_free_message(struct pkcs7_message *pkcs7);
extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
const void **_data, size_t *_datalen,
size_t *_headerlen);
+extern const struct public_key_signature *pkcs7_get_message_sig(
+ const struct pkcs7_message *pkcs7);

/*
* pkcs7_trust.c
diff --git a/include/linux/verification.h b/include/linux/verification.h
index cfa4730d607a..7b09a55674a7 100644
--- a/include/linux/verification.h
+++ b/include/linux/verification.h
@@ -35,6 +35,7 @@ extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR];
#ifdef CONFIG_SYSTEM_DATA_VERIFICATION

struct key;
+struct pkcs7_message;

extern int verify_pkcs7_signature(const void *data, size_t len,
const void *raw_pkcs7, size_t pkcs7_len,
@@ -44,6 +45,15 @@ extern int verify_pkcs7_signature(const void *data, size_t len,
const void *data, size_t len,
size_t asn1hdrlen),
void *ctx);
+extern int verify_pkcs7_message_sig(const void *data, size_t len,
+ struct pkcs7_message *pkcs7,
+ struct key *trusted_keys,
+ enum key_being_used_for usage,
+ int (*view_content)(void *ctx,
+ const void *data,
+ size_t len,
+ size_t asn1hdrlen),
+ void *ctx);

#ifdef CONFIG_SIGNED_PE_FILE_VERIFICATION
extern int verify_pefile_signature(const void *pebuf, unsigned pelen,

2018-11-17 06:22:50

by Thiago Jung Bauermann

[permalink] [raw]
Subject: [PATCH v8 08/14] ima: Introduce is_signed()

With the introduction of another IMA signature type (modsig), some places
will need to check for both of them. It is cleaner to do that if there's a
helper function to tell whether an xattr_value represents an IMA
signature.

Suggested-by: Mimi Zohar <[email protected]>
Signed-off-by: Thiago Jung Bauermann <[email protected]>
---
security/integrity/ima/ima.h | 5 +++++
security/integrity/ima/ima_appraise.c | 7 +++----
security/integrity/ima/ima_template_lib.c | 2 +-
3 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index cc12f3449a72..e4f72b30cb28 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -154,6 +154,11 @@ unsigned long ima_get_binary_runtime_size(void);
int ima_init_template(void);
void ima_init_template_list(void);

+static inline bool is_signed(const struct evm_ima_xattr_data *xattr_value)
+{
+ return xattr_value && xattr_value->type == EVM_IMA_XATTR_DIGSIG;
+}
+
/*
* used to protect h_table and sha_table
*/
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 8bcef90939f8..c6459408e6b2 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -325,15 +325,14 @@ int ima_appraise_measurement(enum ima_hooks func,
} else if (status != INTEGRITY_PASS) {
/* Fix mode, but don't replace file signatures. */
if ((ima_appraise & IMA_APPRAISE_FIX) &&
- (!xattr_value ||
- xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
+ !is_signed(xattr_value)) {
if (!ima_fix_xattr(dentry, iint))
status = INTEGRITY_PASS;
}

/* Permit new files with file signatures, but without data. */
if (inode->i_size == 0 && iint->flags & IMA_NEW_FILE &&
- xattr_value && xattr_value->type == EVM_IMA_XATTR_DIGSIG) {
+ is_signed(xattr_value)) {
status = INTEGRITY_PASS;
}

@@ -448,7 +447,7 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST))
return -EINVAL;
ima_reset_appraise_flags(d_backing_inode(dentry),
- xvalue->type == EVM_IMA_XATTR_DIGSIG);
+ is_signed(xvalue));
result = 0;
}
return result;
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index 43752002c222..300912914b17 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -382,7 +382,7 @@ int ima_eventsig_init(struct ima_event_data *event_data,
{
struct evm_ima_xattr_data *xattr_value = event_data->xattr_value;

- if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG))
+ if (!is_signed(xattr_value))
return 0;

return ima_write_template_field_data(xattr_value, event_data->xattr_len,

2018-11-17 06:22:48

by Thiago Jung Bauermann

[permalink] [raw]
Subject: [PATCH v8 07/14] integrity: Select CONFIG_KEYS instead of depending on it

This avoids a dependency cycle in soon-to-be-introduced
CONFIG_IMA_APPRAISE_MODSIG: it will select CONFIG_MODULE_SIG_FORMAT
which in turn selects CONFIG_KEYS. Kconfig then complains that
CONFIG_INTEGRITY_SIGNATURE depends on CONFIG_KEYS.

Signed-off-by: Thiago Jung Bauermann <[email protected]>
Signed-off-by: Mimi Zohar <[email protected]>
---
security/integrity/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig
index da9565891738..0d642e0317c7 100644
--- a/security/integrity/Kconfig
+++ b/security/integrity/Kconfig
@@ -17,8 +17,8 @@ if INTEGRITY

config INTEGRITY_SIGNATURE
bool "Digital signature verification using multiple keyrings"
- depends on KEYS
default n
+ select KEYS
select SIGNATURE
help
This option enables digital signature verification support

2018-11-17 06:22:27

by Thiago Jung Bauermann

[permalink] [raw]
Subject: [PATCH v8 04/14] integrity: Introduce struct evm_xattr

Even though struct evm_ima_xattr_data includes a fixed-size array to hold a
SHA1 digest, most of the code ignores the array and uses the struct to mean
"type indicator followed by data of unspecified size" and tracks the real
size of what the struct represents in a separate length variable.

The only exception to that is the EVM code, which correctly uses the
definition of struct evm_ima_xattr_data.

So make this explicit in the code by removing the length specification from
the array in struct evm_ima_xattr_data. Also, change the name of the
element from digest to data since in most places the array doesn't hold a
digest.

A separate struct evm_xattr is introduced, with the original definition of
evm_ima_xattr_data to be used in the places that actually expect that
definition.

Signed-off-by: Thiago Jung Bauermann <[email protected]>
---
security/integrity/evm/evm_main.c | 8 ++++----
security/integrity/ima/ima_appraise.c | 7 ++++---
security/integrity/integrity.h | 5 +++++
3 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 7f3f54d89a6e..a1b42d10efc7 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -169,7 +169,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
/* check value type */
switch (xattr_data->type) {
case EVM_XATTR_HMAC:
- if (xattr_len != sizeof(struct evm_ima_xattr_data)) {
+ if (xattr_len != sizeof(struct evm_xattr)) {
evm_status = INTEGRITY_FAIL;
goto out;
}
@@ -179,7 +179,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
xattr_value_len, &digest);
if (rc)
break;
- rc = crypto_memneq(xattr_data->digest, digest.digest,
+ rc = crypto_memneq(xattr_data->data, digest.digest,
SHA1_DIGEST_SIZE);
if (rc)
rc = -EINVAL;
@@ -523,7 +523,7 @@ int evm_inode_init_security(struct inode *inode,
const struct xattr *lsm_xattr,
struct xattr *evm_xattr)
{
- struct evm_ima_xattr_data *xattr_data;
+ struct evm_xattr *xattr_data;
int rc;

if (!evm_key_loaded() || !evm_protected_xattr(lsm_xattr->name))
@@ -533,7 +533,7 @@ int evm_inode_init_security(struct inode *inode,
if (!xattr_data)
return -ENOMEM;

- xattr_data->type = EVM_XATTR_HMAC;
+ xattr_data->data.type = EVM_XATTR_HMAC;
rc = evm_init_hmac(inode, lsm_xattr, xattr_data->digest);
if (rc < 0)
goto out;
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index deec1804a00a..8bcef90939f8 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -167,7 +167,8 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
return sig->hash_algo;
break;
case IMA_XATTR_DIGEST_NG:
- ret = xattr_value->digest[0];
+ /* first byte contains algorithm id */
+ ret = xattr_value->data[0];
if (ret < HASH_ALGO__LAST)
return ret;
break;
@@ -175,7 +176,7 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
/* this is for backward compatibility */
if (xattr_len == 21) {
unsigned int zero = 0;
- if (!memcmp(&xattr_value->digest[16], &zero, 4))
+ if (!memcmp(&xattr_value->data[16], &zero, 4))
return HASH_ALGO_MD5;
else
return HASH_ALGO_SHA1;
@@ -274,7 +275,7 @@ int ima_appraise_measurement(enum ima_hooks func,
/* xattr length may be longer. md5 hash in previous
version occupied 20 bytes in xattr, instead of 16
*/
- rc = memcmp(&xattr_value->digest[hash_start],
+ rc = memcmp(&xattr_value->data[hash_start],
iint->ima_hash->digest,
iint->ima_hash->length);
else
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index e60473b13a8d..20ac02bf1b84 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -79,6 +79,11 @@ enum evm_ima_xattr_type {

struct evm_ima_xattr_data {
u8 type;
+ u8 data[];
+} __packed;
+
+struct evm_xattr {
+ struct evm_ima_xattr_data data;
u8 digest[SHA1_DIGEST_SIZE];
} __packed;


2018-11-17 06:24:16

by Thiago Jung Bauermann

[permalink] [raw]
Subject: [PATCH v8 13/14] ima: Write modsig to the measurement list

Add modsig support to the "sig" template field, allowing the the contents
of the modsig to be included in the measurement list.

Suggested-by: Mimi Zohar <[email protected]>
Signed-off-by: Thiago Jung Bauermann <[email protected]>
---
security/integrity/ima/ima.h | 7 +++++++
security/integrity/ima/ima_modsig.c | 13 +++++++++++++
security/integrity/ima/ima_template_lib.c | 15 ++++++++++++++-
3 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 7f88e4b86156..8e1b1ddbe14f 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -315,6 +315,7 @@ int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
int *xattr_len);
int ima_get_modsig_hash(struct evm_ima_xattr_data *hdr, enum hash_algo *algo,
const u8 **hash, u8 *len);
+int ima_modsig_serialize_data(struct evm_ima_xattr_data **data, int *data_len);
int ima_modsig_verify(const unsigned int keyring_id,
struct evm_ima_xattr_data *hdr);
void ima_free_xattr_data(struct evm_ima_xattr_data *hdr);
@@ -339,6 +340,12 @@ static inline int ima_get_modsig_hash(struct evm_ima_xattr_data *hdr,
return -EOPNOTSUPP;
}

+static inline int ima_modsig_serialize_data(struct evm_ima_xattr_data **data,
+ int *data_len)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int ima_modsig_verify(const unsigned int keyring_id,
struct evm_ima_xattr_data *hdr)
{
diff --git a/security/integrity/ima/ima_modsig.c b/security/integrity/ima/ima_modsig.c
index 584d9d77b2c4..e31ab7dc11db 100644
--- a/security/integrity/ima/ima_modsig.c
+++ b/security/integrity/ima/ima_modsig.c
@@ -167,6 +167,19 @@ int ima_get_modsig_hash(struct evm_ima_xattr_data *hdr, enum hash_algo *algo,
return pkcs7_get_digest(modsig->pkcs7_msg, hash, len);
}

+int ima_modsig_serialize_data(struct evm_ima_xattr_data **data, int *data_len)
+{
+ struct modsig_hdr *modsig = (struct modsig_hdr *) *data;
+
+ if (!*data || (*data)->type != IMA_MODSIG)
+ return -EINVAL;
+
+ *data = &modsig->raw_pkcs7;
+ *data_len = modsig->raw_pkcs7_len;
+
+ return 0;
+}
+
int ima_modsig_verify(const unsigned int keyring_id,
struct evm_ima_xattr_data *hdr)
{
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index 36d175816894..417cd153ba60 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -411,10 +411,23 @@ int ima_eventsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
struct evm_ima_xattr_data *xattr_value = event_data->xattr_value;
+ int xattr_len = event_data->xattr_len;

if (!is_signed(xattr_value))
return 0;

- return ima_write_template_field_data(xattr_value, event_data->xattr_len,
+ /*
+ * The xattr_value for IMA_MODSIG is a runtime structure containing
+ * pointers. Get its raw data instead.
+ */
+ if (xattr_value->type == IMA_MODSIG) {
+ int rc;
+
+ rc = ima_modsig_serialize_data(&xattr_value, &xattr_len);
+ if (rc)
+ return rc;
+ }
+
+ return ima_write_template_field_data(xattr_value, xattr_len,
DATA_FMT_HEX, field_data);
}

2018-11-17 06:23:13

by Thiago Jung Bauermann

[permalink] [raw]
Subject: [PATCH v8 11/14] ima: Implement support for module-style appended signatures

Implement the appraise_type=imasig|modsig option, allowing IMA to read and
verify modsig signatures.

In case a file has both an xattr signature and an appended modsig, IMA will
only use the appended signature if the key used by the xattr signature
isn't present in the IMA keyring.

Signed-off-by: Thiago Jung Bauermann <[email protected]>
---
security/integrity/ima/Kconfig | 3 +
security/integrity/ima/ima.h | 36 ++++++-
security/integrity/ima/ima_appraise.c | 65 ++++++++++--
security/integrity/ima/ima_main.c | 17 ++-
security/integrity/ima/ima_modsig.c | 145 ++++++++++++++++++++++++++
security/integrity/integrity.h | 1 +
6 files changed, 256 insertions(+), 11 deletions(-)

diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index bba19f9ea184..0fb542455698 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -234,6 +234,9 @@ config IMA_APPRAISE_BOOTPARAM
config IMA_APPRAISE_MODSIG
bool "Support module-style signatures for appraisal"
depends on IMA_APPRAISE
+ depends on INTEGRITY_ASYMMETRIC_KEYS
+ select PKCS7_MESSAGE_PARSER
+ select MODULE_SIG_FORMAT
default n
help
Adds support for signatures appended to files. The format of the
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 69c06e2d7bd6..312d60cee702 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -156,7 +156,8 @@ void ima_init_template_list(void);

static inline bool is_signed(const struct evm_ima_xattr_data *xattr_value)
{
- return xattr_value && xattr_value->type == EVM_IMA_XATTR_DIGSIG;
+ return xattr_value && (xattr_value->type == EVM_IMA_XATTR_DIGSIG ||
+ xattr_value->type == IMA_MODSIG);
}

/*
@@ -253,6 +254,8 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
enum ima_hooks func);
enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
int xattr_len);
+bool ima_xattr_sig_known_key(const struct evm_ima_xattr_data *xattr_value,
+ int xattr_len);
int ima_read_xattr(struct dentry *dentry,
struct evm_ima_xattr_data **xattr_value);

@@ -291,6 +294,12 @@ ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len)
return ima_hash_algo;
}

+static inline bool ima_xattr_sig_known_key(const struct evm_ima_xattr_data
+ *xattr_value, int xattr_len)
+{
+ return false;
+}
+
static inline int ima_read_xattr(struct dentry *dentry,
struct evm_ima_xattr_data **xattr_value)
{
@@ -301,11 +310,36 @@ static inline int ima_read_xattr(struct dentry *dentry,

#ifdef CONFIG_IMA_APPRAISE_MODSIG
bool ima_hook_supports_modsig(enum ima_hooks func);
+int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
+ struct evm_ima_xattr_data **xattr_value,
+ int *xattr_len);
+int ima_modsig_verify(const unsigned int keyring_id,
+ struct evm_ima_xattr_data *hdr);
+void ima_free_xattr_data(struct evm_ima_xattr_data *hdr);
#else
static inline bool ima_hook_supports_modsig(enum ima_hooks func)
{
return false;
}
+
+static inline int ima_read_modsig(enum ima_hooks func, const void *buf,
+ loff_t buf_len,
+ struct evm_ima_xattr_data **xattr_value,
+ int *xattr_len)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int ima_modsig_verify(const unsigned int keyring_id,
+ struct evm_ima_xattr_data *hdr)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void ima_free_xattr_data(struct evm_ima_xattr_data *hdr)
+{
+ kfree(hdr);
+}
#endif /* CONFIG_IMA_APPRAISE_MODSIG */

/* LSM based policy rules require audit */
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index c6459408e6b2..27a1dbb52544 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -189,6 +189,22 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
return ima_hash_algo;
}

+bool ima_xattr_sig_known_key(const struct evm_ima_xattr_data *xattr_value,
+ int xattr_len)
+{
+ struct key *keyring;
+
+ if (xattr_value->type != EVM_IMA_XATTR_DIGSIG)
+ return false;
+
+ keyring = integrity_keyring_from_id(INTEGRITY_KEYRING_IMA);
+ if (IS_ERR(keyring))
+ return false;
+
+ return asymmetric_sig_has_known_key(keyring, (const char *) xattr_value,
+ xattr_len);
+}
+
int ima_read_xattr(struct dentry *dentry,
struct evm_ima_xattr_data **xattr_value)
{
@@ -198,6 +214,14 @@ int ima_read_xattr(struct dentry *dentry,
0, GFP_NOFS);
if (ret == -EOPNOTSUPP)
ret = 0;
+ /* IMA_MODSIG is only allowed when appended to files. */
+ else if (ret > 0 && (*xattr_value)->type == IMA_MODSIG) {
+ ret = -EINVAL;
+
+ kfree(*xattr_value);
+ *xattr_value = NULL;
+ }
+
return ret;
}

@@ -221,8 +245,12 @@ int ima_appraise_measurement(enum ima_hooks func,
struct inode *inode = d_backing_inode(dentry);
enum integrity_status status = INTEGRITY_UNKNOWN;
int rc = xattr_len, hash_start = 0;
+ size_t xattr_contents_len;
+ void *xattr_contents;

- if (!(inode->i_opflags & IOP_XATTR))
+ /* If not appraising a modsig, we need an xattr. */
+ if ((xattr_value == NULL || xattr_value->type != IMA_MODSIG) &&
+ !(inode->i_opflags & IOP_XATTR))
return INTEGRITY_UNKNOWN;

if (rc <= 0) {
@@ -241,13 +269,30 @@ int ima_appraise_measurement(enum ima_hooks func,
goto out;
}

- status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint);
+ /*
+ * If it's a modsig, we don't have the xattr contents to pass to
+ * evm_verifyxattr().
+ */
+ if (xattr_value->type == IMA_MODSIG) {
+ xattr_contents = NULL;
+ xattr_contents_len = 0;
+ } else {
+ xattr_contents = xattr_value;
+ xattr_contents_len = xattr_len;
+ }
+
+ status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_contents,
+ xattr_contents_len, iint);
switch (status) {
case INTEGRITY_PASS:
case INTEGRITY_PASS_IMMUTABLE:
case INTEGRITY_UNKNOWN:
break;
case INTEGRITY_NOXATTRS: /* No EVM protected xattrs. */
+ /* It's fine not to have xattrs when using a modsig. */
+ if (xattr_value->type == IMA_MODSIG)
+ break;
+ /* fall through */
case INTEGRITY_NOLABEL: /* No security.evm xattr. */
cause = "missing-HMAC";
goto out;
@@ -288,11 +333,16 @@ int ima_appraise_measurement(enum ima_hooks func,
status = INTEGRITY_PASS;
break;
case EVM_IMA_XATTR_DIGSIG:
+ case IMA_MODSIG:
set_bit(IMA_DIGSIG, &iint->atomic_flags);
- rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
- (const char *)xattr_value, rc,
- iint->ima_hash->digest,
- iint->ima_hash->length);
+ if (xattr_value->type == EVM_IMA_XATTR_DIGSIG)
+ rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
+ (const char *)xattr_value,
+ rc, iint->ima_hash->digest,
+ iint->ima_hash->length);
+ else
+ rc = ima_modsig_verify(INTEGRITY_KEYRING_IMA,
+ xattr_value);
if (rc == -EOPNOTSUPP) {
status = INTEGRITY_UNKNOWN;
} else if (rc) {
@@ -444,7 +494,8 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
result = ima_protect_xattr(dentry, xattr_name, xattr_value,
xattr_value_len);
if (result == 1) {
- if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST))
+ if (!xattr_value_len || xvalue->type == IMA_MODSIG ||
+ xvalue->type >= IMA_XATTR_LAST)
return -EINVAL;
ima_reset_appraise_flags(d_backing_inode(dentry),
is_signed(xvalue));
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 41e4771980d5..d5abd22d502a 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -181,6 +181,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
struct evm_ima_xattr_data *xattr_value = NULL;
int xattr_len = 0;
bool violation_check;
+ bool read_sig;
enum hash_algo hash_algo;

if (!ima_policy_flag || !S_ISREG(inode->i_mode))
@@ -274,13 +275,23 @@ static int process_measurement(struct file *file, const struct cred *cred,
}

template_desc = ima_template_desc_current();
- if ((action & IMA_APPRAISE_SUBMASK) ||
- strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0)
+ read_sig = action & IMA_APPRAISE_SUBMASK ||
+ strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0;
+ if (read_sig)
/* read 'security.ima' */
xattr_len = ima_read_xattr(file_dentry(file), &xattr_value);

hash_algo = ima_get_hash_algo(xattr_value, xattr_len);

+ /*
+ * Try to find a modsig if there's no xattr sig or if it is signed by an
+ * unknown key.
+ */
+ if (read_sig && iint->flags & IMA_MODSIG_ALLOWED &&
+ (xattr_len <= 0 || !ima_xattr_sig_known_key(xattr_value,
+ xattr_len)))
+ ima_read_modsig(func, buf, size, &xattr_value, &xattr_len);
+
rc = ima_collect_measurement(iint, file, buf, size, hash_algo);
if (rc != 0 && rc != -EBADF && rc != -EINVAL)
goto out_locked;
@@ -307,7 +318,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
!(iint->flags & IMA_NEW_FILE))
rc = -EACCES;
mutex_unlock(&iint->mutex);
- kfree(xattr_value);
+ ima_free_xattr_data(xattr_value);
out:
if (pathbuf)
__putname(pathbuf);
diff --git a/security/integrity/ima/ima_modsig.c b/security/integrity/ima/ima_modsig.c
index 84d428cbbca8..e095d35d804d 100644
--- a/security/integrity/ima/ima_modsig.c
+++ b/security/integrity/ima/ima_modsig.c
@@ -8,8 +8,25 @@
* Thiago Jung Bauermann <[email protected]>
*/

+#include <linux/types.h>
+#include <linux/module_signature.h>
+#include <keys/asymmetric-type.h>
+#include <crypto/pkcs7.h>
+
#include "ima.h"

+struct modsig_hdr {
+ uint8_t type; /* Should be IMA_MODSIG. */
+ struct pkcs7_message *pkcs7_msg;
+ int raw_pkcs7_len;
+
+ /*
+ * This is what will go to the measurement list if the template requires
+ * storing the signature.
+ */
+ struct evm_ima_xattr_data raw_pkcs7;
+};
+
/**
* ima_hook_supports_modsig - can the policy allow modsig for this hook?
*
@@ -29,3 +46,131 @@ bool ima_hook_supports_modsig(enum ima_hooks func)
return false;
}
}
+
+static bool modsig_has_known_key(struct modsig_hdr *hdr)
+{
+ const struct public_key_signature *pks;
+ struct key *keyring;
+ struct key *key;
+
+ keyring = integrity_keyring_from_id(INTEGRITY_KEYRING_IMA);
+ if (IS_ERR(keyring))
+ return false;
+
+ pks = pkcs7_get_message_sig(hdr->pkcs7_msg);
+ if (!pks)
+ return false;
+
+ key = find_asymmetric_key(keyring, pks->auth_ids[0], NULL, false);
+ if (IS_ERR(key))
+ return false;
+
+ key_put(key);
+
+ return true;
+}
+
+int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
+ struct evm_ima_xattr_data **xattr_value,
+ int *xattr_len)
+{
+ const size_t marker_len = sizeof(MODULE_SIG_STRING) - 1;
+ const struct module_signature *sig;
+ struct modsig_hdr *hdr;
+ size_t sig_len;
+ const void *p;
+ int rc;
+
+ /*
+ * Not supposed to happen. Hooks that support modsig are whitelisted
+ * when parsing the policy using ima_hooks_supports_modsig().
+ */
+ if (!buf || !buf_len) {
+ WARN_ONCE(true, "%s doesn't support modsig\n",
+ func_tokens[func]);
+ return -ENOENT;
+ } else if (buf_len <= marker_len + sizeof(*sig))
+ return -ENOENT;
+
+ p = buf + buf_len - marker_len;
+ if (memcmp(p, MODULE_SIG_STRING, marker_len))
+ return -ENOENT;
+
+ buf_len -= marker_len;
+ sig = (const struct module_signature *) (p - sizeof(*sig));
+
+ rc = validate_module_sig(sig, buf_len, func_tokens[func]);
+ if (rc)
+ return rc;
+
+ sig_len = be32_to_cpu(sig->sig_len);
+ buf_len -= sig_len + sizeof(*sig);
+
+ /* Allocate sig_len additional bytes to hold the raw PKCS#7 data. */
+ hdr = kmalloc(sizeof(*hdr) + sig_len, GFP_KERNEL);
+ if (!hdr)
+ return -ENOMEM;
+
+ hdr->pkcs7_msg = pkcs7_parse_message(buf + buf_len, sig_len);
+ if (IS_ERR(hdr->pkcs7_msg)) {
+ rc = PTR_ERR(hdr->pkcs7_msg);
+ goto err_no_msg;
+ }
+
+ rc = pkcs7_supply_detached_data(hdr->pkcs7_msg, buf, buf_len);
+ if (rc)
+ goto err;
+
+ if (!modsig_has_known_key(hdr)) {
+ rc = -ENOKEY;
+ goto err;
+ }
+
+ memcpy(hdr->raw_pkcs7.data, buf + buf_len, sig_len);
+ hdr->raw_pkcs7_len = sig_len + 1;
+ hdr->raw_pkcs7.type = IMA_MODSIG;
+
+ hdr->type = IMA_MODSIG;
+
+ *xattr_value = (typeof(*xattr_value)) hdr;
+ *xattr_len = sizeof(*hdr);
+
+ return 0;
+
+ err:
+ pkcs7_free_message(hdr->pkcs7_msg);
+ err_no_msg:
+ kfree(hdr);
+ return rc;
+}
+
+int ima_modsig_verify(const unsigned int keyring_id,
+ struct evm_ima_xattr_data *hdr)
+{
+ struct modsig_hdr *modsig = (struct modsig_hdr *) hdr;
+ struct key *keyring;
+
+ if (!hdr || hdr->type != IMA_MODSIG)
+ return -EINVAL;
+
+ keyring = integrity_keyring_from_id(keyring_id);
+ if (IS_ERR(keyring))
+ return PTR_ERR(keyring);
+
+ return verify_pkcs7_message_sig(NULL, 0, modsig->pkcs7_msg, keyring,
+ VERIFYING_MODULE_SIGNATURE, NULL, NULL);
+}
+
+void ima_free_xattr_data(struct evm_ima_xattr_data *hdr)
+{
+ if (!hdr)
+ return;
+
+ if (hdr->type == IMA_MODSIG) {
+ struct modsig_hdr *modsig = (struct modsig_hdr *) hdr;
+
+ pkcs7_free_message(modsig->pkcs7_msg);
+ }
+
+ kfree(hdr);
+}
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index a55dcd235dc0..e8c379211a96 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -75,6 +75,7 @@ enum evm_ima_xattr_type {
EVM_IMA_XATTR_DIGSIG,
IMA_XATTR_DIGEST_NG,
EVM_XATTR_PORTABLE_DIGSIG,
+ IMA_MODSIG,
IMA_XATTR_LAST
};


2018-11-17 06:22:31

by Thiago Jung Bauermann

[permalink] [raw]
Subject: [PATCH v8 05/14] integrity: Introduce integrity_keyring_from_id()

IMA will need to obtain the keyring used to verify file signatures so that
it can verify the module-style signature appended to files.

Signed-off-by: Thiago Jung Bauermann <[email protected]>
Signed-off-by: Mimi Zohar <[email protected]>
---
security/integrity/digsig.c | 28 +++++++++++++++++++++-------
security/integrity/integrity.h | 6 ++++++
2 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index 5eacba858e4b..bfa27f166184 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -43,11 +43,10 @@ static const char * const keyring_name[INTEGRITY_KEYRING_MAX] = {
#define restrict_link_to_ima restrict_link_by_builtin_trusted
#endif

-int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
- const char *digest, int digestlen)
+struct key *integrity_keyring_from_id(const unsigned int id)
{
- if (id >= INTEGRITY_KEYRING_MAX || siglen < 2)
- return -EINVAL;
+ if (id >= INTEGRITY_KEYRING_MAX)
+ return ERR_PTR(-EINVAL);

if (!keyring[id]) {
keyring[id] =
@@ -56,17 +55,32 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
int err = PTR_ERR(keyring[id]);
pr_err("no %s keyring: %d\n", keyring_name[id], err);
keyring[id] = NULL;
- return err;
+ return ERR_PTR(err);
}
}

+ return keyring[id];
+}
+
+int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
+ const char *digest, int digestlen)
+{
+ struct key *keyring;
+
+ if (siglen < 2)
+ return -EINVAL;
+
+ keyring = integrity_keyring_from_id(id);
+ if (IS_ERR(keyring))
+ return PTR_ERR(keyring);
+
switch (sig[1]) {
case 1:
/* v1 API expect signature without xattr type */
- return digsig_verify(keyring[id], sig + 1, siglen - 1,
+ return digsig_verify(keyring, sig + 1, siglen - 1,
digest, digestlen);
case 2:
- return asymmetric_verify(keyring[id], sig, siglen,
+ return asymmetric_verify(keyring, sig, siglen,
digest, digestlen);
}

diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 20ac02bf1b84..91c41351e18a 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -153,6 +153,7 @@ extern struct dentry *integrity_dir;

#ifdef CONFIG_INTEGRITY_SIGNATURE

+struct key *integrity_keyring_from_id(const unsigned int id);
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
const char *digest, int digestlen);

@@ -160,6 +161,11 @@ int __init integrity_init_keyring(const unsigned int id);
int __init integrity_load_x509(const unsigned int id, const char *path);
#else

+static inline struct key *integrity_keyring_from_id(const unsigned int id)
+{
+ return ERR_PTR(-EINVAL);
+}
+
static inline int integrity_digsig_verify(const unsigned int id,
const char *sig, int siglen,
const char *digest, int digestlen)

2018-11-17 06:23:11

by Thiago Jung Bauermann

[permalink] [raw]
Subject: [PATCH v8 12/14] ima: Add new "d-sig" template field

Define new "d-sig" template field which holds the digest that is expected
to match the one contained in the modsig.

Suggested-by: Mimi Zohar <[email protected]>
Signed-off-by: Thiago Jung Bauermann <[email protected]>
---
Documentation/security/IMA-templates.rst | 5 ++++
security/integrity/ima/ima.h | 9 +++++++
security/integrity/ima/ima_modsig.c | 23 ++++++++++++++++
security/integrity/ima/ima_template.c | 4 ++-
security/integrity/ima/ima_template_lib.c | 32 ++++++++++++++++++++++-
security/integrity/ima/ima_template_lib.h | 2 ++
6 files changed, 73 insertions(+), 2 deletions(-)

diff --git a/Documentation/security/IMA-templates.rst b/Documentation/security/IMA-templates.rst
index 2cd0e273cc9a..f2a0f4225857 100644
--- a/Documentation/security/IMA-templates.rst
+++ b/Documentation/security/IMA-templates.rst
@@ -68,6 +68,11 @@ descriptors by adding their identifier to the format string
- 'd-ng': the digest of the event, calculated with an arbitrary hash
algorithm (field format: [<hash algo>:]digest, where the digest
prefix is shown only if the hash algorithm is not SHA1 or MD5);
+ - 'd-sig': the digest of the event for files that have an appended modsig. This
+ field is calculated without including the modsig and thus will differ from
+ the total digest of the file, but it is what should match the digest
+ contained in the modsig (if it doesn't, the signature is invalid). It is
+ shown in the same format as 'd-ng';
- 'n-ng': the name of the event, without size limitations;
- 'sig': the file signature.

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 312d60cee702..7f88e4b86156 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -313,6 +313,8 @@ bool ima_hook_supports_modsig(enum ima_hooks func);
int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
struct evm_ima_xattr_data **xattr_value,
int *xattr_len);
+int ima_get_modsig_hash(struct evm_ima_xattr_data *hdr, enum hash_algo *algo,
+ const u8 **hash, u8 *len);
int ima_modsig_verify(const unsigned int keyring_id,
struct evm_ima_xattr_data *hdr);
void ima_free_xattr_data(struct evm_ima_xattr_data *hdr);
@@ -330,6 +332,13 @@ static inline int ima_read_modsig(enum ima_hooks func, const void *buf,
return -EOPNOTSUPP;
}

+static inline int ima_get_modsig_hash(struct evm_ima_xattr_data *hdr,
+ enum hash_algo *algo, const u8 **hash,
+ u8 *len)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int ima_modsig_verify(const unsigned int keyring_id,
struct evm_ima_xattr_data *hdr)
{
diff --git a/security/integrity/ima/ima_modsig.c b/security/integrity/ima/ima_modsig.c
index e095d35d804d..584d9d77b2c4 100644
--- a/security/integrity/ima/ima_modsig.c
+++ b/security/integrity/ima/ima_modsig.c
@@ -144,6 +144,29 @@ int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
return rc;
}

+int ima_get_modsig_hash(struct evm_ima_xattr_data *hdr, enum hash_algo *algo,
+ const u8 **hash, u8 *len)
+{
+ struct modsig_hdr *modsig = (typeof(modsig)) hdr;
+ const struct public_key_signature *pks;
+ int i;
+
+ if (!hdr || hdr->type != IMA_MODSIG)
+ return -EINVAL;
+
+ pks = pkcs7_get_message_sig(modsig->pkcs7_msg);
+ if (!pks)
+ return -EBADMSG;
+
+ for (i = 0; i < HASH_ALGO__LAST; i++)
+ if (!strcmp(hash_algo_name[i], pks->hash_algo))
+ break;
+
+ *algo = i;
+
+ return pkcs7_get_digest(modsig->pkcs7_msg, hash, len);
+}
+
int ima_modsig_verify(const unsigned int keyring_id,
struct evm_ima_xattr_data *hdr)
{
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index b631b8bc7624..045ad508cbb8 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -43,8 +43,10 @@ static const struct ima_template_field supported_fields[] = {
.field_show = ima_show_template_string},
{.field_id = "sig", .field_init = ima_eventsig_init,
.field_show = ima_show_template_sig},
+ {.field_id = "d-sig", .field_init = ima_eventdigest_sig_init,
+ .field_show = ima_show_template_digest_ng},
};
-#define MAX_TEMPLATE_NAME_LEN 15
+#define MAX_TEMPLATE_NAME_LEN 24

static struct ima_template_desc *ima_template;
static struct ima_template_desc *lookup_template_desc(const char *name);
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index 300912914b17..36d175816894 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -222,7 +222,8 @@ int ima_parse_buf(void *bufstartp, void *bufendp, void **bufcurp,
return 0;
}

-static int ima_eventdigest_init_common(u8 *digest, u32 digestsize, u8 hash_algo,
+static int ima_eventdigest_init_common(const u8 *digest, u32 digestsize,
+ u8 hash_algo,
struct ima_field_data *field_data)
{
/*
@@ -325,6 +326,35 @@ int ima_eventdigest_ng_init(struct ima_event_data *event_data,
hash_algo, field_data);
}

+/*
+ * This function writes the digest of the file which is expected to match the
+ * digest contained in the file's embedded signature.
+ */
+int ima_eventdigest_sig_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data)
+{
+ struct evm_ima_xattr_data *xattr_value = event_data->xattr_value;
+ enum hash_algo hash_algo = HASH_ALGO_SHA1;
+ const u8 *cur_digest = NULL;
+ u8 cur_digestsize = 0;
+ int ret;
+
+ if (!xattr_value || xattr_value->type != IMA_MODSIG)
+ return 0;
+
+ if (event_data->violation) /* recording a violation. */
+ goto out;
+
+ ret = ima_get_modsig_hash(xattr_value, &hash_algo, &cur_digest,
+ &cur_digestsize);
+ if (ret)
+ return ret;
+
+ out:
+ return ima_eventdigest_init_common(cur_digest, cur_digestsize,
+ hash_algo, field_data);
+}
+
static int ima_eventname_init_common(struct ima_event_data *event_data,
struct ima_field_data *field_data,
bool size_limit)
diff --git a/security/integrity/ima/ima_template_lib.h b/security/integrity/ima/ima_template_lib.h
index 6a3d8b831deb..3cd353e83f73 100644
--- a/security/integrity/ima/ima_template_lib.h
+++ b/security/integrity/ima/ima_template_lib.h
@@ -38,6 +38,8 @@ int ima_eventname_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventdigest_ng_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
+int ima_eventdigest_sig_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
int ima_eventname_ng_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventsig_init(struct ima_event_data *event_data,

2018-11-17 06:22:21

by Thiago Jung Bauermann

[permalink] [raw]
Subject: [PATCH v8 03/14] PKCS#7: Introduce pkcs7_get_digest()

IMA will need to access the digest of the PKCS7 message (as calculated by
the kernel) before the signature is verified, so introduce
pkcs7_get_digest() for that purpose.

Also, modify pkcs7_digest() to detect when the digest was already
calculated so that it doesn't have to do redundant work. Verifying that
sinfo->sig->digest isn't NULL is sufficient because both places which
allocate sinfo->sig (pkcs7_parse_message() and pkcs7_note_signed_info())
use kzalloc() so sig->digest is always initialized to zero.

Signed-off-by: Thiago Jung Bauermann <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
Cc: David Howells <[email protected]>
Cc: Herbert Xu <[email protected]>
Cc: "David S. Miller" <[email protected]>
---
crypto/asymmetric_keys/pkcs7_verify.c | 25 +++++++++++++++++++++++++
include/crypto/pkcs7.h | 3 +++
2 files changed, 28 insertions(+)

diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c
index 97c77f66b20d..e61f121bfc87 100644
--- a/crypto/asymmetric_keys/pkcs7_verify.c
+++ b/crypto/asymmetric_keys/pkcs7_verify.c
@@ -33,6 +33,10 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,

kenter(",%u,%s", sinfo->index, sinfo->sig->hash_algo);

+ /* The digest was calculated already. */
+ if (sig->digest)
+ return 0;
+
if (!sinfo->sig->hash_algo)
return -ENOPKG;

@@ -122,6 +126,27 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
return ret;
}

+int pkcs7_get_digest(struct pkcs7_message *pkcs7, const u8 **buf, u8 *len)
+{
+ struct pkcs7_signed_info *sinfo = pkcs7->signed_infos;
+ int ret;
+
+ /*
+ * This function doesn't support messages with more than one signature.
+ */
+ if (sinfo == NULL || sinfo->next != NULL)
+ return -EBADMSG;
+
+ ret = pkcs7_digest(pkcs7, sinfo);
+ if (ret)
+ return ret;
+
+ *buf = sinfo->sig->digest;
+ *len = sinfo->sig->digest_size;
+
+ return 0;
+}
+
/*
* Find the key (X.509 certificate) to use to verify a PKCS#7 message. PKCS#7
* uses the issuer's name and the issuing certificate serial number for
diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h
index 6f51d0cb6d12..cfaea9c37f4a 100644
--- a/include/crypto/pkcs7.h
+++ b/include/crypto/pkcs7.h
@@ -46,4 +46,7 @@ extern int pkcs7_verify(struct pkcs7_message *pkcs7,
extern int pkcs7_supply_detached_data(struct pkcs7_message *pkcs7,
const void *data, size_t datalen);

+extern int pkcs7_get_digest(struct pkcs7_message *pkcs7, const u8 **buf,
+ u8 *len);
+
#endif /* _CRYPTO_PKCS7_H */

2018-11-17 06:22:38

by Thiago Jung Bauermann

[permalink] [raw]
Subject: [PATCH v8 06/14] integrity: Introduce asymmetric_sig_has_known_key()

IMA will only look for a modsig if the xattr sig references a key which is
not in the expected kernel keyring. To that end, introduce
asymmetric_sig_has_known_key().

The logic of extracting the key used in the xattr sig is factored out from
asymmetric_verify() so that it can be used by the new function.

Signed-off-by: Thiago Jung Bauermann <[email protected]>
Signed-off-by: Mimi Zohar <[email protected]>
---
security/integrity/digsig_asymmetric.c | 44 +++++++++++++++++++-------
security/integrity/integrity.h | 8 +++++
2 files changed, 41 insertions(+), 11 deletions(-)

diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index d775e03fbbcc..4c3c49f919f5 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -79,26 +79,48 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid)
return key;
}

-int asymmetric_verify(struct key *keyring, const char *sig,
- int siglen, const char *data, int datalen)
+static struct key *asymmetric_key_from_sig(struct key *keyring, const char *sig,
+ int siglen)
{
- struct public_key_signature pks;
- struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
- struct key *key;
- int ret = -ENOMEM;
+ const struct signature_v2_hdr *hdr = (struct signature_v2_hdr *) sig;

if (siglen <= sizeof(*hdr))
- return -EBADMSG;
+ return ERR_PTR(-EBADMSG);

siglen -= sizeof(*hdr);

if (siglen != be16_to_cpu(hdr->sig_size))
- return -EBADMSG;
+ return ERR_PTR(-EBADMSG);

if (hdr->hash_algo >= HASH_ALGO__LAST)
- return -ENOPKG;
+ return ERR_PTR(-ENOPKG);
+
+ return request_asymmetric_key(keyring, be32_to_cpu(hdr->keyid));
+}
+
+bool asymmetric_sig_has_known_key(struct key *keyring, const char *sig,
+ int siglen)
+{
+ struct key *key;
+
+ key = asymmetric_key_from_sig(keyring, sig, siglen);
+ if (IS_ERR_OR_NULL(key))
+ return false;
+
+ key_put(key);
+
+ return true;
+}
+
+int asymmetric_verify(struct key *keyring, const char *sig,
+ int siglen, const char *data, int datalen)
+{
+ struct public_key_signature pks;
+ struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
+ struct key *key;
+ int ret = -ENOMEM;

- key = request_asymmetric_key(keyring, be32_to_cpu(hdr->keyid));
+ key = asymmetric_key_from_sig(keyring, sig, siglen);
if (IS_ERR(key))
return PTR_ERR(key);

@@ -110,7 +132,7 @@ int asymmetric_verify(struct key *keyring, const char *sig,
pks.digest = (u8 *)data;
pks.digest_size = datalen;
pks.s = hdr->sig;
- pks.s_size = siglen;
+ pks.s_size = siglen - sizeof(*hdr);
ret = verify_signature(key, &pks);
key_put(key);
pr_debug("%s() = %d\n", __func__, ret);
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 91c41351e18a..ae3c79c63674 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -182,12 +182,20 @@ static inline int integrity_init_keyring(const unsigned int id)
#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
int asymmetric_verify(struct key *keyring, const char *sig,
int siglen, const char *data, int datalen);
+bool asymmetric_sig_has_known_key(struct key *keyring, const char *sig,
+ int siglen);
#else
static inline int asymmetric_verify(struct key *keyring, const char *sig,
int siglen, const char *data, int datalen)
{
return -EOPNOTSUPP;
}
+
+static inline bool asymmetric_sig_has_known_key(struct key *keyring,
+ const char *sig, int siglen)
+{
+ return false;
+}
#endif

#ifdef CONFIG_IMA_LOAD_X509

2018-11-17 06:22:55

by Thiago Jung Bauermann

[permalink] [raw]
Subject: [PATCH v8 09/14] ima: Export func_tokens

ima_read_modsig() will need it so that it can show an error message.

Signed-off-by: Thiago Jung Bauermann <[email protected]>
---
security/integrity/ima/ima.h | 2 ++
security/integrity/ima/ima_policy.c | 12 ++++++------
2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index e4f72b30cb28..f0bc2a182cbf 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -195,6 +195,8 @@ enum ima_hooks {
__ima_hooks(__ima_hook_enumify)
};

+extern const char *const func_tokens[];
+
/* LIM API function definitions */
int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
int mask, enum ima_hooks func, int *pcr);
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index b20770704b6c..fd31e42c2bad 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -1136,6 +1136,12 @@ void ima_delete_rules(void)
}
}

+#define __ima_hook_stringify(str) (#str),
+
+const char *const func_tokens[] = {
+ __ima_hooks(__ima_hook_stringify)
+};
+
#ifdef CONFIG_IMA_READ_POLICY
enum {
mask_exec = 0, mask_write, mask_read, mask_append
@@ -1148,12 +1154,6 @@ static const char *const mask_tokens[] = {
"MAY_APPEND"
};

-#define __ima_hook_stringify(str) (#str),
-
-static const char *const func_tokens[] = {
- __ima_hooks(__ima_hook_stringify)
-};
-
void *ima_policy_start(struct seq_file *m, loff_t *pos)
{
loff_t l = *pos;

2018-11-30 08:06:19

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH v8 04/14] integrity: Introduce struct evm_xattr

On Fri, 2018-11-16 at 18:07 -0200, Thiago Jung Bauermann wrote:
> Even though struct evm_ima_xattr_data includes a fixed-size array to hold a
> SHA1 digest, most of the code ignores the array and uses the struct to mean
> "type indicator followed by data of unspecified size" and tracks the real
> size of what the struct represents in a separate length variable.
>
> The only exception to that is the EVM code, which correctly uses the
> definition of struct evm_ima_xattr_data.
>
> So make this explicit in the code by removing the length specification from
> the array in struct evm_ima_xattr_data. Also, change the name of the
> element from digest to data since in most places the array doesn't hold a
> digest.
>
> A separate struct evm_xattr is introduced, with the original definition of
> evm_ima_xattr_data to be used in the places that actually expect that
> definition.

, specifically the EVM HMAC code.

>
> Signed-off-by: Thiago Jung Bauermann <[email protected]>

Other than commenting the evm_xattr usage is limited to HMAC before
the structure definition, this looks good.

Reviewed-by: Mimi Zohar <[email protected]>

> ---
> security/integrity/evm/evm_main.c | 8 ++++----
> security/integrity/ima/ima_appraise.c | 7 ++++---
> security/integrity/integrity.h | 5 +++++
> 3 files changed, 13 insertions(+), 7 deletions(-)
>
> diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
> index 7f3f54d89a6e..a1b42d10efc7 100644
> --- a/security/integrity/evm/evm_main.c
> +++ b/security/integrity/evm/evm_main.c
> @@ -169,7 +169,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
> /* check value type */
> switch (xattr_data->type) {
> case EVM_XATTR_HMAC:
> - if (xattr_len != sizeof(struct evm_ima_xattr_data)) {
> + if (xattr_len != sizeof(struct evm_xattr)) {
> evm_status = INTEGRITY_FAIL;
> goto out;
> }
> @@ -179,7 +179,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
> xattr_value_len, &digest);
> if (rc)
> break;
> - rc = crypto_memneq(xattr_data->digest, digest.digest,
> + rc = crypto_memneq(xattr_data->data, digest.digest,
> SHA1_DIGEST_SIZE);
> if (rc)
> rc = -EINVAL;
> @@ -523,7 +523,7 @@ int evm_inode_init_security(struct inode *inode,
> const struct xattr *lsm_xattr,
> struct xattr *evm_xattr)
> {
> - struct evm_ima_xattr_data *xattr_data;
> + struct evm_xattr *xattr_data;
> int rc;
>
> if (!evm_key_loaded() || !evm_protected_xattr(lsm_xattr->name))
> @@ -533,7 +533,7 @@ int evm_inode_init_security(struct inode *inode,
> if (!xattr_data)
> return -ENOMEM;
>
> - xattr_data->type = EVM_XATTR_HMAC;
> + xattr_data->data.type = EVM_XATTR_HMAC;
> rc = evm_init_hmac(inode, lsm_xattr, xattr_data->digest);
> if (rc < 0)
> goto out;
> diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
> index deec1804a00a..8bcef90939f8 100644
> --- a/security/integrity/ima/ima_appraise.c
> +++ b/security/integrity/ima/ima_appraise.c
> @@ -167,7 +167,8 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
> return sig->hash_algo;
> break;
> case IMA_XATTR_DIGEST_NG:
> - ret = xattr_value->digest[0];
> + /* first byte contains algorithm id */
> + ret = xattr_value->data[0];
> if (ret < HASH_ALGO__LAST)
> return ret;
> break;
> @@ -175,7 +176,7 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
> /* this is for backward compatibility */
> if (xattr_len == 21) {
> unsigned int zero = 0;
> - if (!memcmp(&xattr_value->digest[16], &zero, 4))
> + if (!memcmp(&xattr_value->data[16], &zero, 4))
> return HASH_ALGO_MD5;
> else
> return HASH_ALGO_SHA1;
> @@ -274,7 +275,7 @@ int ima_appraise_measurement(enum ima_hooks func,
> /* xattr length may be longer. md5 hash in previous
> version occupied 20 bytes in xattr, instead of 16
> */
> - rc = memcmp(&xattr_value->digest[hash_start],
> + rc = memcmp(&xattr_value->data[hash_start],
> iint->ima_hash->digest,
> iint->ima_hash->length);
> else
> diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
> index e60473b13a8d..20ac02bf1b84 100644
> --- a/security/integrity/integrity.h
> +++ b/security/integrity/integrity.h
> @@ -79,6 +79,11 @@ enum evm_ima_xattr_type {
>
> struct evm_ima_xattr_data {
> u8 type;
> + u8 data[];
> +} __packed;
> +

Please add a comment here saying that evm_xattr is limited to HMAC.

> +struct evm_xattr {
> + struct evm_ima_xattr_data data;
> u8 digest[SHA1_DIGEST_SIZE];
> } __packed;
>

2018-11-17 06:22:09

by Thiago Jung Bauermann

[permalink] [raw]
Subject: [PATCH v8 01/14] MODSIGN: Export module signature definitions

IMA will use the module_signature format for append signatures, so export
the relevant definitions and factor out the code which verifies that the
appended signature trailer is valid.

Also, create a CONFIG_MODULE_SIG_FORMAT option so that IMA can select it
and be able to use validate_module_sig() without having to depend on
CONFIG_MODULE_SIG.

Signed-off-by: Thiago Jung Bauermann <[email protected]>
Reviewed-by: Mimi Zohar <[email protected]>
Cc: Jessica Yu <[email protected]>
---
include/linux/module.h | 3 --
include/linux/module_signature.h | 47 ++++++++++++++++++
init/Kconfig | 6 ++-
kernel/Makefile | 2 +-
kernel/module.c | 1 +
kernel/module_signing.c | 82 ++++++++++++++------------------
6 files changed, 91 insertions(+), 50 deletions(-)

diff --git a/include/linux/module.h b/include/linux/module.h
index fce6b4335e36..e49bbc5c66ef 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -25,9 +25,6 @@
#include <linux/percpu.h>
#include <asm/module.h>

-/* In stripped ARM and x86-64 modules, ~ is surprisingly rare. */
-#define MODULE_SIG_STRING "~Module signature appended~\n"
-
/* Not Yet Implemented */
#define MODULE_SUPPORTED_DEVICE(name)

diff --git a/include/linux/module_signature.h b/include/linux/module_signature.h
new file mode 100644
index 000000000000..862ddc25dbd1
--- /dev/null
+++ b/include/linux/module_signature.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Module signature handling.
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ */
+
+#ifndef _LINUX_MODULE_SIGNATURE_H
+#define _LINUX_MODULE_SIGNATURE_H
+
+/* In stripped ARM and x86-64 modules, ~ is surprisingly rare. */
+#define MODULE_SIG_STRING "~Module signature appended~\n"
+
+enum pkey_id_type {
+ PKEY_ID_PGP, /* OpenPGP generated key ID */
+ PKEY_ID_X509, /* X.509 arbitrary subjectKeyIdentifier */
+ PKEY_ID_PKCS7, /* Signature in PKCS#7 message */
+};
+
+/*
+ * Module signature information block.
+ *
+ * The constituents of the signature section are, in order:
+ *
+ * - Signer's name
+ * - Key identifier
+ * - Signature data
+ * - Information block
+ */
+struct module_signature {
+ u8 algo; /* Public-key crypto algorithm [0] */
+ u8 hash; /* Digest algorithm [0] */
+ u8 id_type; /* Key identifier type [PKEY_ID_PKCS7] */
+ u8 signer_len; /* Length of signer's name [0] */
+ u8 key_id_len; /* Length of key identifier [0] */
+ u8 __pad[3];
+ __be32 sig_len; /* Length of signature data */
+};
+
+struct load_info;
+
+int validate_module_sig(const struct module_signature *ms, size_t file_len,
+ const char *name);
+int mod_verify_sig(const void *mod, struct load_info *info);
+
+#endif /* _LINUX_MODULE_SIGNATURE_H */
diff --git a/init/Kconfig b/init/Kconfig
index a4112e95724a..cd31593525ee 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1864,7 +1864,7 @@ config MODULE_SRCVERSION_ALL
config MODULE_SIG
bool "Module signature verification"
depends on MODULES
- select SYSTEM_DATA_VERIFICATION
+ select MODULE_SIG_FORMAT
help
Check modules for valid signatures upon load: the signature
is simply appended to the module. For more information see
@@ -1879,6 +1879,10 @@ config MODULE_SIG
debuginfo strip done by some packagers (such as rpmbuild) and
inclusion into an initramfs that wants the module size reduced.

+config MODULE_SIG_FORMAT
+ def_bool n
+ select SYSTEM_DATA_VERIFICATION
+
config MODULE_SIG_FORCE
bool "Require modules to be validly signed"
depends on MODULE_SIG
diff --git a/kernel/Makefile b/kernel/Makefile
index 7343b3a9bff0..e56842571348 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -59,7 +59,7 @@ obj-y += up.o
endif
obj-$(CONFIG_UID16) += uid16.o
obj-$(CONFIG_MODULES) += module.o
-obj-$(CONFIG_MODULE_SIG) += module_signing.o
+obj-$(CONFIG_MODULE_SIG_FORMAT) += module_signing.o
obj-$(CONFIG_KALLSYMS) += kallsyms.o
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
obj-$(CONFIG_CRASH_CORE) += crash_core.o
diff --git a/kernel/module.c b/kernel/module.c
index 49a405891587..205c9eefd08d 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -19,6 +19,7 @@
#include <linux/export.h>
#include <linux/extable.h>
#include <linux/moduleloader.h>
+#include <linux/module_signature.h>
#include <linux/trace_events.h>
#include <linux/init.h>
#include <linux/kallsyms.h>
diff --git a/kernel/module_signing.c b/kernel/module_signing.c
index f2075ce8e4b3..2912b7b1e814 100644
--- a/kernel/module_signing.c
+++ b/kernel/module_signing.c
@@ -11,36 +11,44 @@

#include <linux/kernel.h>
#include <linux/errno.h>
+#include <linux/module_signature.h>
#include <linux/string.h>
#include <linux/verification.h>
#include <crypto/public_key.h>
#include "module-internal.h"

-enum pkey_id_type {
- PKEY_ID_PGP, /* OpenPGP generated key ID */
- PKEY_ID_X509, /* X.509 arbitrary subjectKeyIdentifier */
- PKEY_ID_PKCS7, /* Signature in PKCS#7 message */
-};
-
-/*
- * Module signature information block.
+/**
+ * validate_module_sig - validate that the given signature is sane
*
- * The constituents of the signature section are, in order:
- *
- * - Signer's name
- * - Key identifier
- * - Signature data
- * - Information block
+ * @ms: Signature to validate.
+ * @file_len: Size of the file to which @ms is appended.
*/
-struct module_signature {
- u8 algo; /* Public-key crypto algorithm [0] */
- u8 hash; /* Digest algorithm [0] */
- u8 id_type; /* Key identifier type [PKEY_ID_PKCS7] */
- u8 signer_len; /* Length of signer's name [0] */
- u8 key_id_len; /* Length of key identifier [0] */
- u8 __pad[3];
- __be32 sig_len; /* Length of signature data */
-};
+int validate_module_sig(const struct module_signature *ms, size_t file_len,
+ const char *name)
+{
+ if (be32_to_cpu(ms->sig_len) >= file_len - sizeof(*ms))
+ return -EBADMSG;
+
+ if (ms->id_type != PKEY_ID_PKCS7) {
+ pr_err("%s: Module is not signed with expected PKCS#7 message\n",
+ name);
+ return -ENOPKG;
+ }
+
+ if (ms->algo != 0 ||
+ ms->hash != 0 ||
+ ms->signer_len != 0 ||
+ ms->key_id_len != 0 ||
+ ms->__pad[0] != 0 ||
+ ms->__pad[1] != 0 ||
+ ms->__pad[2] != 0) {
+ pr_err("%s: PKCS#7 signature info has unexpected non-zero params\n",
+ name);
+ return -EBADMSG;
+ }
+
+ return 0;
+}

/*
* Verify the signature on a module.
@@ -49,6 +57,7 @@ int mod_verify_sig(const void *mod, struct load_info *info)
{
struct module_signature ms;
size_t sig_len, modlen = info->len;
+ int ret;

pr_devel("==>%s(,%zu)\n", __func__, modlen);

@@ -56,32 +65,15 @@ int mod_verify_sig(const void *mod, struct load_info *info)
return -EBADMSG;

memcpy(&ms, mod + (modlen - sizeof(ms)), sizeof(ms));
- modlen -= sizeof(ms);
+
+ ret = validate_module_sig(&ms, modlen, info->name);
+ if (ret)
+ return ret;

sig_len = be32_to_cpu(ms.sig_len);
- if (sig_len >= modlen)
- return -EBADMSG;
- modlen -= sig_len;
+ modlen -= sig_len + sizeof(ms);
info->len = modlen;

- if (ms.id_type != PKEY_ID_PKCS7) {
- pr_err("%s: Module is not signed with expected PKCS#7 message\n",
- info->name);
- return -ENOPKG;
- }
-
- if (ms.algo != 0 ||
- ms.hash != 0 ||
- ms.signer_len != 0 ||
- ms.key_id_len != 0 ||
- ms.__pad[0] != 0 ||
- ms.__pad[1] != 0 ||
- ms.__pad[2] != 0) {
- pr_err("%s: PKCS#7 signature info has unexpected non-zero params\n",
- info->name);
- return -EBADMSG;
- }
-
return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
NULL, VERIFYING_MODULE_SIGNATURE,
NULL, NULL);

2018-11-17 06:23:01

by Thiago Jung Bauermann

[permalink] [raw]
Subject: [PATCH v8 10/14] ima: Add modsig appraise_type option for module-style appended signatures

Introduce the modsig keyword to the IMA policy syntax to specify that
a given hook should expect the file to have the IMA signature appended
to it. Here is how it can be used in a rule:

appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig|modsig

With this rule, IMA will accept either a signature stored in the extended
attribute or an appended signature.

For now, the rule above will behave exactly the same as if
appraise_type=imasig was specified. The actual modsig implementation
will be introduced separately.

Suggested-by: Mimi Zohar <[email protected]>
Signed-off-by: Thiago Jung Bauermann <[email protected]>
---
Documentation/ABI/testing/ima_policy | 6 +++++-
security/integrity/ima/Kconfig | 10 +++++++++
security/integrity/ima/Makefile | 1 +
security/integrity/ima/ima.h | 9 ++++++++
security/integrity/ima/ima_modsig.c | 31 ++++++++++++++++++++++++++++
security/integrity/ima/ima_policy.c | 12 +++++++++--
security/integrity/integrity.h | 1 +
7 files changed, 67 insertions(+), 3 deletions(-)

diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy
index 74c6702de74e..9d1dfd0a8891 100644
--- a/Documentation/ABI/testing/ima_policy
+++ b/Documentation/ABI/testing/ima_policy
@@ -37,7 +37,7 @@ Description:
euid:= decimal value
fowner:= decimal value
lsm: are LSM specific
- option: appraise_type:= [imasig]
+ option: appraise_type:= [imasig] [imasig|modsig]
pcr:= decimal value

default policy:
@@ -103,3 +103,7 @@ Description:

measure func=KEXEC_KERNEL_CHECK pcr=4
measure func=KEXEC_INITRAMFS_CHECK pcr=5
+
+ Example of appraise rule allowing modsig appended signatures:
+
+ appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig|modsig
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index a18f8c6d13b5..bba19f9ea184 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -231,6 +231,16 @@ config IMA_APPRAISE_BOOTPARAM
This option enables the different "ima_appraise=" modes
(eg. fix, log) from the boot command line.

+config IMA_APPRAISE_MODSIG
+ bool "Support module-style signatures for appraisal"
+ depends on IMA_APPRAISE
+ default n
+ help
+ Adds support for signatures appended to files. The format of the
+ appended signature is the same used for signed kernel modules.
+ The modsig keyword can be used in the IMA policy to allow a hook
+ to accept such signatures.
+
config IMA_TRUSTED_KEYRING
bool "Require all keys on the .ima keyring be signed (deprecated)"
depends on IMA_APPRAISE && SYSTEM_TRUSTED_KEYRING
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index d921dc4f9eb0..31d57cdf2421 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -9,5 +9,6 @@ obj-$(CONFIG_IMA) += ima.o
ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
ima_policy.o ima_template.o ima_template_lib.o
ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
+ima-$(CONFIG_IMA_APPRAISE_MODSIG) += ima_modsig.o
ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o
obj-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index f0bc2a182cbf..69c06e2d7bd6 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -299,6 +299,15 @@ static inline int ima_read_xattr(struct dentry *dentry,

#endif /* CONFIG_IMA_APPRAISE */

+#ifdef CONFIG_IMA_APPRAISE_MODSIG
+bool ima_hook_supports_modsig(enum ima_hooks func);
+#else
+static inline bool ima_hook_supports_modsig(enum ima_hooks func)
+{
+ return false;
+}
+#endif /* CONFIG_IMA_APPRAISE_MODSIG */
+
/* LSM based policy rules require audit */
#ifdef CONFIG_IMA_LSM_RULES

diff --git a/security/integrity/ima/ima_modsig.c b/security/integrity/ima/ima_modsig.c
new file mode 100644
index 000000000000..84d428cbbca8
--- /dev/null
+++ b/security/integrity/ima/ima_modsig.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * IMA support for appraising module-style appended signatures.
+ *
+ * Copyright (C) 2018 IBM Corporation
+ *
+ * Author:
+ * Thiago Jung Bauermann <[email protected]>
+ */
+
+#include "ima.h"
+
+/**
+ * ima_hook_supports_modsig - can the policy allow modsig for this hook?
+ *
+ * modsig is only supported by hooks using ima_post_read_file, because only they
+ * preload the contents of the file in a buffer. FILE_CHECK does that in some
+ * cases, but not when reached from vfs_open. POLICY_CHECK can support it, but
+ * it's not useful in practice because it's a text file so deny.
+ */
+bool ima_hook_supports_modsig(enum ima_hooks func)
+{
+ switch (func) {
+ case FIRMWARE_CHECK:
+ case KEXEC_KERNEL_CHECK:
+ case KEXEC_INITRAMFS_CHECK:
+ return true;
+ default:
+ return false;
+ }
+}
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index fd31e42c2bad..3e5a64053aa8 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -1034,6 +1034,10 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
ima_log_string(ab, "appraise_type", args[0].from);
if ((strcmp(args[0].from, "imasig")) == 0)
entry->flags |= IMA_DIGSIG_REQUIRED;
+ else if (ima_hook_supports_modsig(entry->func) &&
+ strcmp(args[0].from, "imasig|modsig") == 0)
+ entry->flags |= IMA_DIGSIG_REQUIRED
+ | IMA_MODSIG_ALLOWED;
else
result = -EINVAL;
break;
@@ -1326,8 +1330,12 @@ int ima_policy_show(struct seq_file *m, void *v)
}
}
}
- if (entry->flags & IMA_DIGSIG_REQUIRED)
- seq_puts(m, "appraise_type=imasig ");
+ if (entry->flags & IMA_DIGSIG_REQUIRED) {
+ if (entry->flags & IMA_MODSIG_ALLOWED)
+ seq_puts(m, "appraise_type=imasig|modsig ");
+ else
+ seq_puts(m, "appraise_type=imasig ");
+ }
if (entry->flags & IMA_PERMIT_DIRECTIO)
seq_puts(m, "permit_directio ");
rcu_read_unlock();
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index ae3c79c63674..a55dcd235dc0 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -36,6 +36,7 @@
#define IMA_NEW_FILE 0x04000000
#define EVM_IMMUTABLE_DIGSIG 0x08000000
#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
+#define IMA_MODSIG_ALLOWED 0x20000000

#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
IMA_HASH | IMA_APPRAISE_SUBMASK)

2018-12-04 22:00:37

by James Morris

[permalink] [raw]
Subject: Re: [PATCH v8 00/14] Appended signatures support for IMA appraisal

On Fri, 16 Nov 2018, Thiago Jung Bauermann wrote:

> On the OpenPOWER platform, secure boot and trusted boot are being
> implemented using IMA for taking measurements and verifying signatures.
> Since the kernel image on Power servers is an ELF binary, kernels are
> signed using the scripts/sign-file tool and thus use the same signature
> format as signed kernel modules.
>
> This patch series adds support in IMA for verifying those signatures.

Are you saying you use IMA to verify kernels during boot? From a Linux
bootloader?

> It adds flexibility to OpenPOWER secure boot, because it allows it to boot
> kernels with the signature appended to them as well as kernels where the
> signature is stored in the IMA extended attribute.

Just to clarify, with these patches, IMA will be able to verify the
native form of signed kernel modules? i.e. without xattrs at all, and
this will work with existing signed modules?



--
James Morris
<[email protected]>

2018-12-04 23:35:24

by Thiago Jung Bauermann

[permalink] [raw]
Subject: Re: [PATCH v8 00/14] Appended signatures support for IMA appraisal


Hello James,

Thanks for you interest in these patches.

James Morris <[email protected]> writes:

> On Fri, 16 Nov 2018, Thiago Jung Bauermann wrote:
>
>> On the OpenPOWER platform, secure boot and trusted boot are being
>> implemented using IMA for taking measurements and verifying signatures.
>> Since the kernel image on Power servers is an ELF binary, kernels are
>> signed using the scripts/sign-file tool and thus use the same signature
>> format as signed kernel modules.
>>
>> This patch series adds support in IMA for verifying those signatures.
>
> Are you saying you use IMA to verify kernels during boot? From a Linux
> bootloader?

Yes to both. OpenPOWER machines have embedded in their firmware a Linux
kernel and initramfs to use as bootloader, using Petitboot. kexec is
used to load the OS and boot it.

>> It adds flexibility to OpenPOWER secure boot, because it allows it to boot
>> kernels with the signature appended to them as well as kernels where the
>> signature is stored in the IMA extended attribute.
>
> Just to clarify, with these patches, IMA will be able to verify the
> native form of signed kernel modules?

That wasn't my use case to develop the patches, but I just tested and it
works.

I just had to make a slight modification: there's a whitelist of IMA
hooks that are allowed to use the module signature format (in the
ima_hook_supports_modsig function), and I had to add MODULE_CHECK to it.
The next version of the patches will have this change.

The only difference is that IMA looks for a valid key in the IMA
keyring, while the CONFIG_MODULE_SIG code looks for the module signing
key in the builtin and secondary trusted keyrings.

> i.e. without xattrs at all, and
> this will work with existing signed modules?

No xattrs at all, and yes.

--
Thiago Jung Bauermann
IBM Linux Technology Center