linux/drivers/char/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/tty.h>
  14
  15struct tty_audit_buf {
  16        atomic_t count;
  17        struct mutex mutex;     /* Protects all data below */
  18        int major, minor;       /* 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_alloc(int major, int minor,
  25                                                 int icanon)
  26{
  27        struct tty_audit_buf *buf;
  28
  29        buf = kmalloc(sizeof(*buf), GFP_KERNEL);
  30        if (!buf)
  31                goto err;
  32        buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
  33        if (!buf->data)
  34                goto err_buf;
  35        atomic_set(&buf->count, 1);
  36        mutex_init(&buf->mutex);
  37        buf->major = major;
  38        buf->minor = minor;
  39        buf->icanon = icanon;
  40        buf->valid = 0;
  41        return buf;
  42
  43err_buf:
  44        kfree(buf);
  45err:
  46        return NULL;
  47}
  48
  49static void tty_audit_buf_free(struct tty_audit_buf *buf)
  50{
  51        WARN_ON(buf->valid != 0);
  52        kfree(buf->data);
  53        kfree(buf);
  54}
  55
  56static void tty_audit_buf_put(struct tty_audit_buf *buf)
  57{
  58        if (atomic_dec_and_test(&buf->count))
  59                tty_audit_buf_free(buf);
  60}
  61
  62static void tty_audit_log(const char *description, struct task_struct *tsk,
  63                          uid_t loginuid, unsigned sessionid, int major,
  64                          int minor, unsigned char *data, size_t size)
  65{
  66        struct audit_buffer *ab;
  67
  68        ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY);
  69        if (ab) {
  70                char name[sizeof(tsk->comm)];
  71                uid_t uid = task_uid(tsk);
  72
  73                audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u "
  74                                 "major=%d minor=%d comm=", description,
  75                                 tsk->pid, uid, loginuid, sessionid,
  76                                 major, minor);
  77                get_task_comm(name, tsk);
  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 *      @tsk with @loginuid.  @buf->mutex must be locked.
  90 */
  91static void tty_audit_buf_push(struct task_struct *tsk, uid_t loginuid,
  92                               unsigned int sessionid,
  93                               struct tty_audit_buf *buf)
  94{
  95        if (buf->valid == 0)
  96                return;
  97        if (audit_enabled == 0)
  98                return;
  99        tty_audit_log("tty", tsk, loginuid, sessionid, buf->major, buf->minor,
 100                      buf->data, buf->valid);
 101        buf->valid = 0;
 102}
 103
 104/**
 105 *      tty_audit_buf_push_current      -       Push buffered data out
 106 *
 107 *      Generate an audit message from the contents of @buf, which is owned by
 108 *      the current task.  @buf->mutex must be locked.
 109 */
 110static void tty_audit_buf_push_current(struct tty_audit_buf *buf)
 111{
 112        uid_t auid = audit_get_loginuid(current);
 113        unsigned int sessionid = audit_get_sessionid(current);
 114        tty_audit_buf_push(current, auid, sessionid, buf);
 115}
 116
 117/**
 118 *      tty_audit_exit  -       Handle a task exit
 119 *
 120 *      Make sure all buffered data is written out and deallocate the buffer.
 121 *      Only needs to be called if current->signal->tty_audit_buf != %NULL.
 122 */
 123void tty_audit_exit(void)
 124{
 125        struct tty_audit_buf *buf;
 126
 127        spin_lock_irq(&current->sighand->siglock);
 128        buf = current->signal->tty_audit_buf;
 129        current->signal->tty_audit_buf = NULL;
 130        spin_unlock_irq(&current->sighand->siglock);
 131        if (!buf)
 132                return;
 133
 134        mutex_lock(&buf->mutex);
 135        tty_audit_buf_push_current(buf);
 136        mutex_unlock(&buf->mutex);
 137
 138        tty_audit_buf_put(buf);
 139}
 140
 141/**
 142 *      tty_audit_fork  -       Copy TTY audit state for a new task
 143 *
 144 *      Set up TTY audit state in @sig from current.  @sig needs no locking.
 145 */
 146void tty_audit_fork(struct signal_struct *sig)
 147{
 148        spin_lock_irq(&current->sighand->siglock);
 149        sig->audit_tty = current->signal->audit_tty;
 150        spin_unlock_irq(&current->sighand->siglock);
 151        sig->tty_audit_buf = NULL;
 152}
 153
 154/**
 155 *      tty_audit_tiocsti       -       Log TIOCSTI
 156 */
 157void tty_audit_tiocsti(struct tty_struct *tty, char ch)
 158{
 159        struct tty_audit_buf *buf;
 160        int major, minor, should_audit;
 161
 162        spin_lock_irq(&current->sighand->siglock);
 163        should_audit = current->signal->audit_tty;
 164        buf = current->signal->tty_audit_buf;
 165        if (buf)
 166                atomic_inc(&buf->count);
 167        spin_unlock_irq(&current->sighand->siglock);
 168
 169        major = tty->driver->major;
 170        minor = tty->driver->minor_start + tty->index;
 171        if (buf) {
 172                mutex_lock(&buf->mutex);
 173                if (buf->major == major && buf->minor == minor)
 174                        tty_audit_buf_push_current(buf);
 175                mutex_unlock(&buf->mutex);
 176                tty_audit_buf_put(buf);
 177        }
 178
 179        if (should_audit && audit_enabled) {
 180                uid_t auid;
 181                unsigned int sessionid;
 182
 183                auid = audit_get_loginuid(current);
 184                sessionid = audit_get_sessionid(current);
 185                tty_audit_log("ioctl=TIOCSTI", current, auid, sessionid, major,
 186                              minor, &ch, 1);
 187        }
 188}
 189
 190/**
 191 *      tty_audit_push_task     -       Flush task's pending audit data
 192 */
 193void tty_audit_push_task(struct task_struct *tsk, uid_t loginuid, u32 sessionid)
 194{
 195        struct tty_audit_buf *buf;
 196
 197        spin_lock_irq(&tsk->sighand->siglock);
 198        buf = tsk->signal->tty_audit_buf;
 199        if (buf)
 200                atomic_inc(&buf->count);
 201        spin_unlock_irq(&tsk->sighand->siglock);
 202        if (!buf)
 203                return;
 204
 205        mutex_lock(&buf->mutex);
 206        tty_audit_buf_push(tsk, loginuid, sessionid, buf);
 207        mutex_unlock(&buf->mutex);
 208
 209        tty_audit_buf_put(buf);
 210}
 211
 212/**
 213 *      tty_audit_buf_get       -       Get an audit buffer.
 214 *
 215 *      Get an audit buffer for @tty, allocate it if necessary.  Return %NULL
 216 *      if TTY auditing is disabled or out of memory.  Otherwise, return a new
 217 *      reference to the buffer.
 218 */
 219static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty)
 220{
 221        struct tty_audit_buf *buf, *buf2;
 222
 223        buf = NULL;
 224        buf2 = NULL;
 225        spin_lock_irq(&current->sighand->siglock);
 226        if (likely(!current->signal->audit_tty))
 227                goto out;
 228        buf = current->signal->tty_audit_buf;
 229        if (buf) {
 230                atomic_inc(&buf->count);
 231                goto out;
 232        }
 233        spin_unlock_irq(&current->sighand->siglock);
 234
 235        buf2 = tty_audit_buf_alloc(tty->driver->major,
 236                                   tty->driver->minor_start + tty->index,
 237                                   tty->icanon);
 238        if (buf2 == NULL) {
 239                audit_log_lost("out of memory in TTY auditing");
 240                return NULL;
 241        }
 242
 243        spin_lock_irq(&current->sighand->siglock);
 244        if (!current->signal->audit_tty)
 245                goto out;
 246        buf = current->signal->tty_audit_buf;
 247        if (!buf) {
 248                current->signal->tty_audit_buf = buf2;
 249                buf = buf2;
 250                buf2 = NULL;
 251        }
 252        atomic_inc(&buf->count);
 253        /* Fall through */
 254 out:
 255        spin_unlock_irq(&current->sighand->siglock);
 256        if (buf2)
 257                tty_audit_buf_free(buf2);
 258        return buf;
 259}
 260
 261/**
 262 *      tty_audit_add_data      -       Add data for TTY auditing.
 263 *
 264 *      Audit @data of @size from @tty, if necessary.
 265 */
 266void tty_audit_add_data(struct tty_struct *tty, unsigned char *data,
 267                        size_t size)
 268{
 269        struct tty_audit_buf *buf;
 270        int major, minor;
 271
 272        if (unlikely(size == 0))
 273                return;
 274
 275        if (tty->driver->type == TTY_DRIVER_TYPE_PTY
 276            && tty->driver->subtype == PTY_TYPE_MASTER)
 277                return;
 278
 279        buf = tty_audit_buf_get(tty);
 280        if (!buf)
 281                return;
 282
 283        mutex_lock(&buf->mutex);
 284        major = tty->driver->major;
 285        minor = tty->driver->minor_start + tty->index;
 286        if (buf->major != major || buf->minor != minor
 287            || buf->icanon != tty->icanon) {
 288                tty_audit_buf_push_current(buf);
 289                buf->major = major;
 290                buf->minor = minor;
 291                buf->icanon = tty->icanon;
 292        }
 293        do {
 294                size_t run;
 295
 296                run = N_TTY_BUF_SIZE - buf->valid;
 297                if (run > size)
 298                        run = size;
 299                memcpy(buf->data + buf->valid, data, run);
 300                buf->valid += run;
 301                data += run;
 302                size -= run;
 303                if (buf->valid == N_TTY_BUF_SIZE)
 304                        tty_audit_buf_push_current(buf);
 305        } while (size != 0);
 306        mutex_unlock(&buf->mutex);
 307        tty_audit_buf_put(buf);
 308}
 309
 310/**
 311 *      tty_audit_push  -       Push buffered data out
 312 *
 313 *      Make sure no audit data is pending for @tty on the current process.
 314 */
 315void tty_audit_push(struct tty_struct *tty)
 316{
 317        struct tty_audit_buf *buf;
 318
 319        spin_lock_irq(&current->sighand->siglock);
 320        if (likely(!current->signal->audit_tty)) {
 321                spin_unlock_irq(&current->sighand->siglock);
 322                return;
 323        }
 324        buf = current->signal->tty_audit_buf;
 325        if (buf)
 326                atomic_inc(&buf->count);
 327        spin_unlock_irq(&current->sighand->siglock);
 328
 329        if (buf) {
 330                int major, minor;
 331
 332                major = tty->driver->major;
 333                minor = tty->driver->minor_start + tty->index;
 334                mutex_lock(&buf->mutex);
 335                if (buf->major == major && buf->minor == minor)
 336                        tty_audit_buf_push_current(buf);
 337                mutex_unlock(&buf->mutex);
 338                tty_audit_buf_put(buf);
 339        }
 340}
 341