1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "alloc-util.h"
4 #include "audit-type.h"
8 #include "journald-audit.h"
10 #include "string-util.h"
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
);
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_
char *c
= NULL
;
20 size_t l
= 0, allocated
= 0;
30 c
= malloc(allocated
);
35 for (e
= *p
; !IN_SET(*e
, 0, ' '); e
++) {
36 if (!GREEDY_REALLOC(c
, allocated
, l
+2))
44 if (!GREEDY_REALLOC(*iov
, *n_iov_allocated
, *n_iov
+ 1))
47 (*iov
)[(*n_iov
)++] = IOVEC_MAKE(c
, l
);
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 filter_printable
) {
56 _cleanup_free_
char *c
= NULL
;
65 /* The kernel formats string fields in one of two formats. */
68 /* Normal quoted syntax */
74 l
= strlen(field
) + (e
- s
);
79 *((char*) mempcpy(stpcpy(c
, field
), s
, e
- s
)) = 0;
83 } else if (unhexchar(**p
) >= 0) {
84 /* Hexadecimal escaping */
89 c
= malloc(allocated
);
94 for (e
= *p
; !IN_SET(*e
, 0, ' '); e
+= 2) {
106 x
= ((uint8_t) a
<< 4 | (uint8_t) b
);
108 if (filter_printable
&& x
< (uint8_t) ' ')
111 if (!GREEDY_REALLOC(c
, allocated
, l
+2))
121 if (!GREEDY_REALLOC(*iov
, *n_iov_allocated
, *n_iov
+ 1))
124 (*iov
)[(*n_iov
)++] = IOVEC_MAKE(c
, l
);
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
, false);
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
, true);
140 static int map_generic_field(const char *prefix
, const char **p
, struct iovec
**iov
, size_t *n_iov_allocated
, size_t *n_iov
) {
145 /* Implements fallback mappings for all fields we don't know */
147 for (e
= *p
; e
< *p
+ 16; e
++) {
149 if (IN_SET(*e
, 0, ' '))
155 if (!((*e
>= 'a' && *e
<= 'z') ||
156 (*e
>= 'A' && *e
<= 'Z') ||
157 (*e
>= '0' && *e
<= '9') ||
158 IN_SET(*e
, '_', '-')))
162 if (e
<= *p
|| e
>= *p
+ 16)
165 c
= alloca(strlen(prefix
) + (e
- *p
) + 2);
167 t
= stpcpy(c
, prefix
);
168 for (f
= *p
; f
< e
; f
++) {
171 if (*f
>= 'a' && *f
<= 'z')
172 x
= (*f
- 'a') + 'A'; /* uppercase */
174 x
= '_'; /* dashes → underscores */
184 r
= map_simple_field(c
, &e
, iov
, n_iov_allocated
, n_iov
);
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
[] = {
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
},
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
},
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
},
238 static int map_all_fields(
240 const MapField map_fields
[],
244 size_t *n_iov_allocated
,
251 assert(n_iov_allocated
);
259 p
+= strspn(p
, WHITESPACE
);
265 v
= startswith(p
, "msg='");
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
278 e
= endswith(v
, "'");
280 return 0; /* don't continue splitting up if the final quotation mark is missing */
282 c
= strndupa(v
, e
- v
);
283 return map_all_fields(c
, map_fields_userspace
, "AUDIT_FIELD_", false, iov
, n_iov_allocated
, n_iov
);
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
);
293 r
= m
->map(m
->journal_field
, &v
, iov
, n_iov_allocated
, n_iov
);
295 return log_debug_errno(r
, "Failed to parse audit array: %m");
305 r
= map_generic_field(prefix
, &p
, iov
, n_iov_allocated
, n_iov
);
307 return log_debug_errno(r
, "Failed to parse audit array: %m");
310 /* Couldn't process as generic field, let's just skip over it */
311 p
+= strcspn(p
, WHITESPACE
);
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_
struct iovec
*iov
= NULL
;
319 uint64_t seconds
, msec
, id
;
320 const char *p
, *type_name
;
321 char id_field
[sizeof("_AUDIT_ID=") + DECIMAL_STR_MAX(uint64_t)],
322 type_field
[sizeof("_AUDIT_TYPE=") + DECIMAL_STR_MAX(int)],
323 source_time_field
[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t
)];
324 char *m
, *type_field_name
;
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
))
340 p
= startswith(data
, "audit");
344 if (sscanf(p
, "(%" PRIu64
".%" PRIu64
":%" PRIu64
"):%n",
352 p
+= strspn(p
, WHITESPACE
);
357 n_iov_allocated
= N_IOVEC_META_FIELDS
+ 8;
358 iov
= new(struct iovec
, n_iov_allocated
);
364 iov
[n_iov
++] = IOVEC_MAKE_STRING("_TRANSPORT=audit");
366 sprintf(source_time_field
, "_SOURCE_REALTIME_TIMESTAMP=%" PRIu64
,
367 (usec_t
) seconds
* USEC_PER_SEC
+ (usec_t
) msec
* USEC_PER_MSEC
);
368 iov
[n_iov
++] = IOVEC_MAKE_STRING(source_time_field
);
370 sprintf(type_field
, "_AUDIT_TYPE=%i", type
);
371 iov
[n_iov
++] = IOVEC_MAKE_STRING(type_field
);
373 sprintf(id_field
, "_AUDIT_ID=%" PRIu64
, id
);
374 iov
[n_iov
++] = IOVEC_MAKE_STRING(id_field
);
376 assert_cc(4 == LOG_FAC(LOG_AUTH
));
377 iov
[n_iov
++] = IOVEC_MAKE_STRING("SYSLOG_FACILITY=4");
378 iov
[n_iov
++] = IOVEC_MAKE_STRING("SYSLOG_IDENTIFIER=audit");
380 type_name
= audit_type_name_alloca(type
);
382 type_field_name
= strjoina("_AUDIT_TYPE_NAME=", type_name
);
383 iov
[n_iov
++] = IOVEC_MAKE_STRING(type_field_name
);
385 m
= strjoina("MESSAGE=", type_name
, " ", p
);
386 iov
[n_iov
++] = IOVEC_MAKE_STRING(m
);
390 map_all_fields(p
, map_fields_kernel
, "_AUDIT_FIELD_", true, &iov
, &n_iov_allocated
, &n_iov
);
392 if (!GREEDY_REALLOC(iov
, n_iov_allocated
, n_iov
+ N_IOVEC_META_FIELDS
)) {
397 server_dispatch_message(s
, iov
, n_iov
, n_iov_allocated
, NULL
, NULL
, LOG_NOTICE
, 0);
400 /* free() all entries that map_all_fields() added. All others
401 * are allocated on the stack or are constant. */
403 for (; z
< n_iov
; z
++)
404 free(iov
[z
].iov_base
);
407 void server_process_audit_message(
411 const struct ucred
*ucred
,
412 const union sockaddr_union
*sa
,
415 const struct nlmsghdr
*nl
= buffer
;
419 if (buffer_size
< ALIGN(sizeof(struct nlmsghdr
)))
424 /* Filter out fake data */
426 salen
!= sizeof(struct sockaddr_nl
) ||
427 sa
->nl
.nl_family
!= AF_NETLINK
||
428 sa
->nl
.nl_pid
!= 0) {
429 log_debug("Audit netlink message from invalid sender.");
433 if (!ucred
|| ucred
->pid
!= 0) {
434 log_debug("Audit netlink message with invalid credentials.");
438 if (!NLMSG_OK(nl
, buffer_size
)) {
439 log_error("Audit netlink message truncated.");
443 /* Ignore special Netlink messages */
444 if (IN_SET(nl
->nlmsg_type
, NLMSG_NOOP
, NLMSG_ERROR
))
447 /* Except AUDIT_USER, all messsages below AUDIT_FIRST_USER_MSG are control messages, let's ignore those */
448 if (nl
->nlmsg_type
< AUDIT_FIRST_USER_MSG
&& nl
->nlmsg_type
!= AUDIT_USER
)
451 process_audit_string(s
, nl
->nlmsg_type
, NLMSG_DATA(nl
), nl
->nlmsg_len
- ALIGN(sizeof(struct nlmsghdr
)));
454 static int enable_audit(int fd
, bool b
) {
457 struct nlmsghdr header
;
458 uint8_t header_space
[NLMSG_HDRLEN
];
460 struct audit_status body
;
461 } _packed_ request
= {
462 .header
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct audit_status
)),
463 .header
.nlmsg_type
= AUDIT_SET
,
464 .header
.nlmsg_flags
= NLM_F_REQUEST
,
465 .header
.nlmsg_seq
= 1,
466 .header
.nlmsg_pid
= 0,
467 .body
.mask
= AUDIT_STATUS_ENABLED
,
470 union sockaddr_union sa
= {
471 .nl
.nl_family
= AF_NETLINK
,
474 struct iovec iovec
= {
475 .iov_base
= &request
,
476 .iov_len
= NLMSG_LENGTH(sizeof(struct audit_status
)),
482 .msg_namelen
= sizeof(sa
.nl
),
487 n
= sendmsg(fd
, &mh
, MSG_NOSIGNAL
);
490 if (n
!= NLMSG_LENGTH(sizeof(struct audit_status
)))
493 /* We don't wait for the result here, we can't do anything
499 int server_open_audit(Server
*s
) {
502 if (s
->audit_fd
< 0) {
503 static const union sockaddr_union sa
= {
504 .nl
.nl_family
= AF_NETLINK
,
506 .nl
.nl_groups
= AUDIT_NLGRP_READLOG
,
509 s
->audit_fd
= socket(AF_NETLINK
, SOCK_RAW
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, NETLINK_AUDIT
);
510 if (s
->audit_fd
< 0) {
511 if (IN_SET(errno
, EAFNOSUPPORT
, EPROTONOSUPPORT
))
512 log_debug("Audit not supported in the kernel.");
514 log_warning_errno(errno
, "Failed to create audit socket, ignoring: %m");
519 if (bind(s
->audit_fd
, &sa
.sa
, sizeof(sa
.nl
)) < 0) {
520 log_warning_errno(errno
,
521 "Failed to join audit multicast group. "
522 "The kernel is probably too old or multicast reading is not supported. "
524 s
->audit_fd
= safe_close(s
->audit_fd
);
528 (void) fd_nonblock(s
->audit_fd
, true);
530 r
= setsockopt_int(s
->audit_fd
, SOL_SOCKET
, SO_PASSCRED
, true);
532 return log_error_errno(r
, "Failed to set SO_PASSCRED on audit socket: %m");
534 r
= sd_event_add_io(s
->event
, &s
->audit_event_source
, s
->audit_fd
, EPOLLIN
, server_process_datagram
, s
);
536 return log_error_errno(r
, "Failed to add audit fd to event loop: %m");
538 /* We are listening now, try to enable audit */
539 r
= enable_audit(s
->audit_fd
, true);
541 log_warning_errno(r
, "Failed to issue audit enable call: %m");