diff options
Diffstat (limited to 'security/ipe')
| -rw-r--r-- | security/ipe/Kconfig | 1 | ||||
| -rw-r--r-- | security/ipe/audit.c | 53 | ||||
| -rw-r--r-- | security/ipe/fs.c | 61 | ||||
| -rw-r--r-- | security/ipe/hooks.c | 30 | ||||
| -rw-r--r-- | security/ipe/hooks.h | 3 | ||||
| -rw-r--r-- | security/ipe/ipe.c | 4 | ||||
| -rw-r--r-- | security/ipe/ipe.h | 2 | ||||
| -rw-r--r-- | security/ipe/policy.c | 17 | ||||
| -rw-r--r-- | security/ipe/policy_fs.c | 40 |
9 files changed, 128 insertions, 83 deletions
diff --git a/security/ipe/Kconfig b/security/ipe/Kconfig index 3c75bf267da4..a110a6cd848b 100644 --- a/security/ipe/Kconfig +++ b/security/ipe/Kconfig @@ -6,6 +6,7 @@ menuconfig SECURITY_IPE bool "Integrity Policy Enforcement (IPE)" depends on SECURITY && SECURITYFS && AUDIT && AUDITSYSCALL + select CRYPTO_LIB_SHA256 select PKCS7_MESSAGE_PARSER select SYSTEM_DATA_VERIFICATION select IPE_PROP_DM_VERITY if DM_VERITY diff --git a/security/ipe/audit.c b/security/ipe/audit.c index f05f0caa4850..3f0deeb54912 100644 --- a/security/ipe/audit.c +++ b/security/ipe/audit.c @@ -6,7 +6,7 @@ #include <linux/slab.h> #include <linux/audit.h> #include <linux/types.h> -#include <crypto/hash.h> +#include <crypto/sha2.h> #include "ipe.h" #include "eval.h" @@ -17,10 +17,12 @@ #define ACTSTR(x) ((x) == IPE_ACTION_ALLOW ? "ALLOW" : "DENY") -#define IPE_AUDIT_HASH_ALG "sha256" +#define IPE_AUDIT_HASH_ALG "sha256" /* keep in sync with audit_policy() */ #define AUDIT_POLICY_LOAD_FMT "policy_name=\"%s\" policy_version=%hu.%hu.%hu "\ "policy_digest=" IPE_AUDIT_HASH_ALG ":" +#define AUDIT_POLICY_LOAD_NULL_FMT "policy_name=? policy_version=? "\ + "policy_digest=?" #define AUDIT_OLD_ACTIVE_POLICY_FMT "old_active_pol_name=\"%s\" "\ "old_active_pol_version=%hu.%hu.%hu "\ "old_policy_digest=" IPE_AUDIT_HASH_ALG ":" @@ -44,6 +46,7 @@ static const char *const audit_op_names[__IPE_OP_MAX + 1] = { static const char *const audit_hook_names[__IPE_HOOK_MAX] = { "BPRM_CHECK", + "BPRM_CREDS_FOR_EXEC", "MMAP", "MPROTECT", "KERNEL_READ", @@ -180,37 +183,14 @@ static void audit_policy(struct audit_buffer *ab, const char *audit_format, const struct ipe_policy *const p) { - SHASH_DESC_ON_STACK(desc, tfm); - struct crypto_shash *tfm; - u8 *digest = NULL; + u8 digest[SHA256_DIGEST_SIZE]; - tfm = crypto_alloc_shash(IPE_AUDIT_HASH_ALG, 0, 0); - if (IS_ERR(tfm)) - return; - - desc->tfm = tfm; - - digest = kzalloc(crypto_shash_digestsize(tfm), GFP_KERNEL); - if (!digest) - goto out; - - if (crypto_shash_init(desc)) - goto out; - - if (crypto_shash_update(desc, p->pkcs7, p->pkcs7len)) - goto out; - - if (crypto_shash_final(desc, digest)) - goto out; + sha256(p->pkcs7, p->pkcs7len, digest); audit_log_format(ab, audit_format, p->parsed->name, p->parsed->version.major, p->parsed->version.minor, p->parsed->version.rev); - audit_log_n_hex(ab, digest, crypto_shash_digestsize(tfm)); - -out: - kfree(digest); - crypto_free_shash(tfm); + audit_log_n_hex(ab, digest, sizeof(digest)); } /** @@ -248,22 +228,29 @@ void ipe_audit_policy_activation(const struct ipe_policy *const op, } /** - * ipe_audit_policy_load() - Audit a policy being loaded into the kernel. - * @p: Supplies a pointer to the policy to audit. + * ipe_audit_policy_load() - Audit a policy loading event. + * @p: Supplies a pointer to the policy to audit or an error pointer. */ void ipe_audit_policy_load(const struct ipe_policy *const p) { struct audit_buffer *ab; + int err = 0; ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_IPE_POLICY_LOAD); if (!ab) return; - audit_policy(ab, AUDIT_POLICY_LOAD_FMT, p); - audit_log_format(ab, " auid=%u ses=%u lsm=ipe res=1", + if (!IS_ERR(p)) { + audit_policy(ab, AUDIT_POLICY_LOAD_FMT, p); + } else { + audit_log_format(ab, AUDIT_POLICY_LOAD_NULL_FMT); + err = PTR_ERR(p); + } + + audit_log_format(ab, " auid=%u ses=%u lsm=ipe res=%d errno=%d", from_kuid(&init_user_ns, audit_get_loginuid(current)), - audit_get_sessionid(current)); + audit_get_sessionid(current), !err, err); audit_log_end(ab); } diff --git a/security/ipe/fs.c b/security/ipe/fs.c index 5b6d19fb844a..076c111c85c8 100644 --- a/security/ipe/fs.c +++ b/security/ipe/fs.c @@ -12,11 +12,8 @@ #include "policy.h" #include "audit.h" -static struct dentry *np __ro_after_init; static struct dentry *root __ro_after_init; struct dentry *policy_root __ro_after_init; -static struct dentry *audit_node __ro_after_init; -static struct dentry *enforce_node __ro_after_init; /** * setaudit() - Write handler for the securityfs node, "ipe/success_audit" @@ -133,6 +130,8 @@ static ssize_t getenforce(struct file *f, char __user *data, * * %-ERANGE - Policy version number overflow * * %-EINVAL - Policy version parsing error * * %-EEXIST - Same name policy already deployed + * * %-ENOKEY - Policy signing key not found + * * %-EKEYREJECTED - Policy signature verification failed */ static ssize_t new_policy(struct file *f, const char __user *data, size_t len, loff_t *offset) @@ -141,12 +140,17 @@ static ssize_t new_policy(struct file *f, const char __user *data, char *copy = NULL; int rc = 0; - if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN)) - return -EPERM; + if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN)) { + rc = -EPERM; + goto out; + } copy = memdup_user_nul(data, len); - if (IS_ERR(copy)) - return PTR_ERR(copy); + if (IS_ERR(copy)) { + rc = PTR_ERR(copy); + copy = NULL; + goto out; + } p = ipe_new_policy(NULL, 0, copy, len); if (IS_ERR(p)) { @@ -158,12 +162,14 @@ static ssize_t new_policy(struct file *f, const char __user *data, if (rc) goto out; - ipe_audit_policy_load(p); - out: - if (rc < 0) - ipe_free_policy(p); kfree(copy); + if (rc < 0) { + ipe_free_policy(p); + ipe_audit_policy_load(ERR_PTR(rc)); + } else { + ipe_audit_policy_load(p); + } return (rc < 0) ? rc : len; } @@ -187,31 +193,30 @@ static const struct file_operations enforce_fops = { * Return: %0 on success. If an error occurs, the function will return * the -errno. */ -static int __init ipe_init_securityfs(void) +int __init ipe_init_securityfs(void) { int rc = 0; struct ipe_policy *ap; + struct dentry *dentry; if (!ipe_enabled) return -EOPNOTSUPP; root = securityfs_create_dir("ipe", NULL); - if (IS_ERR(root)) { - rc = PTR_ERR(root); - goto err; - } + if (IS_ERR(root)) + return PTR_ERR(root); - audit_node = securityfs_create_file("success_audit", 0600, root, + dentry = securityfs_create_file("success_audit", 0600, root, NULL, &audit_fops); - if (IS_ERR(audit_node)) { - rc = PTR_ERR(audit_node); + if (IS_ERR(dentry)) { + rc = PTR_ERR(dentry); goto err; } - enforce_node = securityfs_create_file("enforce", 0600, root, NULL, + dentry = securityfs_create_file("enforce", 0600, root, NULL, &enforce_fops); - if (IS_ERR(enforce_node)) { - rc = PTR_ERR(enforce_node); + if (IS_ERR(dentry)) { + rc = PTR_ERR(dentry); goto err; } @@ -228,20 +233,14 @@ static int __init ipe_init_securityfs(void) goto err; } - np = securityfs_create_file("new_policy", 0200, root, NULL, &np_fops); - if (IS_ERR(np)) { - rc = PTR_ERR(np); + dentry = securityfs_create_file("new_policy", 0200, root, NULL, &np_fops); + if (IS_ERR(dentry)) { + rc = PTR_ERR(dentry); goto err; } return 0; err: - securityfs_remove(np); - securityfs_remove(policy_root); - securityfs_remove(enforce_node); - securityfs_remove(audit_node); securityfs_remove(root); return rc; } - -fs_initcall(ipe_init_securityfs); diff --git a/security/ipe/hooks.c b/security/ipe/hooks.c index d0323b81cd8f..603abdc9ce3b 100644 --- a/security/ipe/hooks.c +++ b/security/ipe/hooks.c @@ -36,6 +36,33 @@ int ipe_bprm_check_security(struct linux_binprm *bprm) } /** + * ipe_bprm_creds_for_exec() - ipe security hook function for bprm creds check. + * @bprm: Supplies a pointer to a linux_binprm structure to source the file + * being evaluated. + * + * This LSM hook is called when userspace signals the kernel to check a file + * for execution through the execveat syscall with the AT_EXECVE_CHECK flag. + * The hook triggers IPE policy evaluation on the script file and returns + * the policy decision to userspace. The userspace program receives the + * return code and can decide whether to proceed with script execution. + * + * Return: + * * %0 - Success + * * %-EACCES - Did not pass IPE policy + */ +int ipe_bprm_creds_for_exec(struct linux_binprm *bprm) +{ + struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT; + + if (!bprm->is_check) + return 0; + + ipe_build_eval_ctx(&ctx, bprm->file, IPE_OP_EXEC, + IPE_HOOK_BPRM_CREDS_FOR_EXEC); + return ipe_evaluate_event(&ctx); +} + +/** * ipe_mmap_file() - ipe security hook function for mmap check. * @f: File being mmap'd. Can be NULL in the case of anonymous memory. * @reqprot: The requested protection on the mmap, passed from usermode. @@ -118,6 +145,7 @@ int ipe_kernel_read_file(struct file *file, enum kernel_read_file_id id, op = IPE_OP_FIRMWARE; break; case READING_MODULE: + case READING_MODULE_COMPRESSED: op = IPE_OP_KERNEL_MODULE; break; case READING_KEXEC_INITRAMFS: @@ -311,4 +339,4 @@ int ipe_inode_setintegrity(const struct inode *inode, return -EINVAL; } -#endif /* CONFIG_CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */ +#endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */ diff --git a/security/ipe/hooks.h b/security/ipe/hooks.h index 38d4a387d039..07db37332740 100644 --- a/security/ipe/hooks.h +++ b/security/ipe/hooks.h @@ -13,6 +13,7 @@ enum ipe_hook_type { IPE_HOOK_BPRM_CHECK = 0, + IPE_HOOK_BPRM_CREDS_FOR_EXEC, IPE_HOOK_MMAP, IPE_HOOK_MPROTECT, IPE_HOOK_KERNEL_READ, @@ -24,6 +25,8 @@ enum ipe_hook_type { int ipe_bprm_check_security(struct linux_binprm *bprm); +int ipe_bprm_creds_for_exec(struct linux_binprm *bprm); + int ipe_mmap_file(struct file *f, unsigned long reqprot, unsigned long prot, unsigned long flags); diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c index 4317134cb0da..495bb765de1b 100644 --- a/security/ipe/ipe.c +++ b/security/ipe/ipe.c @@ -47,6 +47,7 @@ struct ipe_inode *ipe_inode(const struct inode *inode) static struct security_hook_list ipe_hooks[] __ro_after_init = { LSM_HOOK_INIT(bprm_check_security, ipe_bprm_check_security), + LSM_HOOK_INIT(bprm_creds_for_exec, ipe_bprm_creds_for_exec), LSM_HOOK_INIT(mmap_file, ipe_mmap_file), LSM_HOOK_INIT(file_mprotect, ipe_file_mprotect), LSM_HOOK_INIT(kernel_read_file, ipe_kernel_read_file), @@ -92,7 +93,8 @@ static int __init ipe_init(void) } DEFINE_LSM(ipe) = { - .name = "ipe", + .id = &ipe_lsmid, .init = ipe_init, .blobs = &ipe_blobs, + .initcall_fs = ipe_init_securityfs, }; diff --git a/security/ipe/ipe.h b/security/ipe/ipe.h index fb37513812dd..25cfdb8f0c20 100644 --- a/security/ipe/ipe.h +++ b/security/ipe/ipe.h @@ -23,4 +23,6 @@ struct ipe_bdev *ipe_bdev(struct block_device *b); struct ipe_inode *ipe_inode(const struct inode *inode); #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */ +int ipe_init_securityfs(void); + #endif /* _IPE_H */ diff --git a/security/ipe/policy.c b/security/ipe/policy.c index b628f696e32b..1c58c29886e8 100644 --- a/security/ipe/policy.c +++ b/security/ipe/policy.c @@ -84,8 +84,11 @@ static int set_pkcs7_data(void *ctx, const void *data, size_t len, * ipe_new_policy. * * Context: Requires root->i_rwsem to be held. - * Return: %0 on success. If an error occurs, the function will return - * the -errno. + * Return: + * * %0 - Success + * * %-ENOENT - Policy was deleted while updating + * * %-EINVAL - Policy name mismatch + * * %-ESTALE - Policy version too old */ int ipe_update_policy(struct inode *root, const char *text, size_t textlen, const char *pkcs7, size_t pkcs7len) @@ -146,10 +149,12 @@ err: * * Return: * * a pointer to the ipe_policy structure - Success - * * %-EBADMSG - Policy is invalid - * * %-ENOMEM - Out of memory (OOM) - * * %-ERANGE - Policy version number overflow - * * %-EINVAL - Policy version parsing error + * * %-EBADMSG - Policy is invalid + * * %-ENOMEM - Out of memory (OOM) + * * %-ERANGE - Policy version number overflow + * * %-EINVAL - Policy version parsing error + * * %-ENOKEY - Policy signing key not found + * * %-EKEYREJECTED - Policy signature verification failed */ struct ipe_policy *ipe_new_policy(const char *text, size_t textlen, const char *pkcs7, size_t pkcs7len) diff --git a/security/ipe/policy_fs.c b/security/ipe/policy_fs.c index 3bcd8cbd09df..9d92d8a14b13 100644 --- a/security/ipe/policy_fs.c +++ b/security/ipe/policy_fs.c @@ -12,11 +12,16 @@ #include "policy.h" #include "eval.h" #include "fs.h" +#include "audit.h" #define MAX_VERSION_SIZE ARRAY_SIZE("65535.65535.65535") /** - * ipefs_file - defines a file in securityfs. + * struct ipefs_file - defines a file in securityfs. + * + * @name: file name inside the policy subdirectory + * @access: file permissions + * @fops: &file_operations specific to this file */ struct ipefs_file { const char *name; @@ -282,8 +287,13 @@ static ssize_t getactive(struct file *f, char __user *data, * On success this updates the policy represented by $name, * in-place. * - * Return: Length of buffer written on success. If an error occurs, - * the function will return the -errno. + * Return: + * * Length of buffer written - Success + * * %-EPERM - Insufficient permission + * * %-ENOMEM - Out of memory (OOM) + * * %-ENOENT - Policy was deleted while updating + * * %-EINVAL - Policy name mismatch + * * %-ESTALE - Policy version too old */ static ssize_t update_policy(struct file *f, const char __user *data, size_t len, loff_t *offset) @@ -292,21 +302,29 @@ static ssize_t update_policy(struct file *f, const char __user *data, char *copy = NULL; int rc = 0; - if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN)) - return -EPERM; + if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN)) { + rc = -EPERM; + goto out; + } copy = memdup_user(data, len); - if (IS_ERR(copy)) - return PTR_ERR(copy); + if (IS_ERR(copy)) { + rc = PTR_ERR(copy); + copy = NULL; + goto out; + } root = d_inode(f->f_path.dentry->d_parent); inode_lock(root); rc = ipe_update_policy(root, NULL, 0, copy, len); inode_unlock(root); +out: kfree(copy); - if (rc) + if (rc) { + ipe_audit_policy_load(ERR_PTR(rc)); return rc; + } return len; } @@ -401,7 +419,7 @@ static const struct file_operations delete_fops = { .write = delete_policy, }; -/** +/* * policy_subdir - files under a policy subdirectory */ static const struct ipefs_file policy_subdir[] = { @@ -420,7 +438,7 @@ static const struct ipefs_file policy_subdir[] = { */ void ipe_del_policyfs_node(struct ipe_policy *p) { - securityfs_recursive_remove(p->policyfs); + securityfs_remove(p->policyfs); p->policyfs = NULL; } @@ -467,6 +485,6 @@ int ipe_new_policyfs_node(struct ipe_policy *p) return 0; err: - securityfs_recursive_remove(policyfs); + securityfs_remove(policyfs); return rc; } |
