diff options
Diffstat (limited to 'framework/src/audit/src/auditctl-listing.c')
-rw-r--r-- | framework/src/audit/src/auditctl-listing.c | 577 |
1 files changed, 577 insertions, 0 deletions
diff --git a/framework/src/audit/src/auditctl-listing.c b/framework/src/audit/src/auditctl-listing.c new file mode 100644 index 00000000..88dac6c8 --- /dev/null +++ b/framework/src/audit/src/auditctl-listing.c @@ -0,0 +1,577 @@ +/* auditctl-listing.c -- + * Copyright 2014 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "auditctl-listing.h" +#include "private.h" +#include "auditctl-llist.h" +#include "auparse-idata.h" + +/* Global vars */ +static llist l; +static int printed; +extern int list_requested, interpret; +extern char key[AUDIT_MAX_KEY_LEN+1]; +extern const char key_sep[2]; + +/* + * Returns 1 if rule should be printed & 0 if not + */ +int key_match(const struct audit_rule_data *r) +{ + int i; + size_t boffset = 0; + + if (key[0] == 0) + return 1; + + // At this point, we have a key + for (i = 0; i < r->field_count; i++) { + int field = r->fields[i] & ~AUDIT_OPERATORS; + if (field == AUDIT_FILTERKEY) { + char *keyptr; + if (asprintf(&keyptr, "%.*s", r->values[i], + &r->buf[boffset]) < 0) + keyptr = NULL; + else if (strstr(keyptr, key)) { + free(keyptr); + return 1; + } + free(keyptr); + } + if (((field >= AUDIT_SUBJ_USER && field <= AUDIT_OBJ_LEV_HIGH) + && field != AUDIT_PPID) || field == AUDIT_WATCH || + field == AUDIT_DIR || field == AUDIT_FILTERKEY) { + boffset += r->values[i]; + } + } + return 0; +} + +/* + * This function detects if we have a watch. A watch is detected when we + * have syscall == all and a perm field. + */ +static int is_watch(const struct audit_rule_data *r) +{ + int i, perm = 0, all = 1; + + for (i = 0; i < r->field_count; i++) { + int field = r->fields[i] & ~AUDIT_OPERATORS; + if (field == AUDIT_PERM) + perm = 1; + // Watches can have only 4 field types + if (field != AUDIT_PERM && field != AUDIT_FILTERKEY && + field != AUDIT_DIR && field != AUDIT_WATCH) + return 0; + } + + if (((r->flags & AUDIT_FILTER_MASK) != AUDIT_FILTER_USER) && + ((r->flags & AUDIT_FILTER_MASK) != AUDIT_FILTER_TASK) && + ((r->flags & AUDIT_FILTER_MASK) != AUDIT_FILTER_EXCLUDE)) { + for (i = 0; i < (AUDIT_BITMASK_SIZE-1); i++) { + if (r->mask[i] != (uint32_t)~0) { + all = 0; + break; + } + } + } + if (perm && all) + return 1; + return 0; +} + +static int print_arch(unsigned int value, int op) +{ + int machine; + _audit_elf = value; + machine = audit_elf_to_machine(_audit_elf); + if (machine < 0) + printf(" -F arch%s0x%X", audit_operator_to_symbol(op), + (unsigned)value); + else { + if (interpret == 0) { + if (__AUDIT_ARCH_64BIT & _audit_elf) + printf(" -F arch%sb64", + audit_operator_to_symbol(op)); + else + printf(" -F arch%sb32", + audit_operator_to_symbol(op)); + } else { + const char *ptr = audit_machine_to_name(machine); + printf(" -F arch%s%s", audit_operator_to_symbol(op), + ptr); + } + } + return machine; +} + +static int print_syscall(const struct audit_rule_data *r, unsigned int *sc) +{ + int count = 0; + int all = 1; + unsigned int i; + int machine = audit_detect_machine(); + + /* Rules on the following filters do not take a syscall */ + if (((r->flags & AUDIT_FILTER_MASK) == AUDIT_FILTER_USER) || + ((r->flags & AUDIT_FILTER_MASK) == AUDIT_FILTER_TASK) || + ((r->flags &AUDIT_FILTER_MASK) == AUDIT_FILTER_EXCLUDE)) + return 0; + + /* See if its all or specific syscalls */ + for (i = 0; i < (AUDIT_BITMASK_SIZE-1); i++) { + if (r->mask[i] != (uint32_t)~0) { + all = 0; + break; + } + } + + if (all) { + printf(" -S all"); + count = i; + } else for (i = 0; i < AUDIT_BITMASK_SIZE * 32; i++) { + int word = AUDIT_WORD(i); + int bit = AUDIT_BIT(i); + if (r->mask[word] & bit) { + const char *ptr; + if (_audit_elf) + machine = audit_elf_to_machine(_audit_elf); + if (machine < 0) + ptr = NULL; + else + ptr = audit_syscall_to_name(i, machine); + if (!count) + printf(" -S "); + if (ptr) + printf("%s%s", !count ? "" : ",", ptr); + else + printf("%s%d", !count ? "" : ",", i); + count++; + *sc = i; + } + } + return count; +} + +static void print_field_cmp(int value, int op) +{ + switch (value) + { + case AUDIT_COMPARE_UID_TO_OBJ_UID: + printf(" -C uid%sobj_uid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_GID_TO_OBJ_GID: + printf(" -C gid%sobj_gid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_EUID_TO_OBJ_UID: + printf(" -C euid%sobj_uid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_EGID_TO_OBJ_GID: + printf(" -C egid%sobj_gid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_AUID_TO_OBJ_UID: + printf(" -C auid%sobj_uid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_SUID_TO_OBJ_UID: + printf(" -C suid%sobj_uid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_SGID_TO_OBJ_GID: + printf(" -C sgid%sobj_gid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_FSUID_TO_OBJ_UID: + printf(" -C fsuid%sobj_uid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_FSGID_TO_OBJ_GID: + printf(" -C fsgid%sobj_gid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_UID_TO_AUID: + printf(" -C uid%sauid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_UID_TO_EUID: + printf(" -C uid%seuid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_UID_TO_FSUID: + printf(" -C uid%sfsuid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_UID_TO_SUID: + printf(" -C uid%ssuid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_AUID_TO_FSUID: + printf(" -C auid%sfsuid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_AUID_TO_SUID: + printf(" -C auid%ssuid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_AUID_TO_EUID: + printf(" -C auid%seuid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_EUID_TO_SUID: + printf(" -C euid%ssuid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_EUID_TO_FSUID: + printf(" -C euid%sfsuid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_SUID_TO_FSUID: + printf(" -C suid%sfsuid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_GID_TO_EGID: + printf(" -C gid%segid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_GID_TO_FSGID: + printf(" -C gid%sfsgid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_GID_TO_SGID: + printf(" -C gid%ssgid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_EGID_TO_FSGID: + printf(" -C egid%sfsgid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_EGID_TO_SGID: + printf(" -C egid%ssgid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_SGID_TO_FSGID: + printf(" -C sgid%sfsgid", + audit_operator_to_symbol(op)); + break; + } +} + +/* + * This function prints 1 rule from the kernel reply + */ +static void print_rule(const struct audit_rule_data *r) +{ + unsigned int i, count = 0, sc = 0; + size_t boffset = 0; + int mach = -1, watch = is_watch(r); + unsigned long long a0 = 0, a1 = 0; + + if (!watch) { /* This is syscall auditing */ + printf("-a %s,%s", + audit_action_to_name((int)r->action), + audit_flag_to_name(r->flags)); + + // Now find the arch and print it + for (i = 0; i < r->field_count; i++) { + int field = r->fields[i] & ~AUDIT_OPERATORS; + if (field == AUDIT_ARCH) { + int op = r->fieldflags[i] & AUDIT_OPERATORS; + mach = print_arch(r->values[i], op); + } + } + // And last do the syscalls + count = print_syscall(r, &sc); + } + + // Now iterate over the fields + for (i = 0; i < r->field_count; i++) { + const char *name; + int op = r->fieldflags[i] & AUDIT_OPERATORS; + int field = r->fields[i] & ~AUDIT_OPERATORS; + + if (field == AUDIT_ARCH) + continue; // already printed + + name = audit_field_to_name(field); + if (name) { + // Special cases to print the different field types + // in a meaningful way. + if (field == AUDIT_MSGTYPE) { + if (!audit_msg_type_to_name(r->values[i])) + printf(" -F %s%s%d", name, + audit_operator_to_symbol(op), + r->values[i]); + else + printf(" -F %s%s%s", name, + audit_operator_to_symbol(op), + audit_msg_type_to_name( + r->values[i])); + } else if ((field >= AUDIT_SUBJ_USER && + field <= AUDIT_OBJ_LEV_HIGH) + && field != AUDIT_PPID) { + printf(" -F %s%s%.*s", name, + audit_operator_to_symbol(op), + r->values[i], &r->buf[boffset]); + boffset += r->values[i]; + } else if (field == AUDIT_WATCH) { + if (watch) + printf("-w %.*s", r->values[i], + &r->buf[boffset]); + else + printf(" -F path=%.*s", r->values[i], + &r->buf[boffset]); + boffset += r->values[i]; + } else if (field == AUDIT_DIR) { + if (watch) + printf("-w %.*s/", r->values[i], + &r->buf[boffset]); + else + printf(" -F dir=%.*s", r->values[i], + &r->buf[boffset]); + + boffset += r->values[i]; + } else if (field == AUDIT_FILTERKEY) { + char *rkey, *ptr, *saved; + if (asprintf(&rkey, "%.*s", r->values[i], + &r->buf[boffset]) < 0) + rkey = NULL; + boffset += r->values[i]; + ptr = strtok_r(rkey, key_sep, &saved); + while (ptr) { + if (watch) + printf(" -k %s", ptr); + else + printf(" -F key=%s", ptr); + ptr = strtok_r(NULL, key_sep, &saved); + } + free(rkey); + } else if (field == AUDIT_PERM) { + char perms[5]; + int val=r->values[i]; + perms[0] = 0; + if (val & AUDIT_PERM_READ) + strcat(perms, "r"); + if (val & AUDIT_PERM_WRITE) + strcat(perms, "w"); + if (val & AUDIT_PERM_EXEC) + strcat(perms, "x"); + if (val & AUDIT_PERM_ATTR) + strcat(perms, "a"); + if (watch) + printf(" -p %s", perms); + else + printf(" -F perm=%s", perms); + } else if (field == AUDIT_INODE) { + // This is unsigned + printf(" -F %s%s%u", name, + audit_operator_to_symbol(op), + r->values[i]); + } else if (field == AUDIT_FIELD_COMPARE) { + print_field_cmp(r->values[i], op); + } else if (field >= AUDIT_ARG0 && field <= AUDIT_ARG3){ + if (field == AUDIT_ARG0) + a0 = r->values[i]; + else if (field == AUDIT_ARG1) + a1 = r->values[i]; + + // Show these as hex + if (count > 1 || interpret == 0) + printf(" -F %s%s0x%X", name, + audit_operator_to_symbol(op), + r->values[i]); + else { // Use ignore to mean interpret + const char *out; + idata id; + char val[32]; + int type; + + id.syscall = sc; + id.machine = mach; + id.a0 = a0; + id.a1 = a1; + id.name = name; + snprintf(val, 32, "%x", r->values[i]); + id.val = val; + type = auparse_interp_adjust_type( + AUDIT_SYSCALL, name, val); + out = auparse_do_interpretation(type, + &id); + printf(" -F %s%s%s", name, + audit_operator_to_symbol(op), + out); + free((void *)out); + } + } else if (field == AUDIT_EXIT) { + int e = abs((int)r->values[i]); + const char *err = audit_errno_to_name(e); + + if (((int)r->values[i] < 0) && err) + printf(" -F %s%s-%s", name, + audit_operator_to_symbol(op), + err); + else + printf(" -F %s%s%d", name, + audit_operator_to_symbol(op), + (int)r->values[i]); + } else { + // The default is signed decimal + printf(" -F %s%s%d", name, + audit_operator_to_symbol(op), + r->values[i]); + } + } else { + // The field name is unknown + printf(" f%d%s%d", r->fields[i], + audit_operator_to_symbol(op), + r->values[i]); + } + } + printf("\n"); +} + +void audit_print_init(void) +{ + printed = 0; + list_create(&l); +} + +const char *get_enable(unsigned e) +{ + switch (e) + { + case 0: + return "disable"; + case 1: + return "enabled"; + case 2: + return "enabl;ed+immutable"; + default: + return "unknown"; + } +} + +const char *get_failure(unsigned f) +{ + switch (f) + { + case 0: + return "silent"; + case 1: + return "printk"; + case 2: + return "panic"; + default: + return "unknown"; + } +} + +/* + * This function interprets the reply and prints it to stdout. It returns + * 0 if no more should be read and 1 to indicate that more messages of this + * type may need to be read. + */ +int audit_print_reply(struct audit_reply *rep, int fd) +{ + _audit_elf = 0; + + switch (rep->type) { + case NLMSG_NOOP: + return 1; + case NLMSG_DONE: + // Close the socket so kernel can do other things + audit_close(fd); + if (printed == 0) + printf("No rules\n"); + else { + lnode *n; + list_first(&l); + n = l.cur; + while (n) { + print_rule(n->r); + n = list_next(&l); + } + list_clear(&l); + } + break; + case NLMSG_ERROR: + printf("NLMSG_ERROR %d (%s)\n", + -rep->error->error, + strerror(-rep->error->error)); + printed = 1; + break; + case AUDIT_GET: + if (interpret) + printf("enabled %s\nfailure %s\n", + get_enable(rep->status->enabled), + get_failure(rep->status->failure)); + else + printf("enabled %u\nfailure %u\n", + rep->status->enabled, rep->status->failure); + printf("pid %u\nrate_limit %u\nbacklog_limit %u\n" + "lost %u\nbacklog %u\n", + rep->status->pid, rep->status->rate_limit, + rep->status->backlog_limit, rep->status->lost, + rep->status->backlog); +#if HAVE_DECL_AUDIT_VERSION_BACKLOG_WAIT_TIME + printf("backlog_wait_time %u\n", + rep->status->backlog_wait_time); +#endif + printed = 1; + break; +#if HAVE_DECL_AUDIT_FEATURE_VERSION + case AUDIT_GET_FEATURE: + { + uint32_t mask = AUDIT_FEATURE_TO_MASK(AUDIT_FEATURE_LOGINUID_IMMUTABLE); + if (rep->features->mask & mask) + printf("loginuid_immutable %u %s\n", + !!(rep->features->features & mask), + rep->features->lock & mask ? "locked" : + "unlocked"); + } + printed = 1; + break; +#endif + case AUDIT_LIST_RULES: + list_requested = 0; + if (key_match(rep->ruledata)) + list_append(&l, rep->ruledata, + sizeof(struct audit_rule_data) + + rep->ruledata->buflen); + printed = 1; + return 1; + default: + printf("Unknown: type=%d, len=%d\n", rep->type, + rep->nlh->nlmsg_len); + printed = 1; + break; + } + return 0; +} + |