xref: /linux/drivers/tty/tty_audit.c (revision fc284d631894d8673d229fad92762b66c9875cab)
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(&current->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(&current->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(&current->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(&current->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(&current->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(&current->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(&current->sighand->siglock, flags);
28046e959eaSRichard Guy Briggs 	audit_log_tty_passwd = current->signal->audit_tty_log_passwd;
28146e959eaSRichard Guy Briggs 	spin_unlock_irqrestore(&current->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(&current->sighand->siglock, flags);
331522ed776SMiloslav Trmac 	if (likely(!current->signal->audit_tty)) {
332bde02ca8SEric Paris 		spin_unlock_irqrestore(&current->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(&current->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