static void feed_auparse(llist *l, auparse_callback_ptr callback) { const lnode *n; list_first(l); n = list_get_cur(l); if (!n) { fprintf(stderr, "Error - no elements in record."); return; } au = auparse_init(AUSOURCE_FEED, 0); auparse_set_escape_mode(au, escape_mode); auparse_add_callback(au, callback, NULL, NULL); do { // Records need to be terminated by a newline // Temporarily replace it. if (l->fmt == LF_ENRICHED) n->message[n->mlen] = AUDIT_INTERP_SEPARATOR; n->message[n->tlen] = 0x0a; auparse_feed(au, n->message, n->tlen+1); if (l->fmt == LF_ENRICHED) n->message[n->mlen] = 0; n->message[n->tlen] = 0; } while ((n=list_next(l))); auparse_flush_feed(au); auparse_destroy(au); }
static void process_bootup(auparse_state_t *au) { lnode *cur; int start; // See if we have unclosed boot up and make into CRASH record list_first(&l); cur = list_get_cur(&l); while (cur) { if (cur->name) { cur->user_end_proof = auparse_get_serial(au); cur->status = CRASH; cur->end = auparse_get_time(au); report_session(cur); } cur = list_next(&l); } // Logout and process anyone still left in the machine list_first(&l); cur = list_get_cur(&l); while (cur) { if (cur->status != CRASH) { cur->user_end_proof = auparse_get_serial(au); cur->status = DOWN; cur->end = auparse_get_time(au); report_session(cur); } cur = list_next(&l); } // Since this is a boot message, all old entries should be gone list_clear(&l); list_create(&l); // make reboot record - user:reboot, tty:system boot, host: kernel start = auparse_get_time(au); list_create_session(&l, 0, 0, 0, auparse_get_serial(au)); cur = list_get_cur(&l); cur->start = start; cur->name = strdup("reboot"); cur->term = strdup("system boot"); if (kernel) cur->host = strdup(kernel); cur->result = 0; }
static void process_bootup(auparse_state_t *au) { lnode *cur; int start; struct utsname ubuf; // See if we have unclosed boot up and make into CRASH record list_first(&l); cur = list_get_cur(&l); while(cur) { if (cur->name) { cur->user_end_proof = auparse_get_serial(au); cur->status = CRASH; cur->end = auparse_get_time(au); report_session(cur); } cur = list_next(&l); } // Logout and process anyone still left in the machine list_first(&l); cur = list_get_cur(&l); while(cur) { if (cur->status != CRASH) { cur->user_end_proof = auparse_get_serial(au); cur->status = DOWN; cur->end = auparse_get_time(au); report_session(cur); } cur = list_next(&l); } list_clear(&l); list_create(&l); // make reboot record - user:reboot, tty:system boot, host: uname -r uname(&ubuf); start = auparse_get_time(au); list_create_session(&l, 0, 0, 0, auparse_get_serial(au)); cur = list_get_cur(&l); cur->start = start; cur->name = strdup("reboot"); cur->term = strdup("system boot"); cur->host = strdup(ubuf.release); cur->result = 0; }
/* This function will output the record as is */ static void output_raw(llist *l) { const lnode *n; list_first(l); n = list_get_cur(l); if (!n) { fprintf(stderr, "Error - no elements in record."); return; } do { printf("%s\n", n->message); } while ((n=list_next(l))); }
static void report_finding(struct result_info *res, llist *l, probe_ctx *ctx) { SEXP_t *item; SEXP_t se_lport_mem, se_rport_mem, se_lfull_mem, se_ffull_mem, *se_uid_mem = NULL; lnode *n = NULL; if (l) { n = list_get_cur(l); } if (n) { item = probe_item_create(OVAL_LINUX_INET_LISTENING_SERVER, NULL, "protocol", OVAL_DATATYPE_STRING, res->proto, "local_address", OVAL_DATATYPE_STRING, res->laddr, "local_port", OVAL_DATATYPE_SEXP, SEXP_number_newu_64_r(&se_lport_mem, res->lport), "local_full_address", OVAL_DATATYPE_SEXP, SEXP_string_newf_r(&se_lfull_mem, "%s:%u", res->laddr, res->lport), "program_name", OVAL_DATATYPE_STRING, n->cmd, "foreign_address", OVAL_DATATYPE_STRING, res->raddr, "foreign_port", OVAL_DATATYPE_SEXP, SEXP_number_newu_64_r(&se_rport_mem, res->rport), "foreign_full_address", OVAL_DATATYPE_SEXP, SEXP_string_newf_r(&se_ffull_mem, "%s:%u", res->raddr, res->rport), "pid", OVAL_DATATYPE_INTEGER, (int64_t)n->pid, "user_id", OVAL_DATATYPE_SEXP, se_uid_mem = SEXP_number_newu_64(n->uid), NULL); } else { item = probe_item_create(OVAL_LINUX_INET_LISTENING_SERVER, NULL, "protocol", OVAL_DATATYPE_STRING, res->proto, "local_address", OVAL_DATATYPE_STRING, res->laddr, "local_port", OVAL_DATATYPE_SEXP, SEXP_number_newu_64_r(&se_lport_mem, res->lport), "local_full_address", OVAL_DATATYPE_SEXP, SEXP_string_newf_r(&se_lfull_mem, "%s:%u", res->laddr, res->lport), "foreign_address", OVAL_DATATYPE_STRING, res->raddr, "foreign_port", OVAL_DATATYPE_SEXP, SEXP_number_newu_64_r(&se_rport_mem, res->rport), "foreign_full_address", OVAL_DATATYPE_SEXP, SEXP_string_newf_r(&se_ffull_mem, "%s:%u", res->raddr, res->rport), NULL); } probe_item_collect(ctx, item); SEXP_free_r(&se_lport_mem); SEXP_free_r(&se_rport_mem); SEXP_free_r(&se_lfull_mem); SEXP_free_r(&se_ffull_mem); SEXP_free(se_uid_mem); }
/* This function will output the record as is */ static void output_raw(llist *l) { const lnode *n; list_first(l); n = list_get_cur(l); if (!n) { fprintf(stderr, "Error - no elements in record."); return; } do { // Only add the separator for enriched events. if (l->fmt == LF_ENRICHED) n->message[n->mlen] = AUDIT_INTERP_SEPARATOR; puts(n->message); } while ((n=list_next(l))); }
/* * This function will take the linked list and format it for output. No * interpretation is performed. The output order is lifo for everything. */ static void output_default(llist *l) { const lnode *n; list_last(l); n = list_get_cur(l); printf("----\ntime->%s", ctime(&l->e.sec)); if (!n) { fprintf(stderr, "Error - no elements in record."); return; } if (n->type >= AUDIT_DAEMON_START && n->type < AUDIT_SYSCALL) printf("%s\n", n->message); else { do { printf("%s\n", n->message); } while ((n=list_prev(l))); } }
/* * This function will take the linked list and format it for output. * Interpretation is performed to aid understanding of records. The output * order is lifo for everything. */ static void output_interpreted(llist *l) { const lnode *n; list_last(l); n = list_get_cur(l); printf("----\n"); if (!n) { fprintf(stderr, "Error - no elements in record."); return; } if (n->type >= AUDIT_DAEMON_START && n->type < AUDIT_SYSCALL) output_interpreted_node(n); else { do { output_interpreted_node(n); } while ((n=list_prev(l))); } }
static void process_shutdown(auparse_state_t *au) { lnode *cur; // Find reboot record list_first(&l); cur = list_get_cur(&l); while (cur) { if (cur->name) { // Found it - close it out and display it time_t end = auparse_get_time(au); list_update_logout(&l, end, auparse_get_serial(au)); report_session(cur); list_delete_cur(&l); return; } cur = list_next(&l); } }
/* * This function will take the linked list and format it for output. No * interpretation is performed. The output order is lifo for everything. */ static void output_default(llist *l) { const lnode *n; list_last(l); n = list_get_cur(l); printf("----\ntime->%s", ctime(&l->e.sec)); if (!n) { fprintf(stderr, "Error - no elements in record."); return; } if (n->type >= AUDIT_DAEMON_START && n->type < AUDIT_SYSCALL) puts(n->message); // No injection possible else { do { safe_print_string_n(n->message, n->mlen, 1); } while ((n=list_prev(l))); } }
static void report_finding(struct result_info *res, llist *l, probe_ctx *ctx, oval_schema_version_t over) { SEXP_t *item, *user_id; lnode *n = list_get_cur(l); if (oval_schema_version_cmp(over, OVAL_SCHEMA_VERSION(5.10)) < 0) user_id = SEXP_string_newf("%d", n->uid); else user_id = SEXP_number_newi_64((int64_t)n->uid); item = probe_item_create(OVAL_LINUX_IFLISTENERS, NULL, "interface_name", OVAL_DATATYPE_STRING, res->interface_name, "protocol", OVAL_DATATYPE_STRING, res->protocol, "hw_address", OVAL_DATATYPE_STRING, res->hw_address, "program_name", OVAL_DATATYPE_STRING, n->cmd, "pid", OVAL_DATATYPE_INTEGER, (int64_t)n->pid, "user_id", OVAL_DATATYPE_SEXP, user_id, NULL); probe_item_collect(ctx, item); SEXP_free(user_id); }
int main(int argc, char *argv[]) { int i, use_stdin = 0; char *user = NULL, *file = NULL; struct passwd *p; auparse_state_t *au; setlocale (LC_ALL, ""); for (i=1; i<argc; i++) { if (argv[i][0] != '-') { //take input and lookup as if it were a user name //if that fails assume its a tty if (user == NULL) { p = getpwnam(argv[i]); if (p) { cuid = p->pw_uid; user = argv[i]; continue; } } if (cterm == NULL) { cterm = argv[i]; } else { usage(); return 1; } } else { if (strcmp(argv[i], "-f") == 0) { if (use_stdin == 0) { i++; file = argv[i]; } else { fprintf(stderr,"stdin already given\n"); return 1; } } else if (strcmp(argv[i], "--bad") == 0) { bad = 1; } else if (strcmp(argv[i], "--proof") == 0) { proof = 1; } else if (strcmp(argv[i], "--extract") == 0) { f = fopen("aulast.log", "wt"); } else if (strcmp(argv[i], "--stdin") == 0) { if (file == NULL) use_stdin = 1; else { fprintf(stderr, "file already given\n"); return 1; } } else if (strcmp(argv[i], "--debug") == 0) { debug = 1; } else { usage(); return 1; } } } list_create(&l); // Search for successful user logins if (file) au = auparse_init(AUSOURCE_FILE, file); else if (use_stdin) au = auparse_init(AUSOURCE_FILE_POINTER, stdin); else { if (getuid()) { fprintf(stderr, "You probably need to be root for this to work\n"); } au = auparse_init(AUSOURCE_LOGS, NULL); } if (au == NULL) { fprintf(stderr, "Error - %s\n", strerror(errno)); goto error_exit_1; } // The theory: iterate though events // 1) when LOGIN is found, create a new session node // 2) if that session number exists, close out the old one // 3) when USER_LOGIN is found, update session node // 4) When USER_END is found update session node and close it out // 5) When BOOT record found make new record and check for previous // 6) If previous boot found, set status to crash and logout everyone // 7) When SHUTDOWN found, close out reboot record while (auparse_next_event(au) > 0) { // We will take advantage of the fact that all events // of interest are one record long int type = auparse_get_type(au); if (type < 0) continue; switch (type) { case AUDIT_LOGIN: create_new_session(au); extract_record(au); break; case AUDIT_USER_LOGIN: update_session_login(au); extract_record(au); break; case AUDIT_USER_END: update_session_logout(au); extract_record(au); break; case AUDIT_SYSTEM_BOOT: process_bootup(au); extract_record(au); break; case AUDIT_SYSTEM_SHUTDOWN: process_shutdown(au); extract_record(au); break; case AUDIT_DAEMON_START: process_kernel(au); extract_record(au); break; } } auparse_destroy(au); // Now output the leftovers list_first(&l); do { lnode *cur = list_get_cur(&l); report_session(cur); } while (list_next(&l)); free(kernel); list_clear(&l); if (f) fclose(f); return 0; error_exit_1: list_clear(&l); if (f) fclose(f); return 1; }
int match(llist *l) { // Are we within time range? if (start_time == 0 || l->e.sec >= start_time) { if (end_time == 0 || l->e.sec <= end_time) { if (event_id == -1 || event_id == l->e.serial) { // OK - do the heavier checking if (extract_search_items(l)) { return 0; } // perform additional tests for the field if (event_node_list) { const snode *sn; int found=0; slist *sptr = event_node_list; if (l->e.node == NULL) return 0; slist_first(sptr); sn=slist_get_cur(sptr); while (sn && !found) { if (sn->str && (!strcmp(sn->str, l->e.node))) found++; else sn=slist_next(sptr); } if (!found) return 0; } if (user_match(l) == 0) return 0; if (group_match(l) == 0) return 0; if ((event_ppid != -1) && (event_ppid != l->s.ppid)) return 0; if ((event_pid != -1) && (event_pid != l->s.pid)) return 0; if (event_machine != -1 && (event_machine != audit_elf_to_machine(l->s.arch))) return 0; if ((event_syscall != -1) && (event_syscall != l->s.syscall)) return 0; if ((event_session_id != -2) && (event_session_id != l->s.session_id)) return 0; if (event_exit_is_set) { if (l->s.exit_is_set == 0) return 0; if (event_exit != l->s.exit) return 0; } if ((event_success != S_UNSET) && (event_success != l->s.success)) return 0; // event_type requires looking at each item if (event_type != NULL) { int found = 0; const lnode *n; list_first(l); n = list_get_cur(l); do { int_node *in; ilist_first(event_type); in = ilist_get_cur(event_type); do { if (in->num == n->type){ found = 1; break; } } while((in = ilist_next(event_type))); if (found) break; } while ((n = list_next(l))); if (!found) return 0; } // Done all the easy compares, now do the // string searches. if (event_filename) { int found = 0; if (l->s.filename == NULL && l->s.cwd == NULL) return 0; if (l->s.filename) { const snode *sn; slist *sptr = l->s.filename; slist_first(sptr); sn=slist_get_cur(sptr); do { if (sn->str == NULL) return 0; if (strmatch( event_filename, sn->str)) { found = 1; break; } } while ((sn=slist_next(sptr))); if (!found && l->s.cwd == NULL) return 0; } if (l->s.cwd && !found) { /* Check cwd, too */ if (strmatch(event_filename, l->s.cwd) == 0) return 0; } } if (event_hostname) { if (l->s.hostname == NULL) return 0; if (strmatch(event_hostname, l->s.hostname) == 0) return 0; } if (event_terminal) { if (l->s.terminal == NULL) return 0; if (strmatch(event_terminal, l->s.terminal) == 0) return 0; } if (event_exe) { if (l->s.exe == NULL) return 0; if (strmatch(event_exe, l->s.exe) == 0) return 0; } if (event_comm) { if (l->s.comm == NULL) return 0; if (strmatch(event_comm, l->s.comm) == 0) return 0; } if (event_key) { if (l->s.key == NULL) return 0; else { int found = 0; const snode *sn; slist *sptr = l->s.key; slist_first(sptr); sn=slist_get_cur(sptr); do { if (sn->str == NULL) return 0; if (strmatch( event_key, sn->str)) { found = 1; break; } } while ((sn=slist_next(sptr))); if (!found) return 0; } } if (event_vmname) { if (l->s.vmname == NULL) return 0; if (strmatch(event_vmname, l->s.vmname) == 0) return 0; } if (event_uuid) { if (l->s.uuid == NULL) return 0; if (strmatch(event_uuid, l->s.uuid) == 0) return 0; } if (context_match(l) == 0) return 0; return 1; } } } return 0; }