From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Add ima securityfs entries to configure per ima namespace: - path to the x509 certificate - ima kernel boot parameters
The x509 certificate will be parsed and loaded when the first process is born into the new ima namespace, paths are not validated when written.
Kernel boot parameters are pre-parsed and applied when the first process is born into the new namespace.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- include/linux/ima.h | 2 + security/integrity/ima/ima.h | 19 ++-- security/integrity/ima/ima_appraise.c | 9 +- security/integrity/ima/ima_fs.c | 121 ++++++++++++++++++++-- security/integrity/ima/ima_init.c | 2 + security/integrity/ima/ima_ns.c | 140 ++++++++++++++++++++++---- security/integrity/ima/ima_policy.c | 40 +++++--- 7 files changed, 278 insertions(+), 55 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h index 91c637c943ed..1270337c1d99 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -235,6 +235,8 @@ struct ima_namespace { struct list_head ns_measurements; atomic_long_t ml_len; /* number of stored measurements in the list */ atomic_long_t violations; + char *x509_path_for_children; + struct ima_policy_setup_data *policy_setup_for_children; } __randomize_layout;
extern struct ima_namespace init_ima_ns; diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 96ac4e5fbe3d..98a10a2d89b4 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -67,6 +67,7 @@ struct ima_policy_setup_data { bool ima_use_appraise_tcb; bool ima_use_appraise_exec_tcb; bool ima_use_appraise_exec_immutable; + bool fail_unverifiable_sigs; };
/* IMA event related data */ @@ -321,15 +322,10 @@ void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos); void ima_policy_stop(struct seq_file *m, void *v); int ima_policy_show(struct seq_file *m, void *v);
-int ima_policy_setup(char *str, - struct ima_policy_setup_data *policy_setup_data, - bool *fail_unverifiable_sigs); -int ima_default_measure_policy_setup(const char *str, - struct ima_policy_setup_data *setup_data); -int ima_default_appraise_policy_setup(const char *str, - struct ima_policy_setup_data *setup_data); -int ima_default_appraise_setup(const char *str, - struct ima_policy_setup_data *setup_data); +int ima_policy_setup(char *str, struct ima_namespace *ima_ns); +int ima_default_measure_policy_setup(char *str, struct ima_namespace *ima_ns); +int ima_default_appraise_policy_setup(char *str, struct ima_namespace *ima_ns); +int ima_default_appraise_setup(char *str, struct ima_namespace *ima_ns);
/* Appraise integrity measurements */ #define IMA_APPRAISE_ENFORCE 0x01 @@ -432,6 +428,11 @@ static inline struct ima_namespace *get_current_ns(void)
void ima_delete_ns_rules(struct ima_policy_data *policy_data, bool is_root_ns); + +ssize_t ima_ns_write_x509_for_children(struct ima_namespace *ima_ns, + char *x509_path); +ssize_t ima_ns_write_kcmd_for_children(struct ima_namespace *ima_ns, + char *kcmd); #else static inline int __init ima_init_namespace(void) { diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 9d041e3deef2..c30262468e4c 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -19,9 +19,12 @@
static bool ima_appraise_req_evm __ro_after_init;
-int ima_default_appraise_setup(const char *str, - struct ima_policy_setup_data *setup_data) +int ima_default_appraise_setup(char *str, + struct ima_namespace *ima_ns) { + struct ima_policy_setup_data *setup_data = (ima_ns == &init_ima_ns) ? + &init_policy_setup_data : ima_ns->policy_setup_for_children; + #ifdef CONFIG_IMA_APPRAISE_BOOTPARAM bool sb_state = arch_ima_get_secureboot(); int appraisal_state = setup_data->ima_appraise; @@ -55,7 +58,7 @@ int ima_default_appraise_setup(const char *str,
static int __init default_appraise_setup(char *str) { - return ima_default_appraise_setup(str, &init_policy_setup_data); + return ima_default_appraise_setup(str, &init_ima_ns); }
__setup("ima_appraise=", default_appraise_setup); diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 97bda1826a94..8436729642fe 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -23,6 +23,8 @@ #include <linux/vmalloc.h> #include <linux/file.h> #include <linux/ctype.h> +#include <linux/string.h> +#include <linux/kernel.h>
#include "ima.h" #include "ima_digest_list.h" @@ -39,6 +41,10 @@ static struct dentry *ima_policy; static struct dentry *digests_count; static struct dentry *digest_list_data; static struct dentry *digest_list_data_del; +#ifdef CONFIG_IMA_NS +static struct dentry *x509_for_children; +static struct dentry *kcmd_for_children; +#endif /* CONFIG_IMA_NS */
bool ima_canonical_fmt; static int __init default_canonical_fmt_setup(char *str) @@ -52,6 +58,16 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup);
static int valid_policy = 1;
+static int ima_open_simple(struct inode *inode, struct file *file) +{ + struct ima_namespace *ima_ns = get_current_ns(); + + if (!ns_capable(ima_ns->user_ns, CAP_SYS_ADMIN)) + return -EPERM; + + return 0; +} + static ssize_t ima_show_htable_value(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { @@ -73,18 +89,8 @@ static ssize_t ima_show_htable_value(struct file *filp, char __user *buf, return simple_read_from_buffer(buf, count, ppos, tmpbuf, len); }
-static int ima_open_htable_value(struct inode *inode, struct file *file) -{ - struct ima_namespace *ima_ns = get_current_ns(); - - if (!ns_capable(ima_ns->user_ns, CAP_SYS_ADMIN)) - return -EPERM; - - return 0; -} - static const struct file_operations ima_htable_value_ops = { - .open = ima_open_htable_value, + .open = ima_open_simple, .read = ima_show_htable_value, .llseek = generic_file_llseek, }; @@ -613,6 +619,79 @@ static const struct file_operations ima_data_upload_ops = { .llseek = generic_file_llseek, };
+#ifdef CONFIG_IMA_NS +static int ima_open_for_children(struct inode *inode, struct file *file) +{ + struct ima_namespace *ima_ns = get_current_ns(); + + /* Allow to set children configuration only after unshare() */ + if (ima_ns == current->nsproxy->ima_ns_for_children) + return -EPERM; + + return ima_open_simple(inode, file); +} + +static ssize_t ima_write_x509_for_children(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t res; + char *x509_path; + struct ima_namespace *ima_ns = current->nsproxy->ima_ns_for_children; + + /* Only allow < page size writes at the beginning of the file */ + if ((*ppos != 0) || (count >= PAGE_SIZE)) + return -EINVAL; + + x509_path = memdup_user_nul(buf, count); + if (IS_ERR(x509_path)) + return PTR_ERR(x509_path); + + res = ima_ns_write_x509_for_children(ima_ns, x509_path); + if (res) { + kfree(x509_path); + count = res; + } + + return count; +} + +static const struct file_operations ima_x509_for_children_ops = { + .open = ima_open_for_children, + .write = ima_write_x509_for_children, +}; + +static ssize_t ima_write_kcmd_for_children(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t res; + char *kcmd; + struct ima_namespace *ima_ns = current->nsproxy->ima_ns_for_children; + + /* Only allow < page size writes at the beginning of the file */ + if ((*ppos != 0) || (count >= PAGE_SIZE)) + return -EINVAL; + + kcmd = memdup_user_nul(buf, count); + if (IS_ERR(kcmd)) + return PTR_ERR(kcmd); + + res = ima_ns_write_kcmd_for_children(ima_ns, kcmd); + if (res) + count = res; + + kfree(kcmd); + + return count; +} + +static const struct file_operations ima_kcmd_for_children_ops = { + .open = ima_open_for_children, + .write = ima_write_kcmd_for_children, +}; +#endif /* CONFIG_IMA_NS */ + int __init ima_fs_init(void) { ima_dir = securityfs_create_dir("ima", integrity_dir); @@ -676,6 +755,22 @@ int __init ima_fs_init(void) if (IS_ERR(digest_list_data_del)) goto out; #endif +#ifdef CONFIG_IMA_NS + x509_for_children = securityfs_create_file("x509_for_children", + 0202, + ima_dir, NULL, + &ima_x509_for_children_ops); + if (IS_ERR(x509_for_children)) + goto out; + + kcmd_for_children = securityfs_create_file("kcmd_for_children", + 0202, + ima_dir, NULL, + &ima_kcmd_for_children_ops); + if (IS_ERR(kcmd_for_children)) + goto out; +#endif /* CONFIG_IMA_NS */ + return 0; out: securityfs_remove(digest_list_data_del); @@ -688,5 +783,9 @@ int __init ima_fs_init(void) securityfs_remove(ima_symlink); securityfs_remove(ima_dir); securityfs_remove(ima_policy); +#ifdef CONFIG_IMA_NS + securityfs_remove(x509_for_children); + securityfs_remove(kcmd_for_children); +#endif /* CONFIG_IMA_NS */ return -1; } diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 923373a12f5c..7a0a640fdf9c 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -31,6 +31,8 @@ struct ima_namespace init_ima_ns = { .ns.inum = PROC_IMA_INIT_INO, #ifdef CONFIG_IMA_NS .ns.ops = &imans_operations, + .x509_path_for_children = NULL, + .policy_setup_for_children = NULL, #endif .frozen = true, .policy_data = &init_policy_data, diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c index ac000920d486..57dc9fedb8fd 100644 --- a/security/integrity/ima/ima_ns.c +++ b/security/integrity/ima/ima_ns.c @@ -27,6 +27,8 @@ #include <linux/workqueue.h> #include <linux/mutex.h> #include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/kernel.h>
#include "ima.h"
@@ -74,23 +76,17 @@ static struct ima_namespace *ima_ns_alloc(void) return NULL; }
-static void ima_set_ns_policy(struct ima_namespace *ima_ns, - char *policy_setup_str) +static void ima_set_ns_policy(struct ima_namespace *ima_ns) { - struct ima_policy_setup_data setup_data; + struct ima_policy_setup_data setup_data = {0};
+ if (!ima_ns->policy_setup_for_children) { #ifdef CONFIG_IMA_APPRAISE - setup_data.ima_appraise = IMA_APPRAISE_ENFORCE; + setup_data.ima_appraise = IMA_APPRAISE_ENFORCE; #endif - /* Configuring IMA namespace will be implemented in the following - * patches. When it is done, parse configuration string and store result - * in setup_data. Temporarily use init_policy_setup_data. - */ - setup_data = init_policy_setup_data; - ima_ns->policy_data->ima_fail_unverifiable_sigs = - init_ima_ns.policy_data->ima_fail_unverifiable_sigs; - - ima_init_ns_policy(ima_ns, &setup_data); + ima_init_ns_policy(ima_ns, &setup_data); + } else + ima_init_ns_policy(ima_ns, ima_ns->policy_setup_for_children); }
static int ima_swap_user_ns(struct ima_namespace *ima_ns, @@ -151,6 +147,9 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns, rwlock_init(&ns->iint_tree->lock); ns->iint_tree->root = RB_ROOT;
+ ns->x509_path_for_children = NULL; + ns->policy_setup_for_children = NULL; + INIT_LIST_HEAD(&ns->ns_measurements); INIT_LIST_HEAD(&ns->policy_data->ima_default_rules); INIT_LIST_HEAD(&ns->policy_data->ima_policy_rules); @@ -218,6 +217,14 @@ static void imans_remove_hash_entries(struct ima_namespace *ima_ns) } }
+static void destroy_child_config(struct ima_namespace *ima_ns) +{ + kfree(ima_ns->x509_path_for_children); + ima_ns->x509_path_for_children = NULL; + kfree(ima_ns->policy_setup_for_children); + ima_ns->policy_setup_for_children = NULL; +} + static void destroy_ima_ns(struct ima_namespace *ns) { bool is_init_ns = (ns == &init_ima_ns); @@ -230,6 +237,7 @@ static void destroy_ima_ns(struct ima_namespace *ns) kfree(ns->iint_tree); ima_delete_ns_rules(ns->policy_data, is_init_ns); kfree(ns->policy_data); + destroy_child_config(ns); kfree(ns); }
@@ -319,27 +327,31 @@ static void imans_put(struct ns_common *ns)
static int imans_activate(struct ima_namespace *ima_ns) { + int res = 0; + if (ima_ns == &init_ima_ns) - return 0; + return res;
if (ima_ns->frozen) - return 0; + return res;
mutex_lock(&frozen_lock); if (ima_ns->frozen) goto out;
- ima_set_ns_policy(ima_ns, NULL); + ima_set_ns_policy(ima_ns);
ima_ns->frozen = true;
down_write(&ima_ns_list_lock); list_add_tail(&ima_ns->list, &ima_ns_list); up_write(&ima_ns_list_lock); + + destroy_child_config(ima_ns); out: mutex_unlock(&frozen_lock);
- return 0; + return res; }
static int imans_install(struct nsset *nsset, struct ns_common *new) @@ -424,3 +436,97 @@ const struct proc_ns_operations imans_for_children_operations = { .owner = imans_owner, };
+struct ima_kernel_param { + const char *name; + int (*set)(char *val, struct ima_namespace *ima_ns); +}; + +/* TODO: add ima_template, ima_template_fmt, ima_hash, ... */ +static const struct ima_kernel_param ima_kernel_params[] = { + {"ima_appraise", ima_default_appraise_setup}, + {"ima_policy", ima_policy_setup}, +}; +static const size_t ima_kernel_params_size = ARRAY_SIZE(ima_kernel_params); + +ssize_t ima_ns_write_x509_for_children(struct ima_namespace *ima_ns, + char *x509_path) +{ + ssize_t retval = 0; + + mutex_lock(&frozen_lock); + if (ima_ns->frozen) { + retval = -EACCES; + goto out; + } + + kfree(ima_ns->x509_path_for_children); + ima_ns->x509_path_for_children = x509_path; +out: + mutex_unlock(&frozen_lock); + + return retval; +} + +ssize_t ima_ns_write_kcmd_for_children(struct ima_namespace *ima_ns, + char *kcmd) +{ + u32 i; + char *param, *val; + ssize_t ret = 0; + + mutex_lock(&frozen_lock); + if (ima_ns->frozen) { + ret = -EACCES; + goto err_unlock; + } + + if (!ima_ns->policy_setup_for_children) { + ima_ns->policy_setup_for_children = + kmalloc(sizeof(struct ima_policy_setup_data), + GFP_KERNEL); + if (!ima_ns->policy_setup_for_children) { + ret = -ENOMEM; + goto err_unlock; + } + } + + memset(ima_ns->policy_setup_for_children, + 0, sizeof(struct ima_policy_setup_data)); + +#ifdef CONFIG_IMA_APPRAISE + ima_ns->policy_setup_for_children->ima_appraise = IMA_APPRAISE_ENFORCE; +#endif + + kcmd = skip_spaces(kcmd); + while (*kcmd) { + kcmd = next_arg(kcmd, ¶m, &val); + if (!val) { + ret = -EINVAL; + goto err_free; + } + + for (i = 0; i < ima_kernel_params_size; i++) { + if (strcmp(param, ima_kernel_params[i].name) == 0) + break; + } + + if (i == ima_kernel_params_size) { + ret = -EINVAL; + goto err_free; + } + + ima_kernel_params[i].set(val, ima_ns); + } + mutex_unlock(&frozen_lock); + + return ret; + +err_free: + kfree(ima_ns->policy_setup_for_children); + ima_ns->policy_setup_for_children = NULL; +err_unlock: + mutex_unlock(&frozen_lock); + + return ret; +} + diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 0ab91cb31121..828793553a0e 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -240,9 +240,13 @@ struct ima_policy_data init_policy_data = { .ima_rules = &init_policy_data.ima_default_rules, };
-int ima_default_measure_policy_setup(const char *str, - struct ima_policy_setup_data *setup_data) +int ima_default_measure_policy_setup(char *str, struct ima_namespace *ima_ns) { + struct ima_policy_setup_data *setup_data; + + setup_data = (ima_ns == &init_ima_ns) ? + &init_policy_setup_data : ima_ns->policy_setup_for_children; + if (setup_data->ima_policy) return 1;
@@ -252,7 +256,7 @@ int ima_default_measure_policy_setup(const char *str,
static int __init default_measure_policy_setup(char *str) { - return ima_default_measure_policy_setup(str, &init_policy_setup_data); + return ima_default_measure_policy_setup(str, &init_ima_ns); } __setup("ima_tcb", default_measure_policy_setup);
@@ -261,15 +265,15 @@ static bool ima_fail_unverifiable_sigs __ro_after_init; /** * ima_policy_setup - parse policy configuration string "ima_policy=" * @str: string to be parsed - * @setup_data: pointer to a structure where parsed data is stored - * @fail_unverifiable_sigs: boolean flag treated separately to preserve - * __ro_after_init + * @ima_ns: pointer to the ima namespace which policy is being set */ -int ima_policy_setup(char *str, - struct ima_policy_setup_data *setup_data, - bool *fail_unverifiable_sigs) +int ima_policy_setup(char *str, struct ima_namespace *ima_ns) { char *p; + struct ima_policy_setup_data *setup_data; + + setup_data = (ima_ns == &init_ima_ns) ? + &init_policy_setup_data : ima_ns->policy_setup_for_children;
while ((p = strsep(&str, " |\n")) != NULL) { if (*p == ' ') @@ -287,7 +291,7 @@ int ima_policy_setup(char *str, else if (strcmp(p, "secure_boot") == 0) setup_data->ima_use_secure_boot = true; else if (strcmp(p, "fail_securely") == 0) - *fail_unverifiable_sigs = true; + setup_data->fail_unverifiable_sigs = true; else pr_err("policy "%s" not found", p); } @@ -297,21 +301,27 @@ int ima_policy_setup(char *str,
static int __init policy_setup(char *str) { - return ima_policy_setup(str, &init_policy_setup_data, - &ima_fail_unverifiable_sigs); + ima_policy_setup(str, &init_ima_ns); + ima_fail_unverifiable_sigs = + init_policy_setup_data.fail_unverifiable_sigs; + return 1; } __setup("ima_policy=", policy_setup);
-int ima_default_appraise_policy_setup(const char *str, - struct ima_policy_setup_data *setup_data) +int ima_default_appraise_policy_setup(char *str, struct ima_namespace *ima_ns) { + struct ima_policy_setup_data *setup_data; + + setup_data = (ima_ns == &init_ima_ns) ? + &init_policy_setup_data : ima_ns->policy_setup_for_children; + setup_data->ima_use_appraise_tcb = true; return 1; }
static int __init default_appraise_policy_setup(char *str) { - return ima_default_appraise_policy_setup(str, &init_policy_setup_data); + return ima_default_appraise_policy_setup(str, &init_ima_ns); } __setup("ima_appraise_tcb", default_appraise_policy_setup);