/
githubmirror
/
audit-userspace
Обзор
Документация
Войти
/
githubmirror
/
audit-userspace
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/ausearch-lol.c
489 строк
11 KB
Steve Grubb
Reject oversized audit event timestamps
20 июл 2026, 20:19
20 июл 2026, 20:19
0891570
Код
Авторство
О чём код?
/* * ausearch-lol.c - linked list of linked lists library * Copyright (c) 2008,2010,2014,2016,2019,2021 Red Hat Inc. * All Rights Reserved. * * This software may be freely redistributed and/or modified under the * terms of the GNU General Public License as published by the Free * Software Foundation; either version 2, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor * Boston, MA 02110-1335, USA. * * Authors: * Steve Grubb <sgrubb@redhat.com> */ #include "ausearch-lol.h" #include <stdlib.h> #include <errno.h> #include <string.h> #include <stdio.h> #include <limits.h> #include "ausearch-common.h" #include "auditd-config.h" #include "common.h" #define ARRAY_LIMIT 80 static int ready = 0; event very_first_event; // End of Event timeout value (in seconds). This can be over-riden via configuration or command line argument. static time_t eoe_timeout = EOE_TIMEOUT; void lol_create(lol *lo) { int size = ARRAY_LIMIT * sizeof(lolnode); lo->maxi = -1; lo->limit = ARRAY_LIMIT; lo->array = (lolnode *)malloc(size); if (lo->array == NULL) { fprintf(stderr, "Out of memory. Check %s file, %d line", __FILE__, __LINE__); lo->limit = 0; return; } memset(lo->array, 0, size); } void lol_clear(lol *lo) { int i; for (i=0; i<=lo->maxi; i++) { if (lo->array[i].status) { list_clear(lo->array[i].l); free(lo->array[i].l); } } free(lo->array); lo->array = NULL; lo->maxi = -1; } /* * lol_append - add an event list to the table of in-progress events * @lo: table that will own the event list on success * @l: completed event list to add * * Returns: 0 on success or -1 when the table cannot grow. The caller * retains ownership of l on failure. */ static int lol_append(lol *lo, llist *l) { int i; size_t new_size; lolnode *ptr; for(i=0; i<lo->limit; i++) { lolnode *cur = &lo->array[i]; if (cur->status == L_EMPTY) { cur->l = l; cur->status = L_BUILDING; if (i > lo->maxi) lo->maxi = i; return 0; } } // Overran the array...lets make it bigger new_size = sizeof(lolnode) * (lo->limit + ARRAY_LIMIT); ptr = realloc(lo->array, new_size); if (ptr) { lo->array = ptr; memset(&lo->array[lo->limit], 0, sizeof(lolnode) * ARRAY_LIMIT); lo->array[i].l = l; lo->array[i].status = L_BUILDING; lo->maxi = i; lo->limit += ARRAY_LIMIT; return 0; } return -1; } static int str2event(char *s, event *e) { char *ptr; unsigned long sec; errno = 0; sec = strtoul(s, NULL, 10); // Check the unsigned value before converting it to signed time_t. if (errno || eoe_timeout < 0 || eoe_timeout >= LONG_MAX || sec > (unsigned long)LONG_MAX - (unsigned long)eoe_timeout - 1) return -1; e->sec = (time_t)sec; ptr = strchr(s, '.'); if (ptr) { ptr++; e->milli = strtoul(ptr, NULL, 10); if (errno || e->milli > 999) return -1; s = ptr; } else e->milli = 0; ptr = strchr(s, ':'); if (ptr) { ptr++; e->serial = strtoul(ptr, NULL, 10); if (errno) return -1; } else e->serial = 0; return 0; } static int inline events_are_equal(event *e1, event *e2) { if (!(e1->serial == e2->serial && e1->milli == e2->milli && e1->sec == e2->sec)) return 0; if (e1->node && e2->node) { if (strcmp(e1->node, e2->node)) return 0; } else if (e1->node || e2->node) return 0; return 1; } // Returns -1 if e1 < e2, 0 if equal, and 1 if e1 > e2 static int compare_event_time(event *e1, event *e2) { if (e1->sec != e2->sec) { if (e1->sec > e2->sec) return 1; return -1; } if (e1->milli != e2->milli) { if (e1->milli > e2->milli) return 1; return -1; } if (e1->serial != e2->serial) { if (e1->serial > e2->serial) return 1; return -1; } return 0; } /* * This function will look at the line and pick out pieces of it. */ static int extract_timestamp(const char *b, event *e) { char *ptr, *tmp, *tnode, *ttype; e->node = NULL; if (*b == 'n') tmp = strndupa(b, 340); else tmp = strndupa(b, 80); ptr = audit_strsplit(tmp); if (ptr) { // Check to see if this is the node info if (*ptr == 'n') { tnode = ptr+5; ptr = audit_strsplit(NULL); if (ptr == NULL) return 0; } else tnode = NULL; // at this point we have type= ttype = ptr+5; // Now should be pointing to msg= ptr = audit_strsplit(NULL); // strlen is for fuzzers that make invalid lines if (ptr && strnlen(ptr, 20) > 18) { if (*(ptr+9) == '(') ptr+=9; else ptr = strchr(ptr, '('); if (ptr) { // now we should be pointed at the timestamp char *eptr; ptr++; eptr = strchr(ptr, ')'); if (eptr) *eptr = 0; if (str2event(ptr, e)) { fprintf(stderr, "Error extracting time stamp (%s)\n", ptr); return 0; } else if ((start_time && e->sec < start_time) || (end_time && e->sec > end_time)) { if (very_first_event.sec == 0) { very_first_event.sec = e->sec; very_first_event.milli = e->milli; } return 0; } else { // If no start time, any event is 1st if (very_first_event.sec == 0 && start_time == 0) { very_first_event.sec = e->sec; very_first_event.milli = e->milli; } if (tnode) e->node = strdup(tnode); e->type = audit_name_to_msg_type(ttype); } return 1; } // else we have a bad line } // else we have a bad line } // else we have a bad line return 0; } // This function will check events to see if they are complete static void check_events(lol *lo, time_t sec) { int i; for(i=0;i<=lo->maxi; i++) { lolnode *cur = &lo->array[i]; if (cur->status == L_BUILDING) { // If eoe_timeout seconds have elapsed, we are done if (cur->l->e.sec + eoe_timeout <= sec) { cur->status = L_COMPLETE; ready++; } else if (audit_is_last_record(cur->l->e.type)) { // If known to be 1 record event, we are done cur->status = L_COMPLETE; ready++; } } } } // This function will check events to see if they are complete but not compare against a given time static void check_events_without_time(lol *lo) { int i; for(i=0;i<=lo->maxi; i++) { lolnode *cur = &lo->array[i]; if (cur->status == L_BUILDING) { /* We now iterate over the event's records but without affecting the node's current * pointer (cur->l->cur). That is, we don't call the list-* routines * We could jump to the last record in the list which is normally a PROCTITLE, but this * may not be guaranteed, so we check all record types */ lnode *ln = cur->l->head; while (ln) { if (audit_is_last_record(ln->type)) { cur->status = L_COMPLETE; ready++; break; } ln = ln->next; } } } } // This function adds a new record to an existing linked list // or creates a new one if its a new event int lol_add_record(lol *lo, char *buff) { int i, fmt; lnode n; event e; char *ptr; llist *l; // Short circuit if event is not of interest if (extract_timestamp(buff, &e) == 0) return 0; n.a0 = 0L; n.a1 = 0L; n.type = e.type; n.message = strdup(buff); if(n.message == NULL) { free((char *)e.node); fprintf(stderr, "Out of memory. Check %s file, %d line", __FILE__, __LINE__); return 0; } ptr = strchr(n.message, AUDIT_INTERP_SEPARATOR); if (ptr) { n.mlen = ptr - n.message; if (n.mlen > MAX_AUDIT_MESSAGE_LENGTH) n.mlen = MAX_AUDIT_MESSAGE_LENGTH; *ptr = 0; n.interp = ptr + 1; // since we are most of the way down the string, scan from there ptr = strrchr(n.interp, 0x0a); if (ptr) { *ptr = 0; n.tlen = ptr - n.message; if (n.tlen > MAX_AUDIT_MESSAGE_LENGTH) n.tlen = MAX_AUDIT_MESSAGE_LENGTH; } else n.tlen = n.mlen; fmt = LF_ENRICHED; } else { ptr = strrchr(n.message, 0x0a); if (ptr) { *ptr = 0; n.mlen = ptr - n.message; if (n.mlen > MAX_AUDIT_MESSAGE_LENGTH) n.mlen = MAX_AUDIT_MESSAGE_LENGTH; } else n.mlen = strlen(n.message); n.interp = NULL; n.tlen = n.mlen; fmt = LF_RAW; } // Now see where this belongs for (i=0; i<=lo->maxi; i++) { if (lo->array[i].status == L_BUILDING) { l = lo->array[i].l; if (events_are_equal(&l->e, &e)) { free((char *)e.node); if (list_append(l, &n)) { /* list_append owns n.message only on success. */ free(n.message); return 0; } if (fmt > l->fmt) l->fmt = fmt; return 1; } } } // Eat standalone EOE, main event was already marked complete if (e.type == AUDIT_EOE) { free((char *)e.node); free(n.message); return 0; } // Create new event and fill it in l = malloc(sizeof(llist)); if (l == NULL) { free((char *)e.node); free(n.message); fprintf(stderr, "Out of memory. Check %s file, %d line", __FILE__, __LINE__); return 0; } list_create(l); l->e.milli = e.milli; l->e.sec = e.sec; l->e.serial = e.serial; l->e.node = e.node; l->e.type = e.type; l->fmt = fmt; if (list_append(l, &n)) { // list_append owns n.message only on success free(n.message); list_clear(l); free(l); return 0; } if (lol_append(lo, l)) { // lol_append owns l only after it has made table space for it list_clear(l); free(l); return 0; } check_events(lo, e.sec); return 1; } // This function will mark all events as "done" void terminate_all_events(lol *lo) { int i; for (i=0; i<=lo->maxi; i++) { lolnode *cur = &lo->array[i]; if (cur->status == L_BUILDING) { cur->status = L_COMPLETE; ready++; } } } // This function will mark all events as complete if it can. void complete_all_events(lol *lo) { check_events_without_time(lo); } /* Search the list for any event that is ready to go. The caller * takes custody of the memory */ llist* get_ready_event(lol *lo) { int i; lolnode *lowest = NULL; if (ready == 0) return NULL; for (i=0; i<=lo->maxi; i++) { // Look for the event with the lowest time stamp lolnode *cur = &lo->array[i]; if (cur->status == L_EMPTY) continue; if (lowest == NULL) lowest = cur; else if (compare_event_time(&(lowest->l->e), &(cur->l->e)) == 1) lowest = cur; } if (lowest && lowest->status == L_COMPLETE) { lowest->status = L_EMPTY; ready--; // Try to consolidate the array so that we iterate // over a smaller portion next time if (lowest == &lo->array[lo->maxi]) { lolnode *ptr = lowest; while (ptr->status == L_EMPTY && lo->maxi > 0) { lo->maxi--; ptr = &lo->array[lo->maxi]; } } return lowest->l; } return NULL; } /* * lol_set_eoe_timeout - set the end of event timeout to given value * * Args * new_eoe_tmo - value * Rtn * void */ void lol_set_eoe_timeout(time_t new_eoe_tmo) { eoe_timeout = new_eoe_tmo; } time_t lol_get_eoe_timeout(void) { return eoe_timeout; }