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(¤t->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(¤t->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