Bug Summary

File:build-scan/../src/journal/journald-audit.c
Warning:line 100, column 40
Potential leak of memory pointed to by 'c'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name journald-audit.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -menable-no-infs -menable-no-nans -menable-unsafe-fp-math -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/12.0.0 -include config.h -I libjournal-core.a.p -I . -I .. -I src/basic -I ../src/basic -I src/shared -I ../src/shared -I src/systemd -I ../src/systemd -I src/journal -I ../src/journal -I src/journal-remote -I ../src/journal-remote -I src/nspawn -I ../src/nspawn -I src/resolve -I ../src/resolve -I src/timesync -I ../src/timesync -I ../src/time-wait-sync -I src/login -I ../src/login -I src/udev -I ../src/udev -I src/libudev -I ../src/libudev -I src/core -I ../src/core -I ../src/libsystemd/sd-bus -I ../src/libsystemd/sd-device -I ../src/libsystemd/sd-hwdb -I ../src/libsystemd/sd-id128 -I ../src/libsystemd/sd-netlink -I ../src/libsystemd/sd-network -I src/libsystemd-network -I ../src/libsystemd-network -D _FILE_OFFSET_BITS=64 -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Wno-format-signedness -Wno-error=nonnull -std=gnu99 -fconst-strings -fdebug-compilation-dir /home/mrc0mmand/repos/@redhat-plumbers/systemd-rhel8/build-scan -ferror-limit 19 -fvisibility hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-07-16-221226-1465241-1 -x c ../src/journal/journald-audit.c
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
12typedef 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
18static 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
55static 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)
;
2
Assuming 'field' is non-null
3
Taking false branch
4
Loop condition is false. Exiting loop
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)
;
5
Assuming 'p' is non-null
6
Taking false branch
7
Loop condition is false. Exiting loop
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)
;
8
Assuming 'iov' is non-null
9
Taking false branch
10
Loop condition is false. Exiting loop
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)
;
11
Assuming 'n_iov' is non-null
12
Taking false branch
13
Loop condition is false. Exiting loop
64
65 /* The kernel formats string fields in one of two formats. */
66
67 if (**p == '"') {
14
Assuming the condition is false
15
Taking false branch
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) {
16
Assuming the condition is true
17
Taking true branch
84 /* Hexadecimal escaping */
85 size_t allocated = 0;
86
87 l = strlen(field);
88 allocated = l + 2;
89 c = malloc(allocated);
18
Memory is allocated
90 if (!c)
19
Assuming 'c' is non-null
20
Taking false branch
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) {
21
Control jumps to the 'default' case at line 94
22
Execution continues on line 94
23
Loop condition is true. Entering loop body
95 int a, b;
96 uint8_t x;
97
98 a = unhexchar(e[0]);
99 if (a < 0)
24
Assuming 'a' is < 0
25
Taking true branch
100 return 0;
26
Potential leak of memory pointed to by 'c'
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
132static 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
136static 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);
1
Calling 'map_string_field_internal'
138}
139
140static 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= */
196static 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= */
229static 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
238static 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
316void 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_4 { 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
400finish:
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
408void 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
455static 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
500int 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}