/* * interpret.c - Lookup values to something more readable * Copyright (c) 2007-09,2011-15 Red Hat Inc., Durham, North Carolina. * 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 */ #include "config.h" #include "nvlist.h" #include "nvpair.h" #include "libaudit.h" #include "internal.h" #include "interpret.h" #include "auparse-idata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // FIXME: remove when ipx.h is fixed #include #include #include #include #include #include "auparse-defs.h" #include "gen_tables.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 "flagtabs.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" typedef enum { AVC_UNSET, AVC_DENIED, AVC_GRANTED } avc_t; typedef enum { S_UNSET=-1, S_FAILED, S_SUCCESS } success_t; static const char *print_signals(const char *val, unsigned int base); static auparse_esc_t escape_mode = AUPARSE_ESC_TTY; /* * 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"; char *ptr; unsigned char total=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; } // Check if any characters need tty escaping. Returns how many found. static unsigned int need_tty_escape(const unsigned char *s, unsigned int len) { unsigned int i = 0, cnt = 0; while (i < len) { if (s[i] < 32) cnt++; i++; } return cnt; } // TTY escaping s string into dest. static void tty_escape(const char *s, char *dest, unsigned int len) { unsigned int i = 0, j = 0; while (i < len) { if ((unsigned char)s[i] < 32) { dest[j++] = ('\\'); dest[j++] = ('0' + ((s[i] & 0300) >> 6)); dest[j++] = ('0' + ((s[i] & 0070) >> 3)); dest[j++] = ('0' + (s[i] & 0007)); } else dest[j++] = s[i]; i++; } } static const char sh_set[] = "\"'`$\\"; static unsigned int need_shell_escape(const char *s, unsigned int len) { unsigned int i = 0, cnt = 0; while (i < len) { if (s[i] < 32) cnt++; else if (strchr(sh_set, s[i])) cnt++; i++; } return cnt; } static void shell_escape(const char *s, char *dest, unsigned int len) { unsigned int i = 0, j = 0; while (i < len) { if ((unsigned char)s[i] < 32) { 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])) { dest[j++] = ('\\'); dest[j++] = s[i]; } else dest[j++] = s[i]; i++; } } static const char quote_set[] = ";'\"`#$&*?[]<>{}\\"; static unsigned int need_shell_quote_escape(const unsigned char *s, unsigned int len) { unsigned int i = 0, cnt = 0; while (i < len) { if (s[i] < 32) cnt++; else if (strchr(quote_set, s[i])) cnt++; i++; } return cnt; } static void shell_quote_escape(const char *s, char *dest, unsigned int len) { unsigned int i = 0, j = 0; while (i < len) { if ((unsigned char)s[i] < 32) { 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])) { dest[j++] = ('\\'); dest[j++] = s[i]; } else dest[j++] = s[i]; i++; } } /* This should return the count of what needs escaping */ static unsigned int need_escaping(const char *s, unsigned int len) { 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; } static void escape(const char *s, char *dest, unsigned int len) { switch (escape_mode) { case AUPARSE_ESC_RAW: return; case AUPARSE_ESC_TTY: return tty_escape(s, dest, len); case AUPARSE_ESC_SHELL: return shell_escape(s, dest, len); case AUPARSE_ESC_SHELL_QUOTE: return shell_quote_escape(s, dest, len); } } int set_escape_mode(auparse_esc_t mode) { if (mode < 0 || mode > AUPARSE_ESC_SHELL_QUOTE) return 1; escape_mode = mode; return 0; } hidden_def(set_escape_mode) static int is_hex_string(const char *str) { while (*str) { if (!isxdigit(*str)) return 0; str++; } return 1; } /* returns a freshly malloc'ed and converted buffer */ char *au_unescape(char *buf) { int len, i; char saved, *str, *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++; } saved = *ptr; *ptr = 0; str = strdup(buf); *ptr = saved; /* 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; } ptr = str; for (i=0; iname; } else { // Add it to cache struct passwd *pw; pw = getpwuid(uid); if (pw) { nvpnode nv; nv.name = strdup(pw->pw_name); nv.val = uid; nvpair_append(&uid_nvl, &nv); name = uid_nvl.cur->name; } } if (name != NULL) snprintf(buf, size, "%s", name); else snprintf(buf, size, "unknown(%d)", uid); return buf; } void aulookup_destroy_uid_list(void) { if (uid_list_created == 0) return; nvpair_clear(&uid_nvl); uid_list_created = 0; } static nvpair gid_nvl; static int gid_list_created=0; static const char *aulookup_gid(gid_t gid, char *buf, size_t size) { char *name = NULL; int rc; if (gid == -1) { snprintf(buf, size, "unset"); return buf; } // Check the cache first if (gid_list_created == 0) { nvpair_create(&gid_nvl); nvpair_clear(&gid_nvl); gid_list_created = 1; } rc = nvpair_find_val(&gid_nvl, gid); if (rc) { name = gid_nvl.cur->name; } else { // Add it to cache struct group *gr; gr = getgrgid(gid); if (gr) { nvpnode nv; nv.name = strdup(gr->gr_name); nv.val = gid; nvpair_append(&gid_nvl, &nv); name = gid_nvl.cur->name; } } if (name != NULL) snprintf(buf, size, "%s", name); else snprintf(buf, size, "unknown(%d)", gid); return buf; } void aulookup_destroy_gid_list(void) { if (gid_list_created == 0) return; nvpair_clear(&gid_nvl); gid_list_created = 0; } static const char *print_uid(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(uid, name, sizeof(name))); } static const char *print_gid(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(gid, name, sizeof(name))); } static const char *print_arch(const char *val, unsigned int machine) { const char *ptr; char *out; if (machine > MACH_AARCH64) { 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(%d)", 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 *out; if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } 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 *out; if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } 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, "%lld(%s)", ival, strerror(-ival)) < 0) out = NULL; return out; } return strdup(val); } static const char *print_escaped(const char *val) { const char *out; if (*val == '"') { char *term; val++; term = strchr(val, '"'); if (term == NULL) return strdup(" "); *term = 0; out = strdup(val); *term = '"'; return out; // 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((char *)&val[2]); // Abstract name af_unix else out = au_unescape((char *)val); if (out) return out; return strdup(val); // Something is wrong with string, just send as is } static const char *print_proctitle(const char *val) { char *out = (char *)print_escaped(val); if (*val != '"') { size_t len = strlen(val) / 2; const char *end = out + len; char *ptr = out; while ((ptr = rawmemchr(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) { int slen, 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((char *)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: { const struct sockaddr_un *un = (struct sockaddr_un *)saddr; if (un->sun_path[0]) rc = asprintf(&out, "%s %s", str, un->sun_path); else // abstract name rc = asprintf(&out, "%s %.108s", str, &un->sun_path[1]); } break; case AF_INET: if (slen < sizeof(struct sockaddr_in)) { rc = asprintf(&out, "%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, "%s host:%s serv:%s", str, name, serv); } else rc = asprintf(&out, "%s (error resolving addr)", str); break; case AF_AX25: { const struct sockaddr_ax25 *x = (struct sockaddr_ax25 *)saddr; rc = asprintf(&out, "%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_IPX: { const struct sockaddr_ipx *ip = (struct sockaddr_ipx *)saddr; rc = asprintf(&out, "%s port:%d net:%u", str, ip->sipx_port, ip->sipx_network); } break; case AF_ATMPVC: { const struct sockaddr_atmpvc* at = (struct sockaddr_atmpvc *)saddr; rc = asprintf(&out, "%s int:%d", str, at->sap_addr.itf); } break; case AF_X25: { const struct sockaddr_x25* x = (struct sockaddr_x25 *)saddr; rc = asprintf(&out, "%s addr:%.15s", str, x->sx25_addr.x25_addr); } break; case AF_INET6: if (slen < sizeof(struct sockaddr_in6)) { rc = asprintf(&out, "%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, "%s host:%s serv:%s", str, name, serv); } else rc = asprintf(&out, "%s (error resolving addr)", str); break; case AF_NETLINK: { const struct sockaddr_nl *n = (struct sockaddr_nl *)saddr; rc = asprintf(&out, "%s pid:%u", str, n->nl_pid); } break; } if (rc < 0) out = NULL; free((char *)host); return out; } /* This is only used in the RHEL4 kernel */ static const char *print_flags(const char *val) { int flags, cnt = 0; size_t i; char *out, buf[80]; errno = 0; flags = strtoul(val, NULL, 16); if (errno) { if (asprintf(&out, "conversion error(%s)", val) < 0) out = NULL; return out; } if (flags == 0) { if (asprintf(&out, "none") < 0) out = NULL; return out; } buf[0] = 0; for (i=0; i> 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(*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) { size_t i; unsigned int flags; int cnt = 0; char *out, buf[178]; errno = 0; flags = strtoul(val, NULL, 16); 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; ip_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[32]; 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; imachine, 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 (strcmp(sys, "renameat") == 0) return print_dirfd(val); else if (strcmp(sys, "readlinkat") == 0) return print_dirfd(val); } else if (*sys == 'c') { if (strcmp(sys, "clone") == 0) return print_clone_flags(val); else if (strcmp(sys, "clock_settime") == 0) return print_clock_id(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 (strcmp(sys, "faccessat") == 0) return print_dirfd(val); else if (strcmp(sys, "futimensat") == 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(val, 16); else if (strcmp(sys, "setreuid") == 0) return print_uid(val, 16); else if (strcmp(sys, "setresuid") == 0) return print_uid(val, 16); else if (strcmp(sys, "setfsuid") == 0) return print_uid(val, 16); else if (strcmp(sys, "setgid") == 0) return print_gid(val, 16); else if (strcmp(sys, "setregid") == 0) return print_gid(val, 16); else if (strcmp(sys, "setresgid") == 0) return print_gid(val, 16); else if (strcmp(sys, "socket") == 0) return print_socket_domain(val); else if (strcmp(sys, "setfsgid") == 0) return print_gid(val, 16); else if (strcmp(sys, "socketcall") == 0) return print_socketcall(val, 16); } else if (strcmp(sys, "linkat") == 0) return print_dirfd(val); else if (strcmp(sys, "newfstatat") == 0) return print_dirfd(val); else if (strcmp(sys, "openat") == 0) return print_dirfd(val); else if (strcmp(sys, "ipccall") == 0) return print_ipccall(val, 16); } if (asprintf(&out, "0x%s", val) < 0) out = NULL; return out; } static const char *print_a1(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 (*sys == 'c') { if (strcmp(sys, "chmod") == 0) return print_mode_short(val, 16); else if (strstr(sys, "chown")) return print_uid(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(val, 16); else if (strcmp(sys, "setresuid") == 0) return print_uid(val, 16); else if (strcmp(sys, "setregid") == 0) return print_gid(val, 16); else if (strcmp(sys, "setresgid") == 0) return print_gid(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); } else if (strcmp(sys, "open") == 0) return print_open_flags(val); else if (strcmp(sys, "access") == 0) return print_access(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(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(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); if ((strcmp(sys, "open") == 0) && (id->a1 & O_CREAT)) return print_mode_short(val, 16); } else if (*sys == 'f') { if (strcmp(sys, "fchmodat") == 0) return print_mode_short(val, 16); else if (strcmp(sys, "faccessat") == 0) return print_access(val); } else if (*sys == 's') { if (strcmp(sys, "setresuid") == 0) return print_uid(val, 16); else if (strcmp(sys, "setresgid") == 0) return print_gid(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 (*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, "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 (*sys == 'l') { if (strcmp(sys, "linkat") == 0) return print_dirfd(val); else if (strcmp(sys, "lseek") == 0) return print_seek(val); } else if (strstr(sys, "chown")) return print_gid(val, 16); else if (strcmp(sys, "tgkill") == 0) return print_signals(val, 16); } normal: if (asprintf(&out, "0x%s", val) < 0) out = NULL; return out; } static const char *print_a3(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); } } 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; } 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 out = strdup(audit_flag_to_name(i)); 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((char *)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); } #define SECCOMP_RET_ACTION 0x7fff0000U 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(code & SECCOMP_RET_ACTION); if (s != NULL) return strdup(s); if (asprintf(&out, "unknown seccomp code (%s)", val) < 0) out = NULL; return out; } int lookup_type(const char *name) { int i; if (type_s2i(name, &i) != 0) return i; return AUPARSE_TYPE_UNCLASSIFIED; } const char *interpret(const rnode *r) { const 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.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(type, &id); 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; /* 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_PATH && *name =='f' && strcmp(name, "flags") == 0) type = AUPARSE_TYPE_FLAGS; 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 type = lookup_type(name); return type; } hidden_def(auparse_interp_adjust_type) const char *auparse_do_interpretation(int type, const idata *id) { const char *out; switch(type) { case AUPARSE_TYPE_UID: out = print_uid(id->val, 10); break; case AUPARSE_TYPE_GID: out = print_gid(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: 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_FLAGS: out = print_flags(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(id->val, id); break; case AUPARSE_TYPE_A1: out = print_a1(id->val, id); break; case AUPARSE_TYPE_A2: out = print_a2(id->val, id); break; case AUPARSE_TYPE_A3: out = print_a3(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: out = print_open_flags(id->val); break; case AUPARSE_TYPE_MMAP: out = print_mmap(id->val); break; case AUPARSE_TYPE_PROCTITLE: out = print_proctitle(id->val); break; case AUPARSE_TYPE_MAC_LABEL: case AUPARSE_TYPE_UNCLASSIFIED: default: out = strdup(id->val); break; } if (escape_mode != AUPARSE_ESC_RAW) { unsigned int len = strlen(out); unsigned int cnt = need_escaping(out, len); if (cnt) { char *dest = malloc(len + 1 + (3*cnt)); if (dest) escape(out, dest, len); free((void *)out); out = dest; } } return out; } hidden_def(auparse_do_interpretation)