From f3b8788cde61b02f1e6c202f8fac4360e6adbafc Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Tue, 12 Sep 2023 13:56:46 -0700 Subject: LSM: Identify modules by more than name Create a struct lsm_id to contain identifying information about Linux Security Modules (LSMs). At inception this contains the name of the module and an identifier associated with the security module. Change the security_add_hooks() interface to use this structure. Change the individual modules to maintain their own struct lsm_id and pass it to security_add_hooks(). The values are for LSM identifiers are defined in a new UAPI header file linux/lsm.h. Each existing LSM has been updated to include it's LSMID in the lsm_id. The LSM ID values are sequential, with the oldest module LSM_ID_CAPABILITY being the lowest value and the existing modules numbered in the order they were included in the main line kernel. This is an arbitrary convention for assigning the values, but none better presents itself. The value 0 is defined as being invalid. The values 1-99 are reserved for any special case uses which may arise in the future. This may include attributes of the LSM infrastructure itself, possibly related to namespacing or network attribute management. A special range is identified for such attributes to help reduce confusion for developers unfamiliar with LSMs. LSM attribute values are defined for the attributes presented by modules that are available today. As with the LSM IDs, The value 0 is defined as being invalid. The values 1-99 are reserved for any special case uses which may arise in the future. Cc: linux-security-module Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook Reviewed-by: Serge Hallyn Reviewed-by: Mickael Salaun Reviewed-by: John Johansen Signed-off-by: Kees Cook Nacked-by: Tetsuo Handa [PM: forward ported beyond v6.6 due merge window changes] Signed-off-by: Paul Moore --- security/selinux/hooks.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'security/selinux') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index feda711c6b7b..f2423dfd19cd 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -92,6 +92,7 @@ #include #include #include +#include #include "avc.h" #include "objsec.h" @@ -6950,6 +6951,11 @@ static int selinux_uring_cmd(struct io_uring_cmd *ioucmd) } #endif /* CONFIG_IO_URING */ +const struct lsm_id selinux_lsmid = { + .name = "selinux", + .id = LSM_ID_SELINUX, +}; + /* * IMPORTANT NOTE: When adding new hooks, please be careful to keep this order: * 1. any hooks that don't belong to (2.) or (3.) below, @@ -7270,7 +7276,8 @@ static __init int selinux_init(void) hashtab_cache_init(); - security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux"); + security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), + &selinux_lsmid); if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET)) panic("SELinux: Unable to register AVC netcache callback\n"); -- cgit v1.2.3 From 762c934317e6f4b576eb4aa75e5facf4968a4a8f Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Tue, 12 Sep 2023 13:56:55 -0700 Subject: SELinux: Add selfattr hooks Add hooks for setselfattr and getselfattr. These hooks are not very different from their setprocattr and getprocattr equivalents, and much of the code is shared. Cc: selinux@vger.kernel.org Signed-off-by: Casey Schaufler Signed-off-by: Paul Moore --- security/selinux/hooks.c | 134 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 107 insertions(+), 27 deletions(-) (limited to 'security/selinux') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f2423dfd19cd..b6c7930a3ab2 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6285,8 +6285,8 @@ static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode) inode_doinit_with_dentry(inode, dentry); } -static int selinux_getprocattr(struct task_struct *p, - const char *name, char **value) +static int selinux_lsm_getattr(unsigned int attr, struct task_struct *p, + char **value) { const struct task_security_struct *__tsec; u32 sid; @@ -6303,20 +6303,27 @@ static int selinux_getprocattr(struct task_struct *p, goto bad; } - if (!strcmp(name, "current")) + switch (attr) { + case LSM_ATTR_CURRENT: sid = __tsec->sid; - else if (!strcmp(name, "prev")) + break; + case LSM_ATTR_PREV: sid = __tsec->osid; - else if (!strcmp(name, "exec")) + break; + case LSM_ATTR_EXEC: sid = __tsec->exec_sid; - else if (!strcmp(name, "fscreate")) + break; + case LSM_ATTR_FSCREATE: sid = __tsec->create_sid; - else if (!strcmp(name, "keycreate")) + break; + case LSM_ATTR_KEYCREATE: sid = __tsec->keycreate_sid; - else if (!strcmp(name, "sockcreate")) + break; + case LSM_ATTR_SOCKCREATE: sid = __tsec->sockcreate_sid; - else { - error = -EINVAL; + break; + default: + error = -EOPNOTSUPP; goto bad; } rcu_read_unlock(); @@ -6334,7 +6341,7 @@ bad: return error; } -static int selinux_setprocattr(const char *name, void *value, size_t size) +static int selinux_lsm_setattr(u64 attr, void *value, size_t size) { struct task_security_struct *tsec; struct cred *new; @@ -6345,23 +6352,31 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) /* * Basic control over ability to set these attributes at all. */ - if (!strcmp(name, "exec")) + switch (attr) { + case LSM_ATTR_EXEC: error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, PROCESS__SETEXEC, NULL); - else if (!strcmp(name, "fscreate")) + break; + case LSM_ATTR_FSCREATE: error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, PROCESS__SETFSCREATE, NULL); - else if (!strcmp(name, "keycreate")) + break; + case LSM_ATTR_KEYCREATE: error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, PROCESS__SETKEYCREATE, NULL); - else if (!strcmp(name, "sockcreate")) + break; + case LSM_ATTR_SOCKCREATE: error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, PROCESS__SETSOCKCREATE, NULL); - else if (!strcmp(name, "current")) + break; + case LSM_ATTR_CURRENT: error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, PROCESS__SETCURRENT, NULL); - else - error = -EINVAL; + break; + default: + error = -EOPNOTSUPP; + break; + } if (error) return error; @@ -6373,13 +6388,14 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) } error = security_context_to_sid(value, size, &sid, GFP_KERNEL); - if (error == -EINVAL && !strcmp(name, "fscreate")) { + if (error == -EINVAL && attr == LSM_ATTR_FSCREATE) { if (!has_cap_mac_admin(true)) { struct audit_buffer *ab; size_t audit_size; - /* We strip a nul only if it is at the end, otherwise the - * context contains a nul and we should audit that */ + /* We strip a nul only if it is at the end, + * otherwise the context contains a nul and + * we should audit that */ if (str[size - 1] == '\0') audit_size = size - 1; else @@ -6390,7 +6406,8 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) if (!ab) return error; audit_log_format(ab, "op=fscreate invalid_context="); - audit_log_n_untrustedstring(ab, value, audit_size); + audit_log_n_untrustedstring(ab, value, + audit_size); audit_log_end(ab); return error; @@ -6413,11 +6430,11 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) checks and may_create for the file creation checks. The operation will then fail if the context is not permitted. */ tsec = selinux_cred(new); - if (!strcmp(name, "exec")) { + if (attr == LSM_ATTR_EXEC) { tsec->exec_sid = sid; - } else if (!strcmp(name, "fscreate")) { + } else if (attr == LSM_ATTR_FSCREATE) { tsec->create_sid = sid; - } else if (!strcmp(name, "keycreate")) { + } else if (attr == LSM_ATTR_KEYCREATE) { if (sid) { error = avc_has_perm(mysid, sid, SECCLASS_KEY, KEY__CREATE, NULL); @@ -6425,9 +6442,9 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) goto abort_change; } tsec->keycreate_sid = sid; - } else if (!strcmp(name, "sockcreate")) { + } else if (attr == LSM_ATTR_SOCKCREATE) { tsec->sockcreate_sid = sid; - } else if (!strcmp(name, "current")) { + } else if (attr == LSM_ATTR_CURRENT) { error = -EINVAL; if (sid == 0) goto abort_change; @@ -6469,6 +6486,67 @@ abort_change: return error; } +static int selinux_getselfattr(unsigned int attr, struct lsm_ctx __user *ctx, + size_t *size, u32 flags) +{ + char *value; + size_t total_len; + int len; + int rc = 0; + + len = selinux_lsm_getattr(attr, current, &value); + if (len < 0) + return len; + + total_len = ALIGN(struct_size(ctx, ctx, len), 8); + + if (total_len > *size) + rc = -E2BIG; + else if (ctx) + rc = lsm_fill_user_ctx(ctx, value, len, LSM_ID_SELINUX, 0); + + kfree(value); + *size = total_len; + if (rc < 0) + return rc; + return 1; +} + +static int selinux_setselfattr(unsigned int attr, struct lsm_ctx *ctx, + size_t size, u32 flags) +{ + int rc; + + rc = selinux_lsm_setattr(attr, ctx->ctx, ctx->ctx_len); + if (rc > 0) + return 0; + return rc; +} + +static int selinux_getprocattr(struct task_struct *p, + const char *name, char **value) +{ + unsigned int attr = lsm_name_to_attr(name); + int rc; + + if (attr) { + rc = selinux_lsm_getattr(attr, p, value); + if (rc != -EOPNOTSUPP) + return rc; + } + + return -EINVAL; +} + +static int selinux_setprocattr(const char *name, void *value, size_t size) +{ + int attr = lsm_name_to_attr(name); + + if (attr) + return selinux_lsm_setattr(attr, value, size); + return -EINVAL; +} + static int selinux_ismaclabel(const char *name) { return (strcmp(name, XATTR_SELINUX_SUFFIX) == 0); @@ -7097,6 +7175,8 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { LSM_HOOK_INIT(d_instantiate, selinux_d_instantiate), + LSM_HOOK_INIT(getselfattr, selinux_getselfattr), + LSM_HOOK_INIT(setselfattr, selinux_setselfattr), LSM_HOOK_INIT(getprocattr, selinux_getprocattr), LSM_HOOK_INIT(setprocattr, selinux_setprocattr), -- cgit v1.2.3 From d7cf3412a9f6c547e5ee443fa7644e08898aa3e2 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 24 Oct 2023 14:44:00 -0400 Subject: lsm: consolidate buffer size handling into lsm_fill_user_ctx() While we have a lsm_fill_user_ctx() helper function designed to make life easier for LSMs which return lsm_ctx structs to userspace, we didn't include all of the buffer length safety checks and buffer padding adjustments in the helper. This led to code duplication across the different LSMs and the possibility for mistakes across the different LSM subsystems. In order to reduce code duplication and decrease the chances of silly mistakes, we're consolidating all of this code into the lsm_fill_user_ctx() helper. The buffer padding is also modified from a fixed 8-byte alignment to an alignment that matches the word length of the machine (BITS_PER_LONG / 8). Signed-off-by: Paul Moore --- security/selinux/hooks.c | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) (limited to 'security/selinux') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b6c7930a3ab2..942f2b8c4ebb 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6486,30 +6486,32 @@ abort_change: return error; } +/** + * selinux_getselfattr - Get SELinux current task attributes + * @attr: the requested attribute + * @ctx: buffer to receive the result + * @size: buffer size (input), buffer size used (output) + * @flags: unused + * + * Fill the passed user space @ctx with the details of the requested + * attribute. + * + * Returns the number of attributes on success, an error code otherwise. + * There will only ever be one attribute. + */ static int selinux_getselfattr(unsigned int attr, struct lsm_ctx __user *ctx, size_t *size, u32 flags) { - char *value; - size_t total_len; - int len; - int rc = 0; - - len = selinux_lsm_getattr(attr, current, &value); - if (len < 0) - return len; - - total_len = ALIGN(struct_size(ctx, ctx, len), 8); - - if (total_len > *size) - rc = -E2BIG; - else if (ctx) - rc = lsm_fill_user_ctx(ctx, value, len, LSM_ID_SELINUX, 0); - - kfree(value); - *size = total_len; - if (rc < 0) - return rc; - return 1; + int rc; + char *val; + int val_len; + + val_len = selinux_lsm_getattr(attr, current, &val); + if (val_len < 0) + return val_len; + rc = lsm_fill_user_ctx(ctx, size, val, val_len, LSM_ID_SELINUX, 0); + kfree(val); + return (!rc ? 1 : rc); } static int selinux_setselfattr(unsigned int attr, struct lsm_ctx *ctx, -- cgit v1.2.3 From b1a867eeb8ab5e097178728b01cc504c6806acca Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 10 Nov 2023 12:09:33 -0500 Subject: lsm: mark the lsm_id variables are marked as static As the kernel test robot helpfully reminded us, all of the lsm_id instances defined inside the various LSMs should be marked as static. The one exception is Landlock which uses its lsm_id variable across multiple source files with an extern declaration in a header file. Reported-by: kernel test robot Suggested-by: Casey Schaufler Reviewed-by: Casey Schaufler Signed-off-by: Paul Moore --- security/selinux/hooks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security/selinux') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 942f2b8c4ebb..b340425ccfae 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -7031,7 +7031,7 @@ static int selinux_uring_cmd(struct io_uring_cmd *ioucmd) } #endif /* CONFIG_IO_URING */ -const struct lsm_id selinux_lsmid = { +static const struct lsm_id selinux_lsmid = { .name = "selinux", .id = LSM_ID_SELINUX, }; -- cgit v1.2.3 From f1bb47a31dff6d4b34fb14e99850860ee74bb003 Mon Sep 17 00:00:00 2001 From: Alfred Piccioni Date: Tue, 19 Dec 2023 10:09:09 +0100 Subject: lsm: new security_file_ioctl_compat() hook Some ioctl commands do not require ioctl permission, but are routed to other permissions such as FILE_GETATTR or FILE_SETATTR. This routing is done by comparing the ioctl cmd to a set of 64-bit flags (FS_IOC_*). However, if a 32-bit process is running on a 64-bit kernel, it emits 32-bit flags (FS_IOC32_*) for certain ioctl operations. These flags are being checked erroneously, which leads to these ioctl operations being routed to the ioctl permission, rather than the correct file permissions. This was also noted in a RED-PEN finding from a while back - "/* RED-PEN how should LSM module know it's handling 32bit? */". This patch introduces a new hook, security_file_ioctl_compat(), that is called from the compat ioctl syscall. All current LSMs have been changed to support this hook. Reviewing the three places where we are currently using security_file_ioctl(), it appears that only SELinux needs a dedicated compat change; TOMOYO and SMACK appear to be functional without any change. Cc: stable@vger.kernel.org Fixes: 0b24dcb7f2f7 ("Revert "selinux: simplify ioctl checking"") Signed-off-by: Alfred Piccioni Reviewed-by: Stephen Smalley [PM: subject tweak, line length fixes, and alignment corrections] Signed-off-by: Paul Moore --- security/selinux/hooks.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'security/selinux') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b340425ccfae..179540441115 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3732,6 +3732,33 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd, return error; } +static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd, + unsigned long arg) +{ + /* + * If we are in a 64-bit kernel running 32-bit userspace, we need to + * make sure we don't compare 32-bit flags to 64-bit flags. + */ + switch (cmd) { + case FS_IOC32_GETFLAGS: + cmd = FS_IOC_GETFLAGS; + break; + case FS_IOC32_SETFLAGS: + cmd = FS_IOC_SETFLAGS; + break; + case FS_IOC32_GETVERSION: + cmd = FS_IOC_GETVERSION; + break; + case FS_IOC32_SETVERSION: + cmd = FS_IOC_SETVERSION; + break; + default: + break; + } + + return selinux_file_ioctl(file, cmd, arg); +} + static int default_noexec __ro_after_init; static int file_map_prot_check(struct file *file, unsigned long prot, int shared) @@ -7122,6 +7149,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { LSM_HOOK_INIT(file_permission, selinux_file_permission), LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security), LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl), + LSM_HOOK_INIT(file_ioctl_compat, selinux_file_ioctl_compat), LSM_HOOK_INIT(mmap_file, selinux_mmap_file), LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr), LSM_HOOK_INIT(file_mprotect, selinux_file_mprotect), -- cgit v1.2.3