File: | build-scan/../src/journal/journald-audit.c |
Warning: | line 122, column 25 Potential leak of memory pointed to by 'c' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |||
2 | ||||
3 | #include "alloc-util.h" | |||
4 | #include "audit-type.h" | |||
5 | #include "fd-util.h" | |||
6 | #include "hexdecoct.h" | |||
7 | #include "io-util.h" | |||
8 | #include "journald-audit.h" | |||
9 | #include "missing.h" | |||
10 | #include "string-util.h" | |||
11 | ||||
12 | typedef struct MapField { | |||
13 | const char *audit_field; | |||
14 | const char *journal_field; | |||
15 | int (*map)(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov); | |||
16 | } MapField; | |||
17 | ||||
18 | static int map_simple_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) { | |||
19 | _cleanup_free___attribute__((cleanup(freep))) char *c = NULL((void*)0); | |||
20 | size_t l = 0, allocated = 0; | |||
21 | const char *e; | |||
22 | ||||
23 | assert(field)do { if ((__builtin_expect(!!(!(field)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("field"), "../src/journal/journald-audit.c" , 23, __PRETTY_FUNCTION__); } while (0); | |||
24 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/journal/journald-audit.c", 24, __PRETTY_FUNCTION__); } while (0); | |||
25 | assert(iov)do { if ((__builtin_expect(!!(!(iov)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("iov"), "../src/journal/journald-audit.c" , 25, __PRETTY_FUNCTION__); } while (0); | |||
26 | assert(n_iov)do { if ((__builtin_expect(!!(!(n_iov)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("n_iov"), "../src/journal/journald-audit.c" , 26, __PRETTY_FUNCTION__); } while (0); | |||
27 | ||||
28 | l = strlen(field); | |||
29 | allocated = l + 1; | |||
30 | c = malloc(allocated); | |||
31 | if (!c) | |||
32 | return -ENOMEM12; | |||
33 | ||||
34 | memcpy(c, field, l); | |||
35 | for (e = *p; !IN_SET(*e, 0, ' ')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){0, ' '})/sizeof(int)]; switch(*e) { case 0: case ' ': _found = 1; break; default: break; } _found; }); e++) { | |||
36 | if (!GREEDY_REALLOC(c, allocated, l+2)greedy_realloc((void**) &(c), &(allocated), (l+2), sizeof ((c)[0]))) | |||
37 | return -ENOMEM12; | |||
38 | ||||
39 | c[l++] = *e; | |||
40 | } | |||
41 | ||||
42 | c[l] = 0; | |||
43 | ||||
44 | if (!GREEDY_REALLOC(*iov, *n_iov_allocated, *n_iov + 1)greedy_realloc((void**) &(*iov), &(*n_iov_allocated), (*n_iov + 1), sizeof((*iov)[0]))) | |||
45 | return -ENOMEM12; | |||
46 | ||||
47 | (*iov)[(*n_iov)++] = IOVEC_MAKE(c, l)(struct iovec) { .iov_base = (c), .iov_len = (l) }; | |||
48 | ||||
49 | *p = e; | |||
50 | c = NULL((void*)0); | |||
51 | ||||
52 | return 1; | |||
53 | } | |||
54 | ||||
55 | static int map_string_field_internal(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov, bool_Bool filter_printable) { | |||
56 | _cleanup_free___attribute__((cleanup(freep))) char *c = NULL((void*)0); | |||
57 | const char *s, *e; | |||
58 | size_t l; | |||
59 | ||||
60 | assert(field)do { if ((__builtin_expect(!!(!(field)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("field"), "../src/journal/journald-audit.c" , 60, __PRETTY_FUNCTION__); } while (0); | |||
61 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/journal/journald-audit.c", 61, __PRETTY_FUNCTION__); } while (0); | |||
62 | assert(iov)do { if ((__builtin_expect(!!(!(iov)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("iov"), "../src/journal/journald-audit.c" , 62, __PRETTY_FUNCTION__); } while (0); | |||
63 | assert(n_iov)do { if ((__builtin_expect(!!(!(n_iov)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("n_iov"), "../src/journal/journald-audit.c" , 63, __PRETTY_FUNCTION__); } while (0); | |||
64 | ||||
65 | /* The kernel formats string fields in one of two formats. */ | |||
66 | ||||
67 | if (**p == '"') { | |||
68 | /* Normal quoted syntax */ | |||
69 | s = *p + 1; | |||
70 | e = strchr(s, '"'); | |||
71 | if (!e) | |||
72 | return 0; | |||
73 | ||||
74 | l = strlen(field) + (e - s); | |||
75 | c = malloc(l+1); | |||
76 | if (!c) | |||
77 | return -ENOMEM12; | |||
78 | ||||
79 | *((char*) mempcpy(stpcpy(c, field), s, e - s)) = 0; | |||
80 | ||||
81 | e += 1; | |||
82 | ||||
83 | } else if (unhexchar(**p) >= 0) { | |||
84 | /* Hexadecimal escaping */ | |||
85 | size_t allocated = 0; | |||
86 | ||||
87 | l = strlen(field); | |||
88 | allocated = l + 2; | |||
89 | c = malloc(allocated); | |||
90 | if (!c) | |||
91 | return -ENOMEM12; | |||
92 | ||||
93 | memcpy(c, field, l); | |||
94 | for (e = *p; !IN_SET(*e, 0, ' ')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){0, ' '})/sizeof(int)]; switch(*e) { case 0: case ' ': _found = 1; break; default: break; } _found; }); e += 2) { | |||
95 | int a, b; | |||
96 | uint8_t x; | |||
97 | ||||
98 | a = unhexchar(e[0]); | |||
99 | if (a < 0) | |||
100 | return 0; | |||
101 | ||||
102 | b = unhexchar(e[1]); | |||
103 | if (b < 0) | |||
104 | return 0; | |||
105 | ||||
106 | x = ((uint8_t) a << 4 | (uint8_t) b); | |||
107 | ||||
108 | if (filter_printable && x < (uint8_t) ' ') | |||
109 | x = (uint8_t) ' '; | |||
110 | ||||
111 | if (!GREEDY_REALLOC(c, allocated, l+2)greedy_realloc((void**) &(c), &(allocated), (l+2), sizeof ((c)[0]))) | |||
112 | return -ENOMEM12; | |||
113 | ||||
114 | c[l++] = (char) x; | |||
115 | } | |||
116 | ||||
117 | c[l] = 0; | |||
118 | } else | |||
119 | return 0; | |||
120 | ||||
121 | if (!GREEDY_REALLOC(*iov, *n_iov_allocated, *n_iov + 1)greedy_realloc((void**) &(*iov), &(*n_iov_allocated), (*n_iov + 1), sizeof((*iov)[0]))) | |||
122 | return -ENOMEM12; | |||
| ||||
123 | ||||
124 | (*iov)[(*n_iov)++] = IOVEC_MAKE(c, l)(struct iovec) { .iov_base = (c), .iov_len = (l) }; | |||
125 | ||||
126 | *p = e; | |||
127 | c = NULL((void*)0); | |||
128 | ||||
129 | return 1; | |||
130 | } | |||
131 | ||||
132 | static int map_string_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) { | |||
133 | return map_string_field_internal(field, p, iov, n_iov_allocated, n_iov, false0); | |||
134 | } | |||
135 | ||||
136 | static int map_string_field_printable(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) { | |||
137 | return map_string_field_internal(field, p, iov, n_iov_allocated, n_iov, true1); | |||
| ||||
138 | } | |||
139 | ||||
140 | static int map_generic_field(const char *prefix, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) { | |||
141 | const char *e, *f; | |||
142 | char *c, *t; | |||
143 | int r; | |||
144 | ||||
145 | /* Implements fallback mappings for all fields we don't know */ | |||
146 | ||||
147 | for (e = *p; e < *p + 16; e++) { | |||
148 | ||||
149 | if (IN_SET(*e, 0, ' ')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){0, ' '})/sizeof(int)]; switch(*e) { case 0: case ' ': _found = 1; break; default: break; } _found; })) | |||
150 | return 0; | |||
151 | ||||
152 | if (*e == '=') | |||
153 | break; | |||
154 | ||||
155 | if (!((*e >= 'a' && *e <= 'z') || | |||
156 | (*e >= 'A' && *e <= 'Z') || | |||
157 | (*e >= '0' && *e <= '9') || | |||
158 | IN_SET(*e, '_', '-')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){'_', '-'})/sizeof(int)]; switch(*e) { case '_': case '-': _found = 1; break; default: break; } _found; } ))) | |||
159 | return 0; | |||
160 | } | |||
161 | ||||
162 | if (e <= *p || e >= *p + 16) | |||
163 | return 0; | |||
164 | ||||
165 | c = alloca(strlen(prefix) + (e - *p) + 2)__builtin_alloca (strlen(prefix) + (e - *p) + 2); | |||
166 | ||||
167 | t = stpcpy(c, prefix); | |||
168 | for (f = *p; f < e; f++) { | |||
169 | char x; | |||
170 | ||||
171 | if (*f >= 'a' && *f <= 'z') | |||
172 | x = (*f - 'a') + 'A'; /* uppercase */ | |||
173 | else if (*f == '-') | |||
174 | x = '_'; /* dashes → underscores */ | |||
175 | else | |||
176 | x = *f; | |||
177 | ||||
178 | *(t++) = x; | |||
179 | } | |||
180 | strcpy(t, "="); | |||
181 | ||||
182 | e++; | |||
183 | ||||
184 | r = map_simple_field(c, &e, iov, n_iov_allocated, n_iov); | |||
185 | if (r < 0) | |||
186 | return r; | |||
187 | ||||
188 | *p = e; | |||
189 | return r; | |||
190 | } | |||
191 | ||||
192 | /* Kernel fields are those occurring in the audit string before | |||
193 | * msg='. All of these fields are trusted, hence carry the "_" prefix. | |||
194 | * We try to translate the fields we know into our native names. The | |||
195 | * other's are generically mapped to _AUDIT_FIELD_XYZ= */ | |||
196 | static const MapField map_fields_kernel[] = { | |||
197 | ||||
198 | /* First, we map certain well-known audit fields into native | |||
199 | * well-known fields */ | |||
200 | { "pid=", "_PID=", map_simple_field }, | |||
201 | { "ppid=", "_PPID=", map_simple_field }, | |||
202 | { "uid=", "_UID=", map_simple_field }, | |||
203 | { "euid=", "_EUID=", map_simple_field }, | |||
204 | { "fsuid=", "_FSUID=", map_simple_field }, | |||
205 | { "gid=", "_GID=", map_simple_field }, | |||
206 | { "egid=", "_EGID=", map_simple_field }, | |||
207 | { "fsgid=", "_FSGID=", map_simple_field }, | |||
208 | { "tty=", "_TTY=", map_simple_field }, | |||
209 | { "ses=", "_AUDIT_SESSION=", map_simple_field }, | |||
210 | { "auid=", "_AUDIT_LOGINUID=", map_simple_field }, | |||
211 | { "subj=", "_SELINUX_CONTEXT=", map_simple_field }, | |||
212 | { "comm=", "_COMM=", map_string_field }, | |||
213 | { "exe=", "_EXE=", map_string_field }, | |||
214 | { "proctitle=", "_CMDLINE=", map_string_field_printable }, | |||
215 | ||||
216 | /* Some fields don't map to native well-known fields. However, | |||
217 | * we know that they are string fields, hence let's undo | |||
218 | * string field escaping for them, though we stick to the | |||
219 | * generic field names. */ | |||
220 | { "path=", "_AUDIT_FIELD_PATH=", map_string_field }, | |||
221 | { "dev=", "_AUDIT_FIELD_DEV=", map_string_field }, | |||
222 | { "name=", "_AUDIT_FIELD_NAME=", map_string_field }, | |||
223 | {} | |||
224 | }; | |||
225 | ||||
226 | /* Userspace fields are those occurring in the audit string after | |||
227 | * msg='. All of these fields are untrusted, hence carry no "_" | |||
228 | * prefix. We map the fields we don't know to AUDIT_FIELD_XYZ= */ | |||
229 | static const MapField map_fields_userspace[] = { | |||
230 | { "cwd=", "AUDIT_FIELD_CWD=", map_string_field }, | |||
231 | { "cmd=", "AUDIT_FIELD_CMD=", map_string_field }, | |||
232 | { "acct=", "AUDIT_FIELD_ACCT=", map_string_field }, | |||
233 | { "exe=", "AUDIT_FIELD_EXE=", map_string_field }, | |||
234 | { "comm=", "AUDIT_FIELD_COMM=", map_string_field }, | |||
235 | {} | |||
236 | }; | |||
237 | ||||
238 | static int map_all_fields( | |||
239 | const char *p, | |||
240 | const MapField map_fields[], | |||
241 | const char *prefix, | |||
242 | bool_Bool handle_msg, | |||
243 | struct iovec **iov, | |||
244 | size_t *n_iov_allocated, | |||
245 | size_t *n_iov) { | |||
246 | ||||
247 | int r; | |||
248 | ||||
249 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/journal/journald-audit.c", 249, __PRETTY_FUNCTION__); } while (0); | |||
250 | assert(iov)do { if ((__builtin_expect(!!(!(iov)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("iov"), "../src/journal/journald-audit.c" , 250, __PRETTY_FUNCTION__); } while (0); | |||
251 | assert(n_iov_allocated)do { if ((__builtin_expect(!!(!(n_iov_allocated)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("n_iov_allocated"), "../src/journal/journald-audit.c" , 251, __PRETTY_FUNCTION__); } while (0); | |||
252 | assert(n_iov)do { if ((__builtin_expect(!!(!(n_iov)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("n_iov"), "../src/journal/journald-audit.c" , 252, __PRETTY_FUNCTION__); } while (0); | |||
253 | ||||
254 | for (;;) { | |||
255 | bool_Bool mapped = false0; | |||
256 | const MapField *m; | |||
257 | const char *v; | |||
258 | ||||
259 | p += strspn(p, WHITESPACE" \t\n\r"); | |||
260 | ||||
261 | if (*p == 0) | |||
262 | return 0; | |||
263 | ||||
264 | if (handle_msg) { | |||
265 | v = startswith(p, "msg='"); | |||
266 | if (v) { | |||
267 | const char *e; | |||
268 | char *c; | |||
269 | ||||
270 | /* Userspace message. It's enclosed in | |||
271 | simple quotation marks, is not | |||
272 | escaped, but the last field in the | |||
273 | line, hence let's remove the | |||
274 | quotation mark, and apply the | |||
275 | userspace mapping instead of the | |||
276 | kernel mapping. */ | |||
277 | ||||
278 | e = endswith(v, "'"); | |||
279 | if (!e) | |||
280 | return 0; /* don't continue splitting up if the final quotation mark is missing */ | |||
281 | ||||
282 | c = strndupa(v, e - v)(__extension__ ({ const char *__old = (v); size_t __len = strnlen (__old, (e - v)); char *__new = (char *) __builtin_alloca (__len + 1); __new[__len] = '\0'; (char *) memcpy (__new, __old, __len ); })); | |||
283 | return map_all_fields(c, map_fields_userspace, "AUDIT_FIELD_", false0, iov, n_iov_allocated, n_iov); | |||
284 | } | |||
285 | } | |||
286 | ||||
287 | /* Try to map the kernel fields to our own names */ | |||
288 | for (m = map_fields; m->audit_field; m++) { | |||
289 | v = startswith(p, m->audit_field); | |||
290 | if (!v) | |||
291 | continue; | |||
292 | ||||
293 | r = m->map(m->journal_field, &v, iov, n_iov_allocated, n_iov); | |||
294 | if (r < 0) | |||
295 | return log_debug_errno(r, "Failed to parse audit array: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/journal/journald-audit.c", 295, __func__, "Failed to parse audit array: %m" ) : -abs(_e); }); | |||
296 | ||||
297 | if (r > 0) { | |||
298 | mapped = true1; | |||
299 | p = v; | |||
300 | break; | |||
301 | } | |||
302 | } | |||
303 | ||||
304 | if (!mapped) { | |||
305 | r = map_generic_field(prefix, &p, iov, n_iov_allocated, n_iov); | |||
306 | if (r < 0) | |||
307 | return log_debug_errno(r, "Failed to parse audit array: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/journal/journald-audit.c", 307, __func__, "Failed to parse audit array: %m" ) : -abs(_e); }); | |||
308 | ||||
309 | if (r == 0) | |||
310 | /* Couldn't process as generic field, let's just skip over it */ | |||
311 | p += strcspn(p, WHITESPACE" \t\n\r"); | |||
312 | } | |||
313 | } | |||
314 | } | |||
315 | ||||
316 | void process_audit_string(Server *s, int type, const char *data, size_t size) { | |||
317 | size_t n_iov_allocated = 0, n_iov = 0, z; | |||
318 | _cleanup_free___attribute__((cleanup(freep))) struct iovec *iov = NULL((void*)0); | |||
319 | uint64_t seconds, msec, id; | |||
320 | const char *p, *type_name; | |||
321 | char id_field[sizeof("_AUDIT_ID=") + DECIMAL_STR_MAX(uint64_t)(2+(sizeof(uint64_t) <= 1 ? 3 : sizeof(uint64_t) <= 2 ? 5 : sizeof(uint64_t) <= 4 ? 10 : sizeof(uint64_t) <= 8 ? 20 : sizeof(int[-2*(sizeof(uint64_t) > 8)])))], | |||
322 | type_field[sizeof("_AUDIT_TYPE=") + DECIMAL_STR_MAX(int)(2+(sizeof(int) <= 1 ? 3 : sizeof(int) <= 2 ? 5 : sizeof (int) <= 4 ? 10 : sizeof(int) <= 8 ? 20 : sizeof(int[-2 *(sizeof(int) > 8)])))], | |||
323 | source_time_field[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)(2+(sizeof(usec_t) <= 1 ? 3 : sizeof(usec_t) <= 2 ? 5 : sizeof(usec_t) <= 4 ? 10 : sizeof(usec_t) <= 8 ? 20 : sizeof (int[-2*(sizeof(usec_t) > 8)])))]; | |||
324 | char *m, *type_field_name; | |||
325 | int k; | |||
326 | ||||
327 | assert(s)do { if ((__builtin_expect(!!(!(s)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("s"), "../src/journal/journald-audit.c", 327, __PRETTY_FUNCTION__); } while (0); | |||
328 | ||||
329 | if (size <= 0) | |||
330 | return; | |||
331 | ||||
332 | if (!data) | |||
333 | return; | |||
334 | ||||
335 | /* Note that the input buffer is NUL terminated, but let's | |||
336 | * check whether there is a spurious NUL byte */ | |||
337 | if (memchr(data, 0, size)) | |||
338 | return; | |||
339 | ||||
340 | p = startswith(data, "audit"); | |||
341 | if (!p) | |||
342 | return; | |||
343 | ||||
344 | k = 0; | |||
345 | if (sscanf(p, "(%" PRIu64"l" "u" ".%" PRIu64"l" "u" ":%" PRIu64"l" "u" "):%n", | |||
346 | &seconds, | |||
347 | &msec, | |||
348 | &id, | |||
349 | &k) != 3 || k == 0) | |||
350 | return; | |||
351 | ||||
352 | p += k; | |||
353 | p += strspn(p, WHITESPACE" \t\n\r"); | |||
354 | ||||
355 | if (isempty(p)) | |||
356 | return; | |||
357 | ||||
358 | n_iov_allocated = N_IOVEC_META_FIELDS22 + 8; | |||
359 | iov = new(struct iovec, n_iov_allocated)((struct iovec*) malloc_multiply(sizeof(struct iovec), (n_iov_allocated ))); | |||
360 | if (!iov) { | |||
361 | log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/journal/journald-audit.c" , 361, __func__); | |||
362 | return; | |||
363 | } | |||
364 | ||||
365 | iov[n_iov++] = IOVEC_MAKE_STRING("_TRANSPORT=audit")(struct iovec) { .iov_base = ((char*) "_TRANSPORT=audit"), .iov_len = (strlen("_TRANSPORT=audit")) }; | |||
366 | ||||
367 | sprintf(source_time_field, "_SOURCE_REALTIME_TIMESTAMP=%" PRIu64"l" "u", | |||
368 | (usec_t) seconds * USEC_PER_SEC((usec_t) 1000000ULL) + (usec_t) msec * USEC_PER_MSEC((usec_t) 1000ULL)); | |||
369 | iov[n_iov++] = IOVEC_MAKE_STRING(source_time_field)(struct iovec) { .iov_base = ((char*) source_time_field), .iov_len = (strlen(source_time_field)) }; | |||
370 | ||||
371 | sprintf(type_field, "_AUDIT_TYPE=%i", type); | |||
372 | iov[n_iov++] = IOVEC_MAKE_STRING(type_field)(struct iovec) { .iov_base = ((char*) type_field), .iov_len = (strlen(type_field)) }; | |||
373 | ||||
374 | sprintf(id_field, "_AUDIT_ID=%" PRIu64"l" "u", id); | |||
375 | iov[n_iov++] = IOVEC_MAKE_STRING(id_field)(struct iovec) { .iov_base = ((char*) id_field), .iov_len = ( strlen(id_field)) }; | |||
376 | ||||
377 | assert_cc(4 == LOG_FAC(LOG_AUTH))GCC diagnostic push
; GCC diagnostic ignored "-Wdeclaration-after-statement" ; struct _assert_struct_5 { char x[(4 == ((((4<<3)) & 0x03f8) >> 3)) ? 0 : -1]; }; GCC diagnostic pop ; | |||
378 | iov[n_iov++] = IOVEC_MAKE_STRING("SYSLOG_FACILITY=4")(struct iovec) { .iov_base = ((char*) "SYSLOG_FACILITY=4"), . iov_len = (strlen("SYSLOG_FACILITY=4")) }; | |||
379 | iov[n_iov++] = IOVEC_MAKE_STRING("SYSLOG_IDENTIFIER=audit")(struct iovec) { .iov_base = ((char*) "SYSLOG_IDENTIFIER=audit" ), .iov_len = (strlen("SYSLOG_IDENTIFIER=audit")) }; | |||
380 | ||||
381 | type_name = audit_type_name_alloca(type)({ const char *_s_; _s_ = audit_type_to_string(type); if (!_s_ ) { _s_ = __builtin_alloca ((sizeof("""AUDIT""") - 1) + (2+(sizeof (int) <= 1 ? 3 : sizeof(int) <= 2 ? 5 : sizeof(int) <= 4 ? 10 : sizeof(int) <= 8 ? 20 : sizeof(int[-2*(sizeof(int ) > 8)])))); sprintf((char*) _s_, "AUDIT%04i", type); } _s_ ; }); | |||
382 | ||||
383 | type_field_name = strjoina("_AUDIT_TYPE_NAME=", type_name)({ const char *_appendees_[] = { "_AUDIT_TYPE_NAME=", type_name }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
384 | iov[n_iov++] = IOVEC_MAKE_STRING(type_field_name)(struct iovec) { .iov_base = ((char*) type_field_name), .iov_len = (strlen(type_field_name)) }; | |||
385 | ||||
386 | m = strjoina("MESSAGE=", type_name, " ", p)({ const char *_appendees_[] = { "MESSAGE=", type_name, " ", p }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
387 | iov[n_iov++] = IOVEC_MAKE_STRING(m)(struct iovec) { .iov_base = ((char*) m), .iov_len = (strlen( m)) }; | |||
388 | ||||
389 | z = n_iov; | |||
390 | ||||
391 | map_all_fields(p, map_fields_kernel, "_AUDIT_FIELD_", true1, &iov, &n_iov_allocated, &n_iov); | |||
392 | ||||
393 | if (!GREEDY_REALLOC(iov, n_iov_allocated, n_iov + N_IOVEC_META_FIELDS)greedy_realloc((void**) &(iov), &(n_iov_allocated), ( n_iov + 22), sizeof((iov)[0]))) { | |||
394 | log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/journal/journald-audit.c" , 394, __func__); | |||
395 | goto finish; | |||
396 | } | |||
397 | ||||
398 | server_dispatch_message(s, iov, n_iov, n_iov_allocated, NULL((void*)0), NULL((void*)0), LOG_NOTICE5, 0); | |||
399 | ||||
400 | finish: | |||
401 | /* free() all entries that map_all_fields() added. All others | |||
402 | * are allocated on the stack or are constant. */ | |||
403 | ||||
404 | for (; z < n_iov; z++) | |||
405 | free(iov[z].iov_base); | |||
406 | } | |||
407 | ||||
408 | void server_process_audit_message( | |||
409 | Server *s, | |||
410 | const void *buffer, | |||
411 | size_t buffer_size, | |||
412 | const struct ucred *ucred, | |||
413 | const union sockaddr_union *sa, | |||
414 | socklen_t salen) { | |||
415 | ||||
416 | const struct nlmsghdr *nl = buffer; | |||
417 | ||||
418 | assert(s)do { if ((__builtin_expect(!!(!(s)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("s"), "../src/journal/journald-audit.c", 418, __PRETTY_FUNCTION__); } while (0); | |||
419 | ||||
420 | if (buffer_size < ALIGN(sizeof(struct nlmsghdr))(((sizeof(struct nlmsghdr)) + 7) & ~7)) | |||
421 | return; | |||
422 | ||||
423 | assert(buffer)do { if ((__builtin_expect(!!(!(buffer)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("buffer"), "../src/journal/journald-audit.c" , 423, __PRETTY_FUNCTION__); } while (0); | |||
424 | ||||
425 | /* Filter out fake data */ | |||
426 | if (!sa || | |||
427 | salen != sizeof(struct sockaddr_nl) || | |||
428 | sa->nl.nl_family != AF_NETLINK16 || | |||
429 | sa->nl.nl_pid != 0) { | |||
430 | log_debug("Audit netlink message from invalid sender.")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/journal/journald-audit.c", 430, __func__, "Audit netlink message from invalid sender." ) : -abs(_e); }); | |||
431 | return; | |||
432 | } | |||
433 | ||||
434 | if (!ucred || ucred->pid != 0) { | |||
435 | log_debug("Audit netlink message with invalid credentials.")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/journal/journald-audit.c", 435, __func__, "Audit netlink message with invalid credentials." ) : -abs(_e); }); | |||
436 | return; | |||
437 | } | |||
438 | ||||
439 | if (!NLMSG_OK(nl, buffer_size)((buffer_size) >= (int)sizeof(struct nlmsghdr) && ( nl)->nlmsg_len >= sizeof(struct nlmsghdr) && (nl )->nlmsg_len <= (buffer_size))) { | |||
440 | log_error("Audit netlink message truncated.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/journal/journald-audit.c", 440, __func__, "Audit netlink message truncated." ) : -abs(_e); }); | |||
441 | return; | |||
442 | } | |||
443 | ||||
444 | /* Ignore special Netlink messages */ | |||
445 | if (IN_SET(nl->nlmsg_type, NLMSG_NOOP, NLMSG_ERROR)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){0x1, 0x2})/sizeof(int)]; switch(nl->nlmsg_type ) { case 0x1: case 0x2: _found = 1; break; default: break; } _found ; })) | |||
446 | return; | |||
447 | ||||
448 | /* Except AUDIT_USER, all messsages below AUDIT_FIRST_USER_MSG are control messages, let's ignore those */ | |||
449 | if (nl->nlmsg_type < AUDIT_FIRST_USER_MSG1100 && nl->nlmsg_type != AUDIT_USER1005) | |||
450 | return; | |||
451 | ||||
452 | process_audit_string(s, nl->nlmsg_type, NLMSG_DATA(nl)((void*)(((char*)nl) + ((0) + ((int) ( ((sizeof(struct nlmsghdr ))+4U -1) & ~(4U -1) ))))), nl->nlmsg_len - ALIGN(sizeof(struct nlmsghdr))(((sizeof(struct nlmsghdr)) + 7) & ~7)); | |||
453 | } | |||
454 | ||||
455 | static int enable_audit(int fd, bool_Bool b) { | |||
456 | struct { | |||
457 | union { | |||
458 | struct nlmsghdr header; | |||
459 | uint8_t header_space[NLMSG_HDRLEN((int) ( ((sizeof(struct nlmsghdr))+4U -1) & ~(4U -1) ))]; | |||
460 | }; | |||
461 | struct audit_status body; | |||
462 | } _packed___attribute__ ((packed)) request = { | |||
463 | .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct audit_status))((sizeof(struct audit_status)) + ((int) ( ((sizeof(struct nlmsghdr ))+4U -1) & ~(4U -1) ))), | |||
464 | .header.nlmsg_type = AUDIT_SET1001, | |||
465 | .header.nlmsg_flags = NLM_F_REQUEST0x01, | |||
466 | .header.nlmsg_seq = 1, | |||
467 | .header.nlmsg_pid = 0, | |||
468 | .body.mask = AUDIT_STATUS_ENABLED0x0001, | |||
469 | .body.enabled = b, | |||
470 | }; | |||
471 | union sockaddr_union sa = { | |||
472 | .nl.nl_family = AF_NETLINK16, | |||
473 | .nl.nl_pid = 0, | |||
474 | }; | |||
475 | struct iovec iovec = { | |||
476 | .iov_base = &request, | |||
477 | .iov_len = NLMSG_LENGTH(sizeof(struct audit_status))((sizeof(struct audit_status)) + ((int) ( ((sizeof(struct nlmsghdr ))+4U -1) & ~(4U -1) ))), | |||
478 | }; | |||
479 | struct msghdr mh = { | |||
480 | .msg_iov = &iovec, | |||
481 | .msg_iovlen = 1, | |||
482 | .msg_name = &sa.sa, | |||
483 | .msg_namelen = sizeof(sa.nl), | |||
484 | }; | |||
485 | ||||
486 | ssize_t n; | |||
487 | ||||
488 | n = sendmsg(fd, &mh, MSG_NOSIGNALMSG_NOSIGNAL); | |||
489 | if (n < 0) | |||
490 | return -errno(*__errno_location ()); | |||
491 | if (n != NLMSG_LENGTH(sizeof(struct audit_status))((sizeof(struct audit_status)) + ((int) ( ((sizeof(struct nlmsghdr ))+4U -1) & ~(4U -1) )))) | |||
492 | return -EIO5; | |||
493 | ||||
494 | /* We don't wait for the result here, we can't do anything | |||
495 | * about it anyway */ | |||
496 | ||||
497 | return 0; | |||
498 | } | |||
499 | ||||
500 | int server_open_audit(Server *s) { | |||
501 | static const int one = 1; | |||
502 | int r; | |||
503 | ||||
504 | if (s->audit_fd < 0) { | |||
505 | static const union sockaddr_union sa = { | |||
506 | .nl.nl_family = AF_NETLINK16, | |||
507 | .nl.nl_pid = 0, | |||
508 | .nl.nl_groups = AUDIT_NLGRP_READLOG, | |||
509 | }; | |||
510 | ||||
511 | s->audit_fd = socket(AF_NETLINK16, SOCK_RAWSOCK_RAW|SOCK_CLOEXECSOCK_CLOEXEC|SOCK_NONBLOCKSOCK_NONBLOCK, NETLINK_AUDIT9); | |||
512 | if (s->audit_fd < 0) { | |||
513 | if (IN_SET(errno, EAFNOSUPPORT, EPROTONOSUPPORT)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){97, 93})/sizeof(int)]; switch((*__errno_location ())) { case 97: case 93: _found = 1; break; default: break; } _found; })) | |||
514 | log_debug("Audit not supported in the kernel.")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/journal/journald-audit.c", 514, __func__, "Audit not supported in the kernel." ) : -abs(_e); }); | |||
515 | else | |||
516 | log_warning_errno(errno, "Failed to create audit socket, ignoring: %m")({ int _level = ((4)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/journal/journald-audit.c", 516, __func__ , "Failed to create audit socket, ignoring: %m") : -abs(_e); } ); | |||
517 | ||||
518 | return 0; | |||
519 | } | |||
520 | ||||
521 | if (bind(s->audit_fd, &sa.sa, sizeof(sa.nl)) < 0) { | |||
522 | log_warning_errno(errno,({ int _level = ((4)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/journal/journald-audit.c", 525, __func__ , "Failed to join audit multicast group. " "The kernel is probably too old or multicast reading is not supported. " "Ignoring: %m") : -abs(_e); }) | |||
523 | "Failed to join audit multicast group. "({ int _level = ((4)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/journal/journald-audit.c", 525, __func__ , "Failed to join audit multicast group. " "The kernel is probably too old or multicast reading is not supported. " "Ignoring: %m") : -abs(_e); }) | |||
524 | "The kernel is probably too old or multicast reading is not supported. "({ int _level = ((4)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/journal/journald-audit.c", 525, __func__ , "Failed to join audit multicast group. " "The kernel is probably too old or multicast reading is not supported. " "Ignoring: %m") : -abs(_e); }) | |||
525 | "Ignoring: %m")({ int _level = ((4)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/journal/journald-audit.c", 525, __func__ , "Failed to join audit multicast group. " "The kernel is probably too old or multicast reading is not supported. " "Ignoring: %m") : -abs(_e); }); | |||
526 | s->audit_fd = safe_close(s->audit_fd); | |||
527 | return 0; | |||
528 | } | |||
529 | } else | |||
530 | fd_nonblock(s->audit_fd, 1); | |||
531 | ||||
532 | r = setsockopt(s->audit_fd, SOL_SOCKET1, SO_PASSCRED16, &one, sizeof(one)); | |||
533 | if (r < 0) | |||
534 | return log_error_errno(errno, "Failed to set SO_PASSCRED on audit socket: %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/journal/journald-audit.c", 534, __func__ , "Failed to set SO_PASSCRED on audit socket: %m") : -abs(_e) ; }); | |||
535 | ||||
536 | r = sd_event_add_io(s->event, &s->audit_event_source, s->audit_fd, EPOLLINEPOLLIN, server_process_datagram, s); | |||
537 | if (r < 0) | |||
538 | return log_error_errno(r, "Failed to add audit fd to event loop: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/journal/journald-audit.c", 538, __func__, "Failed to add audit fd to event loop: %m" ) : -abs(_e); }); | |||
539 | ||||
540 | /* We are listening now, try to enable audit */ | |||
541 | r = enable_audit(s->audit_fd, true1); | |||
542 | if (r < 0) | |||
543 | log_warning_errno(r, "Failed to issue audit enable call: %m")({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/journal/journald-audit.c", 543, __func__, "Failed to issue audit enable call: %m" ) : -abs(_e); }); | |||
544 | ||||
545 | return 0; | |||
546 | } |