blob: 50380d87061d0590f5538eed1ab24acfb4351f10 [file] [log] [blame]
Miloslav Trmac522ed772007-07-15 23:40:56 -07001/*
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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090013#include <linux/slab.h>
Miloslav Trmac522ed772007-07-15 23:40:56 -070014#include <linux/tty.h>
15
16struct tty_audit_buf {
17 atomic_t count;
18 struct mutex mutex; /* Protects all data below */
Peter Hurley4d240b62016-01-09 22:55:32 -080019 dev_t dev; /* The TTY which the data is from */
Miloslav Trmac522ed772007-07-15 23:40:56 -070020 unsigned icanon:1;
21 size_t valid;
22 unsigned char *data; /* Allocated size N_TTY_BUF_SIZE */
23};
24
Peter Hurleya75c9b02016-01-09 22:55:28 -080025static struct tty_audit_buf *tty_audit_buf_alloc(void)
Miloslav Trmac522ed772007-07-15 23:40:56 -070026{
27 struct tty_audit_buf *buf;
28
Alan Cox66c6cea2008-02-08 04:18:46 -080029 buf = kmalloc(sizeof(*buf), GFP_KERNEL);
Miloslav Trmac522ed772007-07-15 23:40:56 -070030 if (!buf)
31 goto err;
Alan Coxc481c702009-06-11 13:04:27 +010032 buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
Miloslav Trmac522ed772007-07-15 23:40:56 -070033 if (!buf->data)
34 goto err_buf;
35 atomic_set(&buf->count, 1);
36 mutex_init(&buf->mutex);
Peter Hurley4d240b62016-01-09 22:55:32 -080037 buf->dev = MKDEV(0, 0);
Peter Hurleya75c9b02016-01-09 22:55:28 -080038 buf->icanon = 0;
Miloslav Trmac522ed772007-07-15 23:40:56 -070039 buf->valid = 0;
40 return buf;
41
42err_buf:
43 kfree(buf);
44err:
45 return NULL;
46}
47
48static void tty_audit_buf_free(struct tty_audit_buf *buf)
49{
50 WARN_ON(buf->valid != 0);
Alan Coxc481c702009-06-11 13:04:27 +010051 kfree(buf->data);
Miloslav Trmac522ed772007-07-15 23:40:56 -070052 kfree(buf);
53}
54
55static void tty_audit_buf_put(struct tty_audit_buf *buf)
56{
57 if (atomic_dec_and_test(&buf->count))
58 tty_audit_buf_free(buf);
59}
60
Peter Hurley4d240b62016-01-09 22:55:32 -080061static void tty_audit_log(const char *description, dev_t dev,
Eric Paris152f4972013-04-19 13:56:11 -040062 unsigned char *data, size_t size)
Al Viro1e641742008-12-09 09:23:33 +000063{
64 struct audit_buffer *ab;
Eric Paris152f4972013-04-19 13:56:11 -040065 struct task_struct *tsk = current;
Richard Guy Briggsf1dc4862013-12-11 13:52:26 -050066 pid_t pid = task_pid_nr(tsk);
Eric Paris152f4972013-04-19 13:56:11 -040067 uid_t uid = from_kuid(&init_user_ns, task_uid(tsk));
68 uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(tsk));
Eric Paris4440e852013-11-27 17:35:17 -050069 unsigned int sessionid = audit_get_sessionid(tsk);
Al Viro1e641742008-12-09 09:23:33 +000070
71 ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY);
72 if (ab) {
73 char name[sizeof(tsk->comm)];
Al Viro1e641742008-12-09 09:23:33 +000074
Eric Paris152f4972013-04-19 13:56:11 -040075 audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d"
Richard Guy Briggsf1dc4862013-12-11 13:52:26 -050076 " minor=%d comm=", description, pid, uid,
Peter Hurley4d240b62016-01-09 22:55:32 -080077 loginuid, sessionid, MAJOR(dev), MINOR(dev));
Al Viro1e641742008-12-09 09:23:33 +000078 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
Miloslav Trmac522ed772007-07-15 23:40:56 -070086/**
87 * tty_audit_buf_push - Push buffered data out
88 *
89 * Generate an audit message from the contents of @buf, which is owned by
Eric Paris152f4972013-04-19 13:56:11 -040090 * the current task. @buf->mutex must be locked.
Miloslav Trmac522ed772007-07-15 23:40:56 -070091 */
Eric Paris152f4972013-04-19 13:56:11 -040092static void tty_audit_buf_push(struct tty_audit_buf *buf)
Miloslav Trmac522ed772007-07-15 23:40:56 -070093{
Miloslav Trmac522ed772007-07-15 23:40:56 -070094 if (buf->valid == 0)
95 return;
Xiaotian Feng00bff392011-03-03 18:08:24 +080096 if (audit_enabled == 0) {
97 buf->valid = 0;
Miloslav Trmac522ed772007-07-15 23:40:56 -070098 return;
Xiaotian Feng00bff392011-03-03 18:08:24 +080099 }
Peter Hurley4d240b62016-01-09 22:55:32 -0800100 tty_audit_log("tty", buf->dev, buf->data, buf->valid);
Miloslav Trmac522ed772007-07-15 23:40:56 -0700101 buf->valid = 0;
102}
103
104/**
Miloslav Trmac522ed772007-07-15 23:40:56 -0700105 * tty_audit_exit - Handle a task exit
106 *
107 * Make sure all buffered data is written out and deallocate the buffer.
108 * Only needs to be called if current->signal->tty_audit_buf != %NULL.
109 */
110void tty_audit_exit(void)
111{
112 struct tty_audit_buf *buf;
113
Miloslav Trmac522ed772007-07-15 23:40:56 -0700114 buf = current->signal->tty_audit_buf;
115 current->signal->tty_audit_buf = NULL;
Miloslav Trmac522ed772007-07-15 23:40:56 -0700116 if (!buf)
117 return;
118
119 mutex_lock(&buf->mutex);
Eric Paris152f4972013-04-19 13:56:11 -0400120 tty_audit_buf_push(buf);
Miloslav Trmac522ed772007-07-15 23:40:56 -0700121 mutex_unlock(&buf->mutex);
122
123 tty_audit_buf_put(buf);
124}
125
126/**
127 * tty_audit_fork - Copy TTY audit state for a new task
128 *
129 * Set up TTY audit state in @sig from current. @sig needs no locking.
130 */
131void tty_audit_fork(struct signal_struct *sig)
132{
Miloslav Trmac522ed772007-07-15 23:40:56 -0700133 sig->audit_tty = current->signal->audit_tty;
Richard Guy Briggs46e959e2013-05-03 14:03:50 -0400134 sig->audit_tty_log_passwd = current->signal->audit_tty_log_passwd;
Miloslav Trmac522ed772007-07-15 23:40:56 -0700135}
136
137/**
Al Viro1e641742008-12-09 09:23:33 +0000138 * tty_audit_tiocsti - Log TIOCSTI
139 */
140void tty_audit_tiocsti(struct tty_struct *tty, char ch)
141{
142 struct tty_audit_buf *buf;
Peter Hurley4d240b62016-01-09 22:55:32 -0800143 dev_t dev;
144 int should_audit;
Eric Parisbde02ca2013-04-30 11:01:14 -0400145 unsigned long flags;
Al Viro1e641742008-12-09 09:23:33 +0000146
Eric Parisbde02ca2013-04-30 11:01:14 -0400147 spin_lock_irqsave(&current->sighand->siglock, flags);
Al Viro1e641742008-12-09 09:23:33 +0000148 should_audit = current->signal->audit_tty;
149 buf = current->signal->tty_audit_buf;
150 if (buf)
151 atomic_inc(&buf->count);
Eric Parisbde02ca2013-04-30 11:01:14 -0400152 spin_unlock_irqrestore(&current->sighand->siglock, flags);
Al Viro1e641742008-12-09 09:23:33 +0000153
Peter Hurley4d240b62016-01-09 22:55:32 -0800154 dev = MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index;
Al Viro1e641742008-12-09 09:23:33 +0000155 if (buf) {
156 mutex_lock(&buf->mutex);
Peter Hurley4d240b62016-01-09 22:55:32 -0800157 if (buf->dev == dev)
Eric Paris152f4972013-04-19 13:56:11 -0400158 tty_audit_buf_push(buf);
Al Viro1e641742008-12-09 09:23:33 +0000159 mutex_unlock(&buf->mutex);
160 tty_audit_buf_put(buf);
161 }
162
163 if (should_audit && audit_enabled) {
Eric W. Biedermane1760bd2012-09-10 22:39:43 -0700164 kuid_t auid;
Al Viro1e641742008-12-09 09:23:33 +0000165 unsigned int sessionid;
166
167 auid = audit_get_loginuid(current);
168 sessionid = audit_get_sessionid(current);
Peter Hurley4d240b62016-01-09 22:55:32 -0800169 tty_audit_log("ioctl=TIOCSTI", dev, &ch, 1);
Al Viro1e641742008-12-09 09:23:33 +0000170 }
171}
172
173/**
Peter Hurley37282a72016-01-09 22:55:31 -0800174 * tty_audit_push - Flush current's pending audit data
Thomas Gleixner3c80fe42009-12-09 14:19:31 +0000175 *
Peter Hurley37282a72016-01-09 22:55:31 -0800176 * Returns 0 if success, -EPERM if tty audit is disabled
Miloslav Trmac522ed772007-07-15 23:40:56 -0700177 */
Peter Hurley37282a72016-01-09 22:55:31 -0800178int tty_audit_push(void)
Miloslav Trmac522ed772007-07-15 23:40:56 -0700179{
Thomas Gleixner3c80fe42009-12-09 14:19:31 +0000180 struct tty_audit_buf *buf = ERR_PTR(-EPERM);
181 unsigned long flags;
Miloslav Trmac522ed772007-07-15 23:40:56 -0700182
Peter Hurleyf229c2c2016-01-09 22:55:29 -0800183 spin_lock_irqsave(&current->sighand->siglock, flags);
184 if (current->signal->audit_tty) {
185 buf = current->signal->tty_audit_buf;
Thomas Gleixner3c80fe42009-12-09 14:19:31 +0000186 if (buf)
187 atomic_inc(&buf->count);
188 }
Peter Hurleyf229c2c2016-01-09 22:55:29 -0800189 spin_unlock_irqrestore(&current->sighand->siglock, flags);
Thomas Gleixner3c80fe42009-12-09 14:19:31 +0000190
191 /*
192 * Return 0 when signal->audit_tty set
Peter Hurleyf229c2c2016-01-09 22:55:29 -0800193 * but current->signal->tty_audit_buf == NULL.
Thomas Gleixner3c80fe42009-12-09 14:19:31 +0000194 */
195 if (!buf || IS_ERR(buf))
196 return PTR_ERR(buf);
Miloslav Trmac522ed772007-07-15 23:40:56 -0700197
198 mutex_lock(&buf->mutex);
Eric Paris152f4972013-04-19 13:56:11 -0400199 tty_audit_buf_push(buf);
Miloslav Trmac522ed772007-07-15 23:40:56 -0700200 mutex_unlock(&buf->mutex);
201
202 tty_audit_buf_put(buf);
Thomas Gleixner3c80fe42009-12-09 14:19:31 +0000203 return 0;
Miloslav Trmac522ed772007-07-15 23:40:56 -0700204}
205
206/**
207 * tty_audit_buf_get - Get an audit buffer.
208 *
Peter Hurleya75c9b02016-01-09 22:55:28 -0800209 * Get an audit buffer, allocate it if necessary. Return %NULL
Miloslav Trmac522ed772007-07-15 23:40:56 -0700210 * if TTY auditing is disabled or out of memory. Otherwise, return a new
211 * reference to the buffer.
212 */
Peter Hurleya75c9b02016-01-09 22:55:28 -0800213static struct tty_audit_buf *tty_audit_buf_get(void)
Miloslav Trmac522ed772007-07-15 23:40:56 -0700214{
215 struct tty_audit_buf *buf, *buf2;
Eric Parisbde02ca2013-04-30 11:01:14 -0400216 unsigned long flags;
Miloslav Trmac522ed772007-07-15 23:40:56 -0700217
218 buf = NULL;
219 buf2 = NULL;
Eric Parisbde02ca2013-04-30 11:01:14 -0400220 spin_lock_irqsave(&current->sighand->siglock, flags);
Miloslav Trmac522ed772007-07-15 23:40:56 -0700221 if (likely(!current->signal->audit_tty))
222 goto out;
223 buf = current->signal->tty_audit_buf;
224 if (buf) {
225 atomic_inc(&buf->count);
226 goto out;
227 }
Eric Parisbde02ca2013-04-30 11:01:14 -0400228 spin_unlock_irqrestore(&current->sighand->siglock, flags);
Miloslav Trmac522ed772007-07-15 23:40:56 -0700229
Peter Hurleya75c9b02016-01-09 22:55:28 -0800230 buf2 = tty_audit_buf_alloc();
Miloslav Trmac522ed772007-07-15 23:40:56 -0700231 if (buf2 == NULL) {
232 audit_log_lost("out of memory in TTY auditing");
233 return NULL;
234 }
235
Eric Parisbde02ca2013-04-30 11:01:14 -0400236 spin_lock_irqsave(&current->sighand->siglock, flags);
Miloslav Trmac522ed772007-07-15 23:40:56 -0700237 if (!current->signal->audit_tty)
238 goto out;
239 buf = current->signal->tty_audit_buf;
240 if (!buf) {
241 current->signal->tty_audit_buf = buf2;
242 buf = buf2;
243 buf2 = NULL;
244 }
245 atomic_inc(&buf->count);
246 /* Fall through */
247 out:
Eric Parisbde02ca2013-04-30 11:01:14 -0400248 spin_unlock_irqrestore(&current->sighand->siglock, flags);
Miloslav Trmac522ed772007-07-15 23:40:56 -0700249 if (buf2)
250 tty_audit_buf_free(buf2);
251 return buf;
252}
253
254/**
255 * tty_audit_add_data - Add data for TTY auditing.
256 *
257 * Audit @data of @size from @tty, if necessary.
258 */
Peter Hurley309426a2016-01-09 22:55:27 -0800259void tty_audit_add_data(struct tty_struct *tty, const void *data, size_t size)
Miloslav Trmac522ed772007-07-15 23:40:56 -0700260{
261 struct tty_audit_buf *buf;
Richard Guy Briggs46e959e2013-05-03 14:03:50 -0400262 int audit_log_tty_passwd;
263 unsigned long flags;
Peter Hurley309426a2016-01-09 22:55:27 -0800264 unsigned int icanon = !!L_ICANON(tty);
Peter Hurley4d240b62016-01-09 22:55:32 -0800265 dev_t dev;
Miloslav Trmac522ed772007-07-15 23:40:56 -0700266
267 if (unlikely(size == 0))
268 return;
269
Peter Hurleyd7c0ba42016-01-09 22:55:25 -0800270 if (tty->driver->type == TTY_DRIVER_TYPE_PTY
271 && tty->driver->subtype == PTY_TYPE_MASTER)
272 return;
273
Richard Guy Briggs46e959e2013-05-03 14:03:50 -0400274 spin_lock_irqsave(&current->sighand->siglock, flags);
275 audit_log_tty_passwd = current->signal->audit_tty_log_passwd;
276 spin_unlock_irqrestore(&current->sighand->siglock, flags);
277 if (!audit_log_tty_passwd && icanon && !L_ECHO(tty))
278 return;
279
Peter Hurleya75c9b02016-01-09 22:55:28 -0800280 buf = tty_audit_buf_get();
Miloslav Trmac522ed772007-07-15 23:40:56 -0700281 if (!buf)
282 return;
283
284 mutex_lock(&buf->mutex);
Peter Hurley4d240b62016-01-09 22:55:32 -0800285 dev = MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index;
286 if (buf->dev != dev || buf->icanon != icanon) {
Eric Paris152f4972013-04-19 13:56:11 -0400287 tty_audit_buf_push(buf);
Peter Hurley4d240b62016-01-09 22:55:32 -0800288 buf->dev = dev;
Jiri Slaby6c633f22012-10-18 22:26:37 +0200289 buf->icanon = icanon;
Miloslav Trmac522ed772007-07-15 23:40:56 -0700290 }
291 do {
292 size_t run;
293
294 run = N_TTY_BUF_SIZE - buf->valid;
295 if (run > size)
296 run = size;
297 memcpy(buf->data + buf->valid, data, run);
298 buf->valid += run;
299 data += run;
300 size -= run;
301 if (buf->valid == N_TTY_BUF_SIZE)
Eric Paris152f4972013-04-19 13:56:11 -0400302 tty_audit_buf_push(buf);
Miloslav Trmac522ed772007-07-15 23:40:56 -0700303 } while (size != 0);
304 mutex_unlock(&buf->mutex);
305 tty_audit_buf_put(buf);
306}