Skip to content

Commit 8f39651

Browse files
committed
Implement parsing single-quoted key/value fields
This is common with various message types that originate in user-space (SERVICE_START, USER_LOGIN, etc.)
1 parent 5fbad3b commit 8f39651

30 files changed

+264
-90
lines changed

src/key.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub enum Common {
2323
Item,
2424
Items,
2525
Key,
26+
Msg,
2627
Mode,
2728
Name,
2829
Nametype,
@@ -51,6 +52,7 @@ const COMMON: &[(&str, Common)] = &[
5152
("item", Common::Item),
5253
("items", Common::Items),
5354
("key", Common::Key),
55+
("msg", Common::Msg),
5456
("mode", Common::Mode),
5557
("name", Common::Name),
5658
("nametype", Common::Nametype),

src/parser.rs

Lines changed: 148 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,18 @@ use crate::*;
1616
/// Parser for Linux Audit messages, with a few configurable options
1717
#[derive(Debug)]
1818
pub struct Parser {
19-
/// Process enriched (i.e. ALL-CAPS keys)
19+
/// Process enriched (i.e. ALL-CAPS keys). Default: true
2020
pub enriched: bool,
21+
/// Try to process common msg='…' strings into key/value maps. Default: true
22+
pub split_msg: bool,
2123
}
2224

2325
impl Default for Parser {
2426
fn default() -> Self {
25-
Self { enriched: true }
27+
Self {
28+
enriched: true,
29+
split_msg: true,
30+
}
2631
}
2732
}
2833

@@ -53,6 +58,9 @@ pub enum ParseError {
5358
/// produce `log_format=ENRICHED` logs, i.e. to resolve `uid`, `gid`,
5459
/// `syscall`, `arch`, `sockaddr` fields, those resolved values are
5560
/// dropped by the parser.
61+
///
62+
/// To maintain compatibility, `parse` does not attempt to process
63+
/// single-quoted `msg='…'` strings into key/value maps.
5664
pub fn parse<'a>(raw: &[u8], skip_enriched: bool) -> Result<Message<'a>, ParseError> {
5765
Parser {
5866
enriched: !skip_enriched,
@@ -135,7 +143,7 @@ impl Parser {
135143

136144
let (input, mut kv) = if !self.enriched {
137145
terminated(
138-
separated_list0(tag(b" "), |input| parse_kv(input, ty)),
146+
separated_list0(tag(b" "), |input| self.parse_kv(input, ty)),
139147
alt((
140148
value((), tuple((tag("\x1d"), is_not("\n"), tag("\n")))),
141149
value((), tag("\n")),
@@ -144,7 +152,7 @@ impl Parser {
144152
} else {
145153
terminated(
146154
separated_list0(take_while1(|c| c == b' ' || c == b'\x1d'), |input| {
147-
parse_kv(input, ty)
155+
self.parse_kv(input, ty)
148156
}),
149157
newline,
150158
)(input)?
@@ -156,6 +164,101 @@ impl Parser {
156164

157165
Ok((input, kv))
158166
}
167+
168+
/// Recognize one key/value pair
169+
#[inline(always)]
170+
fn parse_kv<'a>(&'a self, input: &'a [u8], ty: MessageType) -> IResult<&'a [u8], (Key, Value)> {
171+
let (input, key) = match ty {
172+
// Special case for execve arguments: aX, aX[Y], aX_len
173+
MessageType::EXECVE
174+
if !input.is_empty() && input[0] == b'a' && !input.starts_with(b"argc") =>
175+
{
176+
terminated(
177+
alt((parse_key_a_x_len, parse_key_a_xy, parse_key_a_x)),
178+
tag("="),
179+
)(input)
180+
}
181+
// Special case for syscall params: aX
182+
MessageType::SYSCALL => terminated(alt((parse_key_a_x, parse_key)), tag("="))(input),
183+
_ => terminated(parse_key, tag("="))(input),
184+
}?;
185+
186+
let (input, value) = match (ty, &key) {
187+
(MessageType::SYSCALL, Key::Arg(_, None)) => map(
188+
recognize(terminated(
189+
many1_count(take_while1(is_hex_digit)),
190+
peek(take_while1(is_sep)),
191+
)),
192+
|s| {
193+
let ps = unsafe { str::from_utf8_unchecked(s) };
194+
match u64::from_str_radix(ps, 16) {
195+
Ok(n) => Value::Number(Number::Hex(n)),
196+
Err(_) => Value::Str(s, Quote::None),
197+
}
198+
},
199+
)(input)?,
200+
(MessageType::SYSCALL, Key::Common(c)) => self.parse_common(input, ty, *c)?,
201+
(MessageType::EXECVE, Key::Arg(_, _)) => parse_encoded(input)?,
202+
(MessageType::EXECVE, Key::ArgLen(_)) => parse_dec(input)?,
203+
(_, Key::Name(name)) => parse_named(input, ty, name)?,
204+
(_, Key::Common(c)) => self.parse_common(input, ty, *c)?,
205+
(_, Key::NameUID(name)) | (_, Key::NameGID(name)) => {
206+
alt((parse_dec, |input| parse_unspec_value(input, ty, name)))(input)?
207+
}
208+
_ => parse_encoded(input)?,
209+
};
210+
211+
Ok((input, (key, value)))
212+
}
213+
214+
#[inline(always)]
215+
fn parse_common<'a>(
216+
&'a self,
217+
input: &'a [u8],
218+
ty: MessageType,
219+
c: Common,
220+
) -> IResult<&'a [u8], Value> {
221+
let name = <&str>::from(c).as_bytes();
222+
match c {
223+
Common::Arch | Common::CapFi | Common::CapFp | Common::CapFver => {
224+
alt((parse_hex, |input| parse_unspec_value(input, ty, name)))(input)
225+
}
226+
Common::Argc
227+
| Common::Exit
228+
| Common::CapFe
229+
| Common::Inode
230+
| Common::Item
231+
| Common::Items
232+
| Common::Pid
233+
| Common::PPid
234+
| Common::Ses
235+
| Common::Syscall => {
236+
alt((parse_dec, |input| parse_unspec_value(input, ty, name)))(input)
237+
}
238+
Common::Success
239+
| Common::Cwd
240+
| Common::Dev
241+
| Common::Tty
242+
| Common::Comm
243+
| Common::Exe
244+
| Common::Name
245+
| Common::Nametype
246+
| Common::Subj
247+
| Common::Key => {
248+
alt((parse_encoded, |input| parse_unspec_value(input, ty, name)))(input)
249+
}
250+
Common::Mode => alt((parse_oct, |input| parse_unspec_value(input, ty, name)))(input),
251+
Common::Msg => {
252+
if self.split_msg {
253+
alt((parse_kv_sq_as_map, |input| {
254+
parse_unspec_value(input, ty, name)
255+
}))(input)
256+
} else {
257+
alt((parse_encoded, |input| parse_unspec_value(input, ty, name)))(input)
258+
}
259+
}
260+
}
261+
}
159262
}
160263

161264
/// Recognize the header: node, type, event identifier
@@ -211,52 +314,6 @@ fn parse_msgid(input: &[u8]) -> IResult<&[u8], EventID> {
211314
)(input)
212315
}
213316

214-
/// Recognize one key/value pair
215-
#[inline(always)]
216-
fn parse_kv(input: &[u8], ty: MessageType) -> IResult<&[u8], (Key, Value)> {
217-
let (input, key) = match ty {
218-
// Special case for execve arguments: aX, aX[Y], aX_len
219-
MessageType::EXECVE
220-
if !input.is_empty() && input[0] == b'a' && !input.starts_with(b"argc") =>
221-
{
222-
terminated(
223-
alt((parse_key_a_x_len, parse_key_a_xy, parse_key_a_x)),
224-
tag("="),
225-
)(input)
226-
}
227-
// Special case for syscall params: aX
228-
MessageType::SYSCALL => terminated(alt((parse_key_a_x, parse_key)), tag("="))(input),
229-
_ => terminated(parse_key, tag("="))(input),
230-
}?;
231-
232-
let (input, value) = match (ty, &key) {
233-
(MessageType::SYSCALL, Key::Arg(_, None)) => map(
234-
recognize(terminated(
235-
many1_count(take_while1(is_hex_digit)),
236-
peek(take_while1(is_sep)),
237-
)),
238-
|s| {
239-
let ps = unsafe { str::from_utf8_unchecked(s) };
240-
match u64::from_str_radix(ps, 16) {
241-
Ok(n) => Value::Number(Number::Hex(n)),
242-
Err(_) => Value::Str(s, Quote::None),
243-
}
244-
},
245-
)(input)?,
246-
(MessageType::SYSCALL, Key::Common(c)) => parse_common(input, ty, *c)?,
247-
(MessageType::EXECVE, Key::Arg(_, _)) => parse_encoded(input)?,
248-
(MessageType::EXECVE, Key::ArgLen(_)) => parse_dec(input)?,
249-
(_, Key::Name(name)) => parse_named(input, ty, name)?,
250-
(_, Key::Common(c)) => parse_common(input, ty, *c)?,
251-
(_, Key::NameUID(name)) | (_, Key::NameGID(name)) => {
252-
alt((parse_dec, |input| parse_unspec_value(input, ty, name)))(input)?
253-
}
254-
_ => parse_encoded(input)?,
255-
};
256-
257-
Ok((input, (key, value)))
258-
}
259-
260317
#[inline(always)]
261318
fn parse_named<'a>(input: &'a [u8], ty: MessageType, name: &[u8]) -> IResult<&'a [u8], Value<'a>> {
262319
match FIELD_TYPES.get(name) {
@@ -277,37 +334,6 @@ fn parse_named<'a>(input: &'a [u8], ty: MessageType, name: &[u8]) -> IResult<&'a
277334
}
278335
}
279336

280-
#[inline(always)]
281-
fn parse_common(input: &[u8], ty: MessageType, c: Common) -> IResult<&[u8], Value> {
282-
let name = <&str>::from(c).as_bytes();
283-
match c {
284-
Common::Arch | Common::CapFi | Common::CapFp | Common::CapFver => {
285-
alt((parse_hex, |input| parse_unspec_value(input, ty, name)))(input)
286-
}
287-
Common::Argc
288-
| Common::Exit
289-
| Common::CapFe
290-
| Common::Inode
291-
| Common::Item
292-
| Common::Items
293-
| Common::Pid
294-
| Common::PPid
295-
| Common::Ses
296-
| Common::Syscall => alt((parse_dec, |input| parse_unspec_value(input, ty, name)))(input),
297-
Common::Success
298-
| Common::Cwd
299-
| Common::Dev
300-
| Common::Tty
301-
| Common::Comm
302-
| Common::Exe
303-
| Common::Name
304-
| Common::Nametype
305-
| Common::Subj
306-
| Common::Key => alt((parse_encoded, |input| parse_unspec_value(input, ty, name)))(input),
307-
Common::Mode => alt((parse_oct, |input| parse_unspec_value(input, ty, name)))(input),
308-
}
309-
}
310-
311337
/// Recognize encoded value:
312338
///
313339
/// May be double-quoted string, hex-encoded blob, (null), ?.
@@ -447,6 +473,20 @@ fn parse_str_unq_inside_sq(input: &[u8]) -> IResult<&[u8], &[u8]> {
447473
take_while(|c| is_safe_chr(c) && c != b'\'')(input)
448474
}
449475

476+
#[inline(always)]
477+
fn parse_str_words_inside_sq(input: &[u8]) -> IResult<&[u8], &[u8]> {
478+
let mut rest = input;
479+
loop {
480+
(rest, _) = take_while(|c| !b"' ".contains(&c))(rest)?;
481+
if let Ok(_) = alt((recognize(tuple((space1, parse_key, tag("=")))), tag("'")))(rest) {
482+
break;
483+
}
484+
(rest, _) = space1(rest)?;
485+
}
486+
let l = input.len() - rest.len();
487+
Ok((rest, &input[..l]))
488+
}
489+
450490
/// More "correct" variant of parse_str_sq
451491
#[inline(always)]
452492
fn parse_kv_sq(input: &[u8]) -> IResult<&[u8], &[u8]> {
@@ -464,6 +504,33 @@ fn parse_kv_sq(input: &[u8]) -> IResult<&[u8], &[u8]> {
464504
)(input)
465505
}
466506

507+
/// Recognize a map enclosed in single quotes
508+
#[inline(always)]
509+
fn parse_kv_sq_as_map(input: &[u8]) -> IResult<&[u8], Value> {
510+
map(
511+
delimited(
512+
tag("'"),
513+
separated_list0(
514+
space1,
515+
alt((separated_pair(
516+
parse_key,
517+
alt((
518+
tag("="),
519+
recognize(tuple((tag(":"), space0))), // for 'avc: mumble mumble mumble …'
520+
)),
521+
alt((
522+
parse_encoded,
523+
map(parse_str_words_inside_sq, |v| Value::Str(v, Quote::None)),
524+
map(parse_str_unq_inside_sq, |v| Value::Str(v, Quote::None)),
525+
)),
526+
),)),
527+
),
528+
tag("'"),
529+
),
530+
Value::Map,
531+
)(input)
532+
}
533+
467534
/// More "correct" variant of parse_str_braced
468535
#[inline(always)]
469536
fn parse_kv_braced(input: &[u8]) -> IResult<&[u8], &[u8]> {

0 commit comments

Comments
 (0)