/* This function shows how to iterate through the fields of a record * and print its name and raw value and interpretted value. */ static void dump_fields_of_record(auparse_state_t *au) { printf("record type %d(%s) has %d fields\n", auparse_get_type(au), audit_msg_type_to_name(auparse_get_type(au)), auparse_get_num_fields(au)); printf("line=%d file=%s\n", auparse_get_line_number(au), auparse_get_filename(au) ? auparse_get_filename(au) : "stdin"); const au_event_t *e = auparse_get_timestamp(au); if (e == NULL) { printf("Error getting timestamp - aborting\n"); return; } /* Note that e->sec can be treated as time_t data if you want * something a little more readable */ printf("event time: %u.%u:%lu, host=%s\n", (unsigned)e->sec, e->milli, e->serial, e->host ? e->host : "?"); auparse_first_field(au); do { printf("field: %s=%s (%s)\n", auparse_get_field_name(au), auparse_get_field_str(au), auparse_interpret_field(au)); } while (auparse_next_field(au) > 0); printf("\n"); }
/* this may seem a bit overmuch, but there are some issues with the auparse_interpret_field function not providing anything but a NULL back which makes all the smoke leak out... */ const char* auparse_interpret_field_wrap(auparse_state_t *_au) { const char* ret_val; const char* nval = "NULL"; ret_val = (char*)auparse_interpret_field(_au); if ( ret_val == NULL ) { return nval; } else return ret_val; }
int add_resource(auparse_state_t *au, const char *uuid, uid_t uid, time_t time, const char *name, int success, const char *reason, const char *res_type, const char *res) { if (!is_resource(res)) return 0; struct event *event = event_alloc(); if (event == NULL) return 1; event->type = ET_RES; event->uuid = copy_str(uuid); event->name = copy_str(name); event->success = success; event->reason = copy_str(reason); event->res_type = copy_str(res_type); event->res = copy_str(res); event->uid = uid; event->start = time; add_proof(event, au); /* Get cgroup specific fields. */ if (strcmp("cgroup", res_type) == 0) { event->cgroup_class = copy_str(auparse_find_field(au, "class")); if (event->cgroup_class) { const char *detail = NULL; if (strcmp("path", event->cgroup_class) == 0) { if (auparse_find_field(au, "path")) detail = auparse_interpret_field(au); } else if (strcmp("major", event->cgroup_class) == 0) { detail = auparse_find_field(au, "category"); } event->cgroup_detail = copy_str(detail); } event->cgroup_acl = copy_str(auparse_find_field(au, "acl")); } if (list_append(events, event) == NULL) { event_free(event); return 1; } return 0; }
/* Extract the most common fields from virtualization-related records. */ int extract_virt_fields(auparse_state_t *au, const char **p_uuid, uid_t *p_uid, time_t *p_time, const char **p_name, int *p_suc) { const char *field; auparse_first_record(au); /* Order matters */ if (p_uid) { if (!auparse_find_field(au, field = "uid")) goto error; *p_uid = auparse_get_field_int(au); } if (p_name) { if (!auparse_find_field(au, field = "vm")) goto error; *p_name = auparse_interpret_field(au); } if (p_uuid) { if (!auparse_find_field(au, field = "uuid")) goto error; *p_uuid = auparse_get_field_str(au); } if (p_suc) { const char *res = auparse_find_field(au, field = "res"); if (res == NULL) goto error; *p_suc = (strcmp("success", res) == 0) ? 1 : 0; } if (p_time) { *p_time = auparse_get_time(au); } return 0; error: if (debug) { fprintf(stderr, "Failed to get field \"%s\" for record " "%ld.%03u:%lu\n", field ? field : "", auparse_get_time(au), auparse_get_milli(au), auparse_get_serial(au)); } return 1; }
static void update_session_login(auparse_state_t *au) { const char *tpid, *tses, *tuid, *tacct=NULL, *host, *term, *tres; int pid = -1, uid = -1, ses = -1, result = -1; time_t start; lnode *cur; // Get pid tpid = auparse_find_field(au, "pid"); if (tpid) pid = auparse_get_field_int(au); // Get ses field tses = auparse_find_field(au, "ses"); if (tses) ses = auparse_get_field_int(au); // Get second uid field - we should be positioned past the first one // gdm sends uid, everything else sends id, we try acct as last resort tuid = auparse_find_field(au, "uid"); if (tuid) uid = auparse_get_field_int(au); else { auparse_first_record(au); tuid = auparse_find_field(au, "id"); if (tuid) uid = auparse_get_field_int(au); else { auparse_first_record(au); tuid = auparse_find_field(au, "acct"); if (tuid) { const char *tacct = auparse_interpret_field(au); struct passwd *pw = getpwnam (tacct); if (pw != NULL) uid = pw->pw_uid; } else auparse_first_record(au); } } start = auparse_get_time(au); host = auparse_find_field(au, "hostname"); if (host && strcmp(host, "?") == 0) host = auparse_find_field(au, "addr"); term = auparse_find_field(au, "terminal"); if (term == NULL) term = "?"; tres = auparse_find_field(au, "res"); if (tres) tres = auparse_interpret_field(au); if (tres) { if (strcmp(tres, "success") == 0) result = 0; else result = 1; } // We only get tacct when its a bad login if (result == 1) { auparse_first_record(au); tacct = auparse_find_field(au, "acct"); if (tacct) tacct = auparse_interpret_field(au); } else { // Check that they are valid if (pid == -1 || uid ==-1 || ses == -1) { if (debug) fprintf(stderr, "Bad user login for event: %lu\n", auparse_get_serial(au)); return; } } // See if this session is already open if (result == 0) cur = list_find_auid(&l, uid, pid, ses); else cur = NULL; if (cur) { // If we are limited to a specific terminal and // we find out the session is not associated with // the terminal of interest, delete the current node if (cterm && strstr(term, cterm) == NULL) { list_delete_cur(&l); if (debug) fprintf(stderr, "User login limited to %s for event: %lu\n", cterm, auparse_get_serial(au)); return; } // This means we have an open session - update it list_update_start(&l, start, host, term, result, auparse_get_serial(au)); // If the results were failed, we can close it out /* FIXME: result cannot be true. This is dead code. if (result) { report_session(cur); list_delete_cur(&l); } */ } else if (bad == 1 && result == 1) { // If it were a bad login and we are wanting bad logins // create the record and report it. lnode n; n.auid = uid; n.start = start; n.end = start; n.name = tacct; n.host = host; n.term = term; n.result = result; n.status = LOG_OUT; n.loginuid_proof = auparse_get_serial(au); report_session(&n); } }
int process_avc_apparmor_source(auparse_state_t *au) { uid_t uid = -1; time_t time = 0; struct event *avc; const char *target; /* Get the target object. */ if (auparse_find_field(au, "name") == NULL) { if (debug) { auparse_first_record(au); fprintf(stderr, "Couldn't get the resource name from " "the AVC record: %s\n", auparse_get_record_text(au)); } return 0; } target = auparse_interpret_field(au); /* Loop backwards to find a guest session with the target object * assigned to. */ struct list_node_t *it; struct event *res = NULL; for (it = events->tail; it; it = it->prev) { struct event *event = it->data; if (event->success) { if (event->type == ET_DOWN) { /* It's just possible to find a matching guest * session in the current host session. */ break; } else if (event->type == ET_RES && event->end == 0 && event->res != NULL && strcmp(target, event->res) == 0) { res = event; break; } } } /* Check if a resource event was found. */ if (res == NULL) { if (debug) { fprintf(stderr, "Target object not found for AVC " "event.\n"); } return 0; } if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL)) return 0; avc = event_alloc(); if (avc == NULL) return 1; avc->type = ET_AVC; /* Guest info */ avc->uuid = copy_str(res->uuid); avc->name = copy_str(res->name); memcpy(avc->proof, res->proof, sizeof(avc->proof)); /* AVC info */ avc->start = time; avc->uid = uid; auparse_first_record(au); if (auparse_find_field(au, "apparmor")) { int i; avc->avc_result = copy_str(auparse_interpret_field(au)); for (i = 0; avc->avc_result && avc->avc_result[i]; i++) { avc->avc_result[i] = tolower(avc->avc_result[i]); } } if (auparse_find_field(au, "operation")) avc->avc_operation = copy_str(auparse_interpret_field(au)); avc->target = copy_str(target); if (auparse_find_field(au, "comm")) avc->comm = copy_str(auparse_interpret_field(au)); add_proof(avc, au); if (list_append(events, avc) == NULL) { event_free(avc); return 1; } return 0; }
int process_avc_selinux_context(auparse_state_t *au, const char *context) { const char *seclevel; struct event *machine_id, *avc; uid_t uid; time_t time; seclevel = get_seclevel(auparse_find_field(au, context)); if (seclevel == NULL) { if (debug) { fprintf(stderr, "Security context not found " "for AVC event.\n"); } return 0; } if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL)) return 0; machine_id = get_machine_id_by_seclevel(seclevel); if (machine_id == NULL) { if (debug) { fprintf(stderr, "Couldn't get the security " "level from the AVC event.\n"); } return 0; } avc = event_alloc(); if (avc == NULL) return 1; avc->type = ET_AVC; /* Guest info */ avc->uuid = copy_str(machine_id->uuid); avc->name = copy_str(machine_id->name); memcpy(avc->proof, machine_id->proof, sizeof(avc->proof)); /* AVC info */ avc->start = time; avc->uid = uid; avc->seclevel = copy_str(seclevel); auparse_first_record(au); avc->avc_result = copy_str(auparse_find_field(au, "seresult")); avc->avc_operation = copy_str(auparse_find_field(au, "seperms")); if (auparse_find_field(au, "comm")) avc->comm = copy_str(auparse_interpret_field(au)); if (auparse_find_field(au, "name")) avc->target = copy_str(auparse_interpret_field(au)); /* get the context related to the permission that was denied. */ if (avc->avc_operation) { const char *ctx = NULL; if (strcmp("relabelfrom", avc->avc_operation) == 0) { ctx = auparse_find_field(au, "scontext"); } else if (strcmp("relabelto", avc->avc_operation) == 0) { ctx = auparse_find_field(au, "tcontext"); } avc->context = copy_str(ctx); } add_proof(avc, au); if (list_append(events, avc) == NULL) { event_free(avc); return 1; } return 0; }
int process_resource_event(auparse_state_t *au) { uid_t uid; time_t time; const char *res_type, *uuid, *name; char field[64]; const char *reason; int success; /* Just skip this record if it failed to get some of the fields */ if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success)) return 0; /* Get the resource type */ auparse_first_record(au); res_type = auparse_find_field(au, "resrc"); reason = auparse_find_field(au, "reason"); if (res_type == NULL) { if (debug) fprintf(stderr, "Invalid resrc field.\n"); return 0; } /* Resource records with these types have old and new values. New * values indicate resources assignments and are added to the event * list. Old values are used to update the end time of a resource * assignment. */ int rc = 0; if (strcmp("disk", res_type) == 0 || strcmp("vcpu", res_type) == 0 || strcmp("mem", res_type) == 0 || strcmp("rng", res_type) == 0 || strcmp("net", res_type) == 0) { const char *res = NULL; /* Resource removed */ snprintf(field, sizeof(field), "old-%s", res_type); if(auparse_find_field(au, field)) res = auparse_interpret_field(au); if (res == NULL && debug) { fprintf(stderr, "Failed to get %s field.\n", field); } else { rc += update_resource(au, uuid, uid, time, name, success, reason, res_type, res); } /* Resource added */ res = NULL; snprintf(field, sizeof(field), "new-%s", res_type); if (auparse_find_field(au, field)) res = auparse_interpret_field(au); if (res == NULL && debug) { fprintf(stderr, "Failed to get %s field.\n", field); } else { rc += add_resource(au, uuid, uid, time, name, success, reason, res_type, res); } } else if (strcmp("cgroup", res_type) == 0) { auparse_first_record(au); const char *cgroup = NULL; if (auparse_find_field(au, "cgroup")) cgroup = auparse_interpret_field(au); rc += add_resource(au, uuid, uid, time, name, success, reason, res_type, cgroup); } else if (debug) { fprintf(stderr, "Found an unknown resource: %s.\n", res_type); } return rc; }
int process_avc_apparmor_target(auparse_state_t *au) { uid_t uid; time_t time; const char *profile; struct event *avc; /* Get profile associated with the AVC record */ if (auparse_find_field(au, "profile") == NULL) { if (debug) { auparse_first_record(au); fprintf(stderr, "AppArmor profile not found for AVC " "record: %s\n", auparse_get_record_text(au)); } return 0; } profile = auparse_interpret_field(au); /* Break path to get just the basename */ const char *basename = profile + strlen(profile); while (basename != profile && *basename != '/') basename--; if (*basename == '/') basename++; /* Check if it is an apparmor profile generated by libvirt and get the * guest UUID from it */ const char *prefix = "libvirt-"; if (strncmp(prefix, basename, strlen(prefix)) != 0) { if (debug) { fprintf(stderr, "Found a profile which is not " "generated by libvirt: %s\n", profile); } return 0; } /* Try to find a valid guest session */ const char *uuid = basename + strlen(prefix); struct list_node_t *it; struct event *machine_id = NULL; for (it = events->tail; it; it = it->prev) { struct event *event = it->data; if (event->success) { if (event->uuid != NULL && strcmp(event->uuid, uuid) == 0) { /* machine_id is used here instead of the start * event because it is generated before any * other event when a guest is started. So, * it's possible to correlate AVC events that * occurs during a guest start. */ if (event->type == ET_MACHINE_ID) { machine_id = event; break; } else if (event->type == ET_STOP) { break; } } else if (event->type == ET_DOWN) { break; } } } if (machine_id == NULL) { if (debug) { fprintf(stderr, "Found an AVC record for an unknown " "guest.\n"); } return 0; } if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL)) return 0; avc = event_alloc(); if (avc == NULL) return 1; avc->type = ET_AVC; /* Guest info */ avc->uuid = copy_str(machine_id->uuid); avc->name = copy_str(machine_id->name); memcpy(avc->proof, machine_id->proof, sizeof(avc->proof)); /* AVC info */ avc->start = time; avc->uid = uid; auparse_first_record(au); if (auparse_find_field(au, "apparmor")) { int i; avc->avc_result = copy_str(auparse_interpret_field(au)); for (i = 0; avc->avc_result && avc->avc_result[i]; i++) { avc->avc_result[i] = tolower(avc->avc_result[i]); } } if (auparse_find_field(au, "operation")) avc->avc_operation = copy_str(auparse_interpret_field(au)); if (auparse_find_field(au, "name")) avc->target = copy_str(auparse_interpret_field(au)); if (auparse_find_field(au, "comm")) avc->comm = copy_str(auparse_interpret_field(au)); add_proof(avc, au); if (list_append(events, avc) == NULL) { event_free(avc); return 1; } return 0; }
/* * auparse library callback that's called when an event is ready */ void push_event(auparse_state_t * au, auparse_cb_event_t cb_event_type, void *user_data) { int rc; BerElement *ber; int qualifier; char timestamp[26]; char linkValue[ZOS_REMOTE_LINK_VALUE_SIZE]; char logString[ZOS_REMOTE_LOGSTRING_SIZE]; unsigned long linkValue_tmp; if (cb_event_type != AUPARSE_CB_EVENT_READY) return; const au_event_t *e = auparse_get_timestamp(au); if (e == NULL) return; /* * we have an event. Each record will result in a different 'Item' * (refer ASN.1 definition in zos-remote-ldap.h) */ /* * Create a new BER element to encode the request */ ber = ber_alloc_t(LBER_USE_DER); if (ber == NULL) { log_err("Error allocating memory for BER element"); goto fatal; } /* * Collect some information to fill in every item */ const char *node = auparse_get_node(au); const char *orig_type = auparse_find_field(au, "type"); /* roll back event to get 'success' */ auparse_first_record(au); const char *success = auparse_find_field(au, "success"); /* roll back event to get 'res' */ auparse_first_record(au); const char *res = auparse_find_field(au, "res"); /* check if this event is a success or failure one */ if (success) { if (strncmp(success, "0", 1) == 0 || strncmp(success, "no", 2) == 0) qualifier = ZOS_REMOTE_QUALIF_FAIL; else qualifier = ZOS_REMOTE_QUALIF_SUCCESS; } else if (res) { if (strncmp(res, "0", 1) == 0 || strncmp(res, "failed", 6) == 0) qualifier = ZOS_REMOTE_QUALIF_FAIL; else qualifier = ZOS_REMOTE_QUALIF_SUCCESS; } else qualifier = ZOS_REMOTE_QUALIF_INFO; /* get timestamp text */ ctime_r(&e->sec, timestamp); timestamp[24] = '\0'; /* strip \n' */ /* prepare linkValue which will be used for every item */ linkValue_tmp = htonl(e->serial); /* padronize to use network * byte order */ memset(&linkValue, 0, ZOS_REMOTE_LINK_VALUE_SIZE); memcpy(&linkValue, &linkValue_tmp, sizeof(unsigned long)); /* * Prepare the logString with some meaningful text * We assume the first record type found is the * 'originating' audit record */ sprintf(logString, "Linux (%s): type: %s", node, orig_type); /* * Start writing to BER element. * There's only one field (version) out of the item sequence. * Also open item sequence */ rc = ber_printf(ber, "{i{", ICTX_REQUESTVER); if (rc < 0) goto skip_event; /* * Roll back to first record and iterate through all records */ auparse_first_record(au); do { const char *type = auparse_find_field(au, "type"); if (type == NULL) goto skip_event; log_debug("got record: %s", auparse_get_record_text(au)); /* * First field is item Version, same as global version */ rc = ber_printf(ber, "{i", ICTX_REQUESTVER); /* * Second field is the itemTag * use our internal event counter, increasing it */ rc |= ber_printf(ber, "i", conf.counter++); /* * Third field is the linkValue * using ber_put_ostring since it is not null-terminated */ rc |= ber_put_ostring(ber, linkValue, ZOS_REMOTE_LINK_VALUE_SIZE, LBER_OCTETSTRING); /* * Fourth field is the violation * Don't have anything better yet to put here */ rc |= ber_printf(ber, "b", 0); /* * Fifth field is the event. * FIXME: this might be the place to switch on the * audit record type and map to a more meaningful * SMF type 83, subtype 4 event here */ rc |= ber_printf(ber, "i", ZOS_REMOTE_EVENT_AUTHORIZATION); /* * Sixth field is the qualifier. We map 'success' or * 'res' to this field */ rc |= ber_printf(ber, "i", qualifier); /* * Seventh field is the Class * always use '@LINUX' for this version * max size ZOS_REMOTE_CLASS_SIZE */ rc |= ber_printf(ber, "t", ASN1_IA5STRING_TAG); rc |= ber_printf(ber, "s", "@LINUX"); /* * Eighth field is the resource * use the record type (name) as the resource * max size ZOS_REMOTE_RESOURCE_SIZE */ rc |= ber_printf(ber, "t", ASN1_IA5STRING_TAG); rc |= ber_printf(ber, "s", type); /* * Nineth field is the LogString * we try to put something meaningful here * we also start the relocations sequence */ rc |= ber_printf(ber, "t", ASN1_IA5STRING_TAG); rc |= ber_printf(ber, "s{", logString); /* * Now we start adding the relocations. * Let's add the timestamp as the first one * so it's out of the field loop */ rc |= ber_printf(ber, "{i", ZOS_REMOTE_RELOC_TIMESTAMP); rc |= ber_printf(ber, "t", ASN1_IA5STRING_TAG); rc |= ber_printf(ber, "s}", timestamp); /* * Check that encoding is going OK until now */ if (rc < 0) goto skip_event; /* * Now go to first field, * and iterate through all fields */ auparse_first_field(au); do { /* * we set a maximum of 1024 chars for * relocation data (field=value pairs) * Hopefuly this wont overflow too often */ char data[1024]; const char *name = auparse_get_field_name(au); const char *value = auparse_interpret_field(au); if (name == NULL || value == NULL) goto skip_event; /* * First reloc field is the Relocation type * We use 'OTHER' here since we don't have * anything better */ rc |= ber_printf(ber, "{i", ZOS_REMOTE_RELOC_OTHER); /* * Second field is the relocation data * We use a 'name=value' pair here * Use up to 1023 chars (one char left for '\0') */ snprintf(data, 1023, "%s=%s", name, value); rc |= ber_printf(ber, "t", ASN1_IA5STRING_TAG); rc |= ber_printf(ber, "s}", data); /* * Check encoding status */ if (rc < 0) goto skip_event; } while (auparse_next_field(au) > 0); /* * After adding all relocations we are done with * this item - finalize relocs and item */ rc |= ber_printf(ber, "}}"); /* * Check if we are doing well with encoding */ if (rc < 0) goto skip_event; } while (auparse_next_record(au) > 0); /* * We have all items in - finalize item sequence & request */ rc |= ber_printf(ber, "}}"); /* * Check if everything went alright with encoding */ if (rc < 0) goto skip_event; /* * finally, enqueue request and let the other * thread process it */ log_debug("Encoding done, enqueuing event"); enqueue(ber); return; skip_event: log_warn("Warning - error encoding request, skipping event"); ber_free(ber, 1); /* free it since we're not enqueuing it */ return; fatal: log_err("Error - Fatal error while encoding request. Aborting"); stop = 1; }
/* This function will output a normalized line of audit * fields one line per event as an english sentence */ static void text_event(auparse_state_t *au, auparse_cb_event_t cb_event_type, void *user_data) { if (cb_event_type != AUPARSE_CB_EVENT_READY) return; char tmp[20]; const char *item, *action, *how; int rc, type, id = -2; time_t t = auparse_get_time(au); struct tm *tv = localtime(&t); if (tv) strftime(tmp, sizeof(tmp), "%T %x", tv); else strcpy(tmp, "?"); type = auparse_get_type(au); auparse_normalize(au, NORM_OPT_NO_ATTRS); item = auparse_get_node(au); if (item) { printf("On %s at %s ", auparse_interpret_field(au), tmp); free((void *)item); } else printf("At %s ", tmp); rc = auparse_normalize_subject_primary(au); if (rc == 1) { const char *subj = auparse_interpret_field(au); id = auparse_get_field_int(au); if (strcmp(subj, "unset") == 0) subj = "system"; printf("%s", subj); } // Need to compare auid and uid before doing this rc = auparse_normalize_subject_secondary(au); if (rc == 1) { int uid = auparse_get_field_int(au); if (uid != id && id != -2) printf(", acting as %s,", auparse_interpret_field(au)); } rc = auparse_normalize_get_results(au); if (rc == 1) { int i = 0; const char *res[] = { "unsuccessfully", "successfully" }; item = auparse_interpret_field(au); if (strcmp(item, "yes") == 0) i = 1; else if (strncmp(item, "suc", 3) == 0) i = 1; else if (auparse_get_field_type(au) == AUPARSE_TYPE_SECCOMP && strcmp(item, "allow") == 0) i = 1; printf(" %s ", res[i]); } else putchar(' '); action = auparse_normalize_get_action(au); if (event_debug) { if (action == NULL) printf("error on type:%d\n", type); } printf("%s ", action ? action : "did-unknown"); rc = auparse_normalize_object_primary(au); if (rc == 1) { const char *val = NULL; int ftype; // If we have an object and this is an AVC, add some words if (action && strstr(action, "violated")) val = "accessing "; ftype = auparse_get_field_type(au); if (ftype == AUPARSE_TYPE_ESCAPED_FILE) val = auparse_interpret_realpath(au); else if (ftype == AUPARSE_TYPE_SOCKADDR) { val = auparse_interpret_sock_address(au); if (val == NULL) val = auparse_interpret_sock_family(au); } if (val == NULL) val = auparse_interpret_field(au); printf("%s ", val); } rc = auparse_normalize_object_primary2(au); if (rc == 1) { const char *val; if (auparse_get_field_type(au) == AUPARSE_TYPE_ESCAPED_FILE) val = auparse_interpret_realpath(au); else val = auparse_interpret_field(au); printf("to %s ", val); } how = auparse_normalize_how(au); if (how && action && *action != 'e') // Don't print for ended-session printf("using %s", how); printf("\n"); }
static void csv_event(auparse_state_t *au, auparse_cb_event_t cb_event_type, void *user_data) { if (cb_event_type != AUPARSE_CB_EVENT_READY) return; if (csv_header_done == 0) { csv_header_done = 1; printf( "NODE,EVENT,DATE,TIME,%sSERIAL_NUM,EVENT_KIND," "SESSION,SUBJ_PRIME,SUBJ_SEC,SUBJ_KIND,%sACTION," "RESULT,OBJ_PRIME,OBJ_SEC,%s%sOBJ_KIND,HOW%s\n", extra_time ? "YEAR,MONTH,DAY,WEEKDAY,HOUR,GMT_OFFSET," : "", extra_labels ? "SUBJ_LABEL," : "", extra_obj2 ? "OBJ2," : "", extra_labels ? "OBJ_LABEL," : "", extra_keys ? ",KEY" : ""); } char tmp[20]; const char *item, *type, *evkind, *subj_kind, *action, *str, *how; int rc; time_t t = auparse_get_time(au); struct tm *tv = localtime(&t); // NODE item = auparse_get_node(au); if (item) { printf("%s", auparse_interpret_field(au)); free((void *)item); } putchar(','); // Event type = auparse_get_type_name(au); if (type) printf("%s", type); putchar(','); // Normalize rc = auparse_normalize(au, extra_labels ? NORM_OPT_ALL : NORM_OPT_NO_ATTRS); //DATE if (tv) { strftime(tmp, sizeof(tmp), "%x", tv); printf("%s", tmp); } putchar(','); // TIME if (tv) { strftime(tmp, sizeof(tmp), "%T", tv); printf("%s", tmp); } putchar(','); if (extra_time) { // YEAR if (tv) { strftime(tmp, sizeof(tmp), "%Y", tv); printf("%s", tmp); } putchar(','); // MONTH if (tv) { strftime(tmp, sizeof(tmp), "%m", tv); printf("%s", tmp); } putchar(','); // DAY if (tv) { strftime(tmp, sizeof(tmp), "%d", tv); printf("%s", tmp); } putchar(','); // WEEKDAY if (tv) { strftime(tmp, sizeof(tmp), "%u", tv); printf("%s", tmp); } putchar(','); // HOUR if (tv) { strftime(tmp, sizeof(tmp), "%k", tv); printf("%s", tmp); } putchar(','); if (tv) { char sign = tv->tm_gmtoff >= 0 ? '+' : '-'; unsigned long total = labs(tv->tm_gmtoff); unsigned long hour = total/3600; unsigned long min = (total - (hour * 3600))%60; printf("%c%02lu:%02lu", sign, hour, min); } putchar(','); } // SERIAL_NUMBER printf("%lu,", auparse_get_serial(au)); if (rc) { fprintf(stderr, "error normalizing %s\n", type); // Just dump an empty frame printf(",,,,,,,,,%s%s\n", extra_labels ? ",," : "", extra_keys ? "," : ""); return; } // EVENT_KIND evkind = auparse_normalize_get_event_kind(au); printf("%s", evkind ? evkind : "unknown"); putchar(','); // SESSION rc = auparse_normalize_session(au); if (rc == 1) printf("%s", auparse_interpret_field(au)); putchar(','); // SUBJ_PRIME rc = auparse_normalize_subject_primary(au); if (rc == 1) { const char *subj = auparse_interpret_field(au); if (strcmp(subj, "unset") == 0) subj = "system"; printf("%s", subj); } putchar(','); // SUBJ_SEC rc = auparse_normalize_subject_secondary(au); if (rc == 1) printf("%s", auparse_interpret_field(au)); putchar(','); // SUBJ_KIND subj_kind = auparse_normalize_subject_kind(au); if (subj_kind) printf("%s", subj_kind); putchar(','); // SUBJ_LABEL if (extra_labels) { rc = auparse_normalize_subject_first_attribute(au); do { if (rc == 1) { const char *name = auparse_get_field_name(au); if (strcmp(name, "subj") == 0) { printf("%s", auparse_interpret_field(au)); break; } } } while (auparse_normalize_subject_next_attribute(au) == 1); putchar(','); } // ACTION action = auparse_normalize_get_action(au); printf("%s", action ? action : "did-unknown"); putchar(','); // RESULT rc = auparse_normalize_get_results(au); if (rc == 1) { int i = 0; const char *res[] = { "failed", "success" }; item = auparse_interpret_field(au); if (strcmp(item, "yes") == 0) i = 1; else if (strncmp(item, "suc", 3) == 0) i = 1; else if (auparse_get_field_type(au) == AUPARSE_TYPE_SECCOMP && strcmp(item, "allow") == 0) i = 1; printf("%s", res[i]); } putchar(','); // OBJ_PRIME rc = auparse_normalize_object_primary(au); if (rc == 1) { const char *val; if (auparse_get_field_type(au) == AUPARSE_TYPE_ESCAPED_FILE) val = auparse_interpret_realpath(au); else val = auparse_interpret_field(au); printf("%s", val); } putchar(','); // OBJ_SEC rc = auparse_normalize_object_secondary(au); if (rc == 1) printf("%s", auparse_interpret_field(au)); putchar(','); // OBJECT 2 if (extra_obj2) { rc = auparse_normalize_object_primary2(au); if (rc == 1) { const char *val; if (auparse_get_field_type(au) == AUPARSE_TYPE_ESCAPED_FILE) val = auparse_interpret_realpath(au); else val = auparse_interpret_field(au); printf("%s", val); } putchar(','); } // OBJ_LABEL if (extra_labels) { rc = auparse_normalize_object_first_attribute(au); do { if (rc == 1) { const char *name = auparse_get_field_name(au); if (strcmp(name, "obj") == 0) { printf("%s", auparse_interpret_field(au)); break; } } } while (auparse_normalize_object_next_attribute(au) == 1); putchar(','); } // OBJ_KIND str = auparse_normalize_object_kind(au); printf("%s,", str); // HOW how = auparse_normalize_how(au); if (how) printf("%s", how); // KEY if (extra_keys) { putchar(','); // This is to close out HOW rc = auparse_normalize_key(au); if (rc == 1) printf("%s", auparse_interpret_field(au)); } printf("\n"); }
/* * auparse_callback - callback routine to be executed once a complete event is composed */ void auparse_callback(auparse_state_t * au, auparse_cb_event_t cb_event_type, void *user_data) { int *event_cnt = (int *) user_data; if (cb_event_type == AUPARSE_CB_EVENT_READY) { if (auparse_first_record(au) <= 0) return; /* If no first record, then no event ! */ if (!(flags & F_CHECK)) printf("event=%d records=%d\n", *event_cnt, auparse_get_num_records(au)); do { const au_event_t *e = auparse_get_timestamp(au); if (e == NULL) return; /* If no timestamp, then no event */ /* If checking, we just emit the raw record again */ if (flags & F_CHECK) { if (e->host != NULL) printf("node=%s type=%s msg=audit(%u.%3.3u:%lu):", e->host, auparse_get_type_name(au), (unsigned) e->sec, e->milli, e->serial); else printf("type=%s msg=audit(%u.%3.3u:%lu):", auparse_get_type_name(au), (unsigned) e->sec, e->milli, e->serial); auparse_first_field(au); /* Move to first field */ do { const char *fname = auparse_get_field_name(au); /* We ignore the node and type fields */ if (strcmp(fname, "type") == 0 || strcmp(fname, "node") == 0) continue; printf(" %s=%s", fname, auparse_get_field_str(au)); } while (auparse_next_field(au) > 0); printf("\n"); continue; } printf("fields=%d\t", auparse_get_num_fields(au)); printf("type=%d (%s) ", auparse_get_type(au), auparse_get_type_name(au)); printf("event_tid=%u.%3.3u:%lu ", (unsigned) e->sec, e->milli, e->serial); if (flags & F_VERBOSE) { char *fv, *ifv = NULL; auparse_first_field(au); /* Move to first field */ do { fv = (char *) auparse_get_field_str(au); ifv = (char *) auparse_interpret_field(au); printf("%s=", auparse_get_field_name(au)); print_escape(stdout, fv, "=()"); printf(" ("); print_escape(stdout, ifv, "=()"); printf(") "); } while (auparse_next_field(au) > 0); } printf("\n"); } while (auparse_next_record(au) > 0); (*event_cnt)++; } }
int main(int argc, char **argv) { /* we're probably going to be started by auditd so, you know, whatever */ /* set up stdin to be searched ruthlessly */ FILE *log; auparse_state_t *auparse; uint32_t syscall; int auid, uid; int wtf; uint32_t _argc, i; const char *exe, *path, *success; char *cmdline, *tmp_cmd; char _argv[8]; struct passwd *au, *u; char *real_user, *apparent_user; _argc = 0; cmdline = NULL; log = fopen("/tmp/exemon.log", "w"); /* auparse = auparse_init(AUSOURCE_LOGS, NULL); */ auparse = auparse_init(AUSOURCE_FILE_POINTER, stdin); if (!auparse) { fprintf(log, "Couldn't do the thing with the thing.\n"); exit(1); } while ((wtf = auparse_next_event(auparse)) > 0) { /* Start fresh */ auid = -1; uid = -1; exe = NULL; path = NULL; success = NULL; _argc = 0; if (cmdline) free(cmdline); cmdline = NULL; /* Now we're doing the thing */ /* auparse_first_field(auparse); */ /* auparse_first_record(auparse); */ auparse_first_field(auparse); if (auparse_find_field(auparse, "syscall")) { syscall = auparse_get_field_int(auparse); if (syscall == 59 || syscall == 11) { if (auparse_exhaustive_find_field(auparse, "auid")) { auid = auparse_get_field_int(auparse); au = getpwuid(auid); if (au) real_user = strdup(au->pw_name); else asprintf(&real_user, "UID_%i", auid); au = NULL; } if (auparse_exhaustive_find_field(auparse, "uid")) { uid = auparse_get_field_int(auparse); u = getpwuid(uid); if (u) apparent_user = strdup(u->pw_name); else asprintf(&apparent_user, "UID_%i", uid); u = NULL; } if (auparse_exhaustive_find_field(auparse, "success")) success = auparse_get_field_str(auparse); if (auparse_exhaustive_find_field(auparse, "exe")) exe = auparse_get_field_str(auparse); if (auparse_exhaustive_find_field(auparse, "argc")) { _argc = auparse_get_field_int(auparse); for (i = 0; i < _argc; i++) { snprintf(_argv, 8, "a%i", i); if (auparse_find_field(auparse, _argv)) { if (!cmdline) asprintf(&cmdline, "%s", auparse_interpret_field(auparse)); else { asprintf(&tmp_cmd, "%s %s", cmdline, auparse_interpret_field(auparse)); free(cmdline); /* avoid leaking cmdline */ cmdline = tmp_cmd; } } } } if (auparse_exhaustive_find_field(auparse, "cwd")) path = auparse_get_field_str(auparse); else path = strdup("(unknown)"); if (exe && uid >= 0 && path && success) { if (auid == uid || auid == -1) { if (cmdline && (success[0] == 'y' || success[0] == 'Y')) { fprintf(log, "%s ran %s in path %s with args: %s\n", apparent_user, exe, path, cmdline); } else { fprintf(log, "%s failed to run %s in path %s\n", apparent_user, exe, path); if (!cmdline) { fprintf(log, "note: no cmdline: record: \n"); auparse_dump_records(auparse, log); } } } else { if (cmdline && (success[0] == 'y' || success[0] == 'Y')) { fprintf(log, "%s (as %s) ran %s in path %s with args: %s\n", real_user, apparent_user, exe, path, cmdline); } else { fprintf(log, "%s (as %s) failed to run %s in path %s\n", real_user, apparent_user, exe, path); } } } else { fprintf(log, "Incomplete record? path = %x, success = %x, uid = %i, exe = %x\n", path, success, uid, exe); fprintf(log, "record:\n"); auparse_dump_records(auparse, log); } fflush(log); /* avoid leaking on usernames and unknown paths */ free(apparent_user); free(real_user); if (path[0] == '(') { free(path); path = NULL; } apparent_user = NULL; real_user = NULL; } } } fprintf(log, "destroyed\n"); fclose(log); auparse_destroy(auparse); return 0; }