aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorMiloslav Trmac <mitr@redhat.com>2007-07-16 02:40:56 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-16 12:05:47 -0400
commit522ed7767e800cff6c650ec64b0ee0677303119c (patch)
treef65ecb29f2cf885018d3557f840de3ef4be6ec64 /drivers/char
parent4f27c00bf80f122513d3a5be16ed851573164534 (diff)
Audit: add TTY input auditing
Add TTY input auditing, used to audit system administrator's actions. This is required by various security standards such as DCID 6/3 and PCI to provide non-repudiation of administrator's actions and to allow a review of past actions if the administrator seems to overstep their duties or if the system becomes misconfigured for unknown reasons. These requirements do not make it necessary to audit TTY output as well. Compared to an user-space keylogger, this approach records TTY input using the audit subsystem, correlated with other audit events, and it is completely transparent to the user-space application (e.g. the console ioctls still work). TTY input auditing works on a higher level than auditing all system calls within the session, which would produce an overwhelming amount of mostly useless audit events. Add an "audit_tty" attribute, inherited across fork (). Data read from TTYs by process with the attribute is sent to the audit subsystem by the kernel. The audit netlink interface is extended to allow modifying the audit_tty attribute, and to allow sending explanatory audit events from user-space (for example, a shell might send an event containing the final command, after the interactive command-line editing and history expansion is performed, which might be difficult to decipher from the TTY input alone). Because the "audit_tty" attribute is inherited across fork (), it would be set e.g. for sshd restarted within an audited session. To prevent this, the audit_tty attribute is cleared when a process with no open TTY file descriptors (e.g. after daemon startup) opens a TTY. See https://www.redhat.com/archives/linux-audit/2007-June/msg00000.html for a more detailed rationale document for an older version of this patch. [akpm@linux-foundation.org: build fix] Signed-off-by: Miloslav Trmac <mitr@redhat.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Cc: Paul Fulghum <paulkf@microgate.com> Cc: Casey Schaufler <casey@schaufler-ca.com> Cc: Steve Grubb <sgrubb@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/char')
-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;