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        atomic_t count;
  18        struct mutex mutex;     /* Protects all data below */
  19        int major, minor;       /* The TTY which the data is from */
  20        unsigned icanon:1;
  21        size_t valid;
  22        unsigned char *data;    /* Allocated size N_TTY_BUF_SIZE */
  23};
  24
  25static struct tty_audit_buf *tty_audit_buf_alloc(int major, int minor,
  26                                                 unsigned icanon)
  27{
  28        struct tty_audit_buf *buf;
  29
  30        buf = kmalloc(sizeof(*buf), GFP_KERNEL);
  31        if (!buf)
  32                goto err;
  33        buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
  34        if (!buf->data)
  35                goto err_buf;
  36        atomic_set(&buf->count, 1);
  37        mutex_init(&buf->mutex);
  38        buf->major = major;
  39        buf->minor = minor;
  40        buf->icanon = icanon;
  41        buf->valid = 0;
  42        return buf;
  43
  44err_buf:
  45        kfree(buf);
  46err:
  47        return NULL;
  48}
  49
  50static void tty_audit_buf_free(struct tty_audit_buf *buf)
  51{
  52        WARN_ON(buf->valid != 0);
  53        kfree(buf->data);
  54        kfree(buf);
  55}
  56
  57static void tty_audit_buf_put(struct tty_audit_buf *buf)
  58{
  59        if (atomic_dec_and_test(&buf->count))
  60                tty_audit_buf_free(buf);
  61}
  62
  63static void tty_audit_log(const char *description, int major, int minor,
  64                          unsigned char *data, size_t size)
  65{
  66        struct audit_buffer *ab;
  67        struct task_struct *tsk = current;
  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        u32 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, tsk->pid, uid,
  78                                 loginuid, sessionid, major, minor);
  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->major, buf->minor, 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 */
 111void tty_audit_exit(void)
 112{
 113        struct tty_audit_buf *buf;
 114
 115        buf = current->signal->tty_audit_buf;
 116        current->signal->tty_audit_buf = NULL;
 117        if (!buf)
 118                return;
 119
 120        mutex_lock(&buf->mutex);
 121        tty_audit_buf_push(buf);
 122        mutex_unlock(&buf->mutex);
 123
 124        tty_audit_buf_put(buf);
 125}
 126
 127/**
 128 *      tty_audit_fork  -       Copy TTY audit state for a new task
 129 *
 130 *      Set up TTY audit state in @sig from current.  @sig needs no locking.
 131 */
 132void tty_audit_fork(struct signal_struct *sig)
 133{
 134        sig->audit_tty = current->signal->audit_tty;
 135        sig->audit_tty_log_passwd = current->signal->audit_tty_log_passwd;
 136}
 137
 138/**
 139 *      tty_audit_tiocsti       -       Log TIOCSTI
 140 */
 141void tty_audit_tiocsti(struct tty_struct *tty, char ch)
 142{
 143        struct tty_audit_buf *buf;
 144        int major, minor, should_audit;
 145        unsigned long flags;
 146
 147        spin_lock_irqsave(&current->sighand->siglock, flags);
 148        should_audit = current->signal->audit_tty;
 149        buf = current->signal->tty_audit_buf;
 150        if (buf)
 151                atomic_inc(&buf->count);
 152        spin_unlock_irqrestore(&current->sighand->siglock, flags);
 153
 154        major = tty->driver->major;
 155        minor = tty->driver->minor_start + tty->index;
 156        if (buf) {
 157                mutex_lock(&buf->mutex);
 158                if (buf->major == major && buf->minor == minor)
 159                        tty_audit_buf_push(buf);
 160                mutex_unlock(&buf->mutex);
 161                tty_audit_buf_put(buf);
 162        }
 163
 164        if (should_audit && audit_enabled) {
 165                kuid_t auid;
 166                unsigned int sessionid;
 167
 168                auid = audit_get_loginuid(current);
 169                sessionid = audit_get_sessionid(current);
 170                tty_audit_log("ioctl=TIOCSTI", major, minor, &ch, 1);
 171        }
 172}
 173
 174/**
 175 * tty_audit_push_current -     Flush current's pending audit data
 176 *
 177 * Try to lock sighand and get a reference to the tty audit buffer if available.
 178 * Flush the buffer or return an appropriate error code.
 179 */
 180int tty_audit_push_current(void)
 181{
 182        struct tty_audit_buf *buf = ERR_PTR(-EPERM);
 183        struct task_struct *tsk = current;
 184        unsigned long flags;
 185
 186        if (!lock_task_sighand(tsk, &flags))
 187                return -ESRCH;
 188
 189        if (tsk->signal->audit_tty) {
 190                buf = tsk->signal->tty_audit_buf;
 191                if (buf)
 192                        atomic_inc(&buf->count);
 193        }
 194        unlock_task_sighand(tsk, &flags);
 195
 196        /*
 197         * Return 0 when signal->audit_tty set
 198         * but tsk->signal->tty_audit_buf == NULL.
 199         */
 200        if (!buf || IS_ERR(buf))
 201                return PTR_ERR(buf);
 202
 203        mutex_lock(&buf->mutex);
 204        tty_audit_buf_push(buf);
 205        mutex_unlock(&buf->mutex);
 206
 207        tty_audit_buf_put(buf);
 208        return 0;
 209}
 210
 211/**
 212 *      tty_audit_buf_get       -       Get an audit buffer.
 213 *
 214 *      Get an audit buffer for @tty, allocate it if necessary.  Return %NULL
 215 *      if TTY auditing is disabled or out of memory.  Otherwise, return a new
 216 *      reference to the buffer.
 217 */
 218static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty,
 219                unsigned icanon)
 220{
 221        struct tty_audit_buf *buf, *buf2;
 222        unsigned long flags;
 223
 224        buf = NULL;
 225        buf2 = NULL;
 226        spin_lock_irqsave(&current->sighand->siglock, flags);
 227        if (likely(!current->signal->audit_tty))
 228                goto out;
 229        buf = current->signal->tty_audit_buf;
 230        if (buf) {
 231                atomic_inc(&buf->count);
 232                goto out;
 233        }
 234        spin_unlock_irqrestore(&current->sighand->siglock, flags);
 235
 236        buf2 = tty_audit_buf_alloc(tty->driver->major,
 237                                   tty->driver->minor_start + tty->index,
 238                                   icanon);
 239        if (buf2 == NULL) {
 240                audit_log_lost("out of memory in TTY auditing");
 241                return NULL;
 242        }
 243
 244        spin_lock_irqsave(&current->sighand->siglock, flags);
 245        if (!current->signal->audit_tty)
 246                goto out;
 247        buf = current->signal->tty_audit_buf;
 248        if (!buf) {
 249                current->signal->tty_audit_buf = buf2;
 250                buf = buf2;
 251                buf2 = NULL;
 252        }
 253        atomic_inc(&buf->count);
 254        /* Fall through */
 255 out:
 256        spin_unlock_irqrestore(&current->sighand->siglock, flags);
 257        if (buf2)
 258                tty_audit_buf_free(buf2);
 259        return buf;
 260}
 261
 262/**
 263 *      tty_audit_add_data      -       Add data for TTY auditing.
 264 *
 265 *      Audit @data of @size from @tty, if necessary.
 266 */
 267void tty_audit_add_data(struct tty_struct *tty, unsigned char *data,
 268                        size_t size, unsigned icanon)
 269{
 270        struct tty_audit_buf *buf;
 271        int major, minor;
 272        int audit_log_tty_passwd;
 273        unsigned long flags;
 274
 275        if (unlikely(size == 0))
 276                return;
 277
 278        spin_lock_irqsave(&current->sighand->siglock, flags);
 279        audit_log_tty_passwd = current->signal->audit_tty_log_passwd;
 280        spin_unlock_irqrestore(&current->sighand->siglock, flags);
 281        if (!audit_log_tty_passwd && icanon && !L_ECHO(tty))
 282                return;
 283
 284        if (tty->driver->type == TTY_DRIVER_TYPE_PTY
 285            && tty->driver->subtype == PTY_TYPE_MASTER)
 286                return;
 287
 288        buf = tty_audit_buf_get(tty, icanon);
 289        if (!buf)
 290                return;
 291
 292        mutex_lock(&buf->mutex);
 293        major = tty->driver->major;
 294        minor = tty->driver->minor_start + tty->index;
 295        if (buf->major != major || buf->minor != minor
 296            || buf->icanon != icanon) {
 297                tty_audit_buf_push(buf);
 298                buf->major = major;
 299                buf->minor = minor;
 300                buf->icanon = icanon;
 301        }
 302        do {
 303                size_t run;
 304
 305                run = N_TTY_BUF_SIZE - buf->valid;
 306                if (run > size)
 307                        run = size;
 308                memcpy(buf->data + buf->valid, data, run);
 309                buf->valid += run;
 310                data += run;
 311                size -= run;
 312                if (buf->valid == N_TTY_BUF_SIZE)
 313                        tty_audit_buf_push(buf);
 314        } while (size != 0);
 315        mutex_unlock(&buf->mutex);
 316        tty_audit_buf_put(buf);
 317}
 318
 319/**
 320 *      tty_audit_push  -       Push buffered data out
 321 *
 322 *      Make sure no audit data is pending for @tty on the current process.
 323 */
 324void tty_audit_push(struct tty_struct *tty)
 325{
 326        struct tty_audit_buf *buf;
 327        unsigned long flags;
 328
 329        spin_lock_irqsave(&current->sighand->siglock, flags);
 330        if (likely(!current->signal->audit_tty)) {
 331                spin_unlock_irqrestore(&current->sighand->siglock, flags);
 332                return;
 333        }
 334        buf = current->signal->tty_audit_buf;
 335        if (buf)
 336                atomic_inc(&buf->count);
 337        spin_unlock_irqrestore(&current->sighand->siglock, flags);
 338
 339        if (buf) {
 340                int major, minor;
 341
 342                major = tty->driver->major;
 343                minor = tty->driver->minor_start + tty->index;
 344                mutex_lock(&buf->mutex);
 345                if (buf->major == major && buf->minor == minor)
 346                        tty_audit_buf_push(buf);
 347                mutex_unlock(&buf->mutex);
 348                tty_audit_buf_put(buf);
 349        }
 350}
 351
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.