/* AVC records are correlated to guest through the apparmor path name. */ int process_avc_apparmor(auparse_state_t *au) { if (process_avc_apparmor_target(au)) return 1; auparse_first_record(au); return process_avc_apparmor_source(au); }
// Returns < 0 on error, 0 no data, > 0 success int ausearch_next_event(auparse_state_t *au) { int rc; if (au->expr == NULL) { errno = EINVAL; return -1; } if (au->expr->started == 0) { if ((rc = auparse_first_record(au)) <= 0) return rc; au->expr->started = 1; } else { if ((rc = auparse_next_event(au)) <= 0) return rc; } do { do { if ((rc = ausearch_compare(au)) > 0) { ausearch_reposition_cursors(au); return 1; } else if (rc < 0) return rc; } while ((rc = auparse_next_record(au)) > 0); if (rc < 0) return rc; } while ((rc = auparse_next_event(au)) > 0); if (rc < 0) return rc; return 0; }
static void auparse_callback(auparse_state_t *_au, auparse_cb_event_t cb_event_type, void *user_data) { int *event_cnt = (int *)user_data; int num_records = auparse_get_num_records(_au); int record_cnt; if (cb_event_type == AUPARSE_CB_EVENT_READY) { if (auparse_first_record(_au) <= 0) { return; } record_cnt = 1; do { int audtype = return_audtype(auparse_get_type(_au)); switch(audtype) { case PLACE_OBJ: // au, event number:total rec in event:this num in event process_place_obj(_au, event_cnt, num_records, record_cnt); break; case USER_OBJ: process_user_obj(_au, event_cnt, num_records, record_cnt); break; case SYSCALL_OBJ: process_syscall_obj(_au, event_cnt, num_records, record_cnt); break; case SOCK_OBJ: process_sock_obj(_au, event_cnt, num_records, record_cnt); break; case EXECVE_OBJ: process_execv_obj(_au, event_cnt, num_records, record_cnt); break; case GENERIC_OBJ: process_generic_obj(_au, event_cnt, num_records, record_cnt); break; } const au_event_t *e = auparse_get_timestamp(_au); if (e == NULL) { return; } record_cnt++; } while(auparse_next_record(_au) > 0); // end of do (*event_cnt)++; } // end cb_event_type == AUPARSE_CB_EVENT_READY }
/* This function shows how to dump a whole event by iterating over records */ static void dump_whole_event(auparse_state_t *au) { auparse_first_record(au); do { printf("%s\n", auparse_get_record_text(au)); } while (auparse_next_record(au) > 0); printf("\n"); }
void auparse_dump_records(auparse_state_t *auparse, FILE *fp) { auparse_first_record(auparse); if (!fp) fp = stdout; fprintf(fp, "%s\n", auparse_get_record_text(auparse)); while (auparse_next_record(auparse)) { fprintf(fp, "%s\n", auparse_get_record_text(auparse)); } }
int process_avc(auparse_state_t *au) { /* Check if it is a SELinux AVC record */ if (auparse_find_field(au, "tcontext")) { auparse_first_record(au); return process_avc_selinux(au); } #ifdef WITH_APPARMOR /* Check if it is an AppArmor AVC record */ auparse_first_record(au); if (auparse_find_field(au, "apparmor")) { auparse_first_record(au); return process_avc_apparmor(au); } #endif return 0; }
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; }
int auparse_exhaustive_find_field(auparse_state_t *auparse, const char *field) { const char *status; auparse_first_record(auparse); auparse_first_field(auparse); status = auparse_find_field(auparse, field); while (!status && auparse_next_record(auparse)) { status = auparse_find_field(auparse, field); } return (status ? 1 : 0); }
/* * Returns: -1 if an error occurs, * 0 if no more records in current event, * 1 for success. */ int auparse_next_record(auparse_state_t *au) { rnode *r; free_interpretation_list(); // Its OK if au->le == NULL because get_cnt handles it if (aup_list_get_cnt(au->le) == 0) { int rc = auparse_first_record(au); if (rc <= 0) return rc; } r = aup_list_next(au->le); if (r) { load_interpretation_list(r->interp); return 1; } else 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; }
/* 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; }
/* * 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)++; } }
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 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; }
/* * 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; }
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; }