diff options
Diffstat (limited to 'security')
55 files changed, 1215 insertions, 678 deletions
diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening index bd2aabb2c60f..d766b7d0ffd1 100644 --- a/security/Kconfig.hardening +++ b/security/Kconfig.hardening @@ -22,11 +22,17 @@ menu "Memory initialization" config CC_HAS_AUTO_VAR_INIT_PATTERN def_bool $(cc-option,-ftrivial-auto-var-init=pattern) -config CC_HAS_AUTO_VAR_INIT_ZERO - # GCC ignores the -enable flag, so we can test for the feature with - # a single invocation using the flag, but drop it as appropriate in - # the Makefile, depending on the presence of Clang. +config CC_HAS_AUTO_VAR_INIT_ZERO_BARE + def_bool $(cc-option,-ftrivial-auto-var-init=zero) + +config CC_HAS_AUTO_VAR_INIT_ZERO_ENABLER + # Clang 16 and later warn about using the -enable flag, but it + # is required before then. def_bool $(cc-option,-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang) + depends on !CC_HAS_AUTO_VAR_INIT_ZERO_BARE + +config CC_HAS_AUTO_VAR_INIT_ZERO + def_bool CC_HAS_AUTO_VAR_INIT_ZERO_BARE || CC_HAS_AUTO_VAR_INIT_ZERO_ENABLER choice prompt "Initialize kernel stack variables at function entry" @@ -106,6 +112,7 @@ choice config INIT_STACK_ALL_PATTERN bool "pattern-init everything (strongest)" depends on CC_HAS_AUTO_VAR_INIT_PATTERN + depends on !KMSAN help Initializes everything on the stack (including padding) with a specific debug value. This is intended to eliminate @@ -124,6 +131,7 @@ choice config INIT_STACK_ALL_ZERO bool "zero-init everything (strongest and safest)" depends on CC_HAS_AUTO_VAR_INIT_ZERO + depends on !KMSAN help Initializes everything on the stack (including padding) with a zero value. This is intended to eliminate all @@ -218,6 +226,7 @@ config STACKLEAK_RUNTIME_DISABLE config INIT_ON_ALLOC_DEFAULT_ON bool "Enable heap memory zeroing on allocation by default" + depends on !KMSAN help This has the effect of setting "init_on_alloc=1" on the kernel command line. This can be disabled with "init_on_alloc=0". @@ -230,6 +239,7 @@ config INIT_ON_ALLOC_DEFAULT_ON config INIT_ON_FREE_DEFAULT_ON bool "Enable heap memory zeroing on free by default" + depends on !KMSAN help This has the effect of setting "init_on_free=1" on the kernel command line. This can be disabled with "init_on_free=0". diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index acac3bb3eef2..e0d1dd0a192a 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -106,8 +106,8 @@ config SECURITY_APPARMOR_PARANOID_LOAD Disabling the check will speed up policy loads. config SECURITY_APPARMOR_KUNIT_TEST - bool "Build KUnit tests for policy_unpack.c" if !KUNIT_ALL_TESTS - depends on KUNIT=y && SECURITY_APPARMOR + tristate "Build KUnit tests for policy_unpack.c" if !KUNIT_ALL_TESTS + depends on KUNIT && SECURITY_APPARMOR default KUNIT_ALL_TESTS help This builds the AppArmor KUnit tests. diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 4377123c2b98..b9c5879dd599 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -9,6 +9,9 @@ apparmor-y := apparmorfs.o audit.o capability.o task.o ipc.o lib.o match.o \ policy_compat.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o +obj-$(CONFIG_SECURITY_APPARMOR_KUNIT_TEST) += apparmor_policy_unpack_test.o +apparmor_policy_unpack_test-objs += policy_unpack_test.o + clean-files := capability_names.h rlim_names.h net_names.h # Build a lower case string table of address family names diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index b447bc13ea8e..6dd3cc5309bf 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -299,11 +299,10 @@ static int aa_xattrs_match(const struct linux_binprm *bprm, struct aa_profile *profile, aa_state_t state) { int i; - ssize_t size; struct dentry *d; char *value = NULL; struct aa_attachment *attach = &profile->attach; - int value_size = 0, ret = attach->xattr_count; + int size, value_size = 0, ret = attach->xattr_count; if (!bprm || !attach->xattr_count) return 0; @@ -863,10 +862,10 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm) const char *info = NULL; int error = 0; bool unsafe = false; - kuid_t i_uid = i_uid_into_mnt(file_mnt_user_ns(bprm->file), - file_inode(bprm->file)); + vfsuid_t vfsuid = i_uid_into_vfsuid(file_mnt_user_ns(bprm->file), + file_inode(bprm->file)); struct path_cond cond = { - i_uid, + vfsuid_into_kuid(vfsuid), file_inode(bprm->file)->i_mode }; @@ -974,7 +973,7 @@ audit: error = fn_for_each(label, profile, aa_audit_file(profile, &nullperms, OP_EXEC, MAY_EXEC, bprm->filename, NULL, new, - i_uid, info, error)); + vfsuid_into_kuid(vfsuid), info, error)); aa_put_label(new); goto done; } diff --git a/security/apparmor/file.c b/security/apparmor/file.c index deb73480f0c6..cb3d3060d104 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -459,8 +459,10 @@ static int __file_path_perm(const char *op, struct aa_label *label, { struct aa_profile *profile; struct aa_perms perms = {}; + vfsuid_t vfsuid = i_uid_into_vfsuid(file_mnt_user_ns(file), + file_inode(file)); struct path_cond cond = { - .uid = i_uid_into_mnt(file_mnt_user_ns(file), file_inode(file)), + .uid = vfsuid_into_kuid(vfsuid), .mode = file_inode(file)->i_mode }; char *buffer; diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index 1e10e360a0ec..a6f4611ee50c 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h @@ -51,6 +51,43 @@ enum { }; /* + * The AppArmor interface treats data as a type byte followed by the + * actual data. The interface has the notion of a named entry + * which has a name (AA_NAME typecode followed by name string) followed by + * the entries typecode and data. Named types allow for optional + * elements and extensions to be added and tested for without breaking + * backwards compatibility. + */ + +enum aa_code { + AA_U8, + AA_U16, + AA_U32, + AA_U64, + AA_NAME, /* same as string except it is items name */ + AA_STRING, + AA_BLOB, + AA_STRUCT, + AA_STRUCTEND, + AA_LIST, + AA_LISTEND, + AA_ARRAY, + AA_ARRAYEND, +}; + +/* + * aa_ext is the read of the buffer containing the serialized profile. The + * data is copied into a kernel buffer in apparmorfs and then handed off to + * the unpack routines. + */ +struct aa_ext { + void *start; + void *end; + void *pos; /* pointer to current position in the buffer */ + u32 version; +}; + +/* * struct aa_loaddata - buffer of policy raw_data set * * there is no loaddata ref for being on ns list, nor a ref from @@ -128,4 +165,17 @@ static inline void aa_put_loaddata(struct aa_loaddata *data) kref_put(&data->count, aa_loaddata_kref); } +#if IS_ENABLED(CONFIG_KUNIT) +bool aa_inbounds(struct aa_ext *e, size_t size); +size_t aa_unpack_u16_chunk(struct aa_ext *e, char **chunk); +bool aa_unpack_X(struct aa_ext *e, enum aa_code code); +bool aa_unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name); +bool aa_unpack_u32(struct aa_ext *e, u32 *data, const char *name); +bool aa_unpack_u64(struct aa_ext *e, u64 *data, const char *name); +bool aa_unpack_array(struct aa_ext *e, const char *name, u16 *size); +size_t aa_unpack_blob(struct aa_ext *e, char **blob, const char *name); +int aa_unpack_str(struct aa_ext *e, const char **string, const char *name); +int aa_unpack_strdup(struct aa_ext *e, char **string, const char *name); +#endif + #endif /* __POLICY_INTERFACE_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 25114735bc11..c6728a629437 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -228,8 +228,10 @@ static int common_perm(const char *op, const struct path *path, u32 mask, static int common_perm_cond(const char *op, const struct path *path, u32 mask) { struct user_namespace *mnt_userns = mnt_user_ns(path->mnt); + vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_userns, + d_backing_inode(path->dentry)); struct path_cond cond = { - i_uid_into_mnt(mnt_userns, d_backing_inode(path->dentry)), + vfsuid_into_kuid(vfsuid), d_backing_inode(path->dentry)->i_mode }; @@ -273,11 +275,13 @@ static int common_perm_rm(const char *op, const struct path *dir, struct inode *inode = d_backing_inode(dentry); struct user_namespace *mnt_userns = mnt_user_ns(dir->mnt); struct path_cond cond = { }; + vfsuid_t vfsuid; if (!inode || !path_mediated_fs(dentry)) return 0; - cond.uid = i_uid_into_mnt(mnt_userns, inode); + vfsuid = i_uid_into_vfsuid(mnt_userns, inode); + cond.uid = vfsuid_into_kuid(vfsuid); cond.mode = inode->i_mode; return common_perm_dir_dentry(op, dir, dentry, mask, &cond); @@ -332,6 +336,11 @@ static int apparmor_path_truncate(const struct path *path) return common_perm_cond(OP_TRUNC, path, MAY_WRITE | AA_MAY_SETATTR); } +static int apparmor_file_truncate(struct file *file) +{ + return apparmor_path_truncate(&file->f_path); +} + static int apparmor_path_symlink(const struct path *dir, struct dentry *dentry, const char *old_name) { @@ -371,20 +380,23 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d label = begin_current_label_crit_section(); if (!unconfined(label)) { struct user_namespace *mnt_userns = mnt_user_ns(old_dir->mnt); + vfsuid_t vfsuid; struct path old_path = { .mnt = old_dir->mnt, .dentry = old_dentry }; struct path new_path = { .mnt = new_dir->mnt, .dentry = new_dentry }; struct path_cond cond = { - i_uid_into_mnt(mnt_userns, d_backing_inode(old_dentry)), - d_backing_inode(old_dentry)->i_mode + .mode = d_backing_inode(old_dentry)->i_mode }; + vfsuid = i_uid_into_vfsuid(mnt_userns, d_backing_inode(old_dentry)); + cond.uid = vfsuid_into_kuid(vfsuid); if (flags & RENAME_EXCHANGE) { struct path_cond cond_exchange = { - i_uid_into_mnt(mnt_userns, d_backing_inode(new_dentry)), - d_backing_inode(new_dentry)->i_mode + .mode = d_backing_inode(new_dentry)->i_mode, }; + vfsuid = i_uid_into_vfsuid(mnt_userns, d_backing_inode(old_dentry)); + cond_exchange.uid = vfsuid_into_kuid(vfsuid); error = aa_path_perm(OP_RENAME_SRC, label, &new_path, 0, MAY_READ | AA_MAY_GETATTR | MAY_WRITE | @@ -450,10 +462,12 @@ static int apparmor_file_open(struct file *file) if (!unconfined(label)) { struct user_namespace *mnt_userns = file_mnt_user_ns(file); struct inode *inode = file_inode(file); + vfsuid_t vfsuid; struct path_cond cond = { - i_uid_into_mnt(mnt_userns, inode), - inode->i_mode + .mode = inode->i_mode, }; + vfsuid = i_uid_into_vfsuid(mnt_userns, inode); + cond.uid = vfsuid_into_kuid(vfsuid); error = aa_path_perm(OP_OPEN, label, &file->f_path, 0, aa_map_file_to_perms(file), &cond); @@ -617,7 +631,7 @@ static int apparmor_sb_pivotroot(const struct path *old_path, return error; } -static int apparmor_getprocattr(struct task_struct *task, char *name, +static int apparmor_getprocattr(struct task_struct *task, const char *name, char **value) { int error = -ENOENT; @@ -1107,11 +1121,10 @@ static struct aa_label *sk_peer_label(struct sock *sk) * Note: for tcp only valid if using ipsec or cipso on lan */ static int apparmor_socket_getpeersec_stream(struct socket *sock, - char __user *optval, - int __user *optlen, + sockptr_t optval, sockptr_t optlen, unsigned int len) { - char *name; + char *name = NULL; int slen, error = 0; struct aa_label *label; struct aa_label *peer; @@ -1128,23 +1141,21 @@ static int apparmor_socket_getpeersec_stream(struct socket *sock, /* don't include terminating \0 in slen, it breaks some apps */ if (slen < 0) { error = -ENOMEM; - } else { - if (slen > len) { - error = -ERANGE; - } else if (copy_to_user(optval, name, slen)) { - error = -EFAULT; - goto out; - } - if (put_user(slen, optlen)) - error = -EFAULT; -out: - kfree(name); - + goto done; + } + if (slen > len) { + error = -ERANGE; + goto done_len; } + if (copy_to_sockptr(optval, name, slen)) + error = -EFAULT; +done_len: + if (copy_to_sockptr(optlen, &slen, sizeof(slen))) + error = -EFAULT; done: end_current_label_crit_section(label); - + kfree(name); return error; } @@ -1236,6 +1247,7 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(mmap_file, apparmor_mmap_file), LSM_HOOK_INIT(file_mprotect, apparmor_file_mprotect), LSM_HOOK_INIT(file_lock, apparmor_file_lock), + LSM_HOOK_INIT(file_truncate, apparmor_file_truncate), LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 1bf8cfb8700a..66915653108c 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -14,6 +14,7 @@ */ #include <asm/unaligned.h> +#include <kunit/visibility.h> #include <linux/ctype.h> #include <linux/errno.h> #include <linux/zstd.h> @@ -29,49 +30,6 @@ #include "include/policy_unpack.h" #include "include/policy_compat.h" - -/* - * The AppArmor interface treats data as a type byte followed by the - * actual data. The interface has the notion of a named entry - * which has a name (AA_NAME typecode followed by name string) followed by - * the entries typecode and data. Named types allow for optional - * elements and extensions to be added and tested for without breaking - * backwards compatibility. - */ - -enum aa_code { - AA_U8, - AA_U16, - AA_U32, - AA_U64, - AA_NAME, /* same as string except it is items name */ - AA_STRING, - AA_BLOB, - AA_STRUCT, - AA_STRUCTEND, - AA_LIST, - AA_LISTEND, - AA_ARRAY, - AA_ARRAYEND, -}; - -/* - * aa_ext is the read of the buffer containing the serialized profile. The - * data is copied into a kernel buffer in apparmorfs and then handed off to - * the unpack routines. - */ -struct aa_ext { - void *start; - void *end; - void *pos; /* pointer to current position in the buffer */ - u32 version; -}; - -#define tri int -#define TRI_TRUE 1 -#define TRI_NONE 0 -#define TRI_FALSE -1 - /* audit callback for unpack fields */ static void audit_cb(struct audit_buffer *ab, void *va) { @@ -197,10 +155,11 @@ struct aa_loaddata *aa_loaddata_alloc(size_t size) } /* test if read will be in packed data bounds */ -static bool inbounds(struct aa_ext *e, size_t size) +VISIBLE_IF_KUNIT bool aa_inbounds(struct aa_ext *e, size_t size) { return (size <= e->end - e->pos); } +EXPORT_SYMBOL_IF_KUNIT(aa_inbounds); static void *kvmemdup(const void *src, size_t len) { @@ -212,22 +171,22 @@ static void *kvmemdup(const void *src, size_t len) } /** - * unpack_u16_chunk - test and do bounds checking for a u16 size based chunk + * aa_unpack_u16_chunk - test and do bounds checking for a u16 size based chunk * @e: serialized data read head (NOT NULL) * @chunk: start address for chunk of data (NOT NULL) * * Returns: the size of chunk found with the read head at the end of the chunk. */ -static size_t unpack_u16_chunk(struct aa_ext *e, char **chunk) +VISIBLE_IF_KUNIT size_t aa_unpack_u16_chunk(struct aa_ext *e, char **chunk) { size_t size = 0; void *pos = e->pos; - if (!inbounds(e, sizeof(u16))) + if (!aa_inbounds(e, sizeof(u16))) goto fail; size = le16_to_cpu(get_unaligned((__le16 *) e->pos)); e->pos += sizeof(__le16); - if (!inbounds(e, size)) + if (!aa_inbounds(e, size)) goto fail; *chunk = e->pos; e->pos += size; @@ -237,20 +196,22 @@ fail: e->pos = pos; return 0; } +EXPORT_SYMBOL_IF_KUNIT(aa_unpack_u16_chunk); /* unpack control byte */ -static bool unpack_X(struct aa_ext *e, enum aa_code code) +VISIBLE_IF_KUNIT bool aa_unpack_X(struct aa_ext *e, enum aa_code code) { - if (!inbounds(e, 1)) + if (!aa_inbounds(e, 1)) return false; if (*(u8 *) e->pos != code) return false; e->pos++; return true; } +EXPORT_SYMBOL_IF_KUNIT(aa_unpack_X); /** - * unpack_nameX - check is the next element is of type X with a name of @name + * aa_unpack_nameX - check is the next element is of type X with a name of @name * @e: serialized data extent information (NOT NULL) * @code: type code * @name: name to match to the serialized element. (MAYBE NULL) @@ -265,7 +226,7 @@ static bool unpack_X(struct aa_ext *e, enum aa_code code) * * Returns: false if either match fails, the read head does not move */ -static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) +VISIBLE_IF_KUNIT bool aa_unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) { /* * May need to reset pos if name or type doesn't match @@ -275,9 +236,9 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) * Check for presence of a tagname, and if present name size * AA_NAME tag value is a u16. */ - if (unpack_X(e, AA_NAME)) { + if (aa_unpack_X(e, AA_NAME)) { char *tag = NULL; - size_t size = unpack_u16_chunk(e, &tag); + size_t size = aa_unpack_u16_chunk(e, &tag); /* if a name is specified it must match. otherwise skip tag */ if (name && (!size || tag[size-1] != '\0' || strcmp(name, tag))) goto fail; @@ -287,20 +248,21 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) } /* now check if type code matches */ - if (unpack_X(e, code)) + if (aa_unpack_X(e, code)) return true; fail: e->pos = pos; return false; } +EXPORT_SYMBOL_IF_KUNIT(aa_unpack_nameX); static bool unpack_u8(struct aa_ext *e, u8 *data, const char *name) { void *pos = e->pos; - if (unpack_nameX(e, AA_U8, name)) { - if (!inbounds(e, sizeof(u8))) + if (aa_unpack_nameX(e, AA_U8, name)) { + if (!aa_inbounds(e, sizeof(u8))) goto fail; if (data) *data = *((u8 *)e->pos); @@ -313,12 +275,12 @@ fail: return false; } -static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) +VISIBLE_IF_KUNIT bool aa_unpack_u32(struct aa_ext *e, u32 *data, const char *name) { void *pos = e->pos; - if (unpack_nameX(e, AA_U32, name)) { - if (!inbounds(e, sizeof(u32))) + if (aa_unpack_nameX(e, AA_U32, name)) { + if (!aa_inbounds(e, sizeof(u32))) goto fail; if (data) *data = le32_to_cpu(get_unaligned((__le32 *) e->pos)); @@ -330,13 +292,14 @@ fail: e->pos = pos; return false; } +EXPORT_SYMBOL_IF_KUNIT(aa_unpack_u32); -static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name) +VISIBLE_IF_KUNIT bool aa_unpack_u64(struct aa_ext *e, u64 *data, const char *name) { void *pos = e->pos; - if (unpack_nameX(e, AA_U64, name)) { - if (!inbounds(e, sizeof(u64))) + if (aa_unpack_nameX(e, AA_U64, name)) { + if (!aa_inbounds(e, sizeof(u64))) goto fail; if (data) *data = le64_to_cpu(get_unaligned((__le64 *) e->pos)); @@ -348,36 +311,37 @@ fail: e->pos = pos; return false; } +EXPORT_SYMBOL_IF_KUNIT(aa_unpack_u64); -static tri unpack_array(struct aa_ext *e, const char *name, u16 *size) +VISIBLE_IF_KUNIT bool aa_unpack_array(struct aa_ext *e, const char *name, u16 *size) { void *pos = e->pos; - if (unpack_nameX(e, AA_ARRAY, name)) { - if (!inbounds(e, sizeof(u16))) + if (aa_unpack_nameX(e, AA_ARRAY, name)) { + if (!aa_inbounds(e, sizeof(u16))) goto fail; *size = le16_to_cpu(get_unaligned((__le16 *) e->pos)); e->pos += sizeof(u16); - return TRI_TRUE; + return true; } - return TRI_NONE; fail: e->pos = pos; - return TRI_FALSE; + return false; } +EXPORT_SYMBOL_IF_KUNIT(aa_unpack_array); -static size_t unpack_blob(struct aa_ext *e, char **blob, const char *name) +VISIBLE_IF_KUNIT size_t aa_unpack_blob(struct aa_ext *e, char **blob, const char *name) { void *pos = e->pos; - if (unpack_nameX(e, AA_BLOB, name)) { + if (aa_unpack_nameX(e, AA_BLOB, name)) { u32 size; - if (!inbounds(e, sizeof(u32))) + if (!aa_inbounds(e, sizeof(u32))) goto fail; size = le32_to_cpu(get_unaligned((__le32 *) e->pos)); e->pos += sizeof(u32); - if (inbounds(e, (size_t) size)) { + if (aa_inbounds(e, (size_t) size)) { *blob = e->pos; e->pos += size; return size; @@ -388,15 +352,16 @@ fail: e->pos = pos; return 0; } +EXPORT_SYMBOL_IF_KUNIT(aa_unpack_blob); -static int unpack_str(struct aa_ext *e, const char **string, const char *name) +VISIBLE_IF_KUNIT int aa_unpack_str(struct aa_ext *e, const char **string, const char *name) { char *src_str; size_t size = 0; void *pos = e->pos; *string = NULL; - if (unpack_nameX(e, AA_STRING, name)) { - size = unpack_u16_chunk(e, &src_str); + if (aa_unpack_nameX(e, AA_STRING, name)) { + size = aa_unpack_u16_chunk(e, &src_str); if (size) { /* strings are null terminated, length is size - 1 */ if (src_str[size - 1] != 0) @@ -411,12 +376,13 @@ fail: e->pos = pos; return 0; } +EXPORT_SYMBOL_IF_KUNIT(aa_unpack_str); -static int unpack_strdup(struct aa_ext *e, char **string, const char *name) +VISIBLE_IF_KUNIT int aa_unpack_strdup(struct aa_ext *e, char **string, const char *name) { const char *tmp; void *pos = e->pos; - int res = unpack_str(e, &tmp, name); + int res = aa_unpack_str(e, &tmp, name); *string = NULL; if (!res) @@ -430,6 +396,7 @@ static int unpack_strdup(struct aa_ext *e, char **string, const char *name) return res; } +EXPORT_SYMBOL_IF_KUNIT(aa_unpack_strdup); /** @@ -445,7 +412,7 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e, int flags) size_t size; struct aa_dfa *dfa = NULL; - size = unpack_blob(e, &blob, "aadfa"); + size = aa_unpack_blob(e, &blob, "aadfa"); if (size) { /* * The dfa is aligned with in the blob to 8 bytes @@ -480,11 +447,11 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_str_table *strs) char **table = NULL; /* exec table is optional */ - if (unpack_nameX(e, AA_STRUCT, "xtable")) { + if (aa_unpack_nameX(e, AA_STRUCT, "xtable")) { u16 size; int i; - if (unpack_array(e, NULL, &size) != TRI_TRUE) + if (!aa_unpack_array(e, NULL, &size)) /* * Note: index into trans table array is a max * of 2^24, but unpack array can only unpack @@ -498,8 +465,8 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_str_table *strs) for (i = 0; i < size; i++) { char *str; - int c, j, pos, size2 = unpack_strdup(e, &str, NULL); - /* unpack_strdup verifies that the last character is + int c, j, pos, size2 = aa_unpack_strdup(e, &str, NULL); + /* aa_unpack_strdup verifies that the last character is * null termination byte. */ if (!size2) @@ -522,7 +489,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_str_table *strs) goto fail; /* beginning with : requires an embedded \0, * verify that exactly 1 internal \0 exists - * trailing \0 already verified by unpack_strdup + * trailing \0 already verified by aa_unpack_strdup * * convert \0 back to : for label_parse */ @@ -534,9 +501,9 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_str_table *strs) /* fail - all other cases with embedded \0 */ goto fail; } - if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL)) goto fail; - if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; strs->table = table; @@ -554,23 +521,23 @@ static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile) { void *pos = e->pos; - if (unpack_nameX(e, AA_STRUCT, "xattrs")) { + if (aa_unpack_nameX(e, AA_STRUCT, "xattrs")) { u16 size; int i; - if (unpack_array(e, NULL, &size) != TRI_TRUE) + if (!aa_unpack_array(e, NULL, &size)) goto fail; profile->attach.xattr_count = size; profile->attach.xattrs = kcalloc(size, sizeof(char *), GFP_KERNEL); if (!profile->attach.xattrs) goto fail; for (i = 0; i < size; i++) { - if (!unpack_strdup(e, &profile->attach.xattrs[i], NULL)) + if (!aa_unpack_strdup(e, &profile->attach.xattrs[i], NULL)) goto fail; } - if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL)) goto fail; - if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; } @@ -587,8 +554,8 @@ static bool unpack_secmark(struct aa_ext *e, struct aa_ruleset *rules) u16 size; int i; - if (unpack_nameX(e, AA_STRUCT, "secmark")) { - if (unpack_array(e, NULL, &size) != TRI_TRUE) + if (aa_unpack_nameX(e, AA_STRUCT, "secmark")) { + if (!aa_unpack_array(e, NULL, &size)) goto fail; rules->secmark = kcalloc(size, sizeof(struct aa_secmark), @@ -603,12 +570,12 @@ static bool unpack_secmark(struct aa_ext *e, struct aa_ruleset *rules) goto fail; if (!unpack_u8(e, &rules->secmark[i].deny, NULL)) goto fail; - if (!unpack_strdup(e, &rules->secmark[i].label, NULL)) + if (!aa_unpack_strdup(e, &rules->secmark[i].label, NULL)) goto fail; } - if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL)) goto fail; - if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; } @@ -632,27 +599,27 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_ruleset *rules) void *pos = e->pos; /* rlimits are optional */ - if (unpack_nameX(e, AA_STRUCT, "rlimits")) { + if (aa_unpack_nameX(e, AA_STRUCT, "rlimits")) { u16 size; int i; u32 tmp = 0; - if (!unpack_u32(e, &tmp, NULL)) + if (!aa_unpack_u32(e, &tmp, NULL)) goto fail; rules->rlimits.mask = tmp; - if (unpack_array(e, NULL, &size) != TRI_TRUE || + if (!aa_unpack_array(e, NULL, &size) || size > RLIM_NLIMITS) goto fail; for (i = 0; i < size; i++) { u64 tmp2 = 0; int a = aa_map_resource(i); - if (!unpack_u64(e, &tmp2, NULL)) + if (!aa_unpack_u64(e, &tmp2, NULL)) goto fail; rules->rlimits.limits[a].rlim_max = tmp2; } - if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL)) goto fail; - if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; } return true; @@ -664,27 +631,23 @@ fail: static bool unpack_perm(struct aa_ext *e, u32 version, struct aa_perms *perm) { - bool res; - if (version != 1) return false; - res = unpack_u32(e, &perm->allow, NULL); - res = res && unpack_u32(e, &perm->allow, NULL); - res = res && unpack_u32(e, &perm->deny, NULL); - res = res && unpack_u32(e, &perm->subtree, NULL); - res = res && unpack_u32(e, &perm->cond, NULL); - res = res && unpack_u32(e, &perm->kill, NULL); - res = res && unpack_u32(e, &perm->complain, NULL); - res = res && unpack_u32(e, &perm->prompt, NULL); - res = res && unpack_u32(e, &perm->audit, NULL); - res = res && unpack_u32(e, &perm->quiet, NULL); - res = res && unpack_u32(e, &perm->hide, NULL); - res = res && unpack_u32(e, &perm->xindex, NULL); - res = res && unpack_u32(e, &perm->tag, NULL); - res = res && unpack_u32(e, &perm->label, NULL); - - return res; + return aa_unpack_u32(e, &perm->allow, NULL) && + aa_unpack_u32(e, &perm->allow, NULL) && + aa_unpack_u32(e, &perm->deny, NULL) && + aa_unpack_u32(e, &perm->subtree, NULL) && + aa_unpack_u32(e, &perm->cond, NULL) && + aa_unpack_u32(e, &perm->kill, NULL) && + aa_unpack_u32(e, &perm->complain, NULL) && + aa_unpack_u32(e, &perm->prompt, NULL) && + aa_unpack_u32(e, &perm->audit, NULL) && + aa_unpack_u32(e, &perm->quiet, NULL) && + aa_unpack_u32(e, &perm->hide, NULL) && + aa_unpack_u32(e, &perm->xindex, NULL) && + aa_unpack_u32(e, &perm->tag, NULL) && + aa_unpack_u32(e, &perm->label, NULL); } static ssize_t unpack_perms_table(struct aa_ext *e, struct aa_perms **perms) @@ -697,13 +660,13 @@ static ssize_t unpack_perms_table(struct aa_ext *e, struct aa_perms **perms) * policy perms are optional, in which case perms are embedded * in the dfa accept table */ - if (unpack_nameX(e, AA_STRUCT, "perms")) { + if (aa_unpack_nameX(e, AA_STRUCT, "perms")) { int i; u32 version; - if (!unpack_u32(e, &version, "version")) + if (!aa_unpack_u32(e, &version, "version")) goto fail_reset; - if (unpack_array(e, NULL, &size) != TRI_TRUE) + if (!aa_unpack_array(e, NULL, &size)) goto fail_reset; *perms = kcalloc(size, sizeof(struct aa_perms), GFP_KERNEL); if (!*perms) @@ -712,9 +675,9 @@ static ssize_t unpack_perms_table(struct aa_ext *e, struct aa_perms **perms) if (!unpack_perm(e, version, &(*perms)[i])) goto fail; } - if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL)) goto fail; - if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; } else *perms = NULL; @@ -774,10 +737,10 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb *policy, * sadly start was given different names for file and policydb * but since it is optional we can try both */ - if (!unpack_u32(e, &policy->start[0], "start")) + if (!aa_unpack_u32(e, &policy->start[0], "start")) /* default start state */ policy->start[0] = DFA_START; - if (!unpack_u32(e, &policy->start[AA_CLASS_FILE], "dfa_start")) { + if (!aa_unpack_u32(e, &policy->start[AA_CLASS_FILE], "dfa_start")) { /* default start state for xmatch and file dfa */ policy->start[AA_CLASS_FILE] = DFA_START; } /* setup class index */ @@ -839,9 +802,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) *ns_name = NULL; /* check that we have the right struct being passed */ - if (!unpack_nameX(e, AA_STRUCT, "profile")) + if (!aa_unpack_nameX(e, AA_STRUCT, "profile")) goto fail; - if (!unpack_str(e, &name, NULL)) + if (!aa_unpack_str(e, &name, NULL)) goto fail; if (*name == '\0') goto fail; @@ -866,10 +829,10 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) rules = list_first_entry(&profile->rules, typeof(*rules), list); /* profile renaming is optional */ - (void) unpack_str(e, &profile->rename, "rename"); + (void) aa_unpack_str(e, &profile->rename, "rename"); /* attachment string is optional */ - (void) unpack_str(e, &profile->attach.xmatch_str, "attach"); + (void) aa_unpack_str(e, &profile->attach.xmatch_str, "attach"); /* xmatch is optional and may be NULL */ error = unpack_pdb(e, &profile->attach.xmatch, false, false, &info); @@ -880,7 +843,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) /* neither xmatch_len not xmatch_perms are optional if xmatch is set */ if (profile->attach.xmatch.dfa) { - if (!unpack_u32(e, &tmp, NULL)) { + if (!aa_unpack_u32(e, &tmp, NULL)) { info = "missing xmatch len"; goto fail; } @@ -894,15 +857,15 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) } /* disconnected attachment string is optional */ - (void) unpack_str(e, &profile->disconnected, "disconnected"); + (void) aa_unpack_str(e, &profile->disconnected, "disconnected"); /* per profile debug flags (complain, audit) */ - if (!unpack_nameX(e, AA_STRUCT, "flags")) { + if (!aa_unpack_nameX(e, AA_STRUCT, "flags")) { info = "profile missing flags"; goto fail; } info = "failed to unpack profile flags"; - if (!unpack_u32(e, &tmp, NULL)) + if (!aa_unpack_u32(e, &tmp, NULL)) goto fail; if (tmp & PACKED_FLAG_HAT) profile->label.flags |= FLAG_HAT; @@ -910,7 +873,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) profile->label.flags |= FLAG_DEBUG1; if (tmp & PACKED_FLAG_DEBUG2) profile->label.flags |= FLAG_DEBUG2; - if (!unpack_u32(e, &tmp, NULL)) + if (!aa_unpack_u32(e, &tmp, NULL)) goto fail; if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG)) { profile->mode = APPARMOR_COMPLAIN; @@ -926,16 +889,16 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) } else { goto fail; } - if (!unpack_u32(e, &tmp, NULL)) + if (!aa_unpack_u32(e, &tmp, NULL)) goto fail; if (tmp) profile->audit = AUDIT_ALL; - if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; /* path_flags is optional */ - if (unpack_u32(e, &profile->path_flags, "path_flags")) + if (aa_unpack_u32(e, &profile->path_flags, "path_flags")) profile->path_flags |= profile->label.flags & PATH_MEDIATE_DELETED; else @@ -943,38 +906,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) profile->path_flags = PATH_MEDIATE_DELETED; info = "failed to unpack profile capabilities"; - if (!unpack_u32(e, &(rules->caps.allow.cap[0]), NULL)) + if (!aa_unpack_u32(e, &(rules->caps.allow.cap[0]), NULL)) goto fail; - if (!unpack_u32(e, &(rules->caps.audit.cap[0]), NULL)) + if (!aa_unpack_u32(e, &(rules->caps.audit.cap[0]), NULL)) goto fail; - if (!unpack_u32(e, &(rules->caps.quiet.cap[0]), NULL)) + if (!aa_unpack_u32(e, &(rules->caps.quiet.cap[0]), NULL)) goto fail; - if (!unpack_u32(e, &tmpcap.cap[0], NULL)) + if (!aa_unpack_u32(e, &tmpcap.cap[0], NULL)) goto fail; info = "failed to unpack upper profile capabilities"; - if (unpack_nameX(e, AA_STRUCT, "caps64")) { + if (aa_unpack_nameX(e, AA_STRUCT, "caps64")) { /* optional upper half of 64 bit caps */ - if (!unpack_u32(e, &(rules->caps.allow.cap[1]), NULL)) + if (!aa_unpack_u32(e, &(rules->caps.allow.cap[1]), NULL)) goto fail; - if (!unpack_u32(e, &(rules->caps.audit.cap[1]), NULL)) + if (!aa_unpack_u32(e, &(rules->caps.audit.cap[1]), NULL)) goto fail; - if (!unpack_u32(e, &(rules->caps.quiet.cap[1]), NULL)) + if (!aa_unpack_u32(e, &(rules->caps.quiet.cap[1]), NULL)) goto fail; - if (!unpack_u32(e, &(tmpcap.cap[1]), NULL)) + if (!aa_unpack_u32(e, &(tmpcap.cap[1]), NULL)) goto fail; - if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; } info = "failed to unpack extended profile capabilities"; - if (unpack_nameX(e, AA_STRUCT, "capsx")) { + if (aa_unpack_nameX(e, AA_STRUCT, "capsx")) { /* optional extended caps mediation mask */ - if (!unpack_u32(e, &(rules->caps.extended.cap[0]), NULL)) + if (!aa_unpack_u32(e, &(rules->caps.extended.cap[0]), NULL)) goto fail; - if (!unpack_u32(e, &(rules->caps.extended.cap[1]), NULL)) + if (!aa_unpack_u32(e, &(rules->caps.extended.cap[1]), NULL)) goto fail; - if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; } @@ -993,7 +956,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; } - if (unpack_nameX(e, AA_STRUCT, "policydb")) { + if (aa_unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ info = "failed to unpack policydb"; error = unpack_pdb(e, &rules->policy, true, false, @@ -1007,7 +970,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) aa_dfa_next(rules->policy.dfa, rules->policy.start[0], AA_CLASS_FILE); - if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; error = aa_compat_map_policy(&rules->policy, e->version); if (error) { @@ -1035,7 +998,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) rules->file.dfa = aa_get_dfa(nulldfa); error = -EPROTO; - if (unpack_nameX(e, AA_STRUCT, "data")) { + if (aa_unpack_nameX(e, AA_STRUCT, "data")) { info = "out of memory"; profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL); if (!profile->data) { @@ -1054,7 +1017,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; } - while (unpack_strdup(e, &key, NULL)) { + while (aa_unpack_strdup(e, &key, NULL)) { data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { kfree_sensitive(key); @@ -1063,7 +1026,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) } data->key = key; - data->size = unpack_blob(e, &data->data, NULL); + data->size = aa_unpack_blob(e, &data->data, NULL); data->data = kvmemdup(data->data, data->size); if (data->size && !data->data) { kfree_sensitive(data->key); @@ -1076,13 +1039,13 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) profile->data->p); } - if (!unpack_nameX(e, AA_STRUCTEND, NULL)) { + if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) { info = "failed to unpack end of key, value data table"; goto fail; } } - if (!unpack_nameX(e, AA_STRUCTEND, NULL)) { + if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) { info = "failed to unpack end of profile"; goto fail; } @@ -1122,7 +1085,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) *ns = NULL; /* get the interface version */ - if (!unpack_u32(e, &e->version, "version")) { + if (!aa_unpack_u32(e, &e->version, "version")) { if (required) { audit_iface(NULL, NULL, NULL, "invalid profile format", e, error); @@ -1141,7 +1104,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) } /* read the namespace if present */ - if (unpack_str(e, &name, "namespace")) { + if (aa_unpack_str(e, &name, "namespace")) { if (*name == '\0') { audit_iface(NULL, NULL, NULL, "invalid namespace name", e, error); @@ -1471,7 +1434,3 @@ fail: return error; } - -#ifdef CONFIG_SECURITY_APPARMOR_KUNIT_TEST -#include "policy_unpack_test.c" -#endif /* CONFIG_SECURITY_APPARMOR_KUNIT_TEST */ diff --git a/security/apparmor/policy_unpack_test.c b/security/apparmor/policy_unpack_test.c index 7465da42492d..e1bfdab524b7 100644 --- a/security/apparmor/policy_unpack_test.c +++ b/security/apparmor/policy_unpack_test.c @@ -4,6 +4,7 @@ */ #include <kunit/test.h> +#include <kunit/visibility.h> #include "include/policy.h" #include "include/policy_unpack.h" @@ -43,6 +44,8 @@ #define TEST_ARRAY_BUF_OFFSET \ (TEST_NAMED_ARRAY_BUF_OFFSET + 3 + strlen(TEST_ARRAY_NAME) + 1) +MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING); + struct policy_unpack_fixture { struct aa_ext *e; size_t e_size; @@ -125,16 +128,16 @@ static void policy_unpack_test_inbounds_when_inbounds(struct kunit *test) { struct policy_unpack_fixture *puf = test->priv; - KUNIT_EXPECT_TRUE(test, inbounds(puf->e, 0)); - KUNIT_EXPECT_TRUE(test, inbounds(puf->e, puf->e_size / 2)); - KUNIT_EXPECT_TRUE(test, inbounds(puf->e, puf->e_size)); + KUNIT_EXPECT_TRUE(test, aa_inbounds(puf->e, 0)); + KUNIT_EXPECT_TRUE(test, aa_inbounds(puf->e, puf->e_size / 2)); + KUNIT_EXPECT_TRUE(test, aa_inbounds(puf->e, puf->e_size)); } static void policy_unpack_test_inbounds_when_out_of_bounds(struct kunit *test) { struct policy_unpack_fixture *puf = test->priv; - KUNIT_EXPECT_FALSE(test, inbounds(puf->e, puf->e_size + 1)); + KUNIT_EXPECT_FALSE(test, aa_inbounds(puf->e, puf->e_size + 1)); } static void policy_unpack_test_unpack_array_with_null_name(struct kunit *test) @@ -144,8 +147,7 @@ static void policy_unpack_test_unpack_array_with_null_name(struct kunit *test) puf->e->pos += TEST_ARRAY_BUF_OFFSET; - KUNIT_EXPECT_EQ(test, unpack_array(puf->e, NULL, &array_size), - TRI_TRUE); + KUNIT_EXPECT_TRUE(test, aa_unpack_array(puf->e, NULL, &array_size)); KUNIT_EXPECT_EQ(test, array_size, (u16)TEST_ARRAY_SIZE); KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, puf->e->start + TEST_ARRAY_BUF_OFFSET + sizeof(u16) + 1); @@ -159,8 +161,7 @@ static void policy_unpack_test_unpack_array_with_name(struct kunit *test) puf->e->pos += TEST_NAMED_ARRAY_BUF_OFFSET; - KUNIT_EXPECT_EQ(test, unpack_array(puf->e, name, &array_size), - TRI_TRUE); + KUNIT_EXPECT_TRUE(test, aa_unpack_array(puf->e, name, &array_size)); KUNIT_EXPECT_EQ(test, array_size, (u16)TEST_ARRAY_SIZE); KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, puf->e->start + TEST_ARRAY_BUF_OFFSET + sizeof(u16) + 1); @@ -175,8 +176,7 @@ static void policy_unpack_test_unpack_array_out_of_bounds(struct kunit *test) puf->e->pos += TEST_NAMED_ARRAY_BUF_OFFSET; puf->e->end = puf->e->start + TEST_ARRAY_BUF_OFFSET + sizeof(u16); - KUNIT_EXPECT_EQ(test, unpack_array(puf->e, name, &array_size), - TRI_FALSE); + KUNIT_EXPECT_FALSE(test, aa_unpack_array(puf->e, name, &array_size)); KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, puf->e->start + TEST_NAMED_ARRAY_BUF_OFFSET); } @@ -188,7 +188,7 @@ static void policy_unpack_test_unpack_blob_with_null_name(struct kunit *test) size_t size; puf->e->pos += TEST_BLOB_BUF_OFFSET; - size = unpack_blob(puf->e, &blob, NULL); + size = aa_unpack_blob(puf->e, &blob, NULL); KUNIT_ASSERT_EQ(test, size, TEST_BLOB_DATA_SIZE); KUNIT_EXPECT_TRUE(test, @@ -202,7 +202,7 @@ static void policy_unpack_test_unpack_blob_with_name(struct kunit *test) size_t size; puf->e->pos += TEST_NAMED_BLOB_BUF_OFFSET; - size = unpack_blob(puf->e, &blob, TEST_BLOB_NAME); + size = aa_unpack_blob(puf->e, &blob, TEST_BLOB_NAME); KUNIT_ASSERT_EQ(test, size, TEST_BLOB_DATA_SIZE); KUNIT_EXPECT_TRUE(test, @@ -221,7 +221,7 @@ static void policy_unpack_test_unpack_blob_out_of_bounds(struct kunit *test) puf->e->end = puf->e->start + TEST_BLOB_BUF_OFFSET + TEST_BLOB_DATA_SIZE - 1; - size = unpack_blob(puf->e, &blob, TEST_BLOB_NAME); + size = aa_unpack_blob(puf->e, &blob, TEST_BLOB_NAME); KUNIT_EXPECT_EQ(test, size, 0); KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, start); @@ -234,7 +234,7 @@ static void policy_unpack_test_unpack_str_with_null_name(struct kunit *test) size_t size; puf->e->pos += TEST_STRING_BUF_OFFSET; - size = unpack_str(puf->e, &string, NULL); + size = aa_unpack_str(puf->e, &string, NULL); KUNIT_EXPECT_EQ(test, size, strlen(TEST_STRING_DATA) + 1); KUNIT_EXPECT_STREQ(test, string, TEST_STRING_DATA); @@ -246,7 +246,7 @@ static void policy_unpack_test_unpack_str_with_name(struct kunit *test) const char *string = NULL; size_t size; - size = unpack_str(puf->e, &string, TEST_STRING_NAME); + size = aa_unpack_str(puf->e, &string, TEST_STRING_NAME); KUNIT_EXPECT_EQ(test, size, strlen(TEST_STRING_DATA) + 1); KUNIT_EXPECT_STREQ(test, string, TEST_STRING_DATA); @@ -262,7 +262,7 @@ static void policy_unpack_test_unpack_str_out_of_bounds(struct kunit *test) puf->e->end = puf->e->pos + TEST_STRING_BUF_OFFSET + strlen(TEST_STRING_DATA) - 1; - size = unpack_str(puf->e, &string, TEST_STRING_NAME); + size = aa_unpack_str(puf->e, &string, TEST_STRING_NAME); KUNIT_EXPECT_EQ(test, size, 0); KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, start); @@ -275,7 +275,7 @@ static void policy_unpack_test_unpack_strdup_with_null_name(struct kunit *test) size_t size; puf->e->pos += TEST_STRING_BUF_OFFSET; - size = unpack_strdup(puf->e, &string, NULL); + size = aa_unpack_strdup(puf->e, &string, NULL); KUNIT_EXPECT_EQ(test, size, strlen(TEST_STRING_DATA) + 1); KUNIT_EXPECT_FALSE(test, @@ -290,7 +290,7 @@ static void policy_unpack_test_unpack_strdup_with_name(struct kunit *test) char *string = NULL; size_t size; - size = unpack_strdup(puf->e, &string, TEST_STRING_NAME); + size = aa_unpack_strdup(puf->e, &string, TEST_STRING_NAME); KUNIT_EXPECT_EQ(test, size, strlen(TEST_STRING_DATA) + 1); KUNIT_EXPECT_FALSE(test, @@ -309,7 +309,7 @@ static void policy_unpack_test_unpack_strdup_out_of_bounds(struct kunit *test) puf->e->end = puf->e->pos + TEST_STRING_BUF_OFFSET + strlen(TEST_STRING_DATA) - 1; - size = unpack_strdup(puf->e, &string, TEST_STRING_NAME); + size = aa_unpack_strdup(puf->e, &string, TEST_STRING_NAME); KUNIT_EXPECT_EQ(test, size, 0); KUNIT_EXPECT_NULL(test, string); @@ -323,7 +323,7 @@ static void policy_unpack_test_unpack_nameX_with_null_name(struct kunit *test) puf->e->pos += TEST_U32_BUF_OFFSET; - success = unpack_nameX(puf->e, AA_U32, NULL); + success = aa_unpack_nameX(puf->e, AA_U32, NULL); KUNIT_EXPECT_TRUE(test, success); KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, @@ -337,7 +337,7 @@ static void policy_unpack_test_unpack_nameX_with_wrong_code(struct kunit *test) puf->e->pos += TEST_U32_BUF_OFFSET; - success = unpack_nameX(puf->e, AA_BLOB, NULL); + success = aa_unpack_nameX(puf->e, AA_BLOB, NULL); KUNIT_EXPECT_FALSE(test, success); KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, @@ -352,7 +352,7 @@ static void policy_unpack_test_unpack_nameX_with_name(struct kunit *test) puf->e->pos += TEST_NAMED_U32_BUF_OFFSET; - success = unpack_nameX(puf->e, AA_U32, name); + success = aa_unpack_nameX(puf->e, AA_U32, name); KUNIT_EXPECT_TRUE(test, success); KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, @@ -367,7 +367,7 @@ static void policy_unpack_test_unpack_nameX_with_wrong_name(struct kunit *test) puf->e->pos += TEST_NAMED_U32_BUF_OFFSET; - success = unpack_nameX(puf->e, AA_U32, name); + success = aa_unpack_nameX(puf->e, AA_U32, name); KUNIT_EXPECT_FALSE(test, success); KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, @@ -388,7 +388,7 @@ static void policy_unpack_test_unpack_u16_chunk_basic(struct kunit *test) */ puf->e->end += TEST_U16_DATA; - size = unpack_u16_chunk(puf->e, &chunk); + size = aa_unpack_u16_chunk(puf->e, &chunk); KUNIT_EXPECT_PTR_EQ(test, chunk, puf->e->start + TEST_U16_OFFSET + 2); @@ -405,7 +405,7 @@ static void policy_unpack_test_unpack_u16_chunk_out_of_bounds_1( puf->e->pos = puf->e->end - 1; - size = unpack_u16_chunk(puf->e, &chunk); + size = aa_unpack_u16_chunk(puf->e, &chunk); KUNIT_EXPECT_EQ(test, size, 0); KUNIT_EXPECT_NULL(test, chunk); @@ -427,7 +427,7 @@ static void policy_unpack_test_unpack_u16_chunk_out_of_bounds_2( */ puf->e->end = puf->e->pos + TEST_U16_DATA - 1; - size = unpack_u16_chunk(puf->e, &chunk); + size = aa_unpack_u16_chunk(puf->e, &chunk); KUNIT_EXPECT_EQ(test, size, 0); KUNIT_EXPECT_NULL(test, chunk); @@ -442,7 +442,7 @@ static void policy_unpack_test_unpack_u32_with_null_name(struct kunit *test) puf->e->pos += TEST_U32_BUF_OFFSET; - success = unpack_u32(puf->e, &data, NULL); + success = aa_unpack_u32(puf->e, &data, NULL); KUNIT_EXPECT_TRUE(test, success); KUNIT_EXPECT_EQ(test, data, TEST_U32_DATA); @@ -459,7 +459,7 @@ static void policy_unpack_test_unpack_u32_with_name(struct kunit *test) puf->e->pos += TEST_NAMED_U32_BUF_OFFSET; - success = unpack_u32(puf->e, &data, name); + success = aa_unpack_u32(puf->e, &data, name); KUNIT_EXPECT_TRUE(test, success); KUNIT_EXPECT_EQ(test, data, TEST_U32_DATA); @@ -477,7 +477,7 @@ static void policy_unpack_test_unpack_u32_out_of_bounds(struct kunit *test) puf->e->pos += TEST_NAMED_U32_BUF_OFFSET; puf->e->end = puf->e->start + TEST_U32_BUF_OFFSET + sizeof(u32); - success = unpack_u32(puf->e, &data, name); + success = aa_unpack_u32(puf->e, &data, name); KUNIT_EXPECT_FALSE(test, success); KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, @@ -492,7 +492,7 @@ static void policy_unpack_test_unpack_u64_with_null_name(struct kunit *test) puf->e->pos += TEST_U64_BUF_OFFSET; - success = unpack_u64(puf->e, &data, NULL); + success = aa_unpack_u64(puf->e, &data, NULL); KUNIT_EXPECT_TRUE(test, success); KUNIT_EXPECT_EQ(test, data, TEST_U64_DATA); @@ -509,7 +509,7 @@ static void policy_unpack_test_unpack_u64_with_name(struct kunit *test) puf->e->pos += TEST_NAMED_U64_BUF_OFFSET; - success = unpack_u64(puf->e, &data, name); + success = aa_unpack_u64(puf->e, &data, name); KUNIT_EXPECT_TRUE(test, success); KUNIT_EXPECT_EQ(test, data, TEST_U64_DATA); @@ -527,7 +527,7 @@ static void policy_unpack_test_unpack_u64_out_of_bounds(struct kunit *test) puf->e->pos += TEST_NAMED_U64_BUF_OFFSET; puf->e->end = puf->e->start + TEST_U64_BUF_OFFSET + sizeof(u64); - success = unpack_u64(puf->e, &data, name); + success = aa_unpack_u64(puf->e, &data, name); KUNIT_EXPECT_FALSE(test, success); KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, @@ -537,7 +537,7 @@ static void policy_unpack_test_unpack_u64_out_of_bounds(struct kunit *test) static void policy_unpack_test_unpack_X_code_match(struct kunit *test) { struct policy_unpack_fixture *puf = test->priv; - bool success = unpack_X(puf->e, AA_NAME); + bool success = aa_unpack_X(puf->e, AA_NAME); KUNIT_EXPECT_TRUE(test, success); KUNIT_EXPECT_TRUE(test, puf->e->pos == puf->e->start + 1); @@ -546,7 +546,7 @@ static void policy_unpack_test_unpack_X_code_match(struct kunit *test) static void policy_unpack_test_unpack_X_code_mismatch(struct kunit *test) { struct policy_unpack_fixture *puf = test->priv; - bool success = unpack_X(puf->e, AA_STRING); + bool success = aa_unpack_X(puf->e, AA_STRING); KUNIT_EXPECT_FALSE(test, success); KUNIT_EXPECT_TRUE(test, puf->e->pos == puf->e->start); @@ -558,7 +558,7 @@ static void policy_unpack_test_unpack_X_out_of_bounds(struct kunit *test) bool success; puf->e->pos = puf->e->end; - success = unpack_X(puf->e, AA_NAME); + success = aa_unpack_X(puf->e, AA_NAME); KUNIT_EXPECT_FALSE(test, success); } @@ -604,3 +604,5 @@ static struct kunit_suite apparmor_policy_unpack_test_module = { }; kunit_test_suite(apparmor_policy_unpack_test_module); + +MODULE_LICENSE("GPL"); diff --git a/security/commoncap.c b/security/commoncap.c index 5fc8986c3c77..1164278b97fd 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -328,14 +328,16 @@ int cap_inode_killpriv(struct user_namespace *mnt_userns, struct dentry *dentry) return error; } -static bool rootid_owns_currentns(kuid_t kroot) +static bool rootid_owns_currentns(vfsuid_t rootvfsuid) { struct user_namespace *ns; + kuid_t kroot; - if (!uid_valid(kroot)) + if (!vfsuid_valid(rootvfsuid)) return false; - for (ns = current_user_ns(); ; ns = ns->parent) { + kroot = vfsuid_into_kuid(rootvfsuid); + for (ns = current_user_ns();; ns = ns->parent) { if (from_kuid(ns, kroot) == 0) return true; if (ns == &init_user_ns) @@ -350,14 +352,14 @@ static __u32 sansflags(__u32 m) return m & ~VFS_CAP_FLAGS_EFFECTIVE; } -static bool is_v2header(size_t size, const struct vfs_cap_data *cap) +static bool is_v2header(int size, const struct vfs_cap_data *cap) { if (size != XATTR_CAPS_SZ_2) return false; return sansflags(le32_to_cpu(cap->magic_etc)) == VFS_CAP_REVISION_2; } -static bool is_v3header(size_t size, const struct vfs_cap_data *cap) +static bool is_v3header(int size, const struct vfs_cap_data *cap) { if (size != XATTR_CAPS_SZ_3) return false; @@ -379,8 +381,9 @@ int cap_inode_getsecurity(struct user_namespace *mnt_userns, struct inode *inode, const char *name, void **buffer, bool alloc) { - int size, ret; + int size; kuid_t kroot; + vfsuid_t vfsroot; u32 nsmagic, magic; uid_t root, mappedroot; char *tmpbuf = NULL; @@ -395,20 +398,18 @@ int cap_inode_getsecurity(struct user_namespace *mnt_userns, dentry = d_find_any_alias(inode); if (!dentry) return -EINVAL; - - size = sizeof(struct vfs_ns_cap_data); - ret = (int)vfs_getxattr_alloc(mnt_userns, dentry, XATTR_NAME_CAPS, - &tmpbuf, size, GFP_NOFS); + size = vfs_getxattr_alloc(mnt_userns, dentry, XATTR_NAME_CAPS, &tmpbuf, + sizeof(struct vfs_ns_cap_data), GFP_NOFS); dput(dentry); - - if (ret < 0 || !tmpbuf) - return ret; + /* gcc11 complains if we don't check for !tmpbuf */ + if (size < 0 || !tmpbuf) + goto out_free; fs_ns = inode->i_sb->s_user_ns; cap = (struct vfs_cap_data *) tmpbuf; - if (is_v2header((size_t) ret, cap)) { + if (is_v2header(size, cap)) { root = 0; - } else if (is_v3header((size_t) ret, cap)) { + } else if (is_v3header(size, cap)) { nscap = (struct vfs_ns_cap_data *) tmpbuf; root = le32_to_cpu(nscap->rootid); } else { @@ -419,11 +420,11 @@ int cap_inode_getsecurity(struct user_namespace *mnt_userns, kroot = make_kuid(fs_ns, root); /* If this is an idmapped mount shift the kuid. */ - kroot = mapped_kuid_fs(mnt_userns, fs_ns, kroot); + vfsroot = make_vfsuid(mnt_userns, fs_ns, kroot); /* If the root kuid maps to a valid uid in current ns, then return * this as a nscap. */ - mappedroot = from_kuid(current_user_ns(), kroot); + mappedroot = from_kuid(current_user_ns(), vfsuid_into_kuid(vfsroot)); if (mappedroot != (uid_t)-1 && mappedroot != (uid_t)0) { size = sizeof(struct vfs_ns_cap_data); if (alloc) { @@ -450,7 +451,7 @@ int cap_inode_getsecurity(struct user_namespace *mnt_userns, goto out_free; } - if (!rootid_owns_currentns(kroot)) { + if (!rootid_owns_currentns(vfsroot)) { size = -EOVERFLOW; goto out_free; } @@ -488,29 +489,17 @@ out_free: * @value: vfs caps value which may be modified by this function * @size: size of @ivalue * @task_ns: user namespace of the caller - * @mnt_userns: user namespace of the mount the inode was found from - * @fs_userns: user namespace of the filesystem - * - * If the inode has been found through an idmapped mount the user namespace of - * the vfsmount must be passed through @mnt_userns. This function will then - * take care to map the inode according to @mnt_userns before checking - * permissions. On non-idmapped mounts or if permission checking is to be - * performed on the raw inode simply passs init_user_ns. */ -static kuid_t rootid_from_xattr(const void *value, size_t size, - struct user_namespace *task_ns, - struct user_namespace *mnt_userns, - struct user_namespace *fs_userns) +static vfsuid_t rootid_from_xattr(const void *value, size_t size, + struct user_namespace *task_ns) { const struct vfs_ns_cap_data *nscap = value; - kuid_t rootkid; uid_t rootid = 0; if (size == XATTR_CAPS_SZ_3) rootid = le32_to_cpu(nscap->rootid); - rootkid = make_kuid(task_ns, rootid); - return mapped_kuid_user(mnt_userns, fs_userns, rootkid); + return VFSUIDT_INIT(make_kuid(task_ns, rootid)); } static bool validheader(size_t size, const struct vfs_cap_data *cap) @@ -548,6 +537,7 @@ int cap_convert_nscap(struct user_namespace *mnt_userns, struct dentry *dentry, struct user_namespace *task_ns = current_user_ns(), *fs_ns = inode->i_sb->s_user_ns; kuid_t rootid; + vfsuid_t vfsrootid; size_t newsize; if (!*ivalue) @@ -561,7 +551,11 @@ int cap_convert_nscap(struct user_namespace *mnt_userns, struct dentry *dentry, /* user is privileged, just write the v2 */ return size; - rootid = rootid_from_xattr(*ivalue, size, task_ns, mnt_userns, fs_ns); + vfsrootid = rootid_from_xattr(*ivalue, size, task_ns); + if (!vfsuid_valid(vfsrootid)) + return -EINVAL; + + rootid = from_vfsuid(mnt_userns, fs_ns, vfsrootid); if (!uid_valid(rootid)) return -EINVAL; @@ -655,6 +649,7 @@ int get_vfs_caps_from_disk(struct user_namespace *mnt_userns, struct vfs_ns_cap_data data, *nscaps = &data; struct vfs_cap_data *caps = (struct vfs_cap_data *) &data; kuid_t rootkuid; + vfsuid_t rootvfsuid; struct user_namespace *fs_ns; memset(cpu_caps, 0, sizeof(struct cpu_vfs_cap_data)); @@ -699,11 +694,15 @@ int get_vfs_caps_from_disk(struct user_namespace *mnt_userns, default: return -EINVAL; } + + rootvfsuid = make_vfsuid(mnt_userns, fs_ns, rootkuid); + if (!vfsuid_valid(rootvfsuid)) + return -ENODATA; + /* Limit the caps to the mounter of the filesystem * or the more limited uid specified in the xattr. */ - rootkuid = mapped_kuid_fs(mnt_userns, fs_ns, rootkuid); - if (!rootid_owns_currentns(rootkuid)) + if (!rootid_owns_currentns(rootvfsuid)) return -ENODATA; CAP_FOR_EACH_U32(i) { @@ -716,7 +715,7 @@ int get_vfs_caps_from_disk(struct user_namespace *mnt_userns, cpu_caps->permitted.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; cpu_caps->inheritable.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; - cpu_caps->rootid = rootkuid; + cpu_caps->rootid = vfsuid_into_kuid(rootvfsuid); return 0; } diff --git a/security/device_cgroup.c b/security/device_cgroup.c index a9f8c63a96d1..bef2b9285fb3 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -82,6 +82,17 @@ free_and_exit: return -ENOMEM; } +static void dev_exceptions_move(struct list_head *dest, struct list_head *orig) +{ + struct dev_exception_item *ex, *tmp; + + lockdep_assert_held(&devcgroup_mutex); + + list_for_each_entry_safe(ex, tmp, orig, list) { + list_move_tail(&ex->list, dest); + } +} + /* * called under devcgroup_mutex */ @@ -604,11 +615,13 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, int count, rc = 0; struct dev_exception_item ex; struct dev_cgroup *parent = css_to_devcgroup(devcgroup->css.parent); + struct dev_cgroup tmp_devcgrp; if (!capable(CAP_SYS_ADMIN)) return -EPERM; memset(&ex, 0, sizeof(ex)); + memset(&tmp_devcgrp, 0, sizeof(tmp_devcgrp)); b = buffer; switch (*b) { @@ -620,15 +633,27 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, if (!may_allow_all(parent)) return -EPERM; - dev_exception_clean(devcgroup); - devcgroup->behavior = DEVCG_DEFAULT_ALLOW; - if (!parent) + if (!parent) { + devcgroup->behavior = DEVCG_DEFAULT_ALLOW; + dev_exception_clean(devcgroup); break; + } + INIT_LIST_HEAD(&tmp_devcgrp.exceptions); + rc = dev_exceptions_copy(&tmp_devcgrp.exceptions, + &devcgroup->exceptions); + if (rc) + return rc; + dev_exception_clean(devcgroup); rc = dev_exceptions_copy(&devcgroup->exceptions, &parent->exceptions); - if (rc) + if (rc) { + dev_exceptions_move(&devcgroup->exceptions, + &tmp_devcgrp.exceptions); return rc; + } + devcgroup->behavior = DEVCG_DEFAULT_ALLOW; + dev_exception_clean(&tmp_devcgrp); break; case DEVCG_DENY: if (css_has_online_children(&devcgroup->css)) diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 8a82a6c7f48a..f2193c531f4a 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -126,6 +126,7 @@ int __init integrity_init_keyring(const unsigned int id) { struct key_restriction *restriction; key_perm_t perm; + int ret; perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH; @@ -154,7 +155,10 @@ int __init integrity_init_keyring(const unsigned int id) perm |= KEY_USR_WRITE; out: - return __integrity_init_keyring(id, perm, restriction); + ret = __integrity_init_keyring(id, perm, restriction); + if (ret) + kfree(restriction); + return ret; } static int __init integrity_add_key(const unsigned int id, const void *data, diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 708de9656bbd..fa5ff13fa8c9 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -335,14 +335,15 @@ static int evm_is_immutable(struct dentry *dentry, struct inode *inode) (char **)&xattr_data, 0, GFP_NOFS); if (rc <= 0) { if (rc == -ENODATA) - return 0; - return rc; + rc = 0; + goto out; } if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) rc = 1; else rc = 0; +out: kfree(xattr_data); return rc; } diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 2e6fb6e2ffd2..f02e609460e2 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -8,7 +8,7 @@ * * File: evm_main.c * implements evm_inode_setxattr, evm_inode_post_setxattr, - * evm_inode_removexattr, and evm_verifyxattr + * evm_inode_removexattr, evm_verifyxattr, and evm_inode_set_acl. */ #define pr_fmt(fmt) "EVM: "fmt @@ -435,55 +435,6 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry) } /* - * evm_xattr_acl_change - check if passed ACL changes the inode mode - * @mnt_userns: user namespace of the idmapped mount - * @dentry: pointer to the affected dentry - * @xattr_name: requested xattr - * @xattr_value: requested xattr value - * @xattr_value_len: requested xattr value length - * - * Check if passed ACL changes the inode mode, which is protected by EVM. - * - * Returns 1 if passed ACL causes inode mode change, 0 otherwise. - */ -static int evm_xattr_acl_change(struct user_namespace *mnt_userns, - struct dentry *dentry, const char *xattr_name, - const void *xattr_value, size_t xattr_value_len) -{ -#ifdef CONFIG_FS_POSIX_ACL - umode_t mode; - struct posix_acl *acl = NULL, *acl_res; - struct inode *inode = d_backing_inode(dentry); - int rc; - - /* - * user_ns is not relevant here, ACL_USER/ACL_GROUP don't have impact - * on the inode mode (see posix_acl_equiv_mode()). - */ - acl = posix_acl_from_xattr(&init_user_ns, xattr_value, xattr_value_len); - if (IS_ERR_OR_NULL(acl)) - return 1; - - acl_res = acl; - /* - * Passing mnt_userns is necessary to correctly determine the GID in - * an idmapped mount, as the GID is used to clear the setgid bit in - * the inode mode. - */ - rc = posix_acl_update_mode(mnt_userns, inode, &mode, &acl_res); - - posix_acl_release(acl); - - if (rc) - return 1; - - if (inode->i_mode != mode) - return 1; -#endif - return 0; -} - -/* * evm_xattr_change - check if passed xattr value differs from current value * @mnt_userns: user namespace of the idmapped mount * @dentry: pointer to the affected dentry @@ -502,20 +453,19 @@ static int evm_xattr_change(struct user_namespace *mnt_userns, char *xattr_data = NULL; int rc = 0; - if (posix_xattr_acl(xattr_name)) - return evm_xattr_acl_change(mnt_userns, dentry, xattr_name, - xattr_value, xattr_value_len); - rc = vfs_getxattr_alloc(&init_user_ns, dentry, xattr_name, &xattr_data, 0, GFP_NOFS); - if (rc < 0) - return 1; + if (rc < 0) { + rc = 1; + goto out; + } if (rc == xattr_value_len) rc = !!memcmp(xattr_value, xattr_data, rc); else rc = 1; +out: kfree(xattr_data); return rc; } @@ -659,6 +609,86 @@ int evm_inode_removexattr(struct user_namespace *mnt_userns, return evm_protect_xattr(mnt_userns, dentry, xattr_name, NULL, 0); } +#ifdef CONFIG_FS_POSIX_ACL +static int evm_inode_set_acl_change(struct user_namespace *mnt_userns, + struct dentry *dentry, const char *name, + struct posix_acl *kacl) +{ + int rc; + + umode_t mode; + struct inode *inode = d_backing_inode(dentry); + + if (!kacl) + return 1; + + rc = posix_acl_update_mode(mnt_userns, inode, &mode, &kacl); + if (rc || (inode->i_mode != mode)) + return 1; + + return 0; +} +#else +static inline int evm_inode_set_acl_change(struct user_namespace *mnt_userns, + struct dentry *dentry, + const char *name, + struct posix_acl *kacl) +{ + return 0; +} +#endif + +/** + * evm_inode_set_acl - protect the EVM extended attribute from posix acls + * @mnt_userns: user namespace of the idmapped mount + * @dentry: pointer to the affected dentry + * @acl_name: name of the posix acl + * @kacl: pointer to the posix acls + * + * Prevent modifying posix acls causing the EVM HMAC to be re-calculated + * and 'security.evm' xattr updated, unless the existing 'security.evm' is + * valid. + */ +int evm_inode_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry, + const char *acl_name, struct posix_acl *kacl) +{ + enum integrity_status evm_status; + + /* Policy permits modification of the protected xattrs even though + * there's no HMAC key loaded + */ + if (evm_initialized & EVM_ALLOW_METADATA_WRITES) + return 0; + + evm_status = evm_verify_current_integrity(dentry); + if ((evm_status == INTEGRITY_PASS) || + (evm_status == INTEGRITY_NOXATTRS)) + return 0; + + /* Exception if the HMAC is not going to be calculated. */ + if (evm_hmac_disabled() && (evm_status == INTEGRITY_NOLABEL || + evm_status == INTEGRITY_UNKNOWN)) + return 0; + + /* + * Writing other xattrs is safe for portable signatures, as portable + * signatures are immutable and can never be updated. + */ + if (evm_status == INTEGRITY_FAIL_IMMUTABLE) + return 0; + + if (evm_status == INTEGRITY_PASS_IMMUTABLE && + !evm_inode_set_acl_change(mnt_userns, dentry, acl_name, kacl)) + return 0; + + if (evm_status != INTEGRITY_PASS_IMMUTABLE) + integrity_audit_msg(AUDIT_INTEGRITY_METADATA, d_backing_inode(dentry), + dentry->d_name.name, "appraise_metadata", + integrity_status_msg[evm_status], + -EPERM, 0); + return -EPERM; +} + static void evm_reset_status(struct inode *inode) { struct integrity_iint_cache *iint; diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 7249f16257c7..39caeca47444 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -112,7 +112,7 @@ choice config IMA_DEFAULT_HASH_SM3 bool "SM3" - depends on CRYPTO_SM3=y + depends on CRYPTO_SM3_GENERIC=y endchoice config IMA_DEFAULT_HASH diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index be965a8715e4..03b440921e61 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -326,7 +326,7 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, 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); + struct evm_ima_xattr_data **xattr_value, int xattr_len); #else static inline int ima_check_blacklist(struct integrity_iint_cache *iint, @@ -372,7 +372,8 @@ ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len) } static inline int ima_read_xattr(struct dentry *dentry, - struct evm_ima_xattr_data **xattr_value) + struct evm_ima_xattr_data **xattr_value, + int xattr_len) { return 0; } diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index bde74fcecee3..ee6f7e237f2e 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -221,12 +221,12 @@ enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value, } int ima_read_xattr(struct dentry *dentry, - struct evm_ima_xattr_data **xattr_value) + struct evm_ima_xattr_data **xattr_value, int xattr_len) { - ssize_t ret; + int ret; ret = vfs_getxattr_alloc(&init_user_ns, dentry, XATTR_NAME_IMA, - (char **)xattr_value, 0, GFP_NOFS); + (char **)xattr_value, xattr_len, GFP_NOFS); if (ret == -EOPNOTSUPP) ret = 0; return ret; @@ -750,26 +750,39 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, const struct evm_ima_xattr_data *xvalue = xattr_value; int digsig = 0; int result; + int err; result = ima_protect_xattr(dentry, xattr_name, xattr_value, xattr_value_len); if (result == 1) { if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST)) return -EINVAL; + + err = validate_hash_algo(dentry, xvalue, xattr_value_len); + if (err) + return err; + digsig = (xvalue->type == EVM_IMA_XATTR_DIGSIG); } else if (!strcmp(xattr_name, XATTR_NAME_EVM) && xattr_value_len > 0) { digsig = (xvalue->type == EVM_XATTR_PORTABLE_DIGSIG); } if (result == 1 || evm_revalidate_status(xattr_name)) { - result = validate_hash_algo(dentry, xvalue, xattr_value_len); - if (result) - return result; - ima_reset_appraise_flags(d_backing_inode(dentry), digsig); + if (result == 1) + result = 0; } return result; } +int ima_inode_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry, + const char *acl_name, struct posix_acl *kacl) +{ + if (evm_revalidate_status(acl_name)) + ima_reset_appraise_flags(d_backing_inode(dentry), 0); + + return 0; +} + int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name) { int result; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 040b03ddc1c7..377300973e6c 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -293,7 +293,8 @@ static int process_measurement(struct file *file, const struct cred *cred, /* HASH sets the digital signature and update flags, nothing else */ if ((action & IMA_HASH) && !(test_bit(IMA_DIGSIG, &iint->atomic_flags))) { - xattr_len = ima_read_xattr(file_dentry(file), &xattr_value); + xattr_len = ima_read_xattr(file_dentry(file), + &xattr_value, xattr_len); if ((xattr_value && xattr_len > 2) && (xattr_value->type == EVM_IMA_XATTR_DIGSIG)) set_bit(IMA_DIGSIG, &iint->atomic_flags); @@ -316,7 +317,8 @@ static int process_measurement(struct file *file, const struct cred *cred, if ((action & IMA_APPRAISE_SUBMASK) || strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0) { /* read 'security.ima' */ - xattr_len = ima_read_xattr(file_dentry(file), &xattr_value); + xattr_len = ima_read_xattr(file_dentry(file), + &xattr_value, xattr_len); /* * Read the appended modsig if allowed by the policy, and allow @@ -542,8 +544,13 @@ static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf, rc = ima_collect_measurement(&tmp_iint, file, NULL, 0, ima_hash_algo, NULL); - if (rc < 0) + if (rc < 0) { + /* ima_hash could be allocated in case of failure. */ + if (rc != -ENOMEM) + kfree(tmp_iint.ima_hash); + return -EOPNOTSUPP; + } iint = &tmp_iint; mutex_lock(&iint->mutex); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index a8802b8da946..20cffd32dc9e 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -85,8 +85,8 @@ struct ima_rule_entry { kgid_t fgroup; bool (*uid_op)(kuid_t cred_uid, kuid_t rule_uid); /* Handlers for operators */ bool (*gid_op)(kgid_t cred_gid, kgid_t rule_gid); - bool (*fowner_op)(kuid_t cred_uid, kuid_t rule_uid); /* uid_eq(), uid_gt(), uid_lt() */ - bool (*fgroup_op)(kgid_t cred_gid, kgid_t rule_gid); /* gid_eq(), gid_gt(), gid_lt() */ + bool (*fowner_op)(vfsuid_t vfsuid, kuid_t rule_uid); /* vfsuid_eq_kuid(), vfsuid_gt_kuid(), vfsuid_lt_kuid() */ + bool (*fgroup_op)(vfsgid_t vfsgid, kgid_t rule_gid); /* vfsgid_eq_kgid(), vfsgid_gt_kgid(), vfsgid_lt_kgid() */ int pcr; unsigned int allowed_algos; /* bitfield of allowed hash algorithms */ struct { @@ -186,11 +186,11 @@ static struct ima_rule_entry default_appraise_rules[] __ro_after_init = { .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, #endif #ifndef CONFIG_IMA_APPRAISE_SIGNED_INIT - {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .fowner_op = &uid_eq, + {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .fowner_op = &vfsuid_eq_kuid, .flags = IMA_FOWNER}, #else /* force signature */ - {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .fowner_op = &uid_eq, + {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .fowner_op = &vfsuid_eq_kuid, .flags = IMA_FOWNER | IMA_DIGSIG_REQUIRED}, #endif }; @@ -398,12 +398,6 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry) nentry->lsm[i].type = entry->lsm[i].type; nentry->lsm[i].args_p = entry->lsm[i].args_p; - /* - * Remove the reference from entry so that the associated - * memory will not be freed during a later call to - * ima_lsm_free_rule(entry). - */ - entry->lsm[i].args_p = NULL; ima_filter_rule_init(nentry->lsm[i].type, Audit_equal, nentry->lsm[i].args_p, @@ -417,6 +411,7 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry) static int ima_lsm_update_rule(struct ima_rule_entry *entry) { + int i; struct ima_rule_entry *nentry; nentry = ima_lsm_copy_rule(entry); @@ -431,7 +426,8 @@ static int ima_lsm_update_rule(struct ima_rule_entry *entry) * references and the entry itself. All other memory references will now * be owned by nentry. */ - ima_lsm_free_rule(entry); + for (i = 0; i < MAX_LSM_RULES; i++) + ima_filter_rule_free(entry->lsm[i].rule); kfree(entry); return 0; @@ -549,6 +545,9 @@ static bool ima_match_rules(struct ima_rule_entry *rule, const char *func_data) { int i; + bool result = false; + struct ima_rule_entry *lsm_rule = rule; + bool rule_reinitialized = false; if ((rule->flags & IMA_FUNC) && (rule->func != func && func != POST_SETATTR)) @@ -601,44 +600,66 @@ static bool ima_match_rules(struct ima_rule_entry *rule, return false; } if ((rule->flags & IMA_FOWNER) && - !rule->fowner_op(i_uid_into_mnt(mnt_userns, inode), rule->fowner)) + !rule->fowner_op(i_uid_into_vfsuid(mnt_userns, inode), + rule->fowner)) return false; if ((rule->flags & IMA_FGROUP) && - !rule->fgroup_op(i_gid_into_mnt(mnt_userns, inode), rule->fgroup)) + !rule->fgroup_op(i_gid_into_vfsgid(mnt_userns, inode), + rule->fgroup)) return false; for (i = 0; i < MAX_LSM_RULES; i++) { int rc = 0; u32 osid; - if (!rule->lsm[i].rule) { - if (!rule->lsm[i].args_p) + if (!lsm_rule->lsm[i].rule) { + if (!lsm_rule->lsm[i].args_p) continue; else return false; } + +retry: switch (i) { case LSM_OBJ_USER: case LSM_OBJ_ROLE: case LSM_OBJ_TYPE: security_inode_getsecid(inode, &osid); - rc = ima_filter_rule_match(osid, rule->lsm[i].type, + rc = ima_filter_rule_match(osid, lsm_rule->lsm[i].type, Audit_equal, - rule->lsm[i].rule); + lsm_rule->lsm[i].rule); break; case LSM_SUBJ_USER: case LSM_SUBJ_ROLE: case LSM_SUBJ_TYPE: - rc = ima_filter_rule_match(secid, rule->lsm[i].type, + rc = ima_filter_rule_match(secid, lsm_rule->lsm[i].type, Audit_equal, - rule->lsm[i].rule); + lsm_rule->lsm[i].rule); break; default: break; } - if (!rc) - return false; + + if (rc == -ESTALE && !rule_reinitialized) { + lsm_rule = ima_lsm_copy_rule(rule); + if (lsm_rule) { + rule_reinitialized = true; + goto retry; + } + } + if (!rc) { + result = false; + goto out; + } } - return true; + result = true; + +out: + if (rule_reinitialized) { + for (i = 0; i < MAX_LSM_RULES; i++) + ima_filter_rule_free(lsm_rule->lsm[i].rule); + kfree(lsm_rule); + } + return result; } /* @@ -1371,8 +1392,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) entry->fgroup = INVALID_GID; entry->uid_op = &uid_eq; entry->gid_op = &gid_eq; - entry->fowner_op = &uid_eq; - entry->fgroup_op = &gid_eq; + entry->fowner_op = &vfsuid_eq_kuid; + entry->fgroup_op = &vfsgid_eq_kgid; entry->action = UNKNOWN; while ((p = strsep(&rule, " \t")) != NULL) { substring_t args[MAX_OPT_ARGS]; @@ -1650,11 +1671,11 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) } break; case Opt_fowner_gt: - entry->fowner_op = &uid_gt; + entry->fowner_op = &vfsuid_gt_kuid; fallthrough; case Opt_fowner_lt: if (token == Opt_fowner_lt) - entry->fowner_op = &uid_lt; + entry->fowner_op = &vfsuid_lt_kuid; fallthrough; case Opt_fowner_eq: ima_log_string_op(ab, "fowner", args[0].from, token); @@ -1676,11 +1697,11 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) } break; case Opt_fgroup_gt: - entry->fgroup_op = &gid_gt; + entry->fgroup_op = &vfsgid_gt_kgid; fallthrough; case Opt_fgroup_lt: if (token == Opt_fgroup_lt) - entry->fgroup_op = &gid_lt; + entry->fgroup_op = &vfsgid_lt_kgid; fallthrough; case Opt_fgroup_eq: ima_log_string_op(ab, "fgroup", args[0].from, token); @@ -2151,9 +2172,9 @@ int ima_policy_show(struct seq_file *m, void *v) if (entry->flags & IMA_FOWNER) { snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->fowner)); - if (entry->fowner_op == &uid_gt) + if (entry->fowner_op == &vfsuid_gt_kuid) seq_printf(m, pt(Opt_fowner_gt), tbuf); - else if (entry->fowner_op == &uid_lt) + else if (entry->fowner_op == &vfsuid_lt_kuid) seq_printf(m, pt(Opt_fowner_lt), tbuf); else seq_printf(m, pt(Opt_fowner_eq), tbuf); @@ -2162,9 +2183,9 @@ int ima_policy_show(struct seq_file *m, void *v) if (entry->flags & IMA_FGROUP) { snprintf(tbuf, sizeof(tbuf), "%d", __kgid_val(entry->fgroup)); - if (entry->fgroup_op == &gid_gt) + if (entry->fgroup_op == &vfsgid_gt_kgid) seq_printf(m, pt(Opt_fgroup_gt), tbuf); - else if (entry->fgroup_op == &gid_lt) + else if (entry->fgroup_op == &vfsgid_lt_kgid) seq_printf(m, pt(Opt_fgroup_lt), tbuf); else seq_printf(m, pt(Opt_fgroup_eq), tbuf); diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index c25079faa208..04c49f05cb74 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -245,11 +245,11 @@ int template_desc_init_fields(const char *template_fmt, } if (fields && num_fields) { - *fields = kmalloc_array(i, sizeof(*fields), GFP_KERNEL); + *fields = kmalloc_array(i, sizeof(**fields), GFP_KERNEL); if (*fields == NULL) return -ENOMEM; - memcpy(*fields, found_fields, i * sizeof(*fields)); + memcpy(*fields, found_fields, i * sizeof(**fields)); *num_fields = i; } @@ -340,8 +340,11 @@ static struct ima_template_desc *restore_template_fmt(char *template_name) template_desc->name = ""; template_desc->fmt = kstrdup(template_name, GFP_KERNEL); - if (!template_desc->fmt) + if (!template_desc->fmt) { + kfree(template_desc); + template_desc = NULL; goto out; + } spin_lock(&template_list); list_add_tail_rcu(&template_desc->list, &defined_templates); diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c index 7bf9b1507220..4564faae7d67 100644 --- a/security/integrity/ima/ima_template_lib.c +++ b/security/integrity/ima/ima_template_lib.c @@ -601,16 +601,15 @@ int ima_eventevmsig_init(struct ima_event_data *event_data, rc = vfs_getxattr_alloc(&init_user_ns, file_dentry(event_data->file), XATTR_NAME_EVM, (char **)&xattr_data, 0, GFP_NOFS); - if (rc <= 0) - return 0; - - if (xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG) { - kfree(xattr_data); - return 0; + if (rc <= 0 || xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG) { + rc = 0; + goto out; } rc = ima_write_template_field_data((char *)xattr_data, rc, DATA_FMT_HEX, field_data); + +out: kfree(xattr_data); return rc; } diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c index 093894a640dc..d1fdd113450a 100644 --- a/security/integrity/platform_certs/load_uefi.c +++ b/security/integrity/platform_certs/load_uefi.c @@ -31,10 +31,11 @@ static const struct dmi_system_id uefi_skip_cert[] = { { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookAir8,1") }, { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookAir8,2") }, { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookAir9,1") }, - { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacMini8,1") }, + { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "Macmini8,1") }, { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacPro7,1") }, { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "iMac20,1") }, { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "iMac20,2") }, + { UEFI_QUIRK_SKIP_CERT("Apple Inc.", "iMacPro1,1") }, { } }; diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index e05cfc2e49ae..1e313982af02 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -627,7 +627,7 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key, pr_err("encrypted key: instantiation of keys using provided decrypted data is disabled since CONFIG_USER_DECRYPTED_DATA is set to false\n"); return ERR_PTR(-EINVAL); } - if (strlen(decrypted_data) != decrypted_datalen) { + if (strlen(decrypted_data) != decrypted_datalen * 2) { pr_err("encrypted key: decrypted data provided does not match decrypted data length provided\n"); return ERR_PTR(-EINVAL); } @@ -791,8 +791,8 @@ static int encrypted_init(struct encrypted_key_payload *epayload, ret = encrypted_key_decrypt(epayload, format, hex_encoded_iv); } else if (decrypted_data) { get_random_bytes(epayload->iv, ivsize); - memcpy(epayload->decrypted_data, decrypted_data, - epayload->decrypted_datalen); + ret = hex2bin(epayload->decrypted_data, decrypted_data, + epayload->decrypted_datalen); } else { get_random_bytes(epayload->iv, ivsize); get_random_bytes(epayload->decrypted_data, epayload->decrypted_datalen); diff --git a/security/keys/internal.h b/security/keys/internal.h index 9b9cf3b6fcbb..3c1e7122076b 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -165,8 +165,6 @@ extern struct key *request_key_and_link(struct key_type *type, extern bool lookup_user_key_possessed(const struct key *key, const struct key_match_data *match_data); -#define KEY_LOOKUP_CREATE 0x01 -#define KEY_LOOKUP_PARTIAL 0x02 extern long join_session_keyring(const char *name); extern void key_change_session_keyring(struct callback_head *twork); diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 96a92a645216..d54f73c558f7 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1251,7 +1251,7 @@ long keyctl_instantiate_key(key_serial_t id, struct iov_iter from; int ret; - ret = import_single_range(WRITE, (void __user *)_payload, plen, + ret = import_single_range(ITER_SOURCE, (void __user *)_payload, plen, &iov, &from); if (unlikely(ret)) return ret; @@ -1283,7 +1283,7 @@ long keyctl_instantiate_key_iov(key_serial_t id, if (!_payload_iov) ioc = 0; - ret = import_iovec(WRITE, _payload_iov, ioc, + ret = import_iovec(ITER_SOURCE, _payload_iov, ioc, ARRAY_SIZE(iovstack), &iov, &from); if (ret < 0) return ret; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 5e6a90760753..4448758f643a 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -79,7 +79,7 @@ static void keyring_revoke(struct key *keyring); static void keyring_destroy(struct key *keyring); static void keyring_describe(const struct key *keyring, struct seq_file *m); static long keyring_read(const struct key *keyring, - char __user *buffer, size_t buflen); + char *buffer, size_t buflen); struct key_type key_type_keyring = { .name = "keyring", diff --git a/security/keys/trusted-keys/trusted_tee.c b/security/keys/trusted-keys/trusted_tee.c index c8626686ee1b..ac3e270ade69 100644 --- a/security/keys/trusted-keys/trusted_tee.c +++ b/security/keys/trusted-keys/trusted_tee.c @@ -219,7 +219,8 @@ static int trusted_tee_get_random(unsigned char *key, size_t key_len) static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) { - if (ver->impl_id == TEE_IMPL_ID_OPTEE) + if (ver->impl_id == TEE_IMPL_ID_OPTEE && + ver->gen_caps & TEE_GEN_CAP_REG_MEM) return 1; else return 0; diff --git a/security/landlock/fs.c b/security/landlock/fs.c index ec5a6247cd3e..adcea0fe7e68 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -146,7 +146,18 @@ retry: #define ACCESS_FILE ( \ LANDLOCK_ACCESS_FS_EXECUTE | \ LANDLOCK_ACCESS_FS_WRITE_FILE | \ - LANDLOCK_ACCESS_FS_READ_FILE) + LANDLOCK_ACCESS_FS_READ_FILE | \ + LANDLOCK_ACCESS_FS_TRUNCATE) +/* clang-format on */ + +/* + * All access rights that are denied by default whether they are handled or not + * by a ruleset/layer. This must be ORed with all ruleset->fs_access_masks[] + * entries when we need to get the absolute handled access masks. + */ +/* clang-format off */ +#define ACCESS_INITIALLY_DENIED ( \ + LANDLOCK_ACCESS_FS_REFER) /* clang-format on */ /* @@ -167,7 +178,9 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset, return -EINVAL; /* Transforms relative access rights to absolute ones. */ - access_rights |= LANDLOCK_MASK_ACCESS_FS & ~ruleset->fs_access_masks[0]; + access_rights |= + LANDLOCK_MASK_ACCESS_FS & + ~(ruleset->fs_access_masks[0] | ACCESS_INITIALLY_DENIED); object = get_inode_object(d_backing_inode(path->dentry)); if (IS_ERR(object)) return PTR_ERR(object); @@ -277,25 +290,27 @@ static inline bool is_nouser_or_private(const struct dentry *dentry) static inline access_mask_t get_handled_accesses(const struct landlock_ruleset *const domain) { - access_mask_t access_dom = 0; - unsigned long access_bit; - - for (access_bit = 0; access_bit < LANDLOCK_NUM_ACCESS_FS; - access_bit++) { - size_t layer_level; + access_mask_t access_dom = ACCESS_INITIALLY_DENIED; + size_t layer_level; - for (layer_level = 0; layer_level < domain->num_layers; - layer_level++) { - if (domain->fs_access_masks[layer_level] & - BIT_ULL(access_bit)) { - access_dom |= BIT_ULL(access_bit); - break; - } - } - } - return access_dom; + for (layer_level = 0; layer_level < domain->num_layers; layer_level++) + access_dom |= domain->fs_access_masks[layer_level]; + return access_dom & LANDLOCK_MASK_ACCESS_FS; } +/** + * init_layer_masks - Initialize layer masks from an access request + * + * Populates @layer_masks such that for each access right in @access_request, + * the bits for all the layers are set where this access right is handled. + * + * @domain: The domain that defines the current restrictions. + * @access_request: The requested access rights to check. + * @layer_masks: The layer masks to populate. + * + * Returns: An access mask where each access right bit is set which is handled + * in any of the active layers in @domain. + */ static inline access_mask_t init_layer_masks(const struct landlock_ruleset *const domain, const access_mask_t access_request, @@ -316,8 +331,13 @@ init_layer_masks(const struct landlock_ruleset *const domain, for_each_set_bit(access_bit, &access_req, ARRAY_SIZE(*layer_masks)) { - if (domain->fs_access_masks[layer_level] & - BIT_ULL(access_bit)) { + /* + * Artificially handles all initially denied by default + * access rights. + */ + if (BIT_ULL(access_bit) & + (domain->fs_access_masks[layer_level] | + ACCESS_INITIALLY_DENIED)) { (*layer_masks)[access_bit] |= BIT_ULL(layer_level); handled_accesses |= BIT_ULL(access_bit); @@ -424,7 +444,7 @@ is_eacces(const layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS], } /** - * check_access_path_dual - Check accesses for requests with a common path + * is_access_to_paths_allowed - Check accesses for requests with a common path * * @domain: Domain to check against. * @path: File hierarchy to walk through. @@ -459,14 +479,10 @@ is_eacces(const layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS], * allow the request. * * Returns: - * - 0 if the access request is granted; - * - -EACCES if it is denied because of access right other than - * LANDLOCK_ACCESS_FS_REFER; - * - -EXDEV if the renaming or linking would be a privileged escalation - * (according to each layered policies), or if LANDLOCK_ACCESS_FS_REFER is - * not allowed by the source or the destination. + * - true if the access request is granted; + * - false otherwise. */ -static int check_access_path_dual( +static bool is_access_to_paths_allowed( const struct landlock_ruleset *const domain, const struct path *const path, const access_mask_t access_request_parent1, @@ -486,17 +502,17 @@ static int check_access_path_dual( (*layer_masks_child2)[LANDLOCK_NUM_ACCESS_FS] = NULL; if (!access_request_parent1 && !access_request_parent2) - return 0; + return true; if (WARN_ON_ONCE(!domain || !path)) - return 0; + return true; if (is_nouser_or_private(path->dentry)) - return 0; + return true; if (WARN_ON_ONCE(domain->num_layers < 1 || !layer_masks_parent1)) - return -EACCES; + return false; if (unlikely(layer_masks_parent2)) { if (WARN_ON_ONCE(!dentry_child1)) - return -EACCES; + return false; /* * For a double request, first check for potential privilege * escalation by looking at domain handled accesses (which are @@ -507,7 +523,7 @@ static int check_access_path_dual( is_dom_check = true; } else { if (WARN_ON_ONCE(dentry_child1 || dentry_child2)) - return -EACCES; + return false; /* For a simple request, only check for requested accesses. */ access_masked_parent1 = access_request_parent1; access_masked_parent2 = access_request_parent2; @@ -616,24 +632,7 @@ jump_up: } path_put(&walker_path); - if (allowed_parent1 && allowed_parent2) - return 0; - - /* - * This prioritizes EACCES over EXDEV for all actions, including - * renames with RENAME_EXCHANGE. - */ - if (likely(is_eacces(layer_masks_parent1, access_request_parent1) || - is_eacces(layer_masks_parent2, access_request_parent2))) - return -EACCES; - - /* - * Gracefully forbids reparenting if the destination directory - * hierarchy is not a superset of restrictions of the source directory - * hierarchy, or if LANDLOCK_ACCESS_FS_REFER is not allowed by the - * source or the destination. - */ - return -EXDEV; + return allowed_parent1 && allowed_parent2; } static inline int check_access_path(const struct landlock_ruleset *const domain, @@ -643,8 +642,10 @@ static inline int check_access_path(const struct landlock_ruleset *const domain, layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {}; access_request = init_layer_masks(domain, access_request, &layer_masks); - return check_access_path_dual(domain, path, access_request, - &layer_masks, NULL, 0, NULL, NULL); + if (is_access_to_paths_allowed(domain, path, access_request, + &layer_masks, NULL, 0, NULL, NULL)) + return 0; + return -EACCES; } static inline int current_check_access_path(const struct path *const path, @@ -705,8 +706,9 @@ static inline access_mask_t maybe_remove(const struct dentry *const dentry) * file. While walking from @dir to @mnt_root, we record all the domain's * allowed accesses in @layer_masks_dom. * - * This is similar to check_access_path_dual() but much simpler because it only - * handles walking on the same mount point and only check one set of accesses. + * This is similar to is_access_to_paths_allowed() but much simpler because it + * only handles walking on the same mount point and only checks one set of + * accesses. * * Returns: * - true if all the domain access rights are allowed for @dir; @@ -851,16 +853,13 @@ static int current_check_refer_path(struct dentry *const old_dentry, access_request_parent1 = init_layer_masks( dom, access_request_parent1 | access_request_parent2, &layer_masks_parent1); - return check_access_path_dual(dom, new_dir, - access_request_parent1, - &layer_masks_parent1, NULL, 0, - NULL, NULL); + if (is_access_to_paths_allowed( + dom, new_dir, access_request_parent1, + &layer_masks_parent1, NULL, 0, NULL, NULL)) + return 0; + return -EACCES; } - /* Backward compatibility: no reparenting support. */ - if (!(get_handled_accesses(dom) & LANDLOCK_ACCESS_FS_REFER)) - return -EXDEV; - access_request_parent1 |= LANDLOCK_ACCESS_FS_REFER; access_request_parent2 |= LANDLOCK_ACCESS_FS_REFER; @@ -884,11 +883,27 @@ static int current_check_refer_path(struct dentry *const old_dentry, * parent access rights. This will be useful to compare with the * destination parent access rights. */ - return check_access_path_dual(dom, &mnt_dir, access_request_parent1, - &layer_masks_parent1, old_dentry, - access_request_parent2, - &layer_masks_parent2, - exchange ? new_dentry : NULL); + if (is_access_to_paths_allowed( + dom, &mnt_dir, access_request_parent1, &layer_masks_parent1, + old_dentry, access_request_parent2, &layer_masks_parent2, + exchange ? new_dentry : NULL)) + return 0; + + /* + * This prioritizes EACCES over EXDEV for all actions, including + * renames with RENAME_EXCHANGE. + */ + if (likely(is_eacces(&layer_masks_parent1, access_request_parent1) || + is_eacces(&layer_masks_parent2, access_request_parent2))) + return -EACCES; + + /* + * Gracefully forbids reparenting if the destination directory + * hierarchy is not a superset of restrictions of the source directory + * hierarchy, or if LANDLOCK_ACCESS_FS_REFER is not allowed by the + * source or the destination. + */ + return -EXDEV; } /* Inode hooks */ @@ -1140,9 +1155,23 @@ static int hook_path_rmdir(const struct path *const dir, return current_check_access_path(dir, LANDLOCK_ACCESS_FS_REMOVE_DIR); } +static int hook_path_truncate(const struct path *const path) +{ + return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE); +} + /* File hooks */ -static inline access_mask_t get_file_access(const struct file *const file) +/** + * get_required_file_open_access - Get access needed to open a file + * + * @file: File being opened. + * + * Returns the access rights that are required for opening the given file, + * depending on the file type and open mode. + */ +static inline access_mask_t +get_required_file_open_access(const struct file *const file) { access_mask_t access = 0; @@ -1160,19 +1189,95 @@ static inline access_mask_t get_file_access(const struct file *const file) return access; } +static int hook_file_alloc_security(struct file *const file) +{ + /* + * Grants all access rights, even if most of them are not checked later + * on. It is more consistent. + * + * Notably, file descriptors for regular files can also be acquired + * without going through the file_open hook, for example when using + * memfd_create(2). + */ + landlock_file(file)->allowed_access = LANDLOCK_MASK_ACCESS_FS; + return 0; +} + static int hook_file_open(struct file *const file) { + layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {}; + access_mask_t open_access_request, full_access_request, allowed_access; + const access_mask_t optional_access = LANDLOCK_ACCESS_FS_TRUNCATE; const struct landlock_ruleset *const dom = landlock_get_current_domain(); if (!dom) return 0; + /* - * Because a file may be opened with O_PATH, get_file_access() may - * return 0. This case will be handled with a future Landlock + * Because a file may be opened with O_PATH, get_required_file_open_access() + * may return 0. This case will be handled with a future Landlock * evolution. */ - return check_access_path(dom, &file->f_path, get_file_access(file)); + open_access_request = get_required_file_open_access(file); + + /* + * We look up more access than what we immediately need for open(), so + * that we can later authorize operations on opened files. + */ + full_access_request = open_access_request | optional_access; + + if (is_access_to_paths_allowed( + dom, &file->f_path, + init_layer_masks(dom, full_access_request, &layer_masks), + &layer_masks, NULL, 0, NULL, NULL)) { + allowed_access = full_access_request; + } else { + unsigned long access_bit; + const unsigned long access_req = full_access_request; + + /* + * Calculate the actual allowed access rights from layer_masks. + * Add each access right to allowed_access which has not been + * vetoed by any layer. + */ + allowed_access = 0; + for_each_set_bit(access_bit, &access_req, + ARRAY_SIZE(layer_masks)) { + if (!layer_masks[access_bit]) + allowed_access |= BIT_ULL(access_bit); + } + } + + /* + * For operations on already opened files (i.e. ftruncate()), it is the + * access rights at the time of open() which decide whether the + * operation is permitted. Therefore, we record the relevant subset of + * file access rights in the opened struct file. + */ + landlock_file(file)->allowed_access = allowed_access; + + if ((open_access_request & allowed_access) == open_access_request) + return 0; + + return -EACCES; +} + +static int hook_file_truncate(struct file *const file) +{ + /* + * Allows truncation if the truncate right was available at the time of + * opening the file, to get a consistent access check as for read, write + * and execute operations. + * + * Note: For checks done based on the file's Landlock allowed access, we + * enforce them independently of whether the current thread is in a + * Landlock domain, so that open files passed between independent + * processes retain their behaviour. + */ + if (landlock_file(file)->allowed_access & LANDLOCK_ACCESS_FS_TRUNCATE) + return 0; + return -EACCES; } static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = { @@ -1192,8 +1297,11 @@ static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(path_symlink, hook_path_symlink), LSM_HOOK_INIT(path_unlink, hook_path_unlink), LSM_HOOK_INIT(path_rmdir, hook_path_rmdir), + LSM_HOOK_INIT(path_truncate, hook_path_truncate), + LSM_HOOK_INIT(file_alloc_security, hook_file_alloc_security), LSM_HOOK_INIT(file_open, hook_file_open), + LSM_HOOK_INIT(file_truncate, hook_file_truncate), }; __init void landlock_add_fs_hooks(void) diff --git a/security/landlock/fs.h b/security/landlock/fs.h index 8db7acf9109b..488e4813680a 100644 --- a/security/landlock/fs.h +++ b/security/landlock/fs.h @@ -37,6 +37,24 @@ struct landlock_inode_security { }; /** + * struct landlock_file_security - File security blob + * + * This information is populated when opening a file in hook_file_open, and + * tracks the relevant Landlock access rights that were available at the time + * of opening the file. Other LSM hooks use these rights in order to authorize + * operations on already opened files. + */ +struct landlock_file_security { + /** + * @allowed_access: Access rights that were available at the time of + * opening the file. This is not necessarily the full set of access + * rights available at that time, but it's the necessary subset as + * needed to authorize later operations on the open file. + */ + access_mask_t allowed_access; +}; + +/** * struct landlock_superblock_security - Superblock security blob * * Enable hook_sb_delete() to wait for concurrent calls to release_inode(). @@ -50,6 +68,12 @@ struct landlock_superblock_security { atomic_long_t inode_refs; }; +static inline struct landlock_file_security * +landlock_file(const struct file *const file) +{ + return file->f_security + landlock_blob_sizes.lbs_file; +} + static inline struct landlock_inode_security * landlock_inode(const struct inode *const inode) { diff --git a/security/landlock/limits.h b/security/landlock/limits.h index b54184ab9439..82288f0e9e5e 100644 --- a/security/landlock/limits.h +++ b/security/landlock/limits.h @@ -18,7 +18,7 @@ #define LANDLOCK_MAX_NUM_LAYERS 16 #define LANDLOCK_MAX_NUM_RULES U32_MAX -#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_REFER +#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_TRUNCATE #define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1) #define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS) diff --git a/security/landlock/setup.c b/security/landlock/setup.c index f8e8e980454c..3f196d2ce4f9 100644 --- a/security/landlock/setup.c +++ b/security/landlock/setup.c @@ -19,6 +19,7 @@ bool landlock_initialized __lsm_ro_after_init = false; struct lsm_blob_sizes landlock_blob_sizes __lsm_ro_after_init = { .lbs_cred = sizeof(struct landlock_cred_security), + .lbs_file = sizeof(struct landlock_file_security), .lbs_inode = sizeof(struct landlock_inode_security), .lbs_superblock = sizeof(struct landlock_superblock_security), }; diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c index 735a0865ea11..245cc650a4dc 100644 --- a/security/landlock/syscalls.c +++ b/security/landlock/syscalls.c @@ -129,7 +129,7 @@ static const struct file_operations ruleset_fops = { .write = fop_dummy_write, }; -#define LANDLOCK_ABI_VERSION 2 +#define LANDLOCK_ABI_VERSION 3 /** * sys_landlock_create_ruleset - Create a new ruleset @@ -149,10 +149,10 @@ static const struct file_operations ruleset_fops = { * * Possible returned errors are: * - * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; - * - EINVAL: unknown @flags, or unknown access, or too small @size; - * - E2BIG or EFAULT: @attr or @size inconsistencies; - * - ENOMSG: empty &landlock_ruleset_attr.handled_access_fs. + * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; + * - %EINVAL: unknown @flags, or unknown access, or too small @size; + * - %E2BIG or %EFAULT: @attr or @size inconsistencies; + * - %ENOMSG: empty &landlock_ruleset_attr.handled_access_fs. */ SYSCALL_DEFINE3(landlock_create_ruleset, const struct landlock_ruleset_attr __user *const, attr, @@ -280,7 +280,7 @@ out_fdput: * @ruleset_fd: File descriptor tied to the ruleset that should be extended * with the new rule. * @rule_type: Identify the structure type pointed to by @rule_attr (only - * LANDLOCK_RULE_PATH_BENEATH for now). + * %LANDLOCK_RULE_PATH_BENEATH for now). * @rule_attr: Pointer to a rule (only of type &struct * landlock_path_beneath_attr for now). * @flags: Must be 0. @@ -290,17 +290,17 @@ out_fdput: * * Possible returned errors are: * - * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; - * - EINVAL: @flags is not 0, or inconsistent access in the rule (i.e. + * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; + * - %EINVAL: @flags is not 0, or inconsistent access in the rule (i.e. * &landlock_path_beneath_attr.allowed_access is not a subset of the * ruleset handled accesses); - * - ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access); - * - EBADF: @ruleset_fd is not a file descriptor for the current thread, or a + * - %ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access); + * - %EBADF: @ruleset_fd is not a file descriptor for the current thread, or a * member of @rule_attr is not a file descriptor as expected; - * - EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of + * - %EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of * @rule_attr is not the expected file descriptor type; - * - EPERM: @ruleset_fd has no write access to the underlying ruleset; - * - EFAULT: @rule_attr inconsistency. + * - %EPERM: @ruleset_fd has no write access to the underlying ruleset; + * - %EFAULT: @rule_attr inconsistency. */ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd, const enum landlock_rule_type, rule_type, @@ -378,20 +378,20 @@ out_put_ruleset: * @flags: Must be 0. * * This system call enables to enforce a Landlock ruleset on the current - * thread. Enforcing a ruleset requires that the task has CAP_SYS_ADMIN in its + * thread. Enforcing a ruleset requires that the task has %CAP_SYS_ADMIN in its * namespace or is running with no_new_privs. This avoids scenarios where * unprivileged tasks can affect the behavior of privileged children. * * Possible returned errors are: * - * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; - * - EINVAL: @flags is not 0. - * - EBADF: @ruleset_fd is not a file descriptor for the current thread; - * - EBADFD: @ruleset_fd is not a ruleset file descriptor; - * - EPERM: @ruleset_fd has no read access to the underlying ruleset, or the + * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; + * - %EINVAL: @flags is not 0. + * - %EBADF: @ruleset_fd is not a file descriptor for the current thread; + * - %EBADFD: @ruleset_fd is not a ruleset file descriptor; + * - %EPERM: @ruleset_fd has no read access to the underlying ruleset, or the * current thread is not running with no_new_privs, or it doesn't have - * CAP_SYS_ADMIN in its namespace. - * - E2BIG: The maximum number of stacked rulesets is reached for the current + * %CAP_SYS_ADMIN in its namespace. + * - %E2BIG: The maximum number of stacked rulesets is reached for the current * thread. */ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32, diff --git a/security/loadpin/Kconfig b/security/loadpin/Kconfig index 70e7985b2561..6724eaba3d36 100644 --- a/security/loadpin/Kconfig +++ b/security/loadpin/Kconfig @@ -33,4 +33,9 @@ config SECURITY_LOADPIN_VERITY on the LoadPin securityfs entry 'dm-verity'. The ioctl expects a file descriptor of a file with verity digests as parameter. The file must be located on the pinned root and - contain a comma separated list of digests. + start with the line: + + # LOADPIN_TRUSTED_VERITY_ROOT_DIGESTS + + This is followed by the verity digests, with one digest per + line. diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c index 6ab5f2bbf41f..de41621f4998 100644 --- a/security/loadpin/loadpin.c +++ b/security/loadpin/loadpin.c @@ -21,6 +21,8 @@ #include <linux/dm-verity-loadpin.h> #include <uapi/linux/loadpin.h> +#define VERITY_DIGEST_FILE_HEADER "# LOADPIN_TRUSTED_VERITY_ROOT_DIGESTS" + static void report_load(const char *origin, struct file *file, char *operation) { char *cmdline, *pathname; @@ -292,9 +294,21 @@ static int read_trusted_verity_root_digests(unsigned int fd) p = strim(data); while ((d = strsep(&p, "\n")) != NULL) { - int len = strlen(d); + int len; struct dm_verity_loadpin_trusted_root_digest *trd; + if (d == data) { + /* first line, validate header */ + if (strcmp(d, VERITY_DIGEST_FILE_HEADER)) { + rc = -EPROTO; + goto err; + } + + continue; + } + + len = strlen(d); + if (len % 2) { rc = -EPROTO; goto err; @@ -356,13 +370,11 @@ static long dm_verity_ioctl(struct file *filp, unsigned int cmd, unsigned long a { void __user *uarg = (void __user *)arg; unsigned int fd; - int rc; switch (cmd) { case LOADPIN_IOC_SET_TRUSTED_VERITY_DIGESTS: - rc = copy_from_user(&fd, uarg, sizeof(fd)); - if (rc) - return rc; + if (copy_from_user(&fd, uarg, sizeof(fd))) + return -EFAULT; return read_trusted_verity_root_digests(fd); diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c index 87cbdc64d272..a79b985e917e 100644 --- a/security/lockdown/lockdown.c +++ b/security/lockdown/lockdown.c @@ -63,7 +63,7 @@ static int lockdown_is_locked_down(enum lockdown_reason what) if (kernel_locked_down >= what) { if (lockdown_reasons[what]) - pr_notice("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n", + pr_notice_ratelimited("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n", current->comm, lockdown_reasons[what]); return -EPERM; } diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 78a278f28e49..a7355b4b9bb8 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -44,9 +44,6 @@ int ipv4_skb_to_auditdata(struct sk_buff *skb, struct iphdr *ih; ih = ip_hdr(skb); - if (ih == NULL) - return -EINVAL; - ad->u.net->v4info.saddr = ih->saddr; ad->u.net->v4info.daddr = ih->daddr; @@ -59,8 +56,6 @@ int ipv4_skb_to_auditdata(struct sk_buff *skb, switch (ih->protocol) { case IPPROTO_TCP: { struct tcphdr *th = tcp_hdr(skb); - if (th == NULL) - break; ad->u.net->sport = th->source; ad->u.net->dport = th->dest; @@ -68,8 +63,6 @@ int ipv4_skb_to_auditdata(struct sk_buff *skb, } case IPPROTO_UDP: { struct udphdr *uh = udp_hdr(skb); - if (uh == NULL) - break; ad->u.net->sport = uh->source; ad->u.net->dport = uh->dest; @@ -77,8 +70,6 @@ int ipv4_skb_to_auditdata(struct sk_buff *skb, } case IPPROTO_DCCP: { struct dccp_hdr *dh = dccp_hdr(skb); - if (dh == NULL) - break; ad->u.net->sport = dh->dccph_sport; ad->u.net->dport = dh->dccph_dport; @@ -86,8 +77,7 @@ int ipv4_skb_to_auditdata(struct sk_buff *skb, } case IPPROTO_SCTP: { struct sctphdr *sh = sctp_hdr(skb); - if (sh == NULL) - break; + ad->u.net->sport = sh->source; ad->u.net->dport = sh->dest; break; @@ -115,8 +105,6 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb, __be16 frag_off; ip6 = ipv6_hdr(skb); - if (ip6 == NULL) - return -EINVAL; ad->u.net->v6info.saddr = ip6->saddr; ad->u.net->v6info.daddr = ip6->daddr; /* IPv6 can have several extension header before the Transport header @@ -202,6 +190,7 @@ static inline void print_ipv4_addr(struct audit_buffer *ab, __be32 addr, /** * dump_common_audit_data - helper to dump common audit data + * @ab : the audit buffer * @a : common audit data * */ diff --git a/security/security.c b/security/security.c index 14d30fec8a00..d1571900a8c7 100644 --- a/security/security.c +++ b/security/security.c @@ -52,6 +52,7 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1] = { [LOCKDOWN_IOPORT] = "raw io port access", [LOCKDOWN_MSR] = "raw MSR access", [LOCKDOWN_ACPI_TABLES] = "modifying ACPI tables", + [LOCKDOWN_DEVICE_TREE] = "modifying device tree contents", [LOCKDOWN_PCMCIA_CIS] = "direct PCMCIA CIS storage", [LOCKDOWN_TIOCSSERIAL] = "reconfiguration of serial port IO", [LOCKDOWN_MODULE_PARAMETERS] = "unsafe module parameters", @@ -60,6 +61,7 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1] = { [LOCKDOWN_XMON_WR] = "xmon write access", [LOCKDOWN_BPF_WRITE_USER] = "use of bpf to write user RAM", [LOCKDOWN_DBG_WRITE_KERNEL] = "use of kgdb/kdb to write kernel RAM", + [LOCKDOWN_RTAS_ERROR_INJECTION] = "RTAS error injection", [LOCKDOWN_INTEGRITY_MAX] = "integrity", [LOCKDOWN_KCORE] = "/proc/kcore access", [LOCKDOWN_KPROBES] = "use of kprobes", @@ -159,8 +161,8 @@ static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from) lsm->enabled = &lsm_enabled_true; ordered_lsms[last_lsm++] = lsm; - init_debug("%s ordering: %s (%sabled)\n", from, lsm->name, - is_enabled(lsm) ? "en" : "dis"); + init_debug("%s ordered: %s (%s)\n", from, lsm->name, + is_enabled(lsm) ? "enabled" : "disabled"); } /* Is an LSM allowed to be initialized? */ @@ -183,11 +185,12 @@ static void __init lsm_set_blob_size(int *need, int *lbs) { int offset; - if (*need > 0) { - offset = *lbs; - *lbs += *need; - *need = offset; - } + if (*need <= 0) + return; + + offset = ALIGN(*lbs, sizeof(void *)); + *lbs = offset + *need; + *need = offset; } static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed) @@ -222,7 +225,7 @@ static void __init prepare_lsm(struct lsm_info *lsm) if (enabled) { if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) { exclusive = lsm; - init_debug("exclusive chosen: %s\n", lsm->name); + init_debug("exclusive chosen: %s\n", lsm->name); } lsm_set_blob_sizes(lsm->blobs); @@ -250,7 +253,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) /* LSM_ORDER_FIRST is always first. */ for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { if (lsm->order == LSM_ORDER_FIRST) - append_ordered_lsm(lsm, "first"); + append_ordered_lsm(lsm, " first"); } /* Process "security=", if given. */ @@ -268,7 +271,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) if ((major->flags & LSM_FLAG_LEGACY_MAJOR) && strcmp(major->name, chosen_major_lsm) != 0) { set_enabled(major, false); - init_debug("security=%s disabled: %s\n", + init_debug("security=%s disabled: %s (only one legacy major LSM)\n", chosen_major_lsm, major->name); } } @@ -289,7 +292,8 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) } if (!found) - init_debug("%s ignored: %s\n", origin, name); + init_debug("%s ignored: %s (not built into kernel)\n", + origin, name); } /* Process "security=", if given. */ @@ -307,7 +311,8 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) if (exists_ordered_lsm(lsm)) continue; set_enabled(lsm, false); - init_debug("%s disabled: %s\n", origin, lsm->name); + init_debug("%s skipped: %s (not in requested order)\n", + origin, lsm->name); } kfree(sep); @@ -318,6 +323,24 @@ static void __init lsm_early_task(struct task_struct *task); static int lsm_append(const char *new, char **result); +static void __init report_lsm_order(void) +{ + struct lsm_info **lsm, *early; + int first = 0; + + pr_info("initializing lsm="); + + /* Report each enabled LSM name, comma separated. */ + for (early = __start_early_lsm_info; early < __end_early_lsm_info; early++) + if (is_enabled(early)) + pr_cont("%s%s", first++ == 0 ? "" : ",", early->name); + for (lsm = ordered_lsms; *lsm; lsm++) + if (is_enabled(*lsm)) + pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name); + + pr_cont("\n"); +} + static void __init ordered_lsm_init(void) { struct lsm_info **lsm; @@ -327,7 +350,8 @@ static void __init ordered_lsm_init(void) if (chosen_lsm_order) { if (chosen_major_lsm) { - pr_info("security= is ignored because it is superseded by lsm=\n"); + pr_warn("security=%s is ignored because it is superseded by lsm=%s\n", + chosen_major_lsm, chosen_lsm_order); chosen_major_lsm = NULL; } ordered_lsm_parse(chosen_lsm_order, "cmdline"); @@ -337,6 +361,8 @@ static void __init ordered_lsm_init(void) for (lsm = ordered_lsms; *lsm; lsm++) prepare_lsm(*lsm); + report_lsm_order(); + init_debug("cred blob size = %d\n", blob_sizes.lbs_cred); init_debug("file blob size = %d\n", blob_sizes.lbs_file); init_debug("inode blob size = %d\n", blob_sizes.lbs_inode); @@ -393,13 +419,17 @@ int __init security_init(void) { struct lsm_info *lsm; - pr_info("Security Framework initializing\n"); + init_debug("legacy security=%s\n", chosen_major_lsm ?: " *unspecified*"); + init_debug(" CONFIG_LSM=%s\n", builtin_lsm_order); + init_debug("boot arg lsm=%s\n", chosen_lsm_order ?: " *unspecified*"); /* * Append the names of the early LSM modules now that kmalloc() is * available */ for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) { + init_debug(" early started: %s (%s)\n", lsm->name, + is_enabled(lsm) ? "enabled" : "disabled"); if (lsm->enabled) lsm_append(lsm->name, &lsm_names); } @@ -1370,6 +1400,48 @@ int security_inode_setxattr(struct user_namespace *mnt_userns, return evm_inode_setxattr(mnt_userns, dentry, name, value, size); } +int security_inode_set_acl(struct user_namespace *mnt_userns, + struct dentry *dentry, const char *acl_name, + struct posix_acl *kacl) +{ + int ret; + + if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) + return 0; + ret = call_int_hook(inode_set_acl, 0, mnt_userns, dentry, acl_name, + kacl); + if (ret) + return ret; + ret = ima_inode_set_acl(mnt_userns, dentry, acl_name, kacl); + if (ret) + return ret; + return evm_inode_set_acl(mnt_userns, dentry, acl_name, kacl); +} + +int security_inode_get_acl(struct user_namespace *mnt_userns, + struct dentry *dentry, const char *acl_name) +{ + if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) + return 0; + return call_int_hook(inode_get_acl, 0, mnt_userns, dentry, acl_name); +} + +int security_inode_remove_acl(struct user_namespace *mnt_userns, + struct dentry *dentry, const char *acl_name) +{ + int ret; + + if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) + return 0; + ret = call_int_hook(inode_remove_acl, 0, mnt_userns, dentry, acl_name); + if (ret) + return ret; + ret = ima_inode_remove_acl(mnt_userns, dentry, acl_name); + if (ret) + return ret; + return evm_inode_remove_acl(mnt_userns, dentry, acl_name); +} + void security_inode_post_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { @@ -1650,6 +1722,11 @@ int security_file_open(struct file *file) return fsnotify_perm(file, MAY_OPEN); } +int security_file_truncate(struct file *file) +{ + return call_int_hook(file_truncate, 0, file); +} + int security_task_alloc(struct task_struct *task, unsigned long clone_flags) { int rc = lsm_task_alloc(task); @@ -1909,6 +1986,11 @@ void security_task_to_inode(struct task_struct *p, struct inode *inode) call_void_hook(task_to_inode, p, inode); } +int security_create_user_ns(const struct cred *cred) +{ + return call_int_hook(userns_create, 0, cred); +} + int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag) { return call_int_hook(ipc_permission, 0, ipcp, flag); @@ -2057,8 +2139,8 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode) } EXPORT_SYMBOL(security_d_instantiate); -int security_getprocattr(struct task_struct *p, const char *lsm, char *name, - char **value) +int security_getprocattr(struct task_struct *p, const char *lsm, + const char *name, char **value) { struct security_hook_list *hp; @@ -2260,11 +2342,11 @@ int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) } EXPORT_SYMBOL(security_sock_rcv_skb); -int security_socket_getpeersec_stream(struct socket *sock, char __user *optval, - int __user *optlen, unsigned len) +int security_socket_getpeersec_stream(struct socket *sock, sockptr_t optval, + sockptr_t optlen, unsigned int len) { return call_int_hook(socket_getpeersec_stream, -ENOPROTOOPT, sock, - optval, optlen, len); + optval, optlen, len); } int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid) @@ -2660,4 +2742,8 @@ int security_uring_sqpoll(void) { return call_int_hook(uring_sqpoll, 0); } +int security_uring_cmd(struct io_uring_cmd *ioucmd) +{ + return call_int_hook(uring_cmd, 0, ioucmd); +} #endif /* CONFIG_IO_URING */ diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 79573504783b..3c5be76a9199 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -91,6 +91,7 @@ #include <uapi/linux/mount.h> #include <linux/fsnotify.h> #include <linux/fanotify.h> +#include <linux/io_uring.h> #include "avc.h" #include "objsec.h" @@ -3239,6 +3240,25 @@ static int selinux_inode_setxattr(struct user_namespace *mnt_userns, &ad); } +static int selinux_inode_set_acl(struct user_namespace *mnt_userns, + struct dentry *dentry, const char *acl_name, + struct posix_acl *kacl) +{ + return dentry_has_perm(current_cred(), dentry, FILE__SETATTR); +} + +static int selinux_inode_get_acl(struct user_namespace *mnt_userns, + struct dentry *dentry, const char *acl_name) +{ + return dentry_has_perm(current_cred(), dentry, FILE__GETATTR); +} + +static int selinux_inode_remove_acl(struct user_namespace *mnt_userns, + struct dentry *dentry, const char *acl_name) +{ + return dentry_has_perm(current_cred(), dentry, FILE__SETATTR); +} + static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) @@ -4221,6 +4241,14 @@ static void selinux_task_to_inode(struct task_struct *p, spin_unlock(&isec->lock); } +static int selinux_userns_create(const struct cred *cred) +{ + u32 sid = current_sid(); + + return avc_has_perm(&selinux_state, sid, sid, SECCLASS_USER_NAMESPACE, + USER_NAMESPACE__CREATE, NULL); +} + /* Returns error only if unable to parse addresses */ static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct common_audit_data *ad, u8 *proto) @@ -5110,11 +5138,12 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) return err; } -static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *optval, - int __user *optlen, unsigned len) +static int selinux_socket_getpeersec_stream(struct socket *sock, + sockptr_t optval, sockptr_t optlen, + unsigned int len) { int err = 0; - char *scontext; + char *scontext = NULL; u32 scontext_len; struct sk_security_struct *sksec = sock->sk->sk_security; u32 peer_sid = SECSID_NULL; @@ -5130,17 +5159,15 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op &scontext_len); if (err) return err; - if (scontext_len > len) { err = -ERANGE; goto out_len; } - if (copy_to_user(optval, scontext, scontext_len)) + if (copy_to_sockptr(optval, scontext, scontext_len)) err = -EFAULT; - out_len: - if (put_user(scontext_len, optlen)) + if (copy_to_sockptr(optlen, &scontext_len, sizeof(scontext_len))) err = -EFAULT; kfree(scontext); return err; @@ -5986,7 +6013,6 @@ static int selinux_msg_queue_alloc_security(struct kern_ipc_perm *msq) struct ipc_security_struct *isec; struct common_audit_data ad; u32 sid = current_sid(); - int rc; isec = selinux_ipc(msq); ipc_init_security(isec, SECCLASS_MSGQ); @@ -5994,10 +6020,9 @@ static int selinux_msg_queue_alloc_security(struct kern_ipc_perm *msq) ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = msq->key; - rc = avc_has_perm(&selinux_state, - sid, isec->sid, SECCLASS_MSGQ, - MSGQ__CREATE, &ad); - return rc; + return avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_MSGQ, + MSGQ__CREATE, &ad); } static int selinux_msg_queue_associate(struct kern_ipc_perm *msq, int msqflg) @@ -6125,7 +6150,6 @@ static int selinux_shm_alloc_security(struct kern_ipc_perm *shp) struct ipc_security_struct *isec; struct common_audit_data ad; u32 sid = current_sid(); - int rc; isec = selinux_ipc(shp); ipc_init_security(isec, SECCLASS_SHM); @@ -6133,10 +6157,9 @@ static int selinux_shm_alloc_security(struct kern_ipc_perm *shp) ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = shp->key; - rc = avc_has_perm(&selinux_state, - sid, isec->sid, SECCLASS_SHM, - SHM__CREATE, &ad); - return rc; + return avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_SHM, + SHM__CREATE, &ad); } static int selinux_shm_associate(struct kern_ipc_perm *shp, int shmflg) @@ -6210,7 +6233,6 @@ static int selinux_sem_alloc_security(struct kern_ipc_perm *sma) struct ipc_security_struct *isec; struct common_audit_data ad; u32 sid = current_sid(); - int rc; isec = selinux_ipc(sma); ipc_init_security(isec, SECCLASS_SEM); @@ -6218,10 +6240,9 @@ static int selinux_sem_alloc_security(struct kern_ipc_perm *sma) ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = sma->key; - rc = avc_has_perm(&selinux_state, - sid, isec->sid, SECCLASS_SEM, - SEM__CREATE, &ad); - return rc; + return avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_SEM, + SEM__CREATE, &ad); } static int selinux_sem_associate(struct kern_ipc_perm *sma, int semflg) @@ -6327,7 +6348,7 @@ static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode) } static int selinux_getprocattr(struct task_struct *p, - char *name, char **value) + const char *name, char **value) { const struct task_security_struct *__tsec; u32 sid; @@ -6987,6 +7008,28 @@ static int selinux_uring_sqpoll(void) return avc_has_perm(&selinux_state, sid, sid, SECCLASS_IO_URING, IO_URING__SQPOLL, NULL); } + +/** + * selinux_uring_cmd - check if IORING_OP_URING_CMD is allowed + * @ioucmd: the io_uring command structure + * + * Check to see if the current domain is allowed to execute an + * IORING_OP_URING_CMD against the device/file specified in @ioucmd. + * + */ +static int selinux_uring_cmd(struct io_uring_cmd *ioucmd) +{ + struct file *file = ioucmd->file; + struct inode *inode = file_inode(file); + struct inode_security_struct *isec = selinux_inode(inode); + struct common_audit_data ad; + + ad.type = LSM_AUDIT_DATA_FILE; + ad.u.file = file; + + return avc_has_perm(&selinux_state, current_sid(), isec->sid, + SECCLASS_IO_URING, IO_URING__CMD, &ad); +} #endif /* CONFIG_IO_URING */ /* @@ -7063,6 +7106,9 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(inode_getxattr, selinux_inode_getxattr), LSM_HOOK_INIT(inode_listxattr, selinux_inode_listxattr), LSM_HOOK_INIT(inode_removexattr, selinux_inode_removexattr), + LSM_HOOK_INIT(inode_set_acl, selinux_inode_set_acl), + LSM_HOOK_INIT(inode_get_acl, selinux_inode_get_acl), + LSM_HOOK_INIT(inode_remove_acl, selinux_inode_remove_acl), LSM_HOOK_INIT(inode_getsecurity, selinux_inode_getsecurity), LSM_HOOK_INIT(inode_setsecurity, selinux_inode_setsecurity), LSM_HOOK_INIT(inode_listsecurity, selinux_inode_listsecurity), @@ -7111,6 +7157,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(task_movememory, selinux_task_movememory), LSM_HOOK_INIT(task_kill, selinux_task_kill), LSM_HOOK_INIT(task_to_inode, selinux_task_to_inode), + LSM_HOOK_INIT(userns_create, selinux_userns_create), LSM_HOOK_INIT(ipc_permission, selinux_ipc_permission), LSM_HOOK_INIT(ipc_getsecid, selinux_ipc_getsecid), @@ -7231,6 +7278,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { #ifdef CONFIG_IO_URING LSM_HOOK_INIT(uring_override_creds, selinux_uring_override_creds), LSM_HOOK_INIT(uring_sqpoll, selinux_uring_sqpoll), + LSM_HOOK_INIT(uring_cmd, selinux_uring_cmd), #endif /* diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index ff757ae5f253..a3c380775d41 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -253,7 +253,9 @@ const struct security_class_mapping secclass_map[] = { { "anon_inode", { COMMON_FILE_PERMS, NULL } }, { "io_uring", - { "override_creds", "sqpoll", NULL } }, + { "override_creds", "sqpoll", "cmd", NULL } }, + { "user_namespace", + { "create", NULL } }, { NULL } }; diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 8fcdd494af27..0a6894cdc54d 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -71,7 +71,7 @@ struct selinux_fs_info { struct dentry *bool_dir; unsigned int bool_num; char **bool_pending_names; - unsigned int *bool_pending_values; + int *bool_pending_values; struct dentry *class_dir; unsigned long last_class_ino; bool policy_opened; @@ -294,7 +294,7 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf, */ pr_err("SELinux: Runtime disable is deprecated, use selinux=0 on the kernel cmdline.\n"); pr_err("SELinux: https://github.com/SELinuxProject/selinux-kernel/wiki/DEPRECATE-runtime-disable\n"); - ssleep(5); + ssleep(15); if (count >= PAGE_SIZE) return -ENOMEM; @@ -356,7 +356,7 @@ static const struct file_operations sel_policyvers_ops = { /* declaration for sel_write_load */ static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_dir, unsigned int *bool_num, char ***bool_pending_names, - unsigned int **bool_pending_values); + int **bool_pending_values); static int sel_make_classes(struct selinux_policy *newpolicy, struct dentry *class_dir, unsigned long *last_class_ino); @@ -527,7 +527,7 @@ static const struct file_operations sel_policy_ops = { }; static void sel_remove_old_bool_data(unsigned int bool_num, char **bool_names, - unsigned int *bool_values) + int *bool_values) { u32 i; @@ -545,7 +545,7 @@ static int sel_make_policy_nodes(struct selinux_fs_info *fsi, struct dentry *tmp_parent, *tmp_bool_dir, *tmp_class_dir, *old_dentry; unsigned int tmp_bool_num, old_bool_num; char **tmp_bool_names, **old_bool_names; - unsigned int *tmp_bool_values, *old_bool_values; + int *tmp_bool_values, *old_bool_values; unsigned long tmp_ino = fsi->last_ino; /* Don't increment last_ino in this function */ tmp_parent = sel_make_disconnected_dir(fsi->sb, &tmp_ino); @@ -763,7 +763,7 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf, checkreqprot_set(fsi->state, (new_value ? 1 : 0)); if (new_value) - ssleep(5); + ssleep(15); length = count; selinux_ima_measure_state(fsi->state); @@ -1423,7 +1423,7 @@ static void sel_remove_entries(struct dentry *de) static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_dir, unsigned int *bool_num, char ***bool_pending_names, - unsigned int **bool_pending_values) + int **bool_pending_values) { int ret; ssize_t len; @@ -1917,7 +1917,6 @@ static int sel_make_class_dir_entries(struct selinux_policy *newpolicy, struct selinux_fs_info *fsi = sb->s_fs_info; struct dentry *dentry = NULL; struct inode *inode = NULL; - int rc; dentry = d_alloc_name(dir, "index"); if (!dentry) @@ -1937,9 +1936,7 @@ static int sel_make_class_dir_entries(struct selinux_policy *newpolicy, if (IS_ERR(dentry)) return PTR_ERR(dentry); - rc = sel_make_perm_files(newpolicy, classname, index, dentry); - - return rc; + return sel_make_perm_files(newpolicy, classname, index, dentry); } static int sel_make_classes(struct selinux_policy *newpolicy, diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h index 62990aa1ec9e..eda32c3d4c0a 100644 --- a/security/selinux/ss/context.h +++ b/security/selinux/ss/context.h @@ -38,7 +38,7 @@ static inline void mls_context_init(struct context *c) memset(&c->range, 0, sizeof(c->range)); } -static inline int mls_context_cpy(struct context *dst, struct context *src) +static inline int mls_context_cpy(struct context *dst, const struct context *src) { int rc; @@ -58,7 +58,7 @@ out: /* * Sets both levels in the MLS range of 'dst' to the low level of 'src'. */ -static inline int mls_context_cpy_low(struct context *dst, struct context *src) +static inline int mls_context_cpy_low(struct context *dst, const struct context *src) { int rc; @@ -78,7 +78,7 @@ out: /* * Sets both levels in the MLS range of 'dst' to the high level of 'src'. */ -static inline int mls_context_cpy_high(struct context *dst, struct context *src) +static inline int mls_context_cpy_high(struct context *dst, const struct context *src) { int rc; @@ -97,9 +97,10 @@ out: static inline int mls_context_glblub(struct context *dst, - struct context *c1, struct context *c2) + const struct context *c1, const struct context *c2) { - struct mls_range *dr = &dst->range, *r1 = &c1->range, *r2 = &c2->range; + struct mls_range *dr = &dst->range; + const struct mls_range *r1 = &c1->range, *r2 = &c2->range; int rc = 0; if (r1->level[1].sens < r2->level[0].sens || @@ -127,7 +128,7 @@ out: return rc; } -static inline int mls_context_cmp(struct context *c1, struct context *c2) +static inline int mls_context_cmp(const struct context *c1, const struct context *c2) { return ((c1->range.level[0].sens == c2->range.level[0].sens) && ebitmap_cmp(&c1->range.level[0].cat, &c2->range.level[0].cat) && @@ -147,7 +148,7 @@ static inline void context_init(struct context *c) memset(c, 0, sizeof(*c)); } -static inline int context_cpy(struct context *dst, struct context *src) +static inline int context_cpy(struct context *dst, const struct context *src) { int rc; @@ -180,7 +181,7 @@ static inline void context_destroy(struct context *c) mls_context_destroy(c); } -static inline int context_cmp(struct context *c1, struct context *c2) +static inline int context_cmp(const struct context *c1, const struct context *c2) { if (c1->len && c2->len) return (c1->len == c2->len && !strcmp(c1->str, c2->str)); diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index abde349c8321..d31b87be9a1e 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -28,9 +28,9 @@ static struct kmem_cache *ebitmap_node_cachep __ro_after_init; -int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2) +int ebitmap_cmp(const struct ebitmap *e1, const struct ebitmap *e2) { - struct ebitmap_node *n1, *n2; + const struct ebitmap_node *n1, *n2; if (e1->highbit != e2->highbit) return 0; @@ -50,9 +50,10 @@ int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2) return 1; } -int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src) +int ebitmap_cpy(struct ebitmap *dst, const struct ebitmap *src) { - struct ebitmap_node *n, *new, *prev; + struct ebitmap_node *new, *prev; + const struct ebitmap_node *n; ebitmap_init(dst); n = src->node; @@ -78,7 +79,7 @@ int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src) return 0; } -int ebitmap_and(struct ebitmap *dst, struct ebitmap *e1, struct ebitmap *e2) +int ebitmap_and(struct ebitmap *dst, const struct ebitmap *e1, const struct ebitmap *e2) { struct ebitmap_node *n; int bit, rc; @@ -217,9 +218,9 @@ netlbl_import_failure: * if last_e2bit is non-zero, the highest set bit in e2 cannot exceed * last_e2bit. */ -int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2, u32 last_e2bit) +int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2, u32 last_e2bit) { - struct ebitmap_node *n1, *n2; + const struct ebitmap_node *n1, *n2; int i; if (e1->highbit < e2->highbit) @@ -258,9 +259,9 @@ int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2, u32 last_e2bit) return 1; } -int ebitmap_get_bit(struct ebitmap *e, unsigned long bit) +int ebitmap_get_bit(const struct ebitmap *e, unsigned long bit) { - struct ebitmap_node *n; + const struct ebitmap_node *n; if (e->highbit < bit) return 0; @@ -467,7 +468,7 @@ bad: goto out; } -int ebitmap_write(struct ebitmap *e, void *fp) +int ebitmap_write(const struct ebitmap *e, void *fp) { struct ebitmap_node *n; u32 count; diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h index 58eb822f11ee..e5b57dc3fc53 100644 --- a/security/selinux/ss/ebitmap.h +++ b/security/selinux/ss/ebitmap.h @@ -44,7 +44,7 @@ struct ebitmap { #define ebitmap_length(e) ((e)->highbit) -static inline unsigned int ebitmap_start_positive(struct ebitmap *e, +static inline unsigned int ebitmap_start_positive(const struct ebitmap *e, struct ebitmap_node **n) { unsigned int ofs; @@ -62,7 +62,7 @@ static inline void ebitmap_init(struct ebitmap *e) memset(e, 0, sizeof(*e)); } -static inline unsigned int ebitmap_next_positive(struct ebitmap *e, +static inline unsigned int ebitmap_next_positive(const struct ebitmap *e, struct ebitmap_node **n, unsigned int bit) { @@ -85,7 +85,7 @@ static inline unsigned int ebitmap_next_positive(struct ebitmap *e, #define EBITMAP_NODE_OFFSET(node, bit) \ (((bit) - (node)->startbit) % EBITMAP_UNIT_SIZE) -static inline int ebitmap_node_get_bit(struct ebitmap_node *n, +static inline int ebitmap_node_get_bit(const struct ebitmap_node *n, unsigned int bit) { unsigned int index = EBITMAP_NODE_INDEX(n, bit); @@ -122,15 +122,15 @@ static inline void ebitmap_node_clr_bit(struct ebitmap_node *n, (bit) < ebitmap_length(e); \ (bit) = ebitmap_next_positive(e, &(n), bit)) \ -int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2); -int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src); -int ebitmap_and(struct ebitmap *dst, struct ebitmap *e1, struct ebitmap *e2); -int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2, u32 last_e2bit); -int ebitmap_get_bit(struct ebitmap *e, unsigned long bit); +int ebitmap_cmp(const struct ebitmap *e1, const struct ebitmap *e2); +int ebitmap_cpy(struct ebitmap *dst, const struct ebitmap *src); +int ebitmap_and(struct ebitmap *dst, const struct ebitmap *e1, const struct ebitmap *e2); +int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2, u32 last_e2bit); +int ebitmap_get_bit(const struct ebitmap *e, unsigned long bit); int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value); void ebitmap_destroy(struct ebitmap *e); int ebitmap_read(struct ebitmap *e, void *fp); -int ebitmap_write(struct ebitmap *e, void *fp); +int ebitmap_write(const struct ebitmap *e, void *fp); u32 ebitmap_hash(const struct ebitmap *e, u32 hash); #ifdef CONFIG_NETLABEL diff --git a/security/selinux/ss/mls_types.h b/security/selinux/ss/mls_types.h index 068e0d7809db..7d48d5e52233 100644 --- a/security/selinux/ss/mls_types.h +++ b/security/selinux/ss/mls_types.h @@ -27,13 +27,13 @@ struct mls_range { struct mls_level level[2]; /* low == level[0], high == level[1] */ }; -static inline int mls_level_eq(struct mls_level *l1, struct mls_level *l2) +static inline int mls_level_eq(const struct mls_level *l1, const struct mls_level *l2) { return ((l1->sens == l2->sens) && ebitmap_cmp(&l1->cat, &l2->cat)); } -static inline int mls_level_dom(struct mls_level *l1, struct mls_level *l2) +static inline int mls_level_dom(const struct mls_level *l1, const struct mls_level *l2) { return ((l1->sens >= l2->sens) && ebitmap_contains(&l1->cat, &l2->cat, 0)); diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index fe5fcf571c56..0092b29022f5 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -68,12 +68,6 @@ #include "policycap_names.h" #include "ima.h" -struct convert_context_args { - struct selinux_state *state; - struct policydb *oldp; - struct policydb *newp; -}; - struct selinux_policy_convert_data { struct convert_context_args args; struct sidtab_convert_params sidtab_params; @@ -2014,17 +2008,22 @@ static inline int convert_context_handle_invalid_context( return 0; } -/* - * Convert the values in the security context - * structure `oldc' from the values specified - * in the policy `p->oldp' to the values specified - * in the policy `p->newp', storing the new context - * in `newc'. Verify that the context is valid - * under the new policy. +/** + * services_convert_context - Convert a security context across policies. + * @args: populated convert_context_args struct + * @oldc: original context + * @newc: converted context + * @gfp_flags: allocation flags + * + * Convert the values in the security context structure @oldc from the values + * specified in the policy @args->oldp to the values specified in the policy + * @args->newp, storing the new context in @newc, and verifying that the + * context is valid under the new policy. */ -static int convert_context(struct context *oldc, struct context *newc, void *p) +int services_convert_context(struct convert_context_args *args, + struct context *oldc, struct context *newc, + gfp_t gfp_flags) { - struct convert_context_args *args; struct ocontext *oc; struct role_datum *role; struct type_datum *typdatum; @@ -2033,15 +2032,12 @@ static int convert_context(struct context *oldc, struct context *newc, void *p) u32 len; int rc; - args = p; - if (oldc->str) { - s = kstrdup(oldc->str, GFP_KERNEL); + s = kstrdup(oldc->str, gfp_flags); if (!s) return -ENOMEM; - rc = string_to_context_struct(args->newp, NULL, s, - newc, SECSID_NULL); + rc = string_to_context_struct(args->newp, NULL, s, newc, SECSID_NULL); if (rc == -EINVAL) { /* * Retain string representation for later mapping. @@ -2072,8 +2068,7 @@ static int convert_context(struct context *oldc, struct context *newc, void *p) /* Convert the user. */ usrdatum = symtab_search(&args->newp->p_users, - sym_name(args->oldp, - SYM_USERS, oldc->user - 1)); + sym_name(args->oldp, SYM_USERS, oldc->user - 1)); if (!usrdatum) goto bad; newc->user = usrdatum->value; @@ -2087,8 +2082,7 @@ static int convert_context(struct context *oldc, struct context *newc, void *p) /* Convert the type. */ typdatum = symtab_search(&args->newp->p_types, - sym_name(args->oldp, - SYM_TYPES, oldc->type - 1)); + sym_name(args->oldp, SYM_TYPES, oldc->type - 1)); if (!typdatum) goto bad; newc->type = typdatum->value; @@ -2122,8 +2116,7 @@ static int convert_context(struct context *oldc, struct context *newc, void *p) /* Check the validity of the new context. */ if (!policydb_context_isvalid(args->newp, newc)) { rc = convert_context_handle_invalid_context(args->state, - args->oldp, - oldc); + args->oldp, oldc); if (rc) goto bad; } @@ -2332,21 +2325,21 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len, goto err_free_isids; } + /* + * Convert the internal representations of contexts + * in the new SID table. + */ + convert_data = kmalloc(sizeof(*convert_data), GFP_KERNEL); if (!convert_data) { rc = -ENOMEM; goto err_free_isids; } - /* - * Convert the internal representations of contexts - * in the new SID table. - */ convert_data->args.state = state; convert_data->args.oldp = &oldpolicy->policydb; convert_data->args.newp = &newpolicy->policydb; - convert_data->sidtab_params.func = convert_context; convert_data->sidtab_params.args = &convert_data->args; convert_data->sidtab_params.target = newpolicy->sidtab; diff --git a/security/selinux/ss/services.h b/security/selinux/ss/services.h index 9555ad074303..c4301626487f 100644 --- a/security/selinux/ss/services.h +++ b/security/selinux/ss/services.h @@ -29,10 +29,19 @@ struct selinux_policy { u32 latest_granting; } __randomize_layout; -void services_compute_xperms_drivers(struct extended_perms *xperms, - struct avtab_node *node); +struct convert_context_args { + struct selinux_state *state; + struct policydb *oldp; + struct policydb *newp; +}; +void services_compute_xperms_drivers(struct extended_perms *xperms, + struct avtab_node *node); void services_compute_xperms_decision(struct extended_perms_decision *xpermd, - struct avtab_node *node); + struct avtab_node *node); + +int services_convert_context(struct convert_context_args *args, + struct context *oldc, struct context *newc, + gfp_t gfp_flags); #endif /* _SS_SERVICES_H_ */ diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c index a54b8652bfb5..38d25173aebd 100644 --- a/security/selinux/ss/sidtab.c +++ b/security/selinux/ss/sidtab.c @@ -18,6 +18,7 @@ #include "flask.h" #include "security.h" #include "sidtab.h" +#include "services.h" struct sidtab_str_cache { struct rcu_head rcu_member; @@ -292,7 +293,6 @@ int sidtab_context_to_sid(struct sidtab *s, struct context *context, } count = s->count; - convert = s->convert; /* bail out if we already reached max entries */ rc = -EOVERFLOW; @@ -316,25 +316,29 @@ int sidtab_context_to_sid(struct sidtab *s, struct context *context, * if we are building a new sidtab, we need to convert the context * and insert it there as well */ + convert = s->convert; if (convert) { + struct sidtab *target = convert->target; + rc = -ENOMEM; - dst_convert = sidtab_do_lookup(convert->target, count, 1); + dst_convert = sidtab_do_lookup(target, count, 1); if (!dst_convert) { context_destroy(&dst->context); goto out_unlock; } - rc = convert->func(context, &dst_convert->context, - convert->args); + rc = services_convert_context(convert->args, + context, &dst_convert->context, + GFP_ATOMIC); if (rc) { context_destroy(&dst->context); goto out_unlock; } dst_convert->sid = index_to_sid(count); dst_convert->hash = context_compute_hash(&dst_convert->context); - convert->target->count = count + 1; + target->count = count + 1; - hash_add_rcu(convert->target->context_to_sid, + hash_add_rcu(target->context_to_sid, &dst_convert->list, dst_convert->hash); } @@ -402,9 +406,10 @@ static int sidtab_convert_tree(union sidtab_entry_inner *edst, } i = 0; while (i < SIDTAB_LEAF_ENTRIES && *pos < count) { - rc = convert->func(&esrc->ptr_leaf->entries[i].context, - &edst->ptr_leaf->entries[i].context, - convert->args); + rc = services_convert_context(convert->args, + &esrc->ptr_leaf->entries[i].context, + &edst->ptr_leaf->entries[i].context, + GFP_KERNEL); if (rc) return rc; (*pos)++; diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h index 4eff0e49dcb2..72810a080e77 100644 --- a/security/selinux/ss/sidtab.h +++ b/security/selinux/ss/sidtab.h @@ -65,8 +65,7 @@ struct sidtab_isid_entry { }; struct sidtab_convert_params { - int (*func)(struct context *oldc, struct context *newc, void *args); - void *args; + struct convert_context_args *args; struct sidtab *target; }; diff --git a/security/smack/smack.h b/security/smack/smack.h index fc837dcebf96..e2239be7bd60 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -180,15 +180,6 @@ struct smack_known_list_elem { struct smack_known *smk_label; }; -/* Super block security struct flags for mount options */ -#define FSDEFAULT_MNT 0x01 -#define FSFLOOR_MNT 0x02 -#define FSHAT_MNT 0x04 -#define FSROOT_MNT 0x08 -#define FSTRANS_MNT 0x10 - -#define NUM_SMK_MNT_OPTS 5 - enum { Opt_error = -1, Opt_fsdefault = 0, diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 001831458fa2..9a82a15685d1 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -42,6 +42,7 @@ #include <linux/fs_context.h> #include <linux/fs_parser.h> #include <linux/watch_queue.h> +#include <linux/io_uring.h> #include "smack.h" #define TRANS_TRUE "TRUE" @@ -496,13 +497,11 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) */ static int smack_ptrace_traceme(struct task_struct *ptp) { - int rc; struct smack_known *skp; skp = smk_of_task(smack_cred(current_cred())); - rc = smk_ptrace_rule_check(ptp, skp, PTRACE_MODE_ATTACH, __func__); - return rc; + return smk_ptrace_rule_check(ptp, skp, PTRACE_MODE_ATTACH, __func__); } /** @@ -1394,6 +1393,74 @@ static int smack_inode_removexattr(struct user_namespace *mnt_userns, } /** + * smack_inode_set_acl - Smack check for setting posix acls + * @mnt_userns: the userns attached to the mnt this request came from + * @dentry: the object + * @acl_name: name of the posix acl + * @kacl: the posix acls + * + * Returns 0 if access is permitted, an error code otherwise + */ +static int smack_inode_set_acl(struct user_namespace *mnt_userns, + struct dentry *dentry, const char *acl_name, + struct posix_acl *kacl) +{ + struct smk_audit_info ad; + int rc; + + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); + smk_ad_setfield_u_fs_path_dentry(&ad, dentry); + + rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_WRITE, &ad); + rc = smk_bu_inode(d_backing_inode(dentry), MAY_WRITE, rc); + return rc; +} + +/** + * smack_inode_get_acl - Smack check for getting posix acls + * @mnt_userns: the userns attached to the mnt this request came from + * @dentry: the object + * @acl_name: name of the posix acl + * + * Returns 0 if access is permitted, an error code otherwise + */ +static int smack_inode_get_acl(struct user_namespace *mnt_userns, + struct dentry *dentry, const char *acl_name) +{ + struct smk_audit_info ad; + int rc; + + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); + smk_ad_setfield_u_fs_path_dentry(&ad, dentry); + + rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_READ, &ad); + rc = smk_bu_inode(d_backing_inode(dentry), MAY_READ, rc); + return rc; +} + +/** + * smack_inode_remove_acl - Smack check for getting posix acls + * @mnt_userns: the userns attached to the mnt this request came from + * @dentry: the object + * @acl_name: name of the posix acl + * + * Returns 0 if access is permitted, an error code otherwise + */ +static int smack_inode_remove_acl(struct user_namespace *mnt_userns, + struct dentry *dentry, const char *acl_name) +{ + struct smk_audit_info ad; + int rc; + + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); + smk_ad_setfield_u_fs_path_dentry(&ad, dentry); + + rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_WRITE, &ad); + rc = smk_bu_inode(d_backing_inode(dentry), MAY_WRITE, rc); + return rc; +} + +/** * smack_inode_getsecurity - get smack xattrs * @mnt_userns: active user namespace * @inode: the object @@ -2279,6 +2346,21 @@ static void smack_sk_free_security(struct sock *sk) } /** + * smack_sk_clone_security - Copy security context + * @sk: the old socket + * @newsk: the new socket + * + * Copy the security context of the old socket pointer to the cloned + */ +static void smack_sk_clone_security(const struct sock *sk, struct sock *newsk) +{ + struct socket_smack *ssp_old = sk->sk_security; + struct socket_smack *ssp_new = newsk->sk_security; + + *ssp_new = *ssp_old; +} + +/** * smack_ipv4host_label - check host based restrictions * @sip: the object end * @@ -3479,7 +3561,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) * * Returns the length of the smack label or an error code */ -static int smack_getprocattr(struct task_struct *p, char *name, char **value) +static int smack_getprocattr(struct task_struct *p, const char *name, char **value) { struct smack_known *skp = smk_of_task_struct_obj(p); char *cp; @@ -3992,12 +4074,12 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) * returns zero on success, an error code otherwise */ static int smack_socket_getpeersec_stream(struct socket *sock, - char __user *optval, - int __user *optlen, unsigned len) + sockptr_t optval, sockptr_t optlen, + unsigned int len) { struct socket_smack *ssp; char *rcp = ""; - int slen = 1; + u32 slen = 1; int rc = 0; ssp = sock->sk->sk_security; @@ -4005,15 +4087,16 @@ static int smack_socket_getpeersec_stream(struct socket *sock, rcp = ssp->smk_packet->smk_known; slen = strlen(rcp) + 1; } - - if (slen > len) + if (slen > len) { rc = -ERANGE; - else if (copy_to_user(optval, rcp, slen) != 0) - rc = -EFAULT; + goto out_len; + } - if (put_user(slen, optlen) != 0) + if (copy_to_sockptr(optval, rcp, slen)) + rc = -EFAULT; +out_len: + if (copy_to_sockptr(optlen, &slen, sizeof(slen))) rc = -EFAULT; - return rc; } @@ -4732,6 +4815,36 @@ static int smack_uring_sqpoll(void) return -EPERM; } +/** + * smack_uring_cmd - check on file operations for io_uring + * @ioucmd: the command in question + * + * Make a best guess about whether a io_uring "command" should + * be allowed. Use the same logic used for determining if the + * file could be opened for read in the absence of better criteria. + */ +static int smack_uring_cmd(struct io_uring_cmd *ioucmd) +{ + struct file *file = ioucmd->file; + struct smk_audit_info ad; + struct task_smack *tsp; + struct inode *inode; + int rc; + + if (!file) + return -EINVAL; + + tsp = smack_cred(file->f_cred); + inode = file_inode(file); + + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); + smk_ad_setfield_u_fs_path(&ad, file->f_path); + rc = smk_tskacc(tsp, smk_of_inode(inode), MAY_READ, &ad); + rc = smk_bu_credfile(file->f_cred, file, MAY_READ, rc); + + return rc; +} + #endif /* CONFIG_IO_URING */ struct lsm_blob_sizes smack_blob_sizes __lsm_ro_after_init = { @@ -4772,6 +4885,9 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(inode_post_setxattr, smack_inode_post_setxattr), LSM_HOOK_INIT(inode_getxattr, smack_inode_getxattr), LSM_HOOK_INIT(inode_removexattr, smack_inode_removexattr), + LSM_HOOK_INIT(inode_set_acl, smack_inode_set_acl), + LSM_HOOK_INIT(inode_get_acl, smack_inode_get_acl), + LSM_HOOK_INIT(inode_remove_acl, smack_inode_remove_acl), LSM_HOOK_INIT(inode_getsecurity, smack_inode_getsecurity), LSM_HOOK_INIT(inode_setsecurity, smack_inode_setsecurity), LSM_HOOK_INIT(inode_listsecurity, smack_inode_listsecurity), @@ -4851,6 +4967,7 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(socket_getpeersec_dgram, smack_socket_getpeersec_dgram), LSM_HOOK_INIT(sk_alloc_security, smack_sk_alloc_security), LSM_HOOK_INIT(sk_free_security, smack_sk_free_security), + LSM_HOOK_INIT(sk_clone_security, smack_sk_clone_security), LSM_HOOK_INIT(sock_graft, smack_sock_graft), LSM_HOOK_INIT(inet_conn_request, smack_inet_conn_request), LSM_HOOK_INIT(inet_csk_clone, smack_inet_csk_clone), @@ -4889,6 +5006,7 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { #ifdef CONFIG_IO_URING LSM_HOOK_INIT(uring_override_creds, smack_uring_override_creds), LSM_HOOK_INIT(uring_sqpoll, smack_uring_sqpoll), + LSM_HOOK_INIT(uring_cmd, smack_uring_cmd), #endif }; diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c index 023bedd9dfa3..7cf8fdbb29bf 100644 --- a/security/tomoyo/audit.c +++ b/security/tomoyo/audit.c @@ -423,7 +423,7 @@ void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...) int len; va_start(args, fmt); - len = vsnprintf((char *) &len, 1, fmt, args) + 1; + len = vsnprintf(NULL, 0, fmt, args) + 1; va_end(args); va_start(args, fmt); tomoyo_write_log2(r, len, fmt, args); diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index ff17abc96e5c..f4cd9b58b205 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -2057,7 +2057,7 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) bool quota_exceeded = false; va_start(args, fmt); - len = vsnprintf((char *) &len, 1, fmt, args) + 1; + len = vsnprintf(NULL, 0, fmt, args) + 1; va_end(args); /* Write /sys/kernel/security/tomoyo/audit. */ va_start(args, fmt); diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 1e6077568fde..8f3b90b6e03d 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -717,7 +717,7 @@ int tomoyo_path_number_perm(const u8 type, const struct path *path, int idx; if (tomoyo_init_request_info(&r, NULL, tomoyo_pn2mac[type]) - == TOMOYO_CONFIG_DISABLED || !path->dentry) + == TOMOYO_CONFIG_DISABLED) return 0; idx = tomoyo_read_lock(); if (!tomoyo_get_realpath(&buf, path)) diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index df4798980416..1c483ee7f93d 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -240,11 +240,8 @@ char *tomoyo_realpath_from_path(const struct path *path) char *name = NULL; unsigned int buf_len = PAGE_SIZE / 2; struct dentry *dentry = path->dentry; - struct super_block *sb; + struct super_block *sb = dentry->d_sb; - if (!dentry) - return NULL; - sb = dentry->d_sb; while (1) { char *pos; struct inode *inode; @@ -264,10 +261,8 @@ char *tomoyo_realpath_from_path(const struct path *path) inode = d_backing_inode(sb->s_root); /* * Get local name for filesystems without rename() operation - * or dentry without vfsmount. */ - if (!path->mnt || - (!inode->i_op->rename && + if ((!inode->i_op->rename && !(sb->s_type->fs_flags & FS_REQUIRES_DEV))) pos = tomoyo_get_local_path(path->dentry, buf, buf_len - 1); diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 71e82d855ebf..af04a7b7eb28 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -135,6 +135,18 @@ static int tomoyo_path_truncate(const struct path *path) } /** + * tomoyo_file_truncate - Target for security_file_truncate(). + * + * @file: Pointer to "struct file". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_file_truncate(struct file *file) +{ + return tomoyo_path_truncate(&file->f_path); +} + +/** * tomoyo_path_unlink - Target for security_path_unlink(). * * @parent: Pointer to "struct path". @@ -545,6 +557,7 @@ static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(bprm_check_security, tomoyo_bprm_check_security), LSM_HOOK_INIT(file_fcntl, tomoyo_file_fcntl), LSM_HOOK_INIT(file_open, tomoyo_file_open), + LSM_HOOK_INIT(file_truncate, tomoyo_file_truncate), LSM_HOOK_INIT(path_truncate, tomoyo_path_truncate), LSM_HOOK_INIT(path_unlink, tomoyo_path_unlink), LSM_HOOK_INIT(path_mkdir, tomoyo_path_mkdir), |