Skip to content

Commit 2bb8900

Browse files
committed
auparse: report feed consumption errors
auparse_feed() and auparse_flush_feed() only reported buffering failures even though consume_feed() could encounter parser failures or skip malformed feed records. They also allowed feed data to be consumed without a registered callback, which could silently discard ready events. Convert consume_feed() to return status, require feed callers to have an AUSOURCE_FEED parser with a callback, propagate parser failures with errno, and report malformed feed records as EBADMSG after continuing to consume the available feed data. Malformed records remain rejected and are not delivered to the callback. Add auparse_extra_test coverage for missing callbacks and malformed feed records, and document the stricter feed contract, errno meanings, malformed-record disposition, and errno success caveat in the feed man pages.
1 parent 676e6c7 commit 2bb8900

6 files changed

Lines changed: 254 additions & 27 deletions

File tree

ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- Add KillMode=mixed to the auditd unit file (Issue 272)
44
- Update perm table to add syscalls to it
55
- Auditd - prevent netlink congestion
6+
- Propogate feed consumption errors into auparse_feed and auparse_flush_feed
67

78
4.1.4
89
- Update syscalls and io_uring tables for the 7.0 kernel (Hilko Bengen)

auparse/auparse.c

Lines changed: 105 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ static int debug = 0;
4646

4747
static time_t eoe_timeout = EOE_TIMEOUT;
4848

49+
static int au_auparse_next_event(auparse_state_t *au, int *malformed);
50+
4951
/* like strchr except string is delimited by length, not null byte */
5052
static char *strnchr(const char *s, int c, size_t n)
5153
{
@@ -603,19 +605,60 @@ void auparse_add_callback(auparse_state_t *au, auparse_callback_ptr callback,
603605
au->callback_user_data_destroy = user_destroy_func;
604606
}
605607

606-
static void consume_feed(auparse_state_t *au, int flush)
608+
/* validate_feed - check whether @au is ready to consume feed data
609+
* @au: parser state machine to validate
610+
*
611+
* Return: 0 on success, -1 on failure with errno set.
612+
*/
613+
static int validate_feed(const auparse_state_t *au)
614+
{
615+
if (au == NULL || au->source != AUSOURCE_FEED ||
616+
au->callback == NULL) {
617+
errno = EINVAL;
618+
return -1;
619+
}
620+
621+
return 0;
622+
}
623+
624+
/* consume_feed - process buffered feed data and optionally flush events
625+
* @au: parser state machine
626+
* @flush: non-zero to terminate and deliver outstanding events
627+
*
628+
* Return: 0 on success, -1 on failure with errno set.
629+
*/
630+
static int consume_feed(auparse_state_t *au, int flush)
607631
{
632+
int rc, malformed, saved_errno = 0;
633+
608634
//if (debug) printf("consume feed, flush %d\n", flush);
609-
while (auparse_next_event(au) > 0) {
610-
if (au->callback) {
635+
do {
636+
/*
637+
* This calls the lower-level parser so malformed feed
638+
* records can be reported while preserving auparse_next_event()
639+
* normalizer state handling.
640+
*/
641+
malformed = 0;
642+
clear_normalizer(&au->norm_data);
643+
rc = au_auparse_next_event(au, &malformed);
644+
if (malformed && saved_errno == 0)
645+
saved_errno = EBADMSG;
646+
647+
if (rc > 0)
611648
(*au->callback)(au, AUPARSE_CB_EVENT_READY,
612649
au->callback_user_data);
613-
}
650+
} while (rc > 0);
651+
652+
if (rc < 0) {
653+
if (errno == 0)
654+
errno = EIO;
655+
return -1;
614656
}
657+
615658
if (flush) {
616659
/* Terminate all outstanding events, as we are at end of input
617-
* (ie mark BUILDING events as COMPLETE events) then if we
618-
* have a callback execute the callback on each event
660+
* (ie mark BUILDING events as COMPLETE events) then execute
661+
* the callback on each event
619662
*/
620663
event_list_t *l;
621664

@@ -630,12 +673,17 @@ static void consume_feed(auparse_state_t *au, int flush)
630673
load_interpretation_list(au, r->interp);
631674
aup_list_first_field(l);
632675

633-
if (au->callback) {
634-
(*au->callback)(au, AUPARSE_CB_EVENT_READY,
676+
(*au->callback)(au, AUPARSE_CB_EVENT_READY,
635677
au->callback_user_data);
636-
}
637678
}
638679
}
680+
681+
if (saved_errno) {
682+
errno = saved_errno;
683+
return -1;
684+
}
685+
686+
return 0;
639687
}
640688

641689
int auparse_new_buffer(auparse_state_t *au, const char *data, size_t data_len)
@@ -651,18 +699,39 @@ int auparse_new_buffer(auparse_state_t *au, const char *data, size_t data_len)
651699
return 0;
652700
}
653701

702+
/* auparse_feed - append data to the feed parser and consume what is ready
703+
* @au: parser state machine
704+
* @data: feed data to append
705+
* @data_len: length of @data
706+
*
707+
* Return: 0 on success, -1 on failure with errno set.
708+
*/
654709
int auparse_feed(auparse_state_t *au, const char *data, size_t data_len)
655710
{
656-
if (databuf_append(&au->databuf, data, data_len) < 0)
711+
if (data == NULL && data_len != 0) {
712+
errno = EINVAL;
657713
return -1;
658-
consume_feed(au, 0);
659-
return 0;
714+
}
715+
if (validate_feed(au) < 0)
716+
return -1;
717+
if (databuf_append(&au->databuf, data, data_len) < 0) {
718+
if (errno == 0)
719+
errno = ENOMEM;
720+
return -1;
721+
}
722+
return consume_feed(au, 0);
660723
}
661724

725+
/* auparse_flush_feed - flush pending feed data through the parser
726+
* @au: parser state machine
727+
*
728+
* Return: 0 on success, -1 on failure with errno set.
729+
*/
662730
int auparse_flush_feed(auparse_state_t *au)
663731
{
664-
consume_feed(au, 1);
665-
return 0;
732+
if (validate_feed(au) < 0)
733+
return -1;
734+
return consume_feed(au, 1);
666735
}
667736

668737
// If there is any data in the state machine, return 1.
@@ -696,11 +765,21 @@ int auparse_feed_has_ready_event(auparse_state_t *au)
696765
return 0;
697766
}
698767

768+
/* auparse_feed_age_events - complete feed events aged out by the clock
769+
* @au: parser state machine
770+
*
771+
* Return: none.
772+
*/
699773
void auparse_feed_age_events(auparse_state_t *au)
700774
{
701-
time_t t = time(NULL);
775+
time_t t;
776+
777+
if (validate_feed(au) < 0)
778+
return;
779+
780+
t = time(NULL);
702781
au_check_events(au, t);
703-
consume_feed(au, 0);
782+
(void)consume_feed(au, 0);
704783
}
705784

706785
void auparse_set_escape_mode(auparse_state_t *au, auparse_esc_t mode)
@@ -1476,16 +1555,14 @@ int ausearch_next_event(auparse_state_t *au)
14761555
return 0;
14771556
}
14781557

1479-
/*
1480-
* au_auparse_next_event - Get the next complete event
1481-
* Args:
1482-
* au - the parser state machine
1483-
* Rtns:
1484-
* < 0 - error
1485-
* == 0 - no data
1486-
* > 0 - we have an event and it's set to the 'current event' au->le
1558+
/* au_auparse_next_event - get the next complete event
1559+
* @au: parser state machine
1560+
* @malformed: optional flag set when a malformed record is skipped
1561+
*
1562+
* Return: less than 0 on error, 0 if no data is available, or greater
1563+
* than 0 if an event is available and set as the current event.
14871564
*/
1488-
static int au_auparse_next_event(auparse_state_t *au)
1565+
static int au_auparse_next_event(auparse_state_t *au, int *malformed)
14891566
{
14901567
int rc, i, built;
14911568
event_list_t *l;
@@ -1621,6 +1698,8 @@ static int au_auparse_next_event(auparse_state_t *au)
16211698
if (debug)
16221699
printf("Malformed line:%s\n", au->cur_buf);
16231700
#endif /* LOL_EVENTS_DEBUG01 */
1701+
if (malformed)
1702+
*malformed = 1;
16241703
continue;
16251704
}
16261705

@@ -1716,7 +1795,7 @@ static int au_auparse_next_event(auparse_state_t *au)
17161795
int auparse_next_event(auparse_state_t *au)
17171796
{
17181797
clear_normalizer(&au->norm_data);
1719-
return au_auparse_next_event(au);
1798+
return au_auparse_next_event(au, NULL);
17201799
}
17211800

17221801
/* Accessors to event data */

auparse/test/auparse_extra_test.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "config.h"
22
#include <assert.h>
3+
#include <errno.h>
34
#include <stdio.h>
45
#include <string.h>
56
#include <stdlib.h>
@@ -33,6 +34,7 @@ static void test_feed_state(void)
3334
const char buf[] = "type=LOGIN msg=audit(1143146623.787:142): pid=1\n";
3435
auparse_state_t *au = auparse_init(AUSOURCE_FEED, NULL);
3536
assert(au != NULL);
37+
cb_count = 0;
3638
auparse_add_callback(au, ready_cb, NULL, NULL);
3739
assert(auparse_feed_has_data(au) == 0);
3840
assert(auparse_feed(au, buf, strlen(buf)) == 0);
@@ -42,6 +44,46 @@ static void test_feed_state(void)
4244
auparse_destroy(au);
4345
}
4446

47+
static void test_feed_requires_callback(void)
48+
{
49+
const char buf[] = "type=LOGIN msg=audit(1143146623.787:142): pid=1\n";
50+
auparse_state_t *au = auparse_init(AUSOURCE_FEED, NULL);
51+
52+
assert(au != NULL);
53+
errno = 0;
54+
assert(auparse_feed(au, buf, strlen(buf)) == -1);
55+
assert(errno == EINVAL);
56+
assert(auparse_feed_has_data(au) == 0);
57+
58+
errno = 0;
59+
assert(auparse_flush_feed(au) == -1);
60+
assert(errno == EINVAL);
61+
62+
auparse_destroy(au);
63+
}
64+
65+
static void test_feed_rejects_malformed_record(void)
66+
{
67+
const char buf[] =
68+
"not an audit record\n"
69+
"type=LOGIN msg=audit(1143146623.787:142): pid=1\n";
70+
auparse_state_t *au = auparse_init(AUSOURCE_FEED, NULL);
71+
72+
assert(au != NULL);
73+
cb_count = 0;
74+
auparse_add_callback(au, ready_cb, NULL, NULL);
75+
76+
errno = 0;
77+
assert(auparse_feed(au, buf, strlen(buf)) == -1);
78+
assert(errno == EBADMSG);
79+
assert(cb_count == 0);
80+
81+
assert(auparse_flush_feed(au) == 0);
82+
assert(cb_count == 1);
83+
84+
auparse_destroy(au);
85+
}
86+
4587
static void test_normalize(void)
4688
{
4789
auparse_state_t *au = auparse_init(AUSOURCE_FILE, "./test.log");
@@ -218,6 +260,8 @@ int main(void)
218260
{
219261
test_new_buffer();
220262
test_feed_state();
263+
test_feed_requires_callback();
264+
test_feed_rejects_malformed_record();
221265
test_normalize();
222266
test_compare();
223267
test_timestamp_milli();

docs/auparse_feed.3

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ number of bytes in
2727
supplies new data for the parser to consume.
2828
.I auparse_init()
2929
must have been called with a source type of AUSOURCE_FEED and a NULL pointer.
30+
.I auparse_add_callback()
31+
must be called before feeding data so that complete events have a consumer.
3032
.br
3133
.sp
3234
The parser consumes as much data
@@ -38,10 +40,58 @@ each time the parser recognizes a complete event in the data stream. Data not fu
3840
prepended to the next feed data. After all data has been feed to the parser
3941
.I auparse_flush_feed
4042
should be called to signal the end of input data and flush any pending parse data through the parsing system.
43+
Malformed feed records are rejected and are not passed to the callback.
44+
If malformed records are found, parsing continues for the rest of the
45+
available feed data before
46+
.I auparse_feed
47+
returns an error.
4148

4249
.SH "RETURN VALUE"
4350

4451
Returns \-1 if an error occurs; otherwise, 0 for success.
52+
When \-1 is returned,
53+
.I errno
54+
is set to indicate the error. The value of
55+
.I errno
56+
is meaningful only after an error return; callers should not assume it is
57+
cleared after a successful call.
58+
Applications that may run with libauparse from before audit-4.1.5 should set
59+
.I errno
60+
to 0 before calling
61+
.I auparse_feed.
62+
If
63+
.I auparse_feed
64+
returns \-1 and
65+
.I errno
66+
is still 0, the runtime library is using the older error behavior and did not
67+
set
68+
.I errno.
69+
Clearing
70+
.I errno
71+
first avoids treating a pre-existing value as if it were set by
72+
.I auparse_feed.
73+
74+
.SH "ERRORS"
75+
76+
.TP
77+
.B EINVAL
78+
The parser state is invalid, the parser was not initialized with
79+
.B AUSOURCE_FEED,
80+
the input arguments are invalid, or no callback has been registered.
81+
82+
.TP
83+
.B ENOMEM
84+
Memory allocation failed while buffering or parsing feed data.
85+
86+
.TP
87+
.B EBADMSG
88+
A malformed feed record was found. The malformed record was rejected and was
89+
not passed to the callback. Parsing continues for the rest of the available
90+
feed data before this error is returned.
91+
92+
.TP
93+
.B EIO
94+
An internal parser failure occurred without a more specific error code.
4595

4696
.SH "EXAMPLE"
4797
.nf

docs/auparse_feed_age_events.3

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ auparse_feed_age_events \- check events for complete based on time.
99
.SH "DESCRIPTION"
1010

1111
.I auparse_feed_age_events
12-
should be called to see if any events are complete based on the current clock time. Any newly complete events will be passed to the callback function.
12+
should be called to see if any events are complete based on the current clock time.
13+
.I auparse_add_callback()
14+
must be called before aging feed events so that complete events have a consumer.
15+
Any newly complete events will be passed to the callback function.
1316

1417
.SH "RETURN VALUE"
1518

0 commit comments

Comments
 (0)