static void create_new_session(auparse_state_t *au) { const char *tpid, *tses, *tauid; int pid = -1, auid = -1, ses = -1; lnode *cur; // Get pid tpid = auparse_find_field(au, "pid"); if (tpid) pid = auparse_get_field_int(au); // Get second auid field auparse_find_field(au, "auid"); auparse_next_field(au); tauid = auparse_find_field(au, "auid"); if (tauid) auid = auparse_get_field_int(au); // Get second ses field auparse_find_field(au, "ses"); auparse_next_field(au); tses = auparse_find_field(au, "ses"); if (tses) ses = auparse_get_field_int(au); // Check that they are valid if (pid == -1 || auid ==-1 || ses == -1) { if (debug) fprintf(stderr, "Bad login for event: %lu\n", auparse_get_serial(au)); return; } // See if this session is already open //cur = list_find_auid(&l, auid, pid, ses); cur = list_find_session(&l, ses); if (cur) { // This means we have an open session close it out cur->status = GONE; cur->end = auparse_get_time(au); report_session(cur); list_delete_cur(&l); } // If this is supposed to be limited to a specific // uid and we don't have that record, skip creating it if (cuid != -1 && cuid != auid) { if (debug) fprintf(stderr, "login reporting limited to %d for event: %lu\n", cuid, auparse_get_serial(au)); return; } list_create_session(&l, auid, pid, ses, auparse_get_serial(au)); }
static void update_session_logout(auparse_state_t *au) { const char *tses, *tauid, *tpid; int pid = -1, auid = -1, ses = -1; lnode *cur; // Get pid field tpid = auparse_find_field(au, "pid"); if (tpid) pid = auparse_get_field_int(au); // Get auid field tauid = auparse_find_field(au, "auid"); if (tauid) auid = auparse_get_field_int(au); // Get ses field tses = auparse_find_field(au, "ses"); if (tses) ses = auparse_get_field_int(au); // Check that they are valid if (pid == -1 || auid ==-1 || ses == -1) { if (debug) fprintf(stderr, "Bad user logout for event: %lu\n", auparse_get_serial(au)); return; } // See if this session is already open cur = list_find_auid(&l, auid, pid, ses); if (cur) { // if time never got updated, this must be a cron or su // session...so we will just delete it. if (cur->start) { // This means we have an open session close it out time_t end = auparse_get_time(au); list_update_logout(&l, end, auparse_get_serial(au)); report_session(cur); } else if (debug) fprintf(stderr, "start time error for event: %lu\n", auparse_get_serial(au)); list_delete_cur(&l); } }
int add_start_guest_event(auparse_state_t *au) { struct event *start; uid_t uid; time_t time; const char *uuid, *name; int success; list_node_t *it; /* 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; /* On failure, loop backwards to update all the resources associated to * the last session of this guest. When a machine_id or a stop event is * found the loop can be broken because a machine_id is created at the * beginning of a session and a stop event indicates a previous * session. */ if (!success) { for (it = events->tail; it; it = it->prev) { struct event *event = it->data; if (event->success && event->uuid && strcmp(uuid, event->uuid) == 0) { if (event->type == ET_STOP || event->type == ET_MACHINE_ID) { /* An old session found. */ break; } else if (event->type == ET_RES && event->end == 0) { event->end = time; add_proof(event, au); } } } } start = event_alloc(); if (start == NULL) return 1; start->type = ET_START; start->uuid = copy_str(uuid); start->name = copy_str(name); start->success = success; start->uid = uid; start->start = time; auparse_first_record(au); if (auparse_find_field(au, "vm-pid")) start->pid = auparse_get_field_int(au); add_proof(start, au); if (list_append(events, start) == NULL) { event_free(start); 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 add_stop_guest_event(auparse_state_t *au) { list_node_t *it; struct event *stop, *start = NULL, *event = NULL; uid_t uid; time_t time; const char *uuid, *name; 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; /* Loop backwards to find the last start event for the uuid and * update all resource records related to that guest session. */ for (it = events->tail; it; it = it->prev) { event = it->data; if (event->success && event->uuid && strcmp(uuid, event->uuid) == 0) { if (event->type == ET_START) { /* If an old session is found it's no longer * necessary to update the resource records. */ if (event->end || start) break; /* This is the start event related to the * current session. */ start = event; } else if (event->type == ET_STOP || event->type == ET_MACHINE_ID) { /* Old session found. */ break; } else if (event->type == ET_RES && event->end == 0) { /* Update the resource assignments. */ event->end = time; add_proof(event, au); } } } if (start == NULL) { if (debug) { fprintf(stderr, "Couldn't find the correlated start " "record to the stop event.\n"); } return 0; } /* Create a new stop event */ stop = event_alloc(); if (stop == NULL) return 1; stop->type = ET_STOP; stop->uuid = copy_str(uuid); stop->name = copy_str(name); stop->success = success; stop->uid = uid; stop->start = time; auparse_first_record(au); if (auparse_find_field(au, "vm-pid")) stop->pid = auparse_get_field_int(au); add_proof(stop, au); if (list_append(events, stop) == NULL) { event_free(stop); return 1; } /* Update the correlated start event. */ if (success) { start->end = time; add_proof(start, au); } return 0; }
/* This function tries to correlate an anomaly record to a guest using the qemu * pid or the selinux context. */ int process_anom(auparse_state_t *au) { uid_t uid; time_t time; pid_t pid = -1; list_node_t *it; struct event *anom, *start = NULL; /* An anomaly record is correlated to a guest by the process id */ if (auparse_find_field(au, "pid")) { pid = auparse_get_field_int(au); } else { if (debug) { fprintf(stderr, "Found an anomaly record " "without pid.\n"); } } /* Loop backwards to find a running guest with the same pid. */ if (pid >= 0) { for (it = events->tail; it; it = it->next) { struct event *event = it->data; if (event->pid == pid && event->success) { if (event->type == ET_STOP) { break; } else if (event->type == ET_START) { if (event->end == 0) start = event; break; } } } } /* Try to match using selinux context */ if (start == NULL) { const char *seclevel; struct event *machine_id; seclevel = get_seclevel(auparse_find_field(au, "subj")); if (seclevel == NULL) { if (debug) { auparse_first_record(au); const char *text = auparse_get_record_text(au); fprintf(stderr, "Security context not found " "for anomaly event: %s\n", text ? text : ""); } 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 anomaly event.\n"); } return 0; } for (it = events->tail; it; it = it->next) { struct event *event = it->data; if (event->success && machine_id->uuid && event->uuid && strcmp(machine_id->uuid, event->uuid) == 0) { if (event->type == ET_STOP) { break; } else if (event->type == ET_START) { if (event->end == 0) start = event; break; } } } } if (start == NULL) { if (debug) { const char *text = auparse_get_record_text(au); fprintf(stderr, "Guest not found for " "anomaly record: %s.\n", text ? text : ""); } return 0; } if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL)) return 0; anom = event_alloc(); if (anom == NULL) return 1; anom->type = ET_ANOM; anom->uuid = copy_str(start->uuid); anom->name = copy_str(start->name); anom->uid = uid; anom->start = time; anom->pid = pid; memcpy(anom->proof, start->proof, sizeof(anom->proof)); add_proof(anom, au); if (list_append(events, anom) == NULL) { event_free(anom); return 1; } return 0; }
/* 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"); }
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; }