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