diff --git a/include/linux/ima.h b/include/linux/ima.h index 91c637c943ed218f1f05cf90cc3bdbc4ca77c948..1270337c1d99f44c64392af032d206c9709815b4 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 96ac4e5fbe3d28492b1e8409f64dded8dd1cd985..98a10a2d89b4bb8a1c173272cefc5253fe23d4ea 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 9d041e3deef2d1f572d201d8bc7620a368536b73..c30262468e4c0e121271199576863aa5dc94f94d 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 97bda1826a9457e15410e0b8e24d6c4ca1a81e7a..8436729642fe1224b391359c9f86ebf1e9453ca2 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #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 923373a12f5c2d5fdb1f5af122f8b0bd97ab1e43..7a0a640fdf9c0a73ef5a395cabbcb25a1880d507 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 ac000920d486fd6a1b740599866e33a7418ec04b..57dc9fedb8fdb5f7eabe3fa9e85801bf6ee42abd 100644 --- a/security/integrity/ima/ima_ns.c +++ b/security/integrity/ima/ima_ns.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #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 0ab91cb31121a4c69a3f525cf260a81b44170c83..828793553a0eb8af543b425f2ae784b07e5c0a13 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);