linux/drivers/tty/tty_audit.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Creating audit events from TTY input.
   4 *
   5 * Copyright (C) 2007 Red Hat, Inc.  All rights reserved.
   6 *
   7 * Authors: Miloslav Trmac <mitr@redhat.com>
   8 */
   9
  10#include <linux/audit.h>
  11#include <linux/slab.h>
  12#include <linux/tty.h>
  13#include "tty.h"
  14
  15struct tty_audit_buf {
  16        struct mutex mutex;     /* Protects all data below */
  17        dev_t dev;              /* The TTY which the data is from */
  18        unsigned icanon:1;
  19        size_t valid;
  20        unsigned char *data;    /* Allocated size N_TTY_BUF_SIZE */
  21};
  22
  23static struct tty_audit_buf *tty_audit_buf_ref(void)
  24{
  25        struct tty_audit_buf *buf;
  26
  27        buf = current->signal->tty_audit_buf;
  28        WARN_ON(buf == ERR_PTR(-ESRCH));
  29        return buf;
  30}
  31
  32static struct tty_audit_buf *tty_audit_buf_alloc(void)
  33{
  34        struct tty_audit_buf *buf;
  35
  36        buf = kmalloc(sizeof(*buf), GFP_KERNEL);
  37        if (!buf)
  38                goto err;
  39        buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
  40        if (!buf->data)
  41                goto err_buf;
  42        mutex_init(&buf->mutex);
  43        buf->dev = MKDEV(0, 0);
  44        buf->icanon = 0;
  45        buf->valid = 0;
  46        return buf;
  47
  48err_buf:
  49        kfree(buf);
  50err:
  51        return NULL;
  52}
  53
  54static void tty_audit_buf_free(struct tty_audit_buf *buf)
  55{
  56        WARN_ON(buf->valid != 0);
  57        kfree(buf->data);
  58        kfree(buf);
  59}
  60
  61static void tty_audit_log(const char *description, dev_t dev,
  62                          unsigned char *data, size_t size)
  63{
  64        struct audit_buffer *ab;
  65        pid_t pid = task_pid_nr(current);
  66        uid_t uid = from_kuid(&init_user_ns, task_uid(current));
  67        uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(current));
  68        unsigned int sessionid = audit_get_sessionid(current);
  69
  70        ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_TTY);
  71        if (ab) {
  72                char name[sizeof(current->comm)];
  73
  74                audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d"
  75                                 " minor=%d comm=", description, pid, uid,
  76                                 loginuid, sessionid, MAJOR(dev), MINOR(dev));
  77                get_task_comm(name, current);
  78                audit_log_untrustedstring(ab, name);
  79                audit_log_format(ab, " data=");
  80                audit_log_n_hex(ab, data, size);
  81                audit_log_end(ab);
  82        }
  83}
  84
  85/*
  86 *      tty_audit_buf_push      -       Push buffered data out
  87 *
  88 *      Generate an audit message from the contents of @buf, which is owned by
  89 *      the current task.  @buf->mutex must be locked.
  90 */
  91static void tty_audit_buf_push(struct tty_audit_buf *buf)
  92{
  93        if (buf->valid == 0)
  94                return;
  95        if (audit_enabled == AUDIT_OFF) {
  96                buf->valid = 0;
  97                return;
  98        }
  99        tty_audit_log("tty", buf->dev, buf->data, buf->valid);
 100        buf->valid = 0;
 101}
 102
 103/**
 104 *      tty_audit_exit  -       Handle a task exit
 105 *
 106 *      Make sure all buffered data is written out and deallocate the buffer.
 107 *      Only needs to be called if current->signal->tty_audit_buf != %NULL.
 108 *
 109 *      The process is single-threaded at this point; no other threads share
 110 *      current->signal.
 111 */
 112void tty_audit_exit(void)
 113{
 114        struct tty_audit_buf *buf;
 115
 116        buf = xchg(&current->signal->tty_audit_buf, ERR_PTR(-ESRCH));
 117        if (!buf)
 118                return;
 119
 120        tty_audit_buf_push(buf);
 121        tty_audit_buf_free(buf);
 122}
 123
 124/*
 125 *      tty_audit_fork  -       Copy TTY audit state for a new task
 126 *
 127 *      Set up TTY audit state in @sig from current.  @sig needs no locking.
 128 */
 129void tty_audit_fork(struct signal_struct *sig)
 130{
 131        sig->audit_tty = current->signal->audit_tty;
 132}
 133
 134/*
 135 *      tty_audit_tiocsti       -       Log TIOCSTI
 136 */
 137void tty_audit_tiocsti(struct tty_struct *tty, char ch)
 138{
 139        dev_t dev;
 140
 141        dev = MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index;
 142        if (tty_audit_push())
 143                return;
 144
 145        if (audit_enabled)
 146                tty_audit_log("ioctl=TIOCSTI", dev, &ch, 1);
 147}
 148
 149/*
 150 *      tty_audit_push  -       Flush current's pending audit data
 151 *
 152 *      Returns 0 if success, -EPERM if tty audit is disabled
 153 */
 154int tty_audit_push(void)
 155{
 156        struct tty_audit_buf *buf;
 157
 158        if (~current->signal->audit_tty & AUDIT_TTY_ENABLE)
 159                return -EPERM;
 160
 161        buf = tty_audit_buf_ref();
 162        if (!IS_ERR_OR_NULL(buf)) {
 163                mutex_lock(&buf->mutex);
 164                tty_audit_buf_push(buf);
 165                mutex_unlock(&buf->mutex);
 166        }
 167        return 0;
 168}
 169
 170/*
 171 *      tty_audit_buf_get       -       Get an audit buffer.
 172 *
 173 *      Get an audit buffer, allocate it if necessary.  Return %NULL
 174 *      if out of memory or ERR_PTR(-ESRCH) if tty_audit_exit() has already
 175 *      occurred.  Otherwise, return a new reference to the buffer.
 176 */
 177static struct tty_audit_buf *tty_audit_buf_get(void)
 178{
 179        struct tty_audit_buf *buf;
 180
 181        buf = tty_audit_buf_ref();
 182        if (buf)
 183                return buf;
 184
 185        buf = tty_audit_buf_alloc();
 186        if (buf == NULL) {
 187                audit_log_lost("out of memory in TTY auditing");
 188                return NULL;
 189        }
 190
 191        /* Race to use this buffer, free it if another wins */
 192        if (cmpxchg(&current->signal->tty_audit_buf, NULL, buf) != NULL)
 193                tty_audit_buf_free(buf);
 194        return tty_audit_buf_ref();
 195}
 196
 197/*
 198 *      tty_audit_add_data      -       Add data for TTY auditing.
 199 *
 200 *      Audit @data of @size from @tty, if necessary.
 201 */
 202void tty_audit_add_data(struct tty_struct *tty, const void *data, size_t size)
 203{
 204        struct tty_audit_buf *buf;
 205        unsigned int icanon = !!L_ICANON(tty);
 206        unsigned int audit_tty;
 207        dev_t dev;
 208
 209        audit_tty = READ_ONCE(current->signal->audit_tty);
 210        if (~audit_tty & AUDIT_TTY_ENABLE)
 211                return;
 212
 213        if (unlikely(size == 0))
 214                return;
 215
 216        if (tty->driver->type == TTY_DRIVER_TYPE_PTY
 217            && tty->driver->subtype == PTY_TYPE_MASTER)
 218                return;
 219
 220        if ((~audit_tty & AUDIT_TTY_LOG_PASSWD) && icanon && !L_ECHO(tty))
 221                return;
 222
 223        buf = tty_audit_buf_get();
 224        if (IS_ERR_OR_NULL(buf))
 225                return;
 226
 227        mutex_lock(&buf->mutex);
 228        dev = MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index;
 229        if (buf->dev != dev || buf->icanon != icanon) {
 230                tty_audit_buf_push(buf);
 231                buf->dev = dev;
 232                buf->icanon = icanon;
 233        }
 234        do {
 235                size_t run;
 236
 237                run = N_TTY_BUF_SIZE - buf->valid;
 238                if (run > size)
 239                        run = size;
 240                memcpy(buf->data + buf->valid, data, run);
 241                buf->valid += run;
 242                data += run;
 243                size -= run;
 244                if (buf->valid == N_TTY_BUF_SIZE)
 245                        tty_audit_buf_push(buf);
 246        } while (size != 0);
 247        mutex_unlock(&buf->mutex);
 248}
 249