| 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 | } |