1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2014 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include "alloc-util.h"
22 #include "audit-type.h"
24 #include "hexdecoct.h"
26 #include "journald-audit.h"
28 #include "string-util.h"
30 typedef struct MapField
{
31 const char *audit_field
;
32 const char *journal_field
;
33 int (*map
)(const char *field
, const char **p
, struct iovec
**iov
, size_t *n_iov_allocated
, size_t *n_iov
);
36 static int map_simple_field(const char *field
, const char **p
, struct iovec
**iov
, size_t *n_iov_allocated
, size_t *n_iov
) {
37 _cleanup_free_
char *c
= NULL
;
38 size_t l
= 0, allocated
= 0;
48 c
= malloc(allocated
);
53 for (e
= *p
; !IN_SET(*e
, 0, ' '); e
++) {
54 if (!GREEDY_REALLOC(c
, allocated
, l
+2))
62 if (!GREEDY_REALLOC(*iov
, *n_iov_allocated
, *n_iov
+ 1))
65 (*iov
)[(*n_iov
)++] = IOVEC_MAKE(c
, l
);
73 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
) {
74 _cleanup_free_
char *c
= NULL
;
83 /* The kernel formats string fields in one of two formats. */
86 /* Normal quoted syntax */
92 l
= strlen(field
) + (e
- s
);
97 *((char*) mempcpy(stpcpy(c
, field
), s
, e
- s
)) = 0;
101 } else if (unhexchar(**p
) >= 0) {
102 /* Hexadecimal escaping */
103 size_t allocated
= 0;
107 c
= malloc(allocated
);
112 for (e
= *p
; !IN_SET(*e
, 0, ' '); e
+= 2) {
124 x
= ((uint8_t) a
<< 4 | (uint8_t) b
);
126 if (filter_printable
&& x
< (uint8_t) ' ')
129 if (!GREEDY_REALLOC(c
, allocated
, l
+2))
139 if (!GREEDY_REALLOC(*iov
, *n_iov_allocated
, *n_iov
+ 1))
142 (*iov
)[(*n_iov
)++] = IOVEC_MAKE(c
, l
);
150 static int map_string_field(const char *field
, const char **p
, struct iovec
**iov
, size_t *n_iov_allocated
, size_t *n_iov
) {
151 return map_string_field_internal(field
, p
, iov
, n_iov_allocated
, n_iov
, false);
154 static int map_string_field_printable(const char *field
, const char **p
, struct iovec
**iov
, size_t *n_iov_allocated
, size_t *n_iov
) {
155 return map_string_field_internal(field
, p
, iov
, n_iov_allocated
, n_iov
, true);
158 static int map_generic_field(const char *prefix
, const char **p
, struct iovec
**iov
, size_t *n_iov_allocated
, size_t *n_iov
) {
163 /* Implements fallback mappings for all fields we don't know */
165 for (e
= *p
; e
< *p
+ 16; e
++) {
167 if (IN_SET(*e
, 0, ' '))
173 if (!((*e
>= 'a' && *e
<= 'z') ||
174 (*e
>= 'A' && *e
<= 'Z') ||
175 (*e
>= '0' && *e
<= '9') ||
176 IN_SET(*e
, '_', '-')))
180 if (e
<= *p
|| e
>= *p
+ 16)
183 c
= alloca(strlen(prefix
) + (e
- *p
) + 2);
185 t
= stpcpy(c
, prefix
);
186 for (f
= *p
; f
< e
; f
++) {
189 if (*f
>= 'a' && *f
<= 'z')
190 x
= (*f
- 'a') + 'A'; /* uppercase */
192 x
= '_'; /* dashes → underscores */
202 r
= map_simple_field(c
, &e
, iov
, n_iov_allocated
, n_iov
);
210 /* Kernel fields are those occurring in the audit string before
211 * msg='. All of these fields are trusted, hence carry the "_" prefix.
212 * We try to translate the fields we know into our native names. The
213 * other's are generically mapped to _AUDIT_FIELD_XYZ= */
214 static const MapField map_fields_kernel
[] = {
216 /* First, we map certain well-known audit fields into native
217 * well-known fields */
218 { "pid=", "_PID=", map_simple_field
},
219 { "ppid=", "_PPID=", map_simple_field
},
220 { "uid=", "_UID=", map_simple_field
},
221 { "euid=", "_EUID=", map_simple_field
},
222 { "fsuid=", "_FSUID=", map_simple_field
},
223 { "gid=", "_GID=", map_simple_field
},
224 { "egid=", "_EGID=", map_simple_field
},
225 { "fsgid=", "_FSGID=", map_simple_field
},
226 { "tty=", "_TTY=", map_simple_field
},
227 { "ses=", "_AUDIT_SESSION=", map_simple_field
},
228 { "auid=", "_AUDIT_LOGINUID=", map_simple_field
},
229 { "subj=", "_SELINUX_CONTEXT=", map_simple_field
},
230 { "comm=", "_COMM=", map_string_field
},
231 { "exe=", "_EXE=", map_string_field
},
232 { "proctitle=", "_CMDLINE=", map_string_field_printable
},
234 /* Some fields don't map to native well-known fields. However,
235 * we know that they are string fields, hence let's undo
236 * string field escaping for them, though we stick to the
237 * generic field names. */
238 { "path=", "_AUDIT_FIELD_PATH=", map_string_field
},
239 { "dev=", "_AUDIT_FIELD_DEV=", map_string_field
},
240 { "name=", "_AUDIT_FIELD_NAME=", map_string_field
},
244 /* Userspace fields are those occurring in the audit string after
245 * msg='. All of these fields are untrusted, hence carry no "_"
246 * prefix. We map the fields we don't know to AUDIT_FIELD_XYZ= */
247 static const MapField map_fields_userspace
[] = {
248 { "cwd=", "AUDIT_FIELD_CWD=", map_string_field
},
249 { "cmd=", "AUDIT_FIELD_CMD=", map_string_field
},
250 { "acct=", "AUDIT_FIELD_ACCT=", map_string_field
},
251 { "exe=", "AUDIT_FIELD_EXE=", map_string_field
},
252 { "comm=", "AUDIT_FIELD_COMM=", map_string_field
},
256 static int map_all_fields(
258 const MapField map_fields
[],
262 size_t *n_iov_allocated
,
269 assert(n_iov_allocated
);
277 p
+= strspn(p
, WHITESPACE
);
283 v
= startswith(p
, "msg='");
288 /* Userspace message. It's enclosed in
289 simple quotation marks, is not
290 escaped, but the last field in the
291 line, hence let's remove the
292 quotation mark, and apply the
293 userspace mapping instead of the
296 e
= endswith(v
, "'");
298 return 0; /* don't continue splitting up if the final quotation mark is missing */
300 c
= strndupa(v
, e
- v
);
301 return map_all_fields(c
, map_fields_userspace
, "AUDIT_FIELD_", false, iov
, n_iov_allocated
, n_iov
);
305 /* Try to map the kernel fields to our own names */
306 for (m
= map_fields
; m
->audit_field
; m
++) {
307 v
= startswith(p
, m
->audit_field
);
311 r
= m
->map(m
->journal_field
, &v
, iov
, n_iov_allocated
, n_iov
);
313 return log_debug_errno(r
, "Failed to parse audit array: %m");
323 r
= map_generic_field(prefix
, &p
, iov
, n_iov_allocated
, n_iov
);
325 return log_debug_errno(r
, "Failed to parse audit array: %m");
328 /* Couldn't process as generic field, let's just skip over it */
329 p
+= strcspn(p
, WHITESPACE
);
334 static void process_audit_string(Server
*s
, int type
, const char *data
, size_t size
) {
335 size_t n_iov_allocated
= 0, n_iov
= 0, z
;
336 _cleanup_free_
struct iovec
*iov
= NULL
;
337 uint64_t seconds
, msec
, id
;
338 const char *p
, *type_name
;
339 char id_field
[sizeof("_AUDIT_ID=") + DECIMAL_STR_MAX(uint64_t)],
340 type_field
[sizeof("_AUDIT_TYPE=") + DECIMAL_STR_MAX(int)],
341 source_time_field
[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t
)];
353 /* Note that the input buffer is NUL terminated, but let's
354 * check whether there is a spurious NUL byte */
355 if (memchr(data
, 0, size
))
358 p
= startswith(data
, "audit");
362 if (sscanf(p
, "(%" PRIu64
".%" PRIu64
":%" PRIu64
"):%n",
370 p
+= strspn(p
, WHITESPACE
);
375 n_iov_allocated
= N_IOVEC_META_FIELDS
+ 7;
376 iov
= new(struct iovec
, n_iov_allocated
);
382 iov
[n_iov
++] = IOVEC_MAKE_STRING("_TRANSPORT=audit");
384 sprintf(source_time_field
, "_SOURCE_REALTIME_TIMESTAMP=%" PRIu64
,
385 (usec_t
) seconds
* USEC_PER_SEC
+ (usec_t
) msec
* USEC_PER_MSEC
);
386 iov
[n_iov
++] = IOVEC_MAKE_STRING(source_time_field
);
388 sprintf(type_field
, "_AUDIT_TYPE=%i", type
);
389 iov
[n_iov
++] = IOVEC_MAKE_STRING(type_field
);
391 sprintf(id_field
, "_AUDIT_ID=%" PRIu64
, id
);
392 iov
[n_iov
++] = IOVEC_MAKE_STRING(id_field
);
394 assert_cc(4 == LOG_FAC(LOG_AUTH
));
395 iov
[n_iov
++] = IOVEC_MAKE_STRING("SYSLOG_FACILITY=4");
396 iov
[n_iov
++] = IOVEC_MAKE_STRING("SYSLOG_IDENTIFIER=audit");
398 type_name
= audit_type_name_alloca(type
);
400 m
= strjoina("MESSAGE=", type_name
, " ", p
);
401 iov
[n_iov
++] = IOVEC_MAKE_STRING(m
);
405 map_all_fields(p
, map_fields_kernel
, "_AUDIT_FIELD_", true, &iov
, &n_iov_allocated
, &n_iov
);
407 if (!GREEDY_REALLOC(iov
, n_iov_allocated
, n_iov
+ N_IOVEC_META_FIELDS
)) {
412 server_dispatch_message(s
, iov
, n_iov
, n_iov_allocated
, NULL
, NULL
, LOG_NOTICE
, 0);
415 /* free() all entries that map_all_fields() added. All others
416 * are allocated on the stack or are constant. */
418 for (; z
< n_iov
; z
++)
419 free(iov
[z
].iov_base
);
422 void server_process_audit_message(
426 const struct ucred
*ucred
,
427 const union sockaddr_union
*sa
,
430 const struct nlmsghdr
*nl
= buffer
;
434 if (buffer_size
< ALIGN(sizeof(struct nlmsghdr
)))
439 /* Filter out fake data */
441 salen
!= sizeof(struct sockaddr_nl
) ||
442 sa
->nl
.nl_family
!= AF_NETLINK
||
443 sa
->nl
.nl_pid
!= 0) {
444 log_debug("Audit netlink message from invalid sender.");
448 if (!ucred
|| ucred
->pid
!= 0) {
449 log_debug("Audit netlink message with invalid credentials.");
453 if (!NLMSG_OK(nl
, buffer_size
)) {
454 log_error("Audit netlink message truncated.");
458 /* Ignore special Netlink messages */
459 if (IN_SET(nl
->nlmsg_type
, NLMSG_NOOP
, NLMSG_ERROR
))
462 /* Below AUDIT_FIRST_USER_MSG theer are only control messages, let's ignore those */
463 if (nl
->nlmsg_type
< AUDIT_FIRST_USER_MSG
)
466 process_audit_string(s
, nl
->nlmsg_type
, NLMSG_DATA(nl
), nl
->nlmsg_len
- ALIGN(sizeof(struct nlmsghdr
)));
469 static int enable_audit(int fd
, bool b
) {
472 struct nlmsghdr header
;
473 uint8_t header_space
[NLMSG_HDRLEN
];
475 struct audit_status body
;
476 } _packed_ request
= {
477 .header
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct audit_status
)),
478 .header
.nlmsg_type
= AUDIT_SET
,
479 .header
.nlmsg_flags
= NLM_F_REQUEST
,
480 .header
.nlmsg_seq
= 1,
481 .header
.nlmsg_pid
= 0,
482 .body
.mask
= AUDIT_STATUS_ENABLED
,
485 union sockaddr_union sa
= {
486 .nl
.nl_family
= AF_NETLINK
,
489 struct iovec iovec
= {
490 .iov_base
= &request
,
491 .iov_len
= NLMSG_LENGTH(sizeof(struct audit_status
)),
497 .msg_namelen
= sizeof(sa
.nl
),
502 n
= sendmsg(fd
, &mh
, MSG_NOSIGNAL
);
505 if (n
!= NLMSG_LENGTH(sizeof(struct audit_status
)))
508 /* We don't wait for the result here, we can't do anything
514 int server_open_audit(Server
*s
) {
515 static const int one
= 1;
518 if (s
->audit_fd
< 0) {
519 static const union sockaddr_union sa
= {
520 .nl
.nl_family
= AF_NETLINK
,
522 .nl
.nl_groups
= AUDIT_NLGRP_READLOG
,
525 s
->audit_fd
= socket(AF_NETLINK
, SOCK_RAW
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, NETLINK_AUDIT
);
526 if (s
->audit_fd
< 0) {
527 if (IN_SET(errno
, EAFNOSUPPORT
, EPROTONOSUPPORT
))
528 log_debug("Audit not supported in the kernel.");
530 log_warning_errno(errno
, "Failed to create audit socket, ignoring: %m");
535 if (bind(s
->audit_fd
, &sa
.sa
, sizeof(sa
.nl
)) < 0) {
536 log_warning_errno(errno
,
537 "Failed to join audit multicast group. "
538 "The kernel is probably too old or multicast reading is not supported. "
540 s
->audit_fd
= safe_close(s
->audit_fd
);
544 fd_nonblock(s
->audit_fd
, 1);
546 r
= setsockopt(s
->audit_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
));
548 return log_error_errno(errno
, "Failed to set SO_PASSCRED on audit socket: %m");
550 r
= sd_event_add_io(s
->event
, &s
->audit_event_source
, s
->audit_fd
, EPOLLIN
, server_process_datagram
, s
);
552 return log_error_errno(r
, "Failed to add audit fd to event loop: %m");
554 /* We are listening now, try to enable audit */
555 r
= enable_audit(s
->audit_fd
, true);
557 log_warning_errno(r
, "Failed to issue audit enable call: %m");