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 | struct tty_audit_buf { |
16 | struct mutex mutex; /* Protects all data below */ |
17 | dev_t dev; /* The TTY which the data is from */ |
18 | bool icanon; |
19 | size_t valid; |
20 | u8 *data; /* Allocated size N_TTY_BUF_SIZE */ |
21 | }; |
22 | |
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 | |
32 | static struct tty_audit_buf *tty_audit_buf_alloc(void) |
33 | { |
34 | struct tty_audit_buf *buf; |
35 | |
36 | buf = kzalloc(size: sizeof(*buf), GFP_KERNEL); |
37 | if (!buf) |
38 | goto err; |
39 | |
40 | buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL); |
41 | if (!buf->data) |
42 | goto err_buf; |
43 | |
44 | mutex_init(&buf->mutex); |
45 | |
46 | return buf; |
47 | |
48 | err_buf: |
49 | kfree(objp: 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); |
57 | kfree(objp: buf->data); |
58 | kfree(objp: buf); |
59 | } |
60 | |
61 | static void tty_audit_log(const char *description, dev_t dev, |
62 | const u8 *data, size_t size) |
63 | { |
64 | struct audit_buffer *ab; |
65 | pid_t pid = task_pid_nr(current); |
66 | uid_t uid = from_kuid(to: &init_user_ns, task_uid(current)); |
67 | uid_t loginuid = from_kuid(to: &init_user_ns, uid: audit_get_loginuid(current)); |
68 | unsigned int sessionid = audit_get_sessionid(current); |
69 | char name[TASK_COMM_LEN]; |
70 | |
71 | ab = audit_log_start(ctx: audit_context(), GFP_KERNEL, AUDIT_TTY); |
72 | if (!ab) |
73 | return; |
74 | |
75 | audit_log_format(ab, fmt: "%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, string: name); |
80 | audit_log_format(ab, fmt: " data=" ); |
81 | audit_log_n_hex(ab, buf: data, len: size); |
82 | audit_log_end(ab); |
83 | } |
84 | |
85 | /* |
86 | * tty_audit_buf_push - Push buffered data out |
87 | * |
88 | * Generate an audit message from the contents of @buf, which is owned by |
89 | * the current task. @buf->mutex must be locked. |
90 | */ |
91 | static void tty_audit_buf_push(struct tty_audit_buf *buf) |
92 | { |
93 | if (buf->valid == 0) |
94 | return; |
95 | if (audit_enabled == AUDIT_OFF) { |
96 | buf->valid = 0; |
97 | return; |
98 | } |
99 | tty_audit_log(description: "tty" , dev: buf->dev, data: buf->data, size: buf->valid); |
100 | buf->valid = 0; |
101 | } |
102 | |
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. |
108 | * |
109 | * The process is single-threaded at this point; no other threads share |
110 | * current->signal. |
111 | */ |
112 | void tty_audit_exit(void) |
113 | { |
114 | struct tty_audit_buf *buf; |
115 | |
116 | buf = xchg(¤t->signal->tty_audit_buf, ERR_PTR(-ESRCH)); |
117 | if (!buf) |
118 | return; |
119 | |
120 | tty_audit_buf_push(buf); |
121 | tty_audit_buf_free(buf); |
122 | } |
123 | |
124 | /* |
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 | { |
131 | sig->audit_tty = current->signal->audit_tty; |
132 | } |
133 | |
134 | /* |
135 | * tty_audit_tiocsti - Log TIOCSTI |
136 | */ |
137 | void tty_audit_tiocsti(const struct tty_struct *tty, u8 ch) |
138 | { |
139 | dev_t dev; |
140 | |
141 | dev = MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index; |
142 | if (tty_audit_push()) |
143 | return; |
144 | |
145 | if (audit_enabled) |
146 | tty_audit_log(description: "ioctl=TIOCSTI" , dev, data: &ch, size: 1); |
147 | } |
148 | |
149 | /* |
150 | * tty_audit_push - Flush current's pending audit data |
151 | * |
152 | * Returns 0 if success, -EPERM if tty audit is disabled |
153 | */ |
154 | int tty_audit_push(void) |
155 | { |
156 | struct tty_audit_buf *buf; |
157 | |
158 | if (~current->signal->audit_tty & AUDIT_TTY_ENABLE) |
159 | return -EPERM; |
160 | |
161 | buf = tty_audit_buf_ref(); |
162 | if (!IS_ERR_OR_NULL(ptr: buf)) { |
163 | mutex_lock(&buf->mutex); |
164 | tty_audit_buf_push(buf); |
165 | mutex_unlock(lock: &buf->mutex); |
166 | } |
167 | return 0; |
168 | } |
169 | |
170 | /* |
171 | * tty_audit_buf_get - Get an audit buffer. |
172 | * |
173 | * Get an audit buffer, allocate it if necessary. Return %NULL |
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. |
176 | */ |
177 | static struct tty_audit_buf *tty_audit_buf_get(void) |
178 | { |
179 | struct tty_audit_buf *buf; |
180 | |
181 | buf = tty_audit_buf_ref(); |
182 | if (buf) |
183 | return buf; |
184 | |
185 | buf = tty_audit_buf_alloc(); |
186 | if (buf == NULL) { |
187 | audit_log_lost(message: "out of memory in TTY auditing" ); |
188 | return NULL; |
189 | } |
190 | |
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); |
194 | return tty_audit_buf_ref(); |
195 | } |
196 | |
197 | /* |
198 | * tty_audit_add_data - Add data for TTY auditing. |
199 | * |
200 | * Audit @data of @size from @tty, if necessary. |
201 | */ |
202 | void tty_audit_add_data(const struct tty_struct *tty, const void *data, |
203 | size_t size) |
204 | { |
205 | struct tty_audit_buf *buf; |
206 | unsigned int audit_tty; |
207 | bool icanon = L_ICANON(tty); |
208 | dev_t dev; |
209 | |
210 | audit_tty = READ_ONCE(current->signal->audit_tty); |
211 | if (~audit_tty & AUDIT_TTY_ENABLE) |
212 | return; |
213 | |
214 | if (unlikely(size == 0)) |
215 | return; |
216 | |
217 | if (tty->driver->type == TTY_DRIVER_TYPE_PTY |
218 | && tty->driver->subtype == PTY_TYPE_MASTER) |
219 | return; |
220 | |
221 | if ((~audit_tty & AUDIT_TTY_LOG_PASSWD) && icanon && !L_ECHO(tty)) |
222 | return; |
223 | |
224 | buf = tty_audit_buf_get(); |
225 | if (IS_ERR_OR_NULL(ptr: buf)) |
226 | return; |
227 | |
228 | mutex_lock(&buf->mutex); |
229 | dev = MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index; |
230 | if (buf->dev != dev || buf->icanon != icanon) { |
231 | tty_audit_buf_push(buf); |
232 | buf->dev = dev; |
233 | buf->icanon = icanon; |
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) |
246 | tty_audit_buf_push(buf); |
247 | } while (size != 0); |
248 | mutex_unlock(lock: &buf->mutex); |
249 | } |
250 | |