tty_audit.c 8.3 KB
Newer Older
M
Miloslav Trmac 已提交
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * Creating audit events from TTY input.
 *
 * Copyright (C) 2007 Red Hat, Inc.  All rights reserved.  This copyrighted
 * material is made available to anyone wishing to use, modify, copy, or
 * redistribute it subject to the terms and conditions of the GNU General
 * Public License v.2.
 *
 * Authors: Miloslav Trmac <mitr@redhat.com>
 */

#include <linux/audit.h>
13
#include <linux/slab.h>
M
Miloslav Trmac 已提交
14 15 16 17 18 19 20 21 22 23 24 25
#include <linux/tty.h>

struct tty_audit_buf {
	atomic_t count;
	struct mutex mutex;	/* Protects all data below */
	int major, minor;	/* The TTY which the data is from */
	unsigned icanon:1;
	size_t valid;
	unsigned char *data;	/* Allocated size N_TTY_BUF_SIZE */
};

static struct tty_audit_buf *tty_audit_buf_alloc(int major, int minor,
26
						 unsigned icanon)
M
Miloslav Trmac 已提交
27 28 29
{
	struct tty_audit_buf *buf;

A
Alan Cox 已提交
30
	buf = kmalloc(sizeof(*buf), GFP_KERNEL);
M
Miloslav Trmac 已提交
31 32
	if (!buf)
		goto err;
A
Alan Cox 已提交
33
	buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
M
Miloslav Trmac 已提交
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
	if (!buf->data)
		goto err_buf;
	atomic_set(&buf->count, 1);
	mutex_init(&buf->mutex);
	buf->major = major;
	buf->minor = minor;
	buf->icanon = icanon;
	buf->valid = 0;
	return buf;

err_buf:
	kfree(buf);
err:
	return NULL;
}

static void tty_audit_buf_free(struct tty_audit_buf *buf)
{
	WARN_ON(buf->valid != 0);
A
Alan Cox 已提交
53
	kfree(buf->data);
M
Miloslav Trmac 已提交
54 55 56 57 58 59 60 61 62
	kfree(buf);
}

static void tty_audit_buf_put(struct tty_audit_buf *buf)
{
	if (atomic_dec_and_test(&buf->count))
		tty_audit_buf_free(buf);
}

63 64
static void tty_audit_log(const char *description, int major, int minor,
			  unsigned char *data, size_t size)
M
Miloslav Trmac 已提交
65 66
{
	struct audit_buffer *ab;
67
	struct task_struct *tsk = current;
68
	pid_t pid = task_pid_nr(tsk);
69 70
	uid_t uid = from_kuid(&init_user_ns, task_uid(tsk));
	uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(tsk));
71
	unsigned int sessionid = audit_get_sessionid(tsk);
M
Miloslav Trmac 已提交
72 73 74 75

	ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY);
	if (ab) {
		char name[sizeof(tsk->comm)];
76 77

		audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d"
78
				 " minor=%d comm=", description, pid, uid,
79
				 loginuid, sessionid, major, minor);
M
Miloslav Trmac 已提交
80 81 82
		get_task_comm(name, tsk);
		audit_log_untrustedstring(ab, name);
		audit_log_format(ab, " data=");
A
Al Viro 已提交
83
		audit_log_n_hex(ab, data, size);
M
Miloslav Trmac 已提交
84 85
		audit_log_end(ab);
	}
A
Al Viro 已提交
86 87 88 89 90 91
}

/**
 *	tty_audit_buf_push	-	Push buffered data out
 *
 *	Generate an audit message from the contents of @buf, which is owned by
92
 *	the current task.  @buf->mutex must be locked.
A
Al Viro 已提交
93
 */
94
static void tty_audit_buf_push(struct tty_audit_buf *buf)
A
Al Viro 已提交
95 96 97
{
	if (buf->valid == 0)
		return;
98 99
	if (audit_enabled == 0) {
		buf->valid = 0;
A
Al Viro 已提交
100
		return;
101
	}
102
	tty_audit_log("tty", buf->major, buf->minor, buf->data, buf->valid);
M
Miloslav Trmac 已提交
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
	buf->valid = 0;
}

/**
 *	tty_audit_exit	-	Handle a task exit
 *
 *	Make sure all buffered data is written out and deallocate the buffer.
 *	Only needs to be called if current->signal->tty_audit_buf != %NULL.
 */
void tty_audit_exit(void)
{
	struct tty_audit_buf *buf;

	buf = current->signal->tty_audit_buf;
	current->signal->tty_audit_buf = NULL;
	if (!buf)
		return;

	mutex_lock(&buf->mutex);
122
	tty_audit_buf_push(buf);
M
Miloslav Trmac 已提交
123 124 125 126 127 128 129 130 131 132 133 134 135
	mutex_unlock(&buf->mutex);

	tty_audit_buf_put(buf);
}

/**
 *	tty_audit_fork	-	Copy TTY audit state for a new task
 *
 *	Set up TTY audit state in @sig from current.  @sig needs no locking.
 */
void tty_audit_fork(struct signal_struct *sig)
{
	sig->audit_tty = current->signal->audit_tty;
136
	sig->audit_tty_log_passwd = current->signal->audit_tty_log_passwd;
M
Miloslav Trmac 已提交
137 138
}

A
Al Viro 已提交
139 140 141 142 143 144 145
/**
 *	tty_audit_tiocsti	-	Log TIOCSTI
 */
void tty_audit_tiocsti(struct tty_struct *tty, char ch)
{
	struct tty_audit_buf *buf;
	int major, minor, should_audit;
146
	unsigned long flags;
A
Al Viro 已提交
147

148
	spin_lock_irqsave(&current->sighand->siglock, flags);
A
Al Viro 已提交
149 150 151 152
	should_audit = current->signal->audit_tty;
	buf = current->signal->tty_audit_buf;
	if (buf)
		atomic_inc(&buf->count);
153
	spin_unlock_irqrestore(&current->sighand->siglock, flags);
A
Al Viro 已提交
154 155 156 157 158 159

	major = tty->driver->major;
	minor = tty->driver->minor_start + tty->index;
	if (buf) {
		mutex_lock(&buf->mutex);
		if (buf->major == major && buf->minor == minor)
160
			tty_audit_buf_push(buf);
A
Al Viro 已提交
161 162 163 164 165
		mutex_unlock(&buf->mutex);
		tty_audit_buf_put(buf);
	}

	if (should_audit && audit_enabled) {
166
		kuid_t auid;
A
Al Viro 已提交
167 168 169 170
		unsigned int sessionid;

		auid = audit_get_loginuid(current);
		sessionid = audit_get_sessionid(current);
171
		tty_audit_log("ioctl=TIOCSTI", major, minor, &ch, 1);
A
Al Viro 已提交
172 173 174
	}
}

M
Miloslav Trmac 已提交
175
/**
176
 * tty_audit_push_current -	Flush current's pending audit data
177
 *
178
 * Try to lock sighand and get a reference to the tty audit buffer if available.
179
 * Flush the buffer or return an appropriate error code.
M
Miloslav Trmac 已提交
180
 */
181
int tty_audit_push_current(void)
M
Miloslav Trmac 已提交
182
{
183
	struct tty_audit_buf *buf = ERR_PTR(-EPERM);
184
	struct task_struct *tsk = current;
185
	unsigned long flags;
M
Miloslav Trmac 已提交
186

187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
	if (!lock_task_sighand(tsk, &flags))
		return -ESRCH;

	if (tsk->signal->audit_tty) {
		buf = tsk->signal->tty_audit_buf;
		if (buf)
			atomic_inc(&buf->count);
	}
	unlock_task_sighand(tsk, &flags);

	/*
	 * Return 0 when signal->audit_tty set
	 * but tsk->signal->tty_audit_buf == NULL.
	 */
	if (!buf || IS_ERR(buf))
		return PTR_ERR(buf);
M
Miloslav Trmac 已提交
203 204

	mutex_lock(&buf->mutex);
205
	tty_audit_buf_push(buf);
M
Miloslav Trmac 已提交
206 207 208
	mutex_unlock(&buf->mutex);

	tty_audit_buf_put(buf);
209
	return 0;
M
Miloslav Trmac 已提交
210 211 212 213 214 215 216 217 218
}

/**
 *	tty_audit_buf_get	-	Get an audit buffer.
 *
 *	Get an audit buffer for @tty, allocate it if necessary.  Return %NULL
 *	if TTY auditing is disabled or out of memory.  Otherwise, return a new
 *	reference to the buffer.
 */
219 220
static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty,
		unsigned icanon)
M
Miloslav Trmac 已提交
221 222
{
	struct tty_audit_buf *buf, *buf2;
223
	unsigned long flags;
M
Miloslav Trmac 已提交
224 225 226

	buf = NULL;
	buf2 = NULL;
227
	spin_lock_irqsave(&current->sighand->siglock, flags);
M
Miloslav Trmac 已提交
228 229 230 231 232 233 234
	if (likely(!current->signal->audit_tty))
		goto out;
	buf = current->signal->tty_audit_buf;
	if (buf) {
		atomic_inc(&buf->count);
		goto out;
	}
235
	spin_unlock_irqrestore(&current->sighand->siglock, flags);
M
Miloslav Trmac 已提交
236 237 238

	buf2 = tty_audit_buf_alloc(tty->driver->major,
				   tty->driver->minor_start + tty->index,
239
				   icanon);
M
Miloslav Trmac 已提交
240 241 242 243 244
	if (buf2 == NULL) {
		audit_log_lost("out of memory in TTY auditing");
		return NULL;
	}

245
	spin_lock_irqsave(&current->sighand->siglock, flags);
M
Miloslav Trmac 已提交
246 247 248 249 250 251 252 253 254 255 256
	if (!current->signal->audit_tty)
		goto out;
	buf = current->signal->tty_audit_buf;
	if (!buf) {
		current->signal->tty_audit_buf = buf2;
		buf = buf2;
		buf2 = NULL;
	}
	atomic_inc(&buf->count);
	/* Fall through */
 out:
257
	spin_unlock_irqrestore(&current->sighand->siglock, flags);
M
Miloslav Trmac 已提交
258 259 260 261 262 263 264 265 266 267 268
	if (buf2)
		tty_audit_buf_free(buf2);
	return buf;
}

/**
 *	tty_audit_add_data	-	Add data for TTY auditing.
 *
 *	Audit @data of @size from @tty, if necessary.
 */
void tty_audit_add_data(struct tty_struct *tty, unsigned char *data,
269
			size_t size, unsigned icanon)
M
Miloslav Trmac 已提交
270 271 272
{
	struct tty_audit_buf *buf;
	int major, minor;
273 274
	int audit_log_tty_passwd;
	unsigned long flags;
M
Miloslav Trmac 已提交
275 276 277 278

	if (unlikely(size == 0))
		return;

279 280 281 282 283 284
	spin_lock_irqsave(&current->sighand->siglock, flags);
	audit_log_tty_passwd = current->signal->audit_tty_log_passwd;
	spin_unlock_irqrestore(&current->sighand->siglock, flags);
	if (!audit_log_tty_passwd && icanon && !L_ECHO(tty))
		return;

285 286 287 288
	if (tty->driver->type == TTY_DRIVER_TYPE_PTY
	    && tty->driver->subtype == PTY_TYPE_MASTER)
		return;

289
	buf = tty_audit_buf_get(tty, icanon);
M
Miloslav Trmac 已提交
290 291 292 293 294 295 296
	if (!buf)
		return;

	mutex_lock(&buf->mutex);
	major = tty->driver->major;
	minor = tty->driver->minor_start + tty->index;
	if (buf->major != major || buf->minor != minor
297
	    || buf->icanon != icanon) {
298
		tty_audit_buf_push(buf);
M
Miloslav Trmac 已提交
299 300
		buf->major = major;
		buf->minor = minor;
301
		buf->icanon = icanon;
M
Miloslav Trmac 已提交
302 303 304 305 306 307 308 309 310 311 312 313
	}
	do {
		size_t run;

		run = N_TTY_BUF_SIZE - buf->valid;
		if (run > size)
			run = size;
		memcpy(buf->data + buf->valid, data, run);
		buf->valid += run;
		data += run;
		size -= run;
		if (buf->valid == N_TTY_BUF_SIZE)
314
			tty_audit_buf_push(buf);
M
Miloslav Trmac 已提交
315 316 317 318 319 320 321 322 323 324 325 326 327
	} while (size != 0);
	mutex_unlock(&buf->mutex);
	tty_audit_buf_put(buf);
}

/**
 *	tty_audit_push	-	Push buffered data out
 *
 *	Make sure no audit data is pending for @tty on the current process.
 */
void tty_audit_push(struct tty_struct *tty)
{
	struct tty_audit_buf *buf;
328
	unsigned long flags;
M
Miloslav Trmac 已提交
329

330
	spin_lock_irqsave(&current->sighand->siglock, flags);
M
Miloslav Trmac 已提交
331
	if (likely(!current->signal->audit_tty)) {
332
		spin_unlock_irqrestore(&current->sighand->siglock, flags);
M
Miloslav Trmac 已提交
333 334 335 336 337
		return;
	}
	buf = current->signal->tty_audit_buf;
	if (buf)
		atomic_inc(&buf->count);
338
	spin_unlock_irqrestore(&current->sighand->siglock, flags);
M
Miloslav Trmac 已提交
339 340 341 342 343 344 345 346

	if (buf) {
		int major, minor;

		major = tty->driver->major;
		minor = tty->driver->minor_start + tty->index;
		mutex_lock(&buf->mutex);
		if (buf->major == major && buf->minor == minor)
347
			tty_audit_buf_push(buf);
M
Miloslav Trmac 已提交
348 349 350 351
		mutex_unlock(&buf->mutex);
		tty_audit_buf_put(buf);
	}
}