1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Creating audit events from TTY input. 4 * 5 * Copyright (C) 2007 Red Hat, Inc. All rights reserved. 6 * 7 * Authors: Miloslav Trmac <mitr@redhat.com> 8 */ 9 10 #include <linux/audit.h> 11 #include <linux/slab.h> 12 #include <linux/tty.h> 13 #include "tty.h" 14 15 #define TTY_AUDIT_BUF_SIZE 4096 16 17 struct tty_audit_buf { 18 struct mutex mutex; /* Protects all data below */ 19 dev_t dev; /* The TTY which the data is from */ 20 bool icanon; 21 size_t valid; 22 u8 *data; /* Allocated size TTY_AUDIT_BUF_SIZE */ 23 }; 24 25 static struct tty_audit_buf *tty_audit_buf_ref(void) 26 { 27 struct tty_audit_buf *buf; 28 29 buf = current->signal->tty_audit_buf; 30 WARN_ON(buf == ERR_PTR(-ESRCH)); 31 return buf; 32 } 33 34 static struct tty_audit_buf *tty_audit_buf_alloc(void) 35 { 36 struct tty_audit_buf *buf; 37 38 buf = kzalloc(sizeof(*buf), GFP_KERNEL); 39 if (!buf) 40 goto err; 41 42 buf->data = kmalloc(TTY_AUDIT_BUF_SIZE, GFP_KERNEL); 43 if (!buf->data) 44 goto err_buf; 45 46 mutex_init(&buf->mutex); 47 48 return buf; 49 50 err_buf: 51 kfree(buf); 52 err: 53 return NULL; 54 } 55 56 static void tty_audit_buf_free(struct tty_audit_buf *buf) 57 { 58 WARN_ON(buf->valid != 0); 59 kfree(buf->data); 60 kfree(buf); 61 } 62 63 static void tty_audit_log(const char *description, dev_t dev, 64 const u8 *data, size_t size) 65 { 66 struct audit_buffer *ab; 67 pid_t pid = task_pid_nr(current); 68 uid_t uid = from_kuid(&init_user_ns, task_uid(current)); 69 uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(current)); 70 unsigned int sessionid = audit_get_sessionid(current); 71 char name[TASK_COMM_LEN]; 72 73 ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_TTY); 74 if (!ab) 75 return; 76 77 audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d minor=%d comm=", 78 description, pid, uid, loginuid, sessionid, 79 MAJOR(dev), MINOR(dev)); 80 get_task_comm(name, current); 81 audit_log_untrustedstring(ab, name); 82 audit_log_format(ab, " data="); 83 audit_log_n_hex(ab, data, size); 84 audit_log_end(ab); 85 } 86 87 /* 88 * tty_audit_buf_push - Push buffered data out 89 * 90 * Generate an audit message from the contents of @buf, which is owned by 91 * the current task. @buf->mutex must be locked. 92 */ 93 static void tty_audit_buf_push(struct tty_audit_buf *buf) 94 { 95 if (buf->valid == 0) 96 return; 97 if (audit_enabled == AUDIT_OFF) { 98 buf->valid = 0; 99 return; 100 } 101 tty_audit_log("tty", buf->dev, buf->data, buf->valid); 102 buf->valid = 0; 103 } 104 105 /** 106 * tty_audit_exit - Handle a task exit 107 * 108 * Make sure all buffered data is written out and deallocate the buffer. 109 * Only needs to be called if current->signal->tty_audit_buf != %NULL. 110 * 111 * The process is single-threaded at this point; no other threads share 112 * current->signal. 113 */ 114 void tty_audit_exit(void) 115 { 116 struct tty_audit_buf *buf; 117 118 buf = xchg(¤t->signal->tty_audit_buf, ERR_PTR(-ESRCH)); 119 if (!buf) 120 return; 121 122 tty_audit_buf_push(buf); 123 tty_audit_buf_free(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 */ 131 void tty_audit_fork(struct signal_struct *sig) 132 { 133 sig->audit_tty = current->signal->audit_tty; 134 } 135 136 /* 137 * tty_audit_tiocsti - Log TIOCSTI 138 */ 139 void tty_audit_tiocsti(const struct tty_struct *tty, u8 ch) 140 { 141 dev_t dev; 142 143 dev = MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index; 144 if (tty_audit_push()) 145 return; 146 147 if (audit_enabled) 148 tty_audit_log("ioctl=TIOCSTI", dev, &ch, 1); 149 } 150 151 /* 152 * tty_audit_push - Flush current's pending audit data 153 * 154 * Returns 0 if success, -EPERM if tty audit is disabled 155 */ 156 int tty_audit_push(void) 157 { 158 struct tty_audit_buf *buf; 159 160 if (~current->signal->audit_tty & AUDIT_TTY_ENABLE) 161 return -EPERM; 162 163 buf = tty_audit_buf_ref(); 164 if (!IS_ERR_OR_NULL(buf)) { 165 mutex_lock(&buf->mutex); 166 tty_audit_buf_push(buf); 167 mutex_unlock(&buf->mutex); 168 } 169 return 0; 170 } 171 172 /* 173 * tty_audit_buf_get - Get an audit buffer. 174 * 175 * Get an audit buffer, allocate it if necessary. Return %NULL 176 * if out of memory or ERR_PTR(-ESRCH) if tty_audit_exit() has already 177 * occurred. Otherwise, return a new reference to the buffer. 178 */ 179 static struct tty_audit_buf *tty_audit_buf_get(void) 180 { 181 struct tty_audit_buf *buf; 182 183 buf = tty_audit_buf_ref(); 184 if (buf) 185 return buf; 186 187 buf = tty_audit_buf_alloc(); 188 if (buf == NULL) { 189 audit_log_lost("out of memory in TTY auditing"); 190 return NULL; 191 } 192 193 /* Race to use this buffer, free it if another wins */ 194 if (cmpxchg(¤t->signal->tty_audit_buf, NULL, buf) != NULL) 195 tty_audit_buf_free(buf); 196 return tty_audit_buf_ref(); 197 } 198 199 /* 200 * tty_audit_add_data - Add data for TTY auditing. 201 * 202 * Audit @data of @size from @tty, if necessary. 203 */ 204 void tty_audit_add_data(const struct tty_struct *tty, const void *data, 205 size_t size) 206 { 207 struct tty_audit_buf *buf; 208 unsigned int audit_tty; 209 bool icanon = L_ICANON(tty); 210 dev_t dev; 211 212 audit_tty = READ_ONCE(current->signal->audit_tty); 213 if (~audit_tty & AUDIT_TTY_ENABLE) 214 return; 215 216 if (unlikely(size == 0)) 217 return; 218 219 if (tty->driver->type == TTY_DRIVER_TYPE_PTY 220 && tty->driver->subtype == PTY_TYPE_MASTER) 221 return; 222 223 if ((~audit_tty & AUDIT_TTY_LOG_PASSWD) && icanon && !L_ECHO(tty)) 224 return; 225 226 buf = tty_audit_buf_get(); 227 if (IS_ERR_OR_NULL(buf)) 228 return; 229 230 mutex_lock(&buf->mutex); 231 dev = MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index; 232 if (buf->dev != dev || buf->icanon != icanon) { 233 tty_audit_buf_push(buf); 234 buf->dev = dev; 235 buf->icanon = icanon; 236 } 237 do { 238 size_t run; 239 240 run = TTY_AUDIT_BUF_SIZE - buf->valid; 241 if (run > size) 242 run = size; 243 memcpy(buf->data + buf->valid, data, run); 244 buf->valid += run; 245 data += run; 246 size -= run; 247 if (buf->valid == TTY_AUDIT_BUF_SIZE) 248 tty_audit_buf_push(buf); 249 } while (size != 0); 250 mutex_unlock(&buf->mutex); 251 } 252