selinux/stable-5.16 PR 20211101

-----BEGIN PGP SIGNATURE-----
 
 iQJIBAABCAAyFiEES0KozwfymdVUl37v6iDy2pc3iXMFAmGANbAUHHBhdWxAcGF1
 bC1tb29yZS5jb20ACgkQ6iDy2pc3iXNaMBAAg+9gZr0F7xiafu8JFZqZfx/AQdJ2
 G2cn3le+/tXGZmF8m/+82lOaR6LeQLatgSDJNSkXWkKr0nRwseQJDbtRfvYJdn0t
 Ax05/Fmz6OGxQ2wgRYgaFiSrKpE5p3NhDtiLFVdkCJaQNe/8DZOc7NhBl6EjZf3x
 ubhl2hUiJ4AmiXGwcYhr4uKgP4nhW8OM1/OkskVi+bBMmLA8KTY9kslmIDP5E3BW
 29W4qhqeLNQupY5dGMEMVcyxY9ZUWpO39q4uOaQVZrUGE7xABkj/jhnxT5gFTSlI
 pu8VhsYXm9KuRVveIsv0L5SZfadwoM9YAl7ki1wD3W5rHqOAte3rBTm6VmNlQwfU
 MqxP65Jiyxudxet5Be3/dCRH/+MDQuwBxivgmZXbeVxor2SeznVb0GDaEUC5FSHu
 CJIgWtQzsPJMxgAEGXN4F3QGP0htTTJni56GUPOsrf4TIBW02TT+oLTLFRIokQQL
 INNOfwVSRXElnCsvxsHR4oB+JZ9pJyBaAmeupcQ6jmcKiWlbLj4s+W0U0pM5h91v
 hmMpz7KMxrX6gVL4gB2Jj4aN3r5YRbq26NBu6D+wdwwBTeTTocaHSpAqkv4buClf
 uNk3cG8Hkp8TTg9cM8jYgpxMyzKH/AI/Uw3VhEa1xCiq2Ck3DgfnZvnvcRRaZevU
 FPgmwgqePJXGi60=
 =sb8J
 -----END PGP SIGNATURE-----

Merge tag 'selinux-pr-20211101' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux

Pull selinux updates from Paul Moore:

 - Add LSM/SELinux/Smack controls and auditing for io-uring.

   As usual, the individual commit descriptions have more detail, but we
   were basically missing two things which we're adding here:

      + establishment of a proper audit context so that auditing of
        io-uring ops works similarly to how it does for syscalls (with
        some io-uring additions because io-uring ops are *not* syscalls)

      + additional LSM hooks to enable access control points for some of
        the more unusual io-uring features, e.g. credential overrides.

   The additional audit callouts and LSM hooks were done in conjunction
   with the io-uring folks, based on conversations and RFC patches
   earlier in the year.

 - Fixup the binder credential handling so that the proper credentials
   are used in the LSM hooks; the commit description and the code
   comment which is removed in these patches are helpful to understand
   the background and why this is the proper fix.

 - Enable SELinux genfscon policy support for securityfs, allowing
   improved SELinux filesystem labeling for other subsystems which make
   use of securityfs, e.g. IMA.

* tag 'selinux-pr-20211101' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux:
  security: Return xattr name from security_dentry_init_security()
  selinux: fix a sock regression in selinux_ip_postroute_compat()
  binder: use cred instead of task for getsecid
  binder: use cred instead of task for selinux checks
  binder: use euid from cred instead of using task
  LSM: Avoid warnings about potentially unused hook variables
  selinux: fix all of the W=1 build warnings
  selinux: make better use of the nf_hook_state passed to the NF hooks
  selinux: fix race condition when computing ocontext SIDs
  selinux: remove unneeded ipv6 hook wrappers
  selinux: remove the SELinux lockdown implementation
  selinux: enable genfscon labeling for securityfs
  Smack: Brutalist io_uring support
  selinux: add support for the io_uring access controls
  lsm,io_uring: add LSM hooks to io_uring
  io_uring: convert io_uring to the secure anon inode interface
  fs: add anon_inode_getfile_secure() similar to anon_inode_getfd_secure()
  audit: add filtering for io_uring records
  audit,io_uring,io-wq: add some basic audit support to io_uring
  audit: prepare audit_context for use in calling contexts beyond syscalls
This commit is contained in:
Linus Torvalds 2021-11-01 21:06:18 -07:00
commit cdab10bf32
28 changed files with 883 additions and 420 deletions

View File

@ -2056,7 +2056,7 @@ static int binder_translate_binder(struct flat_binder_object *fp,
ret = -EINVAL;
goto done;
}
if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
if (security_binder_transfer_binder(proc->cred, target_proc->cred)) {
ret = -EPERM;
goto done;
}
@ -2102,7 +2102,7 @@ static int binder_translate_handle(struct flat_binder_object *fp,
proc->pid, thread->pid, fp->handle);
return -EINVAL;
}
if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
if (security_binder_transfer_binder(proc->cred, target_proc->cred)) {
ret = -EPERM;
goto done;
}
@ -2190,7 +2190,7 @@ static int binder_translate_fd(u32 fd, binder_size_t fd_offset,
ret = -EBADF;
goto err_fget;
}
ret = security_binder_transfer_file(proc->tsk, target_proc->tsk, file);
ret = security_binder_transfer_file(proc->cred, target_proc->cred, file);
if (ret < 0) {
ret = -EPERM;
goto err_security;
@ -2595,8 +2595,8 @@ static void binder_transaction(struct binder_proc *proc,
return_error_line = __LINE__;
goto err_invalid_target_handle;
}
if (security_binder_transaction(proc->tsk,
target_proc->tsk) < 0) {
if (security_binder_transaction(proc->cred,
target_proc->cred) < 0) {
return_error = BR_FAILED_REPLY;
return_error_param = -EPERM;
return_error_line = __LINE__;
@ -2711,7 +2711,7 @@ static void binder_transaction(struct binder_proc *proc,
t->from = thread;
else
t->from = NULL;
t->sender_euid = task_euid(proc->tsk);
t->sender_euid = proc->cred->euid;
t->to_proc = target_proc;
t->to_thread = target_thread;
t->code = tr->code;
@ -2722,16 +2722,7 @@ static void binder_transaction(struct binder_proc *proc,
u32 secid;
size_t added_size;
/*
* Arguably this should be the task's subjective LSM secid but
* we can't reliably access the subjective creds of a task
* other than our own so we must use the objective creds, which
* are safe to access. The downside is that if a task is
* temporarily overriding it's creds it will not be reflected
* here; however, it isn't clear that binder would handle that
* case well anyway.
*/
security_task_getsecid_obj(proc->tsk, &secid);
security_cred_getsecid(proc->cred, &secid);
ret = security_secid_to_secctx(secid, &secctx, &secctx_sz);
if (ret) {
return_error = BR_FAILED_REPLY;
@ -4353,6 +4344,7 @@ static void binder_free_proc(struct binder_proc *proc)
}
binder_alloc_deferred_release(&proc->alloc);
put_task_struct(proc->tsk);
put_cred(proc->cred);
binder_stats_deleted(BINDER_STAT_PROC);
kfree(proc);
}
@ -4564,7 +4556,7 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp,
ret = -EBUSY;
goto out;
}
ret = security_binder_set_context_mgr(proc->tsk);
ret = security_binder_set_context_mgr(proc->cred);
if (ret < 0)
goto out;
if (uid_valid(context->binder_context_mgr_uid)) {
@ -5055,6 +5047,7 @@ static int binder_open(struct inode *nodp, struct file *filp)
spin_lock_init(&proc->outer_lock);
get_task_struct(current->group_leader);
proc->tsk = current->group_leader;
proc->cred = get_cred(filp->f_cred);
INIT_LIST_HEAD(&proc->todo);
init_waitqueue_head(&proc->freeze_wait);
proc->default_priority = task_nice(current);

View File

@ -364,6 +364,9 @@ struct binder_ref {
* (invariant after initialized)
* @tsk task_struct for group_leader of process
* (invariant after initialized)
* @cred struct cred associated with the `struct file`
* in binder_open()
* (invariant after initialized)
* @deferred_work_node: element for binder_deferred_list
* (protected by binder_deferred_lock)
* @deferred_work: bitmap of deferred work to perform
@ -426,6 +429,7 @@ struct binder_proc {
struct list_head waiting_threads;
int pid;
struct task_struct *tsk;
const struct cred *cred;
struct hlist_node deferred_work_node;
int deferred_work;
int outstanding_txns;

View File

@ -148,6 +148,35 @@ struct file *anon_inode_getfile(const char *name,
}
EXPORT_SYMBOL_GPL(anon_inode_getfile);
/**
* anon_inode_getfile_secure - Like anon_inode_getfile(), but creates a new
* !S_PRIVATE anon inode rather than reuse the
* singleton anon inode and calls the
* inode_init_security_anon() LSM hook. This
* allows for both the inode to have its own
* security context and for the LSM to enforce
* policy on the inode's creation.
*
* @name: [in] name of the "class" of the new file
* @fops: [in] file operations for the new file
* @priv: [in] private data for the new file (will be file's private_data)
* @flags: [in] flags
* @context_inode:
* [in] the logical relationship with the new inode (optional)
*
* The LSM may use @context_inode in inode_init_security_anon(), but a
* reference to it is not held. Returns the newly created file* or an error
* pointer. See the anon_inode_getfile() documentation for more information.
*/
struct file *anon_inode_getfile_secure(const char *name,
const struct file_operations *fops,
void *priv, int flags,
const struct inode *context_inode)
{
return __anon_inode_getfile(name, fops, priv, flags,
context_inode, true);
}
static int __anon_inode_getfd(const char *name,
const struct file_operations *fops,
void *priv, int flags,

View File

@ -1311,7 +1311,7 @@ int ceph_security_init_secctx(struct dentry *dentry, umode_t mode,
int err;
err = security_dentry_init_security(dentry, mode, &dentry->d_name,
&as_ctx->sec_ctx,
&name, &as_ctx->sec_ctx,
&as_ctx->sec_ctxlen);
if (err < 0) {
WARN_ON_ONCE(err != -EOPNOTSUPP);
@ -1335,7 +1335,6 @@ int ceph_security_init_secctx(struct dentry *dentry, umode_t mode,
* It only supports single security module and only selinux has
* dentry_init_security hook.
*/
name = XATTR_NAME_SELINUX;
name_len = strlen(name);
err = ceph_pagelist_reserve(pagelist,
4 * 2 + name_len + as_ctx->sec_ctxlen);

View File

@ -14,6 +14,7 @@
#include <linux/rculist_nulls.h>
#include <linux/cpu.h>
#include <linux/tracehook.h>
#include <linux/audit.h>
#include <uapi/linux/io_uring.h>
#include "io-wq.h"
@ -593,6 +594,8 @@ static int io_wqe_worker(void *data)
snprintf(buf, sizeof(buf), "iou-wrk-%d", wq->task->pid);
set_task_comm(current, buf);
audit_alloc_kernel(current);
while (!test_bit(IO_WQ_BIT_EXIT, &wq->state)) {
long ret;
@ -631,6 +634,7 @@ loop:
io_worker_handle_work(worker);
}
audit_free(current);
io_worker_exit(worker);
return 0;
}

View File

@ -79,6 +79,8 @@
#include <linux/pagemap.h>
#include <linux/io_uring.h>
#include <linux/tracehook.h>
#include <linux/audit.h>
#include <linux/security.h>
#define CREATE_TRACE_POINTS
#include <trace/events/io_uring.h>
@ -912,6 +914,8 @@ struct io_op_def {
unsigned needs_async_setup : 1;
/* opcode is not supported by this kernel */
unsigned not_supported : 1;
/* skip auditing */
unsigned audit_skip : 1;
/* size of async data needed, if any */
unsigned short async_size;
};
@ -925,6 +929,7 @@ static const struct io_op_def io_op_defs[] = {
.buffer_select = 1,
.needs_async_setup = 1,
.plug = 1,
.audit_skip = 1,
.async_size = sizeof(struct io_async_rw),
},
[IORING_OP_WRITEV] = {
@ -934,16 +939,19 @@ static const struct io_op_def io_op_defs[] = {
.pollout = 1,
.needs_async_setup = 1,
.plug = 1,
.audit_skip = 1,
.async_size = sizeof(struct io_async_rw),
},
[IORING_OP_FSYNC] = {
.needs_file = 1,
.audit_skip = 1,
},
[IORING_OP_READ_FIXED] = {
.needs_file = 1,
.unbound_nonreg_file = 1,
.pollin = 1,
.plug = 1,
.audit_skip = 1,
.async_size = sizeof(struct io_async_rw),
},
[IORING_OP_WRITE_FIXED] = {
@ -952,15 +960,20 @@ static const struct io_op_def io_op_defs[] = {
.unbound_nonreg_file = 1,
.pollout = 1,
.plug = 1,
.audit_skip = 1,
.async_size = sizeof(struct io_async_rw),
},
[IORING_OP_POLL_ADD] = {
.needs_file = 1,
.unbound_nonreg_file = 1,
.audit_skip = 1,
},
[IORING_OP_POLL_REMOVE] = {
.audit_skip = 1,
},
[IORING_OP_POLL_REMOVE] = {},
[IORING_OP_SYNC_FILE_RANGE] = {
.needs_file = 1,
.audit_skip = 1,
},
[IORING_OP_SENDMSG] = {
.needs_file = 1,
@ -978,18 +991,23 @@ static const struct io_op_def io_op_defs[] = {
.async_size = sizeof(struct io_async_msghdr),
},
[IORING_OP_TIMEOUT] = {
.audit_skip = 1,
.async_size = sizeof(struct io_timeout_data),
},
[IORING_OP_TIMEOUT_REMOVE] = {
/* used by timeout updates' prep() */
.audit_skip = 1,
},
[IORING_OP_ACCEPT] = {
.needs_file = 1,
.unbound_nonreg_file = 1,
.pollin = 1,
},
[IORING_OP_ASYNC_CANCEL] = {},
[IORING_OP_ASYNC_CANCEL] = {
.audit_skip = 1,
},
[IORING_OP_LINK_TIMEOUT] = {
.audit_skip = 1,
.async_size = sizeof(struct io_timeout_data),
},
[IORING_OP_CONNECT] = {
@ -1004,14 +1022,19 @@ static const struct io_op_def io_op_defs[] = {
},
[IORING_OP_OPENAT] = {},
[IORING_OP_CLOSE] = {},
[IORING_OP_FILES_UPDATE] = {},
[IORING_OP_STATX] = {},
[IORING_OP_FILES_UPDATE] = {
.audit_skip = 1,
},
[IORING_OP_STATX] = {
.audit_skip = 1,
},
[IORING_OP_READ] = {
.needs_file = 1,
.unbound_nonreg_file = 1,
.pollin = 1,
.buffer_select = 1,
.plug = 1,
.audit_skip = 1,
.async_size = sizeof(struct io_async_rw),
},
[IORING_OP_WRITE] = {
@ -1020,39 +1043,50 @@ static const struct io_op_def io_op_defs[] = {
.unbound_nonreg_file = 1,
.pollout = 1,
.plug = 1,
.audit_skip = 1,
.async_size = sizeof(struct io_async_rw),
},
[IORING_OP_FADVISE] = {
.needs_file = 1,
.audit_skip = 1,
},
[IORING_OP_MADVISE] = {},
[IORING_OP_SEND] = {
.needs_file = 1,
.unbound_nonreg_file = 1,
.pollout = 1,
.audit_skip = 1,
},
[IORING_OP_RECV] = {
.needs_file = 1,
.unbound_nonreg_file = 1,
.pollin = 1,
.buffer_select = 1,
.audit_skip = 1,
},
[IORING_OP_OPENAT2] = {
},
[IORING_OP_EPOLL_CTL] = {
.unbound_nonreg_file = 1,
.audit_skip = 1,
},
[IORING_OP_SPLICE] = {
.needs_file = 1,
.hash_reg_file = 1,
.unbound_nonreg_file = 1,
.audit_skip = 1,
},
[IORING_OP_PROVIDE_BUFFERS] = {
.audit_skip = 1,
},
[IORING_OP_REMOVE_BUFFERS] = {
.audit_skip = 1,
},
[IORING_OP_PROVIDE_BUFFERS] = {},
[IORING_OP_REMOVE_BUFFERS] = {},
[IORING_OP_TEE] = {
.needs_file = 1,
.hash_reg_file = 1,
.unbound_nonreg_file = 1,
.audit_skip = 1,
},
[IORING_OP_SHUTDOWN] = {
.needs_file = 1,
@ -6581,6 +6615,9 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags)
if (unlikely((req->flags & REQ_F_CREDS) && req->creds != current_cred()))
creds = override_creds(req->creds);
if (!io_op_defs[req->opcode].audit_skip)
audit_uring_entry(req->opcode);
switch (req->opcode) {
case IORING_OP_NOP:
ret = io_nop(req, issue_flags);
@ -6696,6 +6733,9 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags)
break;
}
if (!io_op_defs[req->opcode].audit_skip)
audit_uring_exit(!ret, ret);
if (creds)
revert_creds(creds);
if (ret)
@ -7090,10 +7130,17 @@ static int io_init_req(struct io_ring_ctx *ctx, struct io_kiocb *req,
personality = READ_ONCE(sqe->personality);
if (personality) {
int ret;
req->creds = xa_load(&ctx->personalities, personality);
if (!req->creds)
return -EINVAL;
get_cred(req->creds);
ret = security_uring_override_creds(req->creds);
if (ret) {
put_cred(req->creds);
return ret;
}
req->flags |= REQ_F_CREDS;
}
@ -7400,6 +7447,8 @@ static int io_sq_thread(void *data)
set_cpus_allowed_ptr(current, cpu_online_mask);
current->flags |= PF_NO_SETAFFINITY;
audit_alloc_kernel(current);
mutex_lock(&sqd->lock);
while (1) {
bool cap_entries, sqt_spin = false;
@ -7465,6 +7514,8 @@ static int io_sq_thread(void *data)
io_run_task_work();
mutex_unlock(&sqd->lock);
audit_free(current);
complete(&sqd->exited);
do_exit(0);
}
@ -8622,6 +8673,10 @@ static __cold int io_sq_offload_create(struct io_ring_ctx *ctx,
struct io_sq_data *sqd;
bool attached;
ret = security_uring_sqpoll();
if (ret)
return ret;
sqd = io_get_sq_data(p, &attached);
if (IS_ERR(sqd)) {
ret = PTR_ERR(sqd);
@ -10276,8 +10331,8 @@ static struct file *io_uring_get_file(struct io_ring_ctx *ctx)
return ERR_PTR(ret);
#endif
file = anon_inode_getfile("[io_uring]", &io_uring_fops, ctx,
O_RDWR | O_CLOEXEC);
file = anon_inode_getfile_secure("[io_uring]", &io_uring_fops, ctx,
O_RDWR | O_CLOEXEC, NULL);
#if defined(CONFIG_UNIX)
if (IS_ERR(file)) {
sock_release(ctx->ring_sock);

View File

@ -127,7 +127,8 @@ nfs4_label_init_security(struct inode *dir, struct dentry *dentry,
return NULL;
err = security_dentry_init_security(dentry, sattr->ia_mode,
&dentry->d_name, (void **)&label->label, &label->len);
&dentry->d_name, NULL,
(void **)&label->label, &label->len);
if (err == 0)
return label;

View File

@ -15,6 +15,10 @@ struct inode;
struct file *anon_inode_getfile(const char *name,
const struct file_operations *fops,
void *priv, int flags);
struct file *anon_inode_getfile_secure(const char *name,
const struct file_operations *fops,
void *priv, int flags,
const struct inode *context_inode);
int anon_inode_getfd(const char *name, const struct file_operations *fops,
void *priv, int flags);
int anon_inode_getfd_secure(const char *name,

View File

@ -286,7 +286,10 @@ static inline int audit_signal_info(int sig, struct task_struct *t)
/* These are defined in auditsc.c */
/* Public API */
extern int audit_alloc(struct task_struct *task);
extern int audit_alloc_kernel(struct task_struct *task);
extern void __audit_free(struct task_struct *task);
extern void __audit_uring_entry(u8 op);
extern void __audit_uring_exit(int success, long code);
extern void __audit_syscall_entry(int major, unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3);
extern void __audit_syscall_exit(int ret_success, long ret_value);
@ -323,6 +326,21 @@ static inline void audit_free(struct task_struct *task)
if (unlikely(task->audit_context))
__audit_free(task);
}
static inline void audit_uring_entry(u8 op)
{
/*
* We intentionally check audit_context() before audit_enabled as most
* Linux systems (as of ~2021) rely on systemd which forces audit to
* be enabled regardless of the user's audit configuration.
*/
if (unlikely(audit_context() && audit_enabled))
__audit_uring_entry(op);
}
static inline void audit_uring_exit(int success, long code)
{
if (unlikely(!audit_dummy_context()))
__audit_uring_exit(success, code);
}
static inline void audit_syscall_entry(int major, unsigned long a0,
unsigned long a1, unsigned long a2,
unsigned long a3)
@ -554,8 +572,16 @@ static inline int audit_alloc(struct task_struct *task)
{
return 0;
}
static inline int audit_alloc_kernel(struct task_struct *task)
{
return 0;
}
static inline void audit_free(struct task_struct *task)
{ }
static inline void audit_uring_entry(u8 op)
{ }
static inline void audit_uring_exit(int success, long code)
{ }
static inline void audit_syscall_entry(int major, unsigned long a0,
unsigned long a1, unsigned long a2,
unsigned long a3)

View File

@ -26,13 +26,13 @@
* #undef LSM_HOOK
* };
*/
LSM_HOOK(int, 0, binder_set_context_mgr, struct task_struct *mgr)
LSM_HOOK(int, 0, binder_transaction, struct task_struct *from,
struct task_struct *to)
LSM_HOOK(int, 0, binder_transfer_binder, struct task_struct *from,
struct task_struct *to)
LSM_HOOK(int, 0, binder_transfer_file, struct task_struct *from,
struct task_struct *to, struct file *file)
LSM_HOOK(int, 0, binder_set_context_mgr, const struct cred *mgr)
LSM_HOOK(int, 0, binder_transaction, const struct cred *from,
const struct cred *to)
LSM_HOOK(int, 0, binder_transfer_binder, const struct cred *from,
const struct cred *to)
LSM_HOOK(int, 0, binder_transfer_file, const struct cred *from,
const struct cred *to, struct file *file)
LSM_HOOK(int, 0, ptrace_access_check, struct task_struct *child,
unsigned int mode)
LSM_HOOK(int, 0, ptrace_traceme, struct task_struct *parent)
@ -83,7 +83,8 @@ LSM_HOOK(int, 0, sb_add_mnt_opt, const char *option, const char *val,
LSM_HOOK(int, 0, move_mount, const struct path *from_path,
const struct path *to_path)
LSM_HOOK(int, 0, dentry_init_security, struct dentry *dentry,
int mode, const struct qstr *name, void **ctx, u32 *ctxlen)
int mode, const struct qstr *name, const char **xattr_name,
void **ctx, u32 *ctxlen)
LSM_HOOK(int, 0, dentry_create_files_as, struct dentry *dentry, int mode,
struct qstr *name, const struct cred *old, struct cred *new)
@ -402,3 +403,8 @@ LSM_HOOK(void, LSM_RET_VOID, perf_event_free, struct perf_event *event)
LSM_HOOK(int, 0, perf_event_read, struct perf_event *event)
LSM_HOOK(int, 0, perf_event_write, struct perf_event *event)
#endif /* CONFIG_PERF_EVENTS */
#ifdef CONFIG_IO_URING
LSM_HOOK(int, 0, uring_override_creds, const struct cred *new)
LSM_HOOK(int, 0, uring_sqpoll, void)
#endif /* CONFIG_IO_URING */

View File

@ -196,6 +196,9 @@
* @dentry dentry to use in calculating the context.
* @mode mode used to determine resource type.
* @name name of the last path component used to create file
* @xattr_name pointer to place the pointer to security xattr name.
* Caller does not have to free the resulting pointer. Its
* a pointer to static string.
* @ctx pointer to place the pointer to the resulting context in.
* @ctxlen point to place the length of the resulting context.
* @dentry_create_files_as:
@ -1313,22 +1316,22 @@
*
* @binder_set_context_mgr:
* Check whether @mgr is allowed to be the binder context manager.
* @mgr contains the task_struct for the task being registered.
* @mgr contains the struct cred for the current binder process.
* Return 0 if permission is granted.
* @binder_transaction:
* Check whether @from is allowed to invoke a binder transaction call
* to @to.
* @from contains the task_struct for the sending task.
* @to contains the task_struct for the receiving task.
* @from contains the struct cred for the sending process.
* @to contains the struct cred for the receiving process.
* @binder_transfer_binder:
* Check whether @from is allowed to transfer a binder reference to @to.
* @from contains the task_struct for the sending task.
* @to contains the task_struct for the receiving task.
* @from contains the struct cred for the sending process.
* @to contains the struct cred for the receiving process.
* @binder_transfer_file:
* Check whether @from is allowed to transfer @file to @to.
* @from contains the task_struct for the sending task.
* @from contains the struct cred for the sending process.
* @file contains the struct file being transferred.
* @to contains the task_struct for the receiving task.
* @to contains the struct cred for the receiving process.
*
* @ptrace_access_check:
* Check permission before allowing the current process to trace the
@ -1557,6 +1560,19 @@
* Read perf_event security info if allowed.
* @perf_event_write:
* Write perf_event security info if allowed.
*
* Security hooks for io_uring
*
* @uring_override_creds:
* Check if the current task, executing an io_uring operation, is allowed
* to override it's credentials with @new.
*
* @new: the new creds to use
*
* @uring_sqpoll:
* Check whether the current task is allowed to spawn a io_uring polling
* thread (IORING_SETUP_SQPOLL).
*
*/
union security_list_options {
#define LSM_HOOK(RET, DEFAULT, NAME, ...) RET (*NAME)(__VA_ARGS__);

View File

@ -258,13 +258,13 @@ extern int security_init(void);
extern int early_security_init(void);
/* Security operations */
int security_binder_set_context_mgr(struct task_struct *mgr);
int security_binder_transaction(struct task_struct *from,
struct task_struct *to);
int security_binder_transfer_binder(struct task_struct *from,
struct task_struct *to);
int security_binder_transfer_file(struct task_struct *from,
struct task_struct *to, struct file *file);
int security_binder_set_context_mgr(const struct cred *mgr);
int security_binder_transaction(const struct cred *from,
const struct cred *to);
int security_binder_transfer_binder(const struct cred *from,
const struct cred *to);
int security_binder_transfer_file(const struct cred *from,
const struct cred *to, struct file *file);
int security_ptrace_access_check(struct task_struct *child, unsigned int mode);
int security_ptrace_traceme(struct task_struct *parent);
int security_capget(struct task_struct *target,
@ -317,8 +317,9 @@ int security_add_mnt_opt(const char *option, const char *val,
int len, void **mnt_opts);
int security_move_mount(const struct path *from_path, const struct path *to_path);
int security_dentry_init_security(struct dentry *dentry, int mode,
const struct qstr *name, void **ctx,
u32 *ctxlen);
const struct qstr *name,
const char **xattr_name, void **ctx,
u32 *ctxlen);
int security_dentry_create_files_as(struct dentry *dentry, int mode,
struct qstr *name,
const struct cred *old,
@ -508,25 +509,25 @@ static inline int early_security_init(void)
return 0;
}
static inline int security_binder_set_context_mgr(struct task_struct *mgr)
static inline int security_binder_set_context_mgr(const struct cred *mgr)
{
return 0;
}
static inline int security_binder_transaction(struct task_struct *from,
struct task_struct *to)
static inline int security_binder_transaction(const struct cred *from,
const struct cred *to)
{
return 0;
}
static inline int security_binder_transfer_binder(struct task_struct *from,
struct task_struct *to)
static inline int security_binder_transfer_binder(const struct cred *from,
const struct cred *to)
{
return 0;
}
static inline int security_binder_transfer_file(struct task_struct *from,
struct task_struct *to,
static inline int security_binder_transfer_file(const struct cred *from,
const struct cred *to,
struct file *file)
{
return 0;
@ -739,6 +740,7 @@ static inline void security_inode_free(struct inode *inode)
static inline int security_dentry_init_security(struct dentry *dentry,
int mode,
const struct qstr *name,
const char **xattr_name,
void **ctx,
u32 *ctxlen)
{
@ -1041,6 +1043,11 @@ static inline void security_transfer_creds(struct cred *new,
{
}
static inline void security_cred_getsecid(const struct cred *c, u32 *secid)
{
*secid = 0;
}
static inline int security_kernel_act_as(struct cred *cred, u32 secid)
{
return 0;
@ -2038,4 +2045,20 @@ static inline int security_perf_event_write(struct perf_event *event)
#endif /* CONFIG_SECURITY */
#endif /* CONFIG_PERF_EVENTS */
#ifdef CONFIG_IO_URING
#ifdef CONFIG_SECURITY
extern int security_uring_override_creds(const struct cred *new);
extern int security_uring_sqpoll(void);
#else
static inline int security_uring_override_creds(const struct cred *new)
{
return 0;
}
static inline int security_uring_sqpoll(void)
{
return 0;
}
#endif /* CONFIG_SECURITY */
#endif /* CONFIG_IO_URING */
#endif /* ! __LINUX_SECURITY_H */

View File

@ -118,6 +118,7 @@
#define AUDIT_TIME_ADJNTPVAL 1333 /* NTP value adjustment */
#define AUDIT_BPF 1334 /* BPF subsystem */
#define AUDIT_EVENT_LISTENER 1335 /* Task joined multicast read socket */
#define AUDIT_URINGOP 1336 /* io_uring operation */
#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
@ -166,8 +167,9 @@
#define AUDIT_FILTER_EXCLUDE 0x05 /* Apply rule before record creation */
#define AUDIT_FILTER_TYPE AUDIT_FILTER_EXCLUDE /* obsolete misleading naming */
#define AUDIT_FILTER_FS 0x06 /* Apply rule at __audit_inode_child */
#define AUDIT_FILTER_URING_EXIT 0x07 /* Apply rule at io_uring op exit */
#define AUDIT_NR_FILTERS 7
#define AUDIT_NR_FILTERS 8
#define AUDIT_FILTER_PREPEND 0x10 /* Prepend to front of list */

View File

@ -100,10 +100,15 @@ struct audit_proctitle {
/* The per-task audit context. */
struct audit_context {
int dummy; /* must be the first element */
int in_syscall; /* 1 if task is in a syscall */
enum {
AUDIT_CTX_UNUSED, /* audit_context is currently unused */
AUDIT_CTX_SYSCALL, /* in use by syscall */
AUDIT_CTX_URING, /* in use by io_uring */
} context;
enum audit_state state, current_state;
unsigned int serial; /* serial number for record */
int major; /* syscall number */
int uring_op; /* uring operation */
struct timespec64 ctime; /* time of syscall entry */
unsigned long argv[4]; /* syscall arguments */
long return_code;/* syscall return code */

View File

@ -726,7 +726,8 @@ int audit_make_tree(struct audit_krule *rule, char *pathname, u32 op)
{
if (pathname[0] != '/' ||
rule->listnr != AUDIT_FILTER_EXIT ||
(rule->listnr != AUDIT_FILTER_EXIT &&
rule->listnr != AUDIT_FILTER_URING_EXIT) ||
op != Audit_equal ||
rule->inode_f || rule->watch || rule->tree)
return -EINVAL;

View File

@ -183,7 +183,8 @@ int audit_to_watch(struct audit_krule *krule, char *path, int len, u32 op)
return -EOPNOTSUPP;
if (path[0] != '/' || path[len-1] == '/' ||
krule->listnr != AUDIT_FILTER_EXIT ||
(krule->listnr != AUDIT_FILTER_EXIT &&
krule->listnr != AUDIT_FILTER_URING_EXIT) ||
op != Audit_equal ||
krule->inode_f || krule->watch || krule->tree)
return -EINVAL;

View File

@ -44,7 +44,8 @@ struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
LIST_HEAD_INIT(audit_filter_list[4]),
LIST_HEAD_INIT(audit_filter_list[5]),
LIST_HEAD_INIT(audit_filter_list[6]),
#if AUDIT_NR_FILTERS != 7
LIST_HEAD_INIT(audit_filter_list[7]),
#if AUDIT_NR_FILTERS != 8
#error Fix audit_filter_list initialiser
#endif
};
@ -56,6 +57,7 @@ static struct list_head audit_rules_list[AUDIT_NR_FILTERS] = {
LIST_HEAD_INIT(audit_rules_list[4]),
LIST_HEAD_INIT(audit_rules_list[5]),
LIST_HEAD_INIT(audit_rules_list[6]),
LIST_HEAD_INIT(audit_rules_list[7]),
};
DEFINE_MUTEX(audit_filter_mutex);
@ -151,7 +153,8 @@ char *audit_unpack_string(void **bufp, size_t *remain, size_t len)
static inline int audit_to_inode(struct audit_krule *krule,
struct audit_field *f)
{
if (krule->listnr != AUDIT_FILTER_EXIT ||
if ((krule->listnr != AUDIT_FILTER_EXIT &&
krule->listnr != AUDIT_FILTER_URING_EXIT) ||
krule->inode_f || krule->watch || krule->tree ||
(f->op != Audit_equal && f->op != Audit_not_equal))
return -EINVAL;
@ -248,6 +251,7 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule_data *
pr_err("AUDIT_FILTER_ENTRY is deprecated\n");
goto exit_err;
case AUDIT_FILTER_EXIT:
case AUDIT_FILTER_URING_EXIT:
case AUDIT_FILTER_TASK:
#endif
case AUDIT_FILTER_USER:
@ -332,6 +336,10 @@ static int audit_field_valid(struct audit_entry *entry, struct audit_field *f)
if (entry->rule.listnr != AUDIT_FILTER_FS)
return -EINVAL;
break;
case AUDIT_PERM:
if (entry->rule.listnr == AUDIT_FILTER_URING_EXIT)
return -EINVAL;
break;
}
switch (entry->rule.listnr) {
@ -980,7 +988,8 @@ static inline int audit_add_rule(struct audit_entry *entry)
}
entry->rule.prio = ~0ULL;
if (entry->rule.listnr == AUDIT_FILTER_EXIT) {
if (entry->rule.listnr == AUDIT_FILTER_EXIT ||
entry->rule.listnr == AUDIT_FILTER_URING_EXIT) {
if (entry->rule.flags & AUDIT_FILTER_PREPEND)
entry->rule.prio = ++prio_high;
else

View File

@ -805,6 +805,34 @@ static int audit_in_mask(const struct audit_krule *rule, unsigned long val)
return rule->mask[word] & bit;
}
/**
* audit_filter_uring - apply filters to an io_uring operation
* @tsk: associated task
* @ctx: audit context
*/
static void audit_filter_uring(struct task_struct *tsk,
struct audit_context *ctx)
{
struct audit_entry *e;
enum audit_state state;
if (auditd_test_task(tsk))
return;
rcu_read_lock();
list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_URING_EXIT],
list) {
if (audit_in_mask(&e->rule, ctx->uring_op) &&
audit_filter_rules(tsk, &e->rule, ctx, NULL, &state,
false)) {
rcu_read_unlock();
ctx->current_state = state;
return;
}
}
rcu_read_unlock();
}
/* At syscall exit time, this filter is called if the audit_state is
* not low enough that auditing cannot take place, but is also not
* high enough that we already know we have to write an audit record
@ -915,10 +943,81 @@ static inline void audit_free_aux(struct audit_context *context)
context->aux = aux->next;
kfree(aux);
}
context->aux = NULL;
while ((aux = context->aux_pids)) {
context->aux_pids = aux->next;
kfree(aux);
}
context->aux_pids = NULL;
}
/**
* audit_reset_context - reset a audit_context structure
* @ctx: the audit_context to reset
*
* All fields in the audit_context will be reset to an initial state, all
* references held by fields will be dropped, and private memory will be
* released. When this function returns the audit_context will be suitable
* for reuse, so long as the passed context is not NULL or a dummy context.
*/
static void audit_reset_context(struct audit_context *ctx)
{
if (!ctx)
return;
/* if ctx is non-null, reset the "ctx->state" regardless */
ctx->context = AUDIT_CTX_UNUSED;
if (ctx->dummy)
return;
/*
* NOTE: It shouldn't matter in what order we release the fields, so
* release them in the order in which they appear in the struct;
* this gives us some hope of quickly making sure we are
* resetting the audit_context properly.
*
* Other things worth mentioning:
* - we don't reset "dummy"
* - we don't reset "state", we do reset "current_state"
* - we preserve "filterkey" if "state" is AUDIT_STATE_RECORD
* - much of this is likely overkill, but play it safe for now
* - we really need to work on improving the audit_context struct
*/
ctx->current_state = ctx->state;
ctx->serial = 0;
ctx->major = 0;
ctx->uring_op = 0;
ctx->ctime = (struct timespec64){ .tv_sec = 0, .tv_nsec = 0 };
memset(ctx->argv, 0, sizeof(ctx->argv));
ctx->return_code = 0;
ctx->prio = (ctx->state == AUDIT_STATE_RECORD ? ~0ULL : 0);
ctx->return_valid = AUDITSC_INVALID;
audit_free_names(ctx);
if (ctx->state != AUDIT_STATE_RECORD) {
kfree(ctx->filterkey);
ctx->filterkey = NULL;
}
audit_free_aux(ctx);
kfree(ctx->sockaddr);
ctx->sockaddr = NULL;
ctx->sockaddr_len = 0;
ctx->pid = ctx->ppid = 0;
ctx->uid = ctx->euid = ctx->suid = ctx->fsuid = KUIDT_INIT(0);
ctx->gid = ctx->egid = ctx->sgid = ctx->fsgid = KGIDT_INIT(0);
ctx->personality = 0;
ctx->arch = 0;
ctx->target_pid = 0;
ctx->target_auid = ctx->target_uid = KUIDT_INIT(0);
ctx->target_sessionid = 0;
ctx->target_sid = 0;
ctx->target_comm[0] = '\0';
unroll_tree_refs(ctx, NULL, 0);
WARN_ON(!list_empty(&ctx->killed_trees));
ctx->type = 0;
audit_free_module(ctx);
ctx->fds[0] = -1;
audit_proctitle_free(ctx);
}
static inline struct audit_context *audit_alloc_context(enum audit_state state)
@ -928,6 +1027,7 @@ static inline struct audit_context *audit_alloc_context(enum audit_state state)
context = kzalloc(sizeof(*context), GFP_KERNEL);
if (!context)
return NULL;
context->context = AUDIT_CTX_UNUSED;
context->state = state;
context->prio = state == AUDIT_STATE_RECORD ? ~0ULL : 0;
INIT_LIST_HEAD(&context->killed_trees);
@ -953,7 +1053,7 @@ int audit_alloc(struct task_struct *tsk)
char *key = NULL;
if (likely(!audit_ever_enabled))
return 0; /* Return if not auditing. */
return 0;
state = audit_filter_task(tsk, &key);
if (state == AUDIT_STATE_DISABLED) {
@ -973,16 +1073,37 @@ int audit_alloc(struct task_struct *tsk)
return 0;
}
/**
* audit_alloc_kernel - allocate an audit_context for a kernel task
* @tsk: the kernel task
*
* Similar to the audit_alloc() function, but intended for kernel private
* threads. Returns zero on success, negative values on failure.
*/
int audit_alloc_kernel(struct task_struct *tsk)
{
/*
* At the moment we are just going to call into audit_alloc() to
* simplify the code, but there two things to keep in mind with this
* approach:
*
* 1. Filtering internal kernel tasks is a bit laughable in almost all
* cases, but there is at least one case where there is a benefit:
* the '-a task,never' case allows the admin to effectively disable
* task auditing at runtime.
*
* 2. The {set,clear}_task_syscall_work() ops likely have zero effect
* on these internal kernel tasks, but they probably don't hurt either.
*/
return audit_alloc(tsk);
}
static inline void audit_free_context(struct audit_context *context)
{
audit_free_module(context);
audit_free_names(context);
unroll_tree_refs(context, NULL, 0);
/* resetting is extra work, but it is likely just noise */
audit_reset_context(context);
free_tree_refs(context);
audit_free_aux(context);
kfree(context->filterkey);
kfree(context->sockaddr);
audit_proctitle_free(context);
kfree(context);
}
@ -1479,6 +1600,44 @@ out:
audit_log_end(ab);
}
/**
* audit_log_uring - generate a AUDIT_URINGOP record
* @ctx: the audit context
*/
static void audit_log_uring(struct audit_context *ctx)
{
struct audit_buffer *ab;
const struct cred *cred;
ab = audit_log_start(ctx, GFP_ATOMIC, AUDIT_URINGOP);
if (!ab)
return;
cred = current_cred();
audit_log_format(ab, "uring_op=%d", ctx->uring_op);
if (ctx->return_valid != AUDITSC_INVALID)
audit_log_format(ab, " success=%s exit=%ld",
(ctx->return_valid == AUDITSC_SUCCESS ?
"yes" : "no"),
ctx->return_code);
audit_log_format(ab,
" items=%d"
" ppid=%d pid=%d uid=%u gid=%u euid=%u suid=%u"
" fsuid=%u egid=%u sgid=%u fsgid=%u",
ctx->name_count,
task_ppid_nr(current), task_tgid_nr(current),
from_kuid(&init_user_ns, cred->uid),
from_kgid(&init_user_ns, cred->gid),
from_kuid(&init_user_ns, cred->euid),
from_kuid(&init_user_ns, cred->suid),
from_kuid(&init_user_ns, cred->fsuid),
from_kgid(&init_user_ns, cred->egid),
from_kgid(&init_user_ns, cred->sgid),
from_kgid(&init_user_ns, cred->fsgid));
audit_log_task_context(ab);
audit_log_key(ab, ctx->filterkey);
audit_log_end(ab);
}
static void audit_log_exit(void)
{
int i, call_panic = 0;
@ -1489,29 +1648,38 @@ static void audit_log_exit(void)
context->personality = current->personality;
ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL);
if (!ab)
return; /* audit_panic has been called */
audit_log_format(ab, "arch=%x syscall=%d",
context->arch, context->major);
if (context->personality != PER_LINUX)
audit_log_format(ab, " per=%lx", context->personality);
if (context->return_valid != AUDITSC_INVALID)
audit_log_format(ab, " success=%s exit=%ld",
(context->return_valid==AUDITSC_SUCCESS)?"yes":"no",
context->return_code);
audit_log_format(ab,
" a0=%lx a1=%lx a2=%lx a3=%lx items=%d",
context->argv[0],
context->argv[1],
context->argv[2],
context->argv[3],
context->name_count);
audit_log_task_info(ab);
audit_log_key(ab, context->filterkey);
audit_log_end(ab);
switch (context->context) {
case AUDIT_CTX_SYSCALL:
ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL);
if (!ab)
return;
audit_log_format(ab, "arch=%x syscall=%d",
context->arch, context->major);
if (context->personality != PER_LINUX)
audit_log_format(ab, " per=%lx", context->personality);
if (context->return_valid != AUDITSC_INVALID)
audit_log_format(ab, " success=%s exit=%ld",
(context->return_valid == AUDITSC_SUCCESS ?
"yes" : "no"),
context->return_code);
audit_log_format(ab,
" a0=%lx a1=%lx a2=%lx a3=%lx items=%d",
context->argv[0],
context->argv[1],
context->argv[2],
context->argv[3],
context->name_count);
audit_log_task_info(ab);
audit_log_key(ab, context->filterkey);
audit_log_end(ab);
break;
case AUDIT_CTX_URING:
audit_log_uring(context);
break;
default:
BUG();
break;
}
for (aux = context->aux; aux; aux = aux->next) {
@ -1602,21 +1770,22 @@ static void audit_log_exit(void)
audit_log_name(context, n, NULL, i++, &call_panic);
}
audit_log_proctitle();
if (context->context == AUDIT_CTX_SYSCALL)
audit_log_proctitle();
/* Send end of event record to help user space know we are finished */
ab = audit_log_start(context, GFP_KERNEL, AUDIT_EOE);
if (ab)
audit_log_end(ab);
if (call_panic)
audit_panic("error converting sid to string");
audit_panic("error in audit_log_exit()");
}
/**
* __audit_free - free a per-task audit context
* @tsk: task whose audit context block to free
*
* Called from copy_process and do_exit
* Called from copy_process, do_exit, and the io_uring code
*/
void __audit_free(struct task_struct *tsk)
{
@ -1625,6 +1794,7 @@ void __audit_free(struct task_struct *tsk)
if (!context)
return;
/* this may generate CONFIG_CHANGE records */
if (!list_empty(&context->killed_trees))
audit_kill_trees(context);
@ -1633,20 +1803,152 @@ void __audit_free(struct task_struct *tsk)
* random task_struct that doesn't doesn't have any meaningful data we
* need to log via audit_log_exit().
*/
if (tsk == current && !context->dummy && context->in_syscall) {
if (tsk == current && !context->dummy) {
context->return_valid = AUDITSC_INVALID;
context->return_code = 0;
audit_filter_syscall(tsk, context);
audit_filter_inodes(tsk, context);
if (context->current_state == AUDIT_STATE_RECORD)
audit_log_exit();
if (context->context == AUDIT_CTX_SYSCALL) {
audit_filter_syscall(tsk, context);
audit_filter_inodes(tsk, context);
if (context->current_state == AUDIT_STATE_RECORD)
audit_log_exit();
} else if (context->context == AUDIT_CTX_URING) {
/* TODO: verify this case is real and valid */
audit_filter_uring(tsk, context);
audit_filter_inodes(tsk, context);
if (context->current_state == AUDIT_STATE_RECORD)
audit_log_uring(context);
}
}
audit_set_context(tsk, NULL);
audit_free_context(context);
}
/**
* audit_return_fixup - fixup the return codes in the audit_context
* @ctx: the audit_context
* @success: true/false value to indicate if the operation succeeded or not
* @code: operation return code
*
* We need to fixup the return code in the audit logs if the actual return
* codes are later going to be fixed by the arch specific signal handlers.
*/
static void audit_return_fixup(struct audit_context *ctx,
int success, long code)
{
/*
* This is actually a test for:
* (rc == ERESTARTSYS ) || (rc == ERESTARTNOINTR) ||
* (rc == ERESTARTNOHAND) || (rc == ERESTART_RESTARTBLOCK)
*
* but is faster than a bunch of ||
*/
if (unlikely(code <= -ERESTARTSYS) &&
(code >= -ERESTART_RESTARTBLOCK) &&
(code != -ENOIOCTLCMD))
ctx->return_code = -EINTR;
else
ctx->return_code = code;
ctx->return_valid = (success ? AUDITSC_SUCCESS : AUDITSC_FAILURE);
}
/**
* __audit_uring_entry - prepare the kernel task's audit context for io_uring
* @op: the io_uring opcode
*
* This is similar to audit_syscall_entry() but is intended for use by io_uring
* operations. This function should only ever be called from
* audit_uring_entry() as we rely on the audit context checking present in that
* function.
*/
void __audit_uring_entry(u8 op)
{
struct audit_context *ctx = audit_context();
if (ctx->state == AUDIT_STATE_DISABLED)
return;
/*
* NOTE: It's possible that we can be called from the process' context
* before it returns to userspace, and before audit_syscall_exit()
* is called. In this case there is not much to do, just record
* the io_uring details and return.
*/
ctx->uring_op = op;
if (ctx->context == AUDIT_CTX_SYSCALL)
return;
ctx->dummy = !audit_n_rules;
if (!ctx->dummy && ctx->state == AUDIT_STATE_BUILD)
ctx->prio = 0;
ctx->context = AUDIT_CTX_URING;
ctx->current_state = ctx->state;
ktime_get_coarse_real_ts64(&ctx->ctime);
}
/**
* __audit_uring_exit - wrap up the kernel task's audit context after io_uring
* @success: true/false value to indicate if the operation succeeded or not
* @code: operation return code
*
* This is similar to audit_syscall_exit() but is intended for use by io_uring
* operations. This function should only ever be called from
* audit_uring_exit() as we rely on the audit context checking present in that
* function.
*/
void __audit_uring_exit(int success, long code)
{
struct audit_context *ctx = audit_context();
if (ctx->context == AUDIT_CTX_SYSCALL) {
/*
* NOTE: See the note in __audit_uring_entry() about the case
* where we may be called from process context before we
* return to userspace via audit_syscall_exit(). In this
* case we simply emit a URINGOP record and bail, the
* normal syscall exit handling will take care of
* everything else.
* It is also worth mentioning that when we are called,
* the current process creds may differ from the creds
* used during the normal syscall processing; keep that
* in mind if/when we move the record generation code.
*/
/*
* We need to filter on the syscall info here to decide if we
* should emit a URINGOP record. I know it seems odd but this
* solves the problem where users have a filter to block *all*
* syscall records in the "exit" filter; we want to preserve
* the behavior here.
*/
audit_filter_syscall(current, ctx);
if (ctx->current_state != AUDIT_STATE_RECORD)
audit_filter_uring(current, ctx);
audit_filter_inodes(current, ctx);
if (ctx->current_state != AUDIT_STATE_RECORD)
return;
audit_log_uring(ctx);
return;
}
/* this may generate CONFIG_CHANGE records */
if (!list_empty(&ctx->killed_trees))
audit_kill_trees(ctx);
/* run through both filters to ensure we set the filterkey properly */
audit_filter_uring(current, ctx);
audit_filter_inodes(current, ctx);
if (ctx->current_state != AUDIT_STATE_RECORD)
goto out;
audit_return_fixup(ctx, success, code);
audit_log_exit();
out:
audit_reset_context(ctx);
}
/**
* __audit_syscall_entry - fill in an audit record at syscall entry
* @major: major syscall type (function)
@ -1672,7 +1974,12 @@ void __audit_syscall_entry(int major, unsigned long a1, unsigned long a2,
if (!audit_enabled || !context)
return;
BUG_ON(context->in_syscall || context->name_count);
WARN_ON(context->context != AUDIT_CTX_UNUSED);
WARN_ON(context->name_count);
if (context->context != AUDIT_CTX_UNUSED || context->name_count) {
audit_panic("unrecoverable error in audit_syscall_entry()");
return;
}
state = context->state;
if (state == AUDIT_STATE_DISABLED)
@ -1691,10 +1998,8 @@ void __audit_syscall_entry(int major, unsigned long a1, unsigned long a2,
context->argv[1] = a2;
context->argv[2] = a3;
context->argv[3] = a4;
context->serial = 0;
context->in_syscall = 1;
context->context = AUDIT_CTX_SYSCALL;
context->current_state = state;
context->ppid = 0;
ktime_get_coarse_real_ts64(&context->ctime);
}
@ -1711,63 +2016,27 @@ void __audit_syscall_entry(int major, unsigned long a1, unsigned long a2,
*/
void __audit_syscall_exit(int success, long return_code)
{
struct audit_context *context;
struct audit_context *context = audit_context();
context = audit_context();
if (!context)
return;
if (!context || context->dummy ||
context->context != AUDIT_CTX_SYSCALL)
goto out;
/* this may generate CONFIG_CHANGE records */
if (!list_empty(&context->killed_trees))
audit_kill_trees(context);
if (!context->dummy && context->in_syscall) {
if (success)
context->return_valid = AUDITSC_SUCCESS;
else
context->return_valid = AUDITSC_FAILURE;
/* run through both filters to ensure we set the filterkey properly */
audit_filter_syscall(current, context);
audit_filter_inodes(current, context);
if (context->current_state < AUDIT_STATE_RECORD)
goto out;
/*
* we need to fix up the return code in the audit logs if the
* actual return codes are later going to be fixed up by the
* arch specific signal handlers
*
* This is actually a test for:
* (rc == ERESTARTSYS ) || (rc == ERESTARTNOINTR) ||
* (rc == ERESTARTNOHAND) || (rc == ERESTART_RESTARTBLOCK)
*
* but is faster than a bunch of ||
*/
if (unlikely(return_code <= -ERESTARTSYS) &&
(return_code >= -ERESTART_RESTARTBLOCK) &&
(return_code != -ENOIOCTLCMD))
context->return_code = -EINTR;
else
context->return_code = return_code;
audit_return_fixup(context, success, return_code);
audit_log_exit();
audit_filter_syscall(current, context);
audit_filter_inodes(current, context);
if (context->current_state == AUDIT_STATE_RECORD)
audit_log_exit();
}
context->in_syscall = 0;
context->prio = context->state == AUDIT_STATE_RECORD ? ~0ULL : 0;
audit_free_module(context);
audit_free_names(context);
unroll_tree_refs(context, NULL, 0);
audit_free_aux(context);
context->aux = NULL;
context->aux_pids = NULL;
context->target_pid = 0;
context->target_sid = 0;
context->sockaddr_len = 0;
context->type = 0;
context->fds[0] = -1;
if (context->state != AUDIT_STATE_RECORD) {
kfree(context->filterkey);
context->filterkey = NULL;
}
out:
audit_reset_context(context);
}
static inline void handle_one(const struct inode *inode)
@ -1919,7 +2188,7 @@ void __audit_getname(struct filename *name)
struct audit_context *context = audit_context();
struct audit_names *n;
if (!context->in_syscall)
if (context->context == AUDIT_CTX_UNUSED)
return;
n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN);
@ -1991,7 +2260,7 @@ void __audit_inode(struct filename *name, const struct dentry *dentry,
struct list_head *list = &audit_filter_list[AUDIT_FILTER_FS];
int i;
if (!context->in_syscall)
if (context->context == AUDIT_CTX_UNUSED)
return;
rcu_read_lock();
@ -2109,7 +2378,7 @@ void __audit_inode_child(struct inode *parent,
struct list_head *list = &audit_filter_list[AUDIT_FILTER_FS];
int i;
if (!context->in_syscall)
if (context->context == AUDIT_CTX_UNUSED)
return;
rcu_read_lock();
@ -2208,7 +2477,7 @@ EXPORT_SYMBOL_GPL(__audit_inode_child);
int auditsc_get_stamp(struct audit_context *ctx,
struct timespec64 *t, unsigned int *serial)
{
if (!ctx->in_syscall)
if (ctx->context == AUDIT_CTX_UNUSED)
return 0;
if (!ctx->serial)
ctx->serial = audit_serial();
@ -2706,8 +2975,7 @@ void audit_seccomp_actions_logged(const char *names, const char *old_names,
struct list_head *audit_killed_trees(void)
{
struct audit_context *ctx = audit_context();
if (likely(!ctx || !ctx->in_syscall))
if (likely(!ctx || ctx->context == AUDIT_CTX_UNUSED))
return NULL;
return &ctx->killed_trees;
}

View File

@ -706,7 +706,7 @@ static int lsm_superblock_alloc(struct super_block *sb)
#define LSM_RET_DEFAULT(NAME) (NAME##_default)
#define DECLARE_LSM_RET_DEFAULT_void(DEFAULT, NAME)
#define DECLARE_LSM_RET_DEFAULT_int(DEFAULT, NAME) \
static const int LSM_RET_DEFAULT(NAME) = (DEFAULT);
static const int __maybe_unused LSM_RET_DEFAULT(NAME) = (DEFAULT);
#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
DECLARE_LSM_RET_DEFAULT_##RET(DEFAULT, NAME)
@ -747,25 +747,25 @@ static int lsm_superblock_alloc(struct super_block *sb)
/* Security operations */
int security_binder_set_context_mgr(struct task_struct *mgr)
int security_binder_set_context_mgr(const struct cred *mgr)
{
return call_int_hook(binder_set_context_mgr, 0, mgr);
}
int security_binder_transaction(struct task_struct *from,
struct task_struct *to)
int security_binder_transaction(const struct cred *from,
const struct cred *to)
{
return call_int_hook(binder_transaction, 0, from, to);
}
int security_binder_transfer_binder(struct task_struct *from,
struct task_struct *to)
int security_binder_transfer_binder(const struct cred *from,
const struct cred *to)
{
return call_int_hook(binder_transfer_binder, 0, from, to);
}
int security_binder_transfer_file(struct task_struct *from,
struct task_struct *to, struct file *file)
int security_binder_transfer_file(const struct cred *from,
const struct cred *to, struct file *file)
{
return call_int_hook(binder_transfer_file, 0, from, to, file);
}
@ -1052,11 +1052,12 @@ void security_inode_free(struct inode *inode)
}
int security_dentry_init_security(struct dentry *dentry, int mode,
const struct qstr *name, void **ctx,
u32 *ctxlen)
const struct qstr *name,
const char **xattr_name, void **ctx,
u32 *ctxlen)
{
return call_int_hook(dentry_init_security, -EOPNOTSUPP, dentry, mode,
name, ctx, ctxlen);
name, xattr_name, ctx, ctxlen);
}
EXPORT_SYMBOL(security_dentry_init_security);
@ -2625,3 +2626,15 @@ int security_perf_event_write(struct perf_event *event)
return call_int_hook(perf_event_write, 0, event);
}
#endif /* CONFIG_PERF_EVENTS */
#ifdef CONFIG_IO_URING
int security_uring_override_creds(const struct cred *new)
{
return call_int_hook(uring_override_creds, 0, new);
}
int security_uring_sqpoll(void)
{
return call_int_hook(uring_sqpoll, 0);
}
#endif /* CONFIG_IO_URING */

View File

@ -547,6 +547,7 @@ static inline struct avc_node *avc_search_node(struct selinux_avc *avc,
/**
* avc_lookup - Look up an AVC entry.
* @avc: the access vector cache
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
@ -597,6 +598,7 @@ static int avc_latest_notif_update(struct selinux_avc *avc,
/**
* avc_insert - Insert an AVC entry.
* @avc: the access vector cache
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
@ -825,9 +827,14 @@ out:
/**
* avc_update_node - Update an AVC entry
* @avc: the access vector cache
* @event : Updating event
* @perms : Permission mask bits
* @ssid,@tsid,@tclass : identifier of an AVC entry
* @driver: xperm driver information
* @xperm: xperm permissions
* @ssid: AVC entry source sid
* @tsid: AVC entry target sid
* @tclass : AVC entry target object class
* @seqno : sequence number when decision was made
* @xpd: extended_perms_decision to be added to the node
* @flags: the AVC_* flags, e.g. AVC_EXTENDED_PERMS, or 0.
@ -928,6 +935,7 @@ out:
/**
* avc_flush - Flush the cache
* @avc: the access vector cache
*/
static void avc_flush(struct selinux_avc *avc)
{
@ -956,6 +964,7 @@ static void avc_flush(struct selinux_avc *avc)
/**
* avc_ss_reset - Flush the cache and revalidate migrated permissions.
* @avc: the access vector cache
* @seqno: policy sequence number
*/
int avc_ss_reset(struct selinux_avc *avc, u32 seqno)
@ -1105,6 +1114,7 @@ decision:
/**
* avc_has_perm_noaudit - Check permissions but perform no auditing.
* @state: SELinux state
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
@ -1156,6 +1166,7 @@ inline int avc_has_perm_noaudit(struct selinux_state *state,
/**
* avc_has_perm - Check permissions and perform any appropriate auditing.
* @state: SELinux state
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class

View File

@ -255,29 +255,6 @@ static inline u32 task_sid_obj(const struct task_struct *task)
return sid;
}
/*
* get the security ID of a task for use with binder
*/
static inline u32 task_sid_binder(const struct task_struct *task)
{
/*
* In many case where this function is used we should be using the
* task's subjective SID, but we can't reliably access the subjective
* creds of a task other than our own so we must use the objective
* creds/SID, which are safe to access. The downside is that if a task
* is temporarily overriding it's creds it will not be reflected here;
* however, it isn't clear that binder would handle that case well
* anyway.
*
* If this ever changes and we can safely reference the subjective
* creds/SID of another task, this function will make it easier to
* identify the various places where we make use of the task SIDs in
* the binder code. It is also likely that we will need to adjust
* the main drivers/android binder code as well.
*/
return task_sid_obj(task);
}
static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry);
/*
@ -760,7 +737,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
!strcmp(sb->s_type->name, "tracefs") ||
!strcmp(sb->s_type->name, "binder") ||
!strcmp(sb->s_type->name, "bpf") ||
!strcmp(sb->s_type->name, "pstore"))
!strcmp(sb->s_type->name, "pstore") ||
!strcmp(sb->s_type->name, "securityfs"))
sbsec->flags |= SE_SBGENFS;
if (!strcmp(sb->s_type->name, "sysfs") ||
@ -2066,18 +2044,19 @@ static inline u32 open_file_to_av(struct file *file)
/* Hook functions begin here. */
static int selinux_binder_set_context_mgr(struct task_struct *mgr)
static int selinux_binder_set_context_mgr(const struct cred *mgr)
{
return avc_has_perm(&selinux_state,
current_sid(), task_sid_binder(mgr), SECCLASS_BINDER,
current_sid(), cred_sid(mgr), SECCLASS_BINDER,
BINDER__SET_CONTEXT_MGR, NULL);
}
static int selinux_binder_transaction(struct task_struct *from,
struct task_struct *to)
static int selinux_binder_transaction(const struct cred *from,
const struct cred *to)
{
u32 mysid = current_sid();
u32 fromsid = task_sid_binder(from);
u32 fromsid = cred_sid(from);
u32 tosid = cred_sid(to);
int rc;
if (mysid != fromsid) {
@ -2088,24 +2067,24 @@ static int selinux_binder_transaction(struct task_struct *from,
return rc;
}
return avc_has_perm(&selinux_state, fromsid, task_sid_binder(to),
return avc_has_perm(&selinux_state, fromsid, tosid,
SECCLASS_BINDER, BINDER__CALL, NULL);
}
static int selinux_binder_transfer_binder(struct task_struct *from,
struct task_struct *to)
static int selinux_binder_transfer_binder(const struct cred *from,
const struct cred *to)
{
return avc_has_perm(&selinux_state,
task_sid_binder(from), task_sid_binder(to),
cred_sid(from), cred_sid(to),
SECCLASS_BINDER, BINDER__TRANSFER,
NULL);
}
static int selinux_binder_transfer_file(struct task_struct *from,
struct task_struct *to,
static int selinux_binder_transfer_file(const struct cred *from,
const struct cred *to,
struct file *file)
{
u32 sid = task_sid_binder(to);
u32 sid = cred_sid(to);
struct file_security_struct *fsec = selinux_file(file);
struct dentry *dentry = file->f_path.dentry;
struct inode_security_struct *isec;
@ -2948,7 +2927,8 @@ static void selinux_inode_free_security(struct inode *inode)
}
static int selinux_dentry_init_security(struct dentry *dentry, int mode,
const struct qstr *name, void **ctx,
const struct qstr *name,
const char **xattr_name, void **ctx,
u32 *ctxlen)
{
u32 newsid;
@ -2961,6 +2941,9 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode,
if (rc)
return rc;
if (xattr_name)
*xattr_name = XATTR_NAME_SELINUX;
return security_sid_to_context(&selinux_state, newsid, (char **)ctx,
ctxlen);
}
@ -5688,40 +5671,41 @@ static int selinux_tun_dev_open(void *security)
#ifdef CONFIG_NETFILTER
static unsigned int selinux_ip_forward(struct sk_buff *skb,
const struct net_device *indev,
u16 family)
static unsigned int selinux_ip_forward(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
int err;
int ifindex;
u16 family;
char *addrp;
u32 peer_sid;
struct common_audit_data ad;
struct lsm_network_audit net = {0,};
u8 secmark_active;
u8 netlbl_active;
u8 peerlbl_active;
int secmark_active, peerlbl_active;
if (!selinux_policycap_netpeer())
return NF_ACCEPT;
secmark_active = selinux_secmark_enabled();
netlbl_active = netlbl_enabled();
peerlbl_active = selinux_peerlbl_enabled();
if (!secmark_active && !peerlbl_active)
return NF_ACCEPT;
family = state->pf;
if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0)
return NF_DROP;
ifindex = state->in->ifindex;
ad.type = LSM_AUDIT_DATA_NET;
ad.u.net = &net;
ad.u.net->netif = indev->ifindex;
ad.u.net->netif = ifindex;
ad.u.net->family = family;
if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0)
return NF_DROP;
if (peerlbl_active) {
err = selinux_inet_sys_rcv_skb(dev_net(indev), indev->ifindex,
int err;
err = selinux_inet_sys_rcv_skb(state->net, ifindex,
addrp, family, peer_sid, &ad);
if (err) {
selinux_netlbl_err(skb, family, err, 1);
@ -5735,7 +5719,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb,
SECCLASS_PACKET, PACKET__FORWARD_IN, &ad))
return NF_DROP;
if (netlbl_active)
if (netlbl_enabled())
/* we do this in the FORWARD path and not the POST_ROUTING
* path because we want to make sure we apply the necessary
* labeling before IPsec is applied so we can leverage AH
@ -5746,24 +5730,8 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb,
return NF_ACCEPT;
}
static unsigned int selinux_ipv4_forward(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
return selinux_ip_forward(skb, state->in, PF_INET);
}
#if IS_ENABLED(CONFIG_IPV6)
static unsigned int selinux_ipv6_forward(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
return selinux_ip_forward(skb, state->in, PF_INET6);
}
#endif /* IPV6 */
static unsigned int selinux_ip_output(struct sk_buff *skb,
u16 family)
static unsigned int selinux_ip_output(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
struct sock *sk;
u32 sid;
@ -5798,48 +5766,32 @@ static unsigned int selinux_ip_output(struct sk_buff *skb,
sid = sksec->sid;
} else
sid = SECINITSID_KERNEL;
if (selinux_netlbl_skbuff_setsid(skb, family, sid) != 0)
if (selinux_netlbl_skbuff_setsid(skb, state->pf, sid) != 0)
return NF_DROP;
return NF_ACCEPT;
}
static unsigned int selinux_ipv4_output(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
return selinux_ip_output(skb, PF_INET);
}
#if IS_ENABLED(CONFIG_IPV6)
static unsigned int selinux_ipv6_output(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
return selinux_ip_output(skb, PF_INET6);
}
#endif /* IPV6 */
static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
int ifindex,
u16 family)
const struct nf_hook_state *state)
{
struct sock *sk = skb_to_full_sk(skb);
struct sock *sk;
struct sk_security_struct *sksec;
struct common_audit_data ad;
struct lsm_network_audit net = {0,};
char *addrp;
u8 proto;
sk = skb_to_full_sk(skb);
if (sk == NULL)
return NF_ACCEPT;
sksec = sk->sk_security;
ad.type = LSM_AUDIT_DATA_NET;
ad.u.net = &net;
ad.u.net->netif = ifindex;
ad.u.net->family = family;
if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto))
ad.u.net->netif = state->out->ifindex;
ad.u.net->family = state->pf;
if (selinux_parse_skb(skb, &ad, NULL, 0, &proto))
return NF_DROP;
if (selinux_secmark_enabled())
@ -5854,26 +5806,26 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
return NF_ACCEPT;
}
static unsigned int selinux_ip_postroute(struct sk_buff *skb,
const struct net_device *outdev,
u16 family)
static unsigned int selinux_ip_postroute(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
u16 family;
u32 secmark_perm;
u32 peer_sid;
int ifindex = outdev->ifindex;
int ifindex;
struct sock *sk;
struct common_audit_data ad;
struct lsm_network_audit net = {0,};
char *addrp;
u8 secmark_active;
u8 peerlbl_active;
int secmark_active, peerlbl_active;
/* If any sort of compatibility mode is enabled then handoff processing
* to the selinux_ip_postroute_compat() function to deal with the
* special handling. We do this in an attempt to keep this function
* as fast and as clean as possible. */
if (!selinux_policycap_netpeer())
return selinux_ip_postroute_compat(skb, ifindex, family);
return selinux_ip_postroute_compat(skb, state);
secmark_active = selinux_secmark_enabled();
peerlbl_active = selinux_peerlbl_enabled();
@ -5899,6 +5851,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb,
return NF_ACCEPT;
#endif
family = state->pf;
if (sk == NULL) {
/* Without an associated socket the packet is either coming
* from the kernel or it is being forwarded; check the packet
@ -5959,6 +5912,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb,
secmark_perm = PACKET__SEND;
}
ifindex = state->out->ifindex;
ad.type = LSM_AUDIT_DATA_NET;
ad.u.net = &net;
ad.u.net->netif = ifindex;
@ -5976,7 +5930,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb,
u32 if_sid;
u32 node_sid;
if (sel_netif_sid(dev_net(outdev), ifindex, &if_sid))
if (sel_netif_sid(state->net, ifindex, &if_sid))
return NF_DROP;
if (avc_has_perm(&selinux_state,
peer_sid, if_sid,
@ -5993,23 +5947,6 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb,
return NF_ACCEPT;
}
static unsigned int selinux_ipv4_postroute(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
return selinux_ip_postroute(skb, state->out, PF_INET);
}
#if IS_ENABLED(CONFIG_IPV6)
static unsigned int selinux_ipv6_postroute(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
return selinux_ip_postroute(skb, state->out, PF_INET6);
}
#endif /* IPV6 */
#endif /* CONFIG_NETFILTER */
static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
@ -7013,34 +6950,6 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
}
#endif
static int selinux_lockdown(enum lockdown_reason what)
{
struct common_audit_data ad;
u32 sid = current_sid();
int invalid_reason = (what <= LOCKDOWN_NONE) ||
(what == LOCKDOWN_INTEGRITY_MAX) ||
(what >= LOCKDOWN_CONFIDENTIALITY_MAX);
if (WARN(invalid_reason, "Invalid lockdown reason")) {
audit_log(audit_context(),
GFP_ATOMIC, AUDIT_SELINUX_ERR,
"lockdown_reason=invalid");
return -EINVAL;
}
ad.type = LSM_AUDIT_DATA_LOCKDOWN;
ad.u.reason = what;
if (what <= LOCKDOWN_INTEGRITY_MAX)
return avc_has_perm(&selinux_state,
sid, sid, SECCLASS_LOCKDOWN,
LOCKDOWN__INTEGRITY, &ad);
else
return avc_has_perm(&selinux_state,
sid, sid, SECCLASS_LOCKDOWN,
LOCKDOWN__CONFIDENTIALITY, &ad);
}
struct lsm_blob_sizes selinux_blob_sizes __lsm_ro_after_init = {
.lbs_cred = sizeof(struct task_security_struct),
.lbs_file = sizeof(struct file_security_struct),
@ -7111,6 +7020,35 @@ static int selinux_perf_event_write(struct perf_event *event)
}
#endif
#ifdef CONFIG_IO_URING
/**
* selinux_uring_override_creds - check the requested cred override
* @new: the target creds
*
* Check to see if the current task is allowed to override it's credentials
* to service an io_uring operation.
*/
static int selinux_uring_override_creds(const struct cred *new)
{
return avc_has_perm(&selinux_state, current_sid(), cred_sid(new),
SECCLASS_IO_URING, IO_URING__OVERRIDE_CREDS, NULL);
}
/**
* selinux_uring_sqpoll - check if a io_uring polling thread can be created
*
* Check to see if the current task is allowed to create a new io_uring
* kernel polling thread.
*/
static int selinux_uring_sqpoll(void)
{
int sid = current_sid();
return avc_has_perm(&selinux_state, sid, sid,
SECCLASS_IO_URING, IO_URING__SQPOLL, NULL);
}
#endif /* CONFIG_IO_URING */
/*
* 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,
@ -7349,7 +7287,10 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(perf_event_write, selinux_perf_event_write),
#endif
LSM_HOOK_INIT(locked_down, selinux_lockdown),
#ifdef CONFIG_IO_URING
LSM_HOOK_INIT(uring_override_creds, selinux_uring_override_creds),
LSM_HOOK_INIT(uring_sqpoll, selinux_uring_sqpoll),
#endif
/*
* PUT "CLONING" (ACCESSING + ALLOCATING) HOOKS HERE
@ -7470,38 +7411,38 @@ DEFINE_LSM(selinux) = {
static const struct nf_hook_ops selinux_nf_ops[] = {
{
.hook = selinux_ipv4_postroute,
.hook = selinux_ip_postroute,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_SELINUX_LAST,
},
{
.hook = selinux_ipv4_forward,
.hook = selinux_ip_forward,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_SELINUX_FIRST,
},
{
.hook = selinux_ipv4_output,
.hook = selinux_ip_output,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_SELINUX_FIRST,
},
#if IS_ENABLED(CONFIG_IPV6)
{
.hook = selinux_ipv6_postroute,
.hook = selinux_ip_postroute,
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP6_PRI_SELINUX_LAST,
},
{
.hook = selinux_ipv6_forward,
.hook = selinux_ip_forward,
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP6_PRI_SELINUX_FIRST,
},
{
.hook = selinux_ipv6_output,
.hook = selinux_ip_output,
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP6_PRI_SELINUX_FIRST,

View File

@ -250,10 +250,10 @@ struct security_class_mapping secclass_map[] = {
{ COMMON_SOCK_PERMS, NULL } },
{ "perf_event",
{ "open", "cpu", "kernel", "tracepoint", "read", "write", NULL } },
{ "lockdown",
{ "integrity", "confidentiality", NULL } },
{ "anon_inode",
{ COMMON_FILE_PERMS, NULL } },
{ "io_uring",
{ "override_creds", "sqpoll", NULL } },
{ NULL }
};

View File

@ -29,6 +29,7 @@
/**
* selinux_netlbl_sidlookup_cached - Cache a SID lookup
* @skb: the packet
* @family: the packet's address family
* @secattr: the NetLabel security attributes
* @sid: the SID
*
@ -128,6 +129,7 @@ void selinux_netlbl_cache_invalidate(void)
/**
* selinux_netlbl_err - Handle a NetLabel packet error
* @skb: the packet
* @family: the packet's address family
* @error: the error code
* @gateway: true if host is acting as a gateway, false otherwise
*
@ -160,7 +162,6 @@ void selinux_netlbl_sk_security_free(struct sk_security_struct *sksec)
/**
* selinux_netlbl_sk_security_reset - Reset the NetLabel fields
* @sksec: the sk_security_struct
* @family: the socket family
*
* Description:
* Called when the NetLabel state of a sk_security_struct needs to be reset.
@ -313,6 +314,7 @@ assoc_request_return:
/**
* selinux_netlbl_inet_conn_request - Label an incoming stream connection
* @req: incoming connection request socket
* @family: the request socket's address family
*
* Description:
* A new incoming connection request is represented by @req, we need to label
@ -343,6 +345,7 @@ inet_conn_request_return:
/**
* selinux_netlbl_inet_csk_clone - Initialize the newly created sock
* @sk: the new sock
* @family: the sock's address family
*
* Description:
* A new connection has been established using @sk, we've already labeled the
@ -378,7 +381,7 @@ void selinux_netlbl_sctp_sk_clone(struct sock *sk, struct sock *newsk)
/**
* selinux_netlbl_socket_post_create - Label a socket using NetLabel
* @sock: the socket to label
* @sk: the sock to label
* @family: protocol family
*
* Description:

View File

@ -73,7 +73,7 @@ static unsigned int sel_netport_hashfn(u16 pnum)
/**
* sel_netport_find - Search for a port record
* @protocol: protocol
* @port: pnum
* @pnum: port
*
* Description:
* Search the network port table and return the matching record. If an entry

View File

@ -8,6 +8,7 @@
#include <linux/slab.h>
#include <linux/errno.h>
#include "hashtab.h"
#include "security.h"
static struct kmem_cache *hashtab_node_cachep __ro_after_init;

View File

@ -553,6 +553,7 @@ int mls_compute_sid(struct policydb *p,
#ifdef CONFIG_NETLABEL
/**
* mls_export_netlbl_lvl - Export the MLS sensitivity levels to NetLabel
* @p: the policy
* @context: the security context
* @secattr: the NetLabel security attributes
*
@ -574,6 +575,7 @@ void mls_export_netlbl_lvl(struct policydb *p,
/**
* mls_import_netlbl_lvl - Import the NetLabel MLS sensitivity levels
* @p: the policy
* @context: the security context
* @secattr: the NetLabel security attributes
*
@ -595,6 +597,7 @@ void mls_import_netlbl_lvl(struct policydb *p,
/**
* mls_export_netlbl_cat - Export the MLS categories to NetLabel
* @p: the policy
* @context: the security context
* @secattr: the NetLabel security attributes
*
@ -622,6 +625,7 @@ int mls_export_netlbl_cat(struct policydb *p,
/**
* mls_import_netlbl_cat - Import the MLS categories from NetLabel
* @p: the policy
* @context: the security context
* @secattr: the NetLabel security attributes
*

View File

@ -1102,7 +1102,7 @@ allow:
* @state: SELinux state
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
* @orig_tclass: target security class
* @avd: access vector decisions
* @xperms: extended permissions
*
@ -1626,6 +1626,7 @@ int security_context_str_to_sid(struct selinux_state *state,
* @scontext_len: length in bytes
* @sid: security identifier, SID
* @def_sid: default SID to assign on error
* @gfp_flags: the allocator get-free-page (GFP) flags
*
* Obtains a SID associated with the security context that
* has the string representation specified by @scontext.
@ -1919,6 +1920,7 @@ out:
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
* @qstr: object name
* @out_sid: security identifier for new subject/object
*
* Compute a SID to use for labeling a new subject or object in the
@ -1947,6 +1949,7 @@ int security_transition_sid_user(struct selinux_state *state,
/**
* security_member_sid - Compute the SID for member selection.
* @state: SELinux state
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
@ -2273,6 +2276,7 @@ void selinux_policy_commit(struct selinux_state *state,
* @state: SELinux state
* @data: binary policy data
* @len: length of data in bytes
* @load_state: policy load state
*
* Load a new set of security policy configuration data,
* validate it and convert the SID table as necessary.
@ -2376,6 +2380,43 @@ err_policy:
return rc;
}
/**
* ocontext_to_sid - Helper to safely get sid for an ocontext
* @sidtab: SID table
* @c: ocontext structure
* @index: index of the context entry (0 or 1)
* @out_sid: pointer to the resulting SID value
*
* For all ocontexts except OCON_ISID the SID fields are populated
* on-demand when needed. Since updating the SID value is an SMP-sensitive
* operation, this helper must be used to do that safely.
*
* WARNING: This function may return -ESTALE, indicating that the caller
* must retry the operation after re-acquiring the policy pointer!
*/
static int ocontext_to_sid(struct sidtab *sidtab, struct ocontext *c,
size_t index, u32 *out_sid)
{
int rc;
u32 sid;
/* Ensure the associated sidtab entry is visible to this thread. */
sid = smp_load_acquire(&c->sid[index]);
if (!sid) {
rc = sidtab_context_to_sid(sidtab, &c->context[index], &sid);
if (rc)
return rc;
/*
* Ensure the new sidtab entry is visible to other threads
* when they see the SID.
*/
smp_store_release(&c->sid[index], sid);
}
*out_sid = sid;
return 0;
}
/**
* security_port_sid - Obtain the SID for a port.
* @state: SELinux state
@ -2414,17 +2455,13 @@ retry:
}
if (c) {
if (!c->sid[0]) {
rc = sidtab_context_to_sid(sidtab, &c->context[0],
&c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
rc = ocontext_to_sid(sidtab, c, 0, out_sid);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
*out_sid = c->sid[0];
if (rc)
goto out;
} else {
*out_sid = SECINITSID_PORT;
}
@ -2473,18 +2510,13 @@ retry:
}
if (c) {
if (!c->sid[0]) {
rc = sidtab_context_to_sid(sidtab,
&c->context[0],
&c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
rc = ocontext_to_sid(sidtab, c, 0, out_sid);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
*out_sid = c->sid[0];
if (rc)
goto out;
} else
*out_sid = SECINITSID_UNLABELED;
@ -2497,7 +2529,7 @@ out:
* security_ib_endport_sid - Obtain the SID for a subnet management interface.
* @state: SELinux state
* @dev_name: device name
* @port: port number
* @port_num: port number
* @out_sid: security identifier
*/
int security_ib_endport_sid(struct selinux_state *state,
@ -2533,17 +2565,13 @@ retry:
}
if (c) {
if (!c->sid[0]) {
rc = sidtab_context_to_sid(sidtab, &c->context[0],
&c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
rc = ocontext_to_sid(sidtab, c, 0, out_sid);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
*out_sid = c->sid[0];
if (rc)
goto out;
} else
*out_sid = SECINITSID_UNLABELED;
@ -2587,25 +2615,13 @@ retry:
}
if (c) {
if (!c->sid[0] || !c->sid[1]) {
rc = sidtab_context_to_sid(sidtab, &c->context[0],
&c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
rc = sidtab_context_to_sid(sidtab, &c->context[1],
&c->sid[1]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
rc = ocontext_to_sid(sidtab, c, 0, if_sid);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
*if_sid = c->sid[0];
if (rc)
goto out;
} else
*if_sid = SECINITSID_NETIF;
@ -2697,18 +2713,13 @@ retry:
}
if (c) {
if (!c->sid[0]) {
rc = sidtab_context_to_sid(sidtab,
&c->context[0],
&c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
rc = ocontext_to_sid(sidtab, c, 0, out_sid);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
*out_sid = c->sid[0];
if (rc)
goto out;
} else {
*out_sid = SECINITSID_NODE;
}
@ -2849,9 +2860,10 @@ out_unlock:
/**
* __security_genfs_sid - Helper to obtain a SID for a file in a filesystem
* @policy: policy
* @fstype: filesystem type
* @path: path from root of mount
* @sclass: file security class
* @orig_sclass: file security class
* @sid: SID for path
*
* Obtain a SID to use for a file in a filesystem that
@ -2873,7 +2885,7 @@ static inline int __security_genfs_sid(struct selinux_policy *policy,
u16 sclass;
struct genfs *genfs;
struct ocontext *c;
int rc, cmp = 0;
int cmp = 0;
while (path[0] == '/' && path[1] == '/')
path++;
@ -2887,9 +2899,8 @@ static inline int __security_genfs_sid(struct selinux_policy *policy,
break;
}
rc = -ENOENT;
if (!genfs || cmp)
goto out;
return -ENOENT;
for (c = genfs->head; c; c = c->next) {
len = strlen(c->u.name);
@ -2898,20 +2909,10 @@ static inline int __security_genfs_sid(struct selinux_policy *policy,
break;
}
rc = -ENOENT;
if (!c)
goto out;
return -ENOENT;
if (!c->sid[0]) {
rc = sidtab_context_to_sid(sidtab, &c->context[0], &c->sid[0]);
if (rc)
goto out;
}
*sid = c->sid[0];
rc = 0;
out:
return rc;
return ocontext_to_sid(sidtab, c, 0, sid);
}
/**
@ -2919,7 +2920,7 @@ out:
* @state: SELinux state
* @fstype: filesystem type
* @path: path from root of mount
* @sclass: file security class
* @orig_sclass: file security class
* @sid: SID for path
*
* Acquire policy_rwlock before calling __security_genfs_sid() and release
@ -2996,17 +2997,13 @@ retry:
if (c) {
sbsec->behavior = c->v.behavior;
if (!c->sid[0]) {
rc = sidtab_context_to_sid(sidtab, &c->context[0],
&c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
rc = ocontext_to_sid(sidtab, c, 0, &sbsec->sid);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
sbsec->sid = c->sid[0];
if (rc)
goto out;
} else {
rc = __security_genfs_sid(policy, fstype, "/",
SECCLASS_DIR, &sbsec->sid);
@ -3305,6 +3302,7 @@ out_unlock:
* @nlbl_sid: NetLabel SID
* @nlbl_type: NetLabel labeling protocol type
* @xfrm_sid: XFRM SID
* @peer_sid: network peer sid
*
* Description:
* Compare the @nlbl_sid and @xfrm_sid values and if the two SIDs can be

View File

@ -4700,6 +4700,48 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
return 0;
}
#ifdef CONFIG_IO_URING
/**
* smack_uring_override_creds - Is io_uring cred override allowed?
* @new: the target creds
*
* Check to see if the current task is allowed to override it's credentials
* to service an io_uring operation.
*/
static int smack_uring_override_creds(const struct cred *new)
{
struct task_smack *tsp = smack_cred(current_cred());
struct task_smack *nsp = smack_cred(new);
/*
* Allow the degenerate case where the new Smack value is
* the same as the current Smack value.
*/
if (tsp->smk_task == nsp->smk_task)
return 0;
if (smack_privileged_cred(CAP_MAC_OVERRIDE, current_cred()))
return 0;
return -EPERM;
}
/**
* smack_uring_sqpoll - check if a io_uring polling thread can be created
*
* Check to see if the current task is allowed to create a new io_uring
* kernel polling thread.
*/
static int smack_uring_sqpoll(void)
{
if (smack_privileged_cred(CAP_MAC_ADMIN, current_cred()))
return 0;
return -EPERM;
}
#endif /* CONFIG_IO_URING */
struct lsm_blob_sizes smack_blob_sizes __lsm_ro_after_init = {
.lbs_cred = sizeof(struct task_smack),
.lbs_file = sizeof(struct smack_known *),
@ -4852,6 +4894,10 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(inode_copy_up, smack_inode_copy_up),
LSM_HOOK_INIT(inode_copy_up_xattr, smack_inode_copy_up_xattr),
LSM_HOOK_INIT(dentry_create_files_as, smack_dentry_create_files_as),
#ifdef CONFIG_IO_URING
LSM_HOOK_INIT(uring_override_creds, smack_uring_override_creds),
LSM_HOOK_INIT(uring_sqpoll, smack_uring_sqpoll),
#endif
};