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