aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/n_tty.c20
-rw-r--r--drivers/char/tty_audit.c345
-rw-r--r--drivers/char/tty_io.c14
4 files changed, 375 insertions, 5 deletions
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 2f56ecc035aa..f2996a95eb07 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -15,6 +15,7 @@ obj-y += misc.o
15obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o consolemap.o \ 15obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o consolemap.o \
16 consolemap_deftbl.o selection.o keyboard.o 16 consolemap_deftbl.o selection.o keyboard.o
17obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o 17obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o
18obj-$(CONFIG_AUDIT) += tty_audit.o
18obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o 19obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o
19obj-$(CONFIG_ESPSERIAL) += esp.o 20obj-$(CONFIG_ESPSERIAL) += esp.o
20obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o 21obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o
diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index 371631f4bfb9..038056911934 100644
--- a/drivers/char/n_tty.c
+++ b/drivers/char/n_tty.c
@@ -45,6 +45,8 @@
45#include <linux/slab.h> 45#include <linux/slab.h>
46#include <linux/poll.h> 46#include <linux/poll.h>
47#include <linux/bitops.h> 47#include <linux/bitops.h>
48#include <linux/audit.h>
49#include <linux/file.h>
48 50
49#include <asm/uaccess.h> 51#include <asm/uaccess.h>
50#include <asm/system.h> 52#include <asm/system.h>
@@ -78,6 +80,13 @@ static inline void free_buf(unsigned char *buf)
78 free_page((unsigned long) buf); 80 free_page((unsigned long) buf);
79} 81}
80 82
83static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
84 unsigned char __user *ptr)
85{
86 tty_audit_add_data(tty, &x, 1);
87 return put_user(x, ptr);
88}
89
81/** 90/**
82 * n_tty_set__room - receive space 91 * n_tty_set__room - receive space
83 * @tty: terminal 92 * @tty: terminal
@@ -1153,6 +1162,7 @@ static int copy_from_read_buf(struct tty_struct *tty,
1153 if (n) { 1162 if (n) {
1154 retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n); 1163 retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n);
1155 n -= retval; 1164 n -= retval;
1165 tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n);
1156 spin_lock_irqsave(&tty->read_lock, flags); 1166 spin_lock_irqsave(&tty->read_lock, flags);
1157 tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1); 1167 tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
1158 tty->read_cnt -= n; 1168 tty->read_cnt -= n;
@@ -1279,7 +1289,7 @@ do_it_again:
1279 break; 1289 break;
1280 cs = tty->link->ctrl_status; 1290 cs = tty->link->ctrl_status;
1281 tty->link->ctrl_status = 0; 1291 tty->link->ctrl_status = 0;
1282 if (put_user(cs, b++)) { 1292 if (tty_put_user(tty, cs, b++)) {
1283 retval = -EFAULT; 1293 retval = -EFAULT;
1284 b--; 1294 b--;
1285 break; 1295 break;
@@ -1321,7 +1331,7 @@ do_it_again:
1321 1331
1322 /* Deal with packet mode. */ 1332 /* Deal with packet mode. */
1323 if (tty->packet && b == buf) { 1333 if (tty->packet && b == buf) {
1324 if (put_user(TIOCPKT_DATA, b++)) { 1334 if (tty_put_user(tty, TIOCPKT_DATA, b++)) {
1325 retval = -EFAULT; 1335 retval = -EFAULT;
1326 b--; 1336 b--;
1327 break; 1337 break;
@@ -1352,15 +1362,17 @@ do_it_again:
1352 spin_unlock_irqrestore(&tty->read_lock, flags); 1362 spin_unlock_irqrestore(&tty->read_lock, flags);
1353 1363
1354 if (!eol || (c != __DISABLED_CHAR)) { 1364 if (!eol || (c != __DISABLED_CHAR)) {
1355 if (put_user(c, b++)) { 1365 if (tty_put_user(tty, c, b++)) {
1356 retval = -EFAULT; 1366 retval = -EFAULT;
1357 b--; 1367 b--;
1358 break; 1368 break;
1359 } 1369 }
1360 nr--; 1370 nr--;
1361 } 1371 }
1362 if (eol) 1372 if (eol) {
1373 tty_audit_push(tty);
1363 break; 1374 break;
1375 }
1364 } 1376 }
1365 if (retval) 1377 if (retval)
1366 break; 1378 break;
diff --git a/drivers/char/tty_audit.c b/drivers/char/tty_audit.c
new file mode 100644
index 000000000000..d222012c1b0c
--- /dev/null
+++ b/drivers/char/tty_audit.c
@@ -0,0 +1,345 @@
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/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 int icanon)
27{
28 struct tty_audit_buf *buf;
29
30 buf = kmalloc(sizeof (*buf), GFP_KERNEL);
31 if (!buf)
32 goto err;
33 if (PAGE_SIZE != N_TTY_BUF_SIZE)
34 buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
35 else
36 buf->data = (unsigned char *)__get_free_page(GFP_KERNEL);
37 if (!buf->data)
38 goto err_buf;
39 atomic_set(&buf->count, 1);
40 mutex_init(&buf->mutex);
41 buf->major = major;
42 buf->minor = minor;
43 buf->icanon = icanon;
44 buf->valid = 0;
45 return buf;
46
47err_buf:
48 kfree(buf);
49err:
50 return NULL;
51}
52
53static void tty_audit_buf_free(struct tty_audit_buf *buf)
54{
55 WARN_ON(buf->valid != 0);
56 if (PAGE_SIZE != N_TTY_BUF_SIZE)
57 kfree(buf->data);
58 else
59 free_page((unsigned long)buf->data);
60 kfree(buf);
61}
62
63static void tty_audit_buf_put(struct tty_audit_buf *buf)
64{
65 if (atomic_dec_and_test(&buf->count))
66 tty_audit_buf_free(buf);
67}
68
69/**
70 * tty_audit_buf_push - Push buffered data out
71 *
72 * Generate an audit message from the contents of @buf, which is owned by
73 * @tsk with @loginuid. @buf->mutex must be locked.
74 */
75static void tty_audit_buf_push(struct task_struct *tsk, uid_t loginuid,
76 struct tty_audit_buf *buf)
77{
78 struct audit_buffer *ab;
79
80 if (buf->valid == 0)
81 return;
82 if (audit_enabled == 0)
83 return;
84 ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY);
85 if (ab) {
86 char name[sizeof(tsk->comm)];
87
88 audit_log_format(ab, "tty pid=%u uid=%u auid=%u major=%d "
89 "minor=%d comm=", tsk->pid, tsk->uid,
90 loginuid, buf->major, buf->minor);
91 get_task_comm(name, tsk);
92 audit_log_untrustedstring(ab, name);
93 audit_log_format(ab, " data=");
94 audit_log_n_untrustedstring(ab, buf->valid, buf->data);
95 audit_log_end(ab);
96 }
97 buf->valid = 0;
98}
99
100/**
101 * tty_audit_buf_push_current - Push buffered data out
102 *
103 * Generate an audit message from the contents of @buf, which is owned by
104 * the current task. @buf->mutex must be locked.
105 */
106static void tty_audit_buf_push_current(struct tty_audit_buf *buf)
107{
108 tty_audit_buf_push(current, audit_get_loginuid(current->audit_context),
109 buf);
110}
111
112/**
113 * tty_audit_exit - Handle a task exit
114 *
115 * Make sure all buffered data is written out and deallocate the buffer.
116 * Only needs to be called if current->signal->tty_audit_buf != %NULL.
117 */
118void tty_audit_exit(void)
119{
120 struct tty_audit_buf *buf;
121
122 spin_lock_irq(&current->sighand->siglock);
123 buf = current->signal->tty_audit_buf;
124 current->signal->tty_audit_buf = NULL;
125 spin_unlock_irq(&current->sighand->siglock);
126 if (!buf)
127 return;
128
129 mutex_lock(&buf->mutex);
130 tty_audit_buf_push_current(buf);
131 mutex_unlock(&buf->mutex);
132
133 tty_audit_buf_put(buf);
134}
135
136/**
137 * tty_audit_fork - Copy TTY audit state for a new task
138 *
139 * Set up TTY audit state in @sig from current. @sig needs no locking.
140 */
141void tty_audit_fork(struct signal_struct *sig)
142{
143 spin_lock_irq(&current->sighand->siglock);
144 sig->audit_tty = current->signal->audit_tty;
145 spin_unlock_irq(&current->sighand->siglock);
146 sig->tty_audit_buf = NULL;
147}
148
149/**
150 * tty_audit_push_task - Flush task's pending audit data
151 */
152void tty_audit_push_task(struct task_struct *tsk, uid_t loginuid)
153{
154 struct tty_audit_buf *buf;
155
156 spin_lock_irq(&tsk->sighand->siglock);
157 buf = tsk->signal->tty_audit_buf;
158 if (buf)
159 atomic_inc(&buf->count);
160 spin_unlock_irq(&tsk->sighand->siglock);
161 if (!buf)
162 return;
163
164 mutex_lock(&buf->mutex);
165 tty_audit_buf_push(tsk, loginuid, buf);
166 mutex_unlock(&buf->mutex);
167
168 tty_audit_buf_put(buf);
169}
170
171/**
172 * tty_audit_buf_get - Get an audit buffer.
173 *
174 * Get an audit buffer for @tty, allocate it if necessary. Return %NULL
175 * if TTY auditing is disabled or out of memory. Otherwise, return a new
176 * reference to the buffer.
177 */
178static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty)
179{
180 struct tty_audit_buf *buf, *buf2;
181
182 buf = NULL;
183 buf2 = NULL;
184 spin_lock_irq(&current->sighand->siglock);
185 if (likely(!current->signal->audit_tty))
186 goto out;
187 buf = current->signal->tty_audit_buf;
188 if (buf) {
189 atomic_inc(&buf->count);
190 goto out;
191 }
192 spin_unlock_irq(&current->sighand->siglock);
193
194 buf2 = tty_audit_buf_alloc(tty->driver->major,
195 tty->driver->minor_start + tty->index,
196 tty->icanon);
197 if (buf2 == NULL) {
198 audit_log_lost("out of memory in TTY auditing");
199 return NULL;
200 }
201
202 spin_lock_irq(&current->sighand->siglock);
203 if (!current->signal->audit_tty)
204 goto out;
205 buf = current->signal->tty_audit_buf;
206 if (!buf) {
207 current->signal->tty_audit_buf = buf2;
208 buf = buf2;
209 buf2 = NULL;
210 }
211 atomic_inc(&buf->count);
212 /* Fall through */
213 out:
214 spin_unlock_irq(&current->sighand->siglock);
215 if (buf2)
216 tty_audit_buf_free(buf2);
217 return buf;
218}
219
220/**
221 * tty_audit_add_data - Add data for TTY auditing.
222 *
223 * Audit @data of @size from @tty, if necessary.
224 */
225void tty_audit_add_data(struct tty_struct *tty, unsigned char *data,
226 size_t size)
227{
228 struct tty_audit_buf *buf;
229 int major, minor;
230
231 if (unlikely(size == 0))
232 return;
233
234 buf = tty_audit_buf_get(tty);
235 if (!buf)
236 return;
237
238 mutex_lock(&buf->mutex);
239 major = tty->driver->major;
240 minor = tty->driver->minor_start + tty->index;
241 if (buf->major != major || buf->minor != minor
242 || buf->icanon != tty->icanon) {
243 tty_audit_buf_push_current(buf);
244 buf->major = major;
245 buf->minor = minor;
246 buf->icanon = tty->icanon;
247 }
248 do {
249 size_t run;
250
251 run = N_TTY_BUF_SIZE - buf->valid;
252 if (run > size)
253 run = size;
254 memcpy(buf->data + buf->valid, data, run);
255 buf->valid += run;
256 data += run;
257 size -= run;
258 if (buf->valid == N_TTY_BUF_SIZE)
259 tty_audit_buf_push_current(buf);
260 } while (size != 0);
261 mutex_unlock(&buf->mutex);
262 tty_audit_buf_put(buf);
263}
264
265/**
266 * tty_audit_push - Push buffered data out
267 *
268 * Make sure no audit data is pending for @tty on the current process.
269 */
270void tty_audit_push(struct tty_struct *tty)
271{
272 struct tty_audit_buf *buf;
273
274 spin_lock_irq(&current->sighand->siglock);
275 if (likely(!current->signal->audit_tty)) {
276 spin_unlock_irq(&current->sighand->siglock);
277 return;
278 }
279 buf = current->signal->tty_audit_buf;
280 if (buf)
281 atomic_inc(&buf->count);
282 spin_unlock_irq(&current->sighand->siglock);
283
284 if (buf) {
285 int major, minor;
286
287 major = tty->driver->major;
288 minor = tty->driver->minor_start + tty->index;
289 mutex_lock(&buf->mutex);
290 if (buf->major == major && buf->minor == minor)
291 tty_audit_buf_push_current(buf);
292 mutex_unlock(&buf->mutex);
293 tty_audit_buf_put(buf);
294 }
295}
296
297/**
298 * tty_audit_opening - A TTY is being opened.
299 *
300 * As a special hack, tasks that close all their TTYs and open new ones
301 * are assumed to be system daemons (e.g. getty) and auditing is
302 * automatically disabled for them.
303 */
304void tty_audit_opening(void)
305{
306 int disable;
307
308 disable = 1;
309 spin_lock_irq(&current->sighand->siglock);
310 if (current->signal->audit_tty == 0)
311 disable = 0;
312 spin_unlock_irq(&current->sighand->siglock);
313 if (!disable)
314 return;
315
316 task_lock(current);
317 if (current->files) {
318 struct fdtable *fdt;
319 unsigned i;
320
321 /*
322 * We don't take a ref to the file, so we must hold ->file_lock
323 * instead.
324 */
325 spin_lock(&current->files->file_lock);
326 fdt = files_fdtable(current->files);
327 for (i = 0; i < fdt->max_fds; i++) {
328 struct file *filp;
329
330 filp = fcheck_files(current->files, i);
331 if (filp && is_tty(filp)) {
332 disable = 0;
333 break;
334 }
335 }
336 spin_unlock(&current->files->file_lock);
337 }
338 task_unlock(current);
339 if (!disable)
340 return;
341
342 spin_lock_irq(&current->sighand->siglock);
343 current->signal->audit_tty = 0;
344 spin_unlock_irq(&current->sighand->siglock);
345}
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index fde69e589ca7..de37ebc3a4cf 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -1503,6 +1503,15 @@ int tty_hung_up_p(struct file * filp)
1503 1503
1504EXPORT_SYMBOL(tty_hung_up_p); 1504EXPORT_SYMBOL(tty_hung_up_p);
1505 1505
1506/**
1507 * is_tty - checker whether file is a TTY
1508 */
1509int is_tty(struct file *filp)
1510{
1511 return filp->f_op->read == tty_read
1512 || filp->f_op->read == hung_up_tty_read;
1513}
1514
1506static void session_clear_tty(struct pid *session) 1515static void session_clear_tty(struct pid *session)
1507{ 1516{
1508 struct task_struct *p; 1517 struct task_struct *p;
@@ -2673,6 +2682,7 @@ got_driver:
2673 __proc_set_tty(current, tty); 2682 __proc_set_tty(current, tty);
2674 spin_unlock_irq(&current->sighand->siglock); 2683 spin_unlock_irq(&current->sighand->siglock);
2675 mutex_unlock(&tty_mutex); 2684 mutex_unlock(&tty_mutex);
2685 tty_audit_opening();
2676 return 0; 2686 return 0;
2677} 2687}
2678 2688
@@ -2735,8 +2745,10 @@ static int ptmx_open(struct inode * inode, struct file * filp)
2735 2745
2736 check_tty_count(tty, "tty_open"); 2746 check_tty_count(tty, "tty_open");
2737 retval = ptm_driver->open(tty, filp); 2747 retval = ptm_driver->open(tty, filp);
2738 if (!retval) 2748 if (!retval) {
2749 tty_audit_opening();
2739 return 0; 2750 return 0;
2751 }
2740out1: 2752out1:
2741 release_dev(filp); 2753 release_dev(filp);
2742 return retval; 2754 return retval;