From: Mimi Zohar zohar@linux.vnet.ibm.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
This patch adds metadata to a file from a supplied buffer. The buffer might contains multiple metadata records. The format of each record is:
<metadata len (ASCII, 8 chars)><version><type><metadata>
For now, only the TYPE_XATTR metadata type is supported. The specific format of this metadata type is:
<xattr #N name>\0<xattr #N value>
[kamensky: fixed restoring of xattrs for symbolic links by using sys_lsetxattr() instead of sys_setxattr()]
[sassu: removed state management, kept only do_setxattrs(), added support for generic file metadata, replaced sys_lsetxattr() with vfs_setxattr(), added check for entry_size, added check for hdr->c_size, replaced strlen() with strnlen(); moved do_setxattrs() before do_name()]
Signed-off-by: Mimi Zohar zohar@linux.vnet.ibm.com Signed-off-by: Victor Kamensky kamensky@cisco.com Signed-off-by: Taras Kondratiuk takondra@cisco.com Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- include/linux/initramfs.h | 21 ++++++++++ init/initramfs.c | 88 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 include/linux/initramfs.h
diff --git a/include/linux/initramfs.h b/include/linux/initramfs.h new file mode 100644 index 000000000000..2f8cee441236 --- /dev/null +++ b/include/linux/initramfs.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * include/linux/initramfs.h + * + * Include file for file metadata in the initial ram disk. + */ +#ifndef _LINUX_INITRAMFS_H +#define _LINUX_INITRAMFS_H + +#define METADATA_FILENAME "METADATA!!!" + +enum metadata_types { TYPE_NONE, TYPE_XATTR, TYPE__LAST }; + +struct metadata_hdr { + char c_size[8]; /* total size including c_size field */ + char c_version; /* header version */ + char c_type; /* metadata type */ + char c_metadata[]; /* metadata */ +} __packed; + +#endif /*LINUX_INITRAMFS_H*/ diff --git a/init/initramfs.c b/init/initramfs.c index e7a01c2ccd1b..3b272c56a987 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -15,6 +15,8 @@ #include <linux/memblock.h> #include <linux/mm.h> #include <linux/namei.h> +#include <linux/xattr.h> +#include <linux/initramfs.h> #include <linux/init_syscalls.h> #include <linux/task_work.h> #include <linux/umh.h> @@ -177,7 +179,7 @@ static __initdata time64_t mtime;
static __initdata unsigned long ino, major, minor, nlink; static __initdata umode_t mode; -static __initdata unsigned long body_len, name_len; +static __initdata unsigned long body_len, name_len, metadata_len; static __initdata uid_t uid; static __initdata gid_t gid; static __initdata unsigned rdev; @@ -250,7 +252,7 @@ static void __init read_into(char *buf, unsigned size, enum state next) } }
-static __initdata char *header_buf, *symlink_buf, *name_buf; +static __initdata char *header_buf, *symlink_buf, *name_buf, *metadata_buf;
static int __init do_start(void) { @@ -351,6 +353,88 @@ static int __init maybe_link(void) return 0; }
+static int __init do_setxattrs(char *pathname, char *buf, size_t size) +{ + struct path path; + char *xattr_name, *xattr_value; + uint32_t xattr_name_size, xattr_value_size; + int ret; + + xattr_name = buf; + xattr_name_size = strnlen(xattr_name, size); + if (xattr_name_size == size) { + error("malformed xattrs"); + return -EINVAL; + } + + xattr_value = xattr_name + xattr_name_size + 1; + xattr_value_size = buf + size - xattr_value; + + ret = kern_path(pathname, 0, &path); + if (!ret) { + ret = vfs_setxattr(&nop_mnt_idmap, path.dentry, xattr_name, xattr_value, + xattr_value_size, 0); + + path_put(&path); + } + + pr_debug("%s: %s size: %u val: %s (ret: %d)\n", pathname, + xattr_name, xattr_value_size, xattr_value, ret); + + return ret; +} + +static int __init __maybe_unused do_parse_metadata(char *pathname) +{ + char *buf = metadata_buf; + char *bufend = metadata_buf + metadata_len; + struct metadata_hdr *hdr; + char str[sizeof(hdr->c_size) + 1]; + uint32_t entry_size; + + if (!metadata_len) + return 0; + + str[sizeof(hdr->c_size)] = 0; + + while (buf < bufend) { + int ret; + + if (buf + sizeof(*hdr) > bufend) { + error("malformed metadata"); + break; + } + + hdr = (struct metadata_hdr *)buf; + if (hdr->c_version != 1) { + pr_debug("Unsupported header version\n"); + break; + } + + memcpy(str, hdr->c_size, sizeof(hdr->c_size)); + ret = kstrtou32(str, 16, &entry_size); + if (ret || buf + entry_size > bufend || + entry_size < sizeof(*hdr)) { + error("malformed xattrs"); + break; + } + + switch (hdr->c_type) { + case TYPE_XATTR: + do_setxattrs(pathname, buf + sizeof(*hdr), + entry_size - sizeof(*hdr)); + break; + default: + pr_debug("Unsupported metadata type\n"); + break; + } + + buf += entry_size; + } + + return 0; +} + static __initdata struct file *wfile; static __initdata loff_t wfile_pos;
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
Instead of changing the CPIO format, metadata are parsed from regular files with special name 'METADATA!!!'. This file immediately follows the file metadata are added to.
This patch checks if the file being extracted has the special name and, if yes, creates a buffer with the content of that file and calls do_parse_metadata() to parse metadata from the buffer.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reported-by: kbuild test robot lkp@intel.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- init/initramfs.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-)
diff --git a/init/initramfs.c b/init/initramfs.c index 3b272c56a987..0311acda2209 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -253,6 +253,7 @@ static void __init read_into(char *buf, unsigned size, enum state next) }
static __initdata char *header_buf, *symlink_buf, *name_buf, *metadata_buf; +static __initdata char *metadata_buf_ptr, *previous_name_buf;
static int __init do_start(void) { @@ -437,6 +438,7 @@ static int __init __maybe_unused do_parse_metadata(char *pathname)
static __initdata struct file *wfile; static __initdata loff_t wfile_pos; +static int metadata __initdata;
static int __init do_name(void) { @@ -445,6 +447,10 @@ static int __init do_name(void) if (strcmp(collected, "TRAILER!!!") == 0) { free_hash(); return 0; + } else if (strcmp(collected, METADATA_FILENAME) == 0) { + metadata = 1; + } else { + memcpy(previous_name_buf, collected, strlen(collected) + 1); } clean_path(collected, mode); if (S_ISREG(mode)) { @@ -482,11 +488,47 @@ static int __init do_name(void) return 0; }
+static int __init do_process_metadata(char *buf, int len, bool last) +{ + int ret = 0; + + if (!metadata_buf) { + metadata_buf_ptr = metadata_buf = kmalloc(body_len, GFP_KERNEL); + if (!metadata_buf_ptr) { + ret = -ENOMEM; + goto out; + } + + metadata_len = body_len; + } + + if (metadata_buf_ptr + len > metadata_buf + metadata_len) { + ret = -EINVAL; + goto out; + } + + memcpy(metadata_buf_ptr, buf, len); + metadata_buf_ptr += len; + + if (last) + do_parse_metadata(previous_name_buf); +out: + if (ret < 0 || last) { + kfree(metadata_buf); + metadata_buf = NULL; + metadata = 0; + } + + return ret; +} + static int __init do_copy(void) { if (byte_count >= body_len) { if (xwrite(wfile, victim, body_len, &wfile_pos) != body_len) error("write error"); + if (metadata) + do_process_metadata(victim, body_len, true);
do_utime_path(&wfile->f_path, mtime); fput(wfile); @@ -498,6 +540,8 @@ static int __init do_copy(void) } else { if (xwrite(wfile, victim, byte_count, &wfile_pos) != byte_count) error("write error"); + if (metadata) + do_process_metadata(victim, byte_count, false); body_len -= byte_count; eat(byte_count); return 1; @@ -507,6 +551,7 @@ static int __init do_copy(void) static int __init do_symlink(void) { collected[N_ALIGN(name_len) + body_len] = '\0'; + memcpy(previous_name_buf, collected, strlen(collected) + 1); clean_path(collected, 0); init_symlink(collected + N_ALIGN(name_len), collected); init_chown(collected, uid, gid, AT_SYMLINK_NOFOLLOW); @@ -574,8 +619,10 @@ static char * __init unpack_to_rootfs(char *buf, unsigned long len) header_buf = kmalloc(110, GFP_KERNEL); symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL); name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL); + previous_name_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, + GFP_KERNEL);
- if (!header_buf || !symlink_buf || !name_buf) + if (!header_buf || !symlink_buf || !name_buf || !previous_name_buf) panic_show_mem("can't allocate buffers");
state = Start; @@ -620,6 +667,7 @@ static char * __init unpack_to_rootfs(char *buf, unsigned long len) len -= my_inptr; } dir_utime(); + kfree(previous_name_buf); kfree(name_buf); kfree(symlink_buf); kfree(header_buf);
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
This patch adds support for file metadata (only TYPE_XATTR metadata type). gen_init_cpio has been modified to read xattrs from files that will be added to the image and to include file metadata as separate files with the special name 'METADATA!!!'.
This behavior can be selected by setting the desired file metadata type as value for CONFIG_INITRAMFS_FILE_METADATA.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- usr/Kconfig | 8 +++ usr/Makefile | 4 +- usr/gen_init_cpio.c | 136 +++++++++++++++++++++++++++++++++++++++++-- usr/gen_initramfs.sh | 7 ++- 4 files changed, 148 insertions(+), 7 deletions(-)
diff --git a/usr/Kconfig b/usr/Kconfig index 8bbcf699fe3b..be7a8ba66c44 100644 --- a/usr/Kconfig +++ b/usr/Kconfig @@ -227,3 +227,11 @@ config INITRAMFS_COMPRESSION_NONE filesystem image will be present in memory simultaneously
endchoice + +config INITRAMFS_FILE_METADATA + string "File metadata type" + default "" + help + Specify xattr to include xattrs in the image. + + If you are not sure, leave it blank. diff --git a/usr/Makefile b/usr/Makefile index 59d9e8b07a01..f82b6f74aa7b 100644 --- a/usr/Makefile +++ b/usr/Makefile @@ -64,7 +64,9 @@ quiet_cmd_initfs = GEN $@ $(CONFIG_SHELL) $< -o $@ -l $(obj)/.initramfs_data.cpio.d \ $(if $(CONFIG_INITRAMFS_ROOT_UID), -u $(CONFIG_INITRAMFS_ROOT_UID)) \ $(if $(CONFIG_INITRAMFS_ROOT_GID), -g $(CONFIG_INITRAMFS_ROOT_GID)) \ - $(ramfs-input) + $(if $(CONFIG_INITRAMFS_ROOT_GID), -g $(CONFIG_INITRAMFS_ROOT_GID)) \ + $(if $(filter-out "",$(CONFIG_INITRAMFS_FILE_METADATA)), \ + -e $(CONFIG_INITRAMFS_FILE_METADATA)) $(ramfs-input)
# We rebuild initramfs_data.cpio if: # 1) Any included file is newer than initramfs_data.cpio diff --git a/usr/gen_init_cpio.c b/usr/gen_init_cpio.c index 61230532fef1..c9d403403bd3 100644 --- a/usr/gen_init_cpio.c +++ b/usr/gen_init_cpio.c @@ -5,6 +5,7 @@ #include <stdbool.h> #include <sys/types.h> #include <sys/stat.h> +#include <sys/xattr.h> #include <string.h> #include <unistd.h> #include <time.h> @@ -12,6 +13,7 @@ #include <errno.h> #include <ctype.h> #include <limits.h> +#include "../include/linux/initramfs.h"
/* * Original work by Jeff Garzik @@ -28,6 +30,115 @@ static unsigned int offset; static unsigned int ino = 721; static time_t default_mtime; static bool do_csum = false; +static char metadata_path[] = "/tmp/cpio-metadata-XXXXXX"; +static int metadata_fd = -1; + +static enum metadata_types parse_metadata_type(char *arg) +{ + static char *metadata_type_str[TYPE__LAST] = { + [TYPE_NONE] = "none", + [TYPE_XATTR] = "xattr", + }; + int i; + + for (i = 0; i < TYPE__LAST; i++) + if (!strcmp(metadata_type_str[i], arg)) + return i; + + return TYPE_NONE; +} + +static int cpio_mkfile(const char *name, const char *location, + unsigned int mode, uid_t uid, gid_t gid, + unsigned int nlinks); + +static int write_xattrs(const char *path) +{ + struct metadata_hdr hdr = { .c_version = 1, .c_type = TYPE_XATTR }; + char str[sizeof(hdr.c_size) + 1]; + char *xattr_list, *list_ptr, *xattr_value; + ssize_t list_len, name_len, value_len, len; + int ret = -EINVAL; + + if (metadata_fd < 0) + return 0; + + if (path == metadata_path) + return 0; + + list_len = listxattr(path, NULL, 0); + if (list_len <= 0) + return 0; + + list_ptr = xattr_list = malloc(list_len); + if (!list_ptr) { + fprintf(stderr, "out of memory\n"); + return ret; + } + + len = listxattr(path, xattr_list, list_len); + if (len != list_len) + goto out; + + if (ftruncate(metadata_fd, 0)) + goto out; + + lseek(metadata_fd, 0, SEEK_SET); + + while (list_ptr < xattr_list + list_len) { + name_len = strlen(list_ptr); + + value_len = getxattr(path, list_ptr, NULL, 0); + if (value_len < 0) { + fprintf(stderr, "cannot get xattrs\n"); + break; + } + + if (value_len) { + xattr_value = malloc(value_len); + if (!xattr_value) { + fprintf(stderr, "out of memory\n"); + break; + } + } else { + xattr_value = NULL; + } + + len = getxattr(path, list_ptr, xattr_value, value_len); + if (len != value_len) + break; + + snprintf(str, sizeof(str), "%.8lx", + sizeof(hdr) + name_len + 1 + value_len); + + memcpy(hdr.c_size, str, sizeof(hdr.c_size)); + + if (write(metadata_fd, &hdr, sizeof(hdr)) != sizeof(hdr)) + break; + + if (write(metadata_fd, list_ptr, name_len + 1) != name_len + 1) + break; + + if (write(metadata_fd, xattr_value, value_len) != value_len) + break; + + if (fsync(metadata_fd)) + break; + + list_ptr += name_len + 1; + free(xattr_value); + xattr_value = NULL; + } + + free(xattr_value); +out: + if (list_ptr != xattr_list + list_len) + return ret; + + free(xattr_list); + + return cpio_mkfile(METADATA_FILENAME, metadata_path, S_IFREG, 0, 0, 1); +}
struct file_handler { const char *type; @@ -132,7 +243,7 @@ static int cpio_mkslink(const char *name, const char *target, push_pad(); push_string(target); push_pad(); - return 0; + return write_xattrs(name); }
static int cpio_mkslink_line(const char *line) @@ -178,7 +289,7 @@ static int cpio_mkgeneric(const char *name, unsigned int mode, 0); /* chksum */ push_hdr(s); push_rest(name); - return 0; + return write_xattrs(name); }
enum generic_types { @@ -272,7 +383,7 @@ static int cpio_mknod(const char *name, unsigned int mode, 0); /* chksum */ push_hdr(s); push_rest(name); - return 0; + return write_xattrs(name); }
static int cpio_mknod_line(const char *line) @@ -422,7 +533,7 @@ static int cpio_mkfile(const char *name, const char *location, name += namesize; } ino++; - rc = 0; + rc = write_xattrs(location);
error: if (file >= 0) @@ -577,10 +688,11 @@ int main (int argc, char *argv[]) int ec = 0; int line_nr = 0; const char *filename; + enum metadata_types metadata_type = TYPE_NONE;
default_mtime = time(NULL); while (1) { - int opt = getopt(argc, argv, "t:ch"); + int opt = getopt(argc, argv, "t:e:ch"); char *invalid;
if (opt == -1) @@ -595,6 +707,9 @@ int main (int argc, char *argv[]) exit(1); } break; + case 'e': + metadata_type = parse_metadata_type(optarg); + break; case 'c': do_csum = true; break; @@ -629,6 +744,14 @@ int main (int argc, char *argv[]) exit(1); }
+ if (metadata_type != TYPE_NONE) { + metadata_fd = mkstemp(metadata_path); + if (metadata_fd < 0) { + fprintf(stderr, "cannot create temporary file\n"); + exit(1); + } + } + while (fgets(line, LINE_SIZE, cpio_list)) { int type_idx; size_t slen = strlen(line); @@ -684,5 +807,8 @@ int main (int argc, char *argv[]) if (ec == 0) cpio_trailer();
+ if (metadata_type != TYPE_NONE) + close(metadata_fd); + exit(ec); } diff --git a/usr/gen_initramfs.sh b/usr/gen_initramfs.sh index 63476bb70b41..36648620d071 100755 --- a/usr/gen_initramfs.sh +++ b/usr/gen_initramfs.sh @@ -26,6 +26,7 @@ $0 [-o <file>] [-l <dep_list>] [-u <uid>] [-g <gid>] {-d | <cpio_source>} ... <cpio_source> File list or directory for cpio archive. If <cpio_source> is a .cpio file it will be used as direct input to initramfs. + -e <type> File metadata type to include in the cpio archive.
All options except -o and -l may be repeated and are interpreted sequentially and immediately. -u and -g states are preserved across @@ -218,6 +219,10 @@ while [ $# -gt 0 ]; do [ "$root_gid" = "-1" ] && root_gid=$(id -g || echo 0) shift ;; + "-e") # file metadata type + metadata_arg="-e $1" + shift + ;; "-h") usage exit 0 @@ -244,4 +249,4 @@ if test -n "$KBUILD_BUILD_TIMESTAMP"; then timestamp="-t $timestamp" fi fi -usr/gen_init_cpio $timestamp $cpio_list > $output +usr/gen_init_cpio $metadata_arg $timestamp $cpio_list > $output
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
This patch adds the new option initramtmpfs for the kernel command line, to force usage of tmpfs instead of ramfs as filesystem for rootfs.
This option should be used when the initial ram disk contains xattrs, as only tmpfs supports them.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- init/do_mounts.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/init/do_mounts.c b/init/do_mounts.c index 811e94daf0a8..4dcc3af8295e 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -31,6 +31,7 @@ int root_mountflags = MS_RDONLY | MS_SILENT; static char * __initdata root_device_name; static char __initdata saved_root_name[64]; static int root_wait; +static int initramtmpfs;
dev_t ROOT_DEV;
@@ -333,9 +334,16 @@ static int __init root_delay_setup(char *str) return 1; }
+static int __init initramtmpfs_setup(char *str) +{ + initramtmpfs = 1; + return 1; +} + __setup("rootflags=", root_data_setup); __setup("rootfstype=", fs_names_setup); __setup("rootdelay=", root_delay_setup); +__setup("initramtmpfs", initramtmpfs_setup);
/* This can return zero length strings. Caller should check */ static int __init split_fs_names(char *page, size_t size, char *names) @@ -665,7 +673,8 @@ struct file_system_type rootfs_fs_type = {
void __init init_rootfs(void) { - if (IS_ENABLED(CONFIG_TMPFS) && !saved_root_name[0] && + if (IS_ENABLED(CONFIG_TMPFS) && + (!saved_root_name[0] || initramtmpfs) && (!root_fs_names || strstr(root_fs_names, "tmpfs"))) is_tmpfs = true; }
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
IMA and EVM have been designed as two independent subsystems: the first for checking the integrity of file data; the second for checking file metadata. Making them independent allows users to adopt them incrementally.
The point of intersection is in IMA-Appraise, which calls evm_verifyxattr() to ensure that security.ima wasn't modified during an offline attack. The design choice, to ensure incremental adoption, was to continue appraisal verification if evm_verifyxattr() returns INTEGRITY_UNKNOWN. This value is returned when EVM is not enabled in the kernel configuration, or if the HMAC key has not been loaded yet.
Although this choice appears legitimate, it might not be suitable for hardened systems, where the administrator expects that access is denied if there is any error. An attacker could intentionally delete the EVM keys from the system and set the file digest in security.ima to the actual file digest so that the final appraisal status is INTEGRITY_PASS.
This patch allows such hardened systems to strictly enforce an access control policy based on the validity of signatures/HMACs, by introducing two new values for the ima_appraise= kernel option: enforce-evm and log-evm.
Cc: stable@vger.kernel.org Fixes: 2fe5d6def1672 ("ima: integrity appraisal extension") Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- Documentation/admin-guide/kernel-parameters.txt | 3 ++- security/integrity/ima/ima_appraise.c | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 9e5bab29685f..ee50a3b91d73 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1941,7 +1941,8 @@ Set number of hash buckets for inode cache.
ima_appraise= [IMA] appraise integrity measurements - Format: { "off" | "enforce" | "fix" | "log" } + Format: { "off" | "enforce" | "fix" | "log" | + "enforce-evm" | "log-evm" } default: "enforce"
ima_appraise_tcb [IMA] Deprecated. Use ima_policy= instead. diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 491c1aca0b1c..08ae5d7faa1d 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -19,6 +19,7 @@
#include "ima.h"
+static bool ima_appraise_req_evm __ro_after_init; #ifdef CONFIG_IMA_APPRAISE_BOOTPARAM static char *ima_appraise_cmdline_default __initdata; core_param(ima_appraise, ima_appraise_cmdline_default, charp, 0); @@ -52,6 +53,10 @@ void __init ima_appraise_parse_cmdline(void) } else { ima_appraise = appraisal_state; } + + if (strcmp(str, "enforce-evm") == 0 || + strcmp(str, "log-evm") == 0) + ima_appraise_req_evm = true; } #endif
@@ -522,7 +527,11 @@ int ima_appraise_measurement(enum ima_hooks func, switch (status) { case INTEGRITY_PASS: case INTEGRITY_PASS_IMMUTABLE: + break; case INTEGRITY_UNKNOWN: + if (ima_appraise_req_evm && + xattr_value->type != EVM_IMA_XATTR_DIGSIG) + goto out; break; case INTEGRITY_NOXATTRS: /* No EVM protected xattrs. */ /* It's fine not to have xattrs when using a modsig. */
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
IMA reads the hash algorithm from security.ima, if exists, so that a signature can be verified with the correct file digest.
This patch moves ima_read_xattr() and ima_get_hash_algo() to ima_main.c, so that the file digest in the measurement list or in the audit logs can be compared with a reference value calculated with a specific hash algorithm.
In addition, this patch also allows the usage of security.ima with type EVM_IMA_XATTR_DIGSIG and signature length zero, so that the xattr can be used just to specify the hash algorithm.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/ima/ima.h | 17 -------- security/integrity/ima/ima_appraise.c | 16 ++------ security/integrity/ima/ima_main.c | 57 +++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 29 deletions(-)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index c29db699c996..8b869e2951d8 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -324,10 +324,6 @@ int ima_must_appraise(struct mnt_idmap *idmap, struct inode *inode, void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file); enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, enum ima_hooks func); -enum hash_algo ima_get_hash_algo(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, int xattr_len);
#else static inline int ima_check_blacklist(struct integrity_iint_cache *iint, @@ -366,19 +362,6 @@ static inline enum integrity_status ima_get_cache_status(struct integrity_iint_c return INTEGRITY_UNKNOWN; }
-static inline enum hash_algo -ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len) -{ - return ima_hash_algo; -} - -static inline int ima_read_xattr(struct dentry *dentry, - struct evm_ima_xattr_data **xattr_value, - int xattr_len) -{ - return 0; -} - #endif /* CONFIG_IMA_APPRAISE */
#ifdef CONFIG_IMA_APPRAISE_MODSIG diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 08ae5d7faa1d..274a0fec59f5 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -228,18 +228,6 @@ enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value, return ima_hash_algo; }
-int ima_read_xattr(struct dentry *dentry, - struct evm_ima_xattr_data **xattr_value, int xattr_len) -{ - int ret; - - ret = vfs_getxattr_alloc(&nop_mnt_idmap, dentry, XATTR_NAME_IMA, - (char **)xattr_value, xattr_len, GFP_NOFS); - if (ret == -EOPNOTSUPP) - ret = 0; - return ret; -} - /* * calc_file_id_hash - calculate the hash of the ima_file_id struct data * @type: xattr type [enum evm_ima_xattr_type] @@ -498,6 +486,10 @@ int ima_appraise_measurement(enum ima_hooks func, if (!(inode->i_opflags & IOP_XATTR) && !try_modsig) return INTEGRITY_UNKNOWN;
+ if (xattr_value && xattr_value->type == EVM_IMA_XATTR_DIGSIG && + xattr_len == sizeof(struct signature_v2_hdr)) + rc = -ENODATA; + /* If reading the xattr failed and there's no modsig, error out. */ if (rc <= 0 && !try_modsig) { if (rc && rc != -ENODATA) diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index d66a0a36415e..847be55912eb 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -153,6 +153,63 @@ static void ima_rdwr_violation_check(struct file *file, "invalid_pcr", "open_writers"); }
+static enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value, + int xattr_len) +{ + struct signature_v2_hdr *sig; + enum hash_algo ret; + + if (!xattr_value || xattr_len < 2) + /* return default hash algo */ + return ima_hash_algo; + + switch (xattr_value->type) { + case IMA_VERITY_DIGSIG: + sig = (typeof(sig))xattr_value; + if (sig->version != 3 || xattr_len < sizeof(*sig) || + sig->hash_algo >= HASH_ALGO__LAST) + return ima_hash_algo; + return sig->hash_algo; + case EVM_IMA_XATTR_DIGSIG: + sig = (typeof(sig))xattr_value; + if (sig->version != 2 || xattr_len <= sizeof(*sig) + || sig->hash_algo >= HASH_ALGO__LAST) + return ima_hash_algo; + return sig->hash_algo; + case IMA_XATTR_DIGEST_NG: + /* first byte contains algorithm id */ + ret = xattr_value->data[0]; + if (ret < HASH_ALGO__LAST) + return ret; + break; + case IMA_XATTR_DIGEST: + /* this is for backward compatibility */ + if (xattr_len == 21) { + unsigned int zero = 0; + if (!memcmp(&xattr_value->data[16], &zero, 4)) + return HASH_ALGO_MD5; + else + return HASH_ALGO_SHA1; + } else if (xattr_len == 17) + return HASH_ALGO_MD5; + break; + } + + /* return default hash algo */ + return ima_hash_algo; +} + +static int ima_read_xattr(struct dentry *dentry, + struct evm_ima_xattr_data **xattr_value, int xattr_len) +{ + int ret; + + ret = vfs_getxattr_alloc(&nop_mnt_idmap, dentry, XATTR_NAME_IMA, + (char **)xattr_value, xattr_len, GFP_NOFS); + if (ret == -EOPNOTSUPP) + ret = 0; + return ret; +} static void ima_check_last_writer(struct integrity_iint_cache *iint, struct inode *inode, struct file *file) {
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
This patch renames ima_read_policy() to ima_read_file() so that the function can be used to read files for different purposes. It also adds the opened file in securityfs as parameter so that the function can determine which action it should do with the passed data.
This patch replaces kernel_read_file_from_path() with filp_open() + kernel_read_file() so that the file descriptor can be used for further checks.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/ima/ima_fs.c | 50 ++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 16 deletions(-)
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index cd1683dad3bf..cae42c5896bd 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -21,11 +21,20 @@ #include <linux/rcupdate.h> #include <linux/parser.h> #include <linux/vmalloc.h> +#include <linux/file.h>
#include "ima.h"
static DEFINE_MUTEX(ima_write_mutex);
+static struct dentry *ima_dir; +static struct dentry *ima_symlink; +static struct dentry *binary_runtime_measurements; +static struct dentry *ascii_runtime_measurements; +static struct dentry *runtime_measurements_count; +static struct dentry *violations; +static struct dentry *ima_policy; + bool ima_canonical_fmt; static int __init default_canonical_fmt_setup(char *str) { @@ -271,11 +280,13 @@ static const struct file_operations ima_ascii_measurements_ops = { .release = seq_release, };
-static ssize_t ima_read_policy(char *path) +static ssize_t ima_read_file(char *path, struct dentry *dentry) { void *data = NULL; char *datap; size_t size; + struct file *file; + enum kernel_read_file_id file_id = READING_POLICY; int rc, pathlen = strlen(path);
char *p; @@ -284,25 +295,39 @@ static ssize_t ima_read_policy(char *path) datap = path; strsep(&datap, "\n");
- rc = kernel_read_file_from_path(path, 0, &data, INT_MAX, NULL, - READING_POLICY); + file = filp_open(path, O_RDONLY, 0); + if (IS_ERR(file)) { + pr_err("Unable to open file: %s (%ld)", path, PTR_ERR(file)); + return PTR_ERR(file); + } + + rc = kernel_read_file(file, 0, &data, INT_MAX, NULL, file_id); if (rc < 0) { - pr_err("Unable to open file: %s (%d)", path, rc); + pr_err("Unable to read file: %s (%d)", path, rc); + fput(file); return rc; } size = rc; rc = 0;
datap = data; - while (size > 0 && (p = strsep(&datap, "\n"))) { - pr_debug("rule: %s\n", p); - rc = ima_parse_add_rule(p); + while (size > 0) { + if (dentry == ima_policy) { + p = strsep(&datap, "\n"); + if (p == NULL) + break; + + pr_debug("rule: %s\n", p); + rc = ima_parse_add_rule(p); + } + if (rc < 0) break; size -= rc; }
vfree(data); + fput(file); if (rc < 0) return rc; else if (size) @@ -316,6 +341,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, { char *data; ssize_t result; + struct dentry *dentry = file_dentry(file);
if (datalen >= PAGE_SIZE) datalen = PAGE_SIZE - 1; @@ -336,7 +362,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, goto out_free;
if (data[0] == '/') { - result = ima_read_policy(data); + result = ima_read_file(data, dentry); } else if (ima_appraise & IMA_APPRAISE_POLICY) { pr_err("signed policy file (specified as an absolute pathname) required\n"); integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, @@ -356,14 +382,6 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, return result; }
-static struct dentry *ima_dir; -static struct dentry *ima_symlink; -static struct dentry *binary_runtime_measurements; -static struct dentry *ascii_runtime_measurements; -static struct dentry *runtime_measurements_count; -static struct dentry *violations; -static struct dentry *ima_policy; - enum ima_fs_flags { IMA_FS_BUSY, };
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
ima_write_policy() is being used to load a new policy from user space. This function can be reused to load different types of data.
This patch renames ima_write_policy() to ima_write_data() and executes the appropriate actions depending on the opened file in securityfs.
Also, this patch raises the uploaded data size limit to 64M, to accept files (e.g. digest lists) larger than a policy. The same limit is used for the SELinux policy.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/ima/ima_fs.c | 50 ++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 19 deletions(-)
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index cae42c5896bd..57933bba19e8 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -336,26 +336,32 @@ static ssize_t ima_read_file(char *path, struct dentry *dentry) return pathlen; }
-static ssize_t ima_write_policy(struct file *file, const char __user *buf, - size_t datalen, loff_t *ppos) +static ssize_t ima_write_data(struct file *file, const char __user *buf, + size_t datalen, loff_t *ppos) { char *data; ssize_t result; struct dentry *dentry = file_dentry(file);
- if (datalen >= PAGE_SIZE) - datalen = PAGE_SIZE - 1; - /* No partial writes. */ result = -EINVAL; if (*ppos != 0) goto out;
- data = memdup_user_nul(buf, datalen); - if (IS_ERR(data)) { - result = PTR_ERR(data); + result = -EFBIG; + if (datalen > 64 * 1024 * 1024 - 1) goto out; - } + + result = -ENOMEM; + data = vmalloc(datalen + 1); + if (!data) + goto out; + + result = -EFAULT; + if (copy_from_user(data, buf, datalen) != 0) + goto out_free; + + data[datalen] = '\0';
result = mutex_lock_interruptible(&ima_write_mutex); if (result < 0) @@ -363,20 +369,26 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
if (data[0] == '/') { result = ima_read_file(data, dentry); - } else if (ima_appraise & IMA_APPRAISE_POLICY) { - pr_err("signed policy file (specified as an absolute pathname) required\n"); - integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, - "policy_update", "signed policy required", - 1, 0); - result = -EACCES; + } else if (dentry == ima_policy) { + if (ima_appraise & IMA_APPRAISE_POLICY) { + pr_err("signed policy file (specified " + "as an absolute pathname) required\n"); + integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, + "policy_update", + "signed policy required", 1, 0); + result = -EACCES; + } else { + result = ima_parse_add_rule(data); + } } else { - result = ima_parse_add_rule(data); + pr_err("Unknown data type\n"); + result = -EINVAL; } mutex_unlock(&ima_write_mutex); out_free: - kfree(data); + vfree(data); out: - if (result < 0) + if (dentry == ima_policy && result < 0) valid_policy = 0;
return result; @@ -462,7 +474,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
static const struct file_operations ima_measure_policy_ops = { .open = ima_open_policy, - .write = ima_write_policy, + .write = ima_write_data, .read = seq_read, .release = ima_release_policy, .llseek = generic_file_llseek,
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
This patch renames ima_open_policy() and ima_release_policy() respectively to ima_open_data_upload() and ima_release_data_upload(). They will be used to implement file operations for interfaces allowing to load data from user space.
A new flag (IMA_POLICY_BUSY) has been defined to prevent concurrent policy upload.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/ima/ima_fs.c | 60 ++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 16 deletions(-)
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 57933bba19e8..b012f853f452 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -395,9 +395,20 @@ static ssize_t ima_write_data(struct file *file, const char __user *buf, }
enum ima_fs_flags { + IMA_POLICY_BUSY, IMA_FS_BUSY, };
+static enum ima_fs_flags ima_get_dentry_flag(struct dentry *dentry) +{ + enum ima_fs_flags flag = IMA_FS_BUSY; + + if (dentry == ima_policy) + flag = IMA_POLICY_BUSY; + + return flag; +} + static unsigned long ima_fs_flags;
#ifdef CONFIG_IMA_READ_POLICY @@ -410,40 +421,57 @@ static const struct seq_operations ima_policy_seqops = { #endif
/* - * ima_open_policy: sequentialize access to the policy file + * ima_open_data_upload: sequentialize access to the data upload interface */ -static int ima_open_policy(struct inode *inode, struct file *filp) +static int ima_open_data_upload(struct inode *inode, struct file *filp) { + struct dentry *dentry = file_dentry(filp); + const struct seq_operations *seq_ops = NULL; + enum ima_fs_flags flag = ima_get_dentry_flag(dentry); + bool read_allowed = false; + + if (dentry == ima_policy) { +#ifdef CONFIG_IMA_READ_POLICY + read_allowed = true; + seq_ops = &ima_policy_seqops; +#endif + } + if (!(filp->f_flags & O_WRONLY)) { -#ifndef CONFIG_IMA_READ_POLICY - return -EACCES; -#else + if (!read_allowed) + return -EACCES; if ((filp->f_flags & O_ACCMODE) != O_RDONLY) return -EACCES; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - return seq_open(filp, &ima_policy_seqops); -#endif + return seq_open(filp, seq_ops); } - if (test_and_set_bit(IMA_FS_BUSY, &ima_fs_flags)) + if (test_and_set_bit(flag, &ima_fs_flags)) return -EBUSY; return 0; }
/* - * ima_release_policy - start using the new measure policy rules. + * ima_release_data_upload - start using the new measure policy rules. * * Initially, ima_measure points to the default policy rules, now * point to the new policy rules, and remove the securityfs policy file, * assuming a valid policy. */ -static int ima_release_policy(struct inode *inode, struct file *file) +static int ima_release_data_upload(struct inode *inode, struct file *file) { + struct dentry *dentry = file_dentry(file); const char *cause = valid_policy ? "completed" : "failed"; + enum ima_fs_flags flag = ima_get_dentry_flag(dentry);
if ((file->f_flags & O_ACCMODE) == O_RDONLY) return seq_release(inode, file);
+ if (dentry != ima_policy) { + clear_bit(flag, &ima_fs_flags); + return 0; + } + if (valid_policy && ima_check_policy() < 0) { cause = "failed"; valid_policy = 0; @@ -456,7 +484,7 @@ static int ima_release_policy(struct inode *inode, struct file *file) if (!valid_policy) { ima_delete_rules(); valid_policy = 1; - clear_bit(IMA_FS_BUSY, &ima_fs_flags); + clear_bit(flag, &ima_fs_flags); return 0; }
@@ -465,18 +493,18 @@ static int ima_release_policy(struct inode *inode, struct file *file) securityfs_remove(ima_policy); ima_policy = NULL; #elif defined(CONFIG_IMA_WRITE_POLICY) - clear_bit(IMA_FS_BUSY, &ima_fs_flags); + clear_bit(flag, &ima_fs_flags); #elif defined(CONFIG_IMA_READ_POLICY) inode->i_mode &= ~S_IWUSR; #endif return 0; }
-static const struct file_operations ima_measure_policy_ops = { - .open = ima_open_policy, +static const struct file_operations ima_data_upload_ops = { + .open = ima_open_data_upload, .write = ima_write_data, .read = seq_read, - .release = ima_release_policy, + .release = ima_release_data_upload, .llseek = generic_file_llseek, };
@@ -532,7 +560,7 @@ int __init ima_fs_init(void)
ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS, ima_dir, NULL, - &ima_measure_policy_ops); + &ima_data_upload_ops); if (IS_ERR(ima_policy)) { ret = PTR_ERR(ima_policy); goto out;
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
ima_show_htable_violations() and ima_show_measurements_count() both call ima_show_htable_value() to copy the value of an atomic_long_t variable to a buffer.
This patch modifies the definition of ima_show_htable_value(), so that this function can be used in any file_operations structure. The atomic_long_t variable used as source is chosen depending on the opened file in the securityfs filesystem.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/ima/ima_fs.c | 38 +++++++++++---------------------- 1 file changed, 12 insertions(+), 26 deletions(-)
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index b012f853f452..5d28e163db50 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -47,38 +47,24 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup);
static int valid_policy = 1;
-static ssize_t ima_show_htable_value(char __user *buf, size_t count, - loff_t *ppos, atomic_long_t *val) +static ssize_t ima_show_htable_value(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) { + atomic_long_t *val = NULL; char tmpbuf[32]; /* greater than largest 'long' string value */ ssize_t len;
+ if (filp->f_path.dentry == violations) + val = &ima_htable.violations; + else if (filp->f_path.dentry == runtime_measurements_count) + val = &ima_htable.len; + len = scnprintf(tmpbuf, sizeof(tmpbuf), "%li\n", atomic_long_read(val)); return simple_read_from_buffer(buf, count, ppos, tmpbuf, len); }
-static ssize_t ima_show_htable_violations(struct file *filp, - char __user *buf, - size_t count, loff_t *ppos) -{ - return ima_show_htable_value(buf, count, ppos, &ima_htable.violations); -} - -static const struct file_operations ima_htable_violations_ops = { - .read = ima_show_htable_violations, - .llseek = generic_file_llseek, -}; - -static ssize_t ima_show_measurements_count(struct file *filp, - char __user *buf, - size_t count, loff_t *ppos) -{ - return ima_show_htable_value(buf, count, ppos, &ima_htable.len); - -} - -static const struct file_operations ima_measurements_count_ops = { - .read = ima_show_measurements_count, +static const struct file_operations ima_htable_value_ops = { + .read = ima_show_htable_value, .llseek = generic_file_llseek, };
@@ -544,7 +530,7 @@ int __init ima_fs_init(void) runtime_measurements_count = securityfs_create_file("runtime_measurements_count", S_IRUSR | S_IRGRP, ima_dir, NULL, - &ima_measurements_count_ops); + &ima_htable_value_ops); if (IS_ERR(runtime_measurements_count)) { ret = PTR_ERR(runtime_measurements_count); goto out; @@ -552,7 +538,7 @@ int __init ima_fs_init(void)
violations = securityfs_create_file("violations", S_IRUSR | S_IRGRP, - ima_dir, NULL, &ima_htable_violations_ops); + ima_dir, NULL, &ima_htable_value_ops); if (IS_ERR(violations)) { ret = PTR_ERR(violations); goto out;
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
This patch introduces the parser of the compact digest list. The format is optimized to store a large quantity of data with the same type. It is the only format supported by the kernel. Digest lists can be uploaded by writing the path to securityfs, as the same as for IMA policies.
A compact list is a set of consecutive data blocks, each consisting of a header and a payload. The header indicates the version of the header, the type of data, type modifiers, the hash algorithm, how many elements and the length of the payload.
COMPACT_KEY identifies public keys used for signature verification of the digest lists; COMPACT_PARSER identifies digests of user space parsers allowed to directly upload parsed digest lists to the kernel; COMPACT_FILE identifies digests of regular files; COMPACT_METADATA identifies digest of file metadata.
Type modifiers indicate attributes of the elements included in the payload. The COMPACT_MOD_IMMUTABLE modifier indicates that a file or metadata are immutable.
This patch also introduces ima_lookup_loaded_digest() and ima_add_digest_data_entry() to search and add digests in the new hash table (ima_digests_htable).
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/ima/Kconfig | 10 ++ security/integrity/ima/Makefile | 1 + security/integrity/ima/ima_digest_list.c | 176 +++++++++++++++++++++++ security/integrity/ima/ima_digest_list.h | 32 +++++ security/integrity/integrity.h | 38 +++++ 5 files changed, 257 insertions(+) create mode 100644 security/integrity/ima/ima_digest_list.c create mode 100644 security/integrity/ima/ima_digest_list.h
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 60a511c6b583..cf130ae38827 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -338,3 +338,13 @@ config IMA_DISABLE_HTABLE default n help This option disables htable to allow measurement of duplicate records. + +config IMA_DIGEST_LIST + bool "Measure and appraise files with digest lists" + depends on IMA + default n + help + This option allows users to load digest lists. If calculated digests + of accessed files are found in one of those lists, no new entries are + added to the measurement list, and access to the file is granted if + appraisal is in enforcing mode. diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 2499f2485c04..fb9744a1c3d4 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -14,6 +14,7 @@ ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o ima-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o ima-$(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) += ima_asymmetric_keys.o ima-$(CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS) += ima_queue_keys.o +ima-$(CONFIG_IMA_DIGEST_LIST) += ima_digest_list.o
ifeq ($(CONFIG_EFI),y) ima-$(CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT) += ima_efi.o diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c new file mode 100644 index 000000000000..0dcc69954887 --- /dev/null +++ b/security/integrity/ima/ima_digest_list.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2017-2019 Huawei Technologies Duesseldorf GmbH + * + * Author: Roberto Sassu roberto.sassu@huawei.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * File: ima_digest_list.c + * Functions to manage digest lists. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/vmalloc.h> +#include <linux/module.h> + +#include "ima.h" +#include "ima_digest_list.h" + +struct ima_h_table ima_digests_htable = { + .len = ATOMIC_LONG_INIT(0), + .queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT +}; + +/************************* + * Get/add/del functions * + *************************/ +struct ima_digest *ima_lookup_digest(u8 *digest, enum hash_algo algo, + enum compact_types type) +{ + struct ima_digest *d = NULL; + int digest_len = hash_digest_size[algo]; + unsigned int key = ima_hash_key(digest); + + rcu_read_lock(); + hlist_for_each_entry_rcu(d, &ima_digests_htable.queue[key], hnext) + if (d->algo == algo && d->type == type && + !memcmp(d->digest, digest, digest_len)) + break; + + rcu_read_unlock(); + return d; +} + +static int ima_add_digest_data_entry(u8 *digest, enum hash_algo algo, + enum compact_types type, u16 modifiers) +{ + struct ima_digest *d; + int digest_len = hash_digest_size[algo]; + unsigned int key = ima_hash_key(digest); + + d = ima_lookup_digest(digest, algo, type); + if (d) { + d->modifiers |= modifiers; + if (d->count < (u16)(~((u16)0))) + d->count++; + return -EEXIST; + } + + d = kmalloc(sizeof(*d) + digest_len, GFP_KERNEL); + if (d == NULL) + return -ENOMEM; + + d->algo = algo; + d->type = type; + d->modifiers = modifiers; + d->count = 1; + + memcpy(d->digest, digest, digest_len); + hlist_add_head_rcu(&d->hnext, &ima_digests_htable.queue[key]); + atomic_long_inc(&ima_digests_htable.len); + return 0; +} + +static void ima_del_digest_data_entry(u8 *digest, enum hash_algo algo, + enum compact_types type) +{ + struct ima_digest *d; + + d = ima_lookup_digest(digest, algo, type); + if (!d) + return; + + if (--d->count > 0) + return; + + hlist_del_rcu(&d->hnext); + atomic_long_dec(&ima_digests_htable.len); +} + +/*********************** + * Compact list parser * + ***********************/ +struct compact_list_hdr { + u8 version; + u8 _reserved; + u16 type; + u16 modifiers; + u16 algo; + u32 count; + u32 datalen; +} __packed; + +int ima_parse_compact_list(loff_t size, void *buf, int op) +{ + u8 *digest; + void *bufp = buf, *bufendp = buf + size; + struct compact_list_hdr *hdr; + size_t digest_len; + int ret = 0, i; + + while (bufp < bufendp) { + if (bufp + sizeof(*hdr) > bufendp) { + pr_err("compact list, invalid data\n"); + return -EINVAL; + } + + hdr = bufp; + + if (hdr->version != 1) { + pr_err("compact list, unsupported version\n"); + return -EINVAL; + } + + if (ima_canonical_fmt) { + hdr->type = le16_to_cpu(hdr->type); + hdr->modifiers = le16_to_cpu(hdr->modifiers); + hdr->algo = le16_to_cpu(hdr->algo); + hdr->count = le32_to_cpu(hdr->count); + hdr->datalen = le32_to_cpu(hdr->datalen); + } + + if (hdr->algo >= HASH_ALGO__LAST) + return -EINVAL; + + digest_len = hash_digest_size[hdr->algo]; + + if (hdr->type >= COMPACT__LAST) { + pr_err("compact list, invalid type %d\n", hdr->type); + return -EINVAL; + } + + bufp += sizeof(*hdr); + + for (i = 0; i < hdr->count; i++) { + if (bufp + digest_len > bufendp) { + pr_err("compact list, invalid data\n"); + return -EINVAL; + } + + digest = bufp; + bufp += digest_len; + + if (op == DIGEST_LIST_OP_ADD) + ret = ima_add_digest_data_entry(digest, + hdr->algo, hdr->type, hdr->modifiers); + else if (op == DIGEST_LIST_OP_DEL) + ima_del_digest_data_entry(digest, hdr->algo, + hdr->type); + if (ret < 0 && ret != -EEXIST) + return ret; + } + + if (i != hdr->count || + bufp != (void *)hdr + sizeof(*hdr) + hdr->datalen) { + pr_err("compact list, invalid data\n"); + return -EINVAL; + } + } + + return bufp - buf; +} diff --git a/security/integrity/ima/ima_digest_list.h b/security/integrity/ima/ima_digest_list.h new file mode 100644 index 000000000000..ac6b0ee0aec6 --- /dev/null +++ b/security/integrity/ima/ima_digest_list.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2017-2019 Huawei Technologies Duesseldorf GmbH + * + * Author: Roberto Sassu roberto.sassu@huawei.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * File: ima_digest_list.h + * Header of ima_digest_list.c + */ + +#ifndef __LINUX_IMA_DIGEST_LIST_H +#define __LINUX_IMA_DIGEST_LIST_H + +#define DIGEST_LIST_OP_ADD 0 +#define DIGEST_LIST_OP_DEL 1 + +#ifdef CONFIG_IMA_DIGEST_LIST +extern struct ima_h_table ima_digests_htable; + +int ima_parse_compact_list(loff_t size, void *buf, int op); +#else +static inline int ima_parse_compact_list(loff_t size, void *buf, int op) +{ + return -EOPNOTSUPP; +} +#endif /*CONFIG_IMA_DIGEST_LIST*/ +#endif /*LINUX_IMA_DIGEST_LIST_H*/ diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 7167a6e99bdc..5870b7459cef 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -18,6 +18,8 @@ #include <crypto/hash.h> #include <linux/key.h> #include <linux/audit.h> +#include <crypto/sha2.h> +#include <linux/hash_info.h>
/* iint action cache flags */ #define IMA_MEASURE 0x00000001 @@ -173,6 +175,42 @@ struct integrity_iint_cache { struct ima_digest_data *ima_hash; };
+enum compact_types { COMPACT_KEY, COMPACT_PARSER, COMPACT_FILE, + COMPACT_METADATA, COMPACT__LAST }; +enum compact_modifiers { COMPACT_MOD_IMMUTABLE, COMPACT_MOD__LAST }; + +struct ima_digest { + struct hlist_node hnext; + enum hash_algo algo; + enum compact_types type; + u16 modifiers; + u16 count; + u8 digest[0]; +}; + +static inline bool ima_digest_is_immutable(struct ima_digest *digest) +{ + return (digest->modifiers & (1 << COMPACT_MOD_IMMUTABLE)); +} + +#ifdef CONFIG_IMA_DIGEST_LIST +struct ima_digest *ima_lookup_digest(u8 *digest, enum hash_algo algo, + enum compact_types type); +struct ima_digest *ima_digest_allow(struct ima_digest *digest, int action); +#else +static inline struct ima_digest *ima_lookup_digest(u8 *digest, + enum hash_algo algo, + enum compact_types type) +{ + return NULL; +} +static inline struct ima_digest *ima_digest_allow(struct ima_digest *digest, + int action) +{ + return NULL; +} +#endif + /* rbtree tree calls to lookup, insert, delete * integrity data associated with an inode. */
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
Loading a digest list affects the behavior of IMA for subsequent operations. For example, if the digest of a file is found in a loaded digest list, the file won't be added to the measurement list (with PCR 11). If an administrator loaded the digest list before the IMA policy, he could hide from verifiers the fact that files in that digest list were accessed.
To avoid this situation, this patch prevents usage of digest lists for an IMA submodule if that submodule didn't process it. If a digest list wasn't measured, the digest of measured files will not be searched in the digest list and regular measurement will be performed. The same mechanism applies for appraisal.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/ima/ima.h | 1 + security/integrity/ima/ima_digest_list.c | 48 ++++++++++++++++++++++++ security/integrity/ima/ima_digest_list.h | 4 ++ security/integrity/ima/ima_main.c | 4 ++ 4 files changed, 57 insertions(+)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 8b869e2951d8..6610269051c3 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -57,6 +57,7 @@ extern int ima_extra_slots __ro_after_init; extern int ima_appraise; extern struct tpm_chip *ima_tpm_chip; extern const char boot_aggregate_name[]; +extern int ima_digest_list_actions;
/* IMA event related data */ struct ima_event_data { diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index 0dcc69954887..901990c8dfa8 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -113,6 +113,9 @@ int ima_parse_compact_list(loff_t size, void *buf, int op) size_t digest_len; int ret = 0, i;
+ if (!(ima_digest_list_actions & ima_policy_flag)) + return -EACCES; + while (bufp < bufendp) { if (bufp + sizeof(*hdr) > bufendp) { pr_err("compact list, invalid data\n"); @@ -174,3 +177,48 @@ int ima_parse_compact_list(loff_t size, void *buf, int op)
return bufp - buf; } + +/*************************** + * Digest list usage check * + ***************************/ +void ima_check_measured_appraised(struct file *file) +{ + struct integrity_iint_cache *iint; + + if (!ima_digest_list_actions) + return; + + iint = integrity_iint_find(file_inode(file)); + if (!iint) { + pr_err("%s not processed, disabling digest lists lookup\n", + file_dentry(file)->d_name.name); + ima_digest_list_actions = 0; + return; + } + + mutex_lock(&iint->mutex); + if ((ima_digest_list_actions & IMA_MEASURE) && + !(iint->flags & IMA_MEASURED)) { + pr_err("%s not measured, disabling digest lists lookup " + "for measurement\n", file_dentry(file)->d_name.name); + ima_digest_list_actions &= ~IMA_MEASURE; + } + + if ((ima_digest_list_actions & IMA_APPRAISE) && + (!(iint->flags & IMA_APPRAISED) || + !test_bit(IMA_DIGSIG, &iint->atomic_flags))) { + pr_err("%s not appraised, disabling digest lists lookup " + "for appraisal\n", file_dentry(file)->d_name.name); + ima_digest_list_actions &= ~IMA_APPRAISE; + } + + mutex_unlock(&iint->mutex); +} + +struct ima_digest *ima_digest_allow(struct ima_digest *digest, int action) +{ + if (!(ima_digest_list_actions & action)) + return NULL; + + return digest; +} diff --git a/security/integrity/ima/ima_digest_list.h b/security/integrity/ima/ima_digest_list.h index ac6b0ee0aec6..5bd2388ff95e 100644 --- a/security/integrity/ima/ima_digest_list.h +++ b/security/integrity/ima/ima_digest_list.h @@ -23,10 +23,14 @@ extern struct ima_h_table ima_digests_htable;
int ima_parse_compact_list(loff_t size, void *buf, int op); +void ima_check_measured_appraised(struct file *file); #else static inline int ima_parse_compact_list(loff_t size, void *buf, int op) { return -EOPNOTSUPP; } +static inline void ima_check_measured_appraised(struct file *file) +{ +} #endif /*CONFIG_IMA_DIGEST_LIST*/ #endif /*LINUX_IMA_DIGEST_LIST_H*/ diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 847be55912eb..31c109d9fae5 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -36,6 +36,10 @@ int ima_appraise; #endif
int __ro_after_init ima_hash_algo = HASH_ALGO_SHA1; + +/* Actions (measure/appraisal) for which digest lists can be used */ +int ima_digest_list_actions; + static int hash_setup_done;
static struct notifier_block ima_lsm_policy_notifier = {
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
This patch introduces three new files in the securityfs filesystem. digest_list_data: loads a digest list from the specified path and adds the digests to the hash table; digest_list_data_del: does the same but removes the digests from the hash table; digests_count: shows the current number of digests stored in the hash table.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- include/linux/kernel_read_file.h | 1 + security/integrity/ima/ima_fs.c | 48 ++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+)
diff --git a/include/linux/kernel_read_file.h b/include/linux/kernel_read_file.h index 90451e2e12bd..a7724cd7c870 100644 --- a/include/linux/kernel_read_file.h +++ b/include/linux/kernel_read_file.h @@ -13,6 +13,7 @@ id(KEXEC_IMAGE, kexec-image) \ id(KEXEC_INITRAMFS, kexec-initramfs) \ id(POLICY, security-policy) \ + id(DIGEST_LIST, digest-list) \ id(X509_CERTIFICATE, x509-certificate) \ id(MAX_ID, )
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 5d28e163db50..2998c3dfbc30 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -24,6 +24,7 @@ #include <linux/file.h>
#include "ima.h" +#include "ima_digest_list.h"
static DEFINE_MUTEX(ima_write_mutex);
@@ -34,6 +35,9 @@ static struct dentry *ascii_runtime_measurements; static struct dentry *runtime_measurements_count; static struct dentry *violations; static struct dentry *ima_policy; +static struct dentry *digests_count; +static struct dentry *digest_list_data; +static struct dentry *digest_list_data_del;
bool ima_canonical_fmt; static int __init default_canonical_fmt_setup(char *str) @@ -58,6 +62,10 @@ static ssize_t ima_show_htable_value(struct file *filp, char __user *buf, val = &ima_htable.violations; else if (filp->f_path.dentry == runtime_measurements_count) val = &ima_htable.len; +#ifdef CONFIG_IMA_DIGEST_LIST + else if (filp->f_path.dentry == digests_count) + val = &ima_digests_htable.len; +#endif
len = scnprintf(tmpbuf, sizeof(tmpbuf), "%li\n", atomic_long_read(val)); return simple_read_from_buffer(buf, count, ppos, tmpbuf, len); @@ -273,6 +281,7 @@ static ssize_t ima_read_file(char *path, struct dentry *dentry) size_t size; struct file *file; enum kernel_read_file_id file_id = READING_POLICY; + int op = DIGEST_LIST_OP_ADD; int rc, pathlen = strlen(path);
char *p; @@ -281,6 +290,9 @@ static ssize_t ima_read_file(char *path, struct dentry *dentry) datap = path; strsep(&datap, "\n");
+ if (dentry == digest_list_data || dentry == digest_list_data_del) + file_id = READING_DIGEST_LIST; + file = filp_open(path, O_RDONLY, 0); if (IS_ERR(file)) { pr_err("Unable to open file: %s (%ld)", path, PTR_ERR(file)); @@ -305,6 +317,18 @@ static ssize_t ima_read_file(char *path, struct dentry *dentry)
pr_debug("rule: %s\n", p); rc = ima_parse_add_rule(p); + } else if (dentry == digest_list_data || + dentry == digest_list_data_del) { + /* + * Disable usage of digest lists if not measured + * or appraised. + */ + ima_check_measured_appraised(file); + + if (dentry == digest_list_data_del) + op = DIGEST_LIST_OP_DEL; + + rc = ima_parse_compact_list(size, data, op); }
if (rc < 0) @@ -382,6 +406,7 @@ static ssize_t ima_write_data(struct file *file, const char __user *buf,
enum ima_fs_flags { IMA_POLICY_BUSY, + IMA_DIGEST_LIST_DATA_BUSY, IMA_FS_BUSY, };
@@ -391,6 +416,8 @@ static enum ima_fs_flags ima_get_dentry_flag(struct dentry *dentry)
if (dentry == ima_policy) flag = IMA_POLICY_BUSY; + else if (dentry == digest_list_data || dentry == digest_list_data_del) + flag = IMA_DIGEST_LIST_DATA_BUSY;
return flag; } @@ -551,10 +578,31 @@ int __init ima_fs_init(void) ret = PTR_ERR(ima_policy); goto out; } +#ifdef CONFIG_IMA_DIGEST_LIST + digests_count = securityfs_create_file("digests_count", + S_IRUSR | S_IRGRP, ima_dir, + NULL, &ima_htable_value_ops); + if (IS_ERR(digests_count)) + goto out;
+ digest_list_data = securityfs_create_file("digest_list_data", S_IWUSR, + ima_dir, NULL, + &ima_data_upload_ops); + if (IS_ERR(digest_list_data)) + goto out; + + digest_list_data_del = securityfs_create_file("digest_list_data_del", + S_IWUSR, ima_dir, NULL, + &ima_data_upload_ops); + if (IS_ERR(digest_list_data_del)) + goto out; +#endif return 0; out: securityfs_remove(ima_policy); + securityfs_remove(digest_list_data_del); + securityfs_remove(digest_list_data); + securityfs_remove(digests_count); securityfs_remove(violations); securityfs_remove(runtime_measurements_count); securityfs_remove(ascii_runtime_measurements);
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
This patch introduces a new hook called DIGEST_LIST_CHECK to measure and appraise digest lists in addition to executables and shared libraries, without including the FILE_CHECK hook in the IMA policy.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/ima/ima.h | 2 ++ security/integrity/ima/ima_main.c | 3 ++- security/integrity/ima/ima_policy.c | 8 ++++++++ 3 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 6610269051c3..6df0cd6cdd6b 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -204,6 +204,7 @@ static inline unsigned int ima_hash_key(u8 *digest) hook(KEY_CHECK, key) \ hook(CRITICAL_DATA, critical_data) \ hook(SETXATTR_CHECK, setxattr_check) \ + hook(DIGEST_LIST_CHECK, digest_list) \ hook(MAX_CHECK, none)
#define __ima_hook_enumify(ENUM, str) ENUM, @@ -311,6 +312,7 @@ int ima_policy_show(struct seq_file *m, void *v); #define IMA_APPRAISE_FIRMWARE 0x10 #define IMA_APPRAISE_POLICY 0x20 #define IMA_APPRAISE_KEXEC 0x40 +#define IMA_APPRAISE_DIGEST_LIST 0x80
#ifdef CONFIG_IMA_APPRAISE int ima_check_blacklist(struct integrity_iint_cache *iint, diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 31c109d9fae5..20cd855c0a96 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -828,7 +828,8 @@ const int read_idmap[READING_MAX_ID] = { [READING_MODULE] = MODULE_CHECK, [READING_KEXEC_IMAGE] = KEXEC_KERNEL_CHECK, [READING_KEXEC_INITRAMFS] = KEXEC_INITRAMFS_CHECK, - [READING_POLICY] = POLICY_CHECK + [READING_POLICY] = POLICY_CHECK, + [READING_DIGEST_LIST] = DIGEST_LIST_CHECK };
/** diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 3ca8b7348c2e..cae4d1cebfd7 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -188,6 +188,7 @@ static struct ima_rule_entry default_measurement_rules[] __ro_after_init = { {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, {.action = MEASURE, .func = POLICY_CHECK, .flags = IMA_FUNC}, + {.action = MEASURE, .func = DIGEST_LIST_CHECK, .flags = IMA_FUNC}, };
static struct ima_rule_entry default_appraise_rules[] __ro_after_init = { @@ -247,6 +248,8 @@ static struct ima_rule_entry secure_boot_rules[] __ro_after_init = { .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, {.action = APPRAISE, .func = POLICY_CHECK, .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, + {.action = APPRAISE, .func = DIGEST_LIST_CHECK, + .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, };
static struct ima_rule_entry critical_data_rules[] __ro_after_init = { @@ -855,6 +858,8 @@ static int ima_appraise_flag(enum ima_hooks func) return IMA_APPRAISE_POLICY; else if (func == KEXEC_KERNEL_CHECK) return IMA_APPRAISE_KEXEC; + else if (func == DIGEST_LIST_CHECK) + return IMA_APPRAISE_DIGEST_LIST; return 0; }
@@ -1273,6 +1278,7 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) case POST_SETATTR: case FIRMWARE_CHECK: case POLICY_CHECK: + case DIGEST_LIST_CHECK: if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC | IMA_UID | IMA_FOWNER | IMA_FSUUID | IMA_INMASK | IMA_EUID | IMA_PCR | @@ -1529,6 +1535,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) entry->func = CRITICAL_DATA; else if (strcmp(args[0].from, "SETXATTR_CHECK") == 0) entry->func = SETXATTR_CHECK; + else if (strcmp(args[0].from, "DIGEST_LIST_CHECK") == 0) + entry->func = DIGEST_LIST_CHECK; else result = -EINVAL; if (!result)
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
Digest lists should be uploaded to IMA as soon as possible, otherwise file digests would appear in the measurement list or access would be denied if appraisal is in enforcing mode.
This patch adds a call to ima_load_digest_lists() in integrity_load_keys(), so that the function is executed when rootfs becomes available, before files are accessed.
ima_load_digest_lists() iterates in the directory specified as value of CONFIG_IMA_DIGEST_LISTS_DIR and uploads all digest lists to the kernel.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/iint.c | 2 + security/integrity/ima/Kconfig | 8 ++ security/integrity/ima/ima_digest_list.c | 95 ++++++++++++++++++++++++ security/integrity/integrity.h | 4 + 4 files changed, 109 insertions(+)
diff --git a/security/integrity/iint.c b/security/integrity/iint.c index c73858e8c6d5..ecb9bd378a04 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -204,6 +204,8 @@ void __init integrity_load_keys(void)
if (!IS_ENABLED(CONFIG_IMA_LOAD_X509)) evm_load_x509(); + + ima_load_digest_lists(); }
static int __init integrity_fs_init(void) diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index cf130ae38827..73c582d97737 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -348,3 +348,11 @@ config IMA_DIGEST_LIST of accessed files are found in one of those lists, no new entries are added to the measurement list, and access to the file is granted if appraisal is in enforcing mode. + +config IMA_DIGEST_LISTS_DIR + string "Path of the directory containing digest lists" + depends on IMA_DIGEST_LIST + default "/etc/ima/digest_lists" + help + This option defines the path of the directory containing digest + lists. diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index 901990c8dfa8..91c70562aa86 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -17,6 +17,9 @@
#include <linux/vmalloc.h> #include <linux/module.h> +#include <linux/file.h> +#include <linux/namei.h> +#include <linux/xattr.h>
#include "ima.h" #include "ima_digest_list.h" @@ -222,3 +225,95 @@ struct ima_digest *ima_digest_allow(struct ima_digest *digest, int action)
return digest; } + +/************************************** + * Digest list loading at kernel init * + **************************************/ +struct readdir_callback { + struct dir_context ctx; + struct path *path; +}; + +static int __init load_digest_list(struct dir_context *__ctx, const char *name, + int namelen, loff_t offset, u64 ino, + unsigned int d_type) +{ + struct readdir_callback *ctx = container_of(__ctx, typeof(*ctx), ctx); + struct path *dir = ctx->path; + struct dentry *dentry; + struct file *file; + u8 *xattr_value = NULL; + void *datap = NULL; + loff_t size; + int ret; + + if (!strcmp(name, ".") || !strcmp(name, "..")) + return 0; + + dentry = lookup_one_len(name, dir->dentry, strlen(name)); + if (IS_ERR(dentry)) + return 0; + + size = vfs_getxattr(&nop_mnt_idmap, dentry, XATTR_NAME_EVM, NULL, 0); + if (size < 0) { + size = vfs_getxattr_alloc(&nop_mnt_idmap, dentry, XATTR_NAME_IMA, + (char **)&xattr_value, 0, GFP_NOFS); + if (size < 0 || xattr_value[0] != EVM_IMA_XATTR_DIGSIG) + goto out; + } + + file = file_open_root(dir, name, O_RDONLY, 0); + if (IS_ERR(file)) { + pr_err("Unable to open file: %s (%ld)", name, PTR_ERR(file)); + goto out; + } + + ret = kernel_read_file(file, 0, &datap, INT_MAX, NULL, + READING_DIGEST_LIST); + if (ret < 0) { + pr_err("Unable to read file: %s (%d)", name, ret); + goto out_fput; + } + + size = ret; + + ima_check_measured_appraised(file); + + ret = ima_parse_compact_list(size, datap, DIGEST_LIST_OP_ADD); + if (ret < 0) + pr_err("Unable to parse file: %s (%d)", name, ret); + + vfree(datap); +out_fput: + fput(file); +out: + kfree(xattr_value); + return 0; +} + +void __init ima_load_digest_lists(void) +{ + struct path path; + struct file *file; + int ret; + struct readdir_callback buf = { + .ctx.actor = load_digest_list, + }; + + if (!(ima_digest_list_actions & ima_policy_flag)) + return; + + ret = kern_path(CONFIG_IMA_DIGEST_LISTS_DIR, 0, &path); + if (ret) + return; + + file = dentry_open(&path, O_RDONLY, current_cred()); + if (IS_ERR(file)) + goto out; + + buf.path = &path; + iterate_dir(file, &buf.ctx); + fput(file); +out: + path_put(&path); +} diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 5870b7459cef..9b9c669634a9 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -197,6 +197,7 @@ static inline bool ima_digest_is_immutable(struct ima_digest *digest) struct ima_digest *ima_lookup_digest(u8 *digest, enum hash_algo algo, enum compact_types type); struct ima_digest *ima_digest_allow(struct ima_digest *digest, int action); +void __init ima_load_digest_lists(void); #else static inline struct ima_digest *ima_lookup_digest(u8 *digest, enum hash_algo algo, @@ -209,6 +210,9 @@ static inline struct ima_digest *ima_digest_allow(struct ima_digest *digest, { return NULL; } +static inline void ima_load_digest_lists(void) +{ +} #endif
/* rbtree tree calls to lookup, insert, delete
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
IMA-Measure creates a new measurement entry every time a file is measured, unless the same entry is already in the measurement list.
This patch introduces a new type of measurement list, recognizable by the PCR number specified with the new ima_digest_list_pcr= kernel option. This type of measurement list includes measurements of digest lists and files not found in those lists.
The benefit of this patch is the availability of a predictable PCR that can be used to seal data or TPM keys to the OS software. Unlike standard measurements, digest list measurements only indicate that files with a digest in those lists could have been accessed, but not if and when. With standard measurements, however, the chosen PCR is unlikely predictable.
Both standard and digest list measurements can be generated at the same time by adding '+' as a prefix to the value of ima_digest_list_pcr= (example: with ima_digest_list_pcr=+11, IMA generates standard measurements with PCR 10 and digest list measurements with PCR 11).
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- .../admin-guide/kernel-parameters.txt | 9 ++++ security/integrity/ima/ima.h | 9 ++-- security/integrity/ima/ima_api.c | 42 +++++++++++++++---- security/integrity/ima/ima_digest_list.c | 25 +++++++++++ security/integrity/ima/ima_init.c | 2 +- security/integrity/ima/ima_main.c | 18 +++++++- security/integrity/ima/ima_policy.c | 3 +- security/integrity/integrity.h | 1 + 8 files changed, 95 insertions(+), 14 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index ee50a3b91d73..b1e74fa1431a 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1953,6 +1953,15 @@ Use the canonical format for the binary runtime measurements, instead of host native format.
+ ima_digest_list_pcr= + [IMA] + Specify which PCR is extended when file digests are + not found in the loaded digest lists. If '+' is not + specified, no measurement entry is created if the + digest is found. Otherwise, IMA creates also entries + with PCR 10, according to the existing behavior. + Format: { [+]<unsigned int> } + ima_hash= [IMA] Format: { md5 | sha1 | rmd160 | sha256 | sha384 | sha512 | ... } diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 6df0cd6cdd6b..8030bd78e4c2 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -56,6 +56,8 @@ extern int ima_hash_algo_idx __ro_after_init; extern int ima_extra_slots __ro_after_init; extern int ima_appraise; extern struct tpm_chip *ima_tpm_chip; +extern int ima_digest_list_pcr; +extern bool ima_plus_standard_pcr; extern const char boot_aggregate_name[]; extern int ima_digest_list_actions;
@@ -270,7 +272,8 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, int xattr_len, const struct modsig *modsig, int pcr, - struct ima_template_desc *template_desc); + struct ima_template_desc *template_desc, + struct ima_digest *digest); int process_buffer_measurement(struct mnt_idmap *idmap, struct inode *inode, const void *buf, int size, const char *eventname, enum ima_hooks func, @@ -282,8 +285,8 @@ int ima_alloc_init_template(struct ima_event_data *event_data, struct ima_template_entry **entry, struct ima_template_desc *template_desc); int ima_store_template(struct ima_template_entry *entry, int violation, - struct inode *inode, - const unsigned char *filename, int pcr); + struct inode *inode, const unsigned char *filename, + int pcr, struct ima_digest *digest); void ima_free_template_entry(struct ima_template_entry *entry); const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index d3662f4acadc..c5b92b99a4b4 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -102,11 +102,13 @@ int ima_alloc_init_template(struct ima_event_data *event_data, */ int ima_store_template(struct ima_template_entry *entry, int violation, struct inode *inode, - const unsigned char *filename, int pcr) + const unsigned char *filename, int pcr, + struct ima_digest *digest) { static const char op[] = "add_template_measure"; static const char audit_cause[] = "hashing_error"; char *template_name = entry->template_desc->name; + struct ima_template_entry *duplicated_entry = NULL; int result;
if (!violation) { @@ -119,8 +121,26 @@ int ima_store_template(struct ima_template_entry *entry, return result; } } + + if (ima_plus_standard_pcr && !digest) { + duplicated_entry = kmemdup(entry, + sizeof(*entry) + entry->template_desc->num_fields * + sizeof(struct ima_field_data), GFP_KERNEL); + if (duplicated_entry) + duplicated_entry->pcr = ima_digest_list_pcr; + } else if (!ima_plus_standard_pcr && ima_digest_list_pcr >= 0) { + pcr = ima_digest_list_pcr; + } + entry->pcr = pcr; result = ima_add_template_entry(entry, violation, op, inode, filename); + if (!result && duplicated_entry) { + result = ima_add_template_entry(duplicated_entry, violation, op, + inode, filename); + if (result < 0) + kfree(duplicated_entry); + } + return result; }
@@ -152,8 +172,8 @@ void ima_add_violation(struct file *file, const unsigned char *filename, result = -ENOMEM; goto err_out; } - result = ima_store_template(entry, violation, inode, - filename, CONFIG_IMA_MEASURE_PCR_IDX); + result = ima_store_template(entry, violation, inode, filename, + CONFIG_IMA_MEASURE_PCR_IDX, NULL); if (result < 0) ima_free_template_entry(entry); err_out: @@ -341,13 +361,14 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, int xattr_len, const struct modsig *modsig, int pcr, - struct ima_template_desc *template_desc) + struct ima_template_desc *template_desc, + struct ima_digest *digest) { static const char op[] = "add_template_measure"; static const char audit_cause[] = "ENOMEM"; int result = -ENOMEM; struct inode *inode = file_inode(file); - struct ima_template_entry *entry; + struct ima_template_entry *entry = NULL; struct ima_event_data event_data = { .iint = iint, .file = file, .filename = filename, @@ -365,6 +386,11 @@ void ima_store_measurement(struct integrity_iint_cache *iint, if (iint->measured_pcrs & (0x1 << pcr) && !modsig) return;
+ if (digest && !ima_plus_standard_pcr && ima_digest_list_pcr >= 0) { + result = -EEXIST; + goto out; + } + result = ima_alloc_init_template(&event_data, &entry, template_desc); if (result < 0) { integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, @@ -372,12 +398,14 @@ void ima_store_measurement(struct integrity_iint_cache *iint, return; }
- result = ima_store_template(entry, violation, inode, filename, pcr); + result = ima_store_template(entry, violation, inode, filename, pcr, + digest); +out: if ((!result || result == -EEXIST) && !(file->f_flags & O_DIRECT)) { iint->flags |= IMA_MEASURED; iint->measured_pcrs |= (0x1 << pcr); } - if (result < 0) + if (result < 0 && entry) ima_free_template_entry(entry); }
diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index 91c70562aa86..42b2306e43cc 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -29,6 +29,31 @@ struct ima_h_table ima_digests_htable = { .queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT };
+static int __init digest_list_pcr_setup(char *str) +{ + int pcr, ret; + + ret = kstrtouint(str, 10, &pcr); + if (ret) { + pr_err("Invalid PCR number %s\n", str); + return 1; + } + + if (pcr == CONFIG_IMA_MEASURE_PCR_IDX) { + pr_err("Default PCR cannot be used for digest lists\n"); + return 1; + } + + ima_digest_list_pcr = pcr; + ima_digest_list_actions |= IMA_MEASURE; + + if (*str == '+') + ima_plus_standard_pcr = true; + + return 1; +} +__setup("ima_digest_list_pcr=", digest_list_pcr_setup); + /************************* * Get/add/del functions * *************************/ diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 63979aefc95f..be2aab51c220 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -85,7 +85,7 @@ static int __init ima_add_boot_aggregate(void)
result = ima_store_template(entry, violation, NULL, boot_aggregate_name, - CONFIG_IMA_MEASURE_PCR_IDX); + CONFIG_IMA_MEASURE_PCR_IDX, NULL); if (result < 0) { ima_free_template_entry(entry); audit_cause = "store_entry"; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 20cd855c0a96..b3c8e9c21f5f 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -28,6 +28,7 @@ #include <linux/fs.h>
#include "ima.h" +#include "ima_digest_list.h"
#ifdef CONFIG_IMA_APPRAISE int ima_appraise = IMA_APPRAISE_ENFORCE; @@ -39,6 +40,10 @@ int __ro_after_init ima_hash_algo = HASH_ALGO_SHA1;
/* Actions (measure/appraisal) for which digest lists can be used */ int ima_digest_list_actions; +/* PCR used for digest list measurements */ +int ima_digest_list_pcr = -1; +/* Flag to include standard measurement if digest list PCR is specified */ +bool ima_plus_standard_pcr;
static int hash_setup_done;
@@ -180,6 +185,8 @@ static enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_v || sig->hash_algo >= HASH_ALGO__LAST) return ima_hash_algo; return sig->hash_algo; + case EVM_IMA_XATTR_DIGEST_LIST: + fallthrough; case IMA_XATTR_DIGEST_NG: /* first byte contains algorithm id */ ret = xattr_value->data[0]; @@ -272,6 +279,7 @@ static int process_measurement(struct file *file, const struct cred *cred, const char *pathname = NULL; int rc = 0, action, must_appraise = 0; int pcr = CONFIG_IMA_MEASURE_PCR_IDX; + struct ima_digest *found_digest; struct evm_ima_xattr_data *xattr_value = NULL; struct modsig *modsig = NULL; int xattr_len = 0; @@ -406,10 +414,16 @@ 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);
+ found_digest = ima_lookup_digest(iint->ima_hash->digest, hash_algo, + COMPACT_FILE); + if (action & IMA_MEASURE) ima_store_measurement(iint, file, pathname, xattr_value, xattr_len, modsig, pcr, - template_desc); + template_desc, + ima_digest_allow(found_digest, + IMA_MEASURE)); + if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) { rc = ima_check_blacklist(iint, modsig, pcr); if (rc != -EPERM) { @@ -1059,7 +1073,7 @@ int process_buffer_measurement(struct mnt_idmap *idmap, goto out; }
- ret = ima_store_template(entry, violation, NULL, event_data.buf, pcr); + ret = ima_store_template(entry, violation, NULL, event_data.buf, pcr, NULL); if (ret < 0) { audit_cause = "store_entry"; ima_free_template_entry(entry); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index cae4d1cebfd7..0c7f135bb556 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -1862,7 +1862,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) ima_log_string(ab, "pcr", args[0].from);
result = kstrtoint(args[0].from, 10, &entry->pcr); - if (result || INVALID_PCR(entry->pcr)) + if (result || INVALID_PCR(entry->pcr) || + entry->pcr == ima_digest_list_pcr) result = -EINVAL; else entry->flags |= IMA_PCR; diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 9b9c669634a9..538090a5f259 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -82,6 +82,7 @@ enum evm_ima_xattr_type { IMA_XATTR_DIGEST_NG, EVM_XATTR_PORTABLE_DIGSIG, IMA_VERITY_DIGSIG, + EVM_IMA_XATTR_DIGEST_LIST, IMA_XATTR_LAST };
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
IMA-Appraise grants access to files with a valid signature or with actual file digest equal to the digest included in security.ima.
This patch adds support for appraisal based on digest lists. Instead of using the reference value from security.ima, this patch checks if the calculated file digest is included in the uploaded digest lists.
This functionality must be explicitly enabled by providing one of the following values for the ima_appraise_digest_list= kernel option:
- digest: this mode enables appraisal verification with digest lists until EVM is initialized; after that, EVM verification must be successful even if the file digest is found in a digest list;
- digest-nometadata: this mode enables appraisal verification with digest lists even after EVM has been initialized; files without security.evm are allowed if the digest of the content is found in the digest list, and security.evm is created with current values of xattrs (trust at first use); all files created in this way will have the new security.ima type EVM_IMA_XATTR_DIGEST_LIST; they can be accessed later only if this mode has been selected.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- .../admin-guide/kernel-parameters.txt | 9 ++ security/integrity/ima/ima.h | 6 +- security/integrity/ima/ima_appraise.c | 85 +++++++++++++++++-- security/integrity/ima/ima_main.c | 6 +- security/integrity/integrity.h | 1 + 5 files changed, 97 insertions(+), 10 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index b1e74fa1431a..da42ecb03667 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1945,6 +1945,15 @@ "enforce-evm" | "log-evm" } default: "enforce"
+ ima_appraise_digest_list= [IMA] + Format: { "digest" | "digest-nometadata" } + + digest: enables appraisal of files with digest lists + until EVM is initialized. + + digest-nometadata: enables appraisal of files with + digest lists even after EVM is initialized. + ima_appraise_tcb [IMA] Deprecated. Use ima_policy= instead. The builtin appraise policy appraises all files owned by uid=0. diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 8030bd78e4c2..124b4e78a784 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -324,7 +324,8 @@ int ima_appraise_measurement(enum ima_hooks func, struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, - int xattr_len, const struct modsig *modsig); + int xattr_len, const struct modsig *modsig, + struct ima_digest *found_digest); int ima_must_appraise(struct mnt_idmap *idmap, struct inode *inode, int mask, enum ima_hooks func); void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file); @@ -344,7 +345,8 @@ static inline int ima_appraise_measurement(enum ima_hooks func, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, int xattr_len, - const struct modsig *modsig) + const struct modsig *modsig, + struct ima_digest *found_digest) { return INTEGRITY_UNKNOWN; } diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 274a0fec59f5..785849243048 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -18,6 +18,7 @@ #include <uapi/linux/fsverity.h>
#include "ima.h" +#include "ima_digest_list.h"
static bool ima_appraise_req_evm __ro_after_init; #ifdef CONFIG_IMA_APPRAISE_BOOTPARAM @@ -60,6 +61,22 @@ void __init ima_appraise_parse_cmdline(void) } #endif
+static bool ima_appraise_no_metadata __ro_after_init; +#ifdef CONFIG_IMA_DIGEST_LIST +static int __init appraise_digest_list_setup(char *str) +{ + if (!strncmp(str, "digest", 6)) { + ima_digest_list_actions |= IMA_APPRAISE; + + if (!strcmp(str + 6, "-nometadata")) + ima_appraise_no_metadata = true; + } + + return 1; +} +__setup("ima_appraise_digest_list=", appraise_digest_list_setup); +#endif + /* * is_ima_appraise_enabled - return appraise status * @@ -101,6 +118,9 @@ static int ima_fix_xattr(struct dentry *dentry, } else { offset = 0; iint->ima_hash->xattr.ng.type = IMA_XATTR_DIGEST_NG; + if (test_bit(IMA_DIGEST_LIST, &iint->atomic_flags)) + iint->ima_hash->xattr.ng.type = + EVM_IMA_XATTR_DIGEST_LIST; iint->ima_hash->xattr.ng.algo = algo; } rc = __vfs_setxattr_noperm(&nop_mnt_idmap, dentry, XATTR_NAME_IMA, @@ -271,20 +291,35 @@ static int calc_file_id_hash(enum evm_ima_xattr_type type, */ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint, struct evm_ima_xattr_data *xattr_value, int xattr_len, - enum integrity_status *status, const char **cause) + enum integrity_status *status, const char **cause, + struct ima_digest *found_digest) { struct ima_max_digest_data hash; struct signature_v2_hdr *sig; int rc = -EINVAL, hash_start = 0; int mask;
+ if (found_digest && *status != INTEGRITY_PASS && + *status != INTEGRITY_PASS_IMMUTABLE) + set_bit(IMA_DIGEST_LIST, &iint->atomic_flags); + switch (xattr_value->type) { + case EVM_IMA_XATTR_DIGEST_LIST: + set_bit(IMA_DIGEST_LIST, &iint->atomic_flags); + + if (!ima_appraise_no_metadata) { + *cause = "IMA-xattr-untrusted"; + *status = INTEGRITY_FAIL; + break; + } + fallthrough; case IMA_XATTR_DIGEST_NG: /* first byte contains algorithm id */ hash_start = 1; fallthrough; case IMA_XATTR_DIGEST: - if (*status != INTEGRITY_PASS_IMMUTABLE) { + if (*status != INTEGRITY_PASS_IMMUTABLE && + (!found_digest || !ima_digest_is_immutable(found_digest))) { if (iint->flags & IMA_DIGSIG_REQUIRED) { if (iint->flags & IMA_VERITY_REQUIRED) *cause = "verity-signature-required"; @@ -472,14 +507,16 @@ int ima_appraise_measurement(enum ima_hooks func, struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, - int xattr_len, const struct modsig *modsig) + int xattr_len, const struct modsig *modsig, + struct ima_digest *found_digest) { static const char op[] = "appraise_data"; const char *cause = "unknown"; struct dentry *dentry = file_dentry(file); struct inode *inode = d_backing_inode(dentry); enum integrity_status status = INTEGRITY_UNKNOWN; - int rc = xattr_len; + int rc = xattr_len, rc_evm; + char _buf[sizeof(struct evm_ima_xattr_data) + 1 + SHA512_DIGEST_SIZE]; bool try_modsig = iint->flags & IMA_MODSIG_ALLOWED && modsig;
/* If not appraising a modsig, we need an xattr. */ @@ -490,6 +527,25 @@ int ima_appraise_measurement(enum ima_hooks func, xattr_len == sizeof(struct signature_v2_hdr)) rc = -ENODATA;
+ if (rc == -ENODATA && found_digest && + !(file->f_mode & FMODE_CREATED)) { + struct evm_ima_xattr_data *xattr_data = NULL; + + rc_evm = vfs_getxattr_alloc(&nop_mnt_idmap, dentry, XATTR_NAME_EVM, + (char **)&xattr_data, 0, GFP_NOFS); + if (rc_evm > 0) { + kfree(xattr_data); + } else { + xattr_value = (struct evm_ima_xattr_data *)_buf; + xattr_value->type = IMA_XATTR_DIGEST_NG; + xattr_value->data[0] = found_digest->algo; + memcpy(&xattr_value->data[1], found_digest->digest, + hash_digest_size[found_digest->algo]); + xattr_len = hash_digest_size[found_digest->algo] + 2; + rc = xattr_len; + } + } + /* If reading the xattr failed and there's no modsig, error out. */ if (rc <= 0 && !try_modsig) { if (rc && rc != -ENODATA) @@ -522,7 +578,7 @@ int ima_appraise_measurement(enum ima_hooks func, break; case INTEGRITY_UNKNOWN: if (ima_appraise_req_evm && - xattr_value->type != EVM_IMA_XATTR_DIGSIG) + xattr_value->type != EVM_IMA_XATTR_DIGSIG && !found_digest) goto out; break; case INTEGRITY_NOXATTRS: /* No EVM protected xattrs. */ @@ -531,6 +587,23 @@ int ima_appraise_measurement(enum ima_hooks func, break; fallthrough; case INTEGRITY_NOLABEL: /* No security.evm xattr. */ + /* + * If the digest-nometadata mode is selected, allow access + * without metadata check. EVM will eventually create an HMAC + * based on current xattr values. + */ + if (ima_appraise_no_metadata && found_digest) + break; + /* Allow access to digest lists without metadata, only if they + * are signed or found in a digest list (immutable) + */ + if (func == DIGEST_LIST_CHECK) { + if (xattr_value->type == EVM_IMA_XATTR_DIGSIG) + break; + if (found_digest && + ima_digest_is_immutable(found_digest)) + break; + } cause = "missing-HMAC"; goto out; case INTEGRITY_FAIL_IMMUTABLE: @@ -546,7 +619,7 @@ int ima_appraise_measurement(enum ima_hooks func,
if (xattr_value) rc = xattr_verify(func, iint, xattr_value, xattr_len, &status, - &cause); + &cause, found_digest);
/* * If we have a modsig and either no imasig or the imasig's key isn't diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index b3c8e9c21f5f..875dbd5d3de9 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -429,8 +429,10 @@ static int process_measurement(struct file *file, const struct cred *cred, if (rc != -EPERM) { inode_lock(inode); rc = ima_appraise_measurement(func, iint, file, - pathname, xattr_value, - xattr_len, modsig); + pathname, xattr_value, + xattr_len, modsig, + ima_digest_allow(found_digest, + IMA_APPRAISE)); inode_unlock(inode); } if (!rc) diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 538090a5f259..eea4adb46fdd 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -74,6 +74,7 @@ #define IMA_CHANGE_ATTR 2 #define IMA_DIGSIG 3 #define IMA_MUST_MEASURE 4 +#define IMA_DIGEST_LIST 5
enum evm_ima_xattr_type { IMA_XATTR_DIGEST = 0x01,
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
This patch adds support in EVM to verify file metadata digest with digest lists. Metadata digest, calculated in the same way as for portable signatures, is searched in the digest lists only if the file has the security.evm xattr with type EVM_IMA_XATTR_DIGEST_LIST.
If the found digest is marked as immutable, content and xattr/attr updates are not allowed. Otherwise, after verification, the existing security.evm with the new type will be replaced with an HMAC, similarly to non-portable signatures.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/evm/evm_crypto.c | 9 ++-- security/integrity/evm/evm_main.c | 78 ++++++++++++++++++++++++++--- 2 files changed, 77 insertions(+), 10 deletions(-)
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 033804f5a5f2..259c7c98bb35 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -154,7 +154,8 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, /* Don't include the inode or generation number in portable * signatures */ - if (type != EVM_XATTR_PORTABLE_DIGSIG) { + if (type != EVM_XATTR_PORTABLE_DIGSIG && + type != EVM_IMA_XATTR_DIGEST_LIST) { hmac_misc.ino = inode->i_ino; hmac_misc.generation = inode->i_generation; } @@ -171,7 +172,8 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, hmac_misc.mode = inode->i_mode; crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc)); if ((evm_hmac_attrs & EVM_ATTR_FSUUID) && - type != EVM_XATTR_PORTABLE_DIGSIG) + type != EVM_XATTR_PORTABLE_DIGSIG && + type != EVM_IMA_XATTR_DIGEST_LIST) crypto_shash_update(desc, (u8 *)&inode->i_sb->s_uuid, UUID_SIZE); crypto_shash_final(desc, digest);
@@ -337,7 +339,8 @@ static int evm_is_immutable(struct dentry *dentry, struct inode *inode) rc = 0; goto out; } - if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) + if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG || + xattr_data->type == EVM_IMA_XATTR_DIGEST_LIST) rc = 1; else rc = 0; diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index cf24c5255583..c681838a0b66 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -128,7 +128,7 @@ static bool evm_hmac_disabled(void) return true; }
-static int evm_find_protected_xattrs(struct dentry *dentry) +static int evm_find_protected_xattrs(struct dentry *dentry, int *ima_present) { struct inode *inode = d_backing_inode(dentry); struct xattr_list *xattr; @@ -145,6 +145,8 @@ static int evm_find_protected_xattrs(struct dentry *dentry) continue; return error; } + if (!strcmp(xattr->name, XATTR_NAME_IMA)) + *ima_present = 1; count++; }
@@ -173,9 +175,14 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, struct evm_ima_xattr_data *xattr_data = NULL; struct signature_v2_hdr *hdr; enum integrity_status evm_status = INTEGRITY_PASS; + enum integrity_status saved_evm_status = INTEGRITY_UNKNOWN; struct evm_digest digest; + struct ima_digest *found_digest; struct inode *inode; - int rc, xattr_len, evm_immutable = 0; + struct signature_v2_hdr evm_fake_xattr = { + .type = EVM_IMA_XATTR_DIGEST_LIST, + .version = 2, .hash_algo = HASH_ALGO_SHA256 }; + int rc, xattr_len, evm_immutable = 0, ima_present = 0;
if (iint && (iint->evm_status == INTEGRITY_PASS || iint->evm_status == INTEGRITY_PASS_IMMUTABLE)) @@ -189,7 +196,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, if (rc <= 0) { evm_status = INTEGRITY_FAIL; if (rc == -ENODATA) { - rc = evm_find_protected_xattrs(dentry); + rc = evm_find_protected_xattrs(dentry, &ima_present); if (rc > 0) evm_status = INTEGRITY_NOLABEL; else if (rc == 0) @@ -197,7 +204,20 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, } else if (rc == -EOPNOTSUPP) { evm_status = INTEGRITY_UNKNOWN; } - goto out; + /* IMA added a fake xattr, set also EVM fake xattr */ + if (!ima_present && xattr_name && + !strcmp(xattr_name, XATTR_NAME_IMA) && + xattr_value_len > 2) { + evm_fake_xattr.hash_algo = + ((struct evm_ima_xattr_data *)xattr_value)->data[0]; + xattr_data = + (struct evm_ima_xattr_data *)&evm_fake_xattr; + rc = sizeof(evm_fake_xattr); + } + if (xattr_data != (struct evm_ima_xattr_data *)&evm_fake_xattr) + goto out; + + saved_evm_status = evm_status; }
xattr_len = rc; @@ -255,12 +275,54 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, } } break; + case EVM_IMA_XATTR_DIGEST_LIST: + /* At this point, we cannot determine whether metadata are + * immutable or not. However, it is safe to return the + * fail_immutable error, as HMAC will not be created for this + * security.evm type. + */ + evm_immutable = 1; + + if (xattr_len < offsetof(struct signature_v2_hdr, keyid)) { + evm_status = INTEGRITY_FAIL; + goto out; + } + + hdr = (struct signature_v2_hdr *)xattr_data; + digest.hdr.algo = hdr->hash_algo; + rc = evm_calc_hash(dentry, xattr_name, xattr_value, + xattr_value_len, xattr_data->type, &digest); + if (rc) + break; + + found_digest = ima_lookup_digest(digest.digest, hdr->hash_algo, + COMPACT_METADATA); + if (!found_digest) { + rc = -ENOENT; + break; + } + + if (!ima_digest_allow(found_digest, IMA_APPRAISE)) { + rc = -EACCES; + break; + } + + if (ima_digest_is_immutable(found_digest)) { + if (iint) + iint->flags |= EVM_IMMUTABLE_DIGSIG; + evm_status = INTEGRITY_PASS_IMMUTABLE; + } else { + evm_status = INTEGRITY_PASS; + } + break; default: rc = -EINVAL; break; }
- if (rc) { + if (rc && xattr_data == (struct evm_ima_xattr_data *)&evm_fake_xattr) { + evm_status = saved_evm_status; + } else if (rc) { if (rc == -ENODATA) evm_status = INTEGRITY_NOXATTRS; else if (evm_immutable) @@ -273,7 +335,8 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, out: if (iint) iint->evm_status = evm_status; - kfree(xattr_data); + if (xattr_data != (struct evm_ima_xattr_data *)&evm_fake_xattr) + kfree(xattr_data); return evm_status; }
@@ -581,7 +644,8 @@ int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, if (!xattr_value_len) return -EINVAL; if (xattr_data->type != EVM_IMA_XATTR_DIGSIG && - xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG) + xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG && + xattr_data->type != EVM_IMA_XATTR_DIGEST_LIST) return -EPERM; } return evm_protect_xattr(idmap, dentry, xattr_name, xattr_value,
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
Currently, IMA supports the appraise_type=imasig option in the policy to require file signatures. This patch introduces the new option appraise_type=meta_immutable to require that file metadata are signed and immutable. This requirement can be satisfied by portable signatures and by digest lists if they are marked as immutable.
The main purpose of this option is to ensure that file metadata are correct at the time of access, so that policies relying on labels can be correctly enforced. For example, requiring immutable metadata would prevent an administrator from altering the label assigned to a process during execve() by changing the label of the executable.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/ima/ima_appraise.c | 9 +++++++++ security/integrity/ima/ima_policy.c | 14 ++++++++++---- security/integrity/integrity.h | 1 + 3 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 785849243048..5eca0fb771aa 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -617,6 +617,15 @@ int ima_appraise_measurement(enum ima_hooks func, WARN_ONCE(true, "Unexpected integrity status %d\n", status); }
+ if ((iint->flags & IMA_META_IMMUTABLE_REQUIRED) && + status != INTEGRITY_PASS_IMMUTABLE) { + status = INTEGRITY_FAIL; + cause = "metadata-modifiable"; + integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, + filename, op, cause, rc, 0); + goto out; + } + if (xattr_value) rc = xattr_verify(func, iint, xattr_value, xattr_len, &status, &cause, found_digest); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 0c7f135bb556..66eaa0e0f66f 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -1251,7 +1251,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
if (entry->action != APPRAISE && entry->flags & (IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED | - IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS)) + IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS | + IMA_META_IMMUTABLE_REQUIRED)) return false;
/* @@ -1285,7 +1286,7 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) IMA_FSNAME | IMA_GID | IMA_EGID | IMA_FGROUP | IMA_DIGSIG_REQUIRED | IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS | - IMA_VERITY_REQUIRED)) + IMA_VERITY_REQUIRED | IMA_META_IMMUTABLE_REQUIRED)) return false;
break; @@ -1298,7 +1299,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) IMA_FSNAME | IMA_GID | IMA_EGID | IMA_FGROUP | IMA_DIGSIG_REQUIRED | IMA_PERMIT_DIRECTIO | IMA_MODSIG_ALLOWED | - IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS)) + IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS | + IMA_META_IMMUTABLE_REQUIRED)) return false;
break; @@ -1824,7 +1826,9 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) else entry->flags |= IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED; - } else { + } else if (strcmp(args[0].from, "meta_immutable") == 0) + entry->flags |= IMA_META_IMMUTABLE_REQUIRED; + else { result = -EINVAL; } break; @@ -2281,6 +2285,8 @@ int ima_policy_show(struct seq_file *m, void *v) seq_puts(m, "digest_type=verity "); if (entry->flags & IMA_CHECK_BLACKLIST) seq_puts(m, "appraise_flag=check_blacklist "); + if (entry->flags & IMA_META_IMMUTABLE_REQUIRED) + seq_puts(m, "appraise_type=meta_immutable "); 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 eea4adb46fdd..a93ad2bcd809 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -43,6 +43,7 @@ #define IMA_MODSIG_ALLOWED 0x20000000 #define IMA_CHECK_BLACKLIST 0x40000000 #define IMA_VERITY_REQUIRED 0x80000000 +#define IMA_META_IMMUTABLE_REQUIRED 0x100000000
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \ IMA_HASH | IMA_APPRAISE_SUBMASK)
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
This patch introduces a new hard-coded policy to measure executable code:
dont_measure fsmagic=0x9fa0 dont_measure fsmagic=0x62656572 dont_measure fsmagic=0x64626720 dont_measure fsmagic=0x1cd1 dont_measure fsmagic=0x42494e4d dont_measure fsmagic=0x73636673 dont_measure fsmagic=0xf97cff8c dont_measure fsmagic=0x43415d53 dont_measure fsmagic=0x27e0eb dont_measure fsmagic=0x63677270 dont_measure fsmagic=0x6e736673 measure func=MMAP_CHECK mask=MAY_EXEC measure func=BPRM_CHECK mask=MAY_EXEC measure func=MODULE_CHECK measure func=FIRMWARE_CHECK measure func=POLICY_CHECK measure func=DIGEST_LIST_CHECK
It can be selected by specifying ima_policy=exec_tcb in the kernel command line. Files in tmpfs are not excluded from measurement.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- .../admin-guide/kernel-parameters.txt | 5 +++++ security/integrity/ima/ima_policy.c | 22 ++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index da42ecb03667..81a3bae90bb3 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1989,6 +1989,11 @@ mode bit set by either the effective uid (euid=0) or uid=0.
+ The "exec_tcb" policy is similar to the "tcb" policy + except for file open, which is not considered. Files + in the tmpfs filesystem are not excluded from + measurement. + The "appraise_tcb" policy appraises the integrity of all files owned by root.
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 66eaa0e0f66f..863f0888b67d 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -62,7 +62,7 @@ enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE };
-enum policy_types { ORIGINAL_TCB = 1, DEFAULT_TCB }; +enum policy_types { ORIGINAL_TCB = 1, DEFAULT_TCB, EXEC_TCB };
enum policy_rule_list { IMA_DEFAULT_POLICY = 1, IMA_CUSTOM_POLICY };
@@ -289,6 +289,8 @@ static int __init policy_setup(char *str) continue; if ((strcmp(p, "tcb") == 0) && !ima_policy) ima_policy = DEFAULT_TCB; + else if ((strcmp(p, "exec_tcb") == 0) && !ima_policy) + ima_policy = EXEC_TCB; else if (strcmp(p, "appraise_tcb") == 0) ima_use_appraise_tcb = true; else if (strcmp(p, "secure_boot") == 0) @@ -863,14 +865,26 @@ static int ima_appraise_flag(enum ima_hooks func) return 0; }
-static void add_rules(struct ima_rule_entry *entries, int count, - enum policy_rule_list policy_rule) +static void __init add_rules(struct ima_rule_entry *entries, int count, + enum policy_rule_list policy_rule) { int i = 0;
for (i = 0; i < count; i++) { struct ima_rule_entry *entry;
+ if (ima_policy == EXEC_TCB) { + if (entries == dont_measure_rules) + if ((entries[i].flags & IMA_FSMAGIC) && + entries[i].fsmagic == TMPFS_MAGIC) + continue; + + if (entries == default_measurement_rules) + if ((entries[i].flags & IMA_FUNC) && + entries[i].func == FILE_CHECK) + continue; + } + if (policy_rule & IMA_DEFAULT_POLICY) list_add_tail(&entries[i].list, &ima_default_rules);
@@ -956,6 +970,8 @@ void __init ima_init_policy(void) ARRAY_SIZE(original_measurement_rules), IMA_DEFAULT_POLICY); break; + case EXEC_TCB: + fallthrough; case DEFAULT_TCB: add_rules(default_measurement_rules, ARRAY_SIZE(default_measurement_rules),
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
This patch introduces a new hard-coded policy to appraise executable code:
appraise func=MODULE_CHECK appraise_type=imasig appraise func=FIRMWARE_CHECK appraise_type=imasig appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig appraise func=POLICY_CHECK appraise_type=imasig appraise func=DIGEST_LIST_CHECK appraise_type=imasig dont_appraise fsmagic=0x9fa0 dont_appraise fsmagic=0x62656572 dont_appraise fsmagic=0x64626720 dont_appraise fsmagic=0x858458f6 dont_appraise fsmagic=0x1cd1 dont_appraise fsmagic=0x42494e4d dont_appraise fsmagic=0x73636673 dont_appraise fsmagic=0xf97cff8c dont_appraise fsmagic=0x43415d53 dont_appraise fsmagic=0x6e736673 dont_appraise fsmagic=0xde5e81e4 dont_appraise fsmagic=0x27e0eb dont_appraise fsmagic=0x63677270 appraise func=BPRM_CHECK appraise_type=imasig appraise func=MMAP_CHECK appraise_type=imasig
The new policy can be selected by specifying ima_policy=appraise_exec_tcb in the kernel command line.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- .../admin-guide/kernel-parameters.txt | 5 ++++ security/integrity/ima/ima_policy.c | 28 +++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 81a3bae90bb3..28643f04d156 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -2001,6 +2001,11 @@ of files (eg. kexec kernel image, kernel modules, firmware, policy, etc) based on file signatures.
+ The "appraise_exec_tcb" includes the "secure_boot" + policy and additionally includes all programs exec'd and + files mmap'd for exec. Files in the tmpfs filesystem are + not excluded from appraisal. + The "fail_securely" policy forces file signature verification failure also on privileged mounted filesystems with the SB_I_UNVERIFIABLE_SIGNATURE diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 863f0888b67d..22acd0c80afd 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -220,6 +220,13 @@ static struct ima_rule_entry default_appraise_rules[] __ro_after_init = { #endif };
+static struct ima_rule_entry appraise_exec_rules[] __ro_after_init = { + {.action = APPRAISE, .func = BPRM_CHECK, + .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, + {.action = APPRAISE, .func = MMAP_CHECK, + .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, +}; + static struct ima_rule_entry build_appraise_rules[] __ro_after_init = { #ifdef CONFIG_IMA_APPRAISE_REQUIRE_MODULE_SIGS {.action = APPRAISE, .func = MODULE_CHECK, @@ -277,6 +284,7 @@ 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_appraise_exec_tcb __initdata; static bool ima_use_secure_boot __initdata; static bool ima_use_critical_data __initdata; static bool ima_fail_unverifiable_sigs __ro_after_init; @@ -293,6 +301,8 @@ static int __init policy_setup(char *str) ima_policy = EXEC_TCB; else if (strcmp(p, "appraise_tcb") == 0) ima_use_appraise_tcb = true; + else if (strcmp(p, "appraise_exec_tcb") == 0) + ima_use_appraise_exec_tcb = true; else if (strcmp(p, "secure_boot") == 0) ima_use_secure_boot = true; else if (strcmp(p, "critical_data") == 0) @@ -885,6 +895,15 @@ static void __init add_rules(struct ima_rule_entry *entries, int count, continue; }
+ if (ima_use_appraise_exec_tcb) { + if (entries == default_appraise_rules) { + if (entries[i].action != DONT_APPRAISE) + continue; + if ((entries[i].flags & IMA_FSMAGIC) && + entries[i].fsmagic == TMPFS_MAGIC) + continue; + } + } if (policy_rule & IMA_DEFAULT_POLICY) list_add_tail(&entries[i].list, &ima_default_rules);
@@ -998,7 +1017,7 @@ void __init ima_init_policy(void) * Insert the builtin "secure_boot" policy rules requiring file * signatures, prior to other appraise rules. */ - if (ima_use_secure_boot) + if (ima_use_secure_boot || ima_use_appraise_exec_tcb) add_rules(secure_boot_rules, ARRAY_SIZE(secure_boot_rules), IMA_DEFAULT_POLICY);
@@ -1018,11 +1037,16 @@ void __init ima_init_policy(void) IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY); }
- if (ima_use_appraise_tcb) + if (ima_use_appraise_tcb || ima_use_appraise_exec_tcb) add_rules(default_appraise_rules, ARRAY_SIZE(default_appraise_rules), IMA_DEFAULT_POLICY);
+ if (ima_use_appraise_exec_tcb) + add_rules(appraise_exec_rules, + ARRAY_SIZE(appraise_exec_rules), + IMA_DEFAULT_POLICY); + if (ima_use_critical_data) add_rules(critical_data_rules, ARRAY_SIZE(critical_data_rules),
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
This patch modifies the existing "appraise_exec_tcb" policy, by adding the appraise_type=meta_immutable requirement for executed files:
appraise func=MODULE_CHECK appraise_type=imasig appraise func=FIRMWARE_CHECK appraise_type=imasig appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig appraise func=POLICY_CHECK appraise_type=imasig appraise func=DIGEST_LIST_CHECK appraise_type=imasig dont_appraise fsmagic=0x9fa0 dont_appraise fsmagic=0x62656572 dont_appraise fsmagic=0x64626720 dont_appraise fsmagic=0x858458f6 dont_appraise fsmagic=0x1cd1 dont_appraise fsmagic=0x42494e4d dont_appraise fsmagic=0x73636673 dont_appraise fsmagic=0xf97cff8c dont_appraise fsmagic=0x43415d53 dont_appraise fsmagic=0x6e736673 dont_appraise fsmagic=0xde5e81e4 dont_appraise fsmagic=0x27e0eb dont_appraise fsmagic=0x63677270 appraise func=BPRM_CHECK appraise_type=imasig appraise_type=meta_immutable appraise func=MMAP_CHECK appraise_type=imasig
This policy can be selected by specifying ima_policy="appraise_exec_tcb|appraise_exec_immutable" in the kernel command line.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- Documentation/admin-guide/kernel-parameters.txt | 4 ++++ security/integrity/ima/ima_policy.c | 10 ++++++++++ 2 files changed, 14 insertions(+)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 28643f04d156..5d84000d344d 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -2006,6 +2006,10 @@ files mmap'd for exec. Files in the tmpfs filesystem are not excluded from appraisal.
+ The "appraise_exec_immutable" policy requires immutable + metadata for executed files, if the "appraise_exec_tcb" + policy is selected. + The "fail_securely" policy forces file signature verification failure also on privileged mounted filesystems with the SB_I_UNVERIFIABLE_SIGNATURE diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 22acd0c80afd..88cd1778680a 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -285,6 +285,7 @@ __setup("ima_tcb", default_measure_policy_setup);
static bool ima_use_appraise_tcb __initdata; static bool ima_use_appraise_exec_tcb __initdata; +static bool ima_use_appraise_exec_immutable __initdata; static bool ima_use_secure_boot __initdata; static bool ima_use_critical_data __initdata; static bool ima_fail_unverifiable_sigs __ro_after_init; @@ -303,6 +304,8 @@ static int __init policy_setup(char *str) ima_use_appraise_tcb = true; else if (strcmp(p, "appraise_exec_tcb") == 0) ima_use_appraise_exec_tcb = true; + else if (strcmp(p, "appraise_exec_immutable") == 0) + ima_use_appraise_exec_immutable = true; else if (strcmp(p, "secure_boot") == 0) ima_use_secure_boot = true; else if (strcmp(p, "critical_data") == 0) @@ -904,6 +907,13 @@ static void __init add_rules(struct ima_rule_entry *entries, int count, continue; } } + + if (ima_use_appraise_exec_immutable) + if (entries == appraise_exec_rules && + (entries[i].flags & IMA_FUNC) && + entries[i].func == BPRM_CHECK) + entries[i].flags |= IMA_META_IMMUTABLE_REQUIRED; + if (policy_rule & IMA_DEFAULT_POLICY) list_add_tail(&entries[i].list, &ima_default_rules);
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
This patch adds the documentation of the IMA Digest Lists extension.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- Documentation/security/IMA-digest-lists.txt | 259 ++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 Documentation/security/IMA-digest-lists.txt
diff --git a/Documentation/security/IMA-digest-lists.txt b/Documentation/security/IMA-digest-lists.txt new file mode 100644 index 000000000000..15f30deab328 --- /dev/null +++ b/Documentation/security/IMA-digest-lists.txt @@ -0,0 +1,259 @@ +========================== +IMA Digest Lists Extension +========================== + +INTRODUCTION +============ + +Integrity Measurement Architecture (IMA) is a security module that performs +measurement of files accessed with the execve(), mmap() and open() system +calls. File measurements can be used for different purposes. They can be +added in a measurement list and sent to a remote verifier for an integrity +evaluation. They can be compared to reference values provided by the +software vendor and access can be denied if there is a mismatch. File +measurements can also be included in system logs for auditing purposes. + +IMA Digest Lists is an extension providing additional functionality for +the IMA submodules and EVM. Its main task is to load reference values for +file content and metadata in the kernel memory, and to communicate to IMA +submodules (measurement and appraisal) or EVM whether the digest of a file +being accessed is found in the uploaded digest lists. + +The IMA-Measure submodule uses the IMA Digest Lists extension to create +a new measurement list (with a different PCR, for example 11) which +contains the measurement of uploaded digest lists and unknown files. Since +the loading of digest lists is sequential, the chosen PCR will have a +predictable value for the whole boot cycle and can be used for sealing +policies based on the OS software integrity. + +Both the standard and the new measurement list can be generated at the same +time, allowing for implicit attestation based on the usage of a TPM key +sealed to the OS, and for explicit attestation when a more precise +integrity evaluation is necessary. + +The IMA-Appraise submodule uses the IMA Digest Lists extension to +grant/deny access to the files depending on whether the file digest is +found in the uploaded digest lists, instead of checking security.ima. + +EVM uses the IMA Digest Lists extension to check file metadata integrity. + +The main advantage of the extension is that reference measurements used for +the digest comparison can be extracted from already existing sources (for +example RPM headers). Another benefit is that the overhead compared to file +signatures is lower as only one signature is verified for all files +included in the digest list. + + + +WORKFLOW +======== + +The IMA workflow is modified as follows. The added steps are marked as +[NEW]. + + +------------+ + | IMA hook | + +------------+ + | + +---------+ + | collect | + +---------+ + | +---------------------+ ++---------------+ ------------| don't measure [NEW] | +| digest lookup | | yes +---------------------+ +| [NEW] | -------------- no +---------------------+ ++---------------+ -----/ digest ---| add to measurement | + | | \ found? [NEW] / | list (PCR 11) [NEW] | + | | -------------- +---------------------+ + +----------+ +-------------+ +--------------------+ + | switch |-------| IMA-Measure |----------------| add to measurement | + | (action) | +-------------+ | list (PCR 10) | + +----------+ +--------------------+ + | + | + | no xattr ++--------------+ --------------- yes +| IMA-Appraise |----/ filec created ------------------------------- ++--------------+ \ created [NEW] / | + xattr | --------------- | + | | no | + | ------------------ no -------------- yes +--------+ + | / EVM initialized? ----/ digest ------| grant | + | \ [NEW] / \ found? [NEW] / | access | + | ------------------ -------------- +--------+ + | | yes | no + | | | +--------+ + | ------------------- no ---------------| deny | + | / digest-nometadata -------------------------| access | + | \ mode? [NEW] / +--------+ + | ------------------- + | ^ | yes +--------+ + | | -------------------------------------| grant | + | | | access | + | | +--------+ + | | yes + | ------------------------- no +--------+ + |----------/ security.ima (new type) -------------------| deny | + | \ present and valid? / | access | + | ------------------------- +--------+ + | | + | -------------------------- no | + -----------/ security.ima (cur types) ----------------------- + \ present and valid? / + -------------------------- + | yes +--------+ + ------------------------------------| grant | + | access | + +--------+ + + ++-----+ -------------------------- yes +--------+ +| EVM |---------/ security.evm (cur types) ---------------------| grant | ++-----+ \ present and valid? / | access | + | -------------------------- +--------+ + | | + | | no +--------+ + | ------------------------------------| deny | + | | access | + | +--------+ + | | + | +-----------+ | + | ------------------------- | calculate | | + -------------/ security.evm (new type) ---| metadata | | + \ present? [NEW] / | digest | | + ------------------------- +-----------+ | + | | + -------------- no | + / digest --------- + \ found? [NEW] / + -------------- +--------+ + | yes | grant | + | | access | + | +--------+ + | | + ------------------ yes | + / digest -----| + \ immutable? [NEW] / | + ------------------ | + | no | + +---------+ | + | convert |------------ + | to HMAC | + +---------+ + + +After the file digest is calculated, it is searched in the hash table +containing all digests extracted from the uploaded digest lists. Then, if +the digest is found, a structure is returned to IMA with information +associated to that digest. The structure is returned only to the IMA +submodules that processed the digest lists (i.e. the action returned by +ima_get_action() was 'measure' or 'appraise'). + +IMA-Measure behavior depends on whether the digest list PCR has been +specified in the kernel command line. If the PCR was not specified, the +submodule behaves as before. If the PCR was specified, IMA-Measure creates +a new measurement with that PCR, only if the file digest is not found in +the digest lists. It additionally creates a measurement with the default +PCR if '+' is added as a prefix to the PCR. + +IMA-Appraise behavior depends on whether either the 'digest' or +'digest-nometadata' appraisal modes have been specified in the kernel +command line. If they were not specified, IMA-Appraise relies solely on the +security.ima xattr for verification. If the 'digest' mode was specified, +verification succeeds if the file digest is found in the digest lists and +EVM is not initialized, as there is no other way to verify file metadata. + +If the 'digest-nometadata' mode was specified, verification succeeds +regardless of the fact that EVM is initialized or not. However, after a +write, files for which access was granted without verifying metadata will +have a new security.ima type, so that they can be identified also after +reboot. Specifying 'digest-nometadata' is required also to access files +with the new security.ima type. + +EVM determines whether metadata digest should be searched in the digest +lists depending on the security.evm type. If the new type is set, EVM +calculates metadata digest and searches it in the digest lists. A structure +is returned to EVM if the digest is found and the digest lists were +appraised. + + + +ARCHITECTURE +============ + ++-----+ 6) digest lookup +| EVM |------------------------------------------------ ++-----+ | + | ++--------------+ 5) digest lookup | +| IMA-Appraise |--------------------------------------| ++--------------+ || + || ++-------------+ 4) digest lookup || +| IMA-Measure | -------------------------------------|| ++-------------+ ||| + ||| ++-------------+ 2) parse compact list +-------------------+ +| IMA (secfs) | ------------------------> | IMA Digests Lists | ++-------------+ +-------------------+ + ^ | 3) add digests + | +------------+ + | | hash table | + | +------------+ + | + | kernel space +---|------------------------------------------------------------------------- + | user space + | + | + | + | + | + | + | +1) echo <digest list path> + +The main addition to IMA is a new hash table (similar to that used to check +for duplicate measurement entries), which contains file content and metadata +digests extracted from the digest lists. + +Digest lists can be uploaded to the kernel by writing their path to +digest_list_data in the securityfs filesystem. After digest lists are +uploaded, they are parsed by the kernel and extracted digests are added to +the hash table. + +IMA submodules, Measure and Appraise, search the digest of an accessed file +in the hash table and perform actions depending on whether the digest was +found or not. IMA submodules can search digests in the hash table only if +they also processed the digest lists. + +EVM searches the metadata digest of an accessed file in the hash table and +returns the result to IMA, which perform actions depending on the result. +EVM can search digests in the hash table if IMA-Appraise processed the +digest lists. + + + +CONFIGURATION +============= + +The first step consists in generating digest lists with the +gen_digest_lists tool included in the digest-list-tools package. +digest-list-tools can be retrieved at the URL: + +https://gitee.com/openeuler/digest-list-tools + +gen_digest_lists can generate digest lists from different sources (for +example: RPM package DB). By default, it saves generated digest lists in +the /etc/ima/digest_lists directory. digest-list-tools includes also two +bash scripts setup_ima_digest_lists and setup_digest_lists_demo to simplify +the digest list generation for the users. + +To use digest lists during early boot, it is necessary to regenerate the +initial ram disk. Digest lists will be added to the ram disk by the new +dracut/initramfs-tools modules, included in the digest-list-tools package. To +include file signatures in the initial ram disk, it is necessary to have the +following patches applied: + +https://gitee.com/src-openeuler/cpio/blob/master/add-option-to-add-metadata-... +https://gitee.com/src-openeuler/dracut/blob/master/add-option-to-include-fil...
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
Introduce the new function to get the number of bits and bytes from an MPI.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- include/linux/mpi.h | 2 ++ lib/mpi/mpicoder.c | 33 ++++++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 7 deletions(-)
diff --git a/include/linux/mpi.h b/include/linux/mpi.h index eb0d1c1db208..a7dd4c9d8120 100644 --- a/include/linux/mpi.h +++ b/include/linux/mpi.h @@ -90,6 +90,8 @@ enum gcry_mpi_format { };
MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes); +int mpi_key_length(const void *xbuffer, unsigned int ret_nread, + unsigned int *nbits_arg, unsigned int *nbytes_arg); MPI mpi_read_from_buffer(const void *buffer, unsigned *ret_nread); int mpi_fromstr(MPI val, const char *str); MPI mpi_scanval(const char *string); diff --git a/lib/mpi/mpicoder.c b/lib/mpi/mpicoder.c index 3cb6bd148fa9..c7461fa9d272 100644 --- a/lib/mpi/mpicoder.c +++ b/lib/mpi/mpicoder.c @@ -79,22 +79,41 @@ MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes) } EXPORT_SYMBOL_GPL(mpi_read_raw_data);
-MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread) +int mpi_key_length(const void *xbuffer, unsigned int ret_nread, + unsigned int *nbits_arg, unsigned int *nbytes_arg) { const uint8_t *buffer = xbuffer; - unsigned int nbits, nbytes; - MPI val; + unsigned int nbits;
- if (*ret_nread < 2) - return ERR_PTR(-EINVAL); + if (ret_nread < 2) + return -EINVAL; nbits = buffer[0] << 8 | buffer[1];
if (nbits > MAX_EXTERN_MPI_BITS) { pr_info("MPI: mpi too large (%u bits)\n", nbits); - return ERR_PTR(-EINVAL); + return -EINVAL; }
- nbytes = DIV_ROUND_UP(nbits, 8); + if (nbits_arg) + *nbits_arg = nbits; + if (nbytes_arg) + *nbytes_arg = DIV_ROUND_UP(nbits, 8); + + return 0; +} +EXPORT_SYMBOL_GPL(mpi_key_length); + +MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread) +{ + const uint8_t *buffer = xbuffer; + unsigned int nbytes; + MPI val; + int ret; + + ret = mpi_key_length(xbuffer, *ret_nread, NULL, &nbytes); + if (ret < 0) + return ERR_PTR(ret); + if (nbytes + 2 > *ret_nread) { pr_info("MPI: mpi larger than buffer nbytes=%u ret_nread=%u\n", nbytes, *ret_nread);
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
Parse the RSA key with RAW format if the ASN.1 parser returns an error.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- crypto/rsa.c | 14 +++++-- crypto/rsa_helper.c | 69 +++++++++++++++++++++++++++++++++++ include/crypto/internal/rsa.h | 6 +++ 3 files changed, 85 insertions(+), 4 deletions(-)
diff --git a/crypto/rsa.c b/crypto/rsa.c index c50f2d2a4d06..0ca65c1db3a9 100644 --- a/crypto/rsa.c +++ b/crypto/rsa.c @@ -216,8 +216,11 @@ static int rsa_set_pub_key(struct crypto_akcipher *tfm, const void *key, rsa_free_mpi_key(mpi_key);
ret = rsa_parse_pub_key(&raw_key, key, keylen); - if (ret) - return ret; + if (ret) { + ret = rsa_parse_pub_key_raw(&raw_key, key, keylen); + if (ret) + return ret; + }
mpi_key->e = mpi_read_raw_data(raw_key.e, raw_key.e_sz); if (!mpi_key->e) @@ -250,8 +253,11 @@ static int rsa_set_priv_key(struct crypto_akcipher *tfm, const void *key, rsa_free_mpi_key(mpi_key);
ret = rsa_parse_priv_key(&raw_key, key, keylen); - if (ret) - return ret; + if (ret) { + ret = rsa_parse_priv_key_raw(&raw_key, key, keylen); + if (ret) + return ret; + }
mpi_key->d = mpi_read_raw_data(raw_key.d, raw_key.d_sz); if (!mpi_key->d) diff --git a/crypto/rsa_helper.c b/crypto/rsa_helper.c index 94266f29049c..fb9443df8f0b 100644 --- a/crypto/rsa_helper.c +++ b/crypto/rsa_helper.c @@ -9,6 +9,7 @@ #include <linux/export.h> #include <linux/err.h> #include <linux/fips.h> +#include <linux/mpi.h> #include <crypto/internal/rsa.h> #include "rsapubkey.asn1.h" #include "rsaprivkey.asn1.h" @@ -148,6 +149,32 @@ int rsa_get_qinv(void *context, size_t hdrlen, unsigned char tag, return 0; }
+typedef int (*rsa_get_func)(void *, size_t, unsigned char, + const void *, size_t); + +static int rsa_parse_key_raw(struct rsa_key *rsa_key, + const void *key, unsigned int key_len, + rsa_get_func *func, int n_func) +{ + unsigned int nbytes, len = key_len; + const void *key_ptr = key; + int ret, i; + + for (i = 0; i < n_func; i++) { + ret = mpi_key_length(key_ptr, len, NULL, &nbytes); + if (ret < 0) + return ret; + + ret = func[i](rsa_key, 0, 0, key_ptr + 2, nbytes); + if (ret < 0) + return ret; + + key_ptr += nbytes + 2; + } + + return (key_ptr == key + key_len) ? 0 : -EINVAL; +} + /** * rsa_parse_pub_key() - decodes the BER encoded buffer and stores in the * provided struct rsa_key, pointers to the raw key as is, @@ -166,6 +193,27 @@ int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key, } EXPORT_SYMBOL_GPL(rsa_parse_pub_key);
+/** + * rsa_parse_pub_key_raw() - parse the RAW key and store in the provided struct + * rsa_key, pointers to the raw key as is, so that + * the caller can copy it or MPI parse it, etc. + * + * @rsa_key: struct rsa_key key representation + * @key: key in RAW format + * @key_len: length of key + * + * Return: 0 on success or error code in case of error + */ +int rsa_parse_pub_key_raw(struct rsa_key *rsa_key, const void *key, + unsigned int key_len) +{ + rsa_get_func pub_func[] = {rsa_get_n, rsa_get_e}; + + return rsa_parse_key_raw(rsa_key, key, key_len, + pub_func, ARRAY_SIZE(pub_func)); +} +EXPORT_SYMBOL_GPL(rsa_parse_pub_key_raw); + /** * rsa_parse_priv_key() - decodes the BER encoded buffer and stores in the * provided struct rsa_key, pointers to the raw key @@ -184,3 +232,24 @@ int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key, return asn1_ber_decoder(&rsaprivkey_decoder, rsa_key, key, key_len); } EXPORT_SYMBOL_GPL(rsa_parse_priv_key); + +/** + * rsa_parse_priv_key_raw() - parse the RAW key and store in the provided struct + * rsa_key, pointers to the raw key as is, so that + * the caller can copy it or MPI parse it, etc. + * + * @rsa_key: struct rsa_key key representation + * @key: key in RAW format + * @key_len: length of key + * + * Return: 0 on success or error code in case of error + */ +int rsa_parse_priv_key_raw(struct rsa_key *rsa_key, const void *key, + unsigned int key_len) +{ + rsa_get_func priv_func[] = {rsa_get_n, rsa_get_e, rsa_get_d}; + + return rsa_parse_key_raw(rsa_key, key, key_len, + priv_func, ARRAY_SIZE(priv_func)); +} +EXPORT_SYMBOL_GPL(rsa_parse_priv_key_raw); diff --git a/include/crypto/internal/rsa.h b/include/crypto/internal/rsa.h index e870133f4b77..7141e806ceea 100644 --- a/include/crypto/internal/rsa.h +++ b/include/crypto/internal/rsa.h @@ -50,8 +50,14 @@ struct rsa_key { int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key, unsigned int key_len);
+int rsa_parse_pub_key_raw(struct rsa_key *rsa_key, const void *key, + unsigned int key_len); + int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key, unsigned int key_len);
+int rsa_parse_priv_key_raw(struct rsa_key *rsa_key, const void *key, + unsigned int key_len); + extern struct crypto_template rsa_pkcs1pad_tmpl; #endif
From: David Howells dhowells@redhat.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
Provide some useful PGP definitions from RFC 4880. These describe details of public key crypto as used by crypto keys for things like signature verification.
Changelog:
v0: - fix style issues (Roberto Sassu)
Signed-off-by: David Howells dhowells@redhat.com Co-developed-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- include/linux/pgp.h | 216 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 include/linux/pgp.h
diff --git a/include/linux/pgp.h b/include/linux/pgp.h new file mode 100644 index 000000000000..71186ac3128b --- /dev/null +++ b/include/linux/pgp.h @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* PGP definitions (RFC 4880) + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_PGP_H +#define _LINUX_PGP_H + +#include <linux/types.h> + +struct pgp_key_ID { + u8 id[8]; +} __packed; + +struct pgp_time { + u8 time[4]; +} __packed; + +/* + * PGP public-key algorithm identifiers [RFC4880: 9.1] + */ +enum pgp_pubkey_algo { + PGP_PUBKEY_RSA_ENC_OR_SIG = 1, + PGP_PUBKEY_RSA_ENC_ONLY = 2, + PGP_PUBKEY_RSA_SIG_ONLY = 3, + PGP_PUBKEY_ELGAMAL = 16, + PGP_PUBKEY_DSA = 17, + PGP_PUBKEY__LAST +}; + +/* + * PGP symmetric-key algorithm identifiers [RFC4880: 9.2] + */ +enum pgp_symkey_algo { + PGP_SYMKEY_PLAINTEXT = 0, + PGP_SYMKEY_IDEA = 1, + PGP_SYMKEY_3DES = 2, + PGP_SYMKEY_CAST5 = 3, + PGP_SYMKEY_BLOWFISH = 4, + PGP_SYMKEY_AES_128KEY = 7, + PGP_SYMKEY_AES_192KEY = 8, + PGP_SYMKEY_AES_256KEY = 9, + PGP_SYMKEY_TWOFISH_256KEY = 10, +}; + +/* + * PGP compression algorithm identifiers [RFC4880: 9.3] + */ +enum pgp_compr_algo { + PGP_COMPR_UNCOMPRESSED = 0, + PGP_COMPR_ZIP = 1, + PGP_COMPR_ZLIB = 2, + PGP_COMPR_BZIP2 = 3, +}; + +/* + * PGP hash algorithm identifiers [RFC4880: 9.4] + */ +enum pgp_hash_algo { + PGP_HASH_MD5 = 1, + PGP_HASH_SHA1 = 2, + PGP_HASH_RIPE_MD_160 = 3, + PGP_HASH_SHA256 = 8, + PGP_HASH_SHA384 = 9, + PGP_HASH_SHA512 = 10, + PGP_HASH_SHA224 = 11, + PGP_HASH__LAST +}; + +extern const char *const pgp_hash_algorithms[PGP_HASH__LAST]; + +/* + * PGP packet type tags [RFC4880: 4.3]. + */ +enum pgp_packet_tag { + PGP_PKT_RESERVED = 0, + PGP_PKT_PUBKEY_ENC_SESSION_KEY = 1, + PGP_PKT_SIGNATURE = 2, + PGP_PKT_SYMKEY_ENC_SESSION_KEY = 3, + PGP_PKT_ONEPASS_SIGNATURE = 4, + PGP_PKT_SECRET_KEY = 5, + PGP_PKT_PUBLIC_KEY = 6, + PGP_PKT_SECRET_SUBKEY = 7, + PGP_PKT_COMPRESSED_DATA = 8, + PGP_PKT_SYM_ENC_DATA = 9, + PGP_PKT_MARKER = 10, + PGP_PKT_LITERAL_DATA = 11, + PGP_PKT_TRUST = 12, + PGP_PKT_USER_ID = 13, + PGP_PKT_PUBLIC_SUBKEY = 14, + PGP_PKT_USER_ATTRIBUTE = 17, + PGP_PKT_SYM_ENC_AND_INTEG_DATA = 18, + PGP_PKT_MODIFY_DETECT_CODE = 19, + PGP_PKT_PRIVATE_0 = 60, + PGP_PKT_PRIVATE_3 = 63, + PGP_PKT__HIGHEST = 63 +}; + +/* + * Signature (tag 2) packet [RFC4880: 5.2]. + */ +enum pgp_signature_version { + PGP_SIG_VERSION_3 = 3, + PGP_SIG_VERSION_4 = 4, +}; + +enum pgp_signature_type { + PGP_SIG_BINARY_DOCUMENT_SIG = 0x00, + PGP_SIG_CANONICAL_TEXT_DOCUMENT_SIG = 0x01, + PGP_SIG_STANDALONE_SIG = 0x02, + PGP_SIG_GENERAL_CERT_OF_UID_PUBKEY = 0x10, + PGP_SIG_PERSONAL_CERT_OF_UID_PUBKEY = 0x11, + PGP_SIG_CASUAL_CERT_OF_UID_PUBKEY = 0x12, + PGP_SIG_POSTITIVE_CERT_OF_UID_PUBKEY = 0x13, + PGP_SIG_SUBKEY_BINDING_SIG = 0x18, + PGP_SIG_PRIMARY_KEY_BINDING_SIG = 0x19, + PGP_SIG_DIRECTLY_ON_KEY = 0x1F, + PGP_SIG_KEY_REVOCATION_SIG = 0x20, + PGP_SIG_SUBKEY_REVOCATION_SIG = 0x28, + PGP_SIG_CERT_REVOCATION_SIG = 0x30, + PGP_SIG_TIMESTAMP_SIG = 0x40, + PGP_SIG_THIRD_PARTY_CONFIRM_SIG = 0x50, +}; + +struct pgp_signature_v3_packet { + enum pgp_signature_version version : 8; /* == PGP_SIG_VERSION_3 */ + u8 length_of_hashed; /* == 5 */ + struct { + enum pgp_signature_type signature_type : 8; + struct pgp_time creation_time; + } __packed hashed; + struct pgp_key_ID issuer; + enum pgp_pubkey_algo pubkey_algo : 8; + enum pgp_hash_algo hash_algo : 8; +} __packed; + +struct pgp_signature_v4_packet { + enum pgp_signature_version version : 8; /* == PGP_SIG_VERSION_4 */ + enum pgp_signature_type signature_type : 8; + enum pgp_pubkey_algo pubkey_algo : 8; + enum pgp_hash_algo hash_algo : 8; +} __packed; + +/* + * V4 signature subpacket types [RFC4880: 5.2.3.1]. + */ +enum pgp_sig_subpkt_type { + PGP_SIG_CREATION_TIME = 2, + PGP_SIG_EXPIRATION_TIME = 3, + PGP_SIG_EXPORTABLE_CERT = 4, + PGP_SIG_TRUST_SIG = 5, + PGP_SIG_REGEXP = 6, + PGP_SIG_REVOCABLE = 7, + PGP_SIG_KEY_EXPIRATION_TIME = 9, + PGP_SIG_PREF_SYM_ALGO = 11, + PGP_SIG_REVOCATION_KEY = 12, + PGP_SIG_ISSUER = 16, + PGP_SIG_NOTATION_DATA = 20, + PGP_SIG_PREF_HASH_ALGO = 21, + PGP_SIG_PREF_COMPR_ALGO = 22, + PGP_SIG_KEY_SERVER_PREFS = 23, + PGP_SIG_PREF_KEY_SERVER = 24, + PGP_SIG_PRIMARY_USER_ID = 25, + PGP_SIG_POLICY_URI = 26, + PGP_SIG_KEY_FLAGS = 27, + PGP_SIG_SIGNERS_USER_ID = 28, + PGP_SIG_REASON_FOR_REVOCATION = 29, + PGP_SIG_FEATURES = 30, + PGP_SIG_TARGET = 31, + PGP_SIG_EMBEDDED_SIG = 32, + PGP_SIG__LAST +}; + +#define PGP_SIG_SUBPKT_TYPE_CRITICAL_MASK 0x80 + +/* + * Key (tag 5, 6, 7 and 14) packet + */ +enum pgp_key_version { + PGP_KEY_VERSION_2 = 2, + PGP_KEY_VERSION_3 = 3, + PGP_KEY_VERSION_4 = 4, +}; + +struct pgp_key_v3_packet { + enum pgp_key_version version : 8; + struct pgp_time creation_time; + u8 expiry[2]; /* 0 or time in days till expiry */ + enum pgp_pubkey_algo pubkey_algo : 8; + u8 key_material[0]; +} __packed; + +struct pgp_key_v4_packet { + enum pgp_key_version version : 8; + struct pgp_time creation_time; + enum pgp_pubkey_algo pubkey_algo : 8; + u8 key_material[0]; +} __packed; + +/* + * Literal Data (tag 11) packet + */ +enum pgp_literal_data_format { + PGP_LIT_FORMAT_BINARY = 0x62, + PGP_LIT_FORMAT_TEXT = 0x74, + PGP_LIT_FORMAT_TEXT_UTF8 = 0x75, +}; + +#endif /* _LINUX_PGP_H */
From: David Howells dhowells@redhat.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
Provide a simple parser that extracts the packets from a PGP packet blob and passes the desirous ones to the given processor function:
struct pgp_parse_context { u64 types_of_interest; int (*process_packet)(struct pgp_parse_context *context, enum pgp_packet_tag type, u8 headerlen, const u8 *data, size_t datalen); };
int pgp_parse_packets(const u8 *data, size_t datalen, struct pgp_parse_context *ctx);
This is configured on with CONFIG_PGP_LIBRARY.
Changelog
v0: - fix style issues (Roberto Sassu) - declare pgp_to_public_key_algo (Roberto Sassu)
Signed-off-by: David Howells dhowells@redhat.com Co-developed-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- crypto/asymmetric_keys/Kconfig | 6 + crypto/asymmetric_keys/Makefile | 5 + crypto/asymmetric_keys/pgp_library.c | 281 +++++++++++++++++++++++++++ crypto/asymmetric_keys/pgp_parser.h | 23 +++ include/linux/pgplib.h | 48 +++++ 5 files changed, 363 insertions(+) create mode 100644 crypto/asymmetric_keys/pgp_library.c create mode 100644 crypto/asymmetric_keys/pgp_parser.h create mode 100644 include/linux/pgplib.h
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 1ef3b46d6f6e..00791af8de8e 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -85,4 +85,10 @@ config FIPS_SIGNATURE_SELFTEST depends on ASYMMETRIC_KEY_TYPE depends on PKCS7_MESSAGE_PARSER=X509_CERTIFICATE_PARSER
+config PGP_LIBRARY + tristate "PGP parsing library" + help + This option enables a library that provides a number of simple + utility functions for parsing PGP (RFC 4880) packet-based messages. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index 0d1fa1b692c6..5417575ac3bc 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -76,3 +76,8 @@ verify_signed_pefile-y := \
$(obj)/mscode_parser.o: $(obj)/mscode.asn1.h $(obj)/mscode.asn1.h $(obj)/mscode.asn1.o: $(obj)/mscode.asn1.c $(obj)/mscode.asn1.h + +# +# PGP handling +# +obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o diff --git a/crypto/asymmetric_keys/pgp_library.c b/crypto/asymmetric_keys/pgp_library.c new file mode 100644 index 000000000000..be307f426f75 --- /dev/null +++ b/crypto/asymmetric_keys/pgp_library.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0 +/* PGP packet parser (RFC 4880) + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PGPL: "fmt +#include <linux/pgplib.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> + +MODULE_LICENSE("GPL"); + +const char *const pgp_hash_algorithms[PGP_HASH__LAST] = { + [PGP_HASH_MD5] = "md5", + [PGP_HASH_SHA1] = "sha1", + [PGP_HASH_RIPE_MD_160] = "rmd160", + [PGP_HASH_SHA256] = "sha256", + [PGP_HASH_SHA384] = "sha384", + [PGP_HASH_SHA512] = "sha512", + [PGP_HASH_SHA224] = "sha224", +}; +EXPORT_SYMBOL_GPL(pgp_hash_algorithms); + +const char *pgp_to_public_key_algo[PGP_PUBKEY__LAST] = { + [PGP_PUBKEY_RSA_ENC_OR_SIG] = "rsa", + [PGP_PUBKEY_RSA_ENC_ONLY] = "rsa", + [PGP_PUBKEY_RSA_SIG_ONLY] = "rsa", + [PGP_PUBKEY_ELGAMAL] = NULL, + [PGP_PUBKEY_DSA] = NULL, +}; +EXPORT_SYMBOL_GPL(pgp_to_public_key_algo); + +/** + * pgp_parse_packet_header - Parse a PGP packet header + * @_data: Start of the PGP packet (updated to PGP packet data) + * @_datalen: Amount of data remaining in buffer (decreased) + * @_type: Where the packet type will be returned + * @_headerlen: Where the header length will be returned + * + * Parse a set of PGP packet header [RFC 4880: 4.2]. + * + * Returns packet data size on success; non-zero on error. If successful, + * *_data and *_datalen will have been updated and *_headerlen will be set to + * hold the length of the packet header. + */ +static ssize_t pgp_parse_packet_header(const u8 **_data, size_t *_datalen, + enum pgp_packet_tag *_type, + u8 *_headerlen) +{ + enum pgp_packet_tag type; + const u8 *data = *_data; + size_t size, datalen = *_datalen; + + pr_devel("-->%s(,%zu,,)\n", __func__, datalen); + + if (datalen < 2) + goto short_packet; + + pr_devel("pkthdr %02x, %02x\n", data[0], data[1]); + + type = *data++; + datalen--; + if (!(type & 0x80)) { + pr_debug("Packet type does not have MSB set\n"); + return -EBADMSG; + } + type &= ~0x80; + + if (type & 0x40) { + /* New packet length format */ + type &= ~0x40; + pr_devel("new format: t=%u\n", type); + switch (data[0]) { + case 0x00 ... 0xbf: + /* One-byte length */ + size = data[0]; + data++; + datalen--; + *_headerlen = 2; + break; + case 0xc0 ... 0xdf: + /* Two-byte length */ + if (datalen < 2) + goto short_packet; + size = (data[0] - 192) * 256; + size += data[1] + 192; + data += 2; + datalen -= 2; + *_headerlen = 3; + break; + case 0xff: + /* Five-byte length */ + if (datalen < 5) + goto short_packet; + size = data[1] << 24; + size |= data[2] << 16; + size |= data[3] << 8; + size |= data[4]; + data += 5; + datalen -= 5; + *_headerlen = 6; + break; + default: + pr_debug("Partial body length packet not supported\n"); + return -EBADMSG; + } + } else { + /* Old packet length format */ + u8 length_type = type & 0x03; + + type >>= 2; + pr_devel("old format: t=%u lt=%u\n", type, length_type); + + switch (length_type) { + case 0: + /* One-byte length */ + size = data[0]; + data++; + datalen--; + *_headerlen = 2; + break; + case 1: + /* Two-byte length */ + if (datalen < 2) + goto short_packet; + size = data[0] << 8; + size |= data[1]; + data += 2; + datalen -= 2; + *_headerlen = 3; + break; + case 2: + /* Four-byte length */ + if (datalen < 4) + goto short_packet; + size = data[0] << 24; + size |= data[1] << 16; + size |= data[2] << 8; + size |= data[3]; + data += 4; + datalen -= 4; + *_headerlen = 5; + break; + default: + pr_debug("Indefinite length packet not supported\n"); + return -EBADMSG; + } + } + + pr_devel("datalen=%zu size=%zu", datalen, size); + if (datalen < size) + goto short_packet; + if ((int)size < 0) + goto too_big; + + *_data = data; + *_datalen = datalen; + *_type = type; + pr_devel("Found packet type=%u size=%zd\n", type, size); + return size; + +short_packet: + pr_debug("Attempt to parse short packet\n"); + return -EBADMSG; +too_big: + pr_debug("Signature subpacket size >2G\n"); + return -EMSGSIZE; +} + +/** + * pgp_parse_packets - Parse a set of PGP packets + * @_data: Data to be parsed (updated) + * @_datalen: Amount of data (updated) + * @ctx: Parsing context + * + * Parse a set of PGP packets [RFC 4880: 4]. + */ +int pgp_parse_packets(const u8 *data, size_t datalen, + struct pgp_parse_context *ctx) +{ + enum pgp_packet_tag type; + ssize_t pktlen; + u8 headerlen; + int ret; + + while (datalen > 2) { + pktlen = pgp_parse_packet_header(&data, &datalen, &type, + &headerlen); + if (pktlen < 0) + return pktlen; + + if ((ctx->types_of_interest >> type) & 1) { + ret = ctx->process_packet(ctx, type, headerlen, + data, pktlen); + if (ret < 0) + return ret; + } + data += pktlen; + datalen -= pktlen; + } + + if (datalen != 0) { + pr_debug("Excess octets in packet stream\n"); + return -EBADMSG; + } + + return 0; +} +EXPORT_SYMBOL_GPL(pgp_parse_packets); + +/** + * pgp_parse_public_key - Parse the common part of a PGP pubkey packet + * @_data: Content of packet (updated) + * @_datalen: Length of packet remaining (updated) + * @pk: Public key data + * + * Parse the common data struct for a PGP pubkey packet [RFC 4880: 5.5.2]. + */ +int pgp_parse_public_key(const u8 **_data, size_t *_datalen, + struct pgp_parse_pubkey *pk) +{ + const u8 *data = *_data; + size_t datalen = *_datalen; + unsigned int tmp; + + if (datalen < 12) { + pr_debug("Public key packet too short\n"); + return -EBADMSG; + } + + pk->version = *data++; + switch (pk->version) { + case PGP_KEY_VERSION_2: + case PGP_KEY_VERSION_3: + case PGP_KEY_VERSION_4: + break; + default: + pr_debug("Public key packet with unhandled version %d\n", + pk->version); + return -EBADMSG; + } + + tmp = *data++ << 24; + tmp |= *data++ << 16; + tmp |= *data++ << 8; + tmp |= *data++; + pk->creation_time = tmp; + if (pk->version == PGP_KEY_VERSION_4) { + pk->expires_at = 0; /* Have to get it from the selfsignature */ + } else { + unsigned short ndays; + + ndays = *data++ << 8; + ndays |= *data++; + if (ndays) + pk->expires_at = pk->creation_time + ndays * 86400UL; + else + pk->expires_at = 0; + datalen -= 2; + } + + pk->pubkey_algo = *data++; + datalen -= 6; + + pr_devel("%x,%x,%lx,%lx\n", + pk->version, pk->pubkey_algo, pk->creation_time, + pk->expires_at); + + *_data = data; + *_datalen = datalen; + return 0; +} +EXPORT_SYMBOL_GPL(pgp_parse_public_key); diff --git a/crypto/asymmetric_keys/pgp_parser.h b/crypto/asymmetric_keys/pgp_parser.h new file mode 100644 index 000000000000..56f9a2b48679 --- /dev/null +++ b/crypto/asymmetric_keys/pgp_parser.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* PGP crypto data parser internal definitions + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/pgp.h> + +#define kenter(FMT, ...) \ + pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__) +#define kleave(FMT, ...) \ + pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__) + +/* + * pgp_library.c + */ +extern const char *pgp_to_public_key_algo[PGP_PUBKEY__LAST]; diff --git a/include/linux/pgplib.h b/include/linux/pgplib.h new file mode 100644 index 000000000000..a4068bcef8c5 --- /dev/null +++ b/include/linux/pgplib.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* PGP library definitions (RFC 4880) + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_PGPLIB_H +#define _LINUX_PGPLIB_H + +#if IS_ENABLED(CONFIG_PGP_LIBRARY) || IS_ENABLED(CONFIG_PGP_LIBRARY_MODULE) + +#include <linux/pgp.h> + +/* + * PGP library packet parser + */ +struct pgp_parse_context { + u64 types_of_interest; + int (*process_packet)(struct pgp_parse_context *context, + enum pgp_packet_tag type, + u8 headerlen, + const u8 *data, + size_t datalen); +}; + +extern int pgp_parse_packets(const u8 *data, size_t datalen, + struct pgp_parse_context *ctx); + +struct pgp_parse_pubkey { + enum pgp_key_version version : 8; + enum pgp_pubkey_algo pubkey_algo : 8; + __kernel_old_time_t creation_time; + __kernel_old_time_t expires_at; +}; + +extern int pgp_parse_public_key(const u8 **_data, size_t *_datalen, + struct pgp_parse_pubkey *pk); + + +#endif /* CONFIG_PGP_LIBRARY */ + +#endif /* _LINUX_PGPLIB_H */
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
Implement a PGP data parser for the crypto key type to use when instantiating a key.
This parser attempts to parse the instantiation data as a PGP packet sequence (RFC 4880) and if it parses okay, attempts to extract a public-key algorithm key or subkey from it.
If it finds such a key, it will set up a public_key subtype payload with appropriate handler routines (DSA or RSA) and attach it to the key.
Thanks to Tetsuo Handa penguin-kernel@I-love.SAKURA.ne.jp for pointing out some errors.
Changelog
v0: - remove declaration of pgp_to_public_key_algo (Roberto Sassu) - replace prep->type_data with prep->payload.data (Roberto Sassu) - replace public_key_destroy() with public_key_free() (Roberto Sassu) - use asymmetric_key_generate_id() to generate the key identifiers (Roberto Sassu) - add raw_fingerprint to pgp_key_data_parse_context structure (Roberto Sassu) - replace algorithm identifiers with strings (Roberto Sassu) - don't parse MPIs (Roberto Sassu) - don't process Public-Subkey packets (only one key will be created) (Roberto Sassu)
Signed-off-by: David Howells dhowells@redhat.com Co-developed-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- crypto/asymmetric_keys/Kconfig | 11 + crypto/asymmetric_keys/Makefile | 4 + crypto/asymmetric_keys/pgp_public_key.c | 339 ++++++++++++++++++++++++ 3 files changed, 354 insertions(+) create mode 100644 crypto/asymmetric_keys/pgp_public_key.c
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 00791af8de8e..62d163fb6951 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -91,4 +91,15 @@ config PGP_LIBRARY This option enables a library that provides a number of simple utility functions for parsing PGP (RFC 4880) packet-based messages.
+config PGP_KEY_PARSER + tristate "PGP key parser" + depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select PGP_LIBRARY + select MD5 # V3 fingerprint generation + select SHA1 # V4 fingerprint generation + help + This option provides support for parsing PGP (RFC 4880) format blobs + for key data and provides the ability to instantiate a crypto key + from a public key packet found inside the blob. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index 5417575ac3bc..75f3c3022dd9 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -81,3 +81,7 @@ $(obj)/mscode.asn1.o: $(obj)/mscode.asn1.c $(obj)/mscode.asn1.h # PGP handling # obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o + +obj-$(CONFIG_PGP_KEY_PARSER) += pgp_key_parser.o +pgp_key_parser-y := \ + pgp_public_key.o diff --git a/crypto/asymmetric_keys/pgp_public_key.c b/crypto/asymmetric_keys/pgp_public_key.c new file mode 100644 index 000000000000..49913b346c18 --- /dev/null +++ b/crypto/asymmetric_keys/pgp_public_key.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Instantiate a public key crypto key from PGP format data [RFC 4880] + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PGP: "fmt +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/mpi.h> +#include <linux/pgplib.h> +#include <keys/asymmetric-subtype.h> +#include <keys/asymmetric-parser.h> +#include <crypto/hash.h> +#include <crypto/public_key.h> +#include "pgp_parser.h" + +#define MAX_MPI 5 + +MODULE_LICENSE("GPL"); + +static inline void digest_putc(struct shash_desc *digest, uint8_t ch) +{ + crypto_shash_update(digest, &ch, 1); +} + +struct pgp_key_data_parse_context { + struct pgp_parse_context pgp; + struct public_key *pub; + unsigned char *raw_fingerprint; + char *fingerprint; +}; + +/* + * Calculate the public key ID (RFC4880 12.2) + */ +static int pgp_calc_pkey_keyid(struct shash_desc *digest, + struct pgp_parse_pubkey *pgp, + struct public_key *pub) +{ + unsigned int nb[MAX_MPI]; + unsigned int nn[MAX_MPI]; + unsigned int n; + size_t keylen = pub->keylen; + u8 *key_ptr = pub->key; + u8 *pp[MAX_MPI]; + u32 a32; + int npkey; + int i, ret; + + kenter(""); + + n = (pgp->version < PGP_KEY_VERSION_4) ? 8 : 6; + for (i = 0; i < MAX_MPI && keylen > 0; i++) { + ret = mpi_key_length(key_ptr, keylen, nb + i, nn + i); + if (ret < 0) + return ret; + + pp[i] = key_ptr + 2; + key_ptr += 2 + nn[i]; + keylen -= 2 + nn[i]; + n += 2 + nn[i]; + } + + if (keylen != 0) { + pr_debug("excess %zu\n", keylen); + return -EBADMSG; + } + + npkey = i; + + digest_putc(digest, 0x99); /* ctb */ + digest_putc(digest, n >> 8); /* 16-bit header length */ + digest_putc(digest, n); + digest_putc(digest, pgp->version); + + a32 = pgp->creation_time; + digest_putc(digest, a32 >> 24); + digest_putc(digest, a32 >> 16); + digest_putc(digest, a32 >> 8); + digest_putc(digest, a32 >> 0); + + if (pgp->version < PGP_KEY_VERSION_4) { + u16 a16; + + if (pgp->expires_at) + a16 = (pgp->expires_at - pgp->creation_time) / 86400UL; + else + a16 = 0; + digest_putc(digest, a16 >> 8); + digest_putc(digest, a16 >> 0); + } + + digest_putc(digest, pgp->pubkey_algo); + + for (i = 0; i < npkey; i++) { + digest_putc(digest, nb[i] >> 8); + digest_putc(digest, nb[i]); + crypto_shash_update(digest, pp[i], nn[i]); + } + ret = 0; + + kleave(" = %d", ret); + return ret; +} + +/* + * Calculate the public key ID fingerprint + */ +static int pgp_generate_fingerprint(struct pgp_key_data_parse_context *ctx, + struct pgp_parse_pubkey *pgp, + struct public_key *pub) +{ + struct crypto_shash *tfm; + struct shash_desc *digest; + char *fingerprint; + u8 *raw_fingerprint; + int digest_size, offset; + int ret, i; + + ret = -ENOMEM; + tfm = crypto_alloc_shash(pgp->version < PGP_KEY_VERSION_4 ? + "md5" : "sha1", 0, 0); + if (!tfm) + goto cleanup; + + digest = kmalloc(sizeof(*digest) + crypto_shash_descsize(tfm), + GFP_KERNEL); + if (!digest) + goto cleanup_tfm; + + digest->tfm = tfm; + crypto_shash_set_flags(digest->tfm, CRYPTO_TFM_REQ_MAY_SLEEP); + ret = crypto_shash_init(digest); + if (ret < 0) + goto cleanup_hash; + + ret = pgp_calc_pkey_keyid(digest, pgp, pub); + if (ret < 0) + goto cleanup_hash; + + digest_size = crypto_shash_digestsize(tfm); + + raw_fingerprint = kmalloc(digest_size, GFP_KERNEL); + if (!raw_fingerprint) + goto cleanup_hash; + + ret = crypto_shash_final(digest, raw_fingerprint); + if (ret < 0) + goto cleanup_raw_fingerprint; + + fingerprint = kmalloc(digest_size * 2 + 1, GFP_KERNEL); + if (!fingerprint) + goto cleanup_raw_fingerprint; + + offset = digest_size - 8; + pr_debug("offset %u/%u\n", offset, digest_size); + + for (i = 0; i < digest_size; i++) + sprintf(fingerprint + i * 2, "%02x", raw_fingerprint[i]); + pr_debug("fingerprint %s\n", fingerprint); + + ctx->raw_fingerprint = raw_fingerprint; + ctx->fingerprint = fingerprint; + ret = 0; +cleanup_raw_fingerprint: + if (ret < 0) + kfree(raw_fingerprint); +cleanup_hash: + kfree(digest); +cleanup_tfm: + crypto_free_shash(tfm); +cleanup: + kleave(" = %d", ret); + return ret; +} + +/* + * Extract a public key or public subkey from the PGP stream. + */ +static int pgp_process_public_key(struct pgp_parse_context *context, + enum pgp_packet_tag type, + u8 headerlen, + const u8 *data, + size_t datalen) +{ + const char *algo; + struct pgp_key_data_parse_context *ctx = + container_of(context, struct pgp_key_data_parse_context, pgp); + struct pgp_parse_pubkey pgp; + struct public_key *pub; + int ret; + + kenter(",%u,%u,,%zu", type, headerlen, datalen); + + if (ctx->fingerprint) { + kleave(" = -ENOKEY [already]"); + return -EBADMSG; + } + + pub = kzalloc(sizeof(struct public_key), GFP_KERNEL); + if (!pub) + return -ENOMEM; + pub->id_type = "PGP"; + + ret = pgp_parse_public_key(&data, &datalen, &pgp); + if (ret < 0) + goto cleanup; + + if (pgp.pubkey_algo >= PGP_PUBKEY__LAST) + goto cleanup_unsupported_pkey_algo; + algo = pgp_to_public_key_algo[pgp.pubkey_algo]; + if (!algo) + goto cleanup_unsupported_pkey_algo; + pub->pkey_algo = algo; + + pub->key = kmemdup(data, datalen, GFP_KERNEL); + if (!pub->key) + goto cleanup_nomem; + + pub->keylen = datalen; + + ret = pgp_generate_fingerprint(ctx, &pgp, pub); + if (ret < 0) + goto cleanup; + + ctx->pub = pub; + kleave(" = 0 [use]"); + return 0; + +cleanup_unsupported_pkey_algo: + pr_debug("Unsupported public key algorithm %u\n", + pgp.pubkey_algo); + ret = -ENOPKG; + goto cleanup; +cleanup_nomem: + ret = -ENOMEM; + goto cleanup; +cleanup: + pr_devel("cleanup"); + kfree(pub->key); + kfree(pub); + kleave(" = %d", ret); + return ret; +} + +static struct asymmetric_key_ids *pgp_key_generate_id( + struct pgp_key_data_parse_context *ctx) +{ + struct asymmetric_key_ids *kids; + struct asymmetric_key_id *kid; + int fingerprint_len = strlen(ctx->fingerprint) / 2; + + kids = kzalloc(sizeof(struct asymmetric_key_ids), GFP_KERNEL); + if (!kids) + return kids; + + kid = asymmetric_key_generate_id(ctx->raw_fingerprint, fingerprint_len, + NULL, 0); + if (IS_ERR(kid)) + goto error; + + kids->id[0] = kid; + kids->id[1] = kmemdup(kid, sizeof(kid) + fingerprint_len, GFP_KERNEL); + if (!kids->id[1]) + goto error; + + return kids; +error: + kfree(kids->id[0]); + kfree(kids); + + return NULL; +} + +/* + * Attempt to parse the instantiation data blob for a key as a PGP packet + * message holding a key. + */ +static int pgp_key_parse(struct key_preparsed_payload *prep) +{ + struct pgp_key_data_parse_context ctx; + int ret; + + kenter(""); + + memset(&ctx, 0, sizeof(ctx)); + ctx.pgp.types_of_interest = (1 << PGP_PKT_PUBLIC_KEY); + ctx.pgp.process_packet = pgp_process_public_key; + + ret = pgp_parse_packets(prep->data, prep->datalen, &ctx.pgp); + if (ret < 0) + goto error; + + /* We're pinning the module by being linked against it */ + __module_get(public_key_subtype.owner); + prep->payload.data[asym_subtype] = &public_key_subtype; + prep->payload.data[asym_key_ids] = pgp_key_generate_id(&ctx); + prep->payload.data[asym_crypto] = ctx.pub; + prep->quotalen = 100; + kfree(ctx.fingerprint); + kfree(ctx.raw_fingerprint); + return 0; + +error: + public_key_free(ctx.pub); + kfree(ctx.fingerprint); + kfree(ctx.raw_fingerprint); + return ret; +} + +static struct asymmetric_key_parser pgp_key_parser = { + .owner = THIS_MODULE, + .name = "pgp", + .parse = pgp_key_parse, +}; + +/* + * Module stuff + */ +static int __init pgp_key_init(void) +{ + return register_asymmetric_key_parser(&pgp_key_parser); +} + +static void __exit pgp_key_exit(void) +{ + unregister_asymmetric_key_parser(&pgp_key_parser); +} + +module_init(pgp_key_init); +module_exit(pgp_key_exit);
From: David Howells dhowells@redhat.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
Provide a facility to autogenerate the name of PGP keys from the contents of the payload. If add_key() is given a blank description, a description is constructed from the last user ID packet in the payload data plus the last 8 hex digits of the key ID. For instance:
keyctl padd asymmetric "" @s </tmp/key.pub
might create a key with a constructed description that can be seen in /proc/keys:
2f674b96 I--Q--- 1 perm 39390000 0 0 crypto \ Sample kernel key 31f0ae93: PGP.RSA 31f0ae93 []
Changelog
v0: - fix style issues (Roberto Sassu)
Signed-off-by: David Howells dhowells@redhat.com Co-developed-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- crypto/asymmetric_keys/pgp_public_key.c | 46 ++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-)
diff --git a/crypto/asymmetric_keys/pgp_public_key.c b/crypto/asymmetric_keys/pgp_public_key.c index 49913b346c18..27b9efeafc4f 100644 --- a/crypto/asymmetric_keys/pgp_public_key.c +++ b/crypto/asymmetric_keys/pgp_public_key.c @@ -36,6 +36,9 @@ struct pgp_key_data_parse_context { struct public_key *pub; unsigned char *raw_fingerprint; char *fingerprint; + const char *user_id; + size_t user_id_len; + size_t fingerprint_len; };
/* @@ -156,6 +159,7 @@ static int pgp_generate_fingerprint(struct pgp_key_data_parse_context *ctx, if (ret < 0) goto cleanup_raw_fingerprint;
+ ctx->fingerprint_len = digest_size * 2; fingerprint = kmalloc(digest_size * 2 + 1, GFP_KERNEL); if (!fingerprint) goto cleanup_raw_fingerprint; @@ -200,6 +204,13 @@ static int pgp_process_public_key(struct pgp_parse_context *context,
kenter(",%u,%u,,%zu", type, headerlen, datalen);
+ if (type == PGP_PKT_USER_ID) { + ctx->user_id = data; + ctx->user_id_len = datalen; + kleave(" = 0 [user ID]"); + return 0; + } + if (ctx->fingerprint) { kleave(" = -ENOKEY [already]"); return -EBADMSG; @@ -292,13 +303,46 @@ static int pgp_key_parse(struct key_preparsed_payload *prep) kenter("");
memset(&ctx, 0, sizeof(ctx)); - ctx.pgp.types_of_interest = (1 << PGP_PKT_PUBLIC_KEY); + ctx.pgp.types_of_interest = (1 << PGP_PKT_PUBLIC_KEY) | + (1 << PGP_PKT_USER_ID); ctx.pgp.process_packet = pgp_process_public_key;
ret = pgp_parse_packets(prep->data, prep->datalen, &ctx.pgp); if (ret < 0) goto error;
+ if (ctx.user_id && ctx.user_id_len > 0) { + /* Propose a description for the key + * (user ID without the comment) + */ + size_t ulen = ctx.user_id_len, flen = ctx.fingerprint_len; + const char *p; + + p = memchr(ctx.user_id, '(', ulen); + if (p) { + /* Remove the comment */ + do { + p--; + } while (*p == ' ' && p > ctx.user_id); + if (*p != ' ') + p++; + ulen = p - ctx.user_id; + } + + if (ulen > 255 - 9) + ulen = 255 - 9; + prep->description = kmalloc(ulen + 1 + 8 + 1, GFP_KERNEL); + ret = -ENOMEM; + if (!prep->description) + goto error; + memcpy(prep->description, ctx.user_id, ulen); + prep->description[ulen] = ' '; + memcpy(prep->description + ulen + 1, + ctx.fingerprint + flen - 8, 8); + prep->description[ulen + 9] = 0; + pr_debug("desc '%s'\n", prep->description); + } + /* We're pinning the module by being linked against it */ __module_get(public_key_subtype.owner); prep->payload.data[asym_subtype] = &public_key_subtype;
From: David Howells dhowells@redhat.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
Provide a function to load keys from a PGP keyring blob for use in initialising the module signing key keyring:
int preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen, struct key *keyring);
Descriptions are generated from user ID notes and key fingerprints. The keys will actually be identified by the ID calculated from the PGP data rather than by the description, so this shouldn't be a problem.
The keys are attached to the keyring supplied.
Looking as root in /proc/keys after the module signing keyring has been loaded:
383a00c1 I------ 1 perm 1f030000 0 0 asymmetri \ Red Hat, Inc. dbeca166: PGP.DSA dbeca166 []
Thanks to Tetsuo Handa penguin-kernel@I-love.SAKURA.ne.jp for some pointing out some errors.
Changelog
v0: - avoid Kconfig circular dependency (Roberto Sassu) - modify flags passed to key_create_or_update() (Roberto Sassu) - don't process Public-Subkey packets (Roberto Sassu)
Signed-off-by: David Howells dhowells@redhat.com Co-developed-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- crypto/asymmetric_keys/Kconfig | 8 ++ crypto/asymmetric_keys/Makefile | 1 + crypto/asymmetric_keys/pgp_preload.c | 119 +++++++++++++++++++++++++++ include/linux/pgp.h | 4 + 4 files changed, 132 insertions(+) create mode 100644 crypto/asymmetric_keys/pgp_preload.c
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 62d163fb6951..9b55ae747eaf 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -102,4 +102,12 @@ config PGP_KEY_PARSER for key data and provides the ability to instantiate a crypto key from a public key packet found inside the blob.
+config PGP_PRELOAD + bool "PGP public key preloading facility" + select PGP_KEY_PARSER + help + This option provides a facility for the kernel to preload PGP-wrapped + bundles of keys during boot. It is used by module signing to load + the module signing keys for example. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index 75f3c3022dd9..fd0b280892b6 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -81,6 +81,7 @@ $(obj)/mscode.asn1.o: $(obj)/mscode.asn1.c $(obj)/mscode.asn1.h # PGP handling # obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o +obj-$(CONFIG_PGP_PRELOAD) += pgp_preload.o
obj-$(CONFIG_PGP_KEY_PARSER) += pgp_key_parser.o pgp_key_parser-y := \ diff --git a/crypto/asymmetric_keys/pgp_preload.c b/crypto/asymmetric_keys/pgp_preload.c new file mode 100644 index 000000000000..418cd4785efb --- /dev/null +++ b/crypto/asymmetric_keys/pgp_preload.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Cryptographic key request handling + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * See Documentation/security/keys-crypto.txt + */ + +#include <linux/module.h> +#include <linux/key.h> +#include <linux/pgplib.h> +#include <linux/pgp.h> +#include <linux/err.h> +#include <keys/asymmetric-type.h> + +struct preload_pgp_keys_context { + struct pgp_parse_context pgp; + key_ref_t keyring; + const u8 *key_start; + const u8 *key_end; + bool found_key; +}; + +/* + * Create a key. + */ +static int __init create_pgp_key(struct preload_pgp_keys_context *ctx) +{ + key_ref_t key; + + key = key_create_or_update(ctx->keyring, + "asymmetric", + NULL, + ctx->key_start, + ctx->key_end - ctx->key_start, + ((KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ), + KEY_ALLOC_NOT_IN_QUOTA | + KEY_ALLOC_BUILT_IN | + KEY_ALLOC_BYPASS_RESTRICTION); + if (IS_ERR(key)) + return PTR_ERR(key); + + pr_notice("Loaded PGP key '%s'\n", + key_ref_to_ptr(key)->description); + + key_ref_put(key); + return 0; +} + +/* + * Extract a public key or subkey from the PGP stream. + */ +static int __init found_pgp_key(struct pgp_parse_context *context, + enum pgp_packet_tag type, u8 headerlen, + const u8 *data, size_t datalen) +{ + struct preload_pgp_keys_context *ctx = + container_of(context, struct preload_pgp_keys_context, pgp); + int ret; + + if (ctx->found_key) { + ctx->key_end = data - headerlen; + ret = create_pgp_key(ctx); + if (ret < 0) + return ret; + } + + ctx->key_start = data - headerlen; + ctx->found_key = true; + return 0; +} + +/** + * preload_pgp_keys - Load keys from a PGP keyring blob + * @pgpdata: The PGP keyring blob containing the keys. + * @pgpdatalen: The size of the @pgpdata blob. + * @keyring: The keyring to add the new keys to. + * + * Preload a pack of keys from a PGP keyring blob. + * + * The keys have their descriptions generated from the user ID and fingerprint + * in the PGP stream. Since keys can be matched on their key IDs independently + * of the key description, the description is mostly irrelevant apart from the + * fact that keys of the same description displace one another from a keyring. + * + * The caller should override the current creds if they want the keys to be + * owned by someone other than the current process's owner. Keys will not be + * accounted towards the owner's quota. + * + * This function may only be called whilst the kernel is booting. + */ +int __init preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen, + struct key *keyring) +{ + struct preload_pgp_keys_context ctx; + int ret; + + ctx.pgp.types_of_interest = (1 << PGP_PKT_PUBLIC_KEY); + ctx.pgp.process_packet = found_pgp_key; + ctx.keyring = make_key_ref(keyring, 1); + ctx.found_key = false; + + ret = pgp_parse_packets(pgpdata, pgpdatalen, &ctx.pgp); + if (ret < 0) + return ret; + + if (ctx.found_key) { + ctx.key_end = pgpdata + pgpdatalen; + return create_pgp_key(&ctx); + } + return 0; +} diff --git a/include/linux/pgp.h b/include/linux/pgp.h index 71186ac3128b..9104939c4770 100644 --- a/include/linux/pgp.h +++ b/include/linux/pgp.h @@ -14,6 +14,7 @@ #define _LINUX_PGP_H
#include <linux/types.h> +#include <linux/key.h>
struct pgp_key_ID { u8 id[8]; @@ -213,4 +214,7 @@ enum pgp_literal_data_format { PGP_LIT_FORMAT_TEXT_UTF8 = 0x75, };
+int __init preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen, + struct key *keyring); + #endif /* _LINUX_PGP_H */
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
Preload PGP keys from 'pubring.gpg', placed in certs/ of the kernel source directory.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- certs/Kconfig | 7 +++++++ certs/Makefile | 7 +++++++ certs/system_certificates.S | 18 ++++++++++++++++++ certs/system_keyring.c | 22 ++++++++++++++++++++++ 4 files changed, 54 insertions(+)
diff --git a/certs/Kconfig b/certs/Kconfig index 1f109b070877..3038adbaf58d 100644 --- a/certs/Kconfig +++ b/certs/Kconfig @@ -138,4 +138,11 @@ config SYSTEM_BLACKLIST_AUTH_UPDATE keyring. The PKCS#7 signature of the description is set in the key payload. Blacklist keys cannot be removed.
+config PGP_PRELOAD_PUBLIC_KEYS + bool "Preload PGP public keys" + select PGP_PRELOAD + default n + help + Provide a keyring of PGP public keys. + endmenu diff --git a/certs/Makefile b/certs/Makefile index 799ad7b9e68a..ab6da6f4e953 100644 --- a/certs/Makefile +++ b/certs/Makefile @@ -65,6 +65,13 @@ endif # CONFIG_MODULE_SIG_KEY
$(obj)/system_certificates.o: $(obj)/signing_key.x509
+ifdef CONFIG_PGP_PRELOAD_PUBLIC_KEYS +ifeq ($(shell ls $(srctree)/certs/pubring.gpg 2> /dev/null), $(srctree)/certs/pubring.gpg) +system_certificates.o += -DHAVE_PUBRING_GPG +$(obj)/system_certificates.o: $(srctree)/certs/pubring.gpg +endif +endif + PKCS11_URI := $(filter pkcs11:%, $(CONFIG_MODULE_SIG_KEY)) ifdef PKCS11_URI $(obj)/signing_key.x509: extract-cert-in := $(PKCS11_URI) diff --git a/certs/system_certificates.S b/certs/system_certificates.S index 003e25d4a17e..b3cbf0811e3f 100644 --- a/certs/system_certificates.S +++ b/certs/system_certificates.S @@ -44,3 +44,21 @@ module_cert_size: #else .long __module_cert_end - __module_cert_start #endif + + .align 8 + .globl pgp_public_keys +pgp_public_keys: +__pgp_key_list_start: +#ifdef HAVE_PUBRING_GPG + .incbin "certs/pubring.gpg" +#endif +__pgp_key_list_end: + + .align 8 + .globl pgp_public_keys_size +pgp_public_keys_size: +#ifdef CONFIG_64BIT + .quad __pgp_key_list_end - __pgp_key_list_start +#else + .long __pgp_key_list_end - __pgp_key_list_start +#endif diff --git a/certs/system_keyring.c b/certs/system_keyring.c index a7a49b17ceb1..da4a8b470cbb 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -11,6 +11,7 @@ #include <linux/cred.h> #include <linux/err.h> #include <linux/slab.h> +#include <linux/pgp.h> #include <linux/uidgid.h> #include <linux/verification.h> #include <keys/asymmetric-type.h> @@ -216,6 +217,27 @@ static __init int load_system_certificate_list(void) } late_initcall(load_system_certificate_list);
+#ifdef CONFIG_PGP_PRELOAD_PUBLIC_KEYS +extern __initconst const u8 pgp_public_keys[]; +extern __initconst const unsigned long pgp_public_keys_size; + +/* + * Load a list of PGP keys. + */ +static __init int load_pgp_public_keyring(void) +{ + pr_notice("Load PGP public keys\n"); + + if (preload_pgp_keys(pgp_public_keys, + pgp_public_keys_size, + builtin_trusted_keys) < 0) + pr_err("Can't load PGP public keys\n"); + + return 0; +} +late_initcall(load_pgp_public_keyring); +#endif /* CONFIG_PGP_PRELOAD_PUBLIC_KEYS */ + #ifdef CONFIG_SYSTEM_DATA_VERIFICATION
/**
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,转换为PR失败! 邮件列表地址:https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/U... 失败原因:补丁集缺失封面信息 建议解决方法:请提供补丁集并重新发送您的补丁集到邮件列表
FeedBack: The patch(es) which you have sent to kernel@openeuler.org has been converted to PR failed! Mailing list address: https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/U... Failed Reason: the cover of the patches is missing Suggest Solution: please checkout and apply the patches' cover and send all again
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
Introduce search_trusted_key() to extend the key search to the primary or secondary built-in keyrings.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- certs/system_keyring.c | 22 ++++++++++++++++++++++ include/linux/verification.h | 4 ++++ 2 files changed, 26 insertions(+)
diff --git a/certs/system_keyring.c b/certs/system_keyring.c index da4a8b470cbb..e9770f4e5449 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -361,6 +361,28 @@ int verify_pkcs7_signature(const void *data, size_t len, } EXPORT_SYMBOL_GPL(verify_pkcs7_signature);
+struct key *search_trusted_key(struct key *trusted_keys, struct key_type *type, + char *name) +{ + key_ref_t kref; + + if (!trusted_keys) { + trusted_keys = builtin_trusted_keys; + } else if (trusted_keys == VERIFY_USE_SECONDARY_KEYRING) { +#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING + trusted_keys = secondary_trusted_keys; +#else + trusted_keys = builtin_trusted_keys; +#endif + } + kref = keyring_search(make_key_ref(trusted_keys, 1), type, name, true); + if (IS_ERR(kref)) + return ERR_CAST(kref); + + return key_ref_to_ptr(kref); +} +EXPORT_SYMBOL_GPL(search_trusted_key); + #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
#ifdef CONFIG_INTEGRITY_PLATFORM_KEYRING diff --git a/include/linux/verification.h b/include/linux/verification.h index f34e50ebcf60..4eac691f82b6 100644 --- a/include/linux/verification.h +++ b/include/linux/verification.h @@ -9,6 +9,7 @@ #define _LINUX_VERIFICATION_H
#include <linux/types.h> +#include <linux/key.h>
/* * Indicate that both builtin trusted keys and secondary trusted keys @@ -68,5 +69,8 @@ extern int verify_pefile_signature(const void *pebuf, unsigned pelen, enum key_being_used_for usage); #endif
+struct key *search_trusted_key(struct key *trusted_keys, struct key_type *type, + char *name); + #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */ #endif /* _LINUX_VERIFY_PEFILE_H */
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
This patch calls search_trusted_key() in request_asymmetric_key() if the key is not found in the IMA/EVM keyrings.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/digsig_asymmetric.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index 895f4b9ce8c6..8c04cb433840 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -9,6 +9,7 @@ #include <linux/err.h> #include <linux/ratelimit.h> #include <linux/key-type.h> +#include <linux/verification.h> #include <crypto/public_key.h> #include <crypto/hash_info.h> #include <keys/asymmetric-type.h> @@ -54,6 +55,15 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) key = request_key(&key_type_asymmetric, name, NULL); }
+ if (IS_ERR(key)) { +#ifdef CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY + keyring = VERIFY_USE_SECONDARY_KEYRING; +#else + keyring = NULL; +#endif + key = search_trusted_key(keyring, &key_type_asymmetric, name); + } + if (IS_ERR(key)) { if (keyring) pr_err_ratelimited("Request for unknown key '%s' in '%s' keyring. err %ld\n",
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
This patch allows direct upload of digest lists by user space parsers. This operation is possible if the digest of the process's executable is found in the digest lists and its type is COMPACT_PARSER.
ima_check_measured_appraised() is called at the end of ima_file_check() to verify that everything accessed by the user space parsers (except for directories and securityfs) has been processed by IMA. If a digest list was not processed by an IMA submodule, digest list lookup is disabled for that submodule.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/ima/ima_appraise.c | 2 +- security/integrity/ima/ima_digest_list.c | 56 ++++++++++++++++++++++++ security/integrity/ima/ima_digest_list.h | 18 ++++++++ security/integrity/ima/ima_fs.c | 22 ++++++++++ security/integrity/ima/ima_main.c | 40 ++++++++++------- 5 files changed, 120 insertions(+), 18 deletions(-)
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 5eca0fb771aa..cdcc20339c8e 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -597,7 +597,7 @@ int ima_appraise_measurement(enum ima_hooks func, /* Allow access to digest lists without metadata, only if they * are signed or found in a digest list (immutable) */ - if (func == DIGEST_LIST_CHECK) { + if (func == DIGEST_LIST_CHECK || ima_current_is_parser()) { if (xattr_value->type == EVM_IMA_XATTR_DIGSIG) break; if (found_digest && diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index 42b2306e43cc..b5d1004e3040 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -20,6 +20,8 @@ #include <linux/file.h> #include <linux/namei.h> #include <linux/xattr.h> +#include <linux/sched/mm.h> +#include <linux/magic.h>
#include "ima.h" #include "ima_digest_list.h" @@ -216,6 +218,10 @@ void ima_check_measured_appraised(struct file *file) if (!ima_digest_list_actions) return;
+ if (file_inode(file)->i_sb->s_magic == SECURITYFS_MAGIC || + S_ISDIR(file_inode(file)->i_mode)) + return; + iint = integrity_iint_find(file_inode(file)); if (!iint) { pr_err("%s not processed, disabling digest lists lookup\n", @@ -342,3 +348,53 @@ void __init ima_load_digest_lists(void) out: path_put(&path); } + +/**************** + * Parser check * + ****************/ +bool ima_check_current_is_parser(void) +{ + struct integrity_iint_cache *parser_iint; + struct file *parser_file; + struct mm_struct *mm; + + mm = get_task_mm(current); + if (!mm) + return false; + + parser_file = get_mm_exe_file(mm); + mmput(mm); + + if (!parser_file) + return false; + + parser_iint = integrity_iint_find(file_inode(parser_file)); + fput(parser_file); + + if (!parser_iint) + return false; + + /* flag cannot be cleared due to write protection of executables */ + if (!(parser_iint->flags & IMA_COLLECTED)) + return false; + + return ima_lookup_digest(parser_iint->ima_hash->digest, + parser_iint->ima_hash->algo, COMPACT_PARSER); +} + +struct task_struct *parser_task; + +void ima_set_parser(void) +{ + parser_task = current; +} + +void ima_unset_parser(void) +{ + parser_task = NULL; +} + +bool ima_current_is_parser(void) +{ + return (current == parser_task); +} diff --git a/security/integrity/ima/ima_digest_list.h b/security/integrity/ima/ima_digest_list.h index 5bd2388ff95e..4a8b0e000ad3 100644 --- a/security/integrity/ima/ima_digest_list.h +++ b/security/integrity/ima/ima_digest_list.h @@ -24,6 +24,10 @@ extern struct ima_h_table ima_digests_htable;
int ima_parse_compact_list(loff_t size, void *buf, int op); void ima_check_measured_appraised(struct file *file); +bool ima_check_current_is_parser(void); +void ima_set_parser(void); +void ima_unset_parser(void); +bool ima_current_is_parser(void); #else static inline int ima_parse_compact_list(loff_t size, void *buf, int op) { @@ -32,5 +36,19 @@ static inline int ima_parse_compact_list(loff_t size, void *buf, int op) static inline void ima_check_measured_appraised(struct file *file) { } +static inline bool ima_check_current_is_parser(void) +{ + return false; +} +static inline void ima_set_parser(void) +{ +} +static inline void ima_unset_parser(void) +{ +} +static inline bool ima_current_is_parser(void) +{ + return false; +} #endif /*CONFIG_IMA_DIGEST_LIST*/ #endif /*LINUX_IMA_DIGEST_LIST_H*/ diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 2998c3dfbc30..7310683a7af7 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -390,6 +390,20 @@ static ssize_t ima_write_data(struct file *file, const char __user *buf, } else { result = ima_parse_add_rule(data); } + } else if (dentry == digest_list_data) { + if (!ima_current_is_parser()) { + result = -EACCES; + } else { + result = ima_parse_compact_list(datalen, data, + DIGEST_LIST_OP_ADD); + } + } else if (dentry == digest_list_data_del) { + if (!ima_current_is_parser()) { + result = -EACCES; + } else { + result = ima_parse_compact_list(datalen, data, + DIGEST_LIST_OP_DEL); + } } else { pr_err("Unknown data type\n"); result = -EINVAL; @@ -461,6 +475,11 @@ static int ima_open_data_upload(struct inode *inode, struct file *filp) } if (test_and_set_bit(flag, &ima_fs_flags)) return -EBUSY; + + if (dentry == digest_list_data || dentry == digest_list_data_del) + if (ima_check_current_is_parser()) + ima_set_parser(); + return 0; }
@@ -480,6 +499,9 @@ static int ima_release_data_upload(struct inode *inode, struct file *file) if ((file->f_flags & O_ACCMODE) == O_RDONLY) return seq_release(inode, file);
+ if (dentry == digest_list_data || dentry == digest_list_data_del) + ima_unset_parser(); + if (dentry != ima_policy) { clear_bit(flag, &ima_fs_flags); return 0; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 875dbd5d3de9..cac1362bec86 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -584,18 +584,18 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot) */ int ima_bprm_check(struct linux_binprm *bprm) { - int ret; - u32 secid; - - security_current_getsecid_subj(&secid); - ret = process_measurement(bprm->file, current_cred(), secid, NULL, 0, - MAY_EXEC, BPRM_CHECK); - if (ret) - return ret; - - security_cred_getsecid(bprm->cred, &secid); - return process_measurement(bprm->file, bprm->cred, secid, NULL, 0, - MAY_EXEC, CREDS_CHECK); + int ret; + u32 secid; + + security_current_getsecid_subj(&secid); + ret = process_measurement(bprm->file, current_cred(), secid, NULL, 0, + MAY_EXEC, BPRM_CHECK); + if (ret) + return ret; + + security_cred_getsecid(bprm->cred, &secid); + return process_measurement(bprm->file, bprm->cred, secid, NULL, 0, + MAY_EXEC, CREDS_CHECK); }
/** @@ -610,12 +610,18 @@ int ima_bprm_check(struct linux_binprm *bprm) */ int ima_file_check(struct file *file, int mask) { - u32 secid; + int ret; + u32 secid; + int rc; + + security_current_getsecid_subj(&secid); + rc = process_measurement(file, current_cred(), secid, NULL, 0, + mask & (MAY_READ | MAY_WRITE | MAY_EXEC | + MAY_APPEND), FILE_CHECK); + if (ima_current_is_parser() && !rc) + ima_check_measured_appraised(file); + return rc;
- security_current_getsecid_subj(&secid); - return process_measurement(file, current_cred(), secid, NULL, 0, - mask & (MAY_READ | MAY_WRITE | MAY_EXEC | - MAY_APPEND), FILE_CHECK); } EXPORT_SYMBOL_GPL(ima_file_check);
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
This patch adds the new policy keyword 'parser' to measure and appraise any file opened by the user space parser, while the parser opened digest_list_data_add or digest_list_data_del securityfs interfaces.
This ensures that all files processed by the user space parser are processed without including the FILE_CHECK hook in the policy. With this keyword it would be possible to have a policy to measure/appraise only executable code and digest lists.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/ima/ima_policy.c | 33 +++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-)
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 88cd1778680a..582d81f8bfde 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -20,6 +20,7 @@ #include <linux/ima.h>
#include "ima.h" +#include "ima_digest_list.h"
/* flags definitions */ #define IMA_FUNC 0x0001 @@ -34,6 +35,7 @@ #define IMA_FSNAME 0x0200 #define IMA_KEYRINGS 0x0400 #define IMA_LABEL 0x0800 +#define IMA_PARSER 0x10000 #define IMA_VALIDATE_ALGOS 0x1000 #define IMA_GID 0x2000 #define IMA_EGID 0x4000 @@ -191,6 +193,10 @@ static struct ima_rule_entry default_measurement_rules[] __ro_after_init = { {.action = MEASURE, .func = DIGEST_LIST_CHECK, .flags = IMA_FUNC}, };
+static struct ima_rule_entry ima_parser_measure_rule __ro_after_init = { + .action = MEASURE, .flags = IMA_PARSER +}; + static struct ima_rule_entry default_appraise_rules[] __ro_after_init = { {.action = DONT_APPRAISE, .fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = SYSFS_MAGIC, .flags = IMA_FSMAGIC}, @@ -259,6 +265,11 @@ static struct ima_rule_entry secure_boot_rules[] __ro_after_init = { .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, };
+static struct ima_rule_entry ima_parser_appraise_rule __ro_after_init = { + .action = APPRAISE, + .flags = IMA_PARSER | IMA_DIGSIG_REQUIRED +}; + static struct ima_rule_entry critical_data_rules[] __ro_after_init = { {.action = MEASURE, .func = CRITICAL_DATA, .flags = IMA_FUNC}, }; @@ -645,6 +656,9 @@ static bool ima_match_rules(struct ima_rule_entry *rule, !rule->fowner_op(i_uid_into_vfsuid(idmap, inode), rule->fowner)) return false; + if ((rule->flags & IMA_PARSER) && + !ima_current_is_parser()) + return false; if ((rule->flags & IMA_FGROUP) && !rule->fgroup_op(i_gid_into_vfsgid(idmap, inode), rule->fgroup)) @@ -1010,6 +1024,9 @@ void __init ima_init_policy(void) break; }
+ if (ima_policy) + add_rules(&ima_parser_measure_rule, 1, IMA_DEFAULT_POLICY); + /* * Based on runtime secure boot flags, insert arch specific measurement * and appraise rules requiring file signatures for both the initial @@ -1064,6 +1081,10 @@ void __init ima_init_policy(void)
atomic_set(&ima_setxattr_allowed_hash_algorithms, 0);
+ if (ima_use_secure_boot || ima_use_appraise_tcb || + ima_use_appraise_exec_tcb) + add_rules(&ima_parser_appraise_rule, 1, IMA_DEFAULT_POLICY); + ima_update_policy_flags(); }
@@ -1127,7 +1148,7 @@ enum policy_opt { Opt_digest_type, Opt_appraise_type, Opt_appraise_flag, Opt_appraise_algos, Opt_permit_directio, Opt_pcr, Opt_template, Opt_keyrings, - Opt_label, Opt_err + Opt_label, Opt_parser, Opt_err };
static const match_table_t policy_tokens = { @@ -1176,6 +1197,7 @@ static const match_table_t policy_tokens = { {Opt_template, "template=%s"}, {Opt_keyrings, "keyrings=%s"}, {Opt_label, "label=%s"}, + {Opt_parser, "parser"}, {Opt_err, NULL} };
@@ -1302,7 +1324,7 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) if (entry->action != APPRAISE && entry->flags & (IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED | IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS | - IMA_META_IMMUTABLE_REQUIRED)) + IMA_META_IMMUTABLE_REQUIRED | IMA_PARSER)) return false;
/* @@ -1945,6 +1967,10 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) &(template_desc->num_fields)); entry->template = template_desc; break; + case Opt_parser: + audit_log_format(ab, "parser "); + entry->flags |= IMA_PARSER; + break; case Opt_err: ima_log_string(ab, "UNKNOWN", p); result = -EINVAL; @@ -2290,6 +2316,9 @@ int ima_policy_show(struct seq_file *m, void *v) seq_puts(m, " "); }
+ if (entry->flags & IMA_PARSER) + seq_puts(m, "parser "); + for (i = 0; i < MAX_LSM_RULES; i++) { if (entry->lsm[i].rule) { switch (i) {
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
Introduce three new values for evm= kernel option:
x509: enable EVM by setting x509 flag; allow_metadata_writes: permit metadata modificatons; complete: don't allow further changes of the EVM status.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- Documentation/admin-guide/kernel-parameters.txt | 10 +++++++--- security/integrity/evm/evm_main.c | 10 ++++++++-- 2 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 5d84000d344d..e92e899ae964 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1476,9 +1476,13 @@ has equivalent usage. See its documentation for details.
evm= [EVM] - Format: { "fix" } - Permit 'security.evm' to be updated regardless of - current integrity status. + Format: { "fix" | "x509" | "allow_metadata_writes" | + "complete" } + fix: permit 'security.evm' to be updated regardless of + current integrity status; + x509: enable EVM by setting x509 flag; + allow_metadata_writes: permit metadata modificatons; + complete: don't allow further changes of the EVM status.
early_page_ext [KNL] Enforces page_ext initialization to earlier stages so cover more early boot allocations. diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index c681838a0b66..842fe18ad58e 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -73,16 +73,22 @@ static struct xattr_list evm_config_default_xattrnames[] = { LIST_HEAD(evm_config_xattrnames);
static int evm_fixmode __ro_after_init; -static int __init evm_set_fixmode(char *str) +static int __init evm_set_param(char *str) { if (strncmp(str, "fix", 3) == 0) evm_fixmode = 1; + else if (strncmp(str, "x509", 4) == 0) + evm_initialized |= EVM_INIT_X509; + else if (strncmp(str, "allow_metadata_writes", 21) == 0) + evm_initialized |= EVM_ALLOW_METADATA_WRITES; + else if (strncmp(str, "complete", 8) == 0) + evm_initialized |= EVM_SETUP_COMPLETE; else pr_err("invalid "%s" mode", str);
return 1; } -__setup("evm=", evm_set_fixmode); +__setup("evm=", evm_set_param);
static void __init evm_init_config(void) {
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
---------------------------
This patch limits the digest lists processed by the kernel by excluding those that are not in the compact format. The patch then executes the user space parsers to process the skipped digest lists.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Acked-by: Hanjun Guo guohanjun@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/ima/Kconfig | 7 +++++ security/integrity/ima/ima_digest_list.c | 38 +++++++++++++++++++++--- 2 files changed, 41 insertions(+), 4 deletions(-)
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 73c582d97737..aa6a06f3e8b1 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -356,3 +356,10 @@ config IMA_DIGEST_LISTS_DIR help This option defines the path of the directory containing digest lists. + +config IMA_PARSER_BINARY_PATH + string "Path of the parser binary" + depends on IMA_DIGEST_LIST + default "/usr/bin/upload_digest_lists" + help + This option defines the path of the parser binary. diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index b5d1004e3040..0712023d6761 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -265,7 +265,7 @@ struct readdir_callback { struct path *path; };
-static int __init load_digest_list(struct dir_context *__ctx, const char *name, +static bool __init load_digest_list(struct dir_context *__ctx, const char *name, int namelen, loff_t offset, u64 ino, unsigned int d_type) { @@ -274,16 +274,33 @@ static int __init load_digest_list(struct dir_context *__ctx, const char *name, struct dentry *dentry; struct file *file; u8 *xattr_value = NULL; + char *type_start, *format_start, *format_end; void *datap = NULL; loff_t size; int ret;
if (!strcmp(name, ".") || !strcmp(name, "..")) - return 0; + return true; + + type_start = strchr(name, '-'); + if (!type_start) + return true; + + format_start = strchr(type_start + 1, '-'); + if (!format_start) + return true; + + format_end = strchr(format_start + 1, '-'); + if (!format_end) + return true; + + if (format_end - format_start - 1 != strlen("compact") || + strncmp(format_start + 1, "compact", format_end - format_start - 1)) + return true;
dentry = lookup_one_len(name, dir->dentry, strlen(name)); if (IS_ERR(dentry)) - return 0; + return true;
size = vfs_getxattr(&nop_mnt_idmap, dentry, XATTR_NAME_EVM, NULL, 0); if (size < 0) { @@ -319,7 +336,18 @@ static int __init load_digest_list(struct dir_context *__ctx, const char *name, fput(file); out: kfree(xattr_value); - return 0; + return true; +} + +static void ima_exec_parser(void) +{ + char *argv[4] = {NULL}, *envp[1] = {NULL}; + + argv[0] = (char *)CONFIG_IMA_PARSER_BINARY_PATH; + argv[1] = "add"; + argv[2] = (char *)CONFIG_IMA_DIGEST_LISTS_DIR; + + call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); }
void __init ima_load_digest_lists(void) @@ -347,6 +375,8 @@ void __init ima_load_digest_lists(void) fput(file); out: path_put(&path); + + ima_exec_parser(); }
/****************
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
-------------------------------------------------
Commit 5feeb61183dd ("evm: Allow non-SHA1 digital signatures") introduced the possibility to use different hash algorithm for signatures, but kept the algorithm for the HMAC hard-coded (SHA1). Switching to a different algorithm for HMAC would require to change the code in different places.
This patch introduces a new global variable called evm_hash_algo, and consistently uses it whenever EVM perform HMAC-related operations. It also introduces a new kernel configuration option called CONFIG_EVM_DEFAULT_HASH so that evm_hash_algo can be defined at kernel compilation time.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Acked-by: Hanjun Guo guohanjun@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/evm/Kconfig | 32 +++++++++++++++++++++++++++++ security/integrity/evm/evm.h | 1 + security/integrity/evm/evm_crypto.c | 15 +++++++++----- security/integrity/evm/evm_main.c | 15 +++++++++----- security/integrity/integrity.h | 2 +- 5 files changed, 54 insertions(+), 11 deletions(-)
diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig index a6e19d23e700..844f2686cce7 100644 --- a/security/integrity/evm/Kconfig +++ b/security/integrity/evm/Kconfig @@ -13,6 +13,38 @@ config EVM
If you are unsure how to answer this question, answer N.
+choice + prompt "Default EVM hash algorithm" + default EVM_DEFAULT_HASH_SHA256 + depends on EVM + help + Select the default hash algorithm used for the HMAC. + + config EVM_DEFAULT_HASH_SHA1 + bool "SHA1 (default)" + depends on CRYPTO_SHA1=y + + config EVM_DEFAULT_HASH_SHA256 + bool "SHA256" + depends on CRYPTO_SHA256=y + + config EVM_DEFAULT_HASH_SHA512 + bool "SHA512" + depends on CRYPTO_SHA512=y + + config EVM_DEFAULT_HASH_WP512 + bool "WP512" + depends on CRYPTO_WP512=y +endchoice + +config EVM_DEFAULT_HASH + string + depends on EVM + default "sha1" if EVM_DEFAULT_HASH_SHA1 + default "sha256" if EVM_DEFAULT_HASH_SHA256 + default "sha512" if EVM_DEFAULT_HASH_SHA512 + default "wp512" if EVM_DEFAULT_HASH_WP512 + config EVM_ATTR_FSUUID bool "FSUUID (version 2)" default y diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h index f8b8c5004fc7..72776e20c0a8 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h @@ -33,6 +33,7 @@ struct xattr_list { };
extern int evm_initialized; +extern enum hash_algo evm_hash_algo;
#define EVM_ATTR_FSUUID 0x0001
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 259c7c98bb35..a6215dbe50b1 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -35,7 +35,7 @@ static DEFINE_MUTEX(mutex);
static unsigned long evm_set_key_flags;
-static const char evm_hmac[] = "hmac(sha1)"; +enum hash_algo evm_hash_algo __ro_after_init = HASH_ALGO_SHA1;
/** * evm_set_key() - set EVM HMAC key from the kernel @@ -76,8 +76,12 @@ static struct shash_desc *init_desc(char type, uint8_t hash_algo) long rc; const char *algo; struct crypto_shash **tfm, *tmp_tfm; + char evm_hmac[CRYPTO_MAX_ALG_NAME]; struct shash_desc *desc;
+ snprintf(evm_hmac, sizeof(evm_hmac), "hmac(%s)", + CONFIG_EVM_DEFAULT_HASH); + if (type == EVM_XATTR_HMAC) { if (!(evm_initialized & EVM_INIT_HMAC)) { pr_err_once("HMAC key is not set\n"); @@ -373,7 +377,7 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name, if (rc) return -EPERM;
- data.hdr.algo = HASH_ALGO_SHA1; + data.hdr.algo = evm_hash_algo; rc = evm_calc_hmac(dentry, xattr_name, xattr_value, xattr_value_len, &data); if (rc == 0) { @@ -381,7 +385,8 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name, rc = __vfs_setxattr_noperm(&nop_mnt_idmap, dentry, XATTR_NAME_EVM, &data.hdr.xattr.data[1], - SHA1_DIGEST_SIZE + 1, 0); + hash_digest_size[evm_hash_algo] + 1, + 0); } else if (rc == -ENODATA && (inode->i_opflags & IOP_XATTR)) { rc = __vfs_removexattr(&nop_mnt_idmap, dentry, XATTR_NAME_EVM); } @@ -393,7 +398,7 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr, { struct shash_desc *desc;
- desc = init_desc(EVM_XATTR_HMAC, HASH_ALGO_SHA1); + desc = init_desc(EVM_XATTR_HMAC, evm_hash_algo); if (IS_ERR(desc)) { pr_info("init_desc failed\n"); return PTR_ERR(desc); @@ -406,7 +411,7 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr, }
/* - * Get the key from the TPM for the SHA1-HMAC + * Get the key from the TPM for the HMAC */ int evm_init_key(void) { diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 842fe18ad58e..305afa22ab9c 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -231,18 +231,18 @@ 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_xattr)) { + if (xattr_len != hash_digest_size[evm_hash_algo] + 1) { evm_status = INTEGRITY_FAIL; goto out; }
- digest.hdr.algo = HASH_ALGO_SHA1; + digest.hdr.algo = evm_hash_algo; rc = evm_calc_hmac(dentry, xattr_name, xattr_value, xattr_value_len, &digest); if (rc) break; rc = crypto_memneq(xattr_data->data, digest.digest, - SHA1_DIGEST_SIZE); + hash_digest_size[evm_hash_algo]); if (rc) rc = -EINVAL; break; @@ -955,7 +955,7 @@ int evm_inode_init_security(struct inode *inode, goto out;
evm_xattr->value = xattr_data; - evm_xattr->value_len = sizeof(*xattr_data); + evm_xattr->value_len = hash_digest_size[evm_hash_algo] + 1; evm_xattr->name = XATTR_EVM_SUFFIX; return 0; out: @@ -977,9 +977,14 @@ void __init evm_load_x509(void)
static int __init init_evm(void) { - int error; + int error, i; struct list_head *pos, *q;
+ i = match_string(hash_algo_name, HASH_ALGO__LAST, + CONFIG_EVM_DEFAULT_HASH); + if (i >= 0) + evm_hash_algo = i; + evm_init_config();
error = integrity_init_keyring(INTEGRITY_KEYRING_EVM); diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index a93ad2bcd809..4f0167052b26 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -96,7 +96,7 @@ struct evm_ima_xattr_data { /* Only used in the EVM HMAC code. */ struct evm_xattr { struct evm_ima_xattr_data data; - u8 digest[SHA1_DIGEST_SIZE]; + u8 digest[SHA512_DIGEST_SIZE]; } __packed;
#define IMA_MAX_DIGEST_SIZE HASH_MAX_DIGESTSIZE
From: Roberto Sassu roberto.sassu@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
---------------------------
Enable digest lists and PGP keys preload.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- arch/arm64/configs/openeuler_defconfig | 7 +++++++ arch/x86/configs/openeuler_defconfig | 27 ++++++++++++++++---------- 2 files changed, 24 insertions(+), 10 deletions(-)
diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig index 63abdb3f8c63..1cb5960fa52f 100644 --- a/arch/arm64/configs/openeuler_defconfig +++ b/arch/arm64/configs/openeuler_defconfig @@ -7003,6 +7003,9 @@ CONFIG_IMA_TRUSTED_KEYRING=y CONFIG_IMA_LOAD_X509=y CONFIG_IMA_X509_PATH="/etc/keys/x509_ima.der" # CONFIG_IMA_APPRAISE_SIGNED_INIT is not set +CONFIG_IMA_DIGEST_LIST=y +CONFIG_IMA_DIGEST_LISTS_DIR="/etc/ima/digest_lists" +CONFIG_IMA_PARSER_BINARY_PATH="/usr/bin/upload_digest_lists" CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS=y CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS=y # CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT is not set @@ -7298,6 +7301,9 @@ CONFIG_PKCS7_MESSAGE_PARSER=y # CONFIG_PKCS7_TEST_KEY is not set CONFIG_SIGNED_PE_FILE_VERIFICATION=y # CONFIG_FIPS_SIGNATURE_SELFTEST is not set +CONFIG_PGP_LIBRARY=y +CONFIG_PGP_KEY_PARSER=y +CONFIG_PGP_PRELOAD=y
# # Certificates for signature checking @@ -7314,6 +7320,7 @@ CONFIG_SYSTEM_BLACKLIST_HASH_LIST="" CONFIG_SYSTEM_REVOCATION_LIST=y CONFIG_SYSTEM_REVOCATION_KEYS="" # CONFIG_SYSTEM_BLACKLIST_AUTH_UPDATE is not set +CONFIG_PGP_PRELOAD_PUBLIC_KEYS=y # end of Certificates for signature checking
CONFIG_BINARY_PRINTF=y diff --git a/arch/x86/configs/openeuler_defconfig b/arch/x86/configs/openeuler_defconfig index 0e05e7a15fdb..fac5531742a4 100644 --- a/arch/x86/configs/openeuler_defconfig +++ b/arch/x86/configs/openeuler_defconfig @@ -3709,22 +3709,22 @@ CONFIG_TCG_TPM=y CONFIG_HW_RANDOM_TPM=y CONFIG_TCG_TIS_CORE=y CONFIG_TCG_TIS=y -CONFIG_TCG_TIS_SPI=m +CONFIG_TCG_TIS_SPI=y # CONFIG_TCG_TIS_SPI_CR50 is not set # CONFIG_TCG_TIS_I2C is not set # CONFIG_TCG_TIS_I2C_CR50 is not set -CONFIG_TCG_TIS_I2C_ATMEL=m -CONFIG_TCG_TIS_I2C_INFINEON=m -CONFIG_TCG_TIS_I2C_NUVOTON=m -CONFIG_TCG_NSC=m -CONFIG_TCG_ATMEL=m -CONFIG_TCG_INFINEON=m +CONFIG_TCG_TIS_I2C_ATMEL=y +CONFIG_TCG_TIS_I2C_INFINEON=y +CONFIG_TCG_TIS_I2C_NUVOTON=y +CONFIG_TCG_NSC=y +CONFIG_TCG_ATMEL=y +CONFIG_TCG_INFINEON=y # CONFIG_TCG_XEN is not set CONFIG_TCG_CRB=y # CONFIG_TCG_VTPM_PROXY is not set -CONFIG_TCG_TIS_ST33ZP24=m -CONFIG_TCG_TIS_ST33ZP24_I2C=m -CONFIG_TCG_TIS_ST33ZP24_SPI=m +CONFIG_TCG_TIS_ST33ZP24=y +CONFIG_TCG_TIS_ST33ZP24_I2C=y +CONFIG_TCG_TIS_ST33ZP24_SPI=y CONFIG_TELCLOCK=m # CONFIG_XILLYBUS is not set # CONFIG_XILLYUSB is not set @@ -8249,6 +8249,9 @@ CONFIG_IMA_TRUSTED_KEYRING=y CONFIG_IMA_LOAD_X509=y CONFIG_IMA_X509_PATH="/etc/keys/x509_ima.der" # CONFIG_IMA_APPRAISE_SIGNED_INIT is not set +CONFIG_IMA_DIGEST_LIST=y +CONFIG_IMA_DIGEST_LISTS_DIR="/etc/ima/digest_lists" +CONFIG_IMA_PARSER_BINARY_PATH="/usr/bin/upload_digest_lists" CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS=y CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS=y # CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT is not set @@ -8539,6 +8542,9 @@ CONFIG_PKCS7_MESSAGE_PARSER=y # CONFIG_PKCS7_TEST_KEY is not set CONFIG_SIGNED_PE_FILE_VERIFICATION=y # CONFIG_FIPS_SIGNATURE_SELFTEST is not set +CONFIG_PGP_LIBRARY=y +CONFIG_PGP_KEY_PARSER=y +CONFIG_PGP_PRELOAD=y
# # Certificates for signature checking @@ -8555,6 +8561,7 @@ CONFIG_SYSTEM_BLACKLIST_HASH_LIST="" CONFIG_SYSTEM_REVOCATION_LIST=y CONFIG_SYSTEM_REVOCATION_KEYS="" # CONFIG_SYSTEM_BLACKLIST_AUTH_UPDATE is not set +CONFIG_PGP_PRELOAD_PUBLIC_KEYS=y # end of Certificates for signature checking
CONFIG_BINARY_PRINTF=y
From: Zheng Zengkai zhengzengkai@huawei.com
hulk inclusion category: feature feature: IMA Digest Lists extension bugzilla: 46797
---------------------------
This reverts commit 9b772f4948fa513c501ae37c7afc89aa8613314c. backport patch from LTS 5.10.50 instead.
Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- Documentation/ABI/testing/evm | 4 ++-- security/integrity/evm/evm_secfs.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/Documentation/ABI/testing/evm b/Documentation/ABI/testing/evm index 44750a933db4..2828e720d91e 100644 --- a/Documentation/ABI/testing/evm +++ b/Documentation/ABI/testing/evm @@ -69,8 +69,8 @@ Description:
and the resulting value will be 3.
- Note that once an HMAC key has been loaded, it will no longer - be possible to enable metadata modification. Signaling that an + Note that once a key has been loaded, it will no longer be + possible to enable metadata modification. Signaling that an HMAC key has been loaded will clear the corresponding flag. For example, if the current value is 6 (2 and 4 set)::
diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c index 9b907c2fee60..5a762ac6411d 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c @@ -86,7 +86,7 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf, * an HMAC key is loaded. */ if ((i & EVM_ALLOW_METADATA_WRITES) && - (evm_initialized & EVM_INIT_HMAC) != 0) + ((evm_initialized & EVM_KEY_MASK) != 0)) return -EPERM;
if (i & EVM_INIT_HMAC) {
From: Zhang Tianxing zhangtianxing3@huawei.com
hulk inclusion category: bugfix bugzilla: 46797 CVE: NA Reference: https://gitee.com/openeuler/kernel/issues/I3C03N
-------------------------------------------------
This patch fix a memory leak problem when deleting digest list.
hlist_del_rcu in ima_del_digest_data_entry only deletes the digest struct from the linked list without releasing the memory it uses.
Signed-off-by: Zhang Tianxing zhangtianxing3@huawei.com Reviewed-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/ima/ima_digest_list.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index 0712023d6761..5aaea24571d9 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -120,6 +120,7 @@ static void ima_del_digest_data_entry(u8 *digest, enum hash_algo algo,
hlist_del_rcu(&d->hnext); atomic_long_dec(&ima_digests_htable.len); + kfree(d); }
/***********************
From: Zhang Tianxing zhangtianxing3@huawei.com
hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I409K9 CVE: NA
-----------------------------------------------------------------
This patch sets max size for IMA digest database to prevent OOM.
A __ro_after_init global variable ima_digest_db_max_size is used to set the maximum data uploaded to digest database.
Another global variable ima_digest_db_size records the data uploaded to kernel digest database and increments when uploading digest lists.
Signed-off-by: Zhang Tianxing zhangtianxing3@huawei.com Reviewed-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- .../admin-guide/kernel-parameters.txt | 4 +++ arch/arm64/configs/openeuler_defconfig | 5 ++++ arch/x86/configs/openeuler_defconfig | 5 ++++ security/integrity/ima/Kconfig | 27 +++++++++++++++++++ security/integrity/ima/ima.h | 4 +++ security/integrity/ima/ima_digest_list.c | 23 ++++++++++++++++ security/integrity/ima/ima_fs.c | 11 ++++++++ 7 files changed, 79 insertions(+)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index e92e899ae964..4a0261de0ed8 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1975,6 +1975,10 @@ with PCR 10, according to the existing behavior. Format: { [+]<unsigned int> }
+ ima_digest_db_size=nn[M] + [IMA] + Sets the maximum data uploaded to IMA digest database. + ima_hash= [IMA] Format: { md5 | sha1 | rmd160 | sha256 | sha384 | sha512 | ... } diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig index 1cb5960fa52f..168a101cb64a 100644 --- a/arch/arm64/configs/openeuler_defconfig +++ b/arch/arm64/configs/openeuler_defconfig @@ -7005,6 +7005,11 @@ CONFIG_IMA_X509_PATH="/etc/keys/x509_ima.der" # CONFIG_IMA_APPRAISE_SIGNED_INIT is not set CONFIG_IMA_DIGEST_LIST=y CONFIG_IMA_DIGEST_LISTS_DIR="/etc/ima/digest_lists" +# CONFIG_IMA_MIN_DIGEST_DB_SIZE is not set +CONFIG_IMA_STANDARD_DIGEST_DB_SIZE=y +# CONFIG_IMA_MAX_DIGEST_DB_SIZE is not set +# CONFIG_IMA_CUSTOM_DIGEST_DB_SIZE is not set +CONFIG_IMA_DIGEST_DB_SIZE=16 CONFIG_IMA_PARSER_BINARY_PATH="/usr/bin/upload_digest_lists" CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS=y CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS=y diff --git a/arch/x86/configs/openeuler_defconfig b/arch/x86/configs/openeuler_defconfig index fac5531742a4..599b8fac2c4b 100644 --- a/arch/x86/configs/openeuler_defconfig +++ b/arch/x86/configs/openeuler_defconfig @@ -8251,6 +8251,11 @@ CONFIG_IMA_X509_PATH="/etc/keys/x509_ima.der" # CONFIG_IMA_APPRAISE_SIGNED_INIT is not set CONFIG_IMA_DIGEST_LIST=y CONFIG_IMA_DIGEST_LISTS_DIR="/etc/ima/digest_lists" +# CONFIG_IMA_MIN_DIGEST_DB_SIZE is not set +CONFIG_IMA_STANDARD_DIGEST_DB_SIZE=y +# CONFIG_IMA_MAX_DIGEST_DB_SIZE is not set +# CONFIG_IMA_CUSTOM_DIGEST_DB_SIZE is not set +CONFIG_IMA_DIGEST_DB_SIZE=16 CONFIG_IMA_PARSER_BINARY_PATH="/usr/bin/upload_digest_lists" CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS=y CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS=y diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index aa6a06f3e8b1..eab73b839c2a 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -357,6 +357,33 @@ config IMA_DIGEST_LISTS_DIR This option defines the path of the directory containing digest lists.
+choice + prompt "Defalut maximum data uploaded to the IMA digest database" + default IMA_STANDARD_DIGEST_DB_SIZE + depends on IMA_DIGEST_LIST + help + This option defines the maximum data uploaded to the IMA digest + database. The compiled default limit can be overwritten using the + kernel command line "ima_digest_db_size". + + config IMA_MIN_DIGEST_DB_SIZE + bool "minimum" + config IMA_STANDARD_DIGEST_DB_SIZE + bool "standard (default)" + config IMA_MAX_DIGEST_DB_SIZE + bool "maximum" + config IMA_CUSTOM_DIGEST_DB_SIZE + bool "custom" +endchoice + +config IMA_DIGEST_DB_MEGABYTES + int + depends on IMA_DIGEST_LIST + range 8 64 + default 8 if IMA_MIN_DIGEST_DB_SIZE + default 16 if IMA_STANDARD_DIGEST_DB_SIZE + default 64 if IMA_MAX_DIGEST_DB_SIZE + config IMA_PARSER_BINARY_PATH string "Path of the parser binary" depends on IMA_DIGEST_LIST diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 124b4e78a784..d19d322309fb 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -60,6 +60,10 @@ extern int ima_digest_list_pcr; extern bool ima_plus_standard_pcr; extern const char boot_aggregate_name[]; extern int ima_digest_list_actions; +#ifdef CONFIG_IMA_DIGEST_LIST +extern size_t ima_digest_db_max_size __ro_after_init; +extern size_t ima_digest_db_size; +#endif
/* IMA event related data */ struct ima_event_data { diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index 5aaea24571d9..021ffda4a6f9 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -14,6 +14,9 @@ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define MIN_DB_SIZE (8 * 1024 * 1024) +#define MAX_DB_SIZE (64 * 1024 * 1024) +#define DEFAULT_DB_SIZE (CONFIG_IMA_DIGEST_DB_MEGABYTES * 1024 * 1024)
#include <linux/vmalloc.h> #include <linux/module.h> @@ -26,11 +29,31 @@ #include "ima.h" #include "ima_digest_list.h"
+size_t ima_digest_db_max_size __ro_after_init = DEFAULT_DB_SIZE; +size_t ima_digest_db_size; + struct ima_h_table ima_digests_htable = { .len = ATOMIC_LONG_INIT(0), .queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT };
+static int __init digest_db_max_size_setup(char *str) +{ + int size; + char *retptr; + + size = memparse(str, &retptr); + if (size < MIN_DB_SIZE || size > MAX_DB_SIZE || *retptr != '\0') { + pr_err("DB size should range from 8M to 64M\n"); + return 0; + } + + ima_digest_db_max_size = size; + + return 1; +} +__setup("ima_digest_db_size=", digest_db_max_size_setup); + static int __init digest_list_pcr_setup(char *str) { int pcr, ret; diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 7310683a7af7..66574a79ac97 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -319,6 +319,12 @@ static ssize_t ima_read_file(char *path, struct dentry *dentry) rc = ima_parse_add_rule(p); } else if (dentry == digest_list_data || dentry == digest_list_data_del) { + /* Only check size when adding digest lists */ + if (dentry == digest_list_data && + size > ima_digest_db_max_size - ima_digest_db_size) { + rc = -ENOMEM; + break; + } /* * Disable usage of digest lists if not measured * or appraised. @@ -334,6 +340,11 @@ static ssize_t ima_read_file(char *path, struct dentry *dentry) if (rc < 0) break; size -= rc; + + if (dentry == digest_list_data) + ima_digest_db_size += rc; + if (dentry == digest_list_data_del) + ima_digest_db_size -= rc; }
vfree(data);
From: Zhang Tianxing zhangtianxing3@huawei.com
hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I409K9 CVE: NA
-----------------------------------------------------------------
Expected error message `ima: Unable to open file:` can be overwritten when the uploaded path contains control characters like `\r` or `\b`. Therefore, When an invalid path (which contains control characters) is uploaded through SecurityFS, unexpected logs can be printed to dmesg.
This patch rejects policy paths with control characters.
Signed-off-by: Zhang Tianxing zhangtianxing3@huawei.com Reviewed-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/ima/ima_fs.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 66574a79ac97..5a7be9595e28 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -22,6 +22,7 @@ #include <linux/parser.h> #include <linux/vmalloc.h> #include <linux/file.h> +#include <linux/ctype.h>
#include "ima.h" #include "ima_digest_list.h" @@ -363,6 +364,7 @@ static ssize_t ima_write_data(struct file *file, const char __user *buf, char *data; ssize_t result; struct dentry *dentry = file_dentry(file); + int i;
/* No partial writes. */ result = -EINVAL; @@ -383,6 +385,13 @@ static ssize_t ima_write_data(struct file *file, const char __user *buf, goto out_free;
data[datalen] = '\0'; + for (i = 0; data[i] != '\n' && data[i] != '\0'; i++) { + if (iscntrl(data[i])) { + pr_err_once("invalid path (control characters are not allowed)\n"); + result = -EINVAL; + goto out_free; + } + }
result = mutex_lock_interruptible(&ima_write_mutex); if (result < 0)
From: Zhang Tianxing zhangtianxing3@huawei.com
hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I409K9 CVE: NA
-----------------------------------------------------------------
Commit 7c9d18bcaa ("ima: Add max size for IMA digest database") adds a new Kconfig for IMA Digest Lists: CONFIG_IMA_DIGEST_DB_MEGABYTES. However, that commit has typos in openeuler_defconfig. This patch is to fix that typo.
Fixes: 7c9d18bcaa ("ima: Add max size for IMA digest database") Signed-off-by: Zhang Tianxing zhangtianxing3@huawei.com Reviewed-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- arch/arm64/configs/openeuler_defconfig | 2 +- arch/x86/configs/openeuler_defconfig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig index 168a101cb64a..f7673afcd6a0 100644 --- a/arch/arm64/configs/openeuler_defconfig +++ b/arch/arm64/configs/openeuler_defconfig @@ -7009,7 +7009,7 @@ CONFIG_IMA_DIGEST_LISTS_DIR="/etc/ima/digest_lists" CONFIG_IMA_STANDARD_DIGEST_DB_SIZE=y # CONFIG_IMA_MAX_DIGEST_DB_SIZE is not set # CONFIG_IMA_CUSTOM_DIGEST_DB_SIZE is not set -CONFIG_IMA_DIGEST_DB_SIZE=16 +CONFIG_IMA_DIGEST_DB_MEGABYTES=16 CONFIG_IMA_PARSER_BINARY_PATH="/usr/bin/upload_digest_lists" CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS=y CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS=y diff --git a/arch/x86/configs/openeuler_defconfig b/arch/x86/configs/openeuler_defconfig index 599b8fac2c4b..2694e416aed5 100644 --- a/arch/x86/configs/openeuler_defconfig +++ b/arch/x86/configs/openeuler_defconfig @@ -8255,7 +8255,7 @@ CONFIG_IMA_DIGEST_LISTS_DIR="/etc/ima/digest_lists" CONFIG_IMA_STANDARD_DIGEST_DB_SIZE=y # CONFIG_IMA_MAX_DIGEST_DB_SIZE is not set # CONFIG_IMA_CUSTOM_DIGEST_DB_SIZE is not set -CONFIG_IMA_DIGEST_DB_SIZE=16 +CONFIG_IMA_DIGEST_DB_MEGABYTES=16 CONFIG_IMA_PARSER_BINARY_PATH="/usr/bin/upload_digest_lists" CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS=y CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS=y
From: Zhang Tianxing zhangtianxing3@huawei.com
hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I4BMOM CVE: NA
-----------------------------------------------------------------
This patch fixes some issues involved with IMA digest db size patches: - use CONFIG_IMA_DIGEST_LIST to seperate db size check in ima_fs.c - remove minimum digest db size limit in Kconfig - change ima_digest_db_size data type from size_t to int, to avoid overflow - change ima_digest_db_size in ima_add_digest_data_entry and ima_del_digest_data_entry - add some debug messages
Fixes: 7c9d18bcaa ("ima: Add max size for IMA digest database") Signed-off-by: Zhang Tianxing zhangtianxing3@huawei.com Reviewed-by: zhujianwei zhujianwei7@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- arch/arm64/configs/openeuler_defconfig | 1 - arch/x86/configs/openeuler_defconfig | 1 - security/integrity/ima/Kconfig | 5 +---- security/integrity/ima/ima.h | 4 ++-- security/integrity/ima/ima_digest_list.c | 21 ++++++++++++++++----- security/integrity/ima/ima_fs.c | 14 +++++++++----- 6 files changed, 28 insertions(+), 18 deletions(-)
diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig index f7673afcd6a0..504a65e08247 100644 --- a/arch/arm64/configs/openeuler_defconfig +++ b/arch/arm64/configs/openeuler_defconfig @@ -7005,7 +7005,6 @@ CONFIG_IMA_X509_PATH="/etc/keys/x509_ima.der" # CONFIG_IMA_APPRAISE_SIGNED_INIT is not set CONFIG_IMA_DIGEST_LIST=y CONFIG_IMA_DIGEST_LISTS_DIR="/etc/ima/digest_lists" -# CONFIG_IMA_MIN_DIGEST_DB_SIZE is not set CONFIG_IMA_STANDARD_DIGEST_DB_SIZE=y # CONFIG_IMA_MAX_DIGEST_DB_SIZE is not set # CONFIG_IMA_CUSTOM_DIGEST_DB_SIZE is not set diff --git a/arch/x86/configs/openeuler_defconfig b/arch/x86/configs/openeuler_defconfig index 2694e416aed5..be7fd428c1b6 100644 --- a/arch/x86/configs/openeuler_defconfig +++ b/arch/x86/configs/openeuler_defconfig @@ -8251,7 +8251,6 @@ CONFIG_IMA_X509_PATH="/etc/keys/x509_ima.der" # CONFIG_IMA_APPRAISE_SIGNED_INIT is not set CONFIG_IMA_DIGEST_LIST=y CONFIG_IMA_DIGEST_LISTS_DIR="/etc/ima/digest_lists" -# CONFIG_IMA_MIN_DIGEST_DB_SIZE is not set CONFIG_IMA_STANDARD_DIGEST_DB_SIZE=y # CONFIG_IMA_MAX_DIGEST_DB_SIZE is not set # CONFIG_IMA_CUSTOM_DIGEST_DB_SIZE is not set diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index eab73b839c2a..d0d45a521083 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -366,8 +366,6 @@ choice database. The compiled default limit can be overwritten using the kernel command line "ima_digest_db_size".
- config IMA_MIN_DIGEST_DB_SIZE - bool "minimum" config IMA_STANDARD_DIGEST_DB_SIZE bool "standard (default)" config IMA_MAX_DIGEST_DB_SIZE @@ -379,8 +377,7 @@ endchoice config IMA_DIGEST_DB_MEGABYTES int depends on IMA_DIGEST_LIST - range 8 64 - default 8 if IMA_MIN_DIGEST_DB_SIZE + range 0 64 default 16 if IMA_STANDARD_DIGEST_DB_SIZE default 64 if IMA_MAX_DIGEST_DB_SIZE
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index d19d322309fb..b62fdf712b27 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -61,8 +61,8 @@ extern bool ima_plus_standard_pcr; extern const char boot_aggregate_name[]; extern int ima_digest_list_actions; #ifdef CONFIG_IMA_DIGEST_LIST -extern size_t ima_digest_db_max_size __ro_after_init; -extern size_t ima_digest_db_size; +extern int ima_digest_db_max_size __ro_after_init; +extern int ima_digest_db_size; #endif
/* IMA event related data */ diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index 021ffda4a6f9..067f79d8799f 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -14,7 +14,6 @@ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define MIN_DB_SIZE (8 * 1024 * 1024) #define MAX_DB_SIZE (64 * 1024 * 1024) #define DEFAULT_DB_SIZE (CONFIG_IMA_DIGEST_DB_MEGABYTES * 1024 * 1024)
@@ -29,8 +28,8 @@ #include "ima.h" #include "ima_digest_list.h"
-size_t ima_digest_db_max_size __ro_after_init = DEFAULT_DB_SIZE; -size_t ima_digest_db_size; +int ima_digest_db_max_size __ro_after_init = DEFAULT_DB_SIZE; +int ima_digest_db_size;
struct ima_h_table ima_digests_htable = { .len = ATOMIC_LONG_INIT(0), @@ -43,12 +42,13 @@ static int __init digest_db_max_size_setup(char *str) char *retptr;
size = memparse(str, &retptr); - if (size < MIN_DB_SIZE || size > MAX_DB_SIZE || *retptr != '\0') { - pr_err("DB size should range from 8M to 64M\n"); + if (size < 0 || size > MAX_DB_SIZE || *retptr != '\0') { + pr_err("digest DB size should range from 0M to 64M\n"); return 0; }
ima_digest_db_max_size = size; + pr_info_once("parse reserve digest DB memory: %s\n", str);
return 1; } @@ -118,6 +118,9 @@ static int ima_add_digest_data_entry(u8 *digest, enum hash_algo algo, if (d == NULL) return -ENOMEM;
+ ima_digest_db_size += sizeof(struct ima_digest); + ima_digest_db_size += digest_len; + d->algo = algo; d->type = type; d->modifiers = modifiers; @@ -141,6 +144,9 @@ static void ima_del_digest_data_entry(u8 *digest, enum hash_algo algo, if (--d->count > 0) return;
+ ima_digest_db_size -= sizeof(struct ima_digest); + ima_digest_db_size -= hash_digest_size[algo]; + hlist_del_rcu(&d->hnext); atomic_long_dec(&ima_digests_htable.len); kfree(d); @@ -349,6 +355,11 @@ static bool __init load_digest_list(struct dir_context *__ctx, const char *name,
size = ret;
+ if (size > ima_digest_db_max_size - ima_digest_db_size) { + pr_err_once("digest DB is full: %d\n", ima_digest_db_size); + goto out_fput; + } + ima_check_measured_appraised(file);
ret = ima_parse_compact_list(size, datap, DIGEST_LIST_OP_ADD); diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 5a7be9595e28..917df82b6b39 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -320,12 +320,15 @@ static ssize_t ima_read_file(char *path, struct dentry *dentry) rc = ima_parse_add_rule(p); } else if (dentry == digest_list_data || dentry == digest_list_data_del) { +#ifdef CONFIG_IMA_DIGEST_LIST /* Only check size when adding digest lists */ if (dentry == digest_list_data && size > ima_digest_db_max_size - ima_digest_db_size) { + pr_err("digest DB is full: %d\n", ima_digest_db_size); rc = -ENOMEM; break; } +#endif /* * Disable usage of digest lists if not measured * or appraised. @@ -340,12 +343,13 @@ static ssize_t ima_read_file(char *path, struct dentry *dentry)
if (rc < 0) break; +#ifdef CONFIG_IMA_DIGEST_LIST + else if (dentry == digest_list_data) + pr_debug("digest imported, current DB size: %d\n", ima_digest_db_size); + else if (dentry == digest_list_data_del) + pr_debug("digest deleted, current DB size: %d\n", ima_digest_db_size); +#endif size -= rc; - - if (dentry == digest_list_data) - ima_digest_db_size += rc; - if (dentry == digest_list_data_del) - ima_digest_db_size -= rc; }
vfree(data);
From: shenxiangwei shenxiangwei1@huawei.com
hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I4XHBM CVE: NA
-------------
The check for control character shouldn't be added when import a binary digest list.
Signed-off-by: shenxiangwei shenxiangwei1@huawei.com Reviewed-by: Lu Huaxin luhuaxin1@huawei.com Reviewed-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Zhou Shuiqingzhoushuiqing2@huawei.com --- security/integrity/ima/ima_fs.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 917df82b6b39..e8856d63df7b 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -389,19 +389,21 @@ static ssize_t ima_write_data(struct file *file, const char __user *buf, goto out_free;
data[datalen] = '\0'; - for (i = 0; data[i] != '\n' && data[i] != '\0'; i++) { - if (iscntrl(data[i])) { - pr_err_once("invalid path (control characters are not allowed)\n"); - result = -EINVAL; - goto out_free; - } - }
result = mutex_lock_interruptible(&ima_write_mutex); if (result < 0) goto out_free;
if (data[0] == '/') { + for (i = 0; data[i] != '\n' && data[i] != '\0'; i++) { + if (iscntrl(data[i])) { + pr_err_once("invalid path (control characters are not allowed)\n"); + result = -EINVAL; + mutex_unlock(&ima_write_mutex); + goto out_free; + } + } + result = ima_read_file(data, dentry); } else if (dentry == ima_policy) { if (ima_appraise & IMA_APPRAISE_POLICY) {