18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Creating audit events from TTY input.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2007 Red Hat, Inc.  All rights reserved.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Authors: Miloslav Trmac <mitr@redhat.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/audit.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/tty.h>
138c2ecf20Sopenharmony_ci#include "tty.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistruct tty_audit_buf {
168c2ecf20Sopenharmony_ci	struct mutex mutex;	/* Protects all data below */
178c2ecf20Sopenharmony_ci	dev_t dev;		/* The TTY which the data is from */
188c2ecf20Sopenharmony_ci	unsigned icanon:1;
198c2ecf20Sopenharmony_ci	size_t valid;
208c2ecf20Sopenharmony_ci	unsigned char *data;	/* Allocated size N_TTY_BUF_SIZE */
218c2ecf20Sopenharmony_ci};
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic struct tty_audit_buf *tty_audit_buf_ref(void)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	struct tty_audit_buf *buf;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	buf = current->signal->tty_audit_buf;
288c2ecf20Sopenharmony_ci	WARN_ON(buf == ERR_PTR(-ESRCH));
298c2ecf20Sopenharmony_ci	return buf;
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic struct tty_audit_buf *tty_audit_buf_alloc(void)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct tty_audit_buf *buf;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	buf = kmalloc(sizeof(*buf), GFP_KERNEL);
378c2ecf20Sopenharmony_ci	if (!buf)
388c2ecf20Sopenharmony_ci		goto err;
398c2ecf20Sopenharmony_ci	buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
408c2ecf20Sopenharmony_ci	if (!buf->data)
418c2ecf20Sopenharmony_ci		goto err_buf;
428c2ecf20Sopenharmony_ci	mutex_init(&buf->mutex);
438c2ecf20Sopenharmony_ci	buf->dev = MKDEV(0, 0);
448c2ecf20Sopenharmony_ci	buf->icanon = 0;
458c2ecf20Sopenharmony_ci	buf->valid = 0;
468c2ecf20Sopenharmony_ci	return buf;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cierr_buf:
498c2ecf20Sopenharmony_ci	kfree(buf);
508c2ecf20Sopenharmony_cierr:
518c2ecf20Sopenharmony_ci	return NULL;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic void tty_audit_buf_free(struct tty_audit_buf *buf)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	WARN_ON(buf->valid != 0);
578c2ecf20Sopenharmony_ci	kfree(buf->data);
588c2ecf20Sopenharmony_ci	kfree(buf);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic void tty_audit_log(const char *description, dev_t dev,
628c2ecf20Sopenharmony_ci			  unsigned char *data, size_t size)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	struct audit_buffer *ab;
658c2ecf20Sopenharmony_ci	pid_t pid = task_pid_nr(current);
668c2ecf20Sopenharmony_ci	uid_t uid = from_kuid(&init_user_ns, task_uid(current));
678c2ecf20Sopenharmony_ci	uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(current));
688c2ecf20Sopenharmony_ci	unsigned int sessionid = audit_get_sessionid(current);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_TTY);
718c2ecf20Sopenharmony_ci	if (ab) {
728c2ecf20Sopenharmony_ci		char name[sizeof(current->comm)];
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci		audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d"
758c2ecf20Sopenharmony_ci				 " minor=%d comm=", description, pid, uid,
768c2ecf20Sopenharmony_ci				 loginuid, sessionid, MAJOR(dev), MINOR(dev));
778c2ecf20Sopenharmony_ci		get_task_comm(name, current);
788c2ecf20Sopenharmony_ci		audit_log_untrustedstring(ab, name);
798c2ecf20Sopenharmony_ci		audit_log_format(ab, " data=");
808c2ecf20Sopenharmony_ci		audit_log_n_hex(ab, data, size);
818c2ecf20Sopenharmony_ci		audit_log_end(ab);
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/**
868c2ecf20Sopenharmony_ci *	tty_audit_buf_push	-	Push buffered data out
878c2ecf20Sopenharmony_ci *
888c2ecf20Sopenharmony_ci *	Generate an audit message from the contents of @buf, which is owned by
898c2ecf20Sopenharmony_ci *	the current task.  @buf->mutex must be locked.
908c2ecf20Sopenharmony_ci */
918c2ecf20Sopenharmony_cistatic void tty_audit_buf_push(struct tty_audit_buf *buf)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	if (buf->valid == 0)
948c2ecf20Sopenharmony_ci		return;
958c2ecf20Sopenharmony_ci	if (audit_enabled == AUDIT_OFF) {
968c2ecf20Sopenharmony_ci		buf->valid = 0;
978c2ecf20Sopenharmony_ci		return;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci	tty_audit_log("tty", buf->dev, buf->data, buf->valid);
1008c2ecf20Sopenharmony_ci	buf->valid = 0;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/**
1048c2ecf20Sopenharmony_ci *	tty_audit_exit	-	Handle a task exit
1058c2ecf20Sopenharmony_ci *
1068c2ecf20Sopenharmony_ci *	Make sure all buffered data is written out and deallocate the buffer.
1078c2ecf20Sopenharmony_ci *	Only needs to be called if current->signal->tty_audit_buf != %NULL.
1088c2ecf20Sopenharmony_ci *
1098c2ecf20Sopenharmony_ci *	The process is single-threaded at this point; no other threads share
1108c2ecf20Sopenharmony_ci *	current->signal.
1118c2ecf20Sopenharmony_ci */
1128c2ecf20Sopenharmony_civoid tty_audit_exit(void)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct tty_audit_buf *buf;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	buf = xchg(&current->signal->tty_audit_buf, ERR_PTR(-ESRCH));
1178c2ecf20Sopenharmony_ci	if (!buf)
1188c2ecf20Sopenharmony_ci		return;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	tty_audit_buf_push(buf);
1218c2ecf20Sopenharmony_ci	tty_audit_buf_free(buf);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci/**
1258c2ecf20Sopenharmony_ci *	tty_audit_fork	-	Copy TTY audit state for a new task
1268c2ecf20Sopenharmony_ci *
1278c2ecf20Sopenharmony_ci *	Set up TTY audit state in @sig from current.  @sig needs no locking.
1288c2ecf20Sopenharmony_ci */
1298c2ecf20Sopenharmony_civoid tty_audit_fork(struct signal_struct *sig)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	sig->audit_tty = current->signal->audit_tty;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci/**
1358c2ecf20Sopenharmony_ci *	tty_audit_tiocsti	-	Log TIOCSTI
1368c2ecf20Sopenharmony_ci */
1378c2ecf20Sopenharmony_civoid tty_audit_tiocsti(struct tty_struct *tty, char ch)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	dev_t dev;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	dev = MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index;
1428c2ecf20Sopenharmony_ci	if (tty_audit_push())
1438c2ecf20Sopenharmony_ci		return;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (audit_enabled)
1468c2ecf20Sopenharmony_ci		tty_audit_log("ioctl=TIOCSTI", dev, &ch, 1);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci/**
1508c2ecf20Sopenharmony_ci *	tty_audit_push	-	Flush current's pending audit data
1518c2ecf20Sopenharmony_ci *
1528c2ecf20Sopenharmony_ci *	Returns 0 if success, -EPERM if tty audit is disabled
1538c2ecf20Sopenharmony_ci */
1548c2ecf20Sopenharmony_ciint tty_audit_push(void)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct tty_audit_buf *buf;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (~current->signal->audit_tty & AUDIT_TTY_ENABLE)
1598c2ecf20Sopenharmony_ci		return -EPERM;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	buf = tty_audit_buf_ref();
1628c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(buf)) {
1638c2ecf20Sopenharmony_ci		mutex_lock(&buf->mutex);
1648c2ecf20Sopenharmony_ci		tty_audit_buf_push(buf);
1658c2ecf20Sopenharmony_ci		mutex_unlock(&buf->mutex);
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci	return 0;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/**
1718c2ecf20Sopenharmony_ci *	tty_audit_buf_get	-	Get an audit buffer.
1728c2ecf20Sopenharmony_ci *
1738c2ecf20Sopenharmony_ci *	Get an audit buffer, allocate it if necessary.  Return %NULL
1748c2ecf20Sopenharmony_ci *	if out of memory or ERR_PTR(-ESRCH) if tty_audit_exit() has already
1758c2ecf20Sopenharmony_ci *	occurred.  Otherwise, return a new reference to the buffer.
1768c2ecf20Sopenharmony_ci */
1778c2ecf20Sopenharmony_cistatic struct tty_audit_buf *tty_audit_buf_get(void)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	struct tty_audit_buf *buf;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	buf = tty_audit_buf_ref();
1828c2ecf20Sopenharmony_ci	if (buf)
1838c2ecf20Sopenharmony_ci		return buf;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	buf = tty_audit_buf_alloc();
1868c2ecf20Sopenharmony_ci	if (buf == NULL) {
1878c2ecf20Sopenharmony_ci		audit_log_lost("out of memory in TTY auditing");
1888c2ecf20Sopenharmony_ci		return NULL;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	/* Race to use this buffer, free it if another wins */
1928c2ecf20Sopenharmony_ci	if (cmpxchg(&current->signal->tty_audit_buf, NULL, buf) != NULL)
1938c2ecf20Sopenharmony_ci		tty_audit_buf_free(buf);
1948c2ecf20Sopenharmony_ci	return tty_audit_buf_ref();
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/**
1988c2ecf20Sopenharmony_ci *	tty_audit_add_data	-	Add data for TTY auditing.
1998c2ecf20Sopenharmony_ci *
2008c2ecf20Sopenharmony_ci *	Audit @data of @size from @tty, if necessary.
2018c2ecf20Sopenharmony_ci */
2028c2ecf20Sopenharmony_civoid tty_audit_add_data(struct tty_struct *tty, const void *data, size_t size)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	struct tty_audit_buf *buf;
2058c2ecf20Sopenharmony_ci	unsigned int icanon = !!L_ICANON(tty);
2068c2ecf20Sopenharmony_ci	unsigned int audit_tty;
2078c2ecf20Sopenharmony_ci	dev_t dev;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	audit_tty = READ_ONCE(current->signal->audit_tty);
2108c2ecf20Sopenharmony_ci	if (~audit_tty & AUDIT_TTY_ENABLE)
2118c2ecf20Sopenharmony_ci		return;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (unlikely(size == 0))
2148c2ecf20Sopenharmony_ci		return;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (tty->driver->type == TTY_DRIVER_TYPE_PTY
2178c2ecf20Sopenharmony_ci	    && tty->driver->subtype == PTY_TYPE_MASTER)
2188c2ecf20Sopenharmony_ci		return;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	if ((~audit_tty & AUDIT_TTY_LOG_PASSWD) && icanon && !L_ECHO(tty))
2218c2ecf20Sopenharmony_ci		return;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	buf = tty_audit_buf_get();
2248c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(buf))
2258c2ecf20Sopenharmony_ci		return;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	mutex_lock(&buf->mutex);
2288c2ecf20Sopenharmony_ci	dev = MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index;
2298c2ecf20Sopenharmony_ci	if (buf->dev != dev || buf->icanon != icanon) {
2308c2ecf20Sopenharmony_ci		tty_audit_buf_push(buf);
2318c2ecf20Sopenharmony_ci		buf->dev = dev;
2328c2ecf20Sopenharmony_ci		buf->icanon = icanon;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci	do {
2358c2ecf20Sopenharmony_ci		size_t run;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci		run = N_TTY_BUF_SIZE - buf->valid;
2388c2ecf20Sopenharmony_ci		if (run > size)
2398c2ecf20Sopenharmony_ci			run = size;
2408c2ecf20Sopenharmony_ci		memcpy(buf->data + buf->valid, data, run);
2418c2ecf20Sopenharmony_ci		buf->valid += run;
2428c2ecf20Sopenharmony_ci		data += run;
2438c2ecf20Sopenharmony_ci		size -= run;
2448c2ecf20Sopenharmony_ci		if (buf->valid == N_TTY_BUF_SIZE)
2458c2ecf20Sopenharmony_ci			tty_audit_buf_push(buf);
2468c2ecf20Sopenharmony_ci	} while (size != 0);
2478c2ecf20Sopenharmony_ci	mutex_unlock(&buf->mutex);
2488c2ecf20Sopenharmony_ci}
249