summaryrefslogtreecommitdiff
path: root/security/integrity
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-07-05 11:26:35 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2017-07-05 11:26:35 -0700
commite24dd9ee5399747b71c1d982a484fc7601795f31 (patch)
tree14fcec8728916092a9f6dbeb0f2b8d5c5a4e5c9a /security/integrity
parent7391786a64dcfe9c609a1f8e2204c1abf42ded23 (diff)
parentc4758fa59285fe4dbfeab4364a6957936d040fbf (diff)
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security layer updates from James Morris: - a major update for AppArmor. From JJ: * several bug fixes and cleanups * the patch to add symlink support to securityfs that was floated on the list earlier and the apparmorfs changes that make use of securityfs symlinks * it introduces the domain labeling base code that Ubuntu has been carrying for several years, with several cleanups applied. And it converts the current mediation over to using the domain labeling base, which brings domain stacking support with it. This finally will bring the base upstream code in line with Ubuntu and provide a base to upstream the new feature work that Ubuntu carries. * This does _not_ contain any of the newer apparmor mediation features/controls (mount, signals, network, keys, ...) that Ubuntu is currently carrying, all of which will be RFC'd on top of this. - Notable also is the Infiniband work in SELinux, and the new file:map permission. From Paul: "While we're down to 21 patches for v4.13 (it was 31 for v4.12), the diffstat jumps up tremendously with over 2k of line changes. Almost all of these changes are the SELinux/IB work done by Daniel Jurgens; some other noteworthy changes include a NFS v4.2 labeling fix, a new file:map permission, and reporting of policy capabilities on policy load" There's also now genfscon labeling support for tracefs, which was lost in v4.1 with the separation from debugfs. - Smack incorporates a safer socket check in file_receive, and adds a cap_capable call in privilege check. - TPM as usual has a bunch of fixes and enhancements. - Multiple calls to security_add_hooks() can now be made for the same LSM, to allow LSMs to have hook declarations across multiple files. - IMA now supports different "ima_appraise=" modes (eg. log, fix) from the boot command line. * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (126 commits) apparmor: put back designators in struct initialisers seccomp: Switch from atomic_t to recount_t seccomp: Adjust selftests to avoid double-join seccomp: Clean up core dump logic IMA: update IMA policy documentation to include pcr= option ima: Log the same audit cause whenever a file has no signature ima: Simplify policy_func_show. integrity: Small code improvements ima: fix get_binary_runtime_size() ima: use ima_parse_buf() to parse template data ima: use ima_parse_buf() to parse measurements headers ima: introduce ima_parse_buf() ima: Add cgroups2 to the defaults list ima: use memdup_user_nul ima: fix up #endif comments IMA: Correct Kconfig dependencies for hash selection ima: define is_ima_appraise_enabled() ima: define Kconfig IMA_APPRAISE_BOOTPARAM option ima: define a set of appraisal rules requiring file signatures ima: extend the "ima_policy" boot command line to support multiple policies ...
Diffstat (limited to 'security/integrity')
-rw-r--r--security/integrity/digsig_asymmetric.c4
-rw-r--r--security/integrity/iint.c2
-rw-r--r--security/integrity/ima/Kconfig16
-rw-r--r--security/integrity/ima/ima.h31
-rw-r--r--security/integrity/ima/ima_appraise.c16
-rw-r--r--security/integrity/ima/ima_fs.c13
-rw-r--r--security/integrity/ima/ima_policy.c106
-rw-r--r--security/integrity/ima/ima_queue.c2
-rw-r--r--security/integrity/ima/ima_template.c124
-rw-r--r--security/integrity/ima/ima_template_lib.c61
-rw-r--r--security/integrity/ima/ima_template_lib.h6
-rw-r--r--security/integrity/integrity.h7
12 files changed, 211 insertions, 177 deletions
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index 80052ed8d467..ab6a029062a1 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -92,13 +92,13 @@ int asymmetric_verify(struct key *keyring, const char *sig,
siglen -= sizeof(*hdr);
- if (siglen != __be16_to_cpu(hdr->sig_size))
+ if (siglen != be16_to_cpu(hdr->sig_size))
return -EBADMSG;
if (hdr->hash_algo >= HASH_ALGO__LAST)
return -ENOPKG;
- key = request_asymmetric_key(keyring, __be32_to_cpu(hdr->keyid));
+ key = request_asymmetric_key(keyring, be32_to_cpu(hdr->keyid));
if (IS_ERR(key))
return PTR_ERR(key);
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index c710d22042f9..6fc888ca468e 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -182,7 +182,7 @@ security_initcall(integrity_iintcache_init);
*
*/
int integrity_kernel_read(struct file *file, loff_t offset,
- char *addr, unsigned long count)
+ void *addr, unsigned long count)
{
mm_segment_t old_fs;
char __user *buf = (char __user *)addr;
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index 370eb2f4dd37..35ef69312811 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -96,19 +96,19 @@ choice
config IMA_DEFAULT_HASH_SHA1
bool "SHA1 (default)"
- depends on CRYPTO_SHA1
+ depends on CRYPTO_SHA1=y
config IMA_DEFAULT_HASH_SHA256
bool "SHA256"
- depends on CRYPTO_SHA256 && !IMA_TEMPLATE
+ depends on CRYPTO_SHA256=y && !IMA_TEMPLATE
config IMA_DEFAULT_HASH_SHA512
bool "SHA512"
- depends on CRYPTO_SHA512 && !IMA_TEMPLATE
+ depends on CRYPTO_SHA512=y && !IMA_TEMPLATE
config IMA_DEFAULT_HASH_WP512
bool "WP512"
- depends on CRYPTO_WP512 && !IMA_TEMPLATE
+ depends on CRYPTO_WP512=y && !IMA_TEMPLATE
endchoice
config IMA_DEFAULT_HASH
@@ -155,6 +155,14 @@ config IMA_APPRAISE
<http://linux-ima.sourceforge.net>
If unsure, say N.
+config IMA_APPRAISE_BOOTPARAM
+ bool "ima_appraise boot parameter"
+ depends on IMA_APPRAISE
+ default y
+ help
+ This option enables the different "ima_appraise=" modes
+ (eg. fix, log) from the boot command line.
+
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/ima.h b/security/integrity/ima/ima.h
index b563fbd4d122..d52b487ad259 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -172,17 +172,22 @@ static inline unsigned long ima_hash_key(u8 *digest)
return hash_long(*digest, IMA_HASH_BITS);
}
+#define __ima_hooks(hook) \
+ hook(NONE) \
+ hook(FILE_CHECK) \
+ hook(MMAP_CHECK) \
+ hook(BPRM_CHECK) \
+ hook(POST_SETATTR) \
+ hook(MODULE_CHECK) \
+ hook(FIRMWARE_CHECK) \
+ hook(KEXEC_KERNEL_CHECK) \
+ hook(KEXEC_INITRAMFS_CHECK) \
+ hook(POLICY_CHECK) \
+ hook(MAX_CHECK)
+#define __ima_hook_enumify(ENUM) ENUM,
+
enum ima_hooks {
- FILE_CHECK = 1,
- MMAP_CHECK,
- BPRM_CHECK,
- POST_SETATTR,
- MODULE_CHECK,
- FIRMWARE_CHECK,
- KEXEC_KERNEL_CHECK,
- KEXEC_INITRAMFS_CHECK,
- POLICY_CHECK,
- MAX_CHECK
+ __ima_hooks(__ima_hook_enumify)
};
/* LIM API function definitions */
@@ -284,7 +289,7 @@ static inline int ima_read_xattr(struct dentry *dentry,
return 0;
}
-#endif
+#endif /* CONFIG_IMA_APPRAISE */
/* LSM based policy rules require audit */
#ifdef CONFIG_IMA_LSM_RULES
@@ -306,12 +311,12 @@ static inline int security_filter_rule_match(u32 secid, u32 field, u32 op,
{
return -EINVAL;
}
-#endif /* CONFIG_IMA_TRUSTED_KEYRING */
+#endif /* CONFIG_IMA_LSM_RULES */
#ifdef CONFIG_IMA_READ_POLICY
#define POLICY_FILE_FLAGS (S_IWUSR | S_IRUSR)
#else
#define POLICY_FILE_FLAGS S_IWUSR
-#endif /* CONFIG_IMA_WRITE_POLICY */
+#endif /* CONFIG_IMA_READ_POLICY */
#endif /* __LINUX_IMA_H */
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 5d0785cfe063..809ba70fbbbf 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -20,18 +20,30 @@
static int __init default_appraise_setup(char *str)
{
+#ifdef CONFIG_IMA_APPRAISE_BOOTPARAM
if (strncmp(str, "off", 3) == 0)
ima_appraise = 0;
else if (strncmp(str, "log", 3) == 0)
ima_appraise = IMA_APPRAISE_LOG;
else if (strncmp(str, "fix", 3) == 0)
ima_appraise = IMA_APPRAISE_FIX;
+#endif
return 1;
}
__setup("ima_appraise=", default_appraise_setup);
/*
+ * is_ima_appraise_enabled - return appraise status
+ *
+ * Only return enabled, if not in ima_appraise="fix" or "log" modes.
+ */
+bool is_ima_appraise_enabled(void)
+{
+ return (ima_appraise & IMA_APPRAISE_ENFORCE) ? 1 : 0;
+}
+
+/*
* ima_must_appraise - set appraise flag
*
* Return 1 to appraise
@@ -205,7 +217,8 @@ int ima_appraise_measurement(enum ima_hooks func,
if (rc && rc != -ENODATA)
goto out;
- cause = "missing-hash";
+ cause = iint->flags & IMA_DIGSIG_REQUIRED ?
+ "IMA-signature-required" : "missing-hash";
status = INTEGRITY_NOLABEL;
if (opened & FILE_CREATED)
iint->flags |= IMA_NEW_FILE;
@@ -228,6 +241,7 @@ int ima_appraise_measurement(enum ima_hooks func,
case IMA_XATTR_DIGEST_NG:
/* first byte contains algorithm id */
hash_start = 1;
+ /* fall through */
case IMA_XATTR_DIGEST:
if (iint->flags & IMA_DIGSIG_REQUIRED) {
cause = "IMA-signature-required";
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index ca303e5d2b94..ad491c51e833 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -323,16 +323,11 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
if (*ppos != 0)
goto out;
- result = -ENOMEM;
- data = kmalloc(datalen + 1, GFP_KERNEL);
- if (!data)
+ data = memdup_user_nul(buf, datalen);
+ if (IS_ERR(data)) {
+ result = PTR_ERR(data);
goto out;
-
- *(data + datalen) = '\0';
-
- result = -EFAULT;
- if (copy_from_user(data, buf, datalen))
- goto out_free;
+ }
result = mutex_lock_interruptible(&ima_write_mutex);
if (result < 0)
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 6f885fab9d84..95209a5f8595 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -96,6 +96,8 @@ static struct ima_rule_entry dont_measure_rules[] __ro_after_init = {
{.action = DONT_MEASURE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_MEASURE, .fsmagic = CGROUP_SUPER_MAGIC,
.flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE, .fsmagic = CGROUP2_SUPER_MAGIC,
+ .flags = IMA_FSMAGIC},
{.action = DONT_MEASURE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC}
};
@@ -139,6 +141,7 @@ static struct ima_rule_entry default_appraise_rules[] __ro_after_init = {
{.action = DONT_APPRAISE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_APPRAISE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_APPRAISE, .fsmagic = CGROUP_SUPER_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_APPRAISE, .fsmagic = CGROUP2_SUPER_MAGIC, .flags = IMA_FSMAGIC},
#ifdef CONFIG_IMA_WRITE_POLICY
{.action = APPRAISE, .func = POLICY_CHECK,
.flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
@@ -153,6 +156,17 @@ static struct ima_rule_entry default_appraise_rules[] __ro_after_init = {
#endif
};
+static struct ima_rule_entry secure_boot_rules[] __ro_after_init = {
+ {.action = APPRAISE, .func = MODULE_CHECK,
+ .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
+ {.action = APPRAISE, .func = FIRMWARE_CHECK,
+ .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
+ {.action = APPRAISE, .func = KEXEC_KERNEL_CHECK,
+ .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
+ {.action = APPRAISE, .func = POLICY_CHECK,
+ .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
+};
+
static LIST_HEAD(ima_default_rules);
static LIST_HEAD(ima_policy_rules);
static LIST_HEAD(ima_temp_rules);
@@ -170,19 +184,27 @@ static int __init default_measure_policy_setup(char *str)
}
__setup("ima_tcb", default_measure_policy_setup);
+static bool ima_use_appraise_tcb __initdata;
+static bool ima_use_secure_boot __initdata;
static int __init policy_setup(char *str)
{
- if (ima_policy)
- return 1;
+ char *p;
- if (strcmp(str, "tcb") == 0)
- ima_policy = DEFAULT_TCB;
+ while ((p = strsep(&str, " |\n")) != NULL) {
+ if (*p == ' ')
+ continue;
+ if ((strcmp(p, "tcb") == 0) && !ima_policy)
+ ima_policy = DEFAULT_TCB;
+ else if (strcmp(p, "appraise_tcb") == 0)
+ ima_use_appraise_tcb = 1;
+ else if (strcmp(p, "secure_boot") == 0)
+ ima_use_secure_boot = 1;
+ }
return 1;
}
__setup("ima_policy=", policy_setup);
-static bool ima_use_appraise_tcb __initdata;
static int __init default_appraise_policy_setup(char *str)
{
ima_use_appraise_tcb = 1;
@@ -405,12 +427,14 @@ void ima_update_policy_flag(void)
*/
void __init ima_init_policy(void)
{
- int i, measure_entries, appraise_entries;
+ int i, measure_entries, appraise_entries, secure_boot_entries;
/* if !ima_policy set entries = 0 so we load NO default rules */
measure_entries = ima_policy ? ARRAY_SIZE(dont_measure_rules) : 0;
appraise_entries = ima_use_appraise_tcb ?
ARRAY_SIZE(default_appraise_rules) : 0;
+ secure_boot_entries = ima_use_secure_boot ?
+ ARRAY_SIZE(secure_boot_rules) : 0;
for (i = 0; i < measure_entries; i++)
list_add_tail(&dont_measure_rules[i].list, &ima_default_rules);
@@ -429,6 +453,14 @@ void __init ima_init_policy(void)
break;
}
+ /*
+ * Insert the appraise rules requiring file signatures, prior to
+ * any other appraise rules.
+ */
+ for (i = 0; i < secure_boot_entries; i++)
+ list_add_tail(&secure_boot_rules[i].list,
+ &ima_default_rules);
+
for (i = 0; i < appraise_entries; i++) {
list_add_tail(&default_appraise_rules[i].list,
&ima_default_rules);
@@ -931,30 +963,17 @@ enum {
mask_exec = 0, mask_write, mask_read, mask_append
};
-static char *mask_tokens[] = {
+static const char *const mask_tokens[] = {
"MAY_EXEC",
"MAY_WRITE",
"MAY_READ",
"MAY_APPEND"
};
-enum {
- func_file = 0, func_mmap, func_bprm,
- func_module, func_firmware, func_post,
- func_kexec_kernel, func_kexec_initramfs,
- func_policy
-};
+#define __ima_hook_stringify(str) (#str),
-static char *func_tokens[] = {
- "FILE_CHECK",
- "MMAP_CHECK",
- "BPRM_CHECK",
- "MODULE_CHECK",
- "FIRMWARE_CHECK",
- "POST_SETATTR",
- "KEXEC_KERNEL_CHECK",
- "KEXEC_INITRAMFS_CHECK",
- "POLICY_CHECK"
+static const char *const func_tokens[] = {
+ __ima_hooks(__ima_hook_stringify)
};
void *ima_policy_start(struct seq_file *m, loff_t *pos)
@@ -991,49 +1010,16 @@ void ima_policy_stop(struct seq_file *m, void *v)
#define pt(token) policy_tokens[token + Opt_err].pattern
#define mt(token) mask_tokens[token]
-#define ft(token) func_tokens[token]
/*
* policy_func_show - display the ima_hooks policy rule
*/
static void policy_func_show(struct seq_file *m, enum ima_hooks func)
{
- char tbuf[64] = {0,};
-
- switch (func) {
- case FILE_CHECK:
- seq_printf(m, pt(Opt_func), ft(func_file));
- break;
- case MMAP_CHECK:
- seq_printf(m, pt(Opt_func), ft(func_mmap));
- break;
- case BPRM_CHECK:
- seq_printf(m, pt(Opt_func), ft(func_bprm));
- break;
- case MODULE_CHECK:
- seq_printf(m, pt(Opt_func), ft(func_module));
- break;
- case FIRMWARE_CHECK:
- seq_printf(m, pt(Opt_func), ft(func_firmware));
- break;
- case POST_SETATTR:
- seq_printf(m, pt(Opt_func), ft(func_post));
- break;
- case KEXEC_KERNEL_CHECK:
- seq_printf(m, pt(Opt_func), ft(func_kexec_kernel));
- break;
- case KEXEC_INITRAMFS_CHECK:
- seq_printf(m, pt(Opt_func), ft(func_kexec_initramfs));
- break;
- case POLICY_CHECK:
- seq_printf(m, pt(Opt_func), ft(func_policy));
- break;
- default:
- snprintf(tbuf, sizeof(tbuf), "%d", func);
- seq_printf(m, pt(Opt_func), tbuf);
- break;
- }
- seq_puts(m, " ");
+ if (func > 0 && func < MAX_CHECK)
+ seq_printf(m, "func=%s ", func_tokens[func]);
+ else
+ seq_printf(m, "func=%d ", func);
}
int ima_policy_show(struct seq_file *m, void *v)
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index d9aa5ab71204..a02a86d51102 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -81,7 +81,7 @@ static int get_binary_runtime_size(struct ima_template_entry *entry)
size += sizeof(u32); /* pcr */
size += sizeof(entry->digest);
size += sizeof(int); /* template name size field */
- size += strlen(entry->template_desc->name) + 1;
+ size += strlen(entry->template_desc->name);
size += sizeof(entry->template_data_len);
size += entry->template_data_len;
return size;
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index cebb37c63629..7412d0291ab9 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -19,6 +19,9 @@
#include "ima.h"
#include "ima_template_lib.h"
+enum header_fields { HDR_PCR, HDR_DIGEST, HDR_TEMPLATE_NAME,
+ HDR_TEMPLATE_DATA, HDR__LAST };
+
static struct ima_template_desc builtin_templates[] = {
{.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT},
{.name = "ima-ng", .fmt = "d-ng|n-ng"},
@@ -274,13 +277,6 @@ static int ima_restore_template_data(struct ima_template_desc *template_desc,
int template_data_size,
struct ima_template_entry **entry)
{
- struct binary_field_data {
- u32 len;
- u8 data[0];
- } __packed;
-
- struct binary_field_data *field_data;
- int offset = 0;
int ret = 0;
int i;
@@ -290,30 +286,19 @@ static int ima_restore_template_data(struct ima_template_desc *template_desc,
if (!*entry)
return -ENOMEM;
+ ret = ima_parse_buf(template_data, template_data + template_data_size,
+ NULL, template_desc->num_fields,
+ (*entry)->template_data, NULL, NULL,
+ ENFORCE_FIELDS | ENFORCE_BUFEND, "template data");
+ if (ret < 0) {
+ kfree(*entry);
+ return ret;
+ }
+
(*entry)->template_desc = template_desc;
for (i = 0; i < template_desc->num_fields; i++) {
- field_data = template_data + offset;
-
- /* Each field of the template data is prefixed with a length. */
- if (offset > (template_data_size - sizeof(*field_data))) {
- pr_err("Restoring the template field failed\n");
- ret = -EINVAL;
- break;
- }
- offset += sizeof(*field_data);
-
- if (ima_canonical_fmt)
- field_data->len = le32_to_cpu(field_data->len);
-
- if (offset > (template_data_size - field_data->len)) {
- pr_err("Restoring the template field data failed\n");
- ret = -EINVAL;
- break;
- }
- offset += field_data->len;
-
- (*entry)->template_data[i].len = field_data->len;
- (*entry)->template_data_len += sizeof(field_data->len);
+ struct ima_field_data *field_data = &(*entry)->template_data[i];
+ u8 *data = field_data->data;
(*entry)->template_data[i].data =
kzalloc(field_data->len + 1, GFP_KERNEL);
@@ -321,8 +306,8 @@ static int ima_restore_template_data(struct ima_template_desc *template_desc,
ret = -ENOMEM;
break;
}
- memcpy((*entry)->template_data[i].data, field_data->data,
- field_data->len);
+ memcpy((*entry)->template_data[i].data, data, field_data->len);
+ (*entry)->template_data_len += sizeof(field_data->len);
(*entry)->template_data_len += field_data->len;
}
@@ -337,27 +322,19 @@ static int ima_restore_template_data(struct ima_template_desc *template_desc,
/* Restore the serialized binary measurement list without extending PCRs. */
int ima_restore_measurement_list(loff_t size, void *buf)
{
- struct binary_hdr_v1 {
- u32 pcr;
- u8 digest[TPM_DIGEST_SIZE];
- u32 template_name_len;
- char template_name[0];
- } __packed;
char template_name[MAX_TEMPLATE_NAME_LEN];
- struct binary_data_v1 {
- u32 template_data_size;
- char template_data[0];
- } __packed;
-
struct ima_kexec_hdr *khdr = buf;
- struct binary_hdr_v1 *hdr_v1;
- struct binary_data_v1 *data_v1;
+ struct ima_field_data hdr[HDR__LAST] = {
+ [HDR_PCR] = {.len = sizeof(u32)},
+ [HDR_DIGEST] = {.len = TPM_DIGEST_SIZE},
+ };
void *bufp = buf + sizeof(*khdr);
void *bufendp;
struct ima_template_entry *entry;
struct ima_template_desc *template_desc;
+ DECLARE_BITMAP(hdr_mask, HDR__LAST);
unsigned long count = 0;
int ret = 0;
@@ -380,6 +357,10 @@ int ima_restore_measurement_list(loff_t size, void *buf)
return -EINVAL;
}
+ bitmap_zero(hdr_mask, HDR__LAST);
+ bitmap_set(hdr_mask, HDR_PCR, 1);
+ bitmap_set(hdr_mask, HDR_DIGEST, 1);
+
/*
* ima kexec buffer prefix: version, buffer size, count
* v1 format: pcr, digest, template-name-len, template-name,
@@ -387,31 +368,25 @@ int ima_restore_measurement_list(loff_t size, void *buf)
*/
bufendp = buf + khdr->buffer_size;
while ((bufp < bufendp) && (count++ < khdr->count)) {
- hdr_v1 = bufp;
- if (bufp > (bufendp - sizeof(*hdr_v1))) {
- pr_err("attempting to restore partial measurement\n");
- ret = -EINVAL;
- break;
- }
- bufp += sizeof(*hdr_v1);
+ int enforce_mask = ENFORCE_FIELDS;
- if (ima_canonical_fmt)
- hdr_v1->template_name_len =
- le32_to_cpu(hdr_v1->template_name_len);
+ enforce_mask |= (count == khdr->count) ? ENFORCE_BUFEND : 0;
+ ret = ima_parse_buf(bufp, bufendp, &bufp, HDR__LAST, hdr, NULL,
+ hdr_mask, enforce_mask, "entry header");
+ if (ret < 0)
+ break;
- if ((hdr_v1->template_name_len >= MAX_TEMPLATE_NAME_LEN) ||
- (bufp > (bufendp - hdr_v1->template_name_len))) {
+ if (hdr[HDR_TEMPLATE_NAME].len >= MAX_TEMPLATE_NAME_LEN) {
pr_err("attempting to restore a template name \
that is too long\n");
ret = -EINVAL;
break;
}
- data_v1 = bufp += (u_int8_t)hdr_v1->template_name_len;
/* template name is not null terminated */
- memcpy(template_name, hdr_v1->template_name,
- hdr_v1->template_name_len);
- template_name[hdr_v1->template_name_len] = 0;
+ memcpy(template_name, hdr[HDR_TEMPLATE_NAME].data,
+ hdr[HDR_TEMPLATE_NAME].len);
+ template_name[hdr[HDR_TEMPLATE_NAME].len] = 0;
if (strcmp(template_name, "ima") == 0) {
pr_err("attempting to restore an unsupported \
@@ -441,34 +416,17 @@ int ima_restore_measurement_list(loff_t size, void *buf)
break;
}
- if (bufp > (bufendp - sizeof(data_v1->template_data_size))) {
- pr_err("restoring the template data size failed\n");
- ret = -EINVAL;
- break;
- }
- bufp += (u_int8_t) sizeof(data_v1->template_data_size);
-
- if (ima_canonical_fmt)
- data_v1->template_data_size =
- le32_to_cpu(data_v1->template_data_size);
-
- if (bufp > (bufendp - data_v1->template_data_size)) {
- pr_err("restoring the template data failed\n");
- ret = -EINVAL;
- break;
- }
- bufp += data_v1->template_data_size;
-
ret = ima_restore_template_data(template_desc,
- data_v1->template_data,
- data_v1->template_data_size,
+ hdr[HDR_TEMPLATE_DATA].data,
+ hdr[HDR_TEMPLATE_DATA].len,
&entry);
if (ret < 0)
break;
- memcpy(entry->digest, hdr_v1->digest, TPM_DIGEST_SIZE);
- entry->pcr =
- !ima_canonical_fmt ? hdr_v1->pcr : le32_to_cpu(hdr_v1->pcr);
+ memcpy(entry->digest, hdr[HDR_DIGEST].data,
+ hdr[HDR_DIGEST].len);
+ entry->pcr = !ima_canonical_fmt ? *(hdr[HDR_PCR].data) :
+ le32_to_cpu(*(hdr[HDR_PCR].data));
ret = ima_restore_measurement_entry(entry);
if (ret < 0)
break;
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index f9ba37b3928d..28af43f63572 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -159,6 +159,67 @@ void ima_show_template_sig(struct seq_file *m, enum ima_show_type show,
ima_show_template_field_data(m, show, DATA_FMT_HEX, field_data);
}
+/**
+ * ima_parse_buf() - Parses lengths and data from an input buffer
+ * @bufstartp: Buffer start address.
+ * @bufendp: Buffer end address.
+ * @bufcurp: Pointer to remaining (non-parsed) data.
+ * @maxfields: Length of fields array.
+ * @fields: Array containing lengths and pointers of parsed data.
+ * @curfields: Number of array items containing parsed data.
+ * @len_mask: Bitmap (if bit is set, data length should not be parsed).
+ * @enforce_mask: Check if curfields == maxfields and/or bufcurp == bufendp.
+ * @bufname: String identifier of the input buffer.
+ *
+ * Return: 0 on success, -EINVAL on error.
+ */
+int ima_parse_buf(void *bufstartp, void *bufendp, void **bufcurp,
+ int maxfields, struct ima_field_data *fields, int *curfields,
+ unsigned long *len_mask, int enforce_mask, char *bufname)
+{
+ void *bufp = bufstartp;
+ int i;
+
+ for (i = 0; i < maxfields; i++) {
+ if (len_mask == NULL || !test_bit(i, len_mask)) {
+ if (bufp > (bufendp - sizeof(u32)))
+ break;
+
+ fields[i].len = *(u32 *)bufp;
+ if (ima_canonical_fmt)
+ fields[i].len = le32_to_cpu(fields[i].len);
+
+ bufp += sizeof(u32);
+ }
+
+ if (bufp > (bufendp - fields[i].len))
+ break;
+
+ fields[i].data = bufp;
+ bufp += fields[i].len;
+ }
+
+ if ((enforce_mask & ENFORCE_FIELDS) && i != maxfields) {
+ pr_err("%s: nr of fields mismatch: expected: %d, current: %d\n",
+ bufname, maxfields, i);
+ return -EINVAL;
+ }
+
+ if ((enforce_mask & ENFORCE_BUFEND) && bufp != bufendp) {
+ pr_err("%s: buf end mismatch: expected: %p, current: %p\n",
+ bufname, bufendp, bufp);
+ return -EINVAL;
+ }
+
+ if (curfields)
+ *curfields = i;
+
+ if (bufcurp)
+ *bufcurp = bufp;
+
+ return 0;
+}
+
static int ima_eventdigest_init_common(u8 *digest, u32 digestsize, u8 hash_algo,
struct ima_field_data *field_data)
{
diff --git a/security/integrity/ima/ima_template_lib.h b/security/integrity/ima/ima_template_lib.h
index c344530c1d69..6a3d8b831deb 100644
--- a/security/integrity/ima/ima_template_lib.h
+++ b/security/integrity/ima/ima_template_lib.h
@@ -18,6 +18,9 @@
#include <linux/seq_file.h>
#include "ima.h"
+#define ENFORCE_FIELDS 0x00000001
+#define ENFORCE_BUFEND 0x00000002
+
void ima_show_template_digest(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
void ima_show_template_digest_ng(struct seq_file *m, enum ima_show_type show,
@@ -26,6 +29,9 @@ void ima_show_template_string(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
void ima_show_template_sig(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
+int ima_parse_buf(void *bufstartp, void *bufendp, void **bufcurp,
+ int maxfields, struct ima_field_data *fields, int *curfields,
+ unsigned long *len_mask, int enforce_mask, char *bufname);
int ima_eventdigest_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventname_init(struct ima_event_data *event_data,
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 24520b4ef3b0..a53e7e4ab06c 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -92,8 +92,8 @@ struct signature_v2_hdr {
uint8_t type; /* xattr type */
uint8_t version; /* signature format version */
uint8_t hash_algo; /* Digest algorithm [enum hash_algo] */
- uint32_t keyid; /* IMA key identifier - not X509/PGP specific */
- uint16_t sig_size; /* signature size */
+ __be32 keyid; /* IMA key identifier - not X509/PGP specific */
+ __be16 sig_size; /* signature size */
uint8_t sig[0]; /* signature payload */
} __packed;
@@ -118,7 +118,8 @@ struct integrity_iint_cache {
struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
int integrity_kernel_read(struct file *file, loff_t offset,
- char *addr, unsigned long count);
+ void *addr, unsigned long count);
+
int __init integrity_read_file(const char *path, char **data);
#define INTEGRITY_KEYRING_EVM 0