aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/tty_audit.c
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2010-11-04 14:10:29 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2010-11-05 11:10:33 -0400
commit96fd7ce58ffb5c7bf376796b5525ba3ea1c9d69f (patch)
treeaca24a6c1c0e506d5fa7b0266c4c1866786607ae /drivers/tty/tty_audit.c
parentc8ddb2713c624f432fa5fe3c7ecffcdda46ea0d4 (diff)
TTY: create drivers/tty and move the tty core files there
The tty code should be in its own subdirectory and not in the char driver with all of the cruft that is currently there. Based on work done by Arnd Bergmann <arnd@arndb.de> Acked-by: Arnd Bergmann <arnd@arndb.de> Cc: Jiri Slaby <jslaby@suse.cz> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/tty/tty_audit.c')
-rw-r--r--drivers/tty/tty_audit.c358
1 files changed, 358 insertions, 0 deletions
diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c
new file mode 100644
index 000000000000..f64582b0f623
--- /dev/null
+++ b/drivers/tty/tty_audit.c
@@ -0,0 +1,358 @@
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/slab.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 buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
34 if (!buf->data)
35 goto err_buf;
36 atomic_set(&buf->count, 1);
37 mutex_init(&buf->mutex);
38 buf->major = major;
39 buf->minor = minor;
40 buf->icanon = icanon;
41 buf->valid = 0;
42 return buf;
43
44err_buf:
45 kfree(buf);
46err:
47 return NULL;
48}
49
50static void tty_audit_buf_free(struct tty_audit_buf *buf)
51{
52 WARN_ON(buf->valid != 0);
53 kfree(buf->data);
54 kfree(buf);
55}
56
57static void tty_audit_buf_put(struct tty_audit_buf *buf)
58{
59 if (atomic_dec_and_test(&buf->count))
60 tty_audit_buf_free(buf);
61}
62
63static void tty_audit_log(const char *description, struct task_struct *tsk,
64 uid_t loginuid, unsigned sessionid, int major,
65 int minor, unsigned char *data, size_t size)
66{
67 struct audit_buffer *ab;
68
69 ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY);
70 if (ab) {
71 char name[sizeof(tsk->comm)];
72 uid_t uid = task_uid(tsk);
73
74 audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u "
75 "major=%d minor=%d comm=", description,
76 tsk->pid, uid, loginuid, sessionid,
77 major, minor);
78 get_task_comm(name, tsk);
79 audit_log_untrustedstring(ab, name);
80 audit_log_format(ab, " data=");
81 audit_log_n_hex(ab, data, size);
82 audit_log_end(ab);
83 }
84}
85
86/**
87 * tty_audit_buf_push - Push buffered data out
88 *
89 * Generate an audit message from the contents of @buf, which is owned by
90 * @tsk with @loginuid. @buf->mutex must be locked.
91 */
92static void tty_audit_buf_push(struct task_struct *tsk, uid_t loginuid,
93 unsigned int sessionid,
94 struct tty_audit_buf *buf)
95{
96 if (buf->valid == 0)
97 return;
98 if (audit_enabled == 0)
99 return;
100 tty_audit_log("tty", tsk, loginuid, sessionid, buf->major, buf->minor,
101 buf->data, buf->valid);
102 buf->valid = 0;
103}
104
105/**
106 * tty_audit_buf_push_current - Push buffered data out
107 *
108 * Generate an audit message from the contents of @buf, which is owned by
109 * the current task. @buf->mutex must be locked.
110 */
111static void tty_audit_buf_push_current(struct tty_audit_buf *buf)
112{
113 uid_t auid = audit_get_loginuid(current);
114 unsigned int sessionid = audit_get_sessionid(current);
115 tty_audit_buf_push(current, auid, sessionid, buf);
116}
117
118/**
119 * tty_audit_exit - Handle a task exit
120 *
121 * Make sure all buffered data is written out and deallocate the buffer.
122 * Only needs to be called if current->signal->tty_audit_buf != %NULL.
123 */
124void tty_audit_exit(void)
125{
126 struct tty_audit_buf *buf;
127
128 spin_lock_irq(&current->sighand->siglock);
129 buf = current->signal->tty_audit_buf;
130 current->signal->tty_audit_buf = NULL;
131 spin_unlock_irq(&current->sighand->siglock);
132 if (!buf)
133 return;
134
135 mutex_lock(&buf->mutex);
136 tty_audit_buf_push_current(buf);
137 mutex_unlock(&buf->mutex);
138
139 tty_audit_buf_put(buf);
140}
141
142/**
143 * tty_audit_fork - Copy TTY audit state for a new task
144 *
145 * Set up TTY audit state in @sig from current. @sig needs no locking.
146 */
147void tty_audit_fork(struct signal_struct *sig)
148{
149 spin_lock_irq(&current->sighand->siglock);
150 sig->audit_tty = current->signal->audit_tty;
151 spin_unlock_irq(&current->sighand->siglock);
152}
153
154/**
155 * tty_audit_tiocsti - Log TIOCSTI
156 */
157void tty_audit_tiocsti(struct tty_struct *tty, char ch)
158{
159 struct tty_audit_buf *buf;
160 int major, minor, should_audit;
161
162 spin_lock_irq(&current->sighand->siglock);
163 should_audit = current->signal->audit_tty;
164 buf = current->signal->tty_audit_buf;
165 if (buf)
166 atomic_inc(&buf->count);
167 spin_unlock_irq(&current->sighand->siglock);
168
169 major = tty->driver->major;
170 minor = tty->driver->minor_start + tty->index;
171 if (buf) {
172 mutex_lock(&buf->mutex);
173 if (buf->major == major && buf->minor == minor)
174 tty_audit_buf_push_current(buf);
175 mutex_unlock(&buf->mutex);
176 tty_audit_buf_put(buf);
177 }
178
179 if (should_audit && audit_enabled) {
180 uid_t auid;
181 unsigned int sessionid;
182
183 auid = audit_get_loginuid(current);
184 sessionid = audit_get_sessionid(current);
185 tty_audit_log("ioctl=TIOCSTI", current, auid, sessionid, major,
186 minor, &ch, 1);
187 }
188}
189
190/**
191 * tty_audit_push_task - Flush task's pending audit data
192 * @tsk: task pointer
193 * @loginuid: sender login uid
194 * @sessionid: sender session id
195 *
196 * Called with a ref on @tsk held. Try to lock sighand and get a
197 * reference to the tty audit buffer if available.
198 * Flush the buffer or return an appropriate error code.
199 */
200int tty_audit_push_task(struct task_struct *tsk, uid_t loginuid, u32 sessionid)
201{
202 struct tty_audit_buf *buf = ERR_PTR(-EPERM);
203 unsigned long flags;
204
205 if (!lock_task_sighand(tsk, &flags))
206 return -ESRCH;
207
208 if (tsk->signal->audit_tty) {
209 buf = tsk->signal->tty_audit_buf;
210 if (buf)
211 atomic_inc(&buf->count);
212 }
213 unlock_task_sighand(tsk, &flags);
214
215 /*
216 * Return 0 when signal->audit_tty set
217 * but tsk->signal->tty_audit_buf == NULL.
218 */
219 if (!buf || IS_ERR(buf))
220 return PTR_ERR(buf);
221
222 mutex_lock(&buf->mutex);
223 tty_audit_buf_push(tsk, loginuid, sessionid, buf);
224 mutex_unlock(&buf->mutex);
225
226 tty_audit_buf_put(buf);
227 return 0;
228}
229
230/**
231 * tty_audit_buf_get - Get an audit buffer.
232 *
233 * Get an audit buffer for @tty, allocate it if necessary. Return %NULL
234 * if TTY auditing is disabled or out of memory. Otherwise, return a new
235 * reference to the buffer.
236 */
237static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty)
238{
239 struct tty_audit_buf *buf, *buf2;
240
241 buf = NULL;
242 buf2 = NULL;
243 spin_lock_irq(&current->sighand->siglock);
244 if (likely(!current->signal->audit_tty))
245 goto out;
246 buf = current->signal->tty_audit_buf;
247 if (buf) {
248 atomic_inc(&buf->count);
249 goto out;
250 }
251 spin_unlock_irq(&current->sighand->siglock);
252
253 buf2 = tty_audit_buf_alloc(tty->driver->major,
254 tty->driver->minor_start + tty->index,
255 tty->icanon);
256 if (buf2 == NULL) {
257 audit_log_lost("out of memory in TTY auditing");
258 return NULL;
259 }
260
261 spin_lock_irq(&current->sighand->siglock);
262 if (!current->signal->audit_tty)
263 goto out;
264 buf = current->signal->tty_audit_buf;
265 if (!buf) {
266 current->signal->tty_audit_buf = buf2;
267 buf = buf2;
268 buf2 = NULL;
269 }
270 atomic_inc(&buf->count);
271 /* Fall through */
272 out:
273 spin_unlock_irq(&current->sighand->siglock);
274 if (buf2)
275 tty_audit_buf_free(buf2);
276 return buf;
277}
278
279/**
280 * tty_audit_add_data - Add data for TTY auditing.
281 *
282 * Audit @data of @size from @tty, if necessary.
283 */
284void tty_audit_add_data(struct tty_struct *tty, unsigned char *data,
285 size_t size)
286{
287 struct tty_audit_buf *buf;
288 int major, minor;
289
290 if (unlikely(size == 0))
291 return;
292
293 if (tty->driver->type == TTY_DRIVER_TYPE_PTY
294 && tty->driver->subtype == PTY_TYPE_MASTER)
295 return;
296
297 buf = tty_audit_buf_get(tty);
298 if (!buf)
299 return;
300
301 mutex_lock(&buf->mutex);
302 major = tty->driver->major;
303 minor = tty->driver->minor_start + tty->index;
304 if (buf->major != major || buf->minor != minor
305 || buf->icanon != tty->icanon) {
306 tty_audit_buf_push_current(buf);
307 buf->major = major;
308 buf->minor = minor;
309 buf->icanon = tty->icanon;
310 }
311 do {
312 size_t run;
313
314 run = N_TTY_BUF_SIZE - buf->valid;
315 if (run > size)
316 run = size;
317 memcpy(buf->data + buf->valid, data, run);
318 buf->valid += run;
319 data += run;
320 size -= run;
321 if (buf->valid == N_TTY_BUF_SIZE)
322 tty_audit_buf_push_current(buf);
323 } while (size != 0);
324 mutex_unlock(&buf->mutex);
325 tty_audit_buf_put(buf);
326}
327
328/**
329 * tty_audit_push - Push buffered data out
330 *
331 * Make sure no audit data is pending for @tty on the current process.
332 */
333void tty_audit_push(struct tty_struct *tty)
334{
335 struct tty_audit_buf *buf;
336
337 spin_lock_irq(&current->sighand->siglock);
338 if (likely(!current->signal->audit_tty)) {
339 spin_unlock_irq(&current->sighand->siglock);
340 return;
341 }
342 buf = current->signal->tty_audit_buf;
343 if (buf)
344 atomic_inc(&buf->count);
345 spin_unlock_irq(&current->sighand->siglock);
346
347 if (buf) {
348 int major, minor;
349
350 major = tty->driver->major;
351 minor = tty->driver->minor_start + tty->index;
352 mutex_lock(&buf->mutex);
353 if (buf->major == major && buf->minor == minor)
354 tty_audit_buf_push_current(buf);
355 mutex_unlock(&buf->mutex);
356 tty_audit_buf_put(buf);
357 }
358}