From: zhoushuiqing zhoushuiqing2@huawei.com
v2: -remove unused variable in ima_main.c
v3: -modify patch header information
v4: -add the CONFIG_IMA_DIGEST_LIST macro to isolate the code
v5: -use other macros to isolate PGP-related code
v6: -remove unnecessary macro controls and fix potential memory leaks in ima_digest_list.c
David Howells (4): PGPLIB: PGP definitions (RFC 4880) PGPLIB: Basic packet parser KEYS: Provide PGP key description autogeneration KEYS: Provide a function to load keys from a PGP keyring blob
Mimi Zohar (1): initramfs: add file metadata
Roberto Sassu (34): initramfs: read metadata from special file METADATA!!! gen_init_cpio: add support for file metadata init: Add kernel option to force usage of tmpfs for rootfs ima: Add enforce-evm and log-evm modes to strictly check EVM status ima: Allow choice of file hash algorithm for measurement and audit ima: Generalize ima_read_policy() ima: Generalize ima_write_policy() and raise uploaded data size limit ima: Generalize policy file operations ima: Use ima_show_htable_value to show violations and hash table data ima: Add parser of compact digest list ima: Prevent usage of digest lists not measured or appraised ima: Introduce new securityfs files ima: Introduce new hook DIGEST_LIST_CHECK ima: Load all digest lists from a directory at boot time ima: Add support for measurement with digest lists ima: Add support for appraisal with digest lists evm: Add support for digest lists of metadata ima: Add meta_immutable appraisal type ima: Introduce exec_tcb policy ima: Introduce appraise_exec_tcb policy ima: Introduce appraise_exec_immutable policy ima: Add Documentation/security/IMA-digest-lists.txt mpi: introduce mpi_key_length() rsa: add parser of raw format KEYS: PGP data parser KEYS: Introduce load_pgp_public_keyring() certs: Introduce search_trusted_key() ima: Search key in the built-in keyrings ima: Allow direct upload of digest lists to securityfs ima: Add parser keyword to the policy evm: Extend evm= with x509. allow_metadata_writes and complete values ima: Execute parser to upload digest lists not recognizable by the kernel evm: Propagate choice of HMAC algorithm in evm_crypto.c config: add digest list options for arm64 and x86
Zhang Tianxing (5): ima: fix a memory leak in ima_del_digest_data_entry ima: Add max size for IMA digest database ima: don't allow control characters in policy path ima: fix CONFIG_IMA_DIGEST_DB_MEGABYTES in openeuler_defconfig ima: fix db size overflow and Kconfig issues
Zheng Zengkai (1): Revert "evm: Refuse EVM_ALLOW_METADATA_WRITES only if an HMAC key is loaded"
shenxiangwei (1): ima: bugfix for digest lists importing
Documentation/ABI/testing/evm | 4 +- .../admin-guide/kernel-parameters.txt | 49 +- Documentation/security/IMA-digest-lists.txt | 259 ++++++++++ arch/arm64/configs/openeuler_defconfig | 11 + arch/x86/configs/openeuler_defconfig | 29 +- certs/Kconfig | 7 + certs/Makefile | 7 + certs/system_certificates.S | 18 + certs/system_keyring.c | 46 ++ crypto/asymmetric_keys/Kconfig | 25 + crypto/asymmetric_keys/Makefile | 10 + crypto/asymmetric_keys/pgp_library.c | 284 +++++++++++ crypto/asymmetric_keys/pgp_parser.h | 27 + crypto/asymmetric_keys/pgp_preload.c | 123 +++++ crypto/asymmetric_keys/pgp_public_key.c | 387 +++++++++++++++ crypto/rsa.c | 16 + crypto/rsa_helper.c | 76 +++ include/crypto/internal/rsa.h | 10 + include/linux/initramfs.h | 21 + include/linux/kernel_read_file.h | 13 + include/linux/mpi.h | 4 + include/linux/pgp.h | 223 +++++++++ include/linux/pgplib.h | 48 ++ include/linux/verification.h | 7 + init/do_mounts.c | 19 + init/initramfs.c | 161 ++++++ lib/mpi/mpicoder.c | 37 ++ security/integrity/digsig_asymmetric.c | 13 + security/integrity/evm/Kconfig | 32 ++ security/integrity/evm/evm.h | 3 + security/integrity/evm/evm_crypto.c | 46 +- security/integrity/evm/evm_main.c | 141 +++++- security/integrity/evm/evm_secfs.c | 4 + security/integrity/iint.c | 4 + security/integrity/ima/Kconfig | 49 ++ security/integrity/ima/Makefile | 1 + security/integrity/ima/ima.h | 56 +++ security/integrity/ima/ima_api.c | 55 ++- security/integrity/ima/ima_appraise.c | 137 +++++ security/integrity/ima/ima_digest_list.c | 466 ++++++++++++++++++ security/integrity/ima/ima_digest_list.h | 54 ++ security/integrity/ima/ima_efi.c | 3 + security/integrity/ima/ima_fs.c | 320 ++++++++++++ security/integrity/ima/ima_init.c | 4 + security/integrity/ima/ima_main.c | 116 ++++- security/integrity/ima/ima_policy.c | 177 ++++++- security/integrity/integrity.h | 42 ++ usr/Kconfig | 8 + usr/Makefile | 4 +- usr/gen_init_cpio.c | 158 ++++++ usr/gen_initramfs.sh | 7 +- 51 files changed, 3798 insertions(+), 23 deletions(-) create mode 100644 Documentation/security/IMA-digest-lists.txt create mode 100644 crypto/asymmetric_keys/pgp_library.c create mode 100644 crypto/asymmetric_keys/pgp_parser.h create mode 100644 crypto/asymmetric_keys/pgp_preload.c create mode 100644 crypto/asymmetric_keys/pgp_public_key.c create mode 100644 include/linux/initramfs.h create mode 100644 include/linux/pgp.h create mode 100644 include/linux/pgplib.h create mode 100644 security/integrity/ima/ima_digest_list.c create mode 100644 security/integrity/ima/ima_digest_list.h
From: Mimi Zohar zohar@linux.vnet.ibm.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- include/linux/initramfs.h | 21 +++++++++ init/initramfs.c | 95 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 include/linux/initramfs.h
diff --git a/include/linux/initramfs.h b/include/linux/initramfs.h new file mode 100644 index 0000000000000..2f8cee441236b --- /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 e7a01c2ccd1b0..31d90e060a722 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -15,6 +15,10 @@ #include <linux/memblock.h> #include <linux/mm.h> #include <linux/namei.h> +#ifdef CONFIG_IMA_DIGEST_LIST +#include <linux/xattr.h> +#include <linux/initramfs.h> +#endif #include <linux/init_syscalls.h> #include <linux/task_work.h> #include <linux/umh.h> @@ -177,7 +181,11 @@ static __initdata time64_t mtime;
static __initdata unsigned long ino, major, minor, nlink; static __initdata umode_t mode; +#ifdef CONFIG_IMA_DIGEST_LIST +static __initdata unsigned long body_len, name_len, metadata_len; +#else static __initdata unsigned long body_len, name_len; +#endif static __initdata uid_t uid; static __initdata gid_t gid; static __initdata unsigned rdev; @@ -250,7 +258,11 @@ static void __init read_into(char *buf, unsigned size, enum state next) } }
+#ifdef CONFIG_IMA_DIGEST_LIST +static __initdata char *header_buf, *symlink_buf, *name_buf, *metadata_buf; +#else static __initdata char *header_buf, *symlink_buf, *name_buf; +#endif
static int __init do_start(void) { @@ -351,6 +363,89 @@ static int __init maybe_link(void) return 0; }
+#ifdef CONFIG_IMA_DIGEST_LIST +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; +} +#endif static __initdata struct file *wfile; static __initdata loff_t wfile_pos;
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- init/initramfs.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+)
diff --git a/init/initramfs.c b/init/initramfs.c index 31d90e060a722..74819f3b47f54 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -260,6 +260,7 @@ static void __init read_into(char *buf, unsigned size, enum state next)
#ifdef CONFIG_IMA_DIGEST_LIST static __initdata char *header_buf, *symlink_buf, *name_buf, *metadata_buf; +static __initdata char *metadata_buf_ptr, *previous_name_buf; #else static __initdata char *header_buf, *symlink_buf, *name_buf; #endif @@ -448,6 +449,9 @@ static int __init __maybe_unused do_parse_metadata(char *pathname) #endif static __initdata struct file *wfile; static __initdata loff_t wfile_pos; +#ifdef CONFIG_IMA_DIGEST_LIST +static int metadata __initdata; +#endif
static int __init do_name(void) { @@ -456,6 +460,12 @@ static int __init do_name(void) if (strcmp(collected, "TRAILER!!!") == 0) { free_hash(); return 0; +#ifdef CONFIG_IMA_DIGEST_LIST + } else if (strcmp(collected, METADATA_FILENAME) == 0) { + metadata = 1; + } else { + memcpy(previous_name_buf, collected, strlen(collected) + 1); +#endif } clean_path(collected, mode); if (S_ISREG(mode)) { @@ -493,11 +503,51 @@ static int __init do_name(void) return 0; }
+#ifdef CONFIG_IMA_DIGEST_LIST +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; +} +#endif + static int __init do_copy(void) { if (byte_count >= body_len) { if (xwrite(wfile, victim, body_len, &wfile_pos) != body_len) error("write error"); +#ifdef CONFIG_IMA_DIGEST_LIST + if (metadata) + do_process_metadata(victim, body_len, true); +#endif
do_utime_path(&wfile->f_path, mtime); fput(wfile); @@ -509,6 +559,10 @@ static int __init do_copy(void) } else { if (xwrite(wfile, victim, byte_count, &wfile_pos) != byte_count) error("write error"); +#ifdef CONFIG_IMA_DIGEST_LIST + if (metadata) + do_process_metadata(victim, byte_count, false); +#endif body_len -= byte_count; eat(byte_count); return 1; @@ -518,6 +572,9 @@ static int __init do_copy(void) static int __init do_symlink(void) { collected[N_ALIGN(name_len) + body_len] = '\0'; +#ifdef CONFIG_IMA_DIGEST_LIST + memcpy(previous_name_buf, collected, strlen(collected) + 1); +#endif clean_path(collected, 0); init_symlink(collected + N_ALIGN(name_len), collected); init_chown(collected, uid, gid, AT_SYMLINK_NOFOLLOW); @@ -585,8 +642,14 @@ 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); +#ifdef CONFIG_IMA_DIGEST_LIST + previous_name_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, + GFP_KERNEL);
+ if (!header_buf || !symlink_buf || !name_buf || !previous_name_buf) +#else if (!header_buf || !symlink_buf || !name_buf) +#endif panic_show_mem("can't allocate buffers");
state = Start; @@ -631,6 +694,9 @@ static char * __init unpack_to_rootfs(char *buf, unsigned long len) len -= my_inptr; } dir_utime(); +#ifdef CONFIG_IMA_DIGEST_LIST + kfree(previous_name_buf); +#endif kfree(name_buf); kfree(symlink_buf); kfree(header_buf);
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- usr/Kconfig | 8 +++ usr/Makefile | 4 +- usr/gen_init_cpio.c | 158 +++++++++++++++++++++++++++++++++++++++++++ usr/gen_initramfs.sh | 7 +- 4 files changed, 175 insertions(+), 2 deletions(-)
diff --git a/usr/Kconfig b/usr/Kconfig index 8bbcf699fe3be..be7a8ba66c449 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 59d9e8b07a017..f82b6f74aa7bd 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 61230532fef10..7651cc74eedfe 100644 --- a/usr/gen_init_cpio.c +++ b/usr/gen_init_cpio.c @@ -5,6 +5,9 @@ #include <stdbool.h> #include <sys/types.h> #include <sys/stat.h> +#ifdef CONFIG_IMA_DIGEST_LIST +#include <sys/xattr.h> +#endif #include <string.h> #include <unistd.h> #include <time.h> @@ -12,6 +15,9 @@ #include <errno.h> #include <ctype.h> #include <limits.h> +#ifdef CONFIG_IMA_DIGEST_LIST +#include "../include/linux/initramfs.h" +#endif
/* * Original work by Jeff Garzik @@ -28,6 +34,117 @@ static unsigned int offset; static unsigned int ino = 721; static time_t default_mtime; static bool do_csum = false; +#ifdef CONFIG_IMA_DIGEST_LIST +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); +} +#endif
struct file_handler { const char *type; @@ -132,7 +249,11 @@ static int cpio_mkslink(const char *name, const char *target, push_pad(); push_string(target); push_pad(); +#ifdef CONFIG_IMA_DIGEST_LIST + return write_xattrs(name); +#else return 0; +#endif }
static int cpio_mkslink_line(const char *line) @@ -178,7 +299,11 @@ static int cpio_mkgeneric(const char *name, unsigned int mode, 0); /* chksum */ push_hdr(s); push_rest(name); +#ifdef CONFIG_IMA_DIGEST_LIST + return write_xattrs(name); +#else return 0; +#endif }
enum generic_types { @@ -272,7 +397,11 @@ static int cpio_mknod(const char *name, unsigned int mode, 0); /* chksum */ push_hdr(s); push_rest(name); +#ifdef CONFIG_IMA_DIGEST_LIST + return write_xattrs(name); +#else return 0; +#endif }
static int cpio_mknod_line(const char *line) @@ -422,7 +551,11 @@ static int cpio_mkfile(const char *name, const char *location, name += namesize; } ino++; +#ifdef CONFIG_IMA_DIGEST_LIST + rc = write_xattrs(location); +#else rc = 0; +#endif
error: if (file >= 0) @@ -577,10 +710,17 @@ int main (int argc, char *argv[]) int ec = 0; int line_nr = 0; const char *filename; +#ifdef CONFIG_IMA_DIGEST_LIST + enum metadata_types metadata_type = TYPE_NONE; +#endif
default_mtime = time(NULL); while (1) { +#ifdef CONFIG_IMA_DIGEST_LIST + int opt = getopt(argc, argv, "t:e:ch"); +#else int opt = getopt(argc, argv, "t:ch"); +#endif char *invalid;
if (opt == -1) @@ -595,6 +735,11 @@ int main (int argc, char *argv[]) exit(1); } break; +#ifdef CONFIG_IMA_DIGEST_LIST + case 'e': + metadata_type = parse_metadata_type(optarg); + break; +#endif case 'c': do_csum = true; break; @@ -628,6 +773,15 @@ int main (int argc, char *argv[]) usage(argv[0]); exit(1); } +#ifdef CONFIG_IMA_DIGEST_LIST + if (metadata_type != TYPE_NONE) { + metadata_fd = mkstemp(metadata_path); + if (metadata_fd < 0) { + fprintf(stderr, "cannot create temporary file\n"); + exit(1); + } + } +#endif
while (fgets(line, LINE_SIZE, cpio_list)) { int type_idx; @@ -684,5 +838,9 @@ int main (int argc, char *argv[]) if (ec == 0) cpio_trailer();
+#ifdef CONFIG_IMA_DIGEST_LIST + if (metadata_type != TYPE_NONE) + close(metadata_fd); +#endif exit(ec); } diff --git a/usr/gen_initramfs.sh b/usr/gen_initramfs.sh index 63476bb70b41e..36648620d0711 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
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- init/do_mounts.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
diff --git a/init/do_mounts.c b/init/do_mounts.c index 811e94daf0a84..c589b458e95bc 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -31,6 +31,9 @@ int root_mountflags = MS_RDONLY | MS_SILENT; static char * __initdata root_device_name; static char __initdata saved_root_name[64]; static int root_wait; +#ifdef CONFIG_IMA_DIGEST_LIST +static int initramtmpfs; +#endif
dev_t ROOT_DEV;
@@ -333,9 +336,20 @@ static int __init root_delay_setup(char *str) return 1; }
+#ifdef CONFIG_IMA_DIGEST_LIST +static int __init initramtmpfs_setup(char *str) +{ + initramtmpfs = 1; + return 1; +} +#endif + __setup("rootflags=", root_data_setup); __setup("rootfstype=", fs_names_setup); __setup("rootdelay=", root_delay_setup); +#ifdef CONFIG_IMA_DIGEST_LIST +__setup("initramtmpfs", initramtmpfs_setup); +#endif
/* This can return zero length strings. Caller should check */ static int __init split_fs_names(char *page, size_t size, char *names) @@ -665,7 +679,12 @@ struct file_system_type rootfs_fs_type = {
void __init init_rootfs(void) { +#ifdef CONFIG_IMA_DIGEST_LIST + if (IS_ENABLED(CONFIG_TMPFS) && + (!saved_root_name[0] || initramtmpfs) && +#else if (IS_ENABLED(CONFIG_TMPFS) && !saved_root_name[0] && +#endif (!root_fs_names || strstr(root_fs_names, "tmpfs"))) is_tmpfs = true; }
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- Documentation/admin-guide/kernel-parameters.txt | 3 ++- security/integrity/ima/ima_appraise.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index d8dac30c988e2..6a9143e0103bb 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1950,7 +1950,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 491c1aca0b1ce..bd54ff20f0acd 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -19,6 +19,9 @@
#include "ima.h"
+#ifdef CONFIG_IMA_DIGEST_LIST +static bool ima_appraise_req_evm __ro_after_init; +#endif #ifdef CONFIG_IMA_APPRAISE_BOOTPARAM static char *ima_appraise_cmdline_default __initdata; core_param(ima_appraise, ima_appraise_cmdline_default, charp, 0); @@ -52,6 +55,11 @@ void __init ima_appraise_parse_cmdline(void) } else { ima_appraise = appraisal_state; } +#ifdef CONFIG_IMA_DIGEST_LIST + if (strcmp(str, "enforce-evm") == 0 || + strcmp(str, "log-evm") == 0) + ima_appraise_req_evm = true; +#endif } #endif
@@ -522,7 +530,15 @@ int ima_appraise_measurement(enum ima_hooks func, switch (status) { case INTEGRITY_PASS: case INTEGRITY_PASS_IMMUTABLE: +#ifdef CONFIG_IMA_DIGEST_LIST + break; +#endif case INTEGRITY_UNKNOWN: +#ifdef CONFIG_IMA_DIGEST_LIST + if (ima_appraise_req_evm && + xattr_value->type != EVM_IMA_XATTR_DIGSIG) + goto out; +#endif 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
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- security/integrity/ima/ima.h | 4 ++ security/integrity/ima/ima_appraise.c | 7 ++++ security/integrity/ima/ima_main.c | 60 +++++++++++++++++++++++++++ 3 files changed, 71 insertions(+)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index c29db699c996e..c470556613324 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -324,10 +324,12 @@ 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); +#ifndef CONFIG_IMA_DIGEST_LIST 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); +#endif /* CONFIG_IMA_DIGEST_LIST */
#else static inline int ima_check_blacklist(struct integrity_iint_cache *iint, @@ -366,6 +368,7 @@ static inline enum integrity_status ima_get_cache_status(struct integrity_iint_c return INTEGRITY_UNKNOWN; }
+#ifndef CONFIG_IMA_DIGEST_LIST static inline enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len) { @@ -378,6 +381,7 @@ static inline int ima_read_xattr(struct dentry *dentry, { return 0; } +#endif
#endif /* CONFIG_IMA_APPRAISE */
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index bd54ff20f0acd..93d75dad3bb12 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -231,6 +231,7 @@ enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value, return ima_hash_algo; }
+#ifndef CONFIG_IMA_DIGEST_LIST int ima_read_xattr(struct dentry *dentry, struct evm_ima_xattr_data **xattr_value, int xattr_len) { @@ -242,6 +243,7 @@ int ima_read_xattr(struct dentry *dentry, ret = 0; return ret; } +#endif /* CONFIG_IMA_DIGEST_LIST */
/* * calc_file_id_hash - calculate the hash of the ima_file_id struct data @@ -501,6 +503,11 @@ int ima_appraise_measurement(enum ima_hooks func, if (!(inode->i_opflags & IOP_XATTR) && !try_modsig) return INTEGRITY_UNKNOWN;
+#ifdef CONFIG_IMA_DIGEST_LIST + if (xattr_value && xattr_value->type == EVM_IMA_XATTR_DIGSIG && + xattr_len == sizeof(struct signature_v2_hdr)) + rc = -ENODATA; +#endif /* 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 d66a0a36415e8..1a1475077982f 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -153,6 +153,66 @@ static void ima_rdwr_violation_check(struct file *file, "invalid_pcr", "open_writers"); }
+#ifdef CONFIG_IMA_DIGEST_LIST +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; +} +#endif static void ima_check_last_writer(struct integrity_iint_cache *iint, struct inode *inode, struct file *file) {
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- security/integrity/ima/ima_fs.c | 58 +++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+)
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index cd1683dad3bf0..8525aac7d18fe 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -22,10 +22,22 @@ #include <linux/parser.h> #include <linux/vmalloc.h>
+#ifdef CONFIG_IMA_DIGEST_LIST +#include <linux/file.h> +#endif #include "ima.h"
static DEFINE_MUTEX(ima_write_mutex);
+#ifdef CONFIG_IMA_DIGEST_LIST +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; +#endif bool ima_canonical_fmt; static int __init default_canonical_fmt_setup(char *str) { @@ -271,11 +283,19 @@ static const struct file_operations ima_ascii_measurements_ops = { .release = seq_release, };
+#ifdef CONFIG_IMA_DIGEST_LIST +static ssize_t ima_read_file(char *path, struct dentry *dentry) +#else static ssize_t ima_read_policy(char *path) +#endif { void *data = NULL; char *datap; size_t size; +#ifdef CONFIG_IMA_DIGEST_LIST + struct file *file; + enum kernel_read_file_id file_id = READING_POLICY; +#endif int rc, pathlen = strlen(path);
char *p; @@ -283,26 +303,55 @@ static ssize_t ima_read_policy(char *path) /* remove \n */ datap = path; strsep(&datap, "\n"); +#ifdef CONFIG_IMA_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)); + return PTR_ERR(file); + }
+ rc = kernel_read_file(file, 0, &data, INT_MAX, NULL, file_id); +#else rc = kernel_read_file_from_path(path, 0, &data, INT_MAX, NULL, READING_POLICY); +#endif if (rc < 0) { +#ifdef CONFIG_IMA_DIGEST_LIST + pr_err("Unable to read file: %s (%d)", path, rc); + fput(file); +#else pr_err("Unable to open file: %s (%d)", path, rc); +#endif return rc; } size = rc; rc = 0;
datap = data; +#ifdef CONFIG_IMA_DIGEST_LIST + 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); + } +#else while (size > 0 && (p = strsep(&datap, "\n"))) { pr_debug("rule: %s\n", p); rc = ima_parse_add_rule(p); +#endif if (rc < 0) break; size -= rc; }
vfree(data); +#ifdef CONFIG_IMA_DIGEST_LIST + fput(file); +#endif if (rc < 0) return rc; else if (size) @@ -316,6 +365,9 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, { char *data; ssize_t result; +#ifdef CONFIG_IMA_DIGEST_LIST + struct dentry *dentry = file_dentry(file); +#endif
if (datalen >= PAGE_SIZE) datalen = PAGE_SIZE - 1; @@ -336,7 +388,11 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, goto out_free;
if (data[0] == '/') { +#ifdef CONFIG_IMA_DIGEST_LIST + result = ima_read_file(data, dentry); +#else result = ima_read_policy(data); +#endif } 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,6 +412,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, return result; }
+#ifndef CONFIG_IMA_DIGEST_LIST static struct dentry *ima_dir; static struct dentry *ima_symlink; static struct dentry *binary_runtime_measurements; @@ -363,6 +420,7 @@ static struct dentry *ascii_runtime_measurements; static struct dentry *runtime_measurements_count; static struct dentry *violations; static struct dentry *ima_policy; +#endif
enum ima_fs_flags { IMA_FS_BUSY,
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- security/integrity/ima/ima_fs.c | 54 ++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-)
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 8525aac7d18fe..9f36731b4cc5b 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -360,8 +360,13 @@ static ssize_t ima_read_policy(char *path) return pathlen; }
+#ifdef CONFIG_IMA_DIGEST_LIST +static ssize_t ima_write_data(struct file *file, const char __user *buf, + size_t datalen, loff_t *ppos) +#else static ssize_t ima_write_policy(struct file *file, const char __user *buf, size_t datalen, loff_t *ppos) +#endif { char *data; ssize_t result; @@ -369,19 +374,38 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, struct dentry *dentry = file_dentry(file); #endif
+#ifndef CONFIG_IMA_DIGEST_LIST if (datalen >= PAGE_SIZE) datalen = PAGE_SIZE - 1; +#endif
/* No partial writes. */ result = -EINVAL; if (*ppos != 0) goto out;
+#ifdef CONFIG_IMA_DIGEST_LIST + 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'; +#else data = memdup_user_nul(buf, datalen); if (IS_ERR(data)) { result = PTR_ERR(data); goto out; } +#endif
result = mutex_lock_interruptible(&ima_write_mutex); if (result < 0) @@ -390,23 +414,47 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, if (data[0] == '/') { #ifdef CONFIG_IMA_DIGEST_LIST result = ima_read_file(data, dentry); + } 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_read_policy(data); -#endif } 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; +#endif } else { +#ifdef CONFIG_IMA_DIGEST_LIST + pr_err("Unknown data type\n"); + result = -EINVAL; +#else result = ima_parse_add_rule(data); +#endif } mutex_unlock(&ima_write_mutex); out_free: +#ifdef CONFIG_IMA_DIGEST_LIST + vfree(data); +#else kfree(data); +#endif out: +#ifdef CONFIG_IMA_DIGEST_LIST + if (dentry == ima_policy && result < 0) +#else if (result < 0) +#endif valid_policy = 0;
return result; @@ -502,7 +550,11 @@ static int ima_release_policy(struct inode *inode, struct file *file)
static const struct file_operations ima_measure_policy_ops = { .open = ima_open_policy, +#ifdef CONFIG_IMA_DIGEST_LIST + .write = ima_write_data, +#else .write = ima_write_policy, +#endif .read = seq_read, .release = ima_release_policy, .llseek = generic_file_llseek,
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- security/integrity/ima/ima_fs.c | 89 ++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-)
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 9f36731b4cc5b..9a8fb2a4185a1 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -471,9 +471,24 @@ static struct dentry *ima_policy; #endif
enum ima_fs_flags { +#ifdef CONFIG_IMA_DIGEST_LIST + IMA_POLICY_BUSY, +#endif IMA_FS_BUSY, };
+#ifdef CONFIG_IMA_DIGEST_LIST +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; +} +#endif + static unsigned long ima_fs_flags;
#ifdef CONFIG_IMA_READ_POLICY @@ -485,12 +500,42 @@ static const struct seq_operations ima_policy_seqops = { }; #endif
+#ifdef CONFIG_IMA_DIGEST_LIST +/* + * ima_open_data_upload: sequentialize access to the data upload interface + */ +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 + } +#else /* * ima_open_policy: sequentialize access to the policy file */ static int ima_open_policy(struct inode *inode, struct file *filp) { +#endif /* CONFIG_IMA_DIGEST_LIST */ if (!(filp->f_flags & O_WRONLY)) { +#ifdef CONFIG_IMA_DIGEST_LIST + 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, seq_ops); + } + if (test_and_set_bit(flag, &ima_fs_flags)) +#else #ifndef CONFIG_IMA_READ_POLICY return -EACCES; #else @@ -502,10 +547,25 @@ static int ima_open_policy(struct inode *inode, struct file *filp) #endif } if (test_and_set_bit(IMA_FS_BUSY, &ima_fs_flags)) +#endif return -EBUSY; return 0; }
+#ifdef CONFIG_IMA_DIGEST_LIST +/* + * 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_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); +#else /* * ima_release_policy - start using the new measure policy rules. * @@ -516,9 +576,16 @@ static int ima_open_policy(struct inode *inode, struct file *filp) static int ima_release_policy(struct inode *inode, struct file *file) { const char *cause = valid_policy ? "completed" : "failed"; +#endif
if ((file->f_flags & O_ACCMODE) == O_RDONLY) return seq_release(inode, file); +#ifdef CONFIG_IMA_DIGEST_LIST + if (dentry != ima_policy) { + clear_bit(flag, &ima_fs_flags); + return 0; + } +#endif
if (valid_policy && ima_check_policy() < 0) { cause = "failed"; @@ -532,7 +599,11 @@ static int ima_release_policy(struct inode *inode, struct file *file) if (!valid_policy) { ima_delete_rules(); valid_policy = 1; +#ifdef CONFIG_IMA_DIGEST_LIST + clear_bit(flag, &ima_fs_flags); +#else clear_bit(IMA_FS_BUSY, &ima_fs_flags); +#endif return 0; }
@@ -541,22 +612,32 @@ static int ima_release_policy(struct inode *inode, struct file *file) securityfs_remove(ima_policy); ima_policy = NULL; #elif defined(CONFIG_IMA_WRITE_POLICY) +#ifdef CONFIG_IMA_DIGEST_LIST + clear_bit(flag, &ima_fs_flags); +#else clear_bit(IMA_FS_BUSY, &ima_fs_flags); +#endif #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, #ifdef CONFIG_IMA_DIGEST_LIST +static const struct file_operations ima_data_upload_ops = { + .open = ima_open_data_upload, .write = ima_write_data, #else +static const struct file_operations ima_measure_policy_ops = { + .open = ima_open_policy, .write = ima_write_policy, #endif .read = seq_read, +#ifdef CONFIG_IMA_DIGEST_LIST + .release = ima_release_data_upload, +#else .release = ima_release_policy, +#endif .llseek = generic_file_llseek, };
@@ -612,7 +693,11 @@ int __init ima_fs_init(void)
ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS, ima_dir, NULL, +#ifdef CONFIG_IMA_DIGEST_LIST + &ima_data_upload_ops); +#else &ima_measure_policy_ops); +#endif if (IS_ERR(ima_policy)) { ret = PTR_ERR(ima_policy); goto out;
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- security/integrity/ima/ima_fs.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 9a8fb2a4185a1..e17b9f371fbac 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -50,16 +50,33 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup);
static int valid_policy = 1;
+#ifdef CONFIG_IMA_DIGEST_LIST +static ssize_t ima_show_htable_value(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + atomic_long_t *val = NULL; +#else static ssize_t ima_show_htable_value(char __user *buf, size_t count, loff_t *ppos, atomic_long_t *val) { +#endif char tmpbuf[32]; /* greater than largest 'long' string value */ ssize_t len;
+#ifdef CONFIG_IMA_DIGEST_LIST + if (filp->f_path.dentry == violations) + val = &ima_htable.violations; + else if (filp->f_path.dentry == runtime_measurements_count) + val = &ima_htable.len; +#endif len = scnprintf(tmpbuf, sizeof(tmpbuf), "%li\n", atomic_long_read(val)); return simple_read_from_buffer(buf, count, ppos, tmpbuf, len); }
+#ifdef CONFIG_IMA_DIGEST_LIST +static const struct file_operations ima_htable_value_ops = { + .read = ima_show_htable_value, +#else static ssize_t ima_show_htable_violations(struct file *filp, char __user *buf, size_t count, loff_t *ppos) @@ -82,6 +99,7 @@ static ssize_t ima_show_measurements_count(struct file *filp,
static const struct file_operations ima_measurements_count_ops = { .read = ima_show_measurements_count, +#endif .llseek = generic_file_llseek, };
@@ -677,7 +695,11 @@ int __init ima_fs_init(void) runtime_measurements_count = securityfs_create_file("runtime_measurements_count", S_IRUSR | S_IRGRP, ima_dir, NULL, +#ifdef CONFIG_IMA_DIGEST_LIST + &ima_htable_value_ops); +#else &ima_measurements_count_ops); +#endif if (IS_ERR(runtime_measurements_count)) { ret = PTR_ERR(runtime_measurements_count); goto out; @@ -685,7 +707,11 @@ int __init ima_fs_init(void)
violations = securityfs_create_file("violations", S_IRUSR | S_IRGRP, +#ifdef CONFIG_IMA_DIGEST_LIST + ima_dir, NULL, &ima_htable_value_ops); +#else ima_dir, NULL, &ima_htable_violations_ops); +#endif if (IS_ERR(violations)) { ret = PTR_ERR(violations); goto out;
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@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 | 28 ++++ 5 files changed, 247 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 60a511c6b583e..cf130ae38827b 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 2499f2485c04f..fb9744a1c3d40 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 0000000000000..0dcc69954887d --- /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 0000000000000..ac6b0ee0aec61 --- /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 7167a6e99bdc0..43debd01745c2 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -18,6 +18,10 @@ #include <crypto/hash.h> #include <linux/key.h> #include <linux/audit.h> +#ifdef CONFIG_IMA_DIGEST_LIST +#include <crypto/sha2.h> +#include <linux/hash_info.h> +#endif
/* iint action cache flags */ #define IMA_MEASURE 0x00000001 @@ -173,6 +177,30 @@ struct integrity_iint_cache { struct ima_digest_data *ima_hash; };
+#ifdef CONFIG_IMA_DIGEST_LIST +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)); +} + +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); +#endif /* CONFIG_IMA_DIGEST_LIST */ + /* rbtree tree calls to lookup, insert, delete * integrity data associated with an inode. */
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- security/integrity/ima/ima.h | 3 ++ security/integrity/ima/ima_digest_list.c | 48 ++++++++++++++++++++++++ security/integrity/ima/ima_digest_list.h | 4 ++ security/integrity/ima/ima_main.c | 5 +++ 4 files changed, 60 insertions(+)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index c470556613324..42ce87c2d8d80 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -57,6 +57,9 @@ 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[]; +#ifdef CONFIG_IMA_DIGEST_LIST +extern int ima_digest_list_actions; +#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 0dcc69954887d..901990c8dfa88 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 ac6b0ee0aec61..5bd2388ff95ee 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 1a1475077982f..64e539fb2607a 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -36,6 +36,11 @@ int ima_appraise; #endif
int __ro_after_init ima_hash_algo = HASH_ALGO_SHA1; + +#ifdef CONFIG_IMA_DIGEST_LIST +/* Actions (measure/appraisal) for which digest lists can be used */ +int ima_digest_list_actions; +#endif static int hash_setup_done;
static struct notifier_block ima_lsm_policy_notifier = {
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- include/linux/kernel_read_file.h | 13 ++++++++ security/integrity/ima/ima_fs.c | 52 ++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+)
diff --git a/include/linux/kernel_read_file.h b/include/linux/kernel_read_file.h index 90451e2e12bd1..be906d6910336 100644 --- a/include/linux/kernel_read_file.h +++ b/include/linux/kernel_read_file.h @@ -6,6 +6,7 @@ #include <linux/types.h>
/* This is a list of *what* is being read, not *how* nor *where*. */ +#ifdef CONFIG_IMA_DIGEST_LIST #define __kernel_read_file_id(id) \ id(UNKNOWN, unknown) \ id(FIRMWARE, firmware) \ @@ -13,8 +14,20 @@ 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, ) +#else +#define __kernel_read_file_id(id) \ + id(UNKNOWN, unknown) \ + id(FIRMWARE, firmware) \ + id(MODULE, kernel-module) \ + id(KEXEC_IMAGE, kexec-image) \ + id(KEXEC_INITRAMFS, kexec-initramfs) \ + id(POLICY, security-policy) \ + id(X509_CERTIFICATE, x509-certificate) \ + id(MAX_ID, ) +#endif
#define __fid_enumify(ENUM, dummy) READING_ ## ENUM, #define __fid_stringify(dummy, str) #str, diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index e17b9f371fbac..7359f44b5a292 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -26,6 +26,9 @@ #include <linux/file.h> #endif #include "ima.h" +#ifdef CONFIG_IMA_DIGEST_LIST +#include "ima_digest_list.h" +#endif
static DEFINE_MUTEX(ima_write_mutex);
@@ -37,6 +40,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; #endif bool ima_canonical_fmt; static int __init default_canonical_fmt_setup(char *str) @@ -68,6 +74,8 @@ static ssize_t ima_show_htable_value(char __user *buf, size_t count, val = &ima_htable.violations; else if (filp->f_path.dentry == runtime_measurements_count) val = &ima_htable.len; + 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); @@ -313,6 +321,7 @@ static ssize_t ima_read_policy(char *path) #ifdef CONFIG_IMA_DIGEST_LIST struct file *file; enum kernel_read_file_id file_id = READING_POLICY; + int op = DIGEST_LIST_OP_ADD; #endif int rc, pathlen = strlen(path);
@@ -322,6 +331,9 @@ static ssize_t ima_read_policy(char *path) datap = path; strsep(&datap, "\n"); #ifdef CONFIG_IMA_DIGEST_LIST + 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)); @@ -355,6 +367,18 @@ static ssize_t ima_read_policy(char *path)
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); } #else while (size > 0 && (p = strsep(&datap, "\n"))) { @@ -491,6 +515,7 @@ static struct dentry *ima_policy; enum ima_fs_flags { #ifdef CONFIG_IMA_DIGEST_LIST IMA_POLICY_BUSY, + IMA_DIGEST_LIST_DATA_BUSY, #endif IMA_FS_BUSY, }; @@ -502,6 +527,10 @@ static enum ima_fs_flags ima_get_dentry_flag(struct dentry *dentry)
if (dentry == ima_policy) flag = IMA_POLICY_BUSY; +#ifdef CONFIG_IMA_DIGEST_LIST + else if (dentry == digest_list_data || dentry == digest_list_data_del) + flag = IMA_DIGEST_LIST_DATA_BUSY; +#endif
return flag; } @@ -728,10 +757,33 @@ 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); +#ifdef CONFIG_IMA_DIGEST_LIST + securityfs_remove(digest_list_data_del); + securityfs_remove(digest_list_data); + securityfs_remove(digests_count); +#endif securityfs_remove(violations); securityfs_remove(runtime_measurements_count); securityfs_remove(ascii_runtime_measurements);
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- security/integrity/ima/ima.h | 25 +++++++++++++++++++++++++ security/integrity/ima/ima_main.c | 5 +++++ security/integrity/ima/ima_policy.c | 18 ++++++++++++++++++ 3 files changed, 48 insertions(+)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 42ce87c2d8d80..aeabdb45c72c9 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -189,6 +189,7 @@ static inline unsigned int ima_hash_key(u8 *digest) return (digest[0] | digest[1] << 8) % IMA_MEASURE_HTABLE_SIZE; }
+#ifdef CONFIG_IMA_DIGEST_LIST #define __ima_hooks(hook) \ hook(NONE, none) \ hook(FILE_CHECK, file) \ @@ -206,7 +207,28 @@ 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) +#else +#define __ima_hooks(hook) \ + hook(NONE, none) \ + hook(FILE_CHECK, file) \ + hook(MMAP_CHECK, mmap) \ + hook(MMAP_CHECK_REQPROT, mmap_reqprot) \ + hook(BPRM_CHECK, bprm) \ + hook(CREDS_CHECK, creds) \ + hook(POST_SETATTR, post_setattr) \ + hook(MODULE_CHECK, module) \ + hook(FIRMWARE_CHECK, firmware) \ + hook(KEXEC_KERNEL_CHECK, kexec_kernel) \ + hook(KEXEC_INITRAMFS_CHECK, kexec_initramfs) \ + hook(POLICY_CHECK, policy) \ + hook(KEXEC_CMDLINE, kexec_cmdline) \ + hook(KEY_CHECK, key) \ + hook(CRITICAL_DATA, critical_data) \ + hook(SETXATTR_CHECK, setxattr_check) \ + hook(MAX_CHECK, none) +#endif
#define __ima_hook_enumify(ENUM, str) ENUM, #define __ima_stringify(arg) (#arg) @@ -313,6 +335,9 @@ 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 +#ifdef CONFIG_IMA_DIGEST_LIST +#define IMA_APPRAISE_DIGEST_LIST 0x80 +#endif
#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 64e539fb2607a..e40975b2cdfdf 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -832,7 +832,12 @@ const int read_idmap[READING_MAX_ID] = { [READING_MODULE] = MODULE_CHECK, [READING_KEXEC_IMAGE] = KEXEC_KERNEL_CHECK, [READING_KEXEC_INITRAMFS] = KEXEC_INITRAMFS_CHECK, +#ifdef CONFIG_IMA_DIGEST_LIST + [READING_POLICY] = POLICY_CHECK, + [READING_DIGEST_LIST] = DIGEST_LIST_CHECK +#else [READING_POLICY] = POLICY_CHECK +#endif };
/** diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 3ca8b7348c2e4..9d9290b6313cd 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -188,6 +188,9 @@ 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}, +#ifdef CONFIG_IMA_DIGEST_LIST + {.action = MEASURE, .func = DIGEST_LIST_CHECK, .flags = IMA_FUNC}, +#endif };
static struct ima_rule_entry default_appraise_rules[] __ro_after_init = { @@ -247,6 +250,10 @@ 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}, +#ifdef CONFIG_IMA_DIGEST_LIST + {.action = APPRAISE, .func = DIGEST_LIST_CHECK, + .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, +#endif };
static struct ima_rule_entry critical_data_rules[] __ro_after_init = { @@ -855,6 +862,10 @@ static int ima_appraise_flag(enum ima_hooks func) return IMA_APPRAISE_POLICY; else if (func == KEXEC_KERNEL_CHECK) return IMA_APPRAISE_KEXEC; +#ifdef CONFIG_IMA_DIGEST_LIST + else if (func == DIGEST_LIST_CHECK) + return IMA_APPRAISE_DIGEST_LIST; +#endif return 0; }
@@ -1273,6 +1284,9 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) case POST_SETATTR: case FIRMWARE_CHECK: case POLICY_CHECK: +#ifdef CONFIG_IMA_DIGEST_LIST + case DIGEST_LIST_CHECK: +#endif if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC | IMA_UID | IMA_FOWNER | IMA_FSUUID | IMA_INMASK | IMA_EUID | IMA_PCR | @@ -1529,6 +1543,10 @@ 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; +#ifdef CONFIG_IMA_DIGEST_LIST + else if (strcmp(args[0].from, "DIGEST_LIST_CHECK") == 0) + entry->func = DIGEST_LIST_CHECK; +#endif else result = -EINVAL; if (!result)
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- security/integrity/iint.c | 4 + security/integrity/ima/Kconfig | 8 ++ security/integrity/ima/ima_digest_list.c | 95 ++++++++++++++++++++++++ security/integrity/integrity.h | 1 + 4 files changed, 108 insertions(+)
diff --git a/security/integrity/iint.c b/security/integrity/iint.c index c73858e8c6d51..a180a0a6fe0d0 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -204,6 +204,10 @@ void __init integrity_load_keys(void)
if (!IS_ENABLED(CONFIG_IMA_LOAD_X509)) evm_load_x509(); + +#ifdef CONFIG_IMA_DIGEST_LIST + ima_load_digest_lists(); +#endif }
static int __init integrity_fs_init(void) diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index cf130ae38827b..73c582d97737c 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 901990c8dfa88..91c70562aa86d 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 43debd01745c2..6cbc872dceada 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -199,6 +199,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); #endif /* CONFIG_IMA_DIGEST_LIST */
/* rbtree tree calls to lookup, insert, delete
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- .../admin-guide/kernel-parameters.txt | 9 +++ security/integrity/ima/ima.h | 12 ++++ security/integrity/ima/ima_api.c | 55 ++++++++++++++++++- security/integrity/ima/ima_digest_list.c | 25 +++++++++ security/integrity/ima/ima_init.c | 4 ++ security/integrity/ima/ima_main.c | 28 +++++++++- security/integrity/ima/ima_policy.c | 5 ++ security/integrity/integrity.h | 3 + 8 files changed, 139 insertions(+), 2 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 6a9143e0103bb..4394c25a0e4d0 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1962,6 +1962,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 aeabdb45c72c9..41879cb5bea32 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -58,6 +58,8 @@ extern int ima_appraise; extern struct tpm_chip *ima_tpm_chip; extern const char boot_aggregate_name[]; #ifdef CONFIG_IMA_DIGEST_LIST +extern int ima_digest_list_pcr; +extern bool ima_plus_standard_pcr; extern int ima_digest_list_actions; #endif
@@ -293,7 +295,12 @@ 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, +#ifdef CONFIG_IMA_DIGEST_LIST + struct ima_template_desc *template_desc, + struct ima_digest *digest); +#else struct ima_template_desc *template_desc); +#endif int process_buffer_measurement(struct mnt_idmap *idmap, struct inode *inode, const void *buf, int size, const char *eventname, enum ima_hooks func, @@ -305,8 +312,13 @@ 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, +#ifdef CONFIG_IMA_DIGEST_LIST + struct inode *inode, const unsigned char *filename, + int pcr, struct ima_digest *digest); +#else struct inode *inode, const unsigned char *filename, int pcr); +#endif 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 d3662f4acadc1..869bc94f273cc 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -102,11 +102,19 @@ 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, +#ifdef CONFIG_IMA_DIGEST_LIST + const unsigned char *filename, int pcr, + struct ima_digest *digest) +#else const unsigned char *filename, int pcr) +#endif { static const char op[] = "add_template_measure"; static const char audit_cause[] = "hashing_error"; char *template_name = entry->template_desc->name; +#ifdef CONFIG_IMA_DIGEST_LIST + struct ima_template_entry *duplicated_entry = NULL; +#endif int result;
if (!violation) { @@ -119,8 +127,27 @@ int ima_store_template(struct ima_template_entry *entry, return result; } } +#ifdef CONFIG_IMA_DIGEST_LIST + 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; + } +#endif entry->pcr = pcr; result = ima_add_template_entry(entry, violation, op, inode, filename); +#ifdef CONFIG_IMA_DIGEST_LIST + if (!result && duplicated_entry) { + result = ima_add_template_entry(duplicated_entry, violation, op, + inode, filename); + if (result < 0) + kfree(duplicated_entry); + } +#endif return result; }
@@ -152,8 +179,13 @@ void ima_add_violation(struct file *file, const unsigned char *filename, result = -ENOMEM; goto err_out; } +#ifdef CONFIG_IMA_DIGEST_LIST + result = ima_store_template(entry, violation, inode, filename, + CONFIG_IMA_MEASURE_PCR_IDX, NULL); +#else result = ima_store_template(entry, violation, inode, filename, CONFIG_IMA_MEASURE_PCR_IDX); +#endif if (result < 0) ima_free_template_entry(entry); err_out: @@ -341,13 +373,18 @@ 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, +#ifdef CONFIG_IMA_DIGEST_LIST + struct ima_template_desc *template_desc, + struct ima_digest *digest) +#else struct ima_template_desc *template_desc) +#endif { 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 +402,12 @@ void ima_store_measurement(struct integrity_iint_cache *iint, if (iint->measured_pcrs & (0x1 << pcr) && !modsig) return;
+#ifdef CONFIG_IMA_DIGEST_LIST + if (digest && !ima_plus_standard_pcr && ima_digest_list_pcr >= 0) { + result = -EEXIST; + goto out; + } +#endif result = ima_alloc_init_template(&event_data, &entry, template_desc); if (result < 0) { integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, @@ -372,12 +415,22 @@ void ima_store_measurement(struct integrity_iint_cache *iint, return; }
+#ifdef CONFIG_IMA_DIGEST_LIST + result = ima_store_template(entry, violation, inode, filename, pcr, + digest); +out: +#else result = ima_store_template(entry, violation, inode, filename, pcr); +#endif if ((!result || result == -EEXIST) && !(file->f_flags & O_DIRECT)) { iint->flags |= IMA_MEASURED; iint->measured_pcrs |= (0x1 << pcr); } +#ifdef CONFIG_IMA_DIGEST_LIST + if (result < 0 && entry) +#else if (result < 0) +#endif ima_free_template_entry(entry); }
diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index 91c70562aa86d..42b2306e43ccd 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 63979aefc95f7..0a4833daf4da0 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -85,7 +85,11 @@ static int __init ima_add_boot_aggregate(void)
result = ima_store_template(entry, violation, NULL, boot_aggregate_name, +#ifdef CONFIG_IMA_DIGEST_LIST + CONFIG_IMA_MEASURE_PCR_IDX, NULL); +#else CONFIG_IMA_MEASURE_PCR_IDX); +#endif 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 e40975b2cdfdf..8004412249919 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -28,6 +28,9 @@ #include <linux/fs.h>
#include "ima.h" +#ifdef CONFIG_IMA_DIGEST_LIST +#include "ima_digest_list.h" +#endif
#ifdef CONFIG_IMA_APPRAISE int ima_appraise = IMA_APPRAISE_ENFORCE; @@ -40,6 +43,10 @@ int __ro_after_init ima_hash_algo = HASH_ALGO_SHA1; #ifdef CONFIG_IMA_DIGEST_LIST /* 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; #endif static int hash_setup_done;
@@ -182,6 +189,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]; @@ -217,7 +226,7 @@ static int ima_read_xattr(struct dentry *dentry, ret = 0; return ret; } -#endif +#endif /* CONFIG_IMA_DIGEST_LIST */ static void ima_check_last_writer(struct integrity_iint_cache *iint, struct inode *inode, struct file *file) { @@ -276,6 +285,9 @@ 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; +#ifdef CONFIG_IMA_DIGEST_LIST + struct ima_digest *found_digest; +#endif struct evm_ima_xattr_data *xattr_value = NULL; struct modsig *modsig = NULL; int xattr_len = 0; @@ -410,10 +422,20 @@ 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);
+#ifdef CONFIG_IMA_DIGEST_LIST + found_digest = ima_lookup_digest(iint->ima_hash->digest, hash_algo, + COMPACT_FILE); +#endif if (action & IMA_MEASURE) ima_store_measurement(iint, file, pathname, xattr_value, xattr_len, modsig, pcr, +#ifdef CONFIG_IMA_DIGEST_LIST + template_desc, + ima_digest_allow(found_digest, + IMA_MEASURE)); +#else template_desc); +#endif if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) { rc = ima_check_blacklist(iint, modsig, pcr); if (rc != -EPERM) { @@ -1067,7 +1089,11 @@ int process_buffer_measurement(struct mnt_idmap *idmap, goto out; }
+#ifdef CONFIG_IMA_DIGEST_LIST + ret = ima_store_template(entry, violation, NULL, event_data.buf, pcr, NULL); +#else ret = ima_store_template(entry, violation, NULL, event_data.buf, pcr); +#endif 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 9d9290b6313cd..53a035ef069be 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -1872,7 +1872,12 @@ 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); +#ifdef CONFIG_IMA_DIGEST_LIST + if (result || INVALID_PCR(entry->pcr) || + entry->pcr == ima_digest_list_pcr) +#else if (result || INVALID_PCR(entry->pcr)) +#endif result = -EINVAL; else entry->flags |= IMA_PCR; diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 6cbc872dceada..00688b5bddf86 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -84,6 +84,9 @@ enum evm_ima_xattr_type { IMA_XATTR_DIGEST_NG, EVM_XATTR_PORTABLE_DIGSIG, IMA_VERITY_DIGSIG, +#ifdef CONFIG_IMA_DIGEST_LIST + EVM_IMA_XATTR_DIGEST_LIST, +#endif IMA_XATTR_LAST };
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- .../admin-guide/kernel-parameters.txt | 9 ++ security/integrity/ima/ima.h | 10 ++ security/integrity/ima/ima_appraise.c | 106 +++++++++++++++++- security/integrity/ima/ima_main.c | 8 +- security/integrity/integrity.h | 3 + 5 files changed, 134 insertions(+), 2 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 4394c25a0e4d0..eb4718bb9ded0 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1954,6 +1954,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 41879cb5bea32..9ba7bff50c15d 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -358,7 +358,12 @@ 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, +#ifdef CONFIG_IMA_DIGEST_LIST + int xattr_len, const struct modsig *modsig, + struct ima_digest *found_digest); +#else int xattr_len, const struct modsig *modsig); +#endif 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); @@ -384,7 +389,12 @@ static inline int ima_appraise_measurement(enum ima_hooks func, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, int xattr_len, +#ifdef CONFIG_IMA_DIGEST_LIST + const struct modsig *modsig, + struct ima_digest *found_digest) +#else const struct modsig *modsig) +#endif { return INTEGRITY_UNKNOWN; } diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 93d75dad3bb12..9fedc894d46f3 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -18,6 +18,9 @@ #include <uapi/linux/fsverity.h>
#include "ima.h" +#ifdef CONFIG_IMA_DIGEST_LIST +#include "ima_digest_list.h" +#endif
#ifdef CONFIG_IMA_DIGEST_LIST static bool ima_appraise_req_evm __ro_after_init; @@ -63,6 +66,22 @@ void __init ima_appraise_parse_cmdline(void) } #endif
+#ifdef CONFIG_IMA_DIGEST_LIST +static bool ima_appraise_no_metadata __ro_after_init; +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 * @@ -104,6 +123,11 @@ static int ima_fix_xattr(struct dentry *dentry, } else { offset = 0; iint->ima_hash->xattr.ng.type = IMA_XATTR_DIGEST_NG; +#ifdef CONFIG_IMA_DIGEST_LIST + if (test_bit(IMA_DIGEST_LIST, &iint->atomic_flags)) + iint->ima_hash->xattr.ng.type = + EVM_IMA_XATTR_DIGEST_LIST; +#endif iint->ima_hash->xattr.ng.algo = algo; } rc = __vfs_setxattr_noperm(&nop_mnt_idmap, dentry, XATTR_NAME_IMA, @@ -288,20 +312,46 @@ 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, +#ifdef CONFIG_IMA_DIGEST_LIST + enum integrity_status *status, const char **cause, + struct ima_digest *found_digest) +#else enum integrity_status *status, const char **cause) +#endif { struct ima_max_digest_data hash; struct signature_v2_hdr *sig; int rc = -EINVAL, hash_start = 0; int mask;
+#ifdef CONFIG_IMA_DIGEST_LIST + if (found_digest && *status != INTEGRITY_PASS && + *status != INTEGRITY_PASS_IMMUTABLE) + set_bit(IMA_DIGEST_LIST, &iint->atomic_flags); +#endif switch (xattr_value->type) { +#ifdef CONFIG_IMA_DIGEST_LIST + 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; +#endif case IMA_XATTR_DIGEST_NG: /* first byte contains algorithm id */ hash_start = 1; fallthrough; case IMA_XATTR_DIGEST: +#ifdef CONFIG_IMA_DIGEST_LIST + if (*status != INTEGRITY_PASS_IMMUTABLE && + (!found_digest || !ima_digest_is_immutable(found_digest))) { +#else if (*status != INTEGRITY_PASS_IMMUTABLE) { +#endif if (iint->flags & IMA_DIGSIG_REQUIRED) { if (iint->flags & IMA_VERITY_REQUIRED) *cause = "verity-signature-required"; @@ -489,14 +539,25 @@ 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, +#ifdef CONFIG_IMA_DIGEST_LIST + int xattr_len, const struct modsig *modsig, + struct ima_digest *found_digest) +#else int xattr_len, const struct modsig *modsig) + +#endif { 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; +#ifdef CONFIG_IMA_DIGEST_LIST + int rc = xattr_len, rc_evm; + char _buf[sizeof(struct evm_ima_xattr_data) + 1 + SHA512_DIGEST_SIZE]; +#else int rc = xattr_len; +#endif bool try_modsig = iint->flags & IMA_MODSIG_ALLOWED && modsig;
/* If not appraising a modsig, we need an xattr. */ @@ -507,6 +568,26 @@ int ima_appraise_measurement(enum ima_hooks func, if (xattr_value && xattr_value->type == EVM_IMA_XATTR_DIGSIG && 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; + } + } + #endif /* If reading the xattr failed and there's no modsig, error out. */ if (rc <= 0 && !try_modsig) { @@ -543,7 +624,7 @@ int ima_appraise_measurement(enum ima_hooks func, case INTEGRITY_UNKNOWN: #ifdef CONFIG_IMA_DIGEST_LIST if (ima_appraise_req_evm && - xattr_value->type != EVM_IMA_XATTR_DIGSIG) + xattr_value->type != EVM_IMA_XATTR_DIGSIG && !found_digest) goto out; #endif break; @@ -553,6 +634,25 @@ int ima_appraise_measurement(enum ima_hooks func, break; fallthrough; case INTEGRITY_NOLABEL: /* No security.evm xattr. */ +#ifdef CONFIG_IMA_DIGEST_LIST + /* + * 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; + } +#endif cause = "missing-HMAC"; goto out; case INTEGRITY_FAIL_IMMUTABLE: @@ -568,7 +668,11 @@ int ima_appraise_measurement(enum ima_hooks func,
if (xattr_value) rc = xattr_verify(func, iint, xattr_value, xattr_len, &status, +#ifdef CONFIG_IMA_DIGEST_LIST + &cause, found_digest); +#else &cause); +#endif
/* * 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 8004412249919..d431fb7ecbe20 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -441,8 +441,14 @@ 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, + pathname, xattr_value, +#ifdef CONFIG_IMA_DIGEST_LIST + xattr_len, modsig, + ima_digest_allow(found_digest, + IMA_APPRAISE)); +#else xattr_len, modsig); +#endif inode_unlock(inode); } if (!rc) diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 00688b5bddf86..65d776e2e6ccb 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -76,6 +76,9 @@ #define IMA_CHANGE_ATTR 2 #define IMA_DIGSIG 3 #define IMA_MUST_MEASURE 4 +#ifdef CONFIG_IMA_DIGEST_LIST +#define IMA_DIGEST_LIST 5 +#endif
enum evm_ima_xattr_type { IMA_XATTR_DIGEST = 0x01,
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- security/integrity/evm/evm_crypto.c | 15 +++++ security/integrity/evm/evm_main.c | 99 ++++++++++++++++++++++++++++- 2 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 033804f5a5f20..f3835b47774fc 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -154,7 +154,12 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, /* Don't include the inode or generation number in portable * signatures */ +#ifdef CONFIG_IMA_DIGEST_LIST + if (type != EVM_XATTR_PORTABLE_DIGSIG && + type != EVM_IMA_XATTR_DIGEST_LIST) { +#else if (type != EVM_XATTR_PORTABLE_DIGSIG) { +#endif hmac_misc.ino = inode->i_ino; hmac_misc.generation = inode->i_generation; } @@ -171,7 +176,12 @@ 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) && +#ifdef CONFIG_IMA_DIGEST_LIST + type != EVM_XATTR_PORTABLE_DIGSIG && + type != EVM_IMA_XATTR_DIGEST_LIST) +#else type != EVM_XATTR_PORTABLE_DIGSIG) +#endif crypto_shash_update(desc, (u8 *)&inode->i_sb->s_uuid, UUID_SIZE); crypto_shash_final(desc, digest);
@@ -337,7 +347,12 @@ static int evm_is_immutable(struct dentry *dentry, struct inode *inode) rc = 0; goto out; } +#ifdef CONFIG_IMA_DIGEST_LIST + if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG || + xattr_data->type == EVM_IMA_XATTR_DIGEST_LIST) +#else if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) +#endif rc = 1; else rc = 0; diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index cf24c5255583c..47ac6e9e796b0 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -128,7 +128,11 @@ static bool evm_hmac_disabled(void) return true; }
+#ifdef CONFIG_IMA_DIGEST_LIST +static int evm_find_protected_xattrs(struct dentry *dentry, int *ima_present) +#else static int evm_find_protected_xattrs(struct dentry *dentry) +#endif { struct inode *inode = d_backing_inode(dentry); struct xattr_list *xattr; @@ -145,6 +149,10 @@ static int evm_find_protected_xattrs(struct dentry *dentry) continue; return error; } +#ifdef CONFIG_IMA_DIGEST_LIST + if (!strcmp(xattr->name, XATTR_NAME_IMA)) + *ima_present = 1; +#endif count++; }
@@ -173,9 +181,20 @@ 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; +#ifdef CONFIG_IMA_DIGEST_LIST + enum integrity_status saved_evm_status = INTEGRITY_UNKNOWN; + struct evm_digest digest; + struct ima_digest *found_digest; + struct inode *inode; + 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; +#else struct evm_digest digest; struct inode *inode; int rc, xattr_len, evm_immutable = 0; +#endif
if (iint && (iint->evm_status == INTEGRITY_PASS || iint->evm_status == INTEGRITY_PASS_IMMUTABLE)) @@ -189,7 +208,11 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, if (rc <= 0) { evm_status = INTEGRITY_FAIL; if (rc == -ENODATA) { +#ifdef CONFIG_IMA_DIGEST_LIST + rc = evm_find_protected_xattrs(dentry, &ima_present); +#else rc = evm_find_protected_xattrs(dentry); +#endif if (rc > 0) evm_status = INTEGRITY_NOLABEL; else if (rc == 0) @@ -197,7 +220,23 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, } else if (rc == -EOPNOTSUPP) { evm_status = INTEGRITY_UNKNOWN; } - goto out; +#ifdef CONFIG_IMA_DIGEST_LIST + /* 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) +#endif + goto out; +#ifdef CONFIG_IMA_DIGEST_LIST + saved_evm_status = evm_status; +#endif }
xattr_len = rc; @@ -255,12 +294,60 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, } } break; +#ifdef CONFIG_IMA_DIGEST_LIST + 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; +#endif /* CONFIG_IMA_DIGEST_LIST */ default: rc = -EINVAL; break; }
+#ifdef CONFIG_IMA_DIGEST_LIST + if (rc && xattr_data == (struct evm_ima_xattr_data *)&evm_fake_xattr) { + evm_status = saved_evm_status; + } else if (rc) { +#else if (rc) { +#endif if (rc == -ENODATA) evm_status = INTEGRITY_NOXATTRS; else if (evm_immutable) @@ -273,7 +360,10 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, out: if (iint) iint->evm_status = evm_status; - kfree(xattr_data); +#ifdef CONFIG_IMA_DIGEST_LIST + if (xattr_data != (struct evm_ima_xattr_data *)&evm_fake_xattr) +#endif + kfree(xattr_data); return evm_status; }
@@ -581,7 +671,12 @@ 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 && +#ifdef CONFIG_IMA_DIGEST_LIST + xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG && + xattr_data->type != EVM_IMA_XATTR_DIGEST_LIST) +#else xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG) +#endif return -EPERM; } return evm_protect_xattr(idmap, dentry, xattr_name, xattr_value,
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- security/integrity/ima/ima_appraise.c | 10 ++++++++++ security/integrity/ima/ima_policy.c | 25 +++++++++++++++++++++++++ security/integrity/integrity.h | 3 +++ 3 files changed, 38 insertions(+)
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 9fedc894d46f3..baee984612bd4 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -665,6 +665,16 @@ int ima_appraise_measurement(enum ima_hooks func, default: WARN_ONCE(true, "Unexpected integrity status %d\n", status); } +#ifdef CONFIG_IMA_DIGEST_LIST + 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; + } +#endif
if (xattr_value) rc = xattr_verify(func, iint, xattr_value, xattr_len, &status, diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 53a035ef069be..4cabbe7f9c511 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -1257,7 +1257,12 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
if (entry->action != APPRAISE && entry->flags & (IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED | +#ifdef CONFIG_IMA_DIGEST_LIST + IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS | + IMA_META_IMMUTABLE_REQUIRED)) +#else IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS)) +#endif return false;
/* @@ -1293,7 +1298,11 @@ 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 | +#ifdef CONFIG_IMA_DIGEST_LIST + IMA_VERITY_REQUIRED | IMA_META_IMMUTABLE_REQUIRED)) +#else IMA_VERITY_REQUIRED)) +#endif return false;
break; @@ -1306,7 +1315,12 @@ 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 | +#ifdef CONFIG_IMA_DIGEST_LIST + IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS | + IMA_META_IMMUTABLE_REQUIRED)) +#else IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS)) +#endif return false;
break; @@ -1834,9 +1848,16 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) else entry->flags |= IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED; +#ifdef CONFIG_IMA_DIGEST_LIST + } else if (strcmp(args[0].from, "meta_immutable") == 0) + entry->flags |= IMA_META_IMMUTABLE_REQUIRED; + else + result = -EINVAL; +#else } else { result = -EINVAL; } +#endif break; case Opt_appraise_flag: ima_log_string(ab, "appraise_flag", args[0].from); @@ -2295,6 +2316,10 @@ 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 "); +#ifdef CONFIG_IMA_DIGEST_LIST + if (entry->flags & IMA_META_IMMUTABLE_REQUIRED) + seq_puts(m, "appraise_type=meta_immutable "); +#endif 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 65d776e2e6ccb..54f75738ff636 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -45,6 +45,9 @@ #define IMA_MODSIG_ALLOWED 0x20000000 #define IMA_CHECK_BLACKLIST 0x40000000 #define IMA_VERITY_REQUIRED 0x80000000 +#ifdef CONFIG_IMA_DIGEST_LIST +#define IMA_META_IMMUTABLE_REQUIRED 0x100000000 +#endif
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \ IMA_HASH | IMA_APPRAISE_SUBMASK)
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- .../admin-guide/kernel-parameters.txt | 5 +++ security/integrity/ima/ima_policy.c | 33 +++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index eb4718bb9ded0..27406e4213046 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1998,6 +1998,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 4cabbe7f9c511..7fbf4fcc25eca 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -62,7 +62,11 @@ enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE };
+#ifdef CONFIG_IMA_DIGEST_LIST +enum policy_types { ORIGINAL_TCB = 1, DEFAULT_TCB, EXEC_TCB }; +#else enum policy_types { ORIGINAL_TCB = 1, DEFAULT_TCB }; +#endif
enum policy_rule_list { IMA_DEFAULT_POLICY = 1, IMA_CUSTOM_POLICY };
@@ -293,6 +297,10 @@ static int __init policy_setup(char *str) continue; if ((strcmp(p, "tcb") == 0) && !ima_policy) ima_policy = DEFAULT_TCB; +#ifdef CONFIG_IMA_DIGEST_LIST + else if ((strcmp(p, "exec_tcb") == 0) && !ima_policy) + ima_policy = EXEC_TCB; +#endif else if (strcmp(p, "appraise_tcb") == 0) ima_use_appraise_tcb = true; else if (strcmp(p, "secure_boot") == 0) @@ -869,14 +877,31 @@ static int ima_appraise_flag(enum ima_hooks func) return 0; }
-static void add_rules(struct ima_rule_entry *entries, int count, +#ifdef CONFIG_IMA_DIGEST_LIST +static void __init add_rules(struct ima_rule_entry *entries, int count, enum policy_rule_list policy_rule) +#else +static void add_rules(struct ima_rule_entry *entries, int count, + enum policy_rule_list policy_rule) +#endif { int i = 0;
for (i = 0; i < count; i++) { struct ima_rule_entry *entry; - +#ifdef CONFIG_IMA_DIGEST_LIST + 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; + } +#endif if (policy_rule & IMA_DEFAULT_POLICY) list_add_tail(&entries[i].list, &ima_default_rules);
@@ -962,6 +987,10 @@ void __init ima_init_policy(void) ARRAY_SIZE(original_measurement_rules), IMA_DEFAULT_POLICY); break; +#ifdef CONFIG_IMA_DIGEST_LIST + case EXEC_TCB: + fallthrough; +#endif case DEFAULT_TCB: add_rules(default_measurement_rules, ARRAY_SIZE(default_measurement_rules),
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- .../admin-guide/kernel-parameters.txt | 5 +++ security/integrity/ima/ima_policy.c | 38 +++++++++++++++++++ 2 files changed, 43 insertions(+)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 27406e4213046..5d4f298daa25f 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -2010,6 +2010,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 7fbf4fcc25eca..73a96427bfa2a 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -226,6 +226,14 @@ static struct ima_rule_entry default_appraise_rules[] __ro_after_init = { #endif };
+#ifdef CONFIG_IMA_DIGEST_LIST +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}, +}; +#endif static struct ima_rule_entry build_appraise_rules[] __ro_after_init = { #ifdef CONFIG_IMA_APPRAISE_REQUIRE_MODULE_SIGS {.action = APPRAISE, .func = MODULE_CHECK, @@ -285,6 +293,9 @@ static int __init default_measure_policy_setup(char *str) __setup("ima_tcb", default_measure_policy_setup);
static bool ima_use_appraise_tcb __initdata; +#ifdef CONFIG_IMA_DIGEST_LIST +static bool ima_use_appraise_exec_tcb __initdata; +#endif 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 +314,10 @@ static int __init policy_setup(char *str) #endif else if (strcmp(p, "appraise_tcb") == 0) ima_use_appraise_tcb = true; +#ifdef CONFIG_IMA_DIGEST_LIST + else if (strcmp(p, "appraise_exec_tcb") == 0) + ima_use_appraise_exec_tcb = true; +#endif else if (strcmp(p, "secure_boot") == 0) ima_use_secure_boot = true; else if (strcmp(p, "critical_data") == 0) @@ -901,6 +916,15 @@ static void add_rules(struct ima_rule_entry *entries, int count, entries[i].func == FILE_CHECK) 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; + } + } #endif if (policy_rule & IMA_DEFAULT_POLICY) list_add_tail(&entries[i].list, &ima_default_rules); @@ -1017,7 +1041,11 @@ void __init ima_init_policy(void) * Insert the builtin "secure_boot" policy rules requiring file * signatures, prior to other appraise rules. */ +#ifdef CONFIG_IMA_DIGEST_LIST + if (ima_use_secure_boot || ima_use_appraise_exec_tcb) +#else if (ima_use_secure_boot) +#endif add_rules(secure_boot_rules, ARRAY_SIZE(secure_boot_rules), IMA_DEFAULT_POLICY);
@@ -1037,11 +1065,21 @@ void __init ima_init_policy(void) IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY); }
+#ifdef CONFIG_IMA_DIGEST_LIST + if (ima_use_appraise_tcb || ima_use_appraise_exec_tcb) +#else if (ima_use_appraise_tcb) +#endif add_rules(default_appraise_rules, ARRAY_SIZE(default_appraise_rules), IMA_DEFAULT_POLICY);
+#ifdef CONFIG_IMA_DIGEST_LIST + if (ima_use_appraise_exec_tcb) + add_rules(appraise_exec_rules, + ARRAY_SIZE(appraise_exec_rules), + IMA_DEFAULT_POLICY); +#endif if (ima_use_critical_data) add_rules(critical_data_rules, ARRAY_SIZE(critical_data_rules),
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@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 5d4f298daa25f..0ac4ed2b8d535 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -2015,6 +2015,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 73a96427bfa2a..75a3699ed2523 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -295,6 +295,7 @@ __setup("ima_tcb", default_measure_policy_setup); static bool ima_use_appraise_tcb __initdata; #ifdef CONFIG_IMA_DIGEST_LIST static bool ima_use_appraise_exec_tcb __initdata; +static bool ima_use_appraise_exec_immutable __initdata; #endif static bool ima_use_secure_boot __initdata; static bool ima_use_critical_data __initdata; @@ -317,6 +318,8 @@ static int __init policy_setup(char *str) #ifdef CONFIG_IMA_DIGEST_LIST 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; #endif else if (strcmp(p, "secure_boot") == 0) ima_use_secure_boot = true; @@ -925,6 +928,13 @@ static void 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; + #endif if (policy_rule & IMA_DEFAULT_POLICY) list_add_tail(&entries[i].list, &ima_default_rules);
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@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 0000000000000..15f30deab3282 --- /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
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- include/linux/mpi.h | 4 ++++ lib/mpi/mpicoder.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+)
diff --git a/include/linux/mpi.h b/include/linux/mpi.h index eb0d1c1db208e..ceddec8b5d090 100644 --- a/include/linux/mpi.h +++ b/include/linux/mpi.h @@ -90,6 +90,10 @@ enum gcry_mpi_format { };
MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes); +#ifdef CONFIG_PGP_LIBRARY +int mpi_key_length(const void *xbuffer, unsigned int ret_nread, + unsigned int *nbits_arg, unsigned int *nbytes_arg); +#endif 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 3cb6bd148fa9e..30ffe0c4397b4 100644 --- a/lib/mpi/mpicoder.c +++ b/lib/mpi/mpicoder.c @@ -79,6 +79,42 @@ MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes) } EXPORT_SYMBOL_GPL(mpi_read_raw_data);
+#ifdef CONFIG_PGP_LIBRARY +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; + + 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 -EINVAL; + } + + 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); +#else MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread) { const uint8_t *buffer = xbuffer; @@ -95,6 +131,7 @@ MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread) }
nbytes = DIV_ROUND_UP(nbits, 8); +#endif /* CONFIG_PGP_LIBRARY */ 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
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- crypto/rsa.c | 16 ++++++++ crypto/rsa_helper.c | 76 +++++++++++++++++++++++++++++++++++ include/crypto/internal/rsa.h | 10 +++++ 3 files changed, 102 insertions(+)
diff --git a/crypto/rsa.c b/crypto/rsa.c index c50f2d2a4d064..7df7f16a987cb 100644 --- a/crypto/rsa.c +++ b/crypto/rsa.c @@ -216,8 +216,16 @@ 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); +#ifdef CONFIG_PGP_LIBRARY + if (ret) { + ret = rsa_parse_pub_key_raw(&raw_key, key, keylen); + if (ret) + return ret; + } +#else if (ret) return ret; +#endif
mpi_key->e = mpi_read_raw_data(raw_key.e, raw_key.e_sz); if (!mpi_key->e) @@ -250,8 +258,16 @@ 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); +#ifdef CONFIG_PGP_LIBRARY + if (ret) { + ret = rsa_parse_priv_key_raw(&raw_key, key, keylen); + if (ret) + return ret; + } +#else if (ret) return ret; +#endif
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 94266f29049c9..191fee7568220 100644 --- a/crypto/rsa_helper.c +++ b/crypto/rsa_helper.c @@ -9,6 +9,9 @@ #include <linux/export.h> #include <linux/err.h> #include <linux/fips.h> +#ifdef CONFIG_PGP_LIBRARY +#include <linux/mpi.h> +#endif #include <crypto/internal/rsa.h> #include "rsapubkey.asn1.h" #include "rsaprivkey.asn1.h" @@ -148,6 +151,34 @@ int rsa_get_qinv(void *context, size_t hdrlen, unsigned char tag, return 0; }
+#ifdef CONFIG_PGP_LIBRARY +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; +} +#endif + /** * 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 +197,28 @@ int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key, } EXPORT_SYMBOL_GPL(rsa_parse_pub_key);
+#ifdef CONFIG_PGP_LIBRARY +/** + * 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); +#endif /** * rsa_parse_priv_key() - decodes the BER encoded buffer and stores in the * provided struct rsa_key, pointers to the raw key @@ -184,3 +237,26 @@ 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); + +#ifdef CONFIG_PGP_LIBRARY +/** + * 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); +#endif \ No newline at end of file diff --git a/include/crypto/internal/rsa.h b/include/crypto/internal/rsa.h index e870133f4b775..fef1d72cd8370 100644 --- a/include/crypto/internal/rsa.h +++ b/include/crypto/internal/rsa.h @@ -50,8 +50,18 @@ struct rsa_key { int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key, unsigned int key_len);
+#ifdef CONFIG_PGP_LIBRARY +int rsa_parse_pub_key_raw(struct rsa_key *rsa_key, const void *key, + unsigned int key_len); +#endif + int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key, unsigned int key_len);
+#ifdef CONFIG_PGP_LIBRARY +int rsa_parse_priv_key_raw(struct rsa_key *rsa_key, const void *key, + unsigned int key_len); +#endif + extern struct crypto_template rsa_pkcs1pad_tmpl; #endif
From: David Howells dhowells@redhat.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- include/linux/pgp.h | 219 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 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 0000000000000..40bd32c4d0eb7 --- /dev/null +++ b/include/linux/pgp.h @@ -0,0 +1,219 @@ +/* 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 + +#ifdef CONFIG_PGP_LIBRARY + +#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 /* CONFIG_PGP_LIBRARY */ +#endif /* _LINUX_PGP_H */
From: David Howells dhowells@redhat.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- crypto/asymmetric_keys/Kconfig | 6 + crypto/asymmetric_keys/Makefile | 5 + crypto/asymmetric_keys/pgp_library.c | 284 +++++++++++++++++++++++++++ crypto/asymmetric_keys/pgp_parser.h | 27 +++ include/linux/pgplib.h | 48 +++++ 5 files changed, 370 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 1ef3b46d6f6e5..00791af8de8ee 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 0d1fa1b692c6b..5417575ac3bc0 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 0000000000000..cfe44f45eef5c --- /dev/null +++ b/crypto/asymmetric_keys/pgp_library.c @@ -0,0 +1,284 @@ +// 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. + */ +#ifdef CONFIG_PGP_LIBRARY + +#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); + +#endif /* #CONFIG_PGP_LIBRARY */ \ No newline at end of file diff --git a/crypto/asymmetric_keys/pgp_parser.h b/crypto/asymmetric_keys/pgp_parser.h new file mode 100644 index 0000000000000..5e10e9b0ef3f0 --- /dev/null +++ b/crypto/asymmetric_keys/pgp_parser.h @@ -0,0 +1,27 @@ +/* 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. + */ + +#ifdef CONFIG_PGP_LIBRARY + +#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]; + +#endif /* CONFIG_PGP_LIBRARY */ \ No newline at end of file diff --git a/include/linux/pgplib.h b/include/linux/pgplib.h new file mode 100644 index 0000000000000..a4068bcef8c5f --- /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
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- crypto/asymmetric_keys/Kconfig | 11 + crypto/asymmetric_keys/Makefile | 4 + crypto/asymmetric_keys/pgp_public_key.c | 343 ++++++++++++++++++++++++ 3 files changed, 358 insertions(+) create mode 100644 crypto/asymmetric_keys/pgp_public_key.c
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 00791af8de8ee..62d163fb69512 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 5417575ac3bc0..75f3c3022dd9d 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 0000000000000..19a7a4ea03c3b --- /dev/null +++ b/crypto/asymmetric_keys/pgp_public_key.c @@ -0,0 +1,343 @@ +// 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. + */ + +#ifdef CONFIG_PGP_KEY_PARSER + +#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); + +#endif /* CONFIG_PGP_KEY_PARSER */ \ No newline at end of file
From: David Howells dhowells@redhat.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@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 19a7a4ea03c3b..c1dd1a9c1c18a 100644 --- a/crypto/asymmetric_keys/pgp_public_key.c +++ b/crypto/asymmetric_keys/pgp_public_key.c @@ -38,6 +38,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; };
/* @@ -158,6 +161,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; @@ -202,6 +206,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; @@ -294,13 +305,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: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- certs/Kconfig | 7 +++++++ certs/Makefile | 7 +++++++ certs/system_certificates.S | 18 ++++++++++++++++++ certs/system_keyring.c | 23 +++++++++++++++++++++++ 4 files changed, 55 insertions(+)
diff --git a/certs/Kconfig b/certs/Kconfig index 1f109b0708778..3038adbaf58d1 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 799ad7b9e68a0..ab6da6f4e9530 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 003e25d4a17e2..b3cbf0811e3f9 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 a7a49b17ceb1f..0ade1cf800f74 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -11,6 +11,9 @@ #include <linux/cred.h> #include <linux/err.h> #include <linux/slab.h> +#ifdef CONFIG_PGP_PRELOAD_PUBLIC_KEYS +#include <linux/pgp.h> +#endif #include <linux/uidgid.h> #include <linux/verification.h> #include <keys/asymmetric-type.h> @@ -215,6 +218,26 @@ static __init int load_system_certificate_list(void) return x509_load_certificate_list(p, size, builtin_trusted_keys); } 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
From: David Howells dhowells@redhat.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- crypto/asymmetric_keys/Kconfig | 8 ++ crypto/asymmetric_keys/Makefile | 1 + crypto/asymmetric_keys/pgp_preload.c | 123 +++++++++++++++++++++++++++ include/linux/pgp.h | 4 + 4 files changed, 136 insertions(+) create mode 100644 crypto/asymmetric_keys/pgp_preload.c
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 62d163fb69512..9b55ae747eafb 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 75f3c3022dd9d..fd0b280892b69 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 0000000000000..953694e183f72 --- /dev/null +++ b/crypto/asymmetric_keys/pgp_preload.c @@ -0,0 +1,123 @@ +// 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 + */ + +#ifdef CONFIG_PGP_PRELOAD + +#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; +} + +#endif /* CONFIG_PGP_PRELOAD */ \ No newline at end of file diff --git a/include/linux/pgp.h b/include/linux/pgp.h index 40bd32c4d0eb7..94cf6e597252d 100644 --- a/include/linux/pgp.h +++ b/include/linux/pgp.h @@ -16,6 +16,7 @@ #ifdef CONFIG_PGP_LIBRARY
#include <linux/types.h> +#include <linux/key.h>
struct pgp_key_ID { u8 id[8]; @@ -215,5 +216,8 @@ 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 /* CONFIG_PGP_LIBRARY */ #endif /* _LINUX_PGP_H */
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- certs/system_keyring.c | 23 +++++++++++++++++++++++ include/linux/verification.h | 7 +++++++ 2 files changed, 30 insertions(+)
diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 0ade1cf800f74..f2581da2b68c7 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -362,6 +362,29 @@ int verify_pkcs7_signature(const void *data, size_t len, } EXPORT_SYMBOL_GPL(verify_pkcs7_signature);
+#ifdef CONFIG_IMA_DIGEST_LIST +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_IMA_DIGEST_LIST */ #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
#ifdef CONFIG_INTEGRITY_PLATFORM_KEYRING diff --git a/include/linux/verification.h b/include/linux/verification.h index f34e50ebcf60a..b01f813f45a8f 100644 --- a/include/linux/verification.h +++ b/include/linux/verification.h @@ -9,6 +9,9 @@ #define _LINUX_VERIFICATION_H
#include <linux/types.h> +#ifdef CONFIG_IMA_DIGEST_LIST +#include <linux/key.h> +#endif
/* * Indicate that both builtin trusted keys and secondary trusted keys @@ -68,5 +71,9 @@ extern int verify_pefile_signature(const void *pebuf, unsigned pelen, enum key_being_used_for usage); #endif
+#ifdef CONFIG_IMA_DIGEST_LIST +struct key *search_trusted_key(struct key *trusted_keys, struct key_type *type, + char *name); +#endif /* CONFIG_IMA_DIGEST_LIST */ #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */ #endif /* _LINUX_VERIFY_PEFILE_H */
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- security/integrity/digsig_asymmetric.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index 895f4b9ce8c6b..b0ccb20cd284e 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -9,6 +9,9 @@ #include <linux/err.h> #include <linux/ratelimit.h> #include <linux/key-type.h> +#ifdef CONFIG_IMA_DIGEST_LIST +#include <linux/verification.h> +#endif #include <crypto/public_key.h> #include <crypto/hash_info.h> #include <keys/asymmetric-type.h> @@ -54,6 +57,16 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) key = request_key(&key_type_asymmetric, name, NULL); }
+#ifdef CONFIG_IMA_DIGEST_LIST + 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); + } +#endif /* CONFIG_IMA_DIGEST_LIST */ 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
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@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 | 25 ++++++++++- security/integrity/ima/ima_main.c | 12 +++++ 5 files changed, 111 insertions(+), 2 deletions(-)
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index baee984612bd4..7ba6df886e334 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -645,7 +645,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 42b2306e43ccd..b5d1004e3040c 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 5bd2388ff95ee..4a8b0e000ad3e 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 7359f44b5a292..2b82913519c4e 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -467,6 +467,20 @@ static ssize_t ima_write_policy(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 result = ima_read_policy(data); } else if (ima_appraise & IMA_APPRAISE_POLICY) { @@ -475,7 +489,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, "policy_update", "signed policy required", 1, 0); result = -EACCES; -#endif +#endif /* CONFIG_IMA_DIGEST_LIST */ } else { #ifdef CONFIG_IMA_DIGEST_LIST pr_err("Unknown data type\n"); @@ -596,6 +610,12 @@ static int ima_open_policy(struct inode *inode, struct file *filp) if (test_and_set_bit(IMA_FS_BUSY, &ima_fs_flags)) #endif return -EBUSY; + +#ifdef CONFIG_IMA_DIGEST_LIST + if (dentry == digest_list_data || dentry == digest_list_data_del) + if (ima_check_current_is_parser()) + ima_set_parser(); +#endif return 0; }
@@ -628,6 +648,9 @@ static int ima_release_policy(struct inode *inode, struct file *file) if ((file->f_flags & O_ACCMODE) == O_RDONLY) return seq_release(inode, file); #ifdef CONFIG_IMA_DIGEST_LIST + 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 d431fb7ecbe20..45c8716ec0564 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -627,11 +627,23 @@ int ima_bprm_check(struct linux_binprm *bprm) int ima_file_check(struct file *file, int mask) { u32 secid; +#ifdef CONFIG_IMA_DIGEST_LIST + int rc; +#endif
security_current_getsecid_subj(&secid); +#ifdef CONFIG_IMA_DIGEST_LIST + 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; +#else return process_measurement(file, current_cred(), secid, NULL, 0, mask & (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND), FILE_CHECK); +#endif } EXPORT_SYMBOL_GPL(ima_file_check);
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- security/integrity/ima/ima_policy.c | 52 ++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-)
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 75a3699ed2523..81a727a3fd8f3 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -20,6 +20,9 @@ #include <linux/ima.h>
#include "ima.h" +#ifdef CONFIG_IMA_DIGEST_LIST +#include "ima_digest_list.h" +#endif
/* flags definitions */ #define IMA_FUNC 0x0001 @@ -34,6 +37,9 @@ #define IMA_FSNAME 0x0200 #define IMA_KEYRINGS 0x0400 #define IMA_LABEL 0x0800 +#ifdef CONFIG_IMA_DIGEST_LIST +#define IMA_PARSER 0x10000 +#endif #define IMA_VALIDATE_ALGOS 0x1000 #define IMA_GID 0x2000 #define IMA_EGID 0x4000 @@ -197,6 +203,12 @@ static struct ima_rule_entry default_measurement_rules[] __ro_after_init = { #endif };
+#ifdef CONFIG_IMA_DIGEST_LIST +static struct ima_rule_entry ima_parser_measure_rule __ro_after_init = { + .action = MEASURE, .flags = IMA_PARSER +}; +#endif + 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}, @@ -268,6 +280,13 @@ static struct ima_rule_entry secure_boot_rules[] __ro_after_init = { #endif };
+#ifdef CONFIG_IMA_DIGEST_LIST +static struct ima_rule_entry ima_parser_appraise_rule __ro_after_init = { + .action = APPRAISE, + .flags = IMA_PARSER | IMA_DIGSIG_REQUIRED +}; +#endif + static struct ima_rule_entry critical_data_rules[] __ro_after_init = { {.action = MEASURE, .func = CRITICAL_DATA, .flags = IMA_FUNC}, }; @@ -660,6 +679,11 @@ static bool ima_match_rules(struct ima_rule_entry *rule, !rule->fowner_op(i_uid_into_vfsuid(idmap, inode), rule->fowner)) return false; +#ifdef CONFIG_IMA_DIGEST_LIST + if ((rule->flags & IMA_PARSER) && + !ima_current_is_parser()) + return false; +#endif if ((rule->flags & IMA_FGROUP) && !rule->fgroup_op(i_gid_into_vfsgid(idmap, inode), rule->fgroup)) @@ -1034,6 +1058,10 @@ void __init ima_init_policy(void) break; }
+#ifdef CONFIG_IMA_DIGEST_LIST + if (ima_policy) + add_rules(&ima_parser_measure_rule, 1, IMA_DEFAULT_POLICY); +#endif /* * Based on runtime secure boot flags, insert arch specific measurement * and appraise rules requiring file signatures for both the initial @@ -1097,6 +1125,11 @@ void __init ima_init_policy(void)
atomic_set(&ima_setxattr_allowed_hash_algorithms, 0);
+#ifdef CONFIG_IMA_DIGEST_LIST + if (ima_use_secure_boot || ima_use_appraise_tcb || + ima_use_appraise_exec_tcb) + add_rules(&ima_parser_appraise_rule, 1, IMA_DEFAULT_POLICY); +#endif ima_update_policy_flags(); }
@@ -1160,7 +1193,11 @@ enum policy_opt { Opt_digest_type, Opt_appraise_type, Opt_appraise_flag, Opt_appraise_algos, Opt_permit_directio, Opt_pcr, Opt_template, Opt_keyrings, +#ifdef CONFIG_IMA_DIGEST_LIST + Opt_label, Opt_parser, Opt_err +#else Opt_label, Opt_err +#endif };
static const match_table_t policy_tokens = { @@ -1209,6 +1246,9 @@ static const match_table_t policy_tokens = { {Opt_template, "template=%s"}, {Opt_keyrings, "keyrings=%s"}, {Opt_label, "label=%s"}, +#ifdef CONFIG_IMA_DIGEST_LIST + {Opt_parser, "parser"}, +#endif {Opt_err, NULL} };
@@ -1336,7 +1376,7 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) entry->flags & (IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED | #ifdef CONFIG_IMA_DIGEST_LIST IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS | - IMA_META_IMMUTABLE_REQUIRED)) + IMA_META_IMMUTABLE_REQUIRED | IMA_PARSER)) #else IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS)) #endif @@ -2003,6 +2043,12 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) &(template_desc->num_fields)); entry->template = template_desc; break; +#ifdef CONFIG_IMA_DIGEST_LIST + case Opt_parser: + audit_log_format(ab, "parser "); + entry->flags |= IMA_PARSER; + break; +#endif case Opt_err: ima_log_string(ab, "UNKNOWN", p); result = -EINVAL; @@ -2348,6 +2394,10 @@ int ima_policy_show(struct seq_file *m, void *v) seq_puts(m, " "); }
+#ifdef CONFIG_IMA_DIGEST_LIST + if (entry->flags & IMA_PARSER) + seq_puts(m, "parser "); +#endif for (i = 0; i < MAX_LSM_RULES; i++) { if (entry->lsm[i].rule) { switch (i) {
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- Documentation/admin-guide/kernel-parameters.txt | 10 +++++++--- security/integrity/evm/evm_main.c | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 0ac4ed2b8d535..84fe3bc6c1481 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 47ac6e9e796b0..40485862362f1 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -73,16 +73,32 @@ static struct xattr_list evm_config_default_xattrnames[] = { LIST_HEAD(evm_config_xattrnames);
static int evm_fixmode __ro_after_init; +#ifdef CONFIG_IMA_DIGEST_LIST +static int __init evm_set_param(char *str) +#else static int __init evm_set_fixmode(char *str) +#endif { if (strncmp(str, "fix", 3) == 0) evm_fixmode = 1; +#ifdef CONFIG_IMA_DIGEST_LIST + 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; +#endif else pr_err("invalid "%s" mode", str);
return 1; } +#ifdef CONFIG_IMA_DIGEST_LIST +__setup("evm=", evm_set_param); +#else __setup("evm=", evm_set_fixmode); +#endif
static void __init evm_init_config(void) {
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
---------------------------
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: zhoushuiqing zhoushuiqing2@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 73c582d97737c..aa6a06f3e8b12 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 b5d1004e3040c..0712023d67610 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
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- security/integrity/evm/Kconfig | 32 +++++++++++++++++++++++++++++ security/integrity/evm/evm.h | 3 +++ security/integrity/evm/evm_crypto.c | 31 +++++++++++++++++++++++++++- security/integrity/evm/evm_main.c | 26 +++++++++++++++++++++++ security/integrity/integrity.h | 4 ++++ 5 files changed, 95 insertions(+), 1 deletion(-)
diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig index a6e19d23e7006..844f2686cce75 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 f8b8c5004fc7c..48bfc7d1899f7 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h @@ -33,6 +33,9 @@ struct xattr_list { };
extern int evm_initialized; +#ifdef CONFIG_IMA_DIGEST_LIST +extern enum hash_algo evm_hash_algo; +#endif
#define EVM_ATTR_FSUUID 0x0001
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index f3835b47774fc..3831d9dc4681d 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -35,7 +35,11 @@ static DEFINE_MUTEX(mutex);
static unsigned long evm_set_key_flags;
+#ifdef CONFIG_IMA_DIGEST_LIST +enum hash_algo evm_hash_algo __ro_after_init = HASH_ALGO_SHA1; +#else static const char evm_hmac[] = "hmac(sha1)"; +#endif
/** * evm_set_key() - set EVM HMAC key from the kernel @@ -76,7 +80,14 @@ static struct shash_desc *init_desc(char type, uint8_t hash_algo) long rc; const char *algo; struct crypto_shash **tfm, *tmp_tfm; +#ifdef CONFIG_IMA_DIGEST_LIST + char evm_hmac[CRYPTO_MAX_ALG_NAME]; +#endif struct shash_desc *desc; +#ifdef CONFIG_IMA_DIGEST_LIST + snprintf(evm_hmac, sizeof(evm_hmac), "hmac(%s)", + CONFIG_EVM_DEFAULT_HASH); +#endif
if (type == EVM_XATTR_HMAC) { if (!(evm_initialized & EVM_INIT_HMAC)) { @@ -384,8 +395,11 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name, return rc; if (rc) return -EPERM; - +#ifdef CONFIG_IMA_DIGEST_LIST + data.hdr.algo = evm_hash_algo; +#else data.hdr.algo = HASH_ALGO_SHA1; +#endif rc = evm_calc_hmac(dentry, xattr_name, xattr_value, xattr_value_len, &data); if (rc == 0) { @@ -393,7 +407,12 @@ 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], +#ifdef CONFIG_IMA_DIGEST_LIST + hash_digest_size[evm_hash_algo] + 1, + 0); +#else SHA1_DIGEST_SIZE + 1, 0); +#endif } else if (rc == -ENODATA && (inode->i_opflags & IOP_XATTR)) { rc = __vfs_removexattr(&nop_mnt_idmap, dentry, XATTR_NAME_EVM); } @@ -405,7 +424,11 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr, { struct shash_desc *desc;
+#ifdef CONFIG_IMA_DIGEST_LIST + desc = init_desc(EVM_XATTR_HMAC, evm_hash_algo); +#else desc = init_desc(EVM_XATTR_HMAC, HASH_ALGO_SHA1); +#endif if (IS_ERR(desc)) { pr_info("init_desc failed\n"); return PTR_ERR(desc); @@ -417,9 +440,15 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr, return 0; }
+#ifdef CONFIG_IMA_DIGEST_LIST +/* + * Get the key from the TPM for the HMAC + */ +#else /* * Get the key from the TPM for the SHA1-HMAC */ +#endif int evm_init_key(void) { struct key *evm_key; diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 40485862362f1..60545c6c511ba 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -260,18 +260,30 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, /* check value type */ switch (xattr_data->type) { case EVM_XATTR_HMAC: +#ifdef CONFIG_IMA_DIGEST_LIST + if (xattr_len != hash_digest_size[evm_hash_algo] + 1) { +#else if (xattr_len != sizeof(struct evm_xattr)) { +#endif evm_status = INTEGRITY_FAIL; goto out; }
+#ifdef CONFIG_IMA_DIGEST_LIST + digest.hdr.algo = evm_hash_algo; +#else digest.hdr.algo = HASH_ALGO_SHA1; +#endif rc = evm_calc_hmac(dentry, xattr_name, xattr_value, xattr_value_len, &digest); if (rc) break; rc = crypto_memneq(xattr_data->data, digest.digest, +#ifdef CONFIG_IMA_DIGEST_LIST + hash_digest_size[evm_hash_algo]); +#else SHA1_DIGEST_SIZE); +#endif if (rc) rc = -EINVAL; break; @@ -996,7 +1008,11 @@ int evm_inode_init_security(struct inode *inode, goto out;
evm_xattr->value = xattr_data; +#ifdef CONFIG_IMA_DIGEST_LIST + evm_xattr->value_len = hash_digest_size[evm_hash_algo] + 1; +#else evm_xattr->value_len = sizeof(*xattr_data); +#endif evm_xattr->name = XATTR_EVM_SUFFIX; return 0; out: @@ -1018,9 +1034,19 @@ void __init evm_load_x509(void)
static int __init init_evm(void) { +#ifdef CONFIG_IMA_DIGEST_LIST + int error, i; +#else int error; +#endif struct list_head *pos, *q;
+#ifdef CONFIG_IMA_DIGEST_LIST + i = match_string(hash_algo_name, HASH_ALGO__LAST, + CONFIG_EVM_DEFAULT_HASH); + if (i >= 0) + evm_hash_algo = i; +#endif evm_init_config();
error = integrity_init_keyring(INTEGRITY_KEYRING_EVM); diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 54f75738ff636..98ae8f9e79e2b 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -104,7 +104,11 @@ struct evm_ima_xattr_data { /* Only used in the EVM HMAC code. */ struct evm_xattr { struct evm_ima_xattr_data data; +#ifdef CONFIG_IMA_DIGEST_LIST + u8 digest[SHA512_DIGEST_SIZE]; +#else u8 digest[SHA1_DIGEST_SIZE]; +#endif } __packed;
#define IMA_MAX_DIGEST_SIZE HASH_MAX_DIGESTSIZE
From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
---------------------------
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: zhoushuiqing zhoushuiqing2@huawei.com --- arch/arm64/configs/openeuler_defconfig | 7 +++++++ arch/x86/configs/openeuler_defconfig | 25 ++++++++++++++++--------- 2 files changed, 23 insertions(+), 9 deletions(-)
diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig index 6345193b52437..93be908dac80d 100644 --- a/arch/arm64/configs/openeuler_defconfig +++ b/arch/arm64/configs/openeuler_defconfig @@ -9554,6 +9554,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 @@ -9855,6 +9858,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 @@ -9871,6 +9877,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 3207c003fe09b..f92f57fe155a4 100644 --- a/arch/x86/configs/openeuler_defconfig +++ b/arch/x86/configs/openeuler_defconfig @@ -4368,18 +4368,18 @@ CONFIG_TCG_TIS_SPI=y # CONFIG_TCG_TIS_SPI_CR50 is not set CONFIG_TCG_TIS_I2C=m CONFIG_TCG_TIS_I2C_CR50=m -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=m CONFIG_TCG_CRB=y CONFIG_TCG_VTPM_PROXY=m -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_CLASS=m CONFIG_XILLYBUS=m @@ -9680,6 +9680,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 @@ -9975,6 +9978,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 @@ -9991,6 +9997,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
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
---------------------------
This reverts commit 9b772f4948fa513c501ae37c7afc89aa8613314c. backport patch from LTS 5.10.50 instead.
Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: zhoushuiqing zhoushuiqing2@huawei.com --- Documentation/ABI/testing/evm | 4 ++-- security/integrity/evm/evm_secfs.c | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/Documentation/ABI/testing/evm b/Documentation/ABI/testing/evm index 44750a933db49..2828e720d91eb 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 9b907c2fee60b..a485c80c6456c 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c @@ -86,7 +86,11 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf, * an HMAC key is loaded. */ if ((i & EVM_ALLOW_METADATA_WRITES) && +#ifdef CONFIG_IMA_DIGEST_LIST + ((evm_initialized & EVM_KEY_MASK) != 0)) +#else (evm_initialized & EVM_INIT_HMAC) != 0) +#endif return -EPERM;
if (i & EVM_INIT_HMAC) {
From: Zhang Tianxing zhangtianxing3@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
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: zhoushuiqing zhoushuiqing2@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 0712023d67610..5aaea24571d9b 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
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M 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: zhoushuiqing zhoushuiqing2@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 | 2 ++ security/integrity/ima/ima_digest_list.c | 23 ++++++++++++++++ security/integrity/ima/ima_fs.c | 13 +++++++++ 7 files changed, 79 insertions(+)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 84fe3bc6c1481..eefb460d3f321 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1984,6 +1984,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 93be908dac80d..6bc60b5eae180 100644 --- a/arch/arm64/configs/openeuler_defconfig +++ b/arch/arm64/configs/openeuler_defconfig @@ -9556,6 +9556,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 f92f57fe155a4..f731417c4cd49 100644 --- a/arch/x86/configs/openeuler_defconfig +++ b/arch/x86/configs/openeuler_defconfig @@ -9682,6 +9682,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 aa6a06f3e8b12..eab73b839c2af 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 9ba7bff50c15d..3d75acdc3db77 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -61,6 +61,8 @@ extern const char boot_aggregate_name[]; extern int ima_digest_list_pcr; extern bool ima_plus_standard_pcr; extern int ima_digest_list_actions; +extern size_t ima_digest_db_max_size __ro_after_init; +extern size_t 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 5aaea24571d9b..021ffda4a6f91 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 2b82913519c4e..7a20c567f081d 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -369,6 +369,12 @@ static ssize_t ima_read_policy(char *path) 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. @@ -388,6 +394,13 @@ static ssize_t ima_read_policy(char *path) if (rc < 0) break; size -= rc; + +#ifdef CONFIG_IMA_DIGEST_LIST + if (dentry == digest_list_data) + ima_digest_db_size += rc; + if (dentry == digest_list_data_del) + ima_digest_db_size -= rc; +#endif }
vfree(data);
From: Zhang Tianxing zhangtianxing3@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M 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: zhoushuiqing zhoushuiqing2@huawei.com --- security/integrity/ima/ima_efi.c | 3 +++ security/integrity/ima/ima_fs.c | 10 ++++++++++ 2 files changed, 13 insertions(+)
diff --git a/security/integrity/ima/ima_efi.c b/security/integrity/ima/ima_efi.c index 9db66fe310d42..e1eba55f9d7ab 100644 --- a/security/integrity/ima/ima_efi.c +++ b/security/integrity/ima/ima_efi.c @@ -6,6 +6,9 @@ #include <linux/module.h> #include <linux/ima.h> #include <asm/efi.h> +#ifdef CONFIG_IMA_DIGEST_LIST +#include <linux/ctype.h> +#endif
#ifndef arch_ima_efi_boot_mode #define arch_ima_efi_boot_mode efi_secureboot_mode_unset diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 7a20c567f081d..e531ee4838d4e 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -24,6 +24,7 @@
#ifdef CONFIG_IMA_DIGEST_LIST #include <linux/file.h> +#include <linux/ctype.h> #endif #include "ima.h" #ifdef CONFIG_IMA_DIGEST_LIST @@ -427,6 +428,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, ssize_t result; #ifdef CONFIG_IMA_DIGEST_LIST struct dentry *dentry = file_dentry(file); + int i; #endif
#ifndef CONFIG_IMA_DIGEST_LIST @@ -454,6 +456,14 @@ static ssize_t ima_write_policy(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; + } + } #else data = memdup_user_nul(buf, datalen); if (IS_ERR(data)) {
From: Zhang Tianxing zhangtianxing3@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M 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: zhoushuiqing zhoushuiqing2@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 6bc60b5eae180..8c780d7812d5e 100644 --- a/arch/arm64/configs/openeuler_defconfig +++ b/arch/arm64/configs/openeuler_defconfig @@ -9560,7 +9560,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 f731417c4cd49..bd3bb01924f07 100644 --- a/arch/x86/configs/openeuler_defconfig +++ b/arch/x86/configs/openeuler_defconfig @@ -9686,7 +9686,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
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M 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: zhoushuiqing zhoushuiqing2@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 | 22 +++++++++++++++++----- security/integrity/ima/ima_fs.c | 12 ++++++------ 6 files changed, 26 insertions(+), 19 deletions(-)
diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig index 8c780d7812d5e..08ac586c81f8a 100644 --- a/arch/arm64/configs/openeuler_defconfig +++ b/arch/arm64/configs/openeuler_defconfig @@ -9556,7 +9556,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 bd3bb01924f07..5f4d70de32f99 100644 --- a/arch/x86/configs/openeuler_defconfig +++ b/arch/x86/configs/openeuler_defconfig @@ -9682,7 +9682,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 eab73b839c2af..d0d45a521083c 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 3d75acdc3db77..1edd7087354c0 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -61,8 +61,8 @@ extern const char boot_aggregate_name[]; extern int ima_digest_list_pcr; extern bool ima_plus_standard_pcr; extern int ima_digest_list_actions; -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 021ffda4a6f91..2e8a5c16bd90f 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,12 @@ 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); + vfree(datap); + 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 e531ee4838d4e..237628466eb38 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -373,6 +373,7 @@ static ssize_t ima_read_policy(char *path) /* 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; } @@ -394,14 +395,13 @@ static ssize_t ima_read_policy(char *path) #endif if (rc < 0) break; - size -= rc; - #ifdef CONFIG_IMA_DIGEST_LIST - if (dentry == digest_list_data) - ima_digest_db_size += rc; - if (dentry == digest_list_data_del) - ima_digest_db_size -= rc; + 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; }
vfree(data);
From: shenxiangwei shenxiangwei1@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M 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: zhoushuiqing zhoushuiqing2@huawei.com --- security/integrity/ima/ima_fs.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 237628466eb38..4b7c075a1837b 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -456,14 +456,6 @@ static ssize_t ima_write_policy(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; - } - } #else data = memdup_user_nul(buf, datalen); if (IS_ERR(data)) { @@ -478,6 +470,15 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
if (data[0] == '/') { #ifdef CONFIG_IMA_DIGEST_LIST + 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) {