/
githubmirror
/
audit-userspace
Обзор
Документация
Войти
/
githubmirror
/
audit-userspace
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v4.2
auparse/interpret.c
3 847 строк
85 KB
Steve Grubb
auparse: validate AX.25 and ATM sockaddr lengths
20 июл 2026, 17:48
20 июл 2026, 17:48
9b6c174
Код
Авторство
О чём код?
/* * interpret.c - Lookup values to something more readable * Copyright (c) 2007-09,2011-16,2018-21,2023 Red Hat Inc. * All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Authors: * Steve Grubb <sgrubb@redhat.com> */ #include "config.h" #include "lru.h" #include "libaudit.h" #include "internal.h" #include "interpret.h" #include "auparse-idata.h" #include "auparse.h" #include "nvlist.h" #include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <ctype.h> #include <errno.h> #include <stdint.h> #include <string.h> #include <pwd.h> #include <grp.h> #include <sys/stat.h> #include <linux/net.h> #include <netdb.h> #include <sys/un.h> #include <linux/ax25.h> #include <linux/atm.h> #include <linux/x25.h> #include <linux/capability.h> #include <linux/netfilter.h> #include <linux/seccomp.h> #include <sys/personality.h> #include <sys/prctl.h> #include <sched.h> #include <limits.h> /* PATH_MAX */ #ifdef USE_FANOTIFY #include <linux/fanotify.h> #else #define FAN_ALLOW 1 #define FAN_DENY 2 #endif // This code is at the center of many performance issues. The following // ensure that it is optimized the most without making all of the audit // subsystem bigger. #pragma GCC optimize("O3") #include "auparse-defs.h" #include "gen_tables64.h" #include "common.h" #if !HAVE_DECL_ADDR_NO_RANDOMIZE # define ADDR_NO_RANDOMIZE 0x0040000 #endif /* This is from asm/ipc.h. Copying it for now as some platforms * have broken headers. */ #define SEMOP 1 #define SEMGET 2 #define SEMCTL 3 #define SEMTIMEDOP 4 #define MSGSND 11 #define MSGRCV 12 #define MSGGET 13 #define MSGCTL 14 #define SHMAT 21 #define SHMDT 22 #define SHMGET 23 #define SHMCTL 24 #define DIPC 25 #include "captabs.h" #include "clone-flagtabs.h" #include "epoll_ctls.h" #include "famtabs.h" #include "fcntl-cmdtabs.h" #include "fsconfigs.h" #include "ipctabs.h" #include "ipccmdtabs.h" #include "mmaptabs.h" #include "mounttabs.h" #include "open-flagtabs.h" #include "persontabs.h" #include "prottabs.h" #include "ptracetabs.h" #include "recvtabs.h" #include "rlimittabs.h" #include "seektabs.h" #include "socktabs.h" #include "socktypetabs.h" #include "signaltabs.h" #include "clocktabs.h" #include "typetabs.h" #include "nfprototabs.h" #include "icmptypetabs.h" #include "seccomptabs.h" #include "accesstabs.h" #include "prctl_opttabs.h" #include "schedtabs.h" #include "shm_modetabs.h" #include "sockoptnametabs.h" #include "sockleveltabs.h" #include "ipoptnametabs.h" #include "ip6optnametabs.h" #include "tcpoptnametabs.h" #include "pktoptnametabs.h" #include "umounttabs.h" #include "ioctlreqtabs.h" #include "inethooktabs.h" #include "arphooktabs.h" #include "netactiontabs.h" #include "bpftabs.h" #include "openat2-resolvetabs.h" #include "xattr-atflagtabs.h" #include "access-flagtabs.h" typedef enum { AVC_UNSET, AVC_DENIED, AVC_GRANTED } avc_t; typedef enum { S_UNSET=-1, S_FAILED, S_SUCCESS } success_t; static char *print_escaped(const char *val); static const char *print_signals(const char *val, unsigned int base); /* * This function will take a pointer to a 2 byte Ascii character buffer and * return the actual hex value. */ static unsigned char x2c(const unsigned char *buf) { static const char AsciiArray[17] = "0123456789ABCDEF"; const char *ptr; unsigned char total=0; if (buf == NULL || buf[0] == 0 || buf[1] == 0) return 0; ptr = strchr(AsciiArray, (char)toupper(buf[0])); if (ptr) total = (unsigned char)(((ptr-AsciiArray) & 0x0F)<<4); ptr = strchr(AsciiArray, (char)toupper(buf[1])); if (ptr) total += (unsigned char)((ptr-AsciiArray) & 0x0F); return total; } typedef struct escape_buffer { char *data; size_t capacity; size_t length; } escape_buffer; /* need_tty_escape - count control bytes requiring TTY escaping * @s: source buffer * @len: number of source bytes * * Return: number of bytes requiring escaping. */ static size_t need_tty_escape(const char *s, size_t len) __nonnull((1)) __attr_access ((__read_only__, 1, 2)); static size_t need_tty_escape(const char *s, size_t len) { size_t i = 0, cnt = 0; while (i < len) { if ((unsigned char)s[i] < 32) cnt++; i++; } return cnt; } /* tty_escape - encode a source buffer for terminal output * @s: source buffer * @len: number of source bytes * @dest: destination buffer * @dlen: destination capacity including the final NUL * * Return: 0 on success and -1 when the destination is too small. */ static int tty_escape(const char *s, size_t len, char *dest, size_t dlen) __nonnull((1, 3)) __attr_access ((__read_only__, 1, 2)) __attr_access ((__write_only__, 3, 4)); static int tty_escape(const char *s, size_t len, char *dest, size_t dlen) { size_t i = 0, j = 0; if (dlen == 0) return -1; while (i < len) { if ((unsigned char)s[i] < 32) { if (dlen - j <= 4) return -1; dest[j++] = ('\\'); dest[j++] = ('0' + ((s[i] & 0300) >> 6)); dest[j++] = ('0' + ((s[i] & 0070) >> 3)); dest[j++] = ('0' + (s[i] & 0007)); } else { if (dlen - j <= 1) return -1; dest[j++] = s[i]; } i++; } dest[j] = '\0'; return 0; } static const char sh_set[] = "\"'`$\\!()| "; /* need_shell_escape - count bytes requiring shell escaping * @s: source buffer * @len: number of source bytes * * Return: number of bytes requiring escaping. */ static size_t need_shell_escape(const char *s, size_t len) __nonnull((1)) __attr_access ((__read_only__, 1, 2)); static size_t need_shell_escape(const char *s, size_t len) { size_t i = 0, cnt = 0; while (i < len) { if ((unsigned char)s[i] < 32) cnt++; else if (strchr(sh_set, s[i])) cnt++; i++; } return cnt; } /* shell_escape - encode a source buffer for unquoted shell output * @s: source buffer * @len: number of source bytes * @dest: destination buffer * @dlen: destination capacity including the final NUL * * Return: 0 on success and -1 when the destination is too small. */ static int shell_escape(const char *s, size_t len, char *dest, size_t dlen) __nonnull((1, 3)) __attr_access ((__read_only__, 1, 2)) __attr_access ((__write_only__, 3, 4)); static int shell_escape(const char *s, size_t len, char *dest, size_t dlen) { size_t i = 0, j = 0; if (dlen == 0) return -1; while (i < len) { if ((unsigned char)s[i] < 32) { if (dlen - j <= 4) return -1; dest[j++] = ('\\'); dest[j++] = ('0' + ((s[i] & 0300) >> 6)); dest[j++] = ('0' + ((s[i] & 0070) >> 3)); dest[j++] = ('0' + (s[i] & 0007)); } else if (strchr(sh_set, s[i])) { if (dlen - j <= 2) return -1; dest[j++] = ('\\'); dest[j++] = s[i]; } else { if (dlen - j <= 1) return -1; dest[j++] = s[i]; } i++; } dest[j] = '\0'; return 0; } static const char quote_set[] = "\"'`$\\!()| ;#&*?[]<>{}"; /* need_shell_quote_escape - count bytes requiring quoted shell escaping * @s: source buffer * @len: number of source bytes * * Return: number of bytes requiring escaping. */ static size_t need_shell_quote_escape(const char *s, size_t len) __nonnull((1)) __attr_access ((__read_only__, 1, 2)); static size_t need_shell_quote_escape(const char *s, size_t len) { size_t i = 0, cnt = 0; while (i < len) { if ((unsigned char)s[i] < 32) cnt++; else if (strchr(quote_set, s[i])) cnt++; i++; } return cnt; } /* shell_quote_escape - encode a source buffer for quoted shell output * @s: source buffer * @len: number of source bytes * @dest: destination buffer * @dlen: destination capacity including the final NUL * * Return: 0 on success and -1 when the destination is too small. */ static int shell_quote_escape(const char *s, size_t len, char *dest, size_t dlen) __nonnull((1, 3)) __attr_access ((__read_only__, 1, 2)) __attr_access ((__write_only__, 3, 4)); static int shell_quote_escape(const char *s, size_t len, char *dest, size_t dlen) { size_t i = 0, j = 0; if (dlen == 0) return -1; while (i < len) { if ((unsigned char)s[i] < 32) { if (dlen - j <= 4) return -1; dest[j++] = ('\\'); dest[j++] = ('0' + ((s[i] & 0300) >> 6)); dest[j++] = ('0' + ((s[i] & 0070) >> 3)); dest[j++] = ('0' + (s[i] & 0007)); } else if (strchr(quote_set, s[i])) { if (dlen - j <= 2) return -1; dest[j++] = ('\\'); dest[j++] = s[i]; } else { if (dlen - j <= 1) return -1; dest[j++] = s[i]; } i++; } dest[j] = '\0'; return 0; } /* need_escaping - count source bytes requiring the selected escaping * @s: source buffer * @len: number of source bytes * @escape_mode: output escaping mode * * Return: number of bytes requiring escaping. */ static size_t need_escaping(const char *s, size_t len, auparse_esc_t escape_mode) __nonnull((1)) __attr_access ((__read_only__, 1, 2)); static size_t need_escaping(const char *s, size_t len, auparse_esc_t escape_mode) { switch (escape_mode) { case AUPARSE_ESC_RAW: break; case AUPARSE_ESC_TTY: return need_tty_escape(s, len); case AUPARSE_ESC_SHELL: return need_shell_escape(s, len); case AUPARSE_ESC_SHELL_QUOTE: return need_shell_quote_escape(s, len); } return 0; } /* escape_size - calculate the maximum escaped allocation size * @len: number of source bytes * @count: number of source bytes requiring escaping * @size: calculated allocation size including the final NUL * * Return: 0 on success and -1 if the calculation would overflow. */ static int escape_size(size_t len, size_t count, size_t *size) __nonnull((3)) __attr_access ((__write_only__, 3)); static int escape_size(size_t len, size_t count, size_t *size) { if (len == SIZE_MAX || count > (SIZE_MAX - len - 1) / 3) return -1; *size = len + 1 + 3 * count; return 0; } /* escape - dispatch bounded escaping into a destination buffer * @s: source buffer * @len: number of source bytes * @dest: destination buffer description and resulting output length * @escape_mode: output escaping mode * * Return: 0 on success and -1 when the destination is too small. */ static int escape(const char *s, size_t len, escape_buffer *dest, auparse_esc_t escape_mode) __nonnull((1, 3)) __attr_access ((__read_only__, 1, 2)); static int escape(const char *s, size_t len, escape_buffer *dest, auparse_esc_t escape_mode) { int rc; if (dest->data == NULL) return -1; switch (escape_mode) { case AUPARSE_ESC_RAW: if (dest->capacity <= len) return -1; memcpy(dest->data, s, len); dest->data[len] = '\0'; break; case AUPARSE_ESC_TTY: rc = tty_escape(s, len, dest->data, dest->capacity); if (rc) return rc; break; case AUPARSE_ESC_SHELL: rc = shell_escape(s, len, dest->data, dest->capacity); if (rc) return rc; break; case AUPARSE_ESC_SHELL_QUOTE: rc = shell_quote_escape(s, len, dest->data, dest->capacity); if (rc) return rc; break; default: return -1; } dest->length = strlen(dest->data); return 0; } /* key_escape - escape each component while preserving key separators * @orig: source key buffer * @len: number of source bytes * @dest: destination buffer description and resulting output length * @escape_mode: output escaping mode * * Return: 0 on success and -1 when the destination is too small. */ static int key_escape(const char *orig, size_t len, escape_buffer *dest, auparse_esc_t escape_mode) __nonnull((1, 3)) __attr_access ((__read_only__, 1, 2)); static int key_escape(const char *orig, size_t len, escape_buffer *dest, auparse_esc_t escape_mode) { size_t offset = 0; if (dest->data == NULL || dest->capacity == 0) return -1; dest->data[0] = '\0'; dest->length = 0; while (offset < len) { const char *start = orig + offset; const char *sep; escape_buffer part; size_t klen; sep = memchr(start, AUDIT_KEY_SEPARATOR, len - offset); klen = sep ? (size_t)(sep - start) : len - offset; part.data = dest->data + dest->length; part.capacity = dest->capacity - dest->length; part.length = 0; if (escape(start, klen, &part, escape_mode)) return -1; dest->length += part.length; if (sep == NULL) break; if (dest->capacity - dest->length <= 1) return -1; dest->data[dest->length++] = AUDIT_KEY_SEPARATOR; dest->data[dest->length] = '\0'; offset += klen + 1; } return 0; } static int is_hex_string(const char *str) { while (*str) { if (!isxdigit(*str)) return 0; str++; } return 1; } /* au_unescape - decode a hexadecimal or parenthesized audit value * @buf: NUL-terminated source string * * Return: a newly allocated decoded string, or NULL on failure. */ char *au_unescape(const char *buf) { size_t olen, len, i, prefix_len; char *str, *work; const char *ptr = buf; /* Find the end of the name */ if (*ptr == '(') { ptr = strchr(ptr, ')'); if (ptr == NULL) return NULL; else ptr++; } else { while (isxdigit(*ptr)) ptr++; } // Make the buffer based on size of original buffer. // This is in case we have unexpected non-hex digit // that causes truncation of the conversion and passes // back a buffer that is not sized on the expectation of // strlen(buf) / 2. olen = strlen(buf); if (olen == SIZE_MAX) return NULL; str = malloc(olen + 1); if (!str) return NULL; prefix_len = (size_t)(ptr - buf); memcpy(str, buf, prefix_len); str[prefix_len] = 0; /* See if its '(null)' from the kernel */ if (*buf == '(') return str; /* We can get away with this since the buffer is 2 times * bigger than what we are putting there. */ len = strlen(str); if (len < 2) { free(str); return NULL; } work = str; for (i=0; i<len; i+=2) { *work = x2c((unsigned char *)&str[i]); work++; } *work = 0; len = (size_t)(work - str) - 1; olen /= 2; // Because *ptr is 0, writing another 0 to it doesn't hurt anything if (olen > len) memset(work, 0, olen - len); return str; } /////////// Interpretation list functions /////////////// void init_interpretation_list(auparse_state_t *au) { if (au == NULL) return; nvlist_create(&au->interpretations); au->interpretations.cnt = NEVER_LOADED; } /* * Returns 0 on error and 1 on success */ int load_interpretation_list(auparse_state_t *au, const char *buffer) { char *saved = NULL, *ptr; char *buf, *val; nvnode n; nvlist *il = &au->interpretations; if (buffer == NULL) return 0; if (il->cnt == NEVER_LOADED) il->cnt = 0; il->record = buf = strdup(buffer); if (buf == NULL) goto err_out; if (strncmp(buf, "SADDR=", 6) == 0) { // We have SOCKADDR record. It has no other values. // Handle it by itself. ptr = strchr(buf+6, '{'); if (ptr) { val = ptr; ptr = strchr(val, '}'); if (ptr) { // Just change the case n.name = strcpy(buf, "saddr"); n.val = val; if (nvlist_append(il, &n)) goto err_out; nvlist_interp_fixup(il); return 1; } } err_out: free(buf); il->record = NULL; il->cnt = NEVER_LOADED; return 0; } else { // We handle everything else in this branch ptr = audit_strsplit_r(buf, &saved); if (ptr == NULL) goto err_out; do { char tmp; val = strchr(ptr, '='); if (val) { *val = 0; val++; } else // Malformed - skip continue; n.name = ptr; char *c = n.name; while (*c) { *c = tolower(*c); c++; } ptr = strchr(val, ' '); if (ptr) { tmp = *ptr; *ptr = 0; } else tmp = 0; n.val = val; if (nvlist_append(il, &n)) continue; // assuming we loaded something nvlist_interp_fixup(il); if (ptr) *ptr = tmp; } while ((ptr = audit_strsplit_r(NULL, &saved))); } // If for some reason it was useless, delete buf if (il->cnt == 0) goto err_out; return 1; } /* * Returns malloc'ed buffer on success and NULL if no match */ const char *_auparse_lookup_interpretation(auparse_state_t *au,const char *name) { nvnode *n; nvlist *il = &au->interpretations; if (il->cnt == NEVER_LOADED) return NULL; nvlist_first(il); if (nvlist_find_name(il, name)) { n = nvlist_get_cur(il); // This is only called from src/ausearch-lookup.c // it only looks up auid and syscall. One needs // escape, the other does not. if (strstr(name, "id")) return print_escaped(n->interp_val); else return strdup(n->interp_val); } return NULL; } void free_interpretation_list(auparse_state_t *au) { nvlist *il = &au->interpretations; if (il->cnt != NEVER_LOADED || il->array) { nvlist_clear(il, 0); il->cnt = NEVER_LOADED; } } // This uses a sentinel to determine if the list has ever been loaded. // If never loaded, returns 0. Otherwise it returns 1 higher than how // many interpretations are loaded. unsigned int interpretation_list_cnt(const auparse_state_t *au) { const nvlist *il = &au->interpretations; if (il->cnt == NEVER_LOADED) return 0; return il->cnt + 1; } //////////// Start Field Value Interpretations ///////////// static const char *success[3]= { "unset", "no", "yes" }; static const char *aulookup_success(int s) { switch (s) { default: return success[0]; case S_FAILED: return success[1]; case S_SUCCESS: return success[2]; } } /* * hash_name - djb2 string hash * @name: string to hash * * We use djb2 for its small code and reasonable distribution on * typical account names, which keeps the cache hash tables balanced * without a performance penalty. */ static unsigned int hash_name(const char *name) { unsigned int h = 5381; unsigned char c; while ((c = *(const unsigned char *)name++)) h = ((h << 5) + h) + c; return h; } /* * aulookup_uid - map a uid to a name using the cache * @au: parser state containing the cache * @uid: user id to resolve * @buf: caller-provided buffer * @size: size of @buf * * Returns @buf containing the best-effort name. On a cache hit the * entry is promoted to the head of the queue; on a miss the system * password database is consulted and the result cached. */ static const char *aulookup_uid(auparse_state_t *au, uid_t uid, char *buf, size_t size) { char *name = NULL; QNode *q_node; if (uid == (uid_t)-1) { snprintf(buf, size, "unset"); return buf; } else if (uid == 0) { snprintf(buf, size, "root"); return buf; } if (au->uid_cache == NULL) au->uid_cache = init_lru(19, NULL, "uid"); q_node = check_lru_uid(au->uid_cache, uid); if (q_node) { if (q_node->uid == uid && q_node->name) name = q_node->name; else { struct passwd *pw; pw = getpwuid(uid); if (pw) { free(q_node->name); q_node->name = strdup(pw->pw_name); if (q_node->name) { unsigned int key = hash_name(q_node->name) % au->uid_cache->total; au->uid_cache->name_hash->array[key] = q_node; q_node->uid = uid; name = q_node->name; } } } } if (name != NULL) snprintf(buf, size, "%s", name); else snprintf(buf, size, "unknown(%d)", uid); return buf; } void _aulookup_destroy_uid_list(auparse_state_t *au) { destroy_lru(au->uid_cache); au->uid_cache = NULL; } /* * lookup_uid_from_name - resolve a name to a uid using the cache * @au: parser state containing the cache * @name: account name to resolve * * Returns the uid on success or (uid_t)-1 on failure. Cache hits are * promoted and misses consult the system password database with the * result stored for future lookups. */ uid_t lookup_uid_from_name(auparse_state_t *au, const char *name) { QNode *q_node; uid_t uid = (uid_t)-1; if (name == NULL) return (uid_t)-1; if (au->uid_cache == NULL) au->uid_cache = init_lru(19, NULL, "uid"); q_node = check_lru_name(au->uid_cache, name); if (q_node) { if (q_node->uid != (uid_t)-1) uid = q_node->uid; else { struct passwd *pw = getpwnam(name); if (pw) { q_node->uid = pw->pw_uid; unsigned int key = q_node->uid % au->uid_cache->total; au->uid_cache->uid_hash->array[key] = q_node; uid = q_node->uid; } } } return uid; } static const char *aulookup_gid(auparse_state_t *au, gid_t gid, char *buf, size_t size) { char *name = NULL; QNode *q_node; if (gid == (gid_t)-1) { snprintf(buf, size, "unset"); return buf; } else if (gid == 0) { snprintf(buf, size, "root"); return buf; } if (au->gid_cache == NULL) au->gid_cache = init_lru(19, NULL, "gid"); q_node = check_lru_uid(au->gid_cache, gid); if (q_node) { if (q_node->uid == gid && q_node->name) name = q_node->name; else { struct group *gr; gr = getgrgid(gid); if (gr) { free(q_node->name); q_node->name = strdup(gr->gr_name); if (q_node->name) { unsigned int key = hash_name(q_node->name) % au->gid_cache->total; au->gid_cache->name_hash->array[key] = q_node; q_node->uid = gid; name = q_node->name; } } } } if (name != NULL) snprintf(buf, size, "%s", name); else snprintf(buf, size, "unknown(%d)", gid); return buf; } void aulookup_destroy_gid_list(auparse_state_t *au) { destroy_lru(au->gid_cache); au->gid_cache = NULL; } void _auparse_flush_caches(auparse_state_t *au) { destroy_lru(au->uid_cache); au->uid_cache = NULL; destroy_lru(au->gid_cache); au->gid_cache = NULL; } void aulookup_metrics(const auparse_state_t *au, unsigned int *uid, unsigned int *gid) { *uid = au->uid_cache ? au->uid_cache->count : 0; *gid = au->gid_cache ? au->gid_cache->count : 0; } static const char *print_uid(auparse_state_t *au, const char *val, unsigned int base) { int uid; char name[64]; errno = 0; uid = strtoul(val, NULL, base); if (errno) { char *out; if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } return strdup(aulookup_uid(au, uid, name, sizeof(name))); } static const char *print_gid(auparse_state_t *au, const char *val, unsigned int base) { int gid; char name[64]; errno = 0; gid = strtoul(val, NULL, base); if (errno) { char *out; if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } return strdup(aulookup_gid(au, gid, name, sizeof(name))); } static const char *print_arch(const char *val, unsigned int machine) { const char *ptr; char *out; if (machine > MACH_RISCV64) { unsigned int ival; errno = 0; ival = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s) ", val) < 0) out = NULL; return out; } machine = audit_elf_to_machine(ival); } if ((int)machine < 0) { if (asprintf(&out, "unknown-elf-type(%s)", val) < 0) out = NULL; return out; } ptr = audit_machine_to_name(machine); if (ptr) return strdup(ptr); else { if (asprintf(&out, "unknown-machine-type(%u)", machine) < 0) out = NULL; return out; } } static const char *print_ipccall(const char *val, unsigned int base) { int a0; char *out; const char *func = NULL; errno = 0; a0 = strtol(val, NULL, base); if (errno) { char *out2; if (asprintf(&out2, "conversion error(%s)", val) < 0) out2 = NULL; return out2; } func = ipc_i2s(a0); if (func) return strdup(func); else { if (asprintf(&out, "unknown-ipccall(%s)", val) < 0) out = NULL; return out; } } static const char *print_socketcall(const char *val, unsigned int base) { int a0; char *out; const char *func = NULL; errno = 0; a0 = strtol(val, NULL, base); if (errno) { char *out2; if (asprintf(&out2, "conversion error(%s)", val) < 0) out2 = NULL; return out2; } func = sock_i2s(a0); if (func) return strdup(func); else { if (asprintf(&out, "unknown-socketcall(%s)", val) < 0) out = NULL; return out; } } static const char *print_syscall(const idata *id) { const char *sys; char *out; int machine = id->machine, syscall = id->syscall; unsigned long long a0 = id->a0; if (machine < 0) machine = audit_detect_machine(); if (machine < 0) { out = strdup(id->val); return out; } sys = audit_syscall_to_name(syscall, machine); if (sys) { const char *func = NULL; if (strcmp(sys, "socketcall") == 0) { if ((int)a0 == a0) func = sock_i2s(a0); } else if (strcmp(sys, "ipc") == 0) if ((int)a0 == a0) func = ipc_i2s(a0); if (func) { if (asprintf(&out, "%s(%s)", sys, func) < 0) out = NULL; } else return strdup(sys); } else { if (asprintf(&out, "unknown-syscall(%d)", syscall) < 0) out = NULL; } return out; } static const char *print_exit(const char *val) { long long ival; char *out; errno = 0; ival = strtoll(val, NULL, 10); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } if (ival < 0) { if (asprintf(&out, "%s(%s)", audit_errno_to_name(-ival), strerror(-ival)) < 0) out = NULL; return out; } return strdup(val); } static char *print_escaped(const char *val) { char *out; if (val == NULL) return strdup(" "); if (*val == '"') { const char *term; val++; term = strchr(val, '"'); if (term == NULL) return strdup(" "); return strndup(val, term - val); // FIXME: working here...was trying to detect (null) and handle that // differently. The other 2 should have " around the file names. /* } else if (*val == '(') { char *term; val++; term = strchr(val, ' '); if (term == NULL) return; *term = 0; printf("%s ", val); */ } else if (val[0] == '0' && val[1] == '0') out = au_unescape(&val[2]); // Abstract name af_unix else out = au_unescape(val); if (out) return out; return strdup(val); // Something is wrong with string, just send as is } // This code is loosely based on glibc-2.27 realpath. static char *path_norm(const char *name) { char *working, *rpath, *dest; char *ret; const char *start, *end, *rpath_limit; size_t component_len, available, add_slash; int old_errno = errno; working = malloc(PATH_MAX); if (working == NULL) return NULL; *working = 0; errno = EINVAL; if (name == NULL) goto err_out; if (name[0] == 0) goto err_out; errno = old_errno; // If not absolute, give it back as is if (name[0] == '.') { ret = strdup(name); free(working); return ret; } rpath = working; dest = rpath; rpath_limit = rpath + PATH_MAX; for (start = name; *start; start = end) { // Remove duplicate '/' while (*start == '/') ++start; // Find end of path component for (end = start; *end && *end != '/'; ++end) ; //empty // if it ends with a slash, we're done if (end - start == 0) break; else if (end - start == 1 && start[0] == '.') ; //empty else if (end - start == 2 && start[0] == '.' && start[1] == '.') { // Back up to previous component, ignore if root while (dest > rpath && dest[-1] != '/') --dest; } else { component_len = end - start; add_slash = (dest == working && name[0] == '/') || (dest == working || dest[-1] != '/'); available = rpath_limit - dest; /* Check the separator with the component so an overlong path * cannot advance dest beyond the allocated buffer. */ if (add_slash >= available || component_len >= available - add_slash) { *dest = 0; break; } if (add_slash) *dest++ = '/'; // Otherwise copy next component dest = mempcpy(dest, start, component_len); *dest = 0; } } ret = strdup(working); free(working); return ret; err_out: free(working); return NULL; } static const char *print_escaped_ext(const idata *id) { if (id->cwd) { char *str1 = NULL, *str2, *str3 = NULL, *out = NULL; str2 = print_escaped(id->val); if (!str2) goto err_out; if (*str2 != '/') { // Glue the cwd and path together str1 = print_escaped(id->cwd); if (!str1) goto err_out; if (asprintf(&str3, "%s/%s", str1, str2) < 0) goto err_out; } else { // Normal looking string str3 = str2; str2 = NULL; } // Check in case /home/../etc/passwd if (strstr(str3, "..") == NULL) { free(str1); free(str2); return str3; // Nope, just return the string } out = path_norm(str3); if (!out) { // If there's an error, just return the original free(str1); free(str2); return str3; } err_out: free(str1); free(str2); free(str3); return out; } else return print_escaped(id->val); } // rawmemchr is faster. Let's use it if we have it. #ifdef HAVE_RAWMEMCHR #define STRCHR rawmemchr #else #define STRCHR strchr #endif static const char *print_proctitle(const char *val) { char *out = (char *)print_escaped(val); /* print_escaped reports allocation failure with a NULL result. */ if (out == NULL) return NULL; if (*val != '"') { size_t len = strlen(val) / 2; const char *end = out + len; char *ptr = out; // Proctitle has arguments separated by NUL bytes // We need to write over the NUL bytes with a space // so that we can see the arguments while ((ptr = STRCHR(ptr, '\0'))) { if (ptr >= end) break; *ptr = ' '; ptr++; } } return out; } static const char *print_perm(const char *val) { int ival, printed=0; char buf[32]; errno = 0; ival = strtol(val, NULL, 10); if (errno) { char *out; if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } buf[0] = 0; /* The kernel treats nothing (0x00) as everything (0x0F) */ if (ival == 0) ival = 0x0F; if (ival & AUDIT_PERM_READ) { strcat(buf, "read"); printed = 1; } if (ival & AUDIT_PERM_WRITE) { if (printed) strcat(buf, ",write"); else strcat(buf, "write"); printed = 1; } if (ival & AUDIT_PERM_EXEC) { if (printed) strcat(buf, ",exec"); else strcat(buf, "exec"); printed = 1; } if (ival & AUDIT_PERM_ATTR) { if (printed) strcat(buf, ",attr"); else strcat(buf, "attr"); } return strdup(buf); } static const char *print_mode(const char *val, unsigned int base) { unsigned int ival; char *out, buf[48]; const char *name; errno = 0; ival = strtoul(val, NULL, base); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } // detect the file type name = audit_ftype_to_name(ival & S_IFMT); if (name != NULL) strcpy(buf, name); else { unsigned first_ifmt_bit; // The lowest-valued "1" bit in S_IFMT first_ifmt_bit = S_IFMT & ~(S_IFMT - 1); sprintf(buf, "%03o", (ival & S_IFMT) / first_ifmt_bit); } // check on special bits if (S_ISUID & ival) strcat(buf, ",suid"); if (S_ISGID & ival) strcat(buf, ",sgid"); if (S_ISVTX & ival) strcat(buf, ",sticky"); // and the read, write, execute flags in octal if (asprintf(&out, "%s,%03o", buf, (S_IRWXU|S_IRWXG|S_IRWXO) & ival) < 0) out = NULL; return out; } static const char *print_mode_short_int(unsigned int ival) { char *out, buf[48]; // check on special bits buf[0] = 0; if (S_ISUID & ival) strcat(buf, "suid"); if (S_ISGID & ival) { if (buf[0]) strcat(buf, ","); strcat(buf, "sgid"); } if (S_ISVTX & ival) { if (buf[0]) strcat(buf, ","); strcat(buf, "sticky"); } // and the read, write, execute flags in octal if (buf[0] == 0) { if (asprintf(&out, "0%03o", (S_IRWXU|S_IRWXG|S_IRWXO) & ival) < 0) out = NULL; } else if (asprintf(&out, "%s,0%03o", buf, (S_IRWXU|S_IRWXG|S_IRWXO) & ival) < 0) out = NULL; return out; } static const char *print_mode_short(const char *val, int base) { unsigned int ival; char *out; errno = 0; ival = strtoul(val, NULL, base); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } return print_mode_short_int(ival); } static const char *print_socket_domain(const char *val) { int i; char *out; const char *str; errno = 0; i = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } str = fam_i2s(i); if (str == NULL) { if (asprintf(&out, "unknown-family(0x%s)", val) < 0) out = NULL; return out; } else return strdup(str); } static const char *print_socket_type(const char *val) { unsigned int type; char *out; const char *str; errno = 0; type = 0xFF & strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } str = sock_type_i2s(type); if (str == NULL) { if (asprintf(&out, "unknown-type(%s)", val) < 0) out = NULL; return out; } else return strdup(str); } static const char *print_socket_proto(const char *val) { unsigned int proto; char *out; struct protoent *p; errno = 0; proto = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } p = getprotobynumber(proto); if (p == NULL) { if (asprintf(&out, "unknown-proto(%s)", val) < 0) out = NULL; return out; } else return strdup(p->p_name); } static const char *print_sockaddr(const char *val) { size_t slen; int rc = 0; const struct sockaddr *saddr; char name[NI_MAXHOST], serv[NI_MAXSERV]; const char *host; char *out = NULL; const char *str; slen = strlen(val)/2; host = au_unescape(val); if (host == NULL) { if (asprintf(&out, "malformed-host(%s)", val) < 0) out = NULL; return out; } saddr = (struct sockaddr *)host; str = fam_i2s(saddr->sa_family); if (str == NULL) { if (asprintf(&out, "unknown-family(%d)", saddr->sa_family) < 0) out = NULL; free((char *)host); return out; } // Now print address for some families switch (saddr->sa_family) { case AF_LOCAL: if (slen < 4) { rc = asprintf(&out, "{ saddr_fam=%s %s }", str, slen == sizeof(saddr->sa_family) ? "unnamed socket" : // ignore sun_path "sockaddr len too short"); break; } else { const struct sockaddr_un *un = (const struct sockaddr_un *)saddr; if (un->sun_path[0]) rc = asprintf(&out, "{ saddr_fam=%s path=%.108s }", str, un->sun_path); else // abstract name rc = asprintf(&out, "{ saddr_fam=%s path=%.108s }", str, &un->sun_path[1]); } break; case AF_INET: if (slen < sizeof(struct sockaddr_in)) { rc = asprintf(&out, "{ saddr_fam=%s sockaddr len too short }", str); break; } slen = sizeof(struct sockaddr_in); if (getnameinfo(saddr, slen, name, NI_MAXHOST, serv, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV) == 0 ) { rc = asprintf(&out, "{ saddr_fam=%s laddr=%s lport=%s }", str, name, serv); } else rc = asprintf(&out, "{ saddr_fam=%s (error resolving addr) }", str); break; case AF_AX25: if (slen < sizeof(struct sockaddr_ax25)) { rc = asprintf(&out, "{ saddr_fam=%s sockaddr len too short }", str); break; } { const struct sockaddr_ax25 *x = (const struct sockaddr_ax25 *)saddr; rc = asprintf(&out, "{ saddr_fam=%s call=%c%c%c%c%c%c%c }", str, x->sax25_call.ax25_call[0], x->sax25_call.ax25_call[1], x->sax25_call.ax25_call[2], x->sax25_call.ax25_call[3], x->sax25_call.ax25_call[4], x->sax25_call.ax25_call[5], x->sax25_call.ax25_call[6]); } break; case AF_ATMPVC: if (slen < sizeof(struct sockaddr_atmpvc)) { rc = asprintf(&out, "{ saddr_fam=%s sockaddr len too short }", str); break; } { const struct sockaddr_atmpvc* at = (const struct sockaddr_atmpvc *)saddr; rc = asprintf(&out, "{ saddr_fam=%s int=%d }", str, at->sap_addr.itf); } break; case AF_X25: { const struct sockaddr_x25* x = (const struct sockaddr_x25 *)saddr; rc = asprintf(&out, "{ saddr_fam=%s laddr=%.15s }", str, x->sx25_addr.x25_addr); } break; case AF_INET6: if (slen < sizeof(struct sockaddr_in6)) { rc = asprintf(&out, "{ saddr_fam=%s sockaddr6 len too short }", str); break; } slen = sizeof(struct sockaddr_in6); if (getnameinfo(saddr, slen, name, NI_MAXHOST, serv, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV) == 0 ) { rc = asprintf(&out, "{ saddr_fam=%s laddr=%s lport=%s }", str, name, serv); } else rc = asprintf(&out, "{ saddr_fam=%s (error resolving addr) }", str); break; case AF_NETLINK: if (slen < sizeof(struct sockaddr_nl)) { rc = asprintf(&out, "{ saddr_fam=%s len too short }", str); break; } else { const struct sockaddr_nl *n = (const struct sockaddr_nl *)saddr; rc = asprintf(&out, "{ saddr_fam=%s nlnk-fam=%u nlnk-pid=%u }", str, n->nl_family, n->nl_pid); } break; default: rc = asprintf(&out, "{ saddr_fam=%s (unsupported) }", str); break; } if (rc < 0) out = NULL; free((char *)host); return out; } static const char *print_promiscuous(const char *val) { int ival; errno = 0; ival = strtol(val, NULL, 10); if (errno) { char *out; if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } if (ival == 0) return strdup("no"); else return strdup("yes"); } static const char *print_capabilities(const char *val, int base) { int cap; char *out; const char *s; errno = 0; cap = strtoul(val, NULL, base); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } s = cap_i2s(cap); if (s != NULL) return strdup(s); if (asprintf(&out, "unknown-capability(%s%s)", base == 16 ? "0x" : "", val) < 0) out = NULL; return out; } static const char *print_cap_bitmap(const char *val) { #define MASK(x) (1U << (x)) unsigned long long temp; __u32 caps[2]; int i, found=0; char *p, buf[600]; // 17 per cap * 33 errno = 0; temp = strtoull(val, NULL, 16); if (errno) { char *out; if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } caps[0] = temp & 0x00000000FFFFFFFFLL; caps[1] = (temp & 0xFFFFFFFF00000000LL) >> 32; p = buf; for (i=0; i <= CAP_LAST_CAP; i++) { if (MASK(i%32) & caps[i/32]) { const char *s; if (found) p = stpcpy(p, ","); s = cap_i2s(i); if (s != NULL) p = stpcpy(p, s); found = 1; } } if (found == 0) return strdup("none"); return strdup(buf); } static const char *print_success(const char *val) { int res; if (isdigit((unsigned char)*val)) { errno = 0; res = strtoul(val, NULL, 10); if (errno) { char *out; if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } return strdup(aulookup_success(res)); } else return strdup(val); } static const char *print_open_flags(const char *val, int base) { size_t i; unsigned long flags; int cnt = 0; char *out, buf[sizeof(open_flag_strings)+OPEN_FLAG_NUM_ENTRIES+1]; errno = 0; flags = strtoul(val, NULL, base); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } buf[0] = 0; if ((flags & O_ACCMODE) == 0) { // Handle O_RDONLY specially strcat(buf, "O_RDONLY"); cnt++; } for (i=0; i<OPEN_FLAG_NUM_ENTRIES; i++) { if (open_flag_table[i].value & flags) { if (!cnt) { strcat(buf, open_flag_strings + open_flag_table[i].offset); cnt++; } else { strcat(buf, "|"); strcat(buf, open_flag_strings + open_flag_table[i].offset); } } } if (buf[0] == 0) snprintf(buf, sizeof(buf), "0x%s", val); return strdup(buf); } static const char* print_xattr_atflags(const char* val) { unsigned int flags; int cnt = 0; char* out, *p, buf[sizeof(xattr_atflag_strings) + XATTR_ATFLAG_NUM_ENTRIES + 1]; errno = 0; flags = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } p = buf; for (unsigned int i = 0; i < XATTR_ATFLAG_NUM_ENTRIES; i++) { if (xattr_atflag_table[i].value & flags) { if (cnt) p = stpcpy(p, "|"); p = stpcpy(p, xattr_atflag_strings + xattr_atflag_table[i].offset); cnt++; } } if (cnt == 0) snprintf(buf, sizeof(buf), "0x%s", val); return strdup(buf); } static const char *print_clone_flags(const char *val) { unsigned int flags, i, clone_sig; int cnt = 0; // + 10 for signal name char *out, buf[sizeof(clone_flag_strings)+CLONE_FLAG_NUM_ENTRIES+10]; errno = 0; flags = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } buf[0] = 0; for (i=0; i<CLONE_FLAG_NUM_ENTRIES; i++) { if (clone_flag_table[i].value & flags) { if (!cnt) { strcat(buf, clone_flag_strings + clone_flag_table[i].offset); cnt++; } else { strcat(buf, "|"); strcat(buf, clone_flag_strings + clone_flag_table[i].offset); } } } clone_sig = flags & 0xFF; if (clone_sig && (clone_sig < 32)) { const char *s = signal_i2s(clone_sig); if (s != NULL) { if (buf[0] != 0) strcat(buf, "|"); strcat(buf, s); } } if (buf[0] == 0) snprintf(buf, sizeof(buf), "0x%x", flags); return strdup(buf); } static const char *print_fcntl_cmd(const char *val) { char *out; const char *s; int cmd; errno = 0; cmd = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } s = fcntl_i2s(cmd); if (s != NULL) return strdup(s); if (asprintf(&out, "unknown-fcntl-command(%d)", cmd) < 0) out = NULL; return out; } static const char *print_epoll_ctl(const char *val) { char *out; const char *s; int cmd; errno = 0; cmd = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } s = epoll_ctl_i2s(cmd); if (s != NULL) return strdup(s); if (asprintf(&out, "unknown-epoll_ctl-operation(%d)", cmd) < 0) out = NULL; return out; } static const char *print_clock_id(const char *val) { int i; char *out; errno = 0; i = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } else if (i < 7) { const char *s = clock_i2s(i); if (s != NULL) return strdup(s); } if (asprintf(&out, "unknown-clk_id(0x%s)", val) < 0) out = NULL; return out; } static const char *print_prot(const char *val, unsigned int is_mmap) { unsigned int prot, i, limit; int cnt = 0; char buf[sizeof(prot_strings)+PROT_NUM_ENTRIES+1]; char *out; errno = 0; prot = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } buf[0] = 0; if ((prot & 0x07) == 0) { // Handle PROT_NONE specially strcat(buf, "PROT_NONE"); return strdup(buf); } if (is_mmap) limit = 4; else limit = 3; for (i=0; i < limit; i++) { if (prot_table[i].value & prot) { if (!cnt) { strcat(buf, prot_strings + prot_table[i].offset); cnt++; } else { strcat(buf, "|"); strcat(buf, prot_strings + prot_table[i].offset); } } } if (buf[0] == 0) snprintf(buf, sizeof(buf), "0x%s", val); return strdup(buf); } static const char *print_mmap(const char *val) { unsigned int maps, i; int cnt = 0; char buf[sizeof(mmap_strings)+MMAP_NUM_ENTRIES+1]; char *out; errno = 0; maps = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } buf[0] = 0; if ((maps & 0x0F) == 0) { // Handle MAP_FILE specially strcat(buf, "MAP_FILE"); cnt++; } for (i=0; i<MMAP_NUM_ENTRIES; i++) { if (mmap_table[i].value & maps) { if (!cnt) { strcat(buf, mmap_strings + mmap_table[i].offset); cnt++; } else { strcat(buf, "|"); strcat(buf, mmap_strings + mmap_table[i].offset); } } } if (buf[0] == 0) snprintf(buf, sizeof(buf), "0x%s", val); return strdup(buf); } static const char *print_personality(const char *val) { int pers, pers2; char *out; const char *s; errno = 0; pers = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } pers2 = pers & PER_MASK; s = person_i2s(pers2); if (s != NULL) { if (pers & ADDR_NO_RANDOMIZE) { if (asprintf(&out, "%s|~ADDR_NO_RANDOMIZE", s) < 0) out = NULL; return out; } else return strdup(s); } if (asprintf(&out, "unknown-personality(0x%s)", val) < 0) out = NULL; return out; } static const char *print_ptrace(const char *val) { int trace; char *out; const char *s; errno = 0; trace = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } s = ptrace_i2s(trace); if (s != NULL) return strdup(s); if (asprintf(&out, "unknown-ptrace(0x%s)", val) < 0) out = NULL; return out; } static const char *print_prctl_opt(const char *val) { int opt; char *out; const char *s; errno = 0; opt = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } s = prctl_opt_i2s(opt); if (s != NULL) return strdup(s); if (asprintf(&out, "unknown-prctl-option(0x%s)", val) < 0) out = NULL; return out; } static const char *print_mount(const char *val) { unsigned int mounts, i; int cnt = 0; char buf[sizeof(mount_strings)+MOUNT_NUM_ENTRIES+1]; char *out; errno = 0; mounts = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } buf[0] = 0; for (i=0; i<MOUNT_NUM_ENTRIES; i++) { if (mount_table[i].value & mounts) { if (!cnt) { strcat(buf, mount_strings + mount_table[i].offset); cnt++; } else { strcat(buf, "|"); strcat(buf, mount_strings + mount_table[i].offset); } } } if (buf[0] == 0) snprintf(buf, sizeof(buf), "0x%s", val); return strdup(buf); } static const char *print_fsconfig(const char *val) { char *out; const char *s; int cmd; errno = 0; cmd = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } s = fsconfig_i2s(cmd); if (s != NULL) return strdup(s); if (asprintf(&out, "unknown-fsconfig-operation(%d)", cmd) < 0) out = NULL; return out; } static const char *print_rlimit(const char *val) { int i; char *out; errno = 0; i = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } else if (i < 17) { const char *s = rlimit_i2s(i); if (s != NULL) return strdup(s); } if (asprintf(&out, "unknown-rlimit(0x%s)", val) < 0) out = NULL; return out; } static const char *print_recv(const char *val) { unsigned int rec, i; int cnt = 0; char buf[sizeof(recv_strings)+RECV_NUM_ENTRIES+1]; char *out; errno = 0; rec = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } buf[0] = 0; for (i=0; i<RECV_NUM_ENTRIES; i++) { if (recv_table[i].value & rec) { if (!cnt) { strcat(buf, recv_strings + recv_table[i].offset); cnt++; } else { strcat(buf, "|"); strcat(buf, recv_strings + recv_table[i].offset); } } } if (buf[0] == 0) snprintf(buf, sizeof(buf), "0x%s", val); return strdup(buf); } static const char* print_access_mode(const char* val) { unsigned long mode; char buf[sizeof(access_strings) + ACCESS_NUM_ENTRIES + 1]; unsigned int i, cnt = 0; errno = 0; mode = strtoul(val, NULL, 16); if (errno) { char* out; if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } if ((mode & 0xF) == 0) return strdup("F_OK"); buf[0] = 0; for (i = 0; i < 3; i++) { if (access_table[i].value & mode) { if (!cnt) { strcat(buf, access_strings + access_table[i].offset); cnt++; } else { strcat(buf, "|"); strcat(buf, access_strings + access_table[i].offset); } } } if (buf[0] == 0) snprintf(buf, sizeof(buf), "0x%s", val); return strdup(buf); } static const char* print_access_flags(const char* val) { unsigned long mode; char buf[sizeof(access_flag_strings) + ACCESS_FLAG_NUM_ENTRIES + 1]; unsigned int i, cnt = 0; errno = 0; mode = strtoul(val, NULL, 16); if (errno) { char* out; if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } buf[0] = 0; for (i = 0; i < ACCESS_FLAG_NUM_ENTRIES; i++) { if (access_flag_table[i].value & mode) { if (!cnt) { strcat(buf, access_flag_strings + access_flag_table[i].offset); cnt++; } else { strcat(buf, "|"); strcat(buf, access_flag_strings + access_flag_table[i].offset); } } } if (buf[0] == 0) snprintf(buf, sizeof(buf), "0x%s", val); return strdup(buf); } static char *print_dirfd(const char *val) { char *out; errno = 0; uint32_t i = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } if (i == 0xffffff9c) { if (asprintf(&out, "AT_FDCWD") < 0) out = NULL; } else { if (asprintf(&out, "0x%s", val) < 0) out = NULL; } return out; } #ifndef SCHED_RESET_ON_FORK #define SCHED_RESET_ON_FORK 0x40000000 #endif static const char *print_sched(const char *val) { unsigned int pol; char *out; const char *s; errno = 0; pol = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } s = sched_i2s(pol & 0x0F); if (s != NULL) { char buf[48]; strcpy(buf, s); if (pol & SCHED_RESET_ON_FORK ) strcat(buf, "|SCHED_RESET_ON_FORK"); return strdup(buf); } if (asprintf(&out, "unknown-scheduler-policy(0x%s)", val) < 0) out = NULL; return out; } static const char *print_sock_opt_level(const char *val) { int lvl; char *out; errno = 0; lvl = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } if (lvl == SOL_SOCKET) return strdup("SOL_SOCKET"); else { struct protoent *p = getprotobynumber(lvl); if (p == NULL) { const char *s = socklevel_i2s(lvl); if (s != NULL) return strdup(s); if (asprintf(&out, "unknown-sockopt-level(0x%s)", val) < 0) out = NULL; } else return strdup(p->p_name); } return out; } static const char *print_sock_opt_name(const char *val, int machine) { int opt; char *out; const char *s; errno = 0; opt = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } // PPC's tables are different if ((machine == MACH_PPC64 || machine == MACH_PPC) && opt >= 16 && opt <= 21) opt+=100; s = sockoptname_i2s(opt); if (s != NULL) return strdup(s); if (asprintf(&out, "unknown-sockopt-name(0x%s)", val) < 0) out = NULL; return out; } static const char *print_ip_opt_name(const char *val) { int opt; char *out; const char *s; errno = 0; opt = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } s = ipoptname_i2s(opt); if (s != NULL) return strdup(s); if (asprintf(&out, "unknown-ipopt-name(0x%s)", val) < 0) out = NULL; return out; } static const char *print_ip6_opt_name(const char *val) { int opt; char *out; const char *s; errno = 0; opt = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } s = ip6optname_i2s(opt); if (s != NULL) return strdup(s); if (asprintf(&out, "unknown-ip6opt-name(0x%s)", val) < 0) out = NULL; return out; } static const char *print_tcp_opt_name(const char *val) { int opt; char *out; const char *s; errno = 0; opt = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } s = tcpoptname_i2s(opt); if (s != NULL) return strdup(s); if (asprintf(&out, "unknown-tcpopt-name(0x%s)", val) < 0) out = NULL; return out; } static const char *print_udp_opt_name(const char *val) { int opt; char *out; errno = 0; opt = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } if (opt == 1) out = strdup("UDP_CORK"); else if (opt == 100) out = strdup("UDP_ENCAP"); else if (asprintf(&out, "unknown-udpopt-name(0x%s)", val) < 0) out = NULL; return out; } static const char *print_pkt_opt_name(const char *val) { int opt; char *out; const char *s; errno = 0; opt = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } s = pktoptname_i2s(opt); if (s != NULL) return strdup(s); if (asprintf(&out, "unknown-pktopt-name(0x%s)", val) < 0) out = NULL; return out; } static const char *print_shmflags(const char *val) { unsigned int flags, partial, i; int cnt = 0; char *out, buf[sizeof(shm_mode_strings)+sizeof(ipccmd_strings)+SHM_MODE_NUM_ENTRIES+IPCCMD_NUM_ENTRIES+1]; errno = 0; flags = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } partial = flags & 00003000; buf[0] = 0; for (i=0; i<IPCCMD_NUM_ENTRIES; i++) { if (ipccmd_table[i].value & partial) { if (!cnt) { strcat(buf, ipccmd_strings + ipccmd_table[i].offset); cnt++; } else { strcat(buf, "|"); strcat(buf, ipccmd_strings + ipccmd_table[i].offset); } } } partial = flags & 00014000; for (i=0; i<SHM_MODE_NUM_ENTRIES; i++) { if (shm_mode_table[i].value & partial) { if (!cnt) { strcat(buf, shm_mode_strings + shm_mode_table[i].offset); cnt++; } else { strcat(buf, "|"); strcat(buf, shm_mode_strings + shm_mode_table[i].offset); } } } partial = flags & 000777; const char *tmode = print_mode_short_int(partial); if (tmode) { if (buf[0] != 0) strcat(buf, "|"); strcat(buf, tmode); free((void *)tmode); } if (buf[0] == 0) snprintf(buf, sizeof(buf), "0x%x", flags); return strdup(buf); } static const char *print_seek(const char *val) { unsigned int whence; char *out; const char *str; errno = 0; whence = 0xFF & strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } str = seek_i2s(whence); if (str == NULL) { if (asprintf(&out, "unknown-whence(%s)", val) < 0) out = NULL; return out; } else return strdup(str); } static const char *print_umount(const char *val) { unsigned int flags, i; int cnt = 0; char buf[sizeof(umount_strings)+UMOUNT_NUM_ENTRIES+1]; char *out; errno = 0; flags = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } buf[0] = 0; for (i=0; i<UMOUNT_NUM_ENTRIES; i++) { if (umount_table[i].value & flags) { if (!cnt) { strcat(buf, umount_strings + umount_table[i].offset); cnt++; } else { strcat(buf, "|"); strcat(buf, umount_strings + umount_table[i].offset); } } } if (buf[0] == 0) snprintf(buf, sizeof(buf), "0x%s", val); return strdup(buf); } static const char *print_ioctl_req(const char *val) { int req; char *out; const char *r; errno = 0; req = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } r = ioctlreq_i2s(req); if (r != NULL) return strdup(r); if (asprintf(&out, "0x%x", req) < 0) out = NULL; return out; } static const char *fanotify[3] = { "unknown", "allow", "deny" }; static const char *aulookup_fanotify(unsigned s) { switch (s) { default: return fanotify[0]; case FAN_ALLOW: return fanotify[1]; case FAN_DENY: return fanotify[2]; } } static const char *print_fanotify(const char *val) { int res; if (isdigit((unsigned char)*val)) { errno = 0; res = strtoul(val, NULL, 10); if (errno) { char *out; if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } return strdup(aulookup_fanotify(res)); } else return strdup(val); } static const char *print_exit_syscall(const char *val) { char *out; if (strcmp(val, "0") == 0) out = strdup("EXIT_SUCCESS"); else if (strcmp(val, "1") == 0) out = strdup("EXIT_FAILURE"); else out = strdup("UNKNOWN"); return out; } static const char *print_bpf(const char *val) { unsigned int cmd; char *out; const char *str; errno = 0; cmd = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } str = bpf_i2s(cmd); if (str == NULL) { if (asprintf(&out, "unknown-bpf-cmd(%s)", val) < 0) out = NULL; return out; } else return strdup(str); } static const char *print_openat2_resolve(const char *val) { size_t i; unsigned long long resolve; int cnt = 0; char *out, buf[sizeof(openat2_resolve_strings)+8]; errno = 0; resolve = strtoull(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } buf[0] = 0; for (i=0; i<OPENAT2_RESOLVE_NUM_ENTRIES; i++) { if (openat2_resolve_table[i].value & resolve) { if (!cnt) { strcat(buf, openat2_resolve_strings + openat2_resolve_table[i].offset); cnt++; } else { strcat(buf, "|"); strcat(buf, openat2_resolve_strings + openat2_resolve_table[i].offset); } } } if (buf[0] == 0) snprintf(buf, sizeof(buf), "0x%s", val); return strdup(buf); } static const char *print_trust(const char *val) { const char *out; if (strcmp(val, "0") == 0) out = strdup("no"); else if (strcmp(val, "1") == 0) out = strdup("yes"); else out = strdup("unknown"); return out; } // fan_type always precedes fan_info static int last_type = 2; static const char *print_fan_type(const char *val) { const char *out; if (strcmp(val, "0") == 0) { out = strdup("none"); last_type = 0; } else if (strcmp(val, "1") == 0) { out = strdup("rule_info"); last_type = 1; } else { out = strdup("unknown"); last_type = 2; } return out; } static const char *print_fan_info(const char *val) { char *out; if (last_type == 1) { errno = 0; unsigned long info = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } else { if (asprintf(&out, "%lu", info) < 0) out = NULL; return out; } } else out = strdup(val); return out; } /* This is in IMA audit events */ static const char *print_errno(const char *val) { char *out; const char *err_name; int err; errno = 0; err = strtoul(val, NULL, 10); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } err_name = audit_errno_to_name(err); if (err_name == NULL) out = strdup("UNKNOWN"); else out = strdup(err_name); return out; } static const char* print_a0(auparse_state_t *au, const char* val, const idata* id) { char* out; int machine = id->machine, syscall = id->syscall; const char* sys = audit_syscall_to_name(syscall, machine); if (sys) { if (*sys == 'r') { if (strcmp(sys, "rt_sigaction") == 0) return print_signals(val, 16); else if (strncmp(sys, "renameat", 8) == 0) return print_dirfd(val); else if (strcmp(sys, "readlinkat") == 0) return print_dirfd(val); else if (strcmp(sys, "removexattrat") == 0) return print_dirfd(val); } else if (*sys == 'c') { if (strcmp(sys, "clock_settime") == 0) return print_clock_id(val); // Don't get fooled by the man page. It always // shows the glibc wrapper arguments. else if (strcmp(sys, "clone") == 0) return print_clone_flags(val); else if (strcmp(sys, "clone2") == 0) return print_clone_flags(val); } else if (*sys == 'p') { if (strcmp(sys, "personality") == 0) return print_personality(val); else if (strcmp(sys, "ptrace") == 0) return print_ptrace(val); else if (strcmp(sys, "prctl") == 0) return print_prctl_opt(val); } else if (*sys == 'm') { if (strcmp(sys, "mkdirat") == 0) return print_dirfd(val); else if (strcmp(sys, "mknodat") == 0) return print_dirfd(val); } else if (*sys == 'f') { if (strcmp(sys, "fchownat") == 0) return print_dirfd(val); else if (strcmp(sys, "futimesat") == 0) return print_dirfd(val); else if (strcmp(sys, "fchmodat") == 0) return print_dirfd(val); else if (strncmp(sys, "faccessat", 9) == 0) return print_dirfd(val); else if (strcmp(sys, "futimensat") == 0) return print_dirfd(val); else if (strcmp(sys, "file_getattr") == 0) return print_dirfd(val); else if (strcmp(sys, "file_setattr") == 0) return print_dirfd(val); } else if (*sys == 'u') { if (strcmp(sys, "unshare") == 0) return print_clone_flags(val); else if (strcmp(sys, "unlinkat") == 0) return print_dirfd(val); else if (strcmp(sys, "utimensat") == 0) return print_dirfd(val); } else if (strcmp(sys + 1, "etrlimit") == 0) return print_rlimit(val); else if (*sys == 's') { if (strcmp(sys, "setuid") == 0) return print_uid(au, val, 16); else if (strcmp(sys, "setreuid") == 0) return print_uid(au, val, 16); else if (strcmp(sys, "setresuid") == 0) return print_uid(au, val, 16); else if (strcmp(sys, "setfsuid") == 0) return print_uid(au, val, 16); else if (strcmp(sys, "setgid") == 0) return print_gid(au, val, 16); else if (strcmp(sys, "setregid") == 0) return print_gid(au, val, 16); else if (strcmp(sys, "setresgid") == 0) return print_gid(au, val, 16); else if (strcmp(sys, "socket") == 0) return print_socket_domain(val); else if (strcmp(sys, "setfsgid") == 0) return print_gid(au, val, 16); else if (strcmp(sys, "socketcall") == 0) return print_socketcall(val, 16); else if (strcmp(sys, "setxattrat") == 0) return print_dirfd(val); } else if (*sys == 'l') { if (strcmp(sys, "linkat") == 0) return print_dirfd(val); else if (strcmp(sys, "listxattrat") == 0) return print_dirfd(val); } else if (strcmp(sys, "newfstatat") == 0) return print_dirfd(val); else if (strncmp(sys, "openat", 6) == 0) return print_dirfd(val); else if (strcmp(sys, "name_to_handle_at") == 0) return print_dirfd(val); else if (strcmp(sys, "ipccall") == 0) return print_ipccall(val, 16); else if (strncmp(sys, "exit", 4) == 0) return print_exit_syscall(val); else if (strcmp(sys, "bpf") == 0) return print_bpf(val); else if (strcmp(sys, "getxattrat") == 0) return print_dirfd(val); } if (asprintf(&out, "0x%s", val) < 0) out = NULL; return out; } static const char *print_a1(auparse_state_t *au, const char *val, const idata *id) { char *out; int machine = id->machine, syscall = id->syscall; const char *sys = audit_syscall_to_name(syscall, machine); if (sys) { if (*sys == 'f') { if (strcmp(sys, "fchmod") == 0) return print_mode_short(val, 16); else if (strncmp(sys, "fcntl", 5) == 0) return print_fcntl_cmd(val); else if (strncmp(sys, "fsconfig", 5) == 0) return print_fsconfig(val); else if (strncmp(sys, "fsopen", 6) == 0) { if (strcmp(val, "1") == 0) return strdup("FSOPEN_CLOEXEC"); } else if (strncmp(sys, "fsmount", 7) == 0) { if (strcmp(val, "1") == 0) return strdup("FSMOUNT_CLOEXEC"); } } else if (*sys == 'c') { if (strcmp(sys, "chmod") == 0) return print_mode_short(val, 16); else if (strstr(sys, "chown")) return print_uid(au, val, 16); else if (strcmp(sys, "creat") == 0) return print_mode_short(val, 16); } if (strcmp(sys+1, "etsockopt") == 0) return print_sock_opt_level(val); else if (*sys == 's') { if (strcmp(sys, "setreuid") == 0) return print_uid(au, val, 16); else if (strcmp(sys, "setresuid") == 0) return print_uid(au, val, 16); else if (strcmp(sys, "setregid") == 0) return print_gid(au, val, 16); else if (strcmp(sys, "setresgid") == 0) return print_gid(au, val, 16); else if (strcmp(sys, "socket") == 0) return print_socket_type(val); else if (strcmp(sys, "setns") == 0) return print_clone_flags(val); else if (strcmp(sys, "sched_setscheduler") == 0) return print_sched(val); } else if (*sys == 'm') { if (strcmp(sys, "mkdir") == 0) return print_mode_short(val, 16); else if (strcmp(sys, "mknod") == 0) return print_mode(val, 16); else if (strcmp(sys, "mq_open") == 0) return print_open_flags(val, 16); } else if (strcmp(sys, "open") == 0) return print_open_flags(val, 16); else if (strcmp(sys, "access") == 0) return print_access_mode(val); else if (strcmp(sys, "epoll_ctl") == 0) return print_epoll_ctl(val); else if (strcmp(sys, "kill") == 0) return print_signals(val, 16); else if (strcmp(sys, "prctl") == 0) { if (id->a0 == PR_CAPBSET_READ || id->a0 == PR_CAPBSET_DROP) return print_capabilities(val, 16); else if (id->a0 == PR_SET_PDEATHSIG) return print_signals(val, 16); } else if (strcmp(sys, "tkill") == 0) return print_signals(val, 16); else if (strcmp(sys, "umount2") == 0) return print_umount(val); else if (strcmp(sys, "ioctl") == 0) return print_ioctl_req(val); } if (asprintf(&out, "0x%s", val) < 0) out = NULL; return out; } static const char *print_a2(auparse_state_t *au, const char *val, const idata *id) { char *out; int machine = id->machine, syscall = id->syscall; const char *sys = audit_syscall_to_name(syscall, machine); if (sys) { if (strncmp(sys, "fcntl", 5) == 0) { int ival; errno = 0; ival = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } switch (id->a1) { case F_SETOWN: return print_uid(au, val, 16); case F_SETFD: if (ival == FD_CLOEXEC) return strdup("FD_CLOEXEC"); /* Fall thru okay. */ case F_SETFL: case F_SETLEASE: case F_GETLEASE: case F_NOTIFY: break; } } else if (strcmp(sys+1, "etsockopt") == 0) { if (id->a1 == IPPROTO_IP) return print_ip_opt_name(val); else if (id->a1 == SOL_SOCKET) return print_sock_opt_name(val, machine); else if (id->a1 == IPPROTO_TCP) return print_tcp_opt_name(val); else if (id->a1 == IPPROTO_UDP) return print_udp_opt_name(val); else if (id->a1 == IPPROTO_IPV6) return print_ip6_opt_name(val); else if (id->a1 == SOL_PACKET) return print_pkt_opt_name(val); else goto normal; } else if (*sys == 'o') { if (strcmp(sys, "openat") == 0) return print_open_flags(val, 16); if ((strcmp(sys, "open") == 0) && (id->a1 & O_CREAT)) return print_mode_short(val, 16); if (strcmp(sys, "open_by_handle_at") == 0) return print_open_flags(val, 16); } else if (*sys == 'f') { if (strcmp(sys, "fchmodat") == 0) return print_mode_short(val, 16); else if (strncmp(sys, "faccessat", 9) == 0) return print_access_mode(val); else if (strncmp(sys, "fsmount", 7) == 0) return print_mount(val); } else if (*sys == 's') { if (strcmp(sys, "setresuid") == 0) return print_uid(au, val, 16); else if (strcmp(sys, "setresgid") == 0) return print_gid(au, val, 16); else if (strcmp(sys, "socket") == 0) return print_socket_proto(val); else if (strcmp(sys, "sendmsg") == 0) return print_recv(val); else if (strcmp(sys, "shmget") == 0) return print_shmflags(val); else if (strcmp(sys, "setxattrat") == 0) return print_xattr_atflags(val); } else if (*sys == 'm') { if (strcmp(sys, "mmap") == 0) return print_prot(val, 1); else if (strcmp(sys, "mkdirat") == 0) return print_mode_short(val, 16); else if (strcmp(sys, "mknodat") == 0) return print_mode_short(val, 16); else if (strcmp(sys, "mprotect") == 0) return print_prot(val, 0); else if (strcmp(sys, "move_mount") == 0) return print_dirfd(val); else if ((strcmp(sys, "mq_open") == 0) && (id->a1 & O_CREAT)) return print_mode_short(val, 16); } else if (*sys == 'r') { if (strcmp(sys, "recvmsg") == 0) return print_recv(val); else if (strcmp(sys, "readlinkat") == 0) return print_dirfd(val); else if (strncmp(sys, "renameat", 8) == 0) return print_dirfd(val); else if (strcmp(sys, "removexattrat") == 0) return print_xattr_atflags(val); } else if (*sys == 'l') { if (strcmp(sys, "linkat") == 0) return print_dirfd(val); else if (strcmp(sys, "lseek") == 0) return print_seek(val); else if (strcmp(sys, "listxattrat") == 0) return print_xattr_atflags(val); } else if (strstr(sys, "chown")) return print_gid(au, val, 16); else if (strcmp(sys, "tgkill") == 0) return print_signals(val, 16); else if (strstr(sys, "getxattrat")) return print_xattr_atflags(val); } normal: if (asprintf(&out, "0x%s", val) < 0) out = NULL; return out; } static const char *print_a3(auparse_state_t *au, const char *val, const idata *id) { char *out; int machine = id->machine, syscall = id->syscall; const char *sys = audit_syscall_to_name(syscall, machine); if (sys) { if (*sys == 'm') { if (strcmp(sys, "mmap") == 0) return print_mmap(val); else if (strcmp(sys, "mount") == 0) return print_mount(val); } else if (*sys == 'r') { if (strcmp(sys, "recv") == 0) return print_recv(val); else if (strcmp(sys, "recvfrom") == 0) return print_recv(val); else if (strcmp(sys, "recvmmsg") == 0) return print_recv(val); } else if (*sys == 's') { if (strcmp(sys, "send") == 0) return print_recv(val); else if (strcmp(sys, "sendto") == 0) return print_recv(val); else if (strcmp(sys, "sendmmsg") == 0) return print_recv(val); } else if (*sys == 'f') { if (strcmp(sys, "faccessat") == 0) return print_access_flags(val); else if (strcmp(sys, "faccessat2") == 0) return print_access_flags(val); } else if (strcmp(sys, "open_tree_attr") == 0) return print_xattr_atflags(val); } if (asprintf(&out, "0x%s", val) < 0) out = NULL; return out; } static const char *print_signals(const char *val, unsigned int base) { int i; char *out; errno = 0; i = strtoul(val, NULL, base); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } else if (i < 32) { const char *s = signal_i2s(i); if (s != NULL) return strdup(s); } if (asprintf(&out, "unknown-signal(%s%s)", base == 16 ? "0x" : "", val) < 0) out = NULL; return out; } static const char *print_nfproto(const char *val) { int proto; char *out; const char *s; errno = 0; proto = strtoul(val, NULL, 10); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } s = nfproto_i2s(proto); if (s != NULL) return strdup(s); if (asprintf(&out, "unknown-netfilter-protocol(%s)", val) < 0) out = NULL; return out; } static const char *print_icmptype(const char *val) { int icmptype; char *out; const char *s; errno = 0; icmptype = strtoul(val, NULL, 10); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } s = icmptype_i2s(icmptype); if (s != NULL) return strdup(s); if (asprintf(&out, "unknown-icmp-type(%s)", val) < 0) out = NULL; return out; } static const char *print_protocol(const char *val) { int i; char *out; errno = 0; i = strtoul(val, NULL, 10); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; } else { struct protoent *p = getprotobynumber(i); if (p) out = strdup(p->p_name); else out = strdup("undefined protocol"); } return out; } /* Netfilter hook names */ static const char *print_hook(auparse_state_t *au, const char *val) { int hook; char *out; const char *str; const char *fam; int proto = -1; unsigned int record, field; errno = 0; hook = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } record = auparse_get_record_num(au); field = auparse_get_field_num(au); fam = auparse_find_field(au, "family"); if (fam) { errno = 0; proto = strtoul(fam, NULL, 10); if (errno) proto = -1; } if (auparse_goto_record_num(au, record)) auparse_goto_field_num(au, field); if (proto == NFPROTO_ARP) str = arphook_i2s(hook); else str = inethook_i2s(hook); if (str == NULL) { if (asprintf(&out, "unknown-hook(%s)", val) < 0) out = NULL; return out; } else return strdup(str); } static const char *print_netaction(const char *val) { int action; char *out; const char *str; errno = 0; action = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } str = netaction_i2s(action); if (str == NULL) { if (asprintf(&out, "unknown-action(%s)", val) < 0) out = NULL; return out; } else return strdup(str); } /* Ethernet packet types */ static const char *print_macproto(const char *val) { int type; char *out; errno = 0; type = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } if (type == 0x0800) return strdup("IP"); else if (type == 0x0806) return strdup("ARP"); return strdup("UNKNOWN"); } static const char *print_addr(const char *val) { char *out = strdup(val); return out; } static const char *print_list(const char *val) { int i; char *out; errno = 0; i = strtoul(val, NULL, 10); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; } else { const char *o = audit_flag_to_name(i); if (o != NULL) out = strdup(o); else if (asprintf(&out, "unknown-list(%s)", val) < 0) out = NULL; } return out; } struct string_buf { char *buf; /* NULL if was ever out of memory */ size_t allocated; size_t pos; }; /* Append c to buf. */ static void append_char(struct string_buf *buf, char c) { if (buf->buf == NULL) return; if (buf->pos == buf->allocated) { char *p; buf->allocated *= 2; p = realloc(buf->buf, buf->allocated); if (p == NULL) { free(buf->buf); buf->buf = NULL; return; } buf->buf = p; } buf->buf[buf->pos] = c; buf->pos++; } /* Represent c as a character within a quoted string, and append it to buf. */ static void tty_append_printable_char(struct string_buf *buf, unsigned char c) { if (c < 0x20 || c > 0x7E) { append_char(buf, '\\'); append_char(buf, '0' + ((c >> 6) & 07)); append_char(buf, '0' + ((c >> 3) & 07)); append_char(buf, '0' + (c & 07)); } else { if (c == '\\' || c == '"') append_char(buf, '\\'); append_char(buf, c); } } /* Search for a name of a sequence of TTY bytes. If found, return the name and advance *INPUT. Return NULL otherwise. */ static const char *tty_find_named_key(unsigned char **input, size_t input_len) { /* NUL-terminated list of (sequence, NUL, name, NUL) entries. First match wins, even if a longer match were possible later */ static const unsigned char named_keys[] = #define E(SEQ, NAME) SEQ "\0" NAME "\0" #include "tty_named_keys.h" #undef E "\0"; unsigned char *src; const unsigned char *nk; src = *input; if (*src >= ' ' && (*src < 0x7F || *src >= 0xA0)) return NULL; /* Fast path */ nk = named_keys; do { const unsigned char *p; size_t nk_len; p = strchr(nk, '\0'); nk_len = p - nk; if (nk_len <= input_len && memcmp(src, nk, nk_len) == 0) { *input += nk_len; return p + 1; } nk = strchr(p + 1, '\0') + 1; } while (*nk != '\0'); return NULL; } static const char *print_tty_data(const char *raw_data) { struct string_buf buf; int in_printable; unsigned char *data, *data_pos, *data_end; if (!is_hex_string(raw_data)) return strdup(raw_data); data = au_unescape(raw_data); if (data == NULL) return NULL; data_end = data + strlen(raw_data) / 2; buf.allocated = 10; buf.buf = malloc(buf.allocated); /* NULL handled in append_char() */ buf.pos = 0; in_printable = 0; data_pos = data; while (data_pos < data_end) { /* FIXME: Unicode */ const char *desc; desc = tty_find_named_key(&data_pos, data_end - data_pos); if (desc != NULL) { if (in_printable != 0) { append_char(&buf, '"'); in_printable = 0; } if (buf.pos != 0) append_char(&buf, ','); append_char(&buf, '<'); while (*desc != '\0') { append_char(&buf, *desc); desc++; } append_char(&buf, '>'); } else { if (in_printable == 0) { if (buf.pos != 0) append_char(&buf, ','); append_char(&buf, '"'); in_printable = 1; } tty_append_printable_char(&buf, *data_pos); data_pos++; } } if (in_printable != 0) append_char(&buf, '"'); append_char(&buf, '\0'); free(data); return buf.buf; } static const char *print_session(const char *val) { if (strcmp(val, "4294967295") == 0) return strdup("unset"); else return strdup(val); } static const char *print_seccomp_code(const char *val) { unsigned long code; char *out; const char *s; errno = 0; code = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } s = seccomp_i2s((unsigned int)code & ~SECCOMP_RET_DATA); if (s != NULL) return strdup(s); if (asprintf(&out, "unknown-seccomp-code(%s)", val) < 0) out = NULL; return out; } static const char *nlmcgrp[2]= { "audit-none", "audit-netlink-multicast" }; static const char *print_nlmcgrp(const char *val) { unsigned long nl; errno = 0; nl = strtoul(val, NULL, 16); if (errno) { char *out; if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } switch (nl) { default: return strdup(nlmcgrp[0]); #ifdef AUDIT_NLGRP_MAX case AUDIT_NLGRP_READLOG: return strdup(nlmcgrp[1]); #endif } } int lookup_type(const char *name) { int i; if (type_s2i(name, &i) != 0) return i; return AUPARSE_TYPE_UNCLASSIFIED; } /* * This is the main entry point for the auparse library. Call chain is: * auparse_interpret_field -> nvlist_interp_cur_val -> do_interpret */ const char *do_interpret(auparse_state_t *au, rnode *r) { nvlist *nv = &r->nv; int type; idata id; nvnode *n; const char *out; id.machine = r->machine; id.syscall = r->syscall; id.a0 = r->a0; id.a1 = r->a1; id.cwd = r->cwd; id.name = nvlist_get_cur_name(nv); id.val = nvlist_get_cur_val(nv); type = auparse_interp_adjust_type(r->type, id.name, id.val); out = auparse_do_interpretation(au, type, &id, au->escape_mode); n = nvlist_get_cur(nv); n->interp_val = (char *)out; return out; } /* * rtype: the record type * name: the current field name * value: the current field value * Returns: field's internal type is returned */ int auparse_interp_adjust_type(int rtype, const char *name, const char *val) { int type = AUPARSE_TYPE_UNCLASSIFIED; if (!name || !val) { return type; } /* This set of statements overrides or corrects the detection. * In almost all cases its a double use of a field. */ if (rtype == AUDIT_EXECVE && *name == 'a' && strcmp(name, "argc") && !strstr(name, "_len")) type = AUPARSE_TYPE_ESCAPED; else if (rtype == AUDIT_AVC && strcmp(name, "saddr") == 0) type = AUPARSE_TYPE_UNCLASSIFIED; else if (rtype == AUDIT_USER_TTY && strcmp(name, "msg") == 0) type = AUPARSE_TYPE_ESCAPED; else if (rtype == AUDIT_NETFILTER_PKT && strcmp(name, "saddr") == 0) type = AUPARSE_TYPE_ADDR; else if (strcmp(name, "acct") == 0) { if (val[0] == '"') type = AUPARSE_TYPE_ESCAPED; else if (is_hex_string(val)) type = AUPARSE_TYPE_ESCAPED; else type = AUPARSE_TYPE_UNCLASSIFIED; } else if (rtype == AUDIT_MQ_OPEN && strcmp(name, "mode") == 0) type = AUPARSE_TYPE_MODE_SHORT; else if (rtype == AUDIT_CRYPTO_KEY_USER && strcmp(name, "fp") == 0) type = AUPARSE_TYPE_UNCLASSIFIED; else if ((strcmp(name, "id") == 0) && (rtype == AUDIT_ADD_GROUP || rtype == AUDIT_GRP_MGMT || rtype == AUDIT_DEL_GROUP)) type = AUPARSE_TYPE_GID; else if (rtype == AUDIT_TRUSTED_APP) { /* * Could be anything. See if we know the type. If not, * take a guess based on contents of value. */ type = lookup_type(name); if (type == AUPARSE_TYPE_UNCLASSIFIED) { if (val[0] == '"') type = AUPARSE_TYPE_ESCAPED; else if (strcmp(name, "pid") == 0) type = AUPARSE_TYPE_UNCLASSIFIED; /* Check if we have string with only HEX symbols */ else if (is_hex_string(val)) type = AUPARSE_TYPE_ESCAPED; /* Otherwise it really is AUPARSE_TYPE_UNCLASSIFIED */ } } else if (rtype == AUDIT_KERN_MODULE && strcmp(name, "name") == 0) type = AUPARSE_TYPE_ESCAPED; else type = lookup_type(name); return type; } /* * This can be called by either interpret() or from ausearch-report or * auditctl-listing.c. Returns a malloc'ed buffer that the caller must free. */ char *auparse_do_interpretation(auparse_state_t *au, int type, const idata *id, auparse_esc_t escape_mode) { const char *out; nvlist *il = &au->interpretations; // Check the interpretations list first if (interpretation_list_cnt(au)) { nvlist_first(il); if (nvlist_find_name(il, id->name)) { nvnode* node = &il->array[il->cur]; const char *val = node->interp_val; if (val) { // If we don't know what it is when auditd // recorded it, try it again incase the // libraries have been updated to support it. if (strncmp(val, "unknown-", 8 ) == 0) goto unknown; if (type == AUPARSE_TYPE_UID || type == AUPARSE_TYPE_GID) return print_escaped(val); else return strdup(val); } } } unknown: switch(type) { case AUPARSE_TYPE_UID: out = print_uid(au, id->val, 10); break; case AUPARSE_TYPE_GID: out = print_gid(au, id->val, 10); break; case AUPARSE_TYPE_SYSCALL: out = print_syscall(id); break; case AUPARSE_TYPE_ARCH: out = print_arch(id->val, id->machine); break; case AUPARSE_TYPE_EXIT: out = print_exit(id->val); break; case AUPARSE_TYPE_ESCAPED: case AUPARSE_TYPE_ESCAPED_FILE: out = print_escaped_ext(id); break; case AUPARSE_TYPE_ESCAPED_KEY: out = print_escaped(id->val); break; case AUPARSE_TYPE_PERM: out = print_perm(id->val); break; case AUPARSE_TYPE_MODE: out = print_mode(id->val,8); break; case AUPARSE_TYPE_MODE_SHORT: out = print_mode_short(id->val,8); break; case AUPARSE_TYPE_SOCKADDR: out = print_sockaddr(id->val); break; case AUPARSE_TYPE_PROMISC: out = print_promiscuous(id->val); break; case AUPARSE_TYPE_CAPABILITY: out = print_capabilities(id->val, 10); break; case AUPARSE_TYPE_SUCCESS: out = print_success(id->val); break; case AUPARSE_TYPE_A0: out = print_a0(au, id->val, id); break; case AUPARSE_TYPE_A1: out = print_a1(au, id->val, id); break; case AUPARSE_TYPE_A2: out = print_a2(au, id->val, id); break; case AUPARSE_TYPE_A3: out = print_a3(au, id->val, id); break; case AUPARSE_TYPE_SIGNAL: out = print_signals(id->val, 10); break; case AUPARSE_TYPE_LIST: out = print_list(id->val); break; case AUPARSE_TYPE_TTY_DATA: out = print_tty_data(id->val); break; case AUPARSE_TYPE_SESSION: out = print_session(id->val); break; case AUPARSE_TYPE_CAP_BITMAP: out = print_cap_bitmap(id->val); break; case AUPARSE_TYPE_NFPROTO: out = print_nfproto(id->val); break; case AUPARSE_TYPE_ICMPTYPE: out = print_icmptype(id->val); break; case AUPARSE_TYPE_PROTOCOL: out = print_protocol(id->val); break; case AUPARSE_TYPE_ADDR: out = print_addr(id->val); break; case AUPARSE_TYPE_PERSONALITY: out = print_personality(id->val); break; case AUPARSE_TYPE_SECCOMP: out = print_seccomp_code(id->val); break; case AUPARSE_TYPE_OFLAG: // AUDIT_OPENAT2,MQ_OPEN out = print_open_flags(id->val, 0); break; case AUPARSE_TYPE_MMAP: out = print_mmap(id->val); break; case AUPARSE_TYPE_PROCTITLE: out = print_proctitle(id->val); break; case AUPARSE_TYPE_HOOK: out = print_hook(au, id->val); break; case AUPARSE_TYPE_NETACTION: out = print_netaction(id->val); break; case AUPARSE_TYPE_MACPROTO: out = print_macproto(id->val); break; case AUPARSE_TYPE_IOCTL_REQ: out = print_ioctl_req(id->val); break; case AUPARSE_TYPE_FANOTIFY: out = print_fanotify(id->val); break; case AUPARSE_TYPE_NLMCGRP: out = print_nlmcgrp(id->val); break; case AUPARSE_TYPE_RESOLVE: out = print_openat2_resolve(id->val); break; case AUPARSE_TYPE_TRUST: out = print_trust(id->val); break; case AUPARSE_TYPE_FAN_TYPE: out = print_fan_type(id->val); break; case AUPARSE_TYPE_FAN_INFO: out = print_fan_info(id->val); break; case AUPARSE_TYPE_ERRNO: out = print_errno(id->val); break; case AUPARSE_TYPE_MAC_LABEL: case AUPARSE_TYPE_UNCLASSIFIED: default: out = strdup(id->val); break; } if (escape_mode != AUPARSE_ESC_RAW && out) { const char *str = NULL; size_t len = strlen(out); if (type == AUPARSE_TYPE_ESCAPED_KEY) { // The audit key separator causes a false // positive in deciding to escape. str = strchr(out, AUDIT_KEY_SEPARATOR); } if (str == NULL) { // This is the normal path size_t cnt = need_escaping(out, len, escape_mode); if (cnt) { escape_buffer dest = { 0 }; if (escape_size(len, cnt, &dest.capacity) == 0) dest.data = malloc(dest.capacity); if (dest.data && escape(out, len, &dest, escape_mode)) { free(dest.data); dest.data = NULL; } free((void *)out); out = dest.data; } } else { // We have multiple keys. Need to look at each one. size_t cnt = 0, offset = 0; while (offset < len) { const char *ptr = out + offset; const char *sep; size_t klen, part_count; sep = memchr(ptr, AUDIT_KEY_SEPARATOR, len - offset); klen = sep ? (size_t)(sep - ptr) : len - offset; part_count = need_escaping(ptr, klen, escape_mode); if (part_count > SIZE_MAX - cnt) { cnt = SIZE_MAX; break; } cnt += part_count; if (sep == NULL) break; offset += klen + 1; } if (cnt) { // I expect this code to never get used. // Its here just in the off chance someone // actually put a control character in a key. escape_buffer dest = { 0 }; if (escape_size(len, cnt, &dest.capacity) == 0) dest.data = malloc(dest.capacity); if (dest.data && key_escape(out, len, &dest, escape_mode)) { free(dest.data); dest.data = NULL; } free((void *)out); out = dest.data; } } } return (char *)out; }