diff options
Diffstat (limited to 'drivers/tty/tty_audit.c')
| -rw-r--r-- | drivers/tty/tty_audit.c | 313 |
1 files changed, 107 insertions, 206 deletions
diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c index a4fdce74f883..75542333c54a 100644 --- a/drivers/tty/tty_audit.c +++ b/drivers/tty/tty_audit.c @@ -1,10 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Creating audit events from TTY input. * - * Copyright (C) 2007 Red Hat, Inc. All rights reserved. This copyrighted - * material is made available to anyone wishing to use, modify, copy, or - * redistribute it subject to the terms and conditions of the GNU General - * Public License v.2. + * Copyright (C) 2007 Red Hat, Inc. All rights reserved. * * Authors: Miloslav Trmac <mitr@redhat.com> */ @@ -12,33 +10,41 @@ #include <linux/audit.h> #include <linux/slab.h> #include <linux/tty.h> +#include "tty.h" + +#define TTY_AUDIT_BUF_SIZE 4096 struct tty_audit_buf { - atomic_t count; struct mutex mutex; /* Protects all data below */ - int major, minor; /* The TTY which the data is from */ - unsigned icanon:1; + dev_t dev; /* The TTY which the data is from */ + bool icanon; size_t valid; - unsigned char *data; /* Allocated size N_TTY_BUF_SIZE */ + u8 *data; /* Allocated size TTY_AUDIT_BUF_SIZE */ }; -static struct tty_audit_buf *tty_audit_buf_alloc(int major, int minor, - unsigned icanon) +static struct tty_audit_buf *tty_audit_buf_ref(void) +{ + struct tty_audit_buf *buf; + + buf = current->signal->tty_audit_buf; + WARN_ON(buf == ERR_PTR(-ESRCH)); + return buf; +} + +static struct tty_audit_buf *tty_audit_buf_alloc(void) { struct tty_audit_buf *buf; - buf = kmalloc(sizeof(*buf), GFP_KERNEL); + buf = kzalloc(sizeof(*buf), GFP_KERNEL); if (!buf) goto err; - buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL); + + buf->data = kmalloc(TTY_AUDIT_BUF_SIZE, GFP_KERNEL); if (!buf->data) goto err_buf; - atomic_set(&buf->count, 1); + mutex_init(&buf->mutex); - buf->major = major; - buf->minor = minor; - buf->icanon = icanon; - buf->valid = 0; + return buf; err_buf: @@ -54,37 +60,31 @@ static void tty_audit_buf_free(struct tty_audit_buf *buf) kfree(buf); } -static void tty_audit_buf_put(struct tty_audit_buf *buf) -{ - if (atomic_dec_and_test(&buf->count)) - tty_audit_buf_free(buf); -} - -static void tty_audit_log(const char *description, int major, int minor, - unsigned char *data, size_t size) +static void tty_audit_log(const char *description, dev_t dev, + const u8 *data, size_t size) { struct audit_buffer *ab; - struct task_struct *tsk = current; - uid_t uid = from_kuid(&init_user_ns, task_uid(tsk)); - uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(tsk)); - u32 sessionid = audit_get_sessionid(tsk); - - ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY); - if (ab) { - char name[sizeof(tsk->comm)]; - - audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d" - " minor=%d comm=", description, tsk->pid, uid, - loginuid, sessionid, major, minor); - get_task_comm(name, tsk); - audit_log_untrustedstring(ab, name); - audit_log_format(ab, " data="); - audit_log_n_hex(ab, data, size); - audit_log_end(ab); - } + pid_t pid = task_pid_nr(current); + uid_t uid = from_kuid(&init_user_ns, task_uid(current)); + uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(current)); + unsigned int sessionid = audit_get_sessionid(current); + char name[TASK_COMM_LEN]; + + ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_TTY); + if (!ab) + return; + + audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d minor=%d comm=", + description, pid, uid, loginuid, sessionid, + MAJOR(dev), MINOR(dev)); + get_task_comm(name, current); + audit_log_untrustedstring(ab, name); + audit_log_format(ab, " data="); + audit_log_n_hex(ab, data, size); + audit_log_end(ab); } -/** +/* * tty_audit_buf_push - Push buffered data out * * Generate an audit message from the contents of @buf, which is owned by @@ -94,11 +94,11 @@ static void tty_audit_buf_push(struct tty_audit_buf *buf) { if (buf->valid == 0) return; - if (audit_enabled == 0) { + if (audit_enabled == AUDIT_OFF) { buf->valid = 0; return; } - tty_audit_log("tty", buf->major, buf->minor, buf->data, buf->valid); + tty_audit_log("tty", buf->dev, buf->data, buf->valid); buf->valid = 0; } @@ -107,24 +107,23 @@ static void tty_audit_buf_push(struct tty_audit_buf *buf) * * Make sure all buffered data is written out and deallocate the buffer. * Only needs to be called if current->signal->tty_audit_buf != %NULL. + * + * The process is single-threaded at this point; no other threads share + * current->signal. */ void tty_audit_exit(void) { struct tty_audit_buf *buf; - buf = current->signal->tty_audit_buf; - current->signal->tty_audit_buf = NULL; + buf = xchg(¤t->signal->tty_audit_buf, ERR_PTR(-ESRCH)); if (!buf) return; - mutex_lock(&buf->mutex); tty_audit_buf_push(buf); - mutex_unlock(&buf->mutex); - - tty_audit_buf_put(buf); + tty_audit_buf_free(buf); } -/** +/* * tty_audit_fork - Copy TTY audit state for a new task * * Set up TTY audit state in @sig from current. @sig needs no locking. @@ -132,219 +131,121 @@ void tty_audit_exit(void) void tty_audit_fork(struct signal_struct *sig) { sig->audit_tty = current->signal->audit_tty; - sig->audit_tty_log_passwd = current->signal->audit_tty_log_passwd; } -/** +/* * tty_audit_tiocsti - Log TIOCSTI */ -void tty_audit_tiocsti(struct tty_struct *tty, char ch) +void tty_audit_tiocsti(const struct tty_struct *tty, u8 ch) { - struct tty_audit_buf *buf; - int major, minor, should_audit; - unsigned long flags; - - spin_lock_irqsave(¤t->sighand->siglock, flags); - should_audit = current->signal->audit_tty; - buf = current->signal->tty_audit_buf; - if (buf) - atomic_inc(&buf->count); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); + dev_t dev; - major = tty->driver->major; - minor = tty->driver->minor_start + tty->index; - if (buf) { - mutex_lock(&buf->mutex); - if (buf->major == major && buf->minor == minor) - tty_audit_buf_push(buf); - mutex_unlock(&buf->mutex); - tty_audit_buf_put(buf); - } - - if (should_audit && audit_enabled) { - kuid_t auid; - unsigned int sessionid; + dev = MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index; + if (tty_audit_push()) + return; - auid = audit_get_loginuid(current); - sessionid = audit_get_sessionid(current); - tty_audit_log("ioctl=TIOCSTI", major, minor, &ch, 1); - } + if (audit_enabled) + tty_audit_log("ioctl=TIOCSTI", dev, &ch, 1); } -/** - * tty_audit_push_current - Flush current's pending audit data +/* + * tty_audit_push - Flush current's pending audit data * - * Try to lock sighand and get a reference to the tty audit buffer if available. - * Flush the buffer or return an appropriate error code. + * Returns 0 if success, -EPERM if tty audit is disabled */ -int tty_audit_push_current(void) +int tty_audit_push(void) { - struct tty_audit_buf *buf = ERR_PTR(-EPERM); - struct task_struct *tsk = current; - unsigned long flags; + struct tty_audit_buf *buf; - if (!lock_task_sighand(tsk, &flags)) - return -ESRCH; + if (~current->signal->audit_tty & AUDIT_TTY_ENABLE) + return -EPERM; - if (tsk->signal->audit_tty) { - buf = tsk->signal->tty_audit_buf; - if (buf) - atomic_inc(&buf->count); + buf = tty_audit_buf_ref(); + if (!IS_ERR_OR_NULL(buf)) { + mutex_lock(&buf->mutex); + tty_audit_buf_push(buf); + mutex_unlock(&buf->mutex); } - unlock_task_sighand(tsk, &flags); - - /* - * Return 0 when signal->audit_tty set - * but tsk->signal->tty_audit_buf == NULL. - */ - if (!buf || IS_ERR(buf)) - return PTR_ERR(buf); - - mutex_lock(&buf->mutex); - tty_audit_buf_push(buf); - mutex_unlock(&buf->mutex); - - tty_audit_buf_put(buf); return 0; } -/** +/* * tty_audit_buf_get - Get an audit buffer. * - * Get an audit buffer for @tty, allocate it if necessary. Return %NULL - * if TTY auditing is disabled or out of memory. Otherwise, return a new - * reference to the buffer. + * Get an audit buffer, allocate it if necessary. Return %NULL + * if out of memory or ERR_PTR(-ESRCH) if tty_audit_exit() has already + * occurred. Otherwise, return a new reference to the buffer. */ -static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, - unsigned icanon) +static struct tty_audit_buf *tty_audit_buf_get(void) { - struct tty_audit_buf *buf, *buf2; - unsigned long flags; - - buf = NULL; - buf2 = NULL; - spin_lock_irqsave(¤t->sighand->siglock, flags); - if (likely(!current->signal->audit_tty)) - goto out; - buf = current->signal->tty_audit_buf; - if (buf) { - atomic_inc(&buf->count); - goto out; - } - spin_unlock_irqrestore(¤t->sighand->siglock, flags); + struct tty_audit_buf *buf; + + buf = tty_audit_buf_ref(); + if (buf) + return buf; - buf2 = tty_audit_buf_alloc(tty->driver->major, - tty->driver->minor_start + tty->index, - icanon); - if (buf2 == NULL) { + buf = tty_audit_buf_alloc(); + if (buf == NULL) { audit_log_lost("out of memory in TTY auditing"); return NULL; } - spin_lock_irqsave(¤t->sighand->siglock, flags); - if (!current->signal->audit_tty) - goto out; - buf = current->signal->tty_audit_buf; - if (!buf) { - current->signal->tty_audit_buf = buf2; - buf = buf2; - buf2 = NULL; - } - atomic_inc(&buf->count); - /* Fall through */ - out: - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - if (buf2) - tty_audit_buf_free(buf2); - return buf; + /* Race to use this buffer, free it if another wins */ + if (cmpxchg(¤t->signal->tty_audit_buf, NULL, buf) != NULL) + tty_audit_buf_free(buf); + return tty_audit_buf_ref(); } -/** +/* * tty_audit_add_data - Add data for TTY auditing. * * Audit @data of @size from @tty, if necessary. */ -void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, - size_t size, unsigned icanon) +void tty_audit_add_data(const struct tty_struct *tty, const void *data, + size_t size) { struct tty_audit_buf *buf; - int major, minor; - int audit_log_tty_passwd; - unsigned long flags; + unsigned int audit_tty; + bool icanon = L_ICANON(tty); + dev_t dev; - if (unlikely(size == 0)) + audit_tty = READ_ONCE(current->signal->audit_tty); + if (~audit_tty & AUDIT_TTY_ENABLE) return; - spin_lock_irqsave(¤t->sighand->siglock, flags); - audit_log_tty_passwd = current->signal->audit_tty_log_passwd; - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - if (!audit_log_tty_passwd && icanon && !L_ECHO(tty)) + if (unlikely(size == 0)) return; if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) return; - buf = tty_audit_buf_get(tty, icanon); - if (!buf) + if ((~audit_tty & AUDIT_TTY_LOG_PASSWD) && icanon && !L_ECHO(tty)) + return; + + buf = tty_audit_buf_get(); + if (IS_ERR_OR_NULL(buf)) return; mutex_lock(&buf->mutex); - major = tty->driver->major; - minor = tty->driver->minor_start + tty->index; - if (buf->major != major || buf->minor != minor - || buf->icanon != icanon) { + dev = MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index; + if (buf->dev != dev || buf->icanon != icanon) { tty_audit_buf_push(buf); - buf->major = major; - buf->minor = minor; + buf->dev = dev; buf->icanon = icanon; } do { size_t run; - run = N_TTY_BUF_SIZE - buf->valid; + run = TTY_AUDIT_BUF_SIZE - buf->valid; if (run > size) run = size; memcpy(buf->data + buf->valid, data, run); buf->valid += run; data += run; size -= run; - if (buf->valid == N_TTY_BUF_SIZE) + if (buf->valid == TTY_AUDIT_BUF_SIZE) tty_audit_buf_push(buf); } while (size != 0); mutex_unlock(&buf->mutex); - tty_audit_buf_put(buf); -} - -/** - * tty_audit_push - Push buffered data out - * - * Make sure no audit data is pending for @tty on the current process. - */ -void tty_audit_push(struct tty_struct *tty) -{ - struct tty_audit_buf *buf; - unsigned long flags; - - spin_lock_irqsave(¤t->sighand->siglock, flags); - if (likely(!current->signal->audit_tty)) { - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - return; - } - buf = current->signal->tty_audit_buf; - if (buf) - atomic_inc(&buf->count); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - - if (buf) { - int major, minor; - - major = tty->driver->major; - minor = tty->driver->minor_start + tty->index; - mutex_lock(&buf->mutex); - if (buf->major == major && buf->minor == minor) - tty_audit_buf_push(buf); - mutex_unlock(&buf->mutex); - tty_audit_buf_put(buf); - } } |
