[PATCH 3/7] ima: Introduce template fields mntuidmap and mntgidmap

Roberto Sassu
2021-05-20T08:57:27Z

This patch introduces the new template fields mntuidmap and mntgidmap,
which include respectively the UID and GID mappings of the idmapped mount,
if the user namespace is not the initial one.

These template fields, which should be included whenever the iuid and the
igid fields are included, allow remote verifiers to find the original UID
and GID of the inode during signature verification. The iuid and igid
fields include the mapped UID and GID when the inode is in an idmapped
mount.

This solution has been preferred to providing always the original UID and
GID, regardless of whether the inode is in an idmapped mount or not, as
the mapped UID and GID are those seen by processes and matched with the IMA
policy.

Cc: Christian Brauner <christian.brauner@ubuntu.com>
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
Documentation/security/IMA-templates.rst | 4 ++
security/integrity/ima/ima_template.c | 6 ++
security/integrity/ima/ima_template_lib.c | 83 +++++++++++++++++++++++
security/integrity/ima/ima_template_lib.h | 4 ++
4 files changed, 97 insertions(+)

diff --git a/Documentation/security/IMA-templates.rst b/Documentation/security/IMA-templates.rst
index bf8ce4cf5878..48a2df22a1a1 100644
--- a/Documentation/security/IMA-templates.rst
+++ b/Documentation/security/IMA-templates.rst
@@ -77,6 +77,10 @@ descriptors by adding their identifier to the format string
- 'evmsig': the EVM portable signature;
- 'iuid': the inode UID;
- 'igid': the inode GID;
+ - 'mntuidmap': the UID mappings of the idmapped mount (nr extents,
+ [ uid_gid_extent1 ] ... [ uid_gid_extentN ], all u32 in canonical format);
+ - 'mntgidmap': the GID mappings of the idmapped mount (same format as
+ 'mntuidmap');

Below, there is the list of defined template descriptors:
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index a5ecd9e2581b..19de115b985b 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -51,6 +51,12 @@ static const struct ima_template_field supported_fields[] = {
.field_show = ima_show_template_uint},
{.field_id = "igid", .field_init = ima_eventinodegid_init,
.field_show = ima_show_template_uint},
+ {.field_id = "mntuidmap",
+ .field_init = ima_eventmnt_userns_uid_map_init,
+ .field_show = ima_show_template_sig},
+ {.field_id = "mntgidmap",
+ .field_init = ima_eventmnt_userns_gid_map_init,
+ .field_show = ima_show_template_sig},
};

/*
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index a191b861548b..bc4919d90c3a 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -609,3 +609,86 @@ int ima_eventinodegid_init(struct ima_event_data *event_data,
{
return ima_eventinodedac_init_common(event_data, field_data, false);
}
+
+int ima_eventmnt_userns_common_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data,
+ bool get_uid_map)
+{
+ struct user_namespace *mnt_userns;
+ u8 *buf, *buf_ptr;
+ struct uid_gid_map *map;
+ int rc, i;
+
+ if (!event_data->file)
+ return 0;
+
+ mnt_userns = file_mnt_user_ns(event_data->file);
+ if (mnt_userns == &init_user_ns)
+ return 0;
+
+ map = &mnt_userns->uid_map;
+ if (!get_uid_map)
+ map = &mnt_userns->gid_map;
+
+ buf_ptr = buf = kmalloc(sizeof(map->nr_extents) +
+ map->nr_extents * sizeof(*map->extent),
+ GFP_KERNEL);
+ if (!buf)
+ return 0;
+
+ memcpy(buf_ptr, &map->nr_extents, sizeof(map->nr_extents));
+
+ if (ima_canonical_fmt)
+ *(u32 *)buf_ptr = cpu_to_le32(*(u32 *)buf_ptr);
+
+ buf_ptr += sizeof(u32);
+
+ for (i = 0; i < map->nr_extents; i++) {
+ struct uid_gid_extent *extent;
+
+ /* Taken from m_start() in kernel/user_namespace.c. */
+ if (map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS)
+ extent = &map->extent[i];
+ else
+ extent = &map->forward[i];
+
+ memcpy(buf_ptr, &extent->first, sizeof(extent->first));
+ if (ima_canonical_fmt)
+ *(u32 *)buf_ptr = cpu_to_le32(*(u32 *)buf_ptr);
+ buf_ptr += sizeof(extent->first);
+ memcpy(buf_ptr, &extent->lower_first,
+ sizeof(extent->lower_first));
+ if (ima_canonical_fmt)
+ *(u32 *)buf_ptr = cpu_to_le32(*(u32 *)buf_ptr);
+ buf_ptr += sizeof(extent->lower_first);
+ memcpy(buf_ptr, &extent->count, sizeof(extent->count));
+ if (ima_canonical_fmt)
+ *(u32 *)buf_ptr = cpu_to_le32(*(u32 *)buf_ptr);
+ buf_ptr += sizeof(extent->count);
+ }
+
+ rc = ima_write_template_field_data((char *)buf, buf_ptr - buf,
+ DATA_FMT_HEX, field_data);
+ kfree(buf);
+ return rc;
+}
+
+/*
+ * ima_eventmnt_userns_uid_map_init - include the UID mappings of the idmapped
+ * mount as part of the template data
+ */
+int ima_eventmnt_userns_uid_map_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data)
+{
+ return ima_eventmnt_userns_common_init(event_data, field_data, true);
+}
+
+/*
+ * ima_eventmnt_userns_gid_map_init - include the GID mappings of the idmapped
+ * mount as part of the template data
+ */
+int ima_eventmnt_userns_gid_map_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data)
+{
+ return ima_eventmnt_userns_common_init(event_data, field_data, false);
+}
diff --git a/security/integrity/ima/ima_template_lib.h b/security/integrity/ima/ima_template_lib.h
index b0aaf109f386..51ee66fc7230 100644
--- a/security/integrity/ima/ima_template_lib.h
+++ b/security/integrity/ima/ima_template_lib.h
@@ -54,4 +54,8 @@ 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_eventmnt_userns_uid_map_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
+int ima_eventmnt_userns_gid_map_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
#endif /* __LINUX_IMA_TEMPLATE_LIB_H */
--
2.25.1