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