summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/security/IMA-templates.rst8
-rw-r--r--include/linux/evm.h16
-rw-r--r--security/integrity/evm/evm.h1
-rw-r--r--security/integrity/evm/evm_crypto.c7
-rw-r--r--security/integrity/evm/evm_main.c125
-rw-r--r--security/integrity/evm/evm_secfs.c18
-rw-r--r--security/integrity/ima/ima_template.c20
-rw-r--r--security/integrity/ima/ima_template_lib.c169
-rw-r--r--security/integrity/ima/ima_template_lib.h14
9 files changed, 363 insertions, 15 deletions
diff --git a/Documentation/security/IMA-templates.rst b/Documentation/security/IMA-templates.rst
index 9f3e86ab028a..5adc22f99496 100644
--- a/Documentation/security/IMA-templates.rst
+++ b/Documentation/security/IMA-templates.rst
@@ -75,6 +75,13 @@ descriptors by adding their identifier to the format string
- 'modsig' the appended file signature;
- 'buf': the buffer data that was used to generate the hash without size limitations;
- 'evmsig': the EVM portable signature;
+ - 'iuid': the inode UID;
+ - 'igid': the inode GID;
+ - 'imode': the inode mode;
+ - 'xattrnames': a list of xattr names (separated by |), only if the xattr is
+ present;
+ - 'xattrlengths': a list of xattr lengths (u32), only if the xattr is present;
+ - 'xattrvalues': a list of xattr values;
Below, there is the list of defined template descriptors:
@@ -84,6 +91,7 @@ Below, there is the list of defined template descriptors:
- "ima-sig": its format is ``d-ng|n-ng|sig``;
- "ima-buf": its format is ``d-ng|n-ng|buf``;
- "ima-modsig": its format is ``d-ng|n-ng|sig|d-modsig|modsig``;
+ - "evm-sig": its format is ``d-ng|n-ng|evmsig|xattrnames|xattrlengths|xattrvalues|iuid|igid|imode``;
Use
diff --git a/include/linux/evm.h b/include/linux/evm.h
index 31ef1dbbb3ac..4c374be70247 100644
--- a/include/linux/evm.h
+++ b/include/linux/evm.h
@@ -38,6 +38,10 @@ extern int evm_inode_init_security(struct inode *inode,
const struct xattr *xattr_array,
struct xattr *evm);
extern bool evm_revalidate_status(const char *xattr_name);
+extern int evm_protected_xattr_if_enabled(const char *req_xattr_name);
+extern int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
+ int buffer_size, char type,
+ bool canonical_fmt);
#ifdef CONFIG_FS_POSIX_ACL
extern int posix_xattr_acl(const char *xattrname);
#else
@@ -114,5 +118,17 @@ static inline bool evm_revalidate_status(const char *xattr_name)
return false;
}
+static inline int evm_protected_xattr_if_enabled(const char *req_xattr_name)
+{
+ return false;
+}
+
+static inline int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
+ int buffer_size, char type,
+ bool canonical_fmt)
+{
+ return -EOPNOTSUPP;
+}
+
#endif /* CONFIG_EVM */
#endif /* LINUX_EVM_H */
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
index f2fef2b5ed51..0d44f41d16f8 100644
--- a/security/integrity/evm/evm.h
+++ b/security/integrity/evm/evm.h
@@ -29,6 +29,7 @@
struct xattr_list {
struct list_head list;
char *name;
+ bool enabled;
};
extern int evm_initialized;
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index d76b006cbcc4..1628e2ca9862 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -216,6 +216,13 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
if (strcmp(xattr->name, XATTR_NAME_IMA) == 0)
is_ima = true;
+ /*
+ * Skip non-enabled xattrs for locally calculated
+ * signatures/HMACs.
+ */
+ if (type != EVM_XATTR_PORTABLE_DIGSIG && !xattr->enabled)
+ continue;
+
if ((req_xattr_name && req_xattr_value)
&& !strcmp(xattr->name, req_xattr_name)) {
error = 0;
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 0196168aeb7d..2c226e634ae9 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -34,24 +34,44 @@ static const char * const integrity_status_msg[] = {
int evm_hmac_attrs;
static struct xattr_list evm_config_default_xattrnames[] = {
+ {.name = XATTR_NAME_SELINUX,
#ifdef CONFIG_SECURITY_SELINUX
- {.name = XATTR_NAME_SELINUX},
+ .enabled = true
#endif
+ },
+ {.name = XATTR_NAME_SMACK,
#ifdef CONFIG_SECURITY_SMACK
- {.name = XATTR_NAME_SMACK},
+ .enabled = true
+#endif
+ },
+ {.name = XATTR_NAME_SMACKEXEC,
#ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS
- {.name = XATTR_NAME_SMACKEXEC},
- {.name = XATTR_NAME_SMACKTRANSMUTE},
- {.name = XATTR_NAME_SMACKMMAP},
+ .enabled = true
#endif
+ },
+ {.name = XATTR_NAME_SMACKTRANSMUTE,
+#ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS
+ .enabled = true
#endif
+ },
+ {.name = XATTR_NAME_SMACKMMAP,
+#ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS
+ .enabled = true
+#endif
+ },
+ {.name = XATTR_NAME_APPARMOR,
#ifdef CONFIG_SECURITY_APPARMOR
- {.name = XATTR_NAME_APPARMOR},
+ .enabled = true
#endif
+ },
+ {.name = XATTR_NAME_IMA,
#ifdef CONFIG_IMA_APPRAISE
- {.name = XATTR_NAME_IMA},
+ .enabled = true
#endif
- {.name = XATTR_NAME_CAPS},
+ },
+ {.name = XATTR_NAME_CAPS,
+ .enabled = true
+ },
};
LIST_HEAD(evm_config_xattrnames);
@@ -76,7 +96,9 @@ static void __init evm_init_config(void)
pr_info("Initialising EVM extended attributes:\n");
for (i = 0; i < xattrs; i++) {
- pr_info("%s\n", evm_config_default_xattrnames[i].name);
+ pr_info("%s%s\n", evm_config_default_xattrnames[i].name,
+ !evm_config_default_xattrnames[i].enabled ?
+ " (disabled)" : "");
list_add_tail(&evm_config_default_xattrnames[i].list,
&evm_config_xattrnames);
}
@@ -257,7 +279,8 @@ out:
return evm_status;
}
-static int evm_protected_xattr(const char *req_xattr_name)
+static int evm_protected_xattr_common(const char *req_xattr_name,
+ bool all_xattrs)
{
int namelen;
int found = 0;
@@ -265,6 +288,9 @@ static int evm_protected_xattr(const char *req_xattr_name)
namelen = strlen(req_xattr_name);
list_for_each_entry_lockless(xattr, &evm_config_xattrnames, list) {
+ if (!all_xattrs && !xattr->enabled)
+ continue;
+
if ((strlen(xattr->name) == namelen)
&& (strncmp(req_xattr_name, xattr->name, namelen) == 0)) {
found = 1;
@@ -281,6 +307,85 @@ static int evm_protected_xattr(const char *req_xattr_name)
return found;
}
+static int evm_protected_xattr(const char *req_xattr_name)
+{
+ return evm_protected_xattr_common(req_xattr_name, false);
+}
+
+int evm_protected_xattr_if_enabled(const char *req_xattr_name)
+{
+ return evm_protected_xattr_common(req_xattr_name, true);
+}
+
+/**
+ * evm_read_protected_xattrs - read EVM protected xattr names, lengths, values
+ * @dentry: dentry of the read xattrs
+ * @inode: inode of the read xattrs
+ * @buffer: buffer xattr names, lengths or values are copied to
+ * @buffer_size: size of buffer
+ * @type: n: names, l: lengths, v: values
+ * @canonical_fmt: data format (true: little endian, false: native format)
+ *
+ * Read protected xattr names (separated by |), lengths (u32) or values for a
+ * given dentry and return the total size of copied data. If buffer is NULL,
+ * just return the total size.
+ *
+ * Returns the total size on success, a negative value on error.
+ */
+int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
+ int buffer_size, char type, bool canonical_fmt)
+{
+ struct xattr_list *xattr;
+ int rc, size, total_size = 0;
+
+ list_for_each_entry_lockless(xattr, &evm_config_xattrnames, list) {
+ rc = __vfs_getxattr(dentry, d_backing_inode(dentry),
+ xattr->name, NULL, 0);
+ if (rc < 0 && rc == -ENODATA)
+ continue;
+ else if (rc < 0)
+ return rc;
+
+ switch (type) {
+ case 'n':
+ size = strlen(xattr->name) + 1;
+ if (buffer) {
+ if (total_size)
+ *(buffer + total_size - 1) = '|';
+
+ memcpy(buffer + total_size, xattr->name, size);
+ }
+ break;
+ case 'l':
+ size = sizeof(u32);
+ if (buffer) {
+ if (canonical_fmt)
+ rc = cpu_to_le32(rc);
+
+ *(u32 *)(buffer + total_size) = rc;
+ }
+ break;
+ case 'v':
+ size = rc;
+ if (buffer) {
+ rc = __vfs_getxattr(dentry,
+ d_backing_inode(dentry), xattr->name,
+ buffer + total_size,
+ buffer_size - total_size);
+ if (rc < 0)
+ return rc;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ total_size += size;
+ }
+
+ return total_size;
+}
+
/**
* evm_verifyxattr - verify the integrity of the requested xattr
* @dentry: object of the verify xattr
diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c
index 5f0da41bccd0..8a9db7dfca7e 100644
--- a/security/integrity/evm/evm_secfs.c
+++ b/security/integrity/evm/evm_secfs.c
@@ -139,8 +139,12 @@ static ssize_t evm_read_xattrs(struct file *filp, char __user *buf,
if (rc)
return -ERESTARTSYS;
- list_for_each_entry(xattr, &evm_config_xattrnames, list)
+ list_for_each_entry(xattr, &evm_config_xattrnames, list) {
+ if (!xattr->enabled)
+ continue;
+
size += strlen(xattr->name) + 1;
+ }
temp = kmalloc(size + 1, GFP_KERNEL);
if (!temp) {
@@ -149,6 +153,9 @@ static ssize_t evm_read_xattrs(struct file *filp, char __user *buf,
}
list_for_each_entry(xattr, &evm_config_xattrnames, list) {
+ if (!xattr->enabled)
+ continue;
+
sprintf(temp + offset, "%s\n", xattr->name);
offset += strlen(xattr->name) + 1;
}
@@ -190,7 +197,7 @@ static ssize_t evm_write_xattrs(struct file *file, const char __user *buf,
ab = audit_log_start(audit_context(), GFP_KERNEL,
AUDIT_INTEGRITY_EVM_XATTR);
- if (!ab)
+ if (!ab && IS_ENABLED(CONFIG_AUDIT))
return -ENOMEM;
xattr = kmalloc(sizeof(struct xattr_list), GFP_KERNEL);
@@ -199,6 +206,7 @@ static ssize_t evm_write_xattrs(struct file *file, const char __user *buf,
goto out;
}
+ xattr->enabled = true;
xattr->name = memdup_user_nul(buf, count);
if (IS_ERR(xattr->name)) {
err = PTR_ERR(xattr->name);
@@ -245,6 +253,10 @@ static ssize_t evm_write_xattrs(struct file *file, const char __user *buf,
list_for_each_entry(tmp, &evm_config_xattrnames, list) {
if (strcmp(xattr->name, tmp->name) == 0) {
err = -EEXIST;
+ if (!tmp->enabled) {
+ tmp->enabled = true;
+ err = count;
+ }
mutex_unlock(&xattr_list_mutex);
goto out;
}
@@ -256,7 +268,7 @@ static ssize_t evm_write_xattrs(struct file *file, const char __user *buf,
audit_log_end(ab);
return count;
out:
- audit_log_format(ab, " res=%d", err);
+ audit_log_format(ab, " res=%d", (err < 0) ? err : 0);
audit_log_end(ab);
if (xattr) {
kfree(xattr->name);
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index 7a60848c04a5..a85963853a91 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -22,6 +22,8 @@ static struct ima_template_desc builtin_templates[] = {
{.name = "ima-sig", .fmt = "d-ng|n-ng|sig"},
{.name = "ima-buf", .fmt = "d-ng|n-ng|buf"},
{.name = "ima-modsig", .fmt = "d-ng|n-ng|sig|d-modsig|modsig"},
+ {.name = "evm-sig",
+ .fmt = "d-ng|n-ng|evmsig|xattrnames|xattrlengths|xattrvalues|iuid|igid|imode"},
{.name = "", .fmt = ""}, /* placeholder for a custom format */
};
@@ -47,6 +49,21 @@ static const struct ima_template_field supported_fields[] = {
.field_show = ima_show_template_sig},
{.field_id = "evmsig", .field_init = ima_eventevmsig_init,
.field_show = ima_show_template_sig},
+ {.field_id = "iuid", .field_init = ima_eventinodeuid_init,
+ .field_show = ima_show_template_uint},
+ {.field_id = "igid", .field_init = ima_eventinodegid_init,
+ .field_show = ima_show_template_uint},
+ {.field_id = "imode", .field_init = ima_eventinodemode_init,
+ .field_show = ima_show_template_uint},
+ {.field_id = "xattrnames",
+ .field_init = ima_eventinodexattrnames_init,
+ .field_show = ima_show_template_string},
+ {.field_id = "xattrlengths",
+ .field_init = ima_eventinodexattrlengths_init,
+ .field_show = ima_show_template_sig},
+ {.field_id = "xattrvalues",
+ .field_init = ima_eventinodexattrvalues_init,
+ .field_show = ima_show_template_sig},
};
/*
@@ -54,7 +71,8 @@ static const struct ima_template_field supported_fields[] = {
* need to be accounted for since they shouldn't be defined in the same template
* description as 'd-ng' and 'n-ng' respectively.
*/
-#define MAX_TEMPLATE_NAME_LEN sizeof("d-ng|n-ng|sig|buf|d-modisg|modsig")
+#define MAX_TEMPLATE_NAME_LEN \
+ sizeof("d-ng|n-ng|evmsig|xattrnames|xattrlengths|xattrvalues|iuid|igid|imode")
static struct ima_template_desc *ima_template;
static struct ima_template_desc *ima_buf_template;
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index 4314d9a3514c..518fd50ea48a 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -11,6 +11,7 @@
#include "ima_template_lib.h"
#include <linux/xattr.h>
+#include <linux/evm.h>
static bool ima_template_hash_algo_allowed(u8 algo)
{
@@ -24,7 +25,8 @@ enum data_formats {
DATA_FMT_DIGEST = 0,
DATA_FMT_DIGEST_WITH_ALGO,
DATA_FMT_STRING,
- DATA_FMT_HEX
+ DATA_FMT_HEX,
+ DATA_FMT_UINT
};
static int ima_write_template_field_data(const void *data, const u32 datalen,
@@ -88,6 +90,35 @@ static void ima_show_template_data_ascii(struct seq_file *m,
case DATA_FMT_STRING:
seq_printf(m, "%s", buf_ptr);
break;
+ case DATA_FMT_UINT:
+ switch (field_data->len) {
+ case sizeof(u8):
+ seq_printf(m, "%u", *(u8 *)buf_ptr);
+ break;
+ case sizeof(u16):
+ if (ima_canonical_fmt)
+ seq_printf(m, "%u",
+ le16_to_cpu(*(u16 *)buf_ptr));
+ else
+ seq_printf(m, "%u", *(u16 *)buf_ptr);
+ break;
+ case sizeof(u32):
+ if (ima_canonical_fmt)
+ seq_printf(m, "%u",
+ le32_to_cpu(*(u32 *)buf_ptr));
+ else
+ seq_printf(m, "%u", *(u32 *)buf_ptr);
+ break;
+ case sizeof(u64):
+ if (ima_canonical_fmt)
+ seq_printf(m, "%llu",
+ le64_to_cpu(*(u64 *)buf_ptr));
+ else
+ seq_printf(m, "%llu", *(u64 *)buf_ptr);
+ break;
+ default:
+ break;
+ }
default:
break;
}
@@ -163,6 +194,12 @@ void ima_show_template_buf(struct seq_file *m, enum ima_show_type show,
ima_show_template_field_data(m, show, DATA_FMT_HEX, field_data);
}
+void ima_show_template_uint(struct seq_file *m, enum ima_show_type show,
+ struct ima_field_data *field_data)
+{
+ ima_show_template_field_data(m, show, DATA_FMT_UINT, field_data);
+}
+
/**
* ima_parse_buf() - Parses lengths and data from an input buffer
* @bufstartp: Buffer start address.
@@ -515,3 +552,133 @@ int ima_eventevmsig_init(struct ima_event_data *event_data,
kfree(xattr_data);
return rc;
}
+
+static int ima_eventinodedac_init_common(struct ima_event_data *event_data,
+ struct ima_field_data *field_data,
+ bool get_uid)
+{
+ unsigned int id;
+
+ if (!event_data->file)
+ return 0;
+
+ if (get_uid)
+ id = i_uid_read(file_inode(event_data->file));
+ else
+ id = i_gid_read(file_inode(event_data->file));
+
+ if (ima_canonical_fmt) {
+ if (sizeof(id) == sizeof(u16))
+ id = cpu_to_le16(id);
+ else
+ id = cpu_to_le32(id);
+ }
+
+ return ima_write_template_field_data((void *)&id, sizeof(id),
+ DATA_FMT_UINT, field_data);
+}
+
+/*
+ * ima_eventinodeuid_init - include the inode UID as part of the template
+ * data
+ */
+int ima_eventinodeuid_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data)
+{
+ return ima_eventinodedac_init_common(event_data, field_data, true);
+}
+
+/*
+ * ima_eventinodegid_init - include the inode GID as part of the template
+ * data
+ */
+int ima_eventinodegid_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data)
+{
+ return ima_eventinodedac_init_common(event_data, field_data, false);
+}
+
+/*
+ * ima_eventinodemode_init - include the inode mode as part of the template
+ * data
+ */
+int ima_eventinodemode_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data)
+{
+ struct inode *inode;
+ umode_t mode;
+
+ if (!event_data->file)
+ return 0;
+
+ inode = file_inode(event_data->file);
+ mode = inode->i_mode;
+ if (ima_canonical_fmt)
+ mode = cpu_to_le16(mode);
+
+ return ima_write_template_field_data((char *)&mode, sizeof(mode),
+ DATA_FMT_UINT, field_data);
+}
+
+static int ima_eventinodexattrs_init_common(struct ima_event_data *event_data,
+ struct ima_field_data *field_data,
+ char type)
+{
+ u8 *buffer = NULL;
+ int rc;
+
+ if (!event_data->file)
+ return 0;
+
+ rc = evm_read_protected_xattrs(file_dentry(event_data->file), NULL, 0,
+ type, ima_canonical_fmt);
+ if (rc < 0)
+ return 0;
+
+ buffer = kmalloc(rc, GFP_KERNEL);
+ if (!buffer)
+ return 0;
+
+ rc = evm_read_protected_xattrs(file_dentry(event_data->file), buffer,
+ rc, type, ima_canonical_fmt);
+ if (rc < 0) {
+ rc = 0;
+ goto out;
+ }
+
+ rc = ima_write_template_field_data((char *)buffer, rc, DATA_FMT_HEX,
+ field_data);
+out:
+ kfree(buffer);
+ return rc;
+}
+
+/*
+ * ima_eventinodexattrnames_init - include a list of xattr names as part of the
+ * template data
+ */
+int ima_eventinodexattrnames_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data)
+{
+ return ima_eventinodexattrs_init_common(event_data, field_data, 'n');
+}
+
+/*
+ * ima_eventinodexattrlengths_init - include a list of xattr lengths as part of
+ * the template data
+ */
+int ima_eventinodexattrlengths_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data)
+{
+ return ima_eventinodexattrs_init_common(event_data, field_data, 'l');
+}
+
+/*
+ * ima_eventinodexattrvalues_init - include a list of xattr values as part of
+ * the template data
+ */
+int ima_eventinodexattrvalues_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data)
+{
+ return ima_eventinodexattrs_init_common(event_data, field_data, 'v');
+}
diff --git a/security/integrity/ima/ima_template_lib.h b/security/integrity/ima/ima_template_lib.h
index f4b2a2056d1d..c71f1de95753 100644
--- a/security/integrity/ima/ima_template_lib.h
+++ b/security/integrity/ima/ima_template_lib.h
@@ -27,6 +27,8 @@ void ima_show_template_sig(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
void ima_show_template_buf(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
+void ima_show_template_uint(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);
@@ -48,4 +50,16 @@ int ima_eventmodsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventevmsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
+int ima_eventinodeuid_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
+int ima_eventinodegid_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
+int ima_eventinodemode_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
+int ima_eventinodexattrnames_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
+int ima_eventinodexattrlengths_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
+int ima_eventinodexattrvalues_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
#endif /* __LINUX_IMA_TEMPLATE_LIB_H */