1522ed776SMiloslav Trmac /* 2522ed776SMiloslav Trmac * Creating audit events from TTY input. 3522ed776SMiloslav Trmac * 4522ed776SMiloslav Trmac * Copyright (C) 2007 Red Hat, Inc. All rights reserved. This copyrighted 5522ed776SMiloslav Trmac * material is made available to anyone wishing to use, modify, copy, or 6522ed776SMiloslav Trmac * redistribute it subject to the terms and conditions of the GNU General 7522ed776SMiloslav Trmac * Public License v.2. 8522ed776SMiloslav Trmac * 9522ed776SMiloslav Trmac * Authors: Miloslav Trmac <mitr@redhat.com> 10522ed776SMiloslav Trmac */ 11522ed776SMiloslav Trmac 12522ed776SMiloslav Trmac #include <linux/audit.h> 135a0e3ad6STejun Heo #include <linux/slab.h> 14522ed776SMiloslav Trmac #include <linux/tty.h> 15522ed776SMiloslav Trmac 16522ed776SMiloslav Trmac struct tty_audit_buf { 17522ed776SMiloslav Trmac atomic_t count; 18522ed776SMiloslav Trmac struct mutex mutex; /* Protects all data below */ 19522ed776SMiloslav Trmac int major, minor; /* The TTY which the data is from */ 20522ed776SMiloslav Trmac unsigned icanon:1; 21522ed776SMiloslav Trmac size_t valid; 22522ed776SMiloslav Trmac unsigned char *data; /* Allocated size N_TTY_BUF_SIZE */ 23522ed776SMiloslav Trmac }; 24522ed776SMiloslav Trmac 25522ed776SMiloslav Trmac static struct tty_audit_buf *tty_audit_buf_alloc(int major, int minor, 266c633f27SJiri Slaby unsigned icanon) 27522ed776SMiloslav Trmac { 28522ed776SMiloslav Trmac struct tty_audit_buf *buf; 29522ed776SMiloslav Trmac 30522ed776SMiloslav Trmac buf = kmalloc(sizeof(*buf), GFP_KERNEL); 31522ed776SMiloslav Trmac if (!buf) 32522ed776SMiloslav Trmac goto err; 33522ed776SMiloslav Trmac buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL); 34522ed776SMiloslav Trmac if (!buf->data) 35522ed776SMiloslav Trmac goto err_buf; 36522ed776SMiloslav Trmac atomic_set(&buf->count, 1); 37522ed776SMiloslav Trmac mutex_init(&buf->mutex); 38522ed776SMiloslav Trmac buf->major = major; 39522ed776SMiloslav Trmac buf->minor = minor; 40522ed776SMiloslav Trmac buf->icanon = icanon; 41522ed776SMiloslav Trmac buf->valid = 0; 42522ed776SMiloslav Trmac return buf; 43522ed776SMiloslav Trmac 44522ed776SMiloslav Trmac err_buf: 45522ed776SMiloslav Trmac kfree(buf); 46522ed776SMiloslav Trmac err: 47522ed776SMiloslav Trmac return NULL; 48522ed776SMiloslav Trmac } 49522ed776SMiloslav Trmac 50522ed776SMiloslav Trmac static void tty_audit_buf_free(struct tty_audit_buf *buf) 51522ed776SMiloslav Trmac { 52522ed776SMiloslav Trmac WARN_ON(buf->valid != 0); 53522ed776SMiloslav Trmac kfree(buf->data); 54522ed776SMiloslav Trmac kfree(buf); 55522ed776SMiloslav Trmac } 56522ed776SMiloslav Trmac 57522ed776SMiloslav Trmac static void tty_audit_buf_put(struct tty_audit_buf *buf) 58522ed776SMiloslav Trmac { 59522ed776SMiloslav Trmac if (atomic_dec_and_test(&buf->count)) 60522ed776SMiloslav Trmac tty_audit_buf_free(buf); 61522ed776SMiloslav Trmac } 62522ed776SMiloslav Trmac 63152f497bSEric Paris static void tty_audit_log(const char *description, int major, int minor, 64152f497bSEric Paris unsigned char *data, size_t size) 651e641743SAl Viro { 661e641743SAl Viro struct audit_buffer *ab; 67152f497bSEric Paris struct task_struct *tsk = current; 68f1dc4867SRichard Guy Briggs pid_t pid = task_pid_nr(tsk); 69152f497bSEric Paris uid_t uid = from_kuid(&init_user_ns, task_uid(tsk)); 70152f497bSEric Paris uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(tsk)); 714440e854SEric Paris unsigned int sessionid = audit_get_sessionid(tsk); 721e641743SAl Viro 731e641743SAl Viro ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY); 741e641743SAl Viro if (ab) { 751e641743SAl Viro char name[sizeof(tsk->comm)]; 761e641743SAl Viro 77152f497bSEric Paris audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d" 78f1dc4867SRichard Guy Briggs " minor=%d comm=", description, pid, uid, 79152f497bSEric Paris loginuid, sessionid, major, minor); 801e641743SAl Viro get_task_comm(name, tsk); 811e641743SAl Viro audit_log_untrustedstring(ab, name); 821e641743SAl Viro audit_log_format(ab, " data="); 831e641743SAl Viro audit_log_n_hex(ab, data, size); 841e641743SAl Viro audit_log_end(ab); 851e641743SAl Viro } 861e641743SAl Viro } 871e641743SAl Viro 88522ed776SMiloslav Trmac /** 89522ed776SMiloslav Trmac * tty_audit_buf_push - Push buffered data out 90522ed776SMiloslav Trmac * 91522ed776SMiloslav Trmac * Generate an audit message from the contents of @buf, which is owned by 92152f497bSEric Paris * the current task. @buf->mutex must be locked. 93522ed776SMiloslav Trmac */ 94152f497bSEric Paris static void tty_audit_buf_push(struct tty_audit_buf *buf) 95522ed776SMiloslav Trmac { 96522ed776SMiloslav Trmac if (buf->valid == 0) 97522ed776SMiloslav Trmac return; 9800bff392SXiaotian Feng if (audit_enabled == 0) { 9900bff392SXiaotian Feng buf->valid = 0; 100522ed776SMiloslav Trmac return; 10100bff392SXiaotian Feng } 102152f497bSEric Paris tty_audit_log("tty", buf->major, buf->minor, buf->data, buf->valid); 103522ed776SMiloslav Trmac buf->valid = 0; 104522ed776SMiloslav Trmac } 105522ed776SMiloslav Trmac 106522ed776SMiloslav Trmac /** 107522ed776SMiloslav Trmac * tty_audit_exit - Handle a task exit 108522ed776SMiloslav Trmac * 109522ed776SMiloslav Trmac * Make sure all buffered data is written out and deallocate the buffer. 110522ed776SMiloslav Trmac * Only needs to be called if current->signal->tty_audit_buf != %NULL. 111522ed776SMiloslav Trmac */ 112522ed776SMiloslav Trmac void tty_audit_exit(void) 113522ed776SMiloslav Trmac { 114522ed776SMiloslav Trmac struct tty_audit_buf *buf; 115522ed776SMiloslav Trmac 116522ed776SMiloslav Trmac buf = current->signal->tty_audit_buf; 117522ed776SMiloslav Trmac current->signal->tty_audit_buf = NULL; 118522ed776SMiloslav Trmac if (!buf) 119522ed776SMiloslav Trmac return; 120522ed776SMiloslav Trmac 121522ed776SMiloslav Trmac mutex_lock(&buf->mutex); 122152f497bSEric Paris tty_audit_buf_push(buf); 123522ed776SMiloslav Trmac mutex_unlock(&buf->mutex); 124522ed776SMiloslav Trmac 125522ed776SMiloslav Trmac tty_audit_buf_put(buf); 126522ed776SMiloslav Trmac } 127522ed776SMiloslav Trmac 128522ed776SMiloslav Trmac /** 129522ed776SMiloslav Trmac * tty_audit_fork - Copy TTY audit state for a new task 130522ed776SMiloslav Trmac * 131522ed776SMiloslav Trmac * Set up TTY audit state in @sig from current. @sig needs no locking. 132522ed776SMiloslav Trmac */ 133522ed776SMiloslav Trmac void tty_audit_fork(struct signal_struct *sig) 134522ed776SMiloslav Trmac { 135522ed776SMiloslav Trmac sig->audit_tty = current->signal->audit_tty; 13646e959eaSRichard Guy Briggs sig->audit_tty_log_passwd = current->signal->audit_tty_log_passwd; 137522ed776SMiloslav Trmac } 138522ed776SMiloslav Trmac 139522ed776SMiloslav Trmac /** 1401e641743SAl Viro * tty_audit_tiocsti - Log TIOCSTI 1411e641743SAl Viro */ 1421e641743SAl Viro void tty_audit_tiocsti(struct tty_struct *tty, char ch) 1431e641743SAl Viro { 1441e641743SAl Viro struct tty_audit_buf *buf; 1451e641743SAl Viro int major, minor, should_audit; 146bde02ca8SEric Paris unsigned long flags; 1471e641743SAl Viro 148bde02ca8SEric Paris spin_lock_irqsave(¤t->sighand->siglock, flags); 1491e641743SAl Viro should_audit = current->signal->audit_tty; 1501e641743SAl Viro buf = current->signal->tty_audit_buf; 1511e641743SAl Viro if (buf) 1521e641743SAl Viro atomic_inc(&buf->count); 153bde02ca8SEric Paris spin_unlock_irqrestore(¤t->sighand->siglock, flags); 1541e641743SAl Viro 1551e641743SAl Viro major = tty->driver->major; 1561e641743SAl Viro minor = tty->driver->minor_start + tty->index; 1571e641743SAl Viro if (buf) { 1581e641743SAl Viro mutex_lock(&buf->mutex); 1591e641743SAl Viro if (buf->major == major && buf->minor == minor) 160152f497bSEric Paris tty_audit_buf_push(buf); 1611e641743SAl Viro mutex_unlock(&buf->mutex); 1621e641743SAl Viro tty_audit_buf_put(buf); 1631e641743SAl Viro } 1641e641743SAl Viro 1651e641743SAl Viro if (should_audit && audit_enabled) { 166e1760bd5SEric W. Biederman kuid_t auid; 1671e641743SAl Viro unsigned int sessionid; 1681e641743SAl Viro 1691e641743SAl Viro auid = audit_get_loginuid(current); 1701e641743SAl Viro sessionid = audit_get_sessionid(current); 171152f497bSEric Paris tty_audit_log("ioctl=TIOCSTI", major, minor, &ch, 1); 1721e641743SAl Viro } 1731e641743SAl Viro } 1741e641743SAl Viro 1751e641743SAl Viro /** 176152f497bSEric Paris * tty_audit_push_current - Flush current's pending audit data 1773c80fe4aSThomas Gleixner * 178152f497bSEric Paris * Try to lock sighand and get a reference to the tty audit buffer if available. 1793c80fe4aSThomas Gleixner * Flush the buffer or return an appropriate error code. 180522ed776SMiloslav Trmac */ 181152f497bSEric Paris int tty_audit_push_current(void) 182522ed776SMiloslav Trmac { 1833c80fe4aSThomas Gleixner struct tty_audit_buf *buf = ERR_PTR(-EPERM); 184152f497bSEric Paris struct task_struct *tsk = current; 1853c80fe4aSThomas Gleixner unsigned long flags; 186522ed776SMiloslav Trmac 1873c80fe4aSThomas Gleixner if (!lock_task_sighand(tsk, &flags)) 1883c80fe4aSThomas Gleixner return -ESRCH; 1893c80fe4aSThomas Gleixner 1903c80fe4aSThomas Gleixner if (tsk->signal->audit_tty) { 191522ed776SMiloslav Trmac buf = tsk->signal->tty_audit_buf; 192522ed776SMiloslav Trmac if (buf) 193522ed776SMiloslav Trmac atomic_inc(&buf->count); 1943c80fe4aSThomas Gleixner } 1953c80fe4aSThomas Gleixner unlock_task_sighand(tsk, &flags); 1963c80fe4aSThomas Gleixner 1973c80fe4aSThomas Gleixner /* 1983c80fe4aSThomas Gleixner * Return 0 when signal->audit_tty set 1993c80fe4aSThomas Gleixner * but tsk->signal->tty_audit_buf == NULL. 2003c80fe4aSThomas Gleixner */ 2013c80fe4aSThomas Gleixner if (!buf || IS_ERR(buf)) 2023c80fe4aSThomas Gleixner return PTR_ERR(buf); 203522ed776SMiloslav Trmac 204522ed776SMiloslav Trmac mutex_lock(&buf->mutex); 205152f497bSEric Paris tty_audit_buf_push(buf); 206522ed776SMiloslav Trmac mutex_unlock(&buf->mutex); 207522ed776SMiloslav Trmac 208522ed776SMiloslav Trmac tty_audit_buf_put(buf); 2093c80fe4aSThomas Gleixner return 0; 210522ed776SMiloslav Trmac } 211522ed776SMiloslav Trmac 212522ed776SMiloslav Trmac /** 213522ed776SMiloslav Trmac * tty_audit_buf_get - Get an audit buffer. 214522ed776SMiloslav Trmac * 215522ed776SMiloslav Trmac * Get an audit buffer for @tty, allocate it if necessary. Return %NULL 216522ed776SMiloslav Trmac * if TTY auditing is disabled or out of memory. Otherwise, return a new 217522ed776SMiloslav Trmac * reference to the buffer. 218522ed776SMiloslav Trmac */ 2196c633f27SJiri Slaby static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, 2206c633f27SJiri Slaby unsigned icanon) 221522ed776SMiloslav Trmac { 222522ed776SMiloslav Trmac struct tty_audit_buf *buf, *buf2; 223bde02ca8SEric Paris unsigned long flags; 224522ed776SMiloslav Trmac 225522ed776SMiloslav Trmac buf = NULL; 226522ed776SMiloslav Trmac buf2 = NULL; 227bde02ca8SEric Paris spin_lock_irqsave(¤t->sighand->siglock, flags); 228522ed776SMiloslav Trmac if (likely(!current->signal->audit_tty)) 229522ed776SMiloslav Trmac goto out; 230522ed776SMiloslav Trmac buf = current->signal->tty_audit_buf; 231522ed776SMiloslav Trmac if (buf) { 232522ed776SMiloslav Trmac atomic_inc(&buf->count); 233522ed776SMiloslav Trmac goto out; 234522ed776SMiloslav Trmac } 235bde02ca8SEric Paris spin_unlock_irqrestore(¤t->sighand->siglock, flags); 236522ed776SMiloslav Trmac 237522ed776SMiloslav Trmac buf2 = tty_audit_buf_alloc(tty->driver->major, 238522ed776SMiloslav Trmac tty->driver->minor_start + tty->index, 2396c633f27SJiri Slaby icanon); 240522ed776SMiloslav Trmac if (buf2 == NULL) { 241522ed776SMiloslav Trmac audit_log_lost("out of memory in TTY auditing"); 242522ed776SMiloslav Trmac return NULL; 243522ed776SMiloslav Trmac } 244522ed776SMiloslav Trmac 245bde02ca8SEric Paris spin_lock_irqsave(¤t->sighand->siglock, flags); 246522ed776SMiloslav Trmac if (!current->signal->audit_tty) 247522ed776SMiloslav Trmac goto out; 248522ed776SMiloslav Trmac buf = current->signal->tty_audit_buf; 249522ed776SMiloslav Trmac if (!buf) { 250522ed776SMiloslav Trmac current->signal->tty_audit_buf = buf2; 251522ed776SMiloslav Trmac buf = buf2; 252522ed776SMiloslav Trmac buf2 = NULL; 253522ed776SMiloslav Trmac } 254522ed776SMiloslav Trmac atomic_inc(&buf->count); 255522ed776SMiloslav Trmac /* Fall through */ 256522ed776SMiloslav Trmac out: 257bde02ca8SEric Paris spin_unlock_irqrestore(¤t->sighand->siglock, flags); 258522ed776SMiloslav Trmac if (buf2) 259522ed776SMiloslav Trmac tty_audit_buf_free(buf2); 260522ed776SMiloslav Trmac return buf; 261522ed776SMiloslav Trmac } 262522ed776SMiloslav Trmac 263522ed776SMiloslav Trmac /** 264522ed776SMiloslav Trmac * tty_audit_add_data - Add data for TTY auditing. 265522ed776SMiloslav Trmac * 266522ed776SMiloslav Trmac * Audit @data of @size from @tty, if necessary. 267522ed776SMiloslav Trmac */ 268*6b2a3d62SPeter Hurley void tty_audit_add_data(struct tty_struct *tty, const void *data, 2696c633f27SJiri Slaby size_t size, unsigned icanon) 270522ed776SMiloslav Trmac { 271522ed776SMiloslav Trmac struct tty_audit_buf *buf; 272522ed776SMiloslav Trmac int major, minor; 27346e959eaSRichard Guy Briggs int audit_log_tty_passwd; 27446e959eaSRichard Guy Briggs unsigned long flags; 275522ed776SMiloslav Trmac 276522ed776SMiloslav Trmac if (unlikely(size == 0)) 277522ed776SMiloslav Trmac return; 278522ed776SMiloslav Trmac 27946e959eaSRichard Guy Briggs spin_lock_irqsave(¤t->sighand->siglock, flags); 28046e959eaSRichard Guy Briggs audit_log_tty_passwd = current->signal->audit_tty_log_passwd; 28146e959eaSRichard Guy Briggs spin_unlock_irqrestore(¤t->sighand->siglock, flags); 28246e959eaSRichard Guy Briggs if (!audit_log_tty_passwd && icanon && !L_ECHO(tty)) 28346e959eaSRichard Guy Briggs return; 28446e959eaSRichard Guy Briggs 28541126226SMiloslav Trmac if (tty->driver->type == TTY_DRIVER_TYPE_PTY 28641126226SMiloslav Trmac && tty->driver->subtype == PTY_TYPE_MASTER) 28741126226SMiloslav Trmac return; 28841126226SMiloslav Trmac 2896c633f27SJiri Slaby buf = tty_audit_buf_get(tty, icanon); 290522ed776SMiloslav Trmac if (!buf) 291522ed776SMiloslav Trmac return; 292522ed776SMiloslav Trmac 293522ed776SMiloslav Trmac mutex_lock(&buf->mutex); 294522ed776SMiloslav Trmac major = tty->driver->major; 295522ed776SMiloslav Trmac minor = tty->driver->minor_start + tty->index; 296522ed776SMiloslav Trmac if (buf->major != major || buf->minor != minor 2976c633f27SJiri Slaby || buf->icanon != icanon) { 298152f497bSEric Paris tty_audit_buf_push(buf); 299522ed776SMiloslav Trmac buf->major = major; 300522ed776SMiloslav Trmac buf->minor = minor; 3016c633f27SJiri Slaby buf->icanon = icanon; 302522ed776SMiloslav Trmac } 303522ed776SMiloslav Trmac do { 304522ed776SMiloslav Trmac size_t run; 305522ed776SMiloslav Trmac 306522ed776SMiloslav Trmac run = N_TTY_BUF_SIZE - buf->valid; 307522ed776SMiloslav Trmac if (run > size) 308522ed776SMiloslav Trmac run = size; 309522ed776SMiloslav Trmac memcpy(buf->data + buf->valid, data, run); 310522ed776SMiloslav Trmac buf->valid += run; 311522ed776SMiloslav Trmac data += run; 312522ed776SMiloslav Trmac size -= run; 313522ed776SMiloslav Trmac if (buf->valid == N_TTY_BUF_SIZE) 314152f497bSEric Paris tty_audit_buf_push(buf); 315522ed776SMiloslav Trmac } while (size != 0); 316522ed776SMiloslav Trmac mutex_unlock(&buf->mutex); 317522ed776SMiloslav Trmac tty_audit_buf_put(buf); 318522ed776SMiloslav Trmac } 319522ed776SMiloslav Trmac 320522ed776SMiloslav Trmac /** 321522ed776SMiloslav Trmac * tty_audit_push - Push buffered data out 322522ed776SMiloslav Trmac * 323522ed776SMiloslav Trmac * Make sure no audit data is pending for @tty on the current process. 324522ed776SMiloslav Trmac */ 325522ed776SMiloslav Trmac void tty_audit_push(struct tty_struct *tty) 326522ed776SMiloslav Trmac { 327522ed776SMiloslav Trmac struct tty_audit_buf *buf; 328bde02ca8SEric Paris unsigned long flags; 329522ed776SMiloslav Trmac 330bde02ca8SEric Paris spin_lock_irqsave(¤t->sighand->siglock, flags); 331522ed776SMiloslav Trmac if (likely(!current->signal->audit_tty)) { 332bde02ca8SEric Paris spin_unlock_irqrestore(¤t->sighand->siglock, flags); 333522ed776SMiloslav Trmac return; 334522ed776SMiloslav Trmac } 335522ed776SMiloslav Trmac buf = current->signal->tty_audit_buf; 336522ed776SMiloslav Trmac if (buf) 337522ed776SMiloslav Trmac atomic_inc(&buf->count); 338bde02ca8SEric Paris spin_unlock_irqrestore(¤t->sighand->siglock, flags); 339522ed776SMiloslav Trmac 340522ed776SMiloslav Trmac if (buf) { 341522ed776SMiloslav Trmac int major, minor; 342522ed776SMiloslav Trmac 343522ed776SMiloslav Trmac major = tty->driver->major; 344522ed776SMiloslav Trmac minor = tty->driver->minor_start + tty->index; 345522ed776SMiloslav Trmac mutex_lock(&buf->mutex); 346522ed776SMiloslav Trmac if (buf->major == major && buf->minor == minor) 347152f497bSEric Paris tty_audit_buf_push(buf); 348522ed776SMiloslav Trmac mutex_unlock(&buf->mutex); 349522ed776SMiloslav Trmac tty_audit_buf_put(buf); 350522ed776SMiloslav Trmac } 351522ed776SMiloslav Trmac } 352