linux-audit-parser-0.2.7/.cargo_vcs_info.json0000644000000001360000000000100145770ustar { "git": { "sha1": "66653c7e21bf5a9e167c0cea93a6563dd048b5b9" }, "path_in_vcs": "" }linux-audit-parser-0.2.7/.github/workflows/rust.yml000064400000000000000000000033461046102023000205120ustar 00000000000000# This workflow runs whenever a PR is opened or updated, or a commit is pushed to main. It runs # several checks: # - fmt: checks that the code is formatted according to rustfmt # - clippy: checks that the code does not contain any clippy warnin # - test: runs the tests # This configuration allows maintainers of this repo to create a branch and pull request based on # the new branch. Restricting the push trigger to the main branch ensures that the PR only gets # built once. on: push: branches: [master] pull_request: paths: - "src/**" - "build.rs" - "Cargo.toml" - "Cargo.lock" - ".github/workflows/rust.yml" # If new code is pushed to a PR branch, then cancel in progress workflows for that PR. Ensures that # we don't waste CI time, and returns results quicker https://github.com/jonhoo/rust-ci-conf/pull/5 concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true name: rust jobs: fmt: runs-on: ubuntu-latest name: format steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 - name: Run rustfmt run: cargo fmt --check clippy: runs-on: ubuntu-latest name: clippy steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 - name: Run clippy run: cargo clippy --tests -- -Dwarnings test: runs-on: ubuntu-latest name: test steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 - name: Run tests run: cargo test build: runs-on: ubuntu-latest name: build steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 - name: Run release build run: cargo build --release linux-audit-parser-0.2.7/.gitignore000064400000000000000000000000101046102023000153460ustar 00000000000000/target linux-audit-parser-0.2.7/Cargo.lock0000644000000137750000000000100125670ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "darling" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", ] [[package]] name = "darling_core" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", "syn", ] [[package]] name = "darling_macro" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", "syn", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "linux-audit-parser" version = "0.2.7" dependencies = [ "indexmap", "lazy_static", "nom", "serde", "serde_test", "serde_with", "thiserror", "tinyvec", ] [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] [[package]] name = "proc-macro2" version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "serde" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_test" version = "1.0.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f901ee573cab6b3060453d2d5f0bae4e6d628c23c0a962ff9b5f1d7c8d4f1ed" dependencies = [ "serde", ] [[package]] name = "serde_with" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" dependencies = [ "serde", "serde_derive", "serde_with_macros", ] [[package]] name = "serde_with_macros" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" dependencies = [ "darling", "proc-macro2", "quote", "syn", ] [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "thiserror" version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tinyvec" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "unicode-ident" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" linux-audit-parser-0.2.7/Cargo.toml0000644000000027510000000000100126020ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "linux-audit-parser" version = "0.2.7" authors = ["Hilko Bengen "] build = "build.rs" autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Parser for Linxu Audit logs" readme = "README.md" keywords = [ "linux", "audit", "auditd", "parser", ] license = "LGPL-3.0-or-later" repository = "https://github.com/hillu/linux-audit-parser-rs" [features] default = ["serde"] serde = [ "dep:serde", "dep:serde_with", ] [lib] name = "linux_audit_parser" path = "src/lib.rs" [dependencies.indexmap] version = "2" [dependencies.lazy_static] version = "1.5.0" [dependencies.nom] version = "7.1.3" [dependencies.serde] version = "1.0.209" optional = true [dependencies.serde_with] version = "3.12.0" features = ["macros"] optional = true default-features = false [dependencies.thiserror] version = ">= 1" [dependencies.tinyvec] version = "1.6" features = ["alloc"] [dev-dependencies.serde_test] version = "1.0.177" linux-audit-parser-0.2.7/Cargo.toml.orig000064400000000000000000000013141046102023000162550ustar 00000000000000[package] name = "linux-audit-parser" description = "Parser for Linxu Audit logs" repository = "https://github.com/hillu/linux-audit-parser-rs" version = "0.2.7" edition = "2021" license = "LGPL-3.0-or-later" authors = ["Hilko Bengen "] keywords = ["linux", "audit", "auditd", "parser"] [dependencies] indexmap = "2" lazy_static = "1.5.0" nom = "7.1.3" serde = { version = "1.0.209", optional = true } serde_with = { version = "3.12.0", default-features = false, features = ["macros"], optional = true } thiserror = ">= 1" tinyvec = { version = "1.6", features = ["alloc"] } [features] serde = ["dep:serde", "dep:serde_with"] default = ["serde"] [dev-dependencies] serde_test = "1.0.177" linux-audit-parser-0.2.7/LICENSE000064400000000000000000000167441046102023000144100ustar 00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. linux-audit-parser-0.2.7/README.md000064400000000000000000000005441046102023000146510ustar 00000000000000# Parser for Linux Audit (_"auditd"_) logs This crate provides functionality and data structures to efficiently parse Linux Audit files. It is heavily based on the parser used in [Laurel](https://github.com/threathunters-io/laurel). ## License GNU Lesser General Public License, version 3.0 or later ## Author Hilko Bengen <> linux-audit-parser-0.2.7/build.rs000064400000000000000000000066301046102023000150410ustar 00000000000000use std::env; use std::fs; use std::io::prelude::*; use std::io::BufReader; use std::iter::FromIterator; use std::path::Path; use std::string::String; fn main() -> Result<(), Box> { let out_dir = env::var_os("OUT_DIR").unwrap(); let field_def_file = "src/audit-specs/fields/field-dictionary.csv"; let msgtype_def_file = "src/audit-specs/messages/message-dictionary.csv"; let const_file = Path::new(&out_dir).join("const.rs"); let msgtype_file = Path::new(&out_dir).join("message_type_impl.rs"); let constants: Vec<(String, String)> = BufReader::new(fs::File::open(msgtype_def_file)?) .lines() .skip(1) // skip over header .map(|line| { line.unwrap() .split(',') .map(|x| x.to_string()) .collect::>() }) .map(|fields| { ( fields[0].strip_prefix("AUDIT_").unwrap().to_string(), fields[1].clone(), ) }) .collect(); let fields: Vec<(String, String)> = BufReader::new(fs::File::open(field_def_file)?) .lines() .skip(3) // skip over heder and regex describing a* mess .map(|line| { line.unwrap() .split(',') .map(|x| x.to_string()) .collect::>() }) .map(|fields| (fields[0].clone(), fields[1].clone())) .collect(); let mut template = Vec::new(); fs::File::open("src/const.rs.in")?.read_to_end(&mut template)?; let template = String::from_utf8(template)?; let buf = template .replace( "/* @EVENT_CONST@ */", &String::from_iter( constants .iter() .map(|(name, value)| format!(r#"("{name}", {value}), "#)), ), ) .replace( "/* @FIELD_TYPES@ */", &String::from_iter( fields .iter() .filter(|(_, typ)| typ == "encoded" || typ.starts_with("numeric")) .map(|(name, typ)| match typ.as_str() { "numeric hexadecimal" => format!(r#"("{name}", FieldType::NumericHex),"#), "numeric decimal" => format!(r#"("{name}", FieldType::NumericDec),"#), "numeric octal" => format!(r#"("{name}", FieldType::NumericOct),"#), "numeric" => format!(r#"("{name}", FieldType::Numeric),"#), "encoded" => format!(r#"("{name}", FieldType::Encoded),"#), _ => format!(r#"("{name}", FieldType::Invalid),"#), }), ), ) .into_bytes(); fs::write(const_file, buf)?; let mut template = Vec::new(); fs::File::open("src/message_type_impl.rs.in")?.read_to_end(&mut template)?; let template = String::from_utf8(template)?; let buf = template.replace( "/* @MSG_TYPE_CONST@ */", &String::from_iter( constants .iter() .map(|(name, value)| format!("pub const {name}: Self = Self({value});\n",)), ), ); fs::write(msgtype_file, buf)?; println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=const.rs.in"); println!("cargo:rerun-if-changed={msgtype_def_file}"); println!("cargo:rerun-if-changed={field_def_file}"); Ok(()) } linux-audit-parser-0.2.7/src/audit-specs/README.md000064400000000000000000000002601046102023000176540ustar 00000000000000The tables in the _fields_ and _messages_ subdirectories have been copied from the [Linux-audit documentation](https://github.com/linux-audit/audit-documentation) repository. linux-audit-parser-0.2.7/src/audit-specs/fields/field-dictionary.csv000064400000000000000000000267011046102023000236160ustar 00000000000000NAME,FORMAT,MEANING,EXCEPTION a[0-3],numeric hexadecimal,the arguments to a syscall,syscall a[[:digit:]+]\[.*\],encoded,the arguments to the execve syscall,execve acct,encoded,a user's account name, acl,alphabet,access mode of resource assigned to vm, action,numeric,netfilter packet disposition, added,numeric,number of new files detected, addr,encoded,the remote address that the user is connecting from, apparmor,encoded,apparmor event information, arch,numeric hexadecimal,the elf architecture flags, argc,numeric decimal,the number of arguments to an execve syscall, audit_backlog_limit,numeric decimal,audit system's backlog queue size, audit_backlog_wait_time,numeric decimal,audit system's backlog wait time, audit_enabled,numeric decimal,audit systems's enable/disable status, audit_failure,numeric decimal,audit system's failure mode, auid,numeric decimal,login user ID, banners,alphanumeric,banners used on printed page, bool,alphanumeric,name of SELinux boolean, bus,alphanumeric,name of subsystem bus a vm resource belongs to, capability,numeric decimal,posix capabilities, cap_fe,numeric decimal,file assigned effective capability map, cap_fi,numeric hexadecimal,file inherited capability map, cap_fp,numeric hexadecimal,file permitted capability map, cap_fver,numeric hexadecimal,file system capabilities version number, cap_pa,numeric hexadecimal,process ambient capability map, cap_pe,numeric hexadecimal,process effective capability map, cap_pi,numeric hexadecimal,process inherited capability map, cap_pp,numeric hexadecimal,process permitted capability map, category,alphabet,resource category assigned to vm, cgroup,encoded,path to cgroup in sysfs, changed,numeric decimal,number of changed files, cipher,alphanumeric,name of crypto cipher selected, class,alphabet,resource class assigned to vm, cmd,encoded,command being executed, code,numeric hexadecimal,seccomp action code, comm,encoded,command line program name, compat,numeric decimal,is_compat_task result, cwd,encoded,the current working directory, daddr,alphanumeric,remote IP address, data,encoded,TTY text, default-context,alphanumeric,default MAC context, dev,numeric,in path records,major and minor for device dev,alphanumeric,device name as found in /dev,avc device,encoded,device name, dir,encoded,directory name, direction,alphanumeric,direction of crypto operation, dmac,numeric,remote MAC address, dport,numeric decimal,remote port number, egid,numeric decimal,effective group ID, enforcing,numeric decimal,new MAC enforcement status, entries,numeric decimal,number of entries in the netfilter table, errno,numeric decimal,error code of the audited operation, euid,numeric decimal,effective user ID, exe,encoded,executable name, exit,numeric decimal,syscall exit code, fam,alphanumeric,socket address family, family,numeric decimal,netfilter protocol, fd,numeric decimal,file descriptor number, file,encoded,file name, flags,numeric hexadecimal,mmap syscall flags, fe,numeric decimal,file assigned effective capability map, feature,alphanumeric,kernel feature being changed, fi,numeric hexadecimal,file assigned inherited capability map, fp,numeric hexadecimal,file assigned permitted capability map, fp,alphanumeric,crypto key finger print,crypto_key format,alphanumeric,audit log's format, fsgid,numeric decimal,file system group ID, fsuid,numeric decimal,file system user ID, fver,numeric hexadecimal,file system capabilities version number, gid,numeric decimal,group ID, grantors,alphanumeric,pam modules approving the action, grp,encoded,group name, hook,numeric,netfilter hook that packet came from, hostname,alphanumeric,the hostname that the user is connecting from, icmp_type,numeric,type of icmp message, id,numeric,during account changes,the user ID of the account igid,numeric decimal,ipc object's group ID, img-ctx,alphanumeric,the vm's disk image context string, inif,numeric,in interface number, ip,alphanumeric,network address of a printer, ipid,numeric decimal,IP datagram fragment identifier, ino,numeric decimal,inode number, inode,numeric decimal,inode number, inode_gid,numeric decimal,group ID of the inode's owner, inode_uid,numeric decimal,user ID of the inode's owner, invalid_context,encoded,SELinux context, ioctlcmd,numeric hexadecimal,The request argument to the ioctl syscall, ipx-net,numeric,IPX network number, item,numeric decimal,which item is being recorded, items,numeric decimal,the number of path records in the event, iuid,numeric decimal,ipc object's user ID, kernel,alphanumeric,kernel's version number, key,encoded,key assigned from triggered audit rule, kind,alphabet,server or client in crypto operation, ksize,numeric,key size for crypto operation, laddr,alphanumeric,local network address, len,numeric decimal,length, lport,numeric decimal,local network port, list,numeric decimal,the audit system's filter list number, mac,alphanumeric,crypto MAC algorithm selected, macproto,numeric,ethernet packet type ID field, maj,numeric,device major number, major,numeric decimal,device major number, minor,numeric decimal,device minor number, mode,numeric octal,mode flags on a file, model,alphanumeric,security model being used for virt, msg,alphanumeric,the payload of the audit record, nargs,numeric decimal,the number of arguments to a socket call, name,encoded,file name in avcs, nametype,alphabet,kind of file operation being referenced, net,alphanumeric,network MAC address, new,numeric,value being set in feature, new-chardev,encoded,new character device being assigned to vm, new-disk,encoded,disk being added to vm, new-enabled,numeric decimal,new TTY audit enabled setting, new-fs,encoded,file system being added to vm, new_gid,numeric decimal,new group ID being assigned, new-level,alphanumeric,new run level, new_lock,numeric decimal,new value of feature lock, new-log_passwd,numeric decimal,new value for TTY password logging, new-mem,numeric,new amount of memory in KB, new-net,encoded,MAC address being assigned to vm, new_pe,numeric,new process effective capability map(deprec), new_pi,numeric,new process inherited capability map(deprec), new_pp,numeric,new process permitted capability map(deprec), new-range,alphanumeric,new SELinux range, new-rng,encoded,device name of rng being added from a vm, new-role,alphanumeric,new SELinux role, new-seuser,alphanumeric,new SELinux user, new-vcpu,numeric,new number of CPU cores, nlnk-fam,numeric,netlink protocol number, nlnk-grp,numeric,netlink group number, nlnk-pid,numeric decimal,pid of netlink packet sender, oauid,numeric decimal,object's login user ID, obj,alphanumeric,lspp object context string, obj_gid,numeric decimal,group ID of object, obj_uid,numeric decimal,user ID of object, oflag,numeric,open syscall flags, ogid,numeric decimal,file owner group ID, ocomm,encoded,object's command line name, old,numeric,present value of kernel feature, old,numeric,old value,audit_enabled audit_backlog audit_failure value old-auid,numeric decimal,previous auid value, old-chardev,encoded,present character device assigned to vm, old-disk,encoded,disk being removed from vm, old-enabled,numeric decimal,present TTY audit enabled setting, old_enforcing,numeric decimal,old MAC enforcement status, old-fs,encoded,file system being removed from vm, old-level,alphanumeric,old run level, old_lock,numeric decimal,present value of feature lock, old-log_passwd,numeric decimal,present value for TTY password logging, old-mem,numeric,present amount of memory in KB, old-net,encoded,present MAC address assigned to vm, old_pa,numeric hexadecimal,old process ambient capability map, old_pe,numeric hexadecimal,old process effective capability map, old_pi,numeric hexadecimal,old process inherited capability map, old_pp,numeric hexadecimal,old process permitted capability map, old_prom,numeric decimal,network promiscuity flag, old-range,alphanumeric,present SELinux range, old-rng,encoded,device name of rng being removed from a vm, old-role,alphanumeric,present SELinux role, old-ses,numeric decimal,previous ses value, old-seuser,alphanumeric,present SELinux user, old_val,numeric decimal,current value of SELinux boolean, old-vcpu,numeric,present number of CPU cores, op,alphanumeric,the operation being performed that is audited, opid,numeric decimal,object's process ID, oses,numeric decimal,object's session ID, ouid,numeric decimal,file owner user ID, outif,numeric,out interface number, pa,numeric hexadecimal,process ambient capability map, pe,numeric hexadecimal,process effective capability map, pi,numeric hexadecimal,process inherited capability map, pp,numeric hexadecimal,process permitted capability map, parent,numeric,the inode number of the parent file, path,encoded,file system path name, per,numeric hexadecimal,linux personality, perm,numeric,the file permission being used, perm_mask,numeric,file permission mask that triggered a watch event, permissive,numeric decimal,SELinux is in permissive mode, pfs,alphanumeric,perfect forward secrecy method, pid,numeric decimal,process ID, ppid,numeric decimal,parent process ID, printer,alphanumeric,printer name, prom,numeric decimal,network promiscuity flag, proctitle,encoded,process title and command line parameters, prog-id,numeric decimal,BPF program ID, proto,numeric decimal,network protocol, qbytes,numeric hexadecimal,ipc objects quantity of bytes, range,alphanumeric,user's SE Linux range, rdev,numeric,the device identifier (special files only), reason,alphanumeric,text string denoting a reason for the action, removed,numeric,number of deleted files, res,numeric decimal,result of the audited operation(success/fail), resrc,alphanumeric,resource being assigned, result,alphanumeric,result of the audited operation(success/fail), role,alphanumeric,user's SELinux role, rport,numeric decimal,remote port number, saddr,encoded,struct socket address structure, sauid,numeric decimal,sent login user ID, scontext,alphanumeric,the subject's context string, selected-context,alphanumeric,new MAC context assigned to session, seperm,alphanumeric,SELinux permission being decided on, seqno,numeric decimal,sequence number, seperms,alphabet,SELinux permissions being used, seresult,alphabet,SELinux AVC decision granted/denied, ses,numeric decimal,login session ID, seuser,alphanumeric,user's SE Linux user acct, sgid,numeric decimal,set group ID, sig,numeric decimal,signal number, sigev_signo,numeric decimal,signal number, smac,numeric,local MAC address, spid,numeric decimal,sent process ID, sport,numeric decimal,local port number, state,alphanumeric,audit daemon configuration resulting state, subj,alphanumeric,lspp subject's context string, success,alphanumeric,whether the syscall was successful or not, suid,numeric decimal,sent user ID, syscall,numeric decimal,syscall number in effect when the event occurred, table,alphanumeric,netfilter table name, tclass,alphanumeric,target's object classification, tcontext,alphanumeric,the target's or object's context string, terminal,alphanumeric,terminal name the user is running programs on, tty,alphanumeric,tty udevice the user is running programs on, type,alphanumeric,the audit record's type, uid,numeric decimal,user ID, unit,alphanumeric,systemd unit, uri,alphanumeric,URI pointing to a printer, uring_op,numeric decimal,IO_URING opcode, user,alphanumeric,account submitted for authentication, uuid,alphanumeric,a UUID, val,alphanumeric,generic value associated with the operation, val,numeric decimal,new value of SELinux boolean, ver,numeric,audit daemon's version number, virt,alphanumeric,kind of virtualization being referenced, vm,encoded,virtual machine name, vm-ctx,alphanumeric,the vm's context string, vm-pid,numeric decimal,vm's process ID, watch,encoded,file name in a watch record, linux-audit-parser-0.2.7/src/audit-specs/messages/message-dictionary.csv000064400000000000000000000324361046102023000245220ustar 00000000000000MACRO NAME,VALUE,ORIGIN,CLASS,DESCRIPITON AUDIT_GET,1000,USER,CTL,Get status AUDIT_SET,1001,USER,CTL,Set status (enable/disable/auditd) AUDIT_LIST,1002,USER,DEP,List syscall rules -- deprecated AUDIT_ADD,1003,USER,DEP,Add syscall rule -- deprecated AUDIT_DEL,1004,USER,DEP,Delete syscall rule -- deprecated AUDIT_USER,1005,USER,DEP,Message from userspace -- deprecated AUDIT_LOGIN,1006,KERN,IND,Define the login ID and information AUDIT_WATCH_INS,1007,USER,DEP,Insert file/dir watch entry AUDIT_WATCH_REM,1008,USER,DEP,Remove file/dir watch entry AUDIT_WATCH_LIST,1009,USER,DEP,List all file/dir watches AUDIT_SIGNAL_INFO,1010,USER,CTL,Get info about sender of signal to auditd AUDIT_ADD_RULE,1011,USER,CTL,Add syscall filtering rule AUDIT_DEL_RULE,1012,USER,CTL,Delete syscall filtering rule AUDIT_LIST_RULES,1013,USER,CTL,List syscall filtering rules AUDIT_TRIM,1014,USER,CTL,Trim junk from watched tree AUDIT_MAKE_EQUIV,1015,USER,CTL,Append to watched tree AUDIT_TTY_GET,1016,USER,CTL,Get TTY auditing status AUDIT_TTY_SET,1017,USER,CTL,Set TTY auditing status AUDIT_SET_FEATURE,1018,USER,CTL,Turn an audit feature on or off AUDIT_GET_FEATURE,1019,USER,CTL,Get which features are enabled AUDIT_USER_AUTH,1100,USER,IND,User system access authentication AUDIT_USER_ACCT,1101,USER,IND,User system access authorization AUDIT_USER_MGMT,1102,USER,IND,User account attribute change AUDIT_CRED_ACQ,1103,USER,IND,User credential acquired AUDIT_CRED_DISP,1104,USER,IND,User credential disposed AUDIT_USER_START,1105,USER,IND,User session start AUDIT_USER_END,1106,USER,IND,User session end AUDIT_USER_AVC,1107,USER,IND,User space AVC (Access Vector Cache) message AUDIT_USER_CHAUTHTOK,1108,USER,IND,User account password or PIN changed AUDIT_USER_ERR,1109,USER,IND,User account state error AUDIT_CRED_REFR,1110,USER,IND,User credential refreshed AUDIT_USYS_CONFIG,1111,USER,IND,User space system config change AUDIT_USER_LOGIN,1112,USER,IND,User has logged in AUDIT_USER_LOGOUT,1113,USER,IND,User has logged out AUDIT_ADD_USER,1114,USER,IND,User account added AUDIT_DEL_USER,1115,USER,IND,User account deleted AUDIT_ADD_GROUP,1116,USER,IND,Group account added AUDIT_DEL_GROUP,1117,USER,IND,Group account deleted AUDIT_DAC_CHECK,1118,USER,IND,User space DAC check results AUDIT_CHGRP_ID,1119,USER,IND,User space group ID changed AUDIT_TEST,1120,USER,IND,Used for test success messages AUDIT_TRUSTED_APP,1121,USER,IND,Trusted app msg - freestyle text AUDIT_USER_SELINUX_ERR,1122,USER,IND,SELinux user space error AUDIT_USER_CMD,1123,USER,IND,User shell command and args AUDIT_USER_TTY,1124,USER,IND,Non-ICANON TTY input meaning AUDIT_CHUSER_ID,1125,USER,IND,Changed user ID supplemental data AUDIT_GRP_AUTH,1126,USER,IND,Authentication for group password AUDIT_SYSTEM_BOOT,1127,USER,IND,System boot AUDIT_SYSTEM_SHUTDOWN,1128,USER,IND,System shutdown AUDIT_SYSTEM_RUNLEVEL,1129,USER,IND,System runlevel change AUDIT_SERVICE_START,1130,USER,IND,Service (daemon) start AUDIT_SERVICE_STOP,1131,USER,IND,Service (daemon) stop AUDIT_GRP_MGMT,1132,USER,IND,Group account attribute was modified AUDIT_GRP_CHAUTHTOK,1133,USER,IND,Group account password or PIN changed AUDIT_MAC_CHECK,1134,USER,IND,User space MAC (Mandatory Access Control) decision results AUDIT_ACCT_LOCK,1135,USER,IND,User's account locked by admin AUDIT_ACCT_UNLOCK,1136,USER,IND,User's account unlocked by admin AUDIT_USER_DEVICE,1137,USER,IND,User space hotplug device changes AUDIT_SOFTWARE_UPDATE,1138,USER,IND,Software update event AUDIT_DAEMON_START,1200,USER,IND,Daemon startup record AUDIT_DAEMON_END,1201,USER,IND,Daemon normal stop record AUDIT_DAEMON_ABORT,1202,USER,IND,Daemon error stop record AUDIT_DAEMON_CONFIG,1203,USER,IND,Daemon config change AUDIT_DAEMON_RECONFIG,1204,USER,IND,Auditd should reconfigure AUDIT_DAEMON_ROTATE,1205,USER,IND,Auditd should rotate logs AUDIT_DAEMON_RESUME,1206,USER,IND,Auditd should resume logging AUDIT_DAEMON_ACCEPT,1207,USER,IND,Auditd accepted remote connection AUDIT_DAEMON_CLOSE,1208,USER,IND,Auditd closed remote connection AUDIT_DAEMON_ERR,1209,USER,IND,Auditd internal error AUDIT_SYSCALL,1300,KERN,SC,System call event information AUDIT_FS_WATCH,1301,KERN,DEP,Deprecated AUDIT_PATH,1302,KERN,SC,Filename path information AUDIT_IPC,1303,KERN,SC,System call IPC (Inter-Process Communication) object AUDIT_SOCKETCALL,1304,KERN,SC,System call socketcall arguments AUDIT_CONFIG_CHANGE,1305,KERN,IND,Audit system configuration change AUDIT_SOCKADDR,1306,KERN,SC,System call socket address argument information AUDIT_CWD,1307,KERN,SC,Current working directory AUDIT_EXECVE,1309,KERN,SC,Arguments supplied to the execve system call AUDIT_IPC_SET_PERM,1311,KERN,SC,IPC new permissions record type AUDIT_MQ_OPEN,1312,KERN,SC,POSIX MQ open record type AUDIT_MQ_SENDRECV,1313,KERN,SC,POSIX MQ send/receive record type AUDIT_MQ_NOTIFY,1314,KERN,SC,POSIX MQ notify record type AUDIT_MQ_GETSETATTR,1315,KERN,SC,POSIX MQ get/set attribute record type AUDIT_KERNEL_OTHER,1316,KERN,IND,For use by 3rd party modules AUDIT_FD_PAIR,1317,KERN,SC,Information for pipe and socketpair system calls AUDIT_OBJ_PID,1318,KERN,SC,ptrace target AUDIT_TTY,1319,KERN,IND,Input on an administrative TTY AUDIT_EOE,1320,KERN,CTL,End of multi-record event AUDIT_BPRM_FCAPS,1321,KERN,SC,Information about file system capabilities increasing permissions AUDIT_CAPSET,1322,KERN,SC,Record showing argument to sys_capset setting process-based capabilities AUDIT_MMAP,1323,KERN,SC,Mmap system call file descriptor and flags AUDIT_NETFILTER_PKT,1324,KERN,IND,Packets traversing netfilter chains AUDIT_NETFILTER_CFG,1325,KERN,IND/SC,Netfilter chain modifications AUDIT_SECCOMP,1326,KERN,IND,Secure Computing event AUDIT_PROCTITLE,1327,KERN,SC,Process Title info AUDIT_FEATURE_CHANGE,1328,KERN,IND,Audit feature changed value AUDIT_REPLACE,1329,KERN,CTL,Replace auditd if this probe unanswerd AUDIT_KERN_MODULE,1330,KERN,SC,Kernel Module events AUDIT_FANOTIFY,1331,KERN,SC,Fanotify access decision AUDIT_TIME_INJOFFSET,1332,KERN,SC,Timekeeping offset injected AUDIT_TIME_ADJNTPVAL,1333,KERN,SC,NTP value adjustment AUDIT_BPF,1334,KERN,SC,BPF load/unload AUDIT_EVENT_LISTENER,1335,KERN,SC,audit mcast sock join/part AUDIT_URINGOP,1336,KERN,SC,io_uring operation AUDIT_OPENAT2,1337,KERN,SC,Record showing openat2 how args AUDIT_DM_CTRL,1338,KERN,SC,Device Mapper target control AUDIT_DM_EVENT,1339,KERN,SC,Device Mapper events AUDIT_AVC,1400,KERN,SC,SELinux AVC (Access Vector Cache) denial or grant AUDIT_SELINUX_ERR,1401,KERN,SC,Internal SELinux errors AUDIT_AVC_PATH,1402,KERN,SC,"dentry, vfsmount pair from AVC" AUDIT_MAC_POLICY_LOAD,1403,KERN,SC,SELinux Policy file load AUDIT_MAC_STATUS,1404,KERN,SC,"SELinux mode (enforcing, permissive, off) changed" AUDIT_MAC_CONFIG_CHANGE,1405,KERN,SC,SELinux Boolean value modification AUDIT_MAC_UNLBL_ALLOW,1406,KERN,SC,NetLabel: allow unlabeled traffic AUDIT_MAC_CIPSOV4_ADD,1407,KERN,SC,NetLabel: add CIPSOv4 (Commercial Internet Protocol Security Option) DOI (Domain of Interpretation) entry AUDIT_MAC_CIPSOV4_DEL,1408,KERN,SC,NetLabel: del CIPSOv4 (Commercial Internet Protocol Security Option) DOI (Domain of Interpretation) entry AUDIT_MAC_MAP_ADD,1409,KERN,SC,NetLabel: add LSM (Linux Security Module) domain mapping AUDIT_MAC_MAP_DEL,1410,KERN,SC,NetLabel: del LSM (Linux Security Module) domain mapping AUDIT_MAC_IPSEC_ADDSA,1411,KERN,DEP,Not used AUDIT_MAC_IPSEC_DELSA,1412,KERN,DEP,Not used AUDIT_MAC_IPSEC_ADDSPD,1413,KERN,DEP,Not used AUDIT_MAC_IPSEC_DELSPD,1414,KERN,DEP,Not used AUDIT_MAC_IPSEC_EVENT,1415,KERN,SC,Audit an IPsec event AUDIT_MAC_UNLBL_STCADD,1416,KERN,SC,NetLabel: add a static label AUDIT_MAC_UNLBL_STCDEL,1417,KERN,SC,NetLabel: del a static label AUDIT_MAC_CALIPSO_ADD,1418,KERN,SC,NetLabel: add CALIPSO DOI (Domain of Interpretation) entry AUDIT_MAC_CALIPSO_DEL,1419,KERN,SC,NetLabel: delete CALIPSO DOI (Domain of Interpretation) entry AUDIT_MAC_TASK_CONTEXTS,1420,KERN,SC,Multiple LSM contexts AUDIT_MAC_OBJ_CONTEXTS,1421,KERN,SC,Multiple LSM object contexts AUDIT_AA,1500,KERN,?, AUDIT_APPARMOR_AUDIT,1501,KERN,SC, AUDIT_APPARMOR_ALLOWED,1502,KERN,SC, AUDIT_APPARMOR_DENIED,1503,KERN,SC, AUDIT_APPARMOR_HINT,1504,KERN,SC, AUDIT_APPARMOR_STATUS,1505,KERN,SC, AUDIT_APPARMOR_ERROR,1506,KERN,SC, AUDIT_APPARMOR_KILL,1507,KERN,SC, AUDIT_ANOM_PROMISCUOUS,1700,KERN,SC/IND,Device changed promiscuous mode AUDIT_ANOM_ABEND,1701,KERN,IND,Process ended abnormally AUDIT_ANOM_LINK,1702,KERN,SC?,Suspicious use of file links AUDIT_ANOM_CREAT,1703,KERN,SC?,Suspicious file creation AUDIT_INTEGRITY_DATA,1800,KERN,SC,Data integrity verification AUDIT_INTEGRITY_METADATA,1801,KERN,SC,Metadata integrity verification AUDIT_INTEGRITY_STATUS,1802,KERN,SC,Integrity enable status AUDIT_INTEGRITY_HASH,1803,KERN,SC,Integrity HASH type AUDIT_INTEGRITY_PCR,1804,KERN,SC,PCR (Platform Configuration Register) invalidation messages AUDIT_INTEGRITY_RULE,1805,KERN,SC/IND,Integrity Policy action AUDIT_INTEGRITY_EVM_XATTR,1806,KERN,SC,EVM XATTRS modifications AUDIT_INTEGRITY_POLICY_RULE,1807,KERN,SC,Integrity Policy rule AUDIT_KERNEL,2000,KERN,IND,Kernel audit status AUDIT_ANOM_LOGIN_FAILURES,2100,USER,IND,Failed login limit reached AUDIT_ANOM_LOGIN_TIME,2101,USER,IND,Login attempted at bad time AUDIT_ANOM_LOGIN_SESSIONS,2102,USER,IND,Maximum concurrent sessions reached AUDIT_ANOM_LOGIN_ACCT,2103,USER,IND,Login attempted to watched account AUDIT_ANOM_LOGIN_LOCATION,2104,USER,IND,Login from forbidden location AUDIT_ANOM_MAX_DAC,2105,USER,IND,Max DAC (Discretionary Access Control) failures reached AUDIT_ANOM_MAX_MAC,2106,USER,IND,Max MAC (Mandatory Access Control) failures reached AUDIT_ANOM_AMTU_FAIL,2107,USER,IND,AMTU (Abstract Machine Test Utility) failure AUDIT_ANOM_RBAC_FAIL,2108,USER,IND,RBAC (Role-Based Access Control) self test failure AUDIT_ANOM_RBAC_INTEGRITY_FAIL,2109,USER,IND,RBAC (Role-Based Access Control) file integrity test failure AUDIT_ANOM_CRYPTO_FAIL,2110,USER,IND,Crypto system test failure AUDIT_ANOM_ACCESS_FS,2111,USER,IND,Access of file or directory ended abnormally AUDIT_ANOM_EXEC,2112,USER,IND,Execution of file ended abnormally AUDIT_ANOM_MK_EXEC,2113,USER,IND,Make an executable AUDIT_ANOM_ADD_ACCT,2114,USER,IND,Adding a user account ended abnormally AUDIT_ANOM_DEL_ACCT,2115,USER,IND,Deleting a user account ended abnormally AUDIT_ANOM_MOD_ACCT,2116,USER,IND,Changing an account ended abnormally AUDIT_ANOM_ROOT_TRANS,2117,USER,IND,User became root AUDIT_ANOM_LOGIN_SERVICE,2118,USER,IND,Service acct attempted login AUDIT_RESP_ANOMALY,2200,USER,IND,Anomaly not reacted to AUDIT_RESP_ALERT,2201,USER,IND,Alert email was sent AUDIT_RESP_KILL_PROC,2202,USER,IND,Kill program AUDIT_RESP_TERM_ACCESS,2203,USER,IND,Terminate session AUDIT_RESP_ACCT_REMOTE,2204,USER,IND,User account locked from remote access AUDIT_RESP_ACCT_LOCK_TIMED,2205,USER,IND,User account locked for time AUDIT_RESP_ACCT_UNLOCK_TIMED,2206,USER,IND,User account unlocked from time AUDIT_RESP_ACCT_LOCK,2207,USER,IND,User account was locked AUDIT_RESP_TERM_LOCK,2208,USER,IND,Terminal was locked AUDIT_RESP_SEBOOL,2209,USER,IND,Set an SELinux boolean AUDIT_RESP_EXEC,2210,USER,IND,Execute a script AUDIT_RESP_SINGLE,2211,USER,IND,Go to single user mode AUDIT_RESP_HALT,2212,USER,IND,Take the system down AUDIT_RESP_ORIGIN_BLOCK,2213,USER,IND,Address blocked by iptables AUDIT_RESP_ORIGIN_BLOCK_TIMED,2214,USER,IND,Address blocked for time AUDIT_USER_ROLE_CHANGE,2300,USER,IND,User changed to a new SELinux role AUDIT_ROLE_ASSIGN,2301,USER,IND,Administrator assigned user to SELinux role AUDIT_ROLE_REMOVE,2302,USER,IND,Administrator removed user from SELinux role AUDIT_LABEL_OVERRIDE,2303,USER,IND,Administrator is overriding a SELinux label AUDIT_LABEL_LEVEL_CHANGE,2304,USER,IND,Object level SELinux label modified AUDIT_USER_LABELED_EXPORT,2305,USER,IND,Object exported with SELinux label AUDIT_USER_UNLABELED_EXPORT,2306,USER,IND,Object exported without SELinux label AUDIT_DEV_ALLOC,2307,USER,IND,Device was allocated AUDIT_DEV_DEALLOC,2308,USER,IND,Device was deallocated AUDIT_FS_RELABEL,2309,USER,IND,Filesystem relabeled AUDIT_USER_MAC_POLICY_LOAD,2310,USER,IND,Usersapce daemon loaded SELinux policy AUDIT_ROLE_MODIFY,2311,USER,IND,Administrator modified an SELinux role AUDIT_USER_MAC_CONFIG_CHANGE,2312,USER,IND,Change made to MAC (Mandatory Access Control) policy AUDIT_USER_MAC_STATUS,2313,USER,IND,Userspc daemon enforcing change AUDIT_CRYPTO_TEST_USER,2400,USER,IND,Cryptographic test results AUDIT_CRYPTO_PARAM_CHANGE_USER,2401,USER,IND,Cryptographic attribute change AUDIT_CRYPTO_LOGIN,2402,USER,IND,Cryptographic officer login AUDIT_CRYPTO_LOGOUT,2403,USER,IND,Cryptographic officer logout AUDIT_CRYPTO_KEY_USER,2404,USER,IND,"Create, delete, negotiate cryptographic key identifier" AUDIT_CRYPTO_FAILURE_USER,2405,USER,IND,"Fail decrypt, encrypt or randomize operation" AUDIT_CRYPTO_REPLAY_USER,2406,USER,IND,Cryptographic replay attack detected AUDIT_CRYPTO_SESSION,2407,USER,IND,Parameters set during TLS session establishment AUDIT_CRYPTO_IKE_SA,2408,USER,IND,Parameters related to IKE SA AUDIT_CRYPTO_IPSEC_SA,2409,USER,IND,Parameters related to IPSEC SA AUDIT_VIRT_CONTROL,2500,USER,IND,"Start, Pause, Stop VM" AUDIT_VIRT_RESOURCE,2501,USER,IND,Resource assignment AUDIT_VIRT_MACHINE_ID,2502,USER,IND,Binding of label to VM AUDIT_VIRT_INTEGRITY_CHECK,2503,USER,IND,Guest integrity results AUDIT_VIRT_CREATE,2504,USER,IND,Creation of guest image AUDIT_VIRT_DESTROY,2505,USER,IND,Destruction of guest image AUDIT_VIRT_MIGRATE_IN,2506,USER,IND,Inbound guest migration info AUDIT_VIRT_MIGRATE_OUT,2507,USER,IND,Outbound guest migration info linux-audit-parser-0.2.7/src/body.rs000064400000000000000000000157261046102023000154740ustar 00000000000000use std::fmt::{self, Debug}; use std::ops::Range; #[cfg(feature = "serde")] use serde::{ de::{MapAccess, Visitor}, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer, }; use crate::*; /// Parsed body of an Audit message, consisting of [`Key`]/[`Value`] pairs. pub struct Body<'a> { elems: Vec<(Key, Value<'a>)>, arena: Vec>, _pin: std::marker::PhantomPinned, } impl<'a> PartialEq> for Body<'a> { fn eq(&self, other: &Self) -> bool { self.elems == other.elems } } impl Default for Body<'_> { fn default() -> Self { Body { elems: Vec::with_capacity(8), arena: vec![], _pin: std::marker::PhantomPinned, } } } impl Debug for Body<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut seq = f.debug_struct("Body"); for (k, v) in self { seq.field(&k.to_string(), &v); } seq.finish() } } #[cfg(feature = "serde")] impl Serialize for Body<'_> { #[inline(always)] fn serialize(&self, s: S) -> Result { let mut map = s.serialize_map(Some(self.elems.len()))?; for (k, v) in self.into_iter() { map.serialize_entry(&k, &v)?; } map.end() } } #[cfg(feature = "serde")] #[derive(Default)] struct BodyVisitor<'a>(std::marker::PhantomData>); #[cfg(feature = "serde")] impl<'a, 'de> Visitor<'de> for BodyVisitor<'a> { type Value = Body<'a>; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a map") } fn visit_map>(self, mut map: A) -> Result { let mut body = Body::new(); while let Some((k, v)) = map.next_entry::()? { body.push((k, v)); } Ok(body) } } #[cfg(feature = "serde")] impl<'de> Deserialize<'de> for Body<'_> { fn deserialize>(d: D) -> Result { d.deserialize_map(BodyVisitor::default()) } } impl Body<'_> { /// Constructs a new, empty `Body`. pub fn new() -> Self { Self::default() } /// Constructs a new, empty `Body` with at least the specified /// `capacity` for `Key`/`Value` entries. pub fn with_capacity(len: usize) -> Self { Self { elems: Vec::with_capacity(len), ..Self::default() } } fn add_slice<'a, 'i>(&mut self, input: &'i [u8]) -> &'a [u8] where 'a: 'i, { let ilen = input.len(); // let changed_buf: &Vec; for buf in self.arena.iter() { let Range { start, end } = input.as_ptr_range(); if buf.as_slice().as_ptr_range().contains(&start) && buf.as_slice().as_ptr_range().contains(&end) { let s = std::ptr::slice_from_raw_parts(start, ilen); return unsafe { &*s }; } } for buf in self.arena.iter_mut() { if buf.capacity() - buf.len() > ilen { let e = buf.len(); buf.extend(input); let s = std::ptr::slice_from_raw_parts(buf[e..].as_ptr(), ilen); return unsafe { &*s }; } } self.arena .push(Vec::with_capacity(1014 * (1 + (ilen / 1024)))); let i = self.arena.len() - 1; let new_buf = &mut self.arena[i]; new_buf.extend(input); let s = std::ptr::slice_from_raw_parts(new_buf[..].as_ptr(), ilen); unsafe { &*s } } fn add_value<'a, 'i>(&mut self, v: Value<'i>) -> Value<'a> where 'a: 'i, { match v { Value::Str(s, q) => Value::Str(self.add_slice(s), q), Value::Owned(s) => Value::Str(self.add_slice(s.as_slice()), Quote::None), Value::List(vs) => Value::List(vs.into_iter().map(|v| self.add_value(v)).collect()), Value::StringifiedList(vs) => { Value::StringifiedList(vs.into_iter().map(|v| self.add_value(v)).collect()) } Value::Segments(vs) => { let vs = vs.iter().map(|s| self.add_slice(s)).collect(); Value::Segments(vs) } Value::Map(vs) => Value::Map( vs.into_iter() .map(|(k, v)| (k, self.add_value(v))) .collect(), ), // safety: These enum variants are self-contained. Value::Empty | Value::Literal(_) | Value::Number(_) | Value::Skipped(_) => unsafe { std::mem::transmute::, Value<'a>>(v) }, } } /// Appends `kv` to the back of a Body. pub fn push(&mut self, kv: (Key, Value)) { let (k, v) = kv; let v = self.add_value(v); self.elems.push((k, v)); } /// Returns the number of elements in the `Body`. pub fn len(&self) -> usize { self.elems.len() } /// Extends Body with the elements of another `Body`. pub fn extend(&mut self, other: Self) { self.arena.extend(other.arena); self.elems.reserve(other.elems.len()); for (k, v) in other.elems { self.push((k, v)); } } /// Returns `true` if the `Body` has a length of 0. pub fn is_empty(&self) -> bool { self.elems.is_empty() } /// Retrieves the first value found for a given `key`. pub fn get>(&self, key: K) -> Option<&Value> { let key = key.as_ref(); self.elems.iter().find(|(k, _)| k == key).map(|(_, v)| v) } /// Reserves capacity for at least `additional` more elements. pub fn reserve(&mut self, additional: usize) { self.elems.reserve(additional); } } impl<'a> Body<'a> { /// Retains only the elements specified by the predicate. pub fn retain(&mut self, f: F) where F: FnMut(&(Key, Value<'a>)) -> bool, { self.elems.retain(f) } } impl Clone for Body<'_> { fn clone(&self) -> Self { let mut new = Body::default(); self.into_iter() .cloned() .for_each(|(k, v)| new.push((k, v))); new } } impl<'a> IntoIterator for &'a Body<'a> { type Item = &'a (Key, Value<'a>); type IntoIter = std::slice::Iter<'a, (Key, Value<'a>)>; fn into_iter(self) -> Self::IntoIter { self.elems.iter() } } pub struct BodyIterator<'a> { iter: std::vec::IntoIter<(Key, Value<'a>)>, _arena: Vec>, _pin: std::marker::PhantomPinned, } impl<'a> Iterator for BodyIterator<'a> { type Item = (Key, Value<'a>); fn next(&mut self) -> Option { self.iter.next() } } impl<'a> IntoIterator for Body<'a> { type Item = (Key, Value<'a>); type IntoIter = BodyIterator<'a>; fn into_iter(self) -> Self::IntoIter { Self::IntoIter { iter: self.elems.into_iter(), _arena: self.arena, _pin: std::marker::PhantomPinned, } } } linux-audit-parser-0.2.7/src/const.rs.in000064400000000000000000000021351046102023000162600ustar 00000000000000use lazy_static::lazy_static; use std::collections::HashMap; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub(crate) enum FieldType { Encoded, Numeric, NumericDec, NumericHex, NumericOct, } lazy_static! { pub(crate) static ref EVENT_IDS: HashMap<&'static[u8], u32> = { let els: &[(&str, u32)] = &[ /* @EVENT_CONST@ */ ]; let mut hm = HashMap::with_capacity(els.len()); for (name, value) in els { hm.insert(name.as_bytes(), *value); } hm }; pub(crate) static ref EVENT_NAMES: HashMap = { let els: &[(&str, u32)] = &[ /* @EVENT_CONST@ */ ]; let mut hm = HashMap::with_capacity(els.len()); for (name, value) in els { hm.insert(*value, *name); } hm }; pub(crate) static ref FIELD_TYPES: HashMap<&'static[u8],FieldType> = { let els: &[(&str, FieldType)] = &[ /* @FIELD_TYPES@ */ ]; let mut hm = HashMap::with_capacity(els.len()); for (name, typ) in els { hm.insert(name.as_bytes(), *typ); } hm }; } linux-audit-parser-0.2.7/src/constants.rs000064400000000000000000000000611046102023000165350ustar 00000000000000include!(concat!(env!("OUT_DIR"), "/const.rs")); linux-audit-parser-0.2.7/src/event_id.rs000064400000000000000000000035021046102023000163210ustar 00000000000000#[cfg(feature = "serde")] use serde_with::{DeserializeFromStr, SerializeDisplay}; use std::fmt::{self, Display}; use std::str::FromStr; use thiserror::Error; /// The identifier of an audit event, corresponding to the /// `msg=audit(…)` part of every Linux Audit log line. /// /// The event ID can reasonably be expected to be unique per system. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)] #[cfg_attr(feature = "serde", derive(DeserializeFromStr, SerializeDisplay))] pub struct EventID { /// Unix epoch-based timestamp, with mullisecond-precision pub timestamp: u64, /// Sequence number pub sequence: u32, } impl Display for EventID { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let sec = self.timestamp / 1000; let msec = self.timestamp % 1000; let seq = self.sequence; write!(f, "{sec}.{msec:03}:{seq}") } } /// The error type returned by [EventID::from_str] #[derive(Debug, Error)] pub enum ParseEventIDError { #[error("wrong format (character '{0}' not found)")] Format(char), #[error("cannot parse number: {0}")] Number(std::num::ParseIntError), } impl FromStr for EventID { type Err = ParseEventIDError; fn from_str(s: &str) -> Result { let (sec, rest) = s.split_once(".").ok_or(ParseEventIDError::Format('.'))?; let (msec, seq) = rest.split_once(":").ok_or(ParseEventIDError::Format(':'))?; Ok(EventID { timestamp: u64::from_str(sec).map_err(ParseEventIDError::Number)? * 1000 + u64::from_str(msec).map_err(ParseEventIDError::Number)?, sequence: u32::from_str(seq).map_err(ParseEventIDError::Number)?, }) } } impl PartialEq for EventID { fn eq(&self, other: &str) -> bool { format!("{self}") == other } } linux-audit-parser-0.2.7/src/key.rs000064400000000000000000000130431046102023000153150ustar 00000000000000use std::convert::Infallible; use std::fmt::{self, Debug, Display}; use std::str::{self, FromStr}; #[cfg(feature = "serde")] use serde_with::{DeserializeFromStr, SerializeDisplay}; /// Common [`Key`]s found in SYSCALL records #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)] #[repr(usize)] pub enum Common { Arch, Argc, CapFe, CapFi, CapFp, CapFver, Comm, Cwd, Dev, Exe, Exit, Inode, Item, Items, Key, Mode, Msg, Name, Nametype, Pid, PPid, Ses, Subj, Success, Syscall, Tty, } const COMMON: &[(&str, Common)] = &[ ("arch", Common::Arch), ("argc", Common::Argc), ("cap_fe", Common::CapFe), ("cap_fi", Common::CapFi), ("cap_fp", Common::CapFp), ("cap_fver", Common::CapFver), ("comm", Common::Comm), ("cwd", Common::Cwd), ("dev", Common::Dev), ("exe", Common::Exe), ("exit", Common::Exit), ("inode", Common::Inode), ("item", Common::Item), ("items", Common::Items), ("key", Common::Key), ("mode", Common::Mode), ("msg", Common::Msg), ("name", Common::Name), ("nametype", Common::Nametype), ("pid", Common::Pid), ("ppid", Common::PPid), ("ses", Common::Ses), ("subj", Common::Subj), ("success", Common::Success), ("syscall", Common::Syscall), ("tty", Common::Tty), ]; impl TryFrom<&[u8]> for Common { type Error = &'static str; fn try_from(value: &[u8]) -> Result { let i = COMMON .binary_search_by_key(&value, |(s, _)| s.as_bytes()) .map_err(|_| "unknown key")?; Ok(COMMON[i].1) } } impl From for &'static str { fn from(value: Common) -> Self { COMMON[value as usize].0 } } impl Display for Common { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let c = COMMON[*self as usize].0; write!(f, "{c}") } } pub(crate) type NVec = tinyvec::TinyVec<[u8; 14]>; /// Representation of the key part of key/value pairs in [`Body`] /// /// [`Body`]: crate::Body #[derive(PartialEq, Eq, Clone)] #[cfg_attr(feature = "serde", derive(SerializeDisplay, DeserializeFromStr))] pub enum Key { /// regular ASCII-only name as returned by parser Name(NVec), /// ASCII-only name for UID fields NameUID(NVec), /// ASCII-only name for GID fields NameGID(NVec), /// special case for common values Common(Common), /// regular ASCII-only name, output/serialization in all-caps, for /// translated / "enriched" values NameTranslated(NVec), /// special case for argument lists: `a0`, `a1`, (`SYSCALL` and /// `EXECVE`); `a2[0]`, `a2[1]` (`EXECVE`) Arg(u32, Option), /// `a0_len` as found in `EXECVE` lines ArgLen(u32), /// Not returned by parser Literal(&'static str), } impl Default for Key { fn default() -> Self { Key::Literal("no_key") } } impl Debug for Key { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.to_string()) } } impl Display for Key { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Key::Arg(x, Some(y)) => write!(f, "a{x}[{y}]"), Key::Arg(x, None) => write!(f, "a{x}"), Key::ArgLen(x) => write!(f, "a{x}_len"), Key::Name(r) | Key::NameUID(r) | Key::NameGID(r) => { // safety: The parser guarantees an ASCII-only key. let s = unsafe { str::from_utf8_unchecked(r) }; f.write_str(s) } Key::Common(c) => write!(f, "{c}"), Key::NameTranslated(r) => { // safety: The parser guarantees an ASCII-only key. let s = unsafe { str::from_utf8_unchecked(r) }; f.write_str(&str::to_ascii_uppercase(s)) } Key::Literal(s) => f.write_str(s), } } } fn try_parse_a(s: &str) -> Option { match s.strip_prefix("a") { Some(s) => { if let Some(s) = s.strip_suffix("]") { let (x, y) = s.split_once("[")?; Some(Key::Arg( u32::from_str(x).ok()?, Some(u16::from_str(y).ok()?), )) } else if let Some(s) = s.strip_suffix("_len") { Some(Key::ArgLen(u32::from_str(s).ok()?)) } else { Some(Key::Arg(u32::from_str(s).ok()?, None)) } } _ => None, } } impl FromStr for Key { type Err = Infallible; fn from_str(s: &str) -> Result { if let Ok(c) = Common::try_from(s.as_bytes()) { Ok(Key::Common(c)) } else if let Some(k) = try_parse_a(s) { Ok(k) } else if s.ends_with("uid") { Ok(Key::NameUID(s.as_bytes().into())) } else if s.ends_with("gid") { Ok(Key::NameGID(s.as_bytes().into())) } else { Ok(Key::from(s.as_bytes())) } } } impl PartialEq for Key { fn eq(&self, other: &str) -> bool { self == other.as_bytes() } } impl PartialEq<[u8]> for Key { fn eq(&self, other: &[u8]) -> bool { match self { Key::Name(r) | Key::NameUID(r) | Key::NameGID(r) => r.as_ref() == other, _ => self.to_string().as_bytes() == other, } } } impl From<&'static str> for Key { fn from(value: &'static str) -> Self { Self::Literal(value) } } impl From<&[u8]> for Key { fn from(value: &[u8]) -> Self { Self::Name(NVec::from(value)) } } linux-audit-parser-0.2.7/src/lib.rs000064400000000000000000000004071046102023000152730ustar 00000000000000mod body; mod constants; mod event_id; mod key; mod message; mod message_type; mod parser; mod value; pub use body::*; pub use event_id::*; pub use key::*; pub use message::*; pub use message_type::*; pub use parser::*; pub use value::*; #[cfg(test)] mod test; linux-audit-parser-0.2.7/src/message.rs000064400000000000000000000010141046102023000161440ustar 00000000000000use crate::*; /// A parsed message corresponding to a single line from the Linux Audit log #[derive(Debug, Clone)] pub struct Message<'a> { /// The identifier of the audit event, corresponding to `msg=audit(…)` in audit log lines pub id: EventID, /// The optional node name, corresponding to `node=…` in audit log lines pub node: Option>, /// Message type, corresponding to `type=…` in audit log lines pub ty: MessageType, /// The set of key/value parirs pub body: Body<'a>, } linux-audit-parser-0.2.7/src/message_type.rs000064400000000000000000000051471046102023000172200ustar 00000000000000#[cfg(feature = "serde")] use serde_with::{DeserializeFromStr, SerializeDisplay}; use std::fmt::{self, Debug, Display}; use std::str::{self, FromStr}; use thiserror::Error; use crate::constants::*; /// Type of an audit message, corresponding to the `type=…` part of /// every Linux Audit log line. /// /// The implementation uses the same 32bit unsigned integer values /// that are used by the Linux Audit API. Mappings between numeric and /// symbolic values is generated using CSV retrieved from the [`Linux /// Audit Project`]'s documentation. /// /// [`Linux Audit Project`]: https://github.com/linux-audit/audit-documentation #[derive(PartialEq, Eq, Hash, Default, Clone, Copy)] #[cfg_attr(feature = "serde", derive(DeserializeFromStr, SerializeDisplay))] pub struct MessageType(pub u32); impl Display for MessageType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match EVENT_NAMES.get(&(self.0)) { Some(name) => write!(f, "{name}"), None => write!(f, "UNKNOWN[{}]", self.0), } } } impl Debug for MessageType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match EVENT_NAMES.get(&(self.0)) { Some(name) => write!(f, "MessageType({name})"), None => write!(f, "MessageType({})", self.0), } } } /// The error type returned by [MessageType::from_str] #[derive(Debug, Error)] pub enum ParseMessageTypeError { #[error("unknown identifier ({0})")] Unknown(String), #[error("malformed UNKNOWN[…] string")] MalformedUnknown, #[error("cannot parse number ({0}): {1}")] Number(String, std::num::ParseIntError), } impl FromStr for MessageType { type Err = ParseMessageTypeError; fn from_str(s: &str) -> Result { if let Some(id) = EVENT_IDS.get(s.as_bytes()) { Ok(MessageType(*id)) } else { let number = s .strip_prefix("UNKNOWN[") .ok_or_else(|| ParseMessageTypeError::Unknown(s.into()))? .strip_suffix("]") .ok_or(ParseMessageTypeError::MalformedUnknown)?; let id = u32::from_str(number) .map_err(|e| ParseMessageTypeError::Number(number.into(), e))?; Ok(MessageType(id)) } } } include!(concat!(env!("OUT_DIR"), "/message_type_impl.rs")); impl MessageType { /// True for messages that are part of multi-part events from /// kernel-space. /// /// This mimics auparse logic as of version 3.0.6 pub fn is_multipart(&self) -> bool { (1300..2100).contains(&self.0) || self == &MessageType::LOGIN } } linux-audit-parser-0.2.7/src/message_type_impl.rs.in000064400000000000000000000000601046102023000206330ustar 00000000000000impl MessageType { /* @MSG_TYPE_CONST@ */ } linux-audit-parser-0.2.7/src/parser.rs000064400000000000000000000500611046102023000160220ustar 00000000000000use std::convert::{From, TryFrom}; use std::str; use nom::{ branch::*, bytes::complete::*, character::complete::*, character::*, combinator::*, multi::*, sequence::*, IResult, }; use nom::character::complete::{i64 as dec_i64, u16 as dec_u16, u32 as dec_u32, u64 as dec_u64}; use thiserror::Error; use crate::constants::*; use crate::*; /// Parser for Linux Audit messages, with a few configurable options #[derive(Debug)] pub struct Parser { /// Process enriched (i.e. ALL-CAPS keys). Default: true pub enriched: bool, /// Try to process common msg='…' strings into key/value maps. Default: true pub split_msg: bool, } impl Default for Parser { fn default() -> Self { Self { enriched: true, split_msg: true, } } } /// Audit parser error type #[derive(Debug, Error)] pub enum ParseError { /// The header (`type= … msg=audit(…):`) could not be parsed. #[error("cannot parse header: {}", String::from_utf8_lossy(.0))] MalformedHeader(Vec), /// The body (everything after the event ID) could not be parsed. #[error("cannot parse body: {}", String::from_utf8_lossy(.0))] MalformedBody(Vec), /// Garbage text was found at the end of the body. #[error("garbage at end of message: {}", String::from_utf8_lossy(.0))] TrailingGarbage(Vec), /// A value in hexadecimal encoding could not be converted. #[error("{id} ({ty}) can't hex-decode {}", String::from_utf8_lossy(.hex_str))] HexDecodeError { ty: MessageType, id: EventID, hex_str: Vec, }, } /// Parse a single log line as produced by _auditd(8)_ /// /// If `skip_enriched` is set and _auditd_ has been configured to /// produce `log_format=ENRICHED` logs, i.e. to resolve `uid`, `gid`, /// `syscall`, `arch`, `sockaddr` fields, those resolved values are /// dropped by the parser. /// /// To maintain compatibility, `parse` does not attempt to process /// single-quoted `msg='…'` strings into key/value maps. pub fn parse<'a>(raw: &[u8], skip_enriched: bool) -> Result, ParseError> { Parser { enriched: !skip_enriched, ..Parser::default() } .parse(raw) } impl Parser { /// Parse a single log line as produced by _auditd(8)_ pub fn parse<'a, 'b>(&'a self, raw: &'a [u8]) -> Result, ParseError> { let (rest, (node, ty, id)) = parse_header(raw).map_err(|_| ParseError::MalformedHeader(raw.to_vec()))?; let (rest, kv) = self .parse_body(rest, ty) .map_err(|_| ParseError::MalformedBody(rest.to_vec()))?; if !rest.is_empty() { return Err(ParseError::TrailingGarbage(rest.to_vec())); } let node = node.map(|s| s.to_vec()); let mut body = Body::new(); for (k, v) in kv { body.push((k, v)); } Ok(Message { id, node, ty, body }) } /// Recognize the body: Multiple key/value pairs, with special cases /// for some irregular messages #[inline(always)] fn parse_body<'a>( &'a self, input: &'a [u8], ty: MessageType, ) -> IResult<&'a [u8], Vec<(Key, Value<'a>)>> { // Handle some corner cases that don't fit the general key=value // scheme. let (input, special) = match ty { MessageType::AVC => opt(map( tuple(( preceded( pair(tag("avc:"), space0), alt((tag("granted"), tag("denied"))), ), delimited( tuple((space0, tag("{"), space0)), many1(terminated(parse_identifier, space0)), tuple((tag("}"), space0, tag("for"), space0)), ), )), |(k, v)| { ( Key::Name(NVec::from(k)), Value::List( v.iter() .map(|e| Value::Str(e, Quote::None)) .collect::>(), ), ) }, ))(input)?, MessageType::TTY => { let (input, _) = opt(tag("tty "))(input)?; (input, None) } MessageType::MAC_POLICY_LOAD => { let (input, _) = opt(tag("policy loaded "))(input)?; (input, None) } _ => opt(map( terminated(tag("netlabel"), pair(tag(":"), space0)), |s| (Key::Name(NVec::from(s)), Value::Empty), ))(input)?, }; let (input, mut kv) = if !self.enriched { terminated( separated_list0(take_while1(|c| c == b' '), |input| self.parse_kv(input, ty)), alt(( value((), tuple((tag("\x1d"), is_not("\n"), tag("\n")))), value((), tag("\n")), )), )(input)? } else { terminated( separated_list0(take_while1(|c| c == b' ' || c == b'\x1d'), |input| { self.parse_kv(input, ty) }), newline, )(input)? }; if let Some(s) = special { kv.push(s) } Ok((input, kv)) } /// Recognize one key/value pair #[inline(always)] fn parse_kv<'a>( &'a self, input: &'a [u8], ty: MessageType, ) -> IResult<&'a [u8], (Key, Value<'a>)> { let (input, key) = match ty { // Special case for execve arguments: aX, aX[Y], aX_len MessageType::EXECVE if !input.is_empty() && input[0] == b'a' && !input.starts_with(b"argc") => { terminated( alt((parse_key_a_x_len, parse_key_a_xy, parse_key_a_x)), tag("="), )(input) } // Special case for syscall params: aX MessageType::SYSCALL => terminated(alt((parse_key_a_x, parse_key)), tag("="))(input), _ => terminated(parse_key, tag("="))(input), }?; let (input, value) = match (ty, &key) { (MessageType::SYSCALL, Key::Arg(_, None)) => map( recognize(terminated( many1_count(take_while1(is_hex_digit)), peek(take_while1(is_sep)), )), |s| { let ps = unsafe { str::from_utf8_unchecked(s) }; match u64::from_str_radix(ps, 16) { Ok(n) => Value::Number(Number::Hex(n)), Err(_) => Value::Str(s, Quote::None), } }, )(input)?, (MessageType::SYSCALL, Key::Common(c)) => self.parse_common(input, ty, *c)?, (MessageType::EXECVE, Key::Arg(_, _)) => parse_encoded(input)?, (MessageType::EXECVE, Key::ArgLen(_)) => parse_dec(input)?, (_, Key::Name(name)) => parse_named(input, ty, name)?, (_, Key::Common(c)) => self.parse_common(input, ty, *c)?, (_, Key::NameUID(name)) | (_, Key::NameGID(name)) => { alt((parse_dec, |input| parse_unspec_value(input, ty, name)))(input)? } _ => parse_encoded(input)?, }; Ok((input, (key, value))) } #[inline(always)] fn parse_common<'a>( &'a self, input: &'a [u8], ty: MessageType, c: Common, ) -> IResult<&'a [u8], Value<'a>> { let name = <&str>::from(c).as_bytes(); match c { Common::Arch | Common::CapFi | Common::CapFp | Common::CapFver => { alt((parse_hex, |input| parse_unspec_value(input, ty, name)))(input) } Common::Argc | Common::Exit | Common::CapFe | Common::Inode | Common::Item | Common::Items | Common::Pid | Common::PPid | Common::Ses | Common::Syscall => { alt((parse_dec, |input| parse_unspec_value(input, ty, name)))(input) } Common::Success | Common::Cwd | Common::Dev | Common::Tty | Common::Comm | Common::Exe | Common::Name | Common::Nametype | Common::Subj | Common::Key => { alt((parse_encoded, |input| parse_unspec_value(input, ty, name)))(input) } Common::Mode => alt((parse_oct, |input| parse_unspec_value(input, ty, name)))(input), Common::Msg => { if self.split_msg { alt((parse_kv_sq_as_map, |input| { parse_unspec_value(input, ty, name) }))(input) } else { alt((parse_encoded, |input| parse_unspec_value(input, ty, name)))(input) } } } } } /// Recognize the header: node, type, event identifier #[inline(always)] #[allow(clippy::type_complexity)] fn parse_header(input: &[u8]) -> IResult<&[u8], (Option<&[u8]>, MessageType, EventID)> { tuple(( opt(terminated(parse_node, is_a(" "))), terminated(parse_type, is_a(" ")), parse_msgid, ))(input) } /// Recognize the node name #[inline(always)] fn parse_node(input: &[u8]) -> IResult<&[u8], &[u8]> { preceded(tag("node="), is_not(" \t\r\n"))(input) } /// Recognize event type #[inline(always)] fn parse_type(input: &[u8]) -> IResult<&[u8], MessageType> { preceded( tag("type="), alt(( map_res( recognize(many1_count(alt((alphanumeric1, tag("_"))))), |s| { EVENT_IDS .get(s) .ok_or(format!("unknown event id {}", String::from_utf8_lossy(s))) .map(|n| MessageType(*n)) }, ), map(delimited(tag("UNKNOWN["), dec_u32, tag("]")), MessageType), )), )(input) } /// Recognize the "msg=audit(…):" event identifier #[inline(always)] fn parse_msgid(input: &[u8]) -> IResult<&[u8], EventID> { map( tuple(( preceded(tag("msg=audit("), dec_u64), delimited(tag("."), dec_u64, tag(":")), terminated(dec_u32, pair(tag("):"), space0)), )), |(sec, msec, sequence)| EventID { timestamp: 1000 * sec + msec, sequence, }, )(input) } #[inline(always)] fn parse_named<'a>(input: &'a [u8], ty: MessageType, name: &[u8]) -> IResult<&'a [u8], Value<'a>> { match FIELD_TYPES.get(name) { Some(&FieldType::Encoded) => { alt((parse_encoded, |input| parse_unspec_value(input, ty, name)))(input) } Some(&FieldType::NumericHex) => { alt((parse_hex, |input| parse_unspec_value(input, ty, name)))(input) } Some(&FieldType::NumericDec) => { alt((parse_dec, |input| parse_unspec_value(input, ty, name)))(input) } Some(&FieldType::NumericOct) => { alt((parse_oct, |input| parse_unspec_value(input, ty, name)))(input) } // FIXME: Some(&FieldType::Numeric) _ => alt((parse_encoded, |input| parse_unspec_value(input, ty, name)))(input), } } /// Recognize encoded value: /// /// May be double-quoted string, hex-encoded blob, (null), ?. #[inline(always)] fn parse_encoded(input: &[u8]) -> IResult<&[u8], Value> { alt(( map(parse_str_dq_safe, |s| Value::Str(s, Quote::Double)), terminated( map( recognize(many1_count(take_while_m_n(2, 2, is_hex_digit))), |hexstr: &[u8]| { let mut recoded = Vec::with_capacity(hexstr.len() / 2); for i in 0..hexstr.len() / 2 { let d = unsafe { str::from_utf8_unchecked(&hexstr[2 * i..2 * i + 2]) }; recoded.push(u8::from_str_radix(d, 16).unwrap()); } Value::Owned(recoded) }, ), peek(take_while1(is_sep)), ), terminated( value(Value::Empty, alt((tag("(null)"), tag("?")))), peek(take_while1(is_sep)), ), ))(input) } /// Recognize hexadecimal value #[inline(always)] fn parse_hex(input: &[u8]) -> IResult<&[u8], Value> { map_res( terminated(take_while1(is_hex_digit), peek(take_while1(is_sep))), |digits| -> Result<_, std::num::ParseIntError> { let digits = unsafe { str::from_utf8_unchecked(digits) }; Ok(Value::Number(Number::Hex(u64::from_str_radix(digits, 16)?))) }, )(input) } /// Recognize decimal value #[inline(always)] fn parse_dec(input: &[u8]) -> IResult<&[u8], Value> { map(terminated(dec_i64, peek(take_while1(is_sep))), |n| { Value::Number(Number::Dec(n)) })(input) } /// Recognize octal value #[inline(always)] fn parse_oct(input: &[u8]) -> IResult<&[u8], Value> { map_res( terminated(take_while1(is_oct_digit), peek(take_while1(is_sep))), |digits| -> Result<_, std::num::ParseIntError> { let digits = unsafe { str::from_utf8_unchecked(digits) }; Ok(Value::Number(Number::Oct(u64::from_str_radix(digits, 8)?))) }, )(input) } #[inline(always)] fn parse_unspec_value<'a>( input: &'a [u8], ty: MessageType, name: &[u8], ) -> IResult<&'a [u8], Value<'a>> { // work around apparent AppArmor breakage match (ty, name) { (_, b"subj") => { if let Ok((input, s)) = recognize(tuple(( opt(tag("=")), parse_str_unq, opt(delimited(tag(" ("), parse_identifier, tag(")"))), )))(input) { return Ok((input, Value::Str(s, Quote::None))); } } (MessageType::AVC, b"info") => { if let Ok((input, s)) = parse_str_dq(input) { return Ok((input, Value::Str(s, Quote::None))); } } (MessageType::SOCKADDR, b"SADDR") => { let broken_string: IResult<&[u8], &[u8]> = recognize(pair(tag("unknown family"), opt(take_till(is_sep))))(input); if let Ok((input, s)) = broken_string { return Ok((input, Value::Str(s, Quote::None))); } } _ => (), }; alt(( terminated( map(take_while1(is_safe_unquoted_chr), |s| { Value::Str(s, Quote::None) }), peek(take_while1(is_sep)), ), map(parse_kv_sq, |s| Value::Str(s, Quote::Single)), map(parse_str_sq, |s| Value::Str(s, Quote::Single)), map(parse_str_dq, |s| Value::Str(s, Quote::Double)), map(parse_kv_braced, |s| Value::Str(s, Quote::Braces)), map(parse_str_braced, |s| Value::Str(s, Quote::Braces)), value(Value::Empty, peek(take_while1(is_sep))), ))(input) } #[inline(always)] fn parse_str_sq(input: &[u8]) -> IResult<&[u8], &[u8]> { delimited(tag("'"), take_while(|c| c != b'\''), tag("'"))(input) } #[inline(always)] fn parse_str_dq_safe(input: &[u8]) -> IResult<&[u8], &[u8]> { delimited(tag("\""), take_while(is_safe_chr), tag("\""))(input) } #[inline(always)] fn parse_str_dq(input: &[u8]) -> IResult<&[u8], &[u8]> { delimited(tag("\""), take_while(|c| c != b'"'), tag("\""))(input) } #[inline(always)] fn parse_str_braced(input: &[u8]) -> IResult<&[u8], &[u8]> { delimited(tag("{ "), take_until(" }"), tag(" }"))(input) } #[inline(always)] fn parse_str_unq(input: &[u8]) -> IResult<&[u8], &[u8]> { take_while(is_safe_chr)(input) } #[inline(always)] fn parse_str_unq_inside_sq(input: &[u8]) -> IResult<&[u8], &[u8]> { take_while(|c| is_safe_chr(c) && c != b'\'')(input) } #[inline(always)] fn parse_str_words_inside_sq(input: &[u8]) -> IResult<&[u8], &[u8]> { let mut rest = input; loop { (rest, _) = take_while(|c| !b"' ".contains(&c))(rest)?; if alt((recognize(tuple((space1, parse_key, tag("=")))), tag("'")))(rest).is_ok() { break; } (rest, _) = space1(rest)?; } let l = input.len() - rest.len(); Ok((rest, &input[..l])) } /// More "correct" variant of parse_str_sq #[inline(always)] fn parse_kv_sq(input: &[u8]) -> IResult<&[u8], &[u8]> { delimited( tag("'"), recognize(separated_list0( tag(" "), tuple(( recognize(pair(alpha1, many0_count(alt((alphanumeric1, is_a("-_")))))), tag("="), alt((parse_str_dq, parse_str_braced, parse_str_unq_inside_sq)), )), )), tag("'"), )(input) } /// Recognize a map enclosed in single quotes #[inline(always)] fn parse_kv_sq_as_map(input: &[u8]) -> IResult<&[u8], Value> { map( delimited( tag("'"), separated_list0( space1, alt((separated_pair( parse_key, alt(( tag("="), recognize(tuple((tag(":"), space0))), // for 'avc: mumble mumble mumble …' )), alt(( parse_encoded, map(parse_str_words_inside_sq, |v| Value::Str(v, Quote::None)), map(parse_str_unq_inside_sq, |v| Value::Str(v, Quote::None)), )), ),)), ), tag("'"), ), Value::Map, )(input) } /// More "correct" variant of parse_str_braced #[inline(always)] fn parse_kv_braced(input: &[u8]) -> IResult<&[u8], &[u8]> { delimited( tag("{ "), recognize(separated_list0( tag(" "), tuple(( recognize(pair(alpha1, many0_count(alt((alphanumeric1, is_a("-_")))))), tag("="), alt((parse_str_sq, parse_str_dq, parse_str_unq)), )), )), tag(" }"), )(input) } /// Recognize regular keys of key/value pairs #[inline(always)] fn parse_key(input: &[u8]) -> IResult<&[u8], Key> { map( recognize(pair(alpha1, many0_count(alt((alphanumeric1, is_a("-_")))))), |s: &[u8]| { if let Ok(c) = Common::try_from(s) { Key::Common(c) } else if s.ends_with(b"uid") { Key::NameUID(NVec::from(s)) } else if s.ends_with(b"gid") { Key::NameGID(NVec::from(s)) } else { Key::Name(NVec::from(s)) } }, )(input) } /// Recognize length specifier for EXECVE split arguments, e.g. a1_len #[inline(always)] fn parse_key_a_x_len(input: &[u8]) -> IResult<&[u8], Key> { map(delimited(tag("a"), dec_u32, tag("_len")), Key::ArgLen)(input) } /// Recognize EXECVE split arguments, e.g. a1[3] #[inline(always)] fn parse_key_a_xy(input: &[u8]) -> IResult<&[u8], Key> { map( pair( preceded(tag("a"), dec_u32), delimited(tag("["), dec_u16, tag("]")), ), |(x, y)| Key::Arg(x, Some(y)), )(input) } /// Recognize SYSCALL, EXECVE regular argument keys, e.g. a1, a2, a3… #[inline(always)] fn parse_key_a_x(input: &[u8]) -> IResult<&[u8], Key> { map(preceded(tag("a"), u32), |x| Key::Arg(x, None))(input) } /// Recognize identifiers (used in some irregular messages) /// Like [A-Za-z_][A-Za-z0-9_]* #[inline(always)] fn parse_identifier(input: &[u8]) -> IResult<&[u8], &[u8]> { recognize(pair( alt((alpha1, tag("_"))), many0_count(alt((alphanumeric1, tag("_")))), ))(input) } /// Characters permitted in kernel "encoded" strings that would /// otherwise be hex-encoded. #[inline(always)] fn is_safe_chr(c: u8) -> bool { c == b'!' || (b'#'..=b'~').contains(&c) } /// Characters permitted in kernel "encoded" strings, minus /// single-quotes, braces #[inline(always)] fn is_safe_unquoted_chr(c: u8) -> bool { (b'#'..=b'&').contains(&c) || (b'('..=b'z').contains(&c) || c == b'!' || c == b'|' || c == b'~' } /// Separator characters #[inline(always)] fn is_sep(c: u8) -> bool { c == b' ' || c == b'\x1d' || c == b'\n' } linux-audit-parser-0.2.7/src/test.rs000064400000000000000000000556031046102023000155140ustar 00000000000000use crate::*; #[cfg(feature = "serde")] use serde_test::{assert_de_tokens, assert_ser_tokens, Token}; #[test] fn parser() { // ensure that constant init works assert_eq!(format!("--{}--", MessageType::EOE), "--EOE--"); assert_eq!(format!("--{}--", MessageType(9999)), "--UNKNOWN[9999]--"); let msg = parse(include_bytes!("testdata/line-eoe.txt"), false).unwrap(); assert_eq!(msg.ty, MessageType::EOE); assert_eq!( msg.id, EventID { timestamp: 1615225617302, sequence: 25836 } ); let msg = parse(include_bytes!("testdata/line-syscall.txt"), false).unwrap(); assert_eq!(msg.ty, MessageType::SYSCALL); assert_eq!( msg.id, EventID { timestamp: 1615114232375, sequence: 15558 } ); assert_eq!( msg.body .into_iter() .map(|(k, v)| format!("{k:?}: {v:?}")) .collect::>(), vec!( "arch: Num:<0xc000003e>", "syscall: Num:<59>", "success: Str:", "exit: Num:<0>", "a0: Num:<0x63b29337fd18>", "a1: Num:<0x63b293387d58>", "a2: Num:<0x63b293375640>", "a3: Num:<0xfffffffffffff000>", "items: Num:<2>", "ppid: Num:<10883>", "pid: Num:<10884>", "auid: Num:<1000>", "uid: Num:<0>", "gid: Num:<0>", "euid: Num:<0>", "suid: Num:<0>", "fsuid: Num:<0>", "egid: Num:<0>", "sgid: Num:<0>", "fsgid: Num:<0>", "tty: Str:", "ses: Num:<1>", "comm: Str:", "exe: Str:", "key: Empty", "ARCH: Str:", "SYSCALL: Str:", "AUID: Str:", "UID: Str:", "GID: Str:", "EUID: Str:", "SUID: Str:", "FSUID: Str:", "EGID: Str:", "SGID: Str:", "FSGID: Str:", ) ); let msg = parse(include_bytes!("testdata/line-execve.txt"), false).unwrap(); assert_eq!(msg.ty, MessageType::EXECVE); assert_eq!( msg.id, EventID { timestamp: 1614788539386, sequence: 13232 } ); assert_eq!( msg.body .into_iter() .map(|(k, v)| format!("{k:?}: {v:?}")) .collect::>(), vec!("argc: Num:<0>", "a0: Str:") ); let msg = parse(include_bytes!("testdata/line-path.txt"), false).unwrap(); assert_eq!(msg.ty, MessageType::PATH); assert_eq!( msg.id, EventID { timestamp: 1614788539386, sequence: 13232 } ); assert_eq!( msg.body .into_iter() .map(|(k, v)| format!("{k:?}: {v:?}")) .collect::>(), vec!( "item: Num:<0>", "name: Str:", "inode: Num:<261214>", "dev: Str:", "mode: Num:<0o100755>", "ouid: Num:<0>", "ogid: Num:<0>", "rdev: Str:<00:00>", "nametype: Str:", "cap_fp: Num:<0x0>", "cap_fi: Num:<0x0>", "cap_fe: Num:<0>", "cap_fver: Num:<0x0>", ) ); let msg = parse(include_bytes!("testdata/line-path-enriched.txt"), false).unwrap(); assert_eq!(msg.ty, MessageType::PATH); assert_eq!( msg.id, EventID { timestamp: 1615113648978, sequence: 15219 } ); assert_eq!( msg.body .into_iter() .map(|(k, v)| format!("{k:?}: {v:?}")) .collect::>(), vec!( "item: Num:<1>", "name: Str:", "inode: Num:<262146>", "dev: Str:", "mode: Num:<0o100755>", "ouid: Num:<0>", "ogid: Num:<0>", "rdev: Str:<00:00>", "nametype: Str:", "cap_fp: Num:<0x0>", "cap_fi: Num:<0x0>", "cap_fe: Num:<0>", "cap_fver: Num:<0x0>", "OUID: Str:", "OGID: Str:", ) ); let msg = parse(include_bytes!("testdata/line-user-acct.txt"), false).unwrap(); assert_eq!(msg.ty, MessageType::USER_ACCT); assert_eq!( msg.id, EventID { timestamp: 1615113648981, sequence: 15220 } ); assert_eq!( msg.body .into_iter() .map(|(k, v)| format!("{k:?}: {v:?}")) .collect::>(), vec!( "pid: Num:<9460>", "uid: Num:<1000>", "auid: Num:<1000>", "ses: Num:<1>", "msg: Map: grantors=Str: acct=Str: exe=Str: hostname=Empty addr=Empty terminal=Str: res=Str:>", "UID: Str:", "AUID: Str:", ) ); let msg = Parser { enriched: false, split_msg: false, } .parse(include_bytes!("testdata/line-user-acct.txt")) .unwrap(); assert_eq!( msg.body .into_iter() .map(|(k, v)| format!("{k:?}: {v:?}")) .collect::>(), vec!( "pid: Num:<9460>", "uid: Num:<1000>", "auid: Num:<1000>", "ses: Num:<1>", r#"msg: Str:"#, ) ); let msg = parse(include_bytes!("testdata/line-unknown.txt"), false).unwrap(); assert_eq!(msg.ty, MessageType::BPF); assert_eq!( msg.id, EventID { timestamp: 1626883065201, sequence: 216697 } ); let msg = parse(include_bytes!("testdata/line-avc-denied.txt"), false).unwrap(); assert_eq!(msg.ty, MessageType::AVC); assert_eq!( msg.body .into_iter() .map(|(k, v)| format!("{k:?}: {v:?}")) .collect::>(), vec!( "pid: Num:<15381>", "comm: Str:", "capability: Num:<7>", "scontext: Str:", "tcontext: Str:", "tclass: Str:", "permissive: Num:<1>", "denied: List:", ) ); let msg = parse(include_bytes!("testdata/line-avc-granted.txt"), false).unwrap(); assert_eq!(msg.ty, MessageType::AVC); assert_eq!( msg.body .into_iter() .map(|(k, v)| format!("{k:?}: {v:?}")) .collect::>(), vec!( "pid: Num:<11209>", "comm: Str:", "scontext: Str:", "tcontext: Str:", "tclass: Str:", "granted: List:", ) ); let msg = parse(include_bytes!("testdata/line-netlabel.txt"), false).unwrap(); assert_eq!(msg.ty, MessageType::MAC_UNLBL_ALLOW); assert_eq!( msg.body .into_iter() .map(|(k, v)| format!("{k:?}: {v:?}")) .collect::>(), vec!( "auid: Num:<0>", "ses: Num:<0>", // FIXME: strings should be numbers "unlbl_accept: Str:<1>", "old: Str:<0>", "AUID: Str:", "netlabel: Empty", ) ); let msg = parse(include_bytes!("testdata/line-broken-subj1.txt"), false).unwrap(); assert_eq!( msg.body .into_iter() .map(|(k, v)| format!("{k:?}: {v:?}")) .collect::>(), vec!( "arch: Num:<0xc000003e>", "syscall: Num:<59>", "success: Str:", "exit: Num:<0>", "a0: Num:<0x55b26d44a6a0>", "a1: Num:<0x55b26d44a878>", "a2: Num:<0x55b26d44a8e8>", "a3: Num:<0x7faeccab5850>", "items: Num:<2>", "ppid: Num:<659>", "pid: Num:<661>", "auid: Num:<4294967295>", "uid: Num:<0>", "gid: Num:<0>", "euid: Num:<0>", "suid: Num:<0>", "fsuid: Num:<0>", "egid: Num:<0>", "sgid: Num:<0>", "fsgid: Num:<0>", "tty: Str:<(none)>", "ses: Num:<4294967295>", "comm: Str:", "exe: Str:", "subj: Str:", "key: Empty", ) ); let msg = parse(include_bytes!("testdata/line-broken-subj2.txt"), false).unwrap(); assert_eq!( msg.body .into_iter() .map(|(k, v)| format!("{k:?}: {v:?}")) .collect::>(), vec!( "arch: Num:<0xc000003e>", "syscall: Num:<49>", "success: Str:", "exit: Num:<0>", "a0: Num:<0x15>", "a1: Num:<0x55c5e046e264>", "a2: Num:<0x1c>", "a3: Num:<0x7ffc8fab77ec>", "items: Num:<0>", "ppid: Num:<1899774>", "pid: Num:<1899780>", "auid: Num:<4294967295>", "uid: Num:<0>", "gid: Num:<0>", "euid: Num:<0>", "suid: Num:<0>", "fsuid: Num:<0>", "egid: Num:<0>", "sgid: Num:<0>", "fsgid: Num:<0>", "tty: Str:<(none)>", "ses: Num:<4294967295>", "comm: Str:", "exe: Str:", "subj: Str:<=/usr/sbin/ntpd (enforce)>", "key: Empty", ) ); let msg = parse(include_bytes!("testdata/line-broken-avc-info.txt"), false).unwrap(); assert_eq!( msg.body .into_iter() .map(|(k, v)| format!("{k:?}: {v:?}")) .collect::>(), vec!( "apparmor: Str:", "operation: Str:", "info: Str:", "profile: Str:", "name: Str:", "pid: Num:<3981295>", "comm: Str:", ) ); for enriched in [true, false] { for split_msg in [true, false] { for (n, line) in [ &include_bytes!("testdata/line-acct-lock.txt")[..], &include_bytes!("testdata/line-add-group.txt")[..], &include_bytes!("testdata/line-add-user.txt")[..], &include_bytes!("testdata/line-anom-abend-2.txt")[..], &include_bytes!("testdata/line-anom-abend.txt")[..], &include_bytes!("testdata/line-avc-denied.txt")[..], &include_bytes!("testdata/line-avc-granted.txt")[..], &include_bytes!("testdata/line-bpf.txt")[..], &include_bytes!("testdata/line-broken-avc-info.txt")[..], &include_bytes!("testdata/line-broken-subj1.txt")[..], &include_bytes!("testdata/line-broken-subj2.txt")[..], &include_bytes!("testdata/line-chgrp-id.txt")[..], &include_bytes!("testdata/line-cred-acq.txt")[..], &include_bytes!("testdata/line-cred-disp.txt")[..], &include_bytes!("testdata/line-cred-refr.txt")[..], &include_bytes!("testdata/line-crypto-key-user.txt")[..], &include_bytes!("testdata/line-crypto-param-change-user.txt")[..], &include_bytes!("testdata/line-crypto-session.txt")[..], &include_bytes!("testdata/line-daemon-end-2.txt")[..], &include_bytes!("testdata/line-daemon-end.txt")[..], &include_bytes!("testdata/line-daemon-start.txt")[..], &include_bytes!("testdata/line-del-group.txt")[..], &include_bytes!("testdata/line-del-user.txt")[..], &include_bytes!("testdata/line-eoe.txt")[..], &include_bytes!("testdata/line-execve.txt")[..], &include_bytes!("testdata/line-grp-mgmt.txt")[..], &include_bytes!("testdata/line-mac-policy-load.txt")[..], &include_bytes!("testdata/line-netfilter.txt")[..], &include_bytes!("testdata/line-netlabel.txt")[..], &include_bytes!("testdata/line-path-enriched.txt")[..], &include_bytes!("testdata/line-path.txt")[..], &include_bytes!("testdata/line-sockaddr-unix-2.txt")[..], &include_bytes!("testdata/line-sockaddr-unix.txt")[..], &include_bytes!("testdata/line-sockaddr-unknown-1.txt")[..], &include_bytes!("testdata/line-sockaddr-unknown-2.txt")[..], &include_bytes!("testdata/line-sockaddr-unknown-3.txt")[..], &include_bytes!("testdata/line-software-update.txt")[..], &include_bytes!("testdata/line-syscall.txt")[..], &include_bytes!("testdata/line-tty.txt")[..], &include_bytes!("testdata/line-unknown.txt")[..], &include_bytes!("testdata/line-uringop.txt")[..], &include_bytes!("testdata/line-user-acct.txt")[..], &include_bytes!("testdata/line-user-auth-2.txt")[..], &include_bytes!("testdata/line-user-auth.txt")[..], &include_bytes!("testdata/line-user-avc-1.txt")[..], &include_bytes!("testdata/line-user-avc-2.txt")[..], &include_bytes!("testdata/line-user-chauthtok.txt")[..], &include_bytes!("testdata/line-user-end.txt")[..], &include_bytes!("testdata/line-user-err.txt")[..], &include_bytes!("testdata/line-user-login.txt")[..], &include_bytes!("testdata/line-user-logout.txt")[..], &include_bytes!("testdata/line-user-mgmt.txt")[..], &include_bytes!("testdata/line-user-role-change.txt")[..], &include_bytes!("testdata/line-user-selinux-err.txt")[..], &include_bytes!("testdata/line-user-start.txt")[..], &include_bytes!("testdata/line-usys-config.txt")[..], ] .iter() .enumerate() { Parser { enriched, split_msg, } .parse(line) .unwrap_or_else(|_| { panic!("failed to parse {n} (enriched={enriched}, split_msg={split_msg}") }); } } } } #[test] fn test_msg_kv() { let p = Parser { split_msg: true, ..Parser::default() }; for (n, line) in [ &include_bytes!("testdata/line-acct-lock.txt")[..], &include_bytes!("testdata/line-add-group.txt")[..], &include_bytes!("testdata/line-add-user.txt")[..], &include_bytes!("testdata/line-chgrp-id.txt")[..], &include_bytes!("testdata/line-cred-acq.txt")[..], &include_bytes!("testdata/line-cred-disp.txt")[..], &include_bytes!("testdata/line-cred-refr.txt")[..], &include_bytes!("testdata/line-crypto-key-user.txt")[..], &include_bytes!("testdata/line-crypto-session.txt")[..], &include_bytes!("testdata/line-crypto-param-change-user.txt")[..], &include_bytes!("testdata/line-daemon-end-2.txt")[..], &include_bytes!("testdata/line-del-group.txt")[..], &include_bytes!("testdata/line-del-user.txt")[..], &include_bytes!("testdata/line-grp-mgmt.txt")[..], &include_bytes!("testdata/line-software-update.txt")[..], &include_bytes!("testdata/line-user-acct.txt")[..], &include_bytes!("testdata/line-user-auth.txt")[..], &include_bytes!("testdata/line-user-auth-2.txt")[..], &include_bytes!("testdata/line-user-chauthtok.txt")[..], &include_bytes!("testdata/line-user-end.txt")[..], &include_bytes!("testdata/line-user-err.txt")[..], &include_bytes!("testdata/line-user-login.txt")[..], &include_bytes!("testdata/line-user-logout.txt")[..], &include_bytes!("testdata/line-user-mgmt.txt")[..], &include_bytes!("testdata/line-user-role-change.txt")[..], &include_bytes!("testdata/line-user-start.txt")[..], &include_bytes!("testdata/line-usys-config.txt")[..], &include_bytes!("testdata/line-user-avc-1.txt")[..], &include_bytes!("testdata/line-user-avc-2.txt")[..], &include_bytes!("testdata/line-user-selinux-err.txt")[..], ] .iter() .enumerate() { let Message { node: _, ty: _, id, body, } = p.parse(line).unwrap(); println!("test {n}: {id}: {body:?}"); let msg = body .get("msg") .unwrap_or_else(|| panic!("test {n}: {id}: Field msg not found")); match msg { Value::Map(_) => {} Value::Str(_, _) => panic!("test {n}: {id}: Field msg was parsed as string"), _ => panic!("test {n}: {id}: Field msg was parsed as something else"), } } } #[test] fn breakage_sockaddr_unknown() { parse( include_bytes!("testdata/line-sockaddr-unknown-1.txt"), false, ) .expect("can't parse line-sockaddr-unknown-1.txt"); parse( include_bytes!("testdata/line-sockaddr-unknown-2.txt"), false, ) .expect("can't parse line-sockaddr-unknown-2.txt"); parse( include_bytes!("testdata/line-sockaddr-unknown-3.txt"), false, ) .expect("can't parse line-sockaddr-unknown-3.txt"); } #[test] #[cfg(feature = "serde")] fn serde_messagetype() { assert_ser_tokens(&MessageType::SYSCALL, &[Token::String("SYSCALL")]); assert_ser_tokens(&MessageType(20000), &[Token::String("UNKNOWN[20000]")]); } #[test] #[cfg(feature = "serde")] fn serde_key() { for (obj, tok) in &[ (&Key::Name(b"foo"[..].into()), &[Token::String("foo")]), (&Key::NameUID(b"euid"[..].into()), &[Token::String("euid")]), (&Key::NameGID(b"egid"[..].into()), &[Token::String("egid")]), (&Key::Common(Common::Arch), &[Token::String("arch")]), (&Key::Arg(1, None), &[Token::String("a1")]), (&Key::Arg(2, Some(3)), &[Token::String("a2[3]")]), (&Key::ArgLen(2), &[Token::String("a2_len")]), ] { assert_ser_tokens(obj, *tok); assert_de_tokens(*obj, *tok); } for (obj, tok) in &[ ( &Key::NameTranslated(b"foo"[..].into()), &[Token::String("FOO")], ), (&Key::Literal("foo"), &[Token::String("foo")]), ] { assert_ser_tokens(obj, *tok); } } #[test] #[cfg(feature = "serde")] fn serde_value() { for q in &[Quote::None, Quote::Single, Quote::Double] { assert_ser_tokens(&Value::Str(&b"foo"[..], *q), &[Token::Bytes(b"foo")]); } assert_ser_tokens( &Value::Str(&b"foo"[..], Quote::Braces), &[Token::Bytes(b"{foo}")], ); for (obj, tok) in &[ (Value::Empty, &[Token::Unit][..]), (Value::Owned(b"foo".to_vec()), &[Token::Bytes(b"foo")]), (Value::Number(Number::Hex(16)), &[Token::String("0x10")]), (Value::Number(Number::Oct(16)), &[Token::String("0o20")]), (Value::Number(Number::Dec(16)), &[Token::I64(16)]), ( Value::List(vec![]), &[Token::Seq { len: Some(0) }, Token::SeqEnd][..], ), ( Value::List(vec![ Value::Owned(b"foo".to_vec()), Value::Owned(b"bar".to_vec()), Value::Owned(b"baz".to_vec()), Value::from(42), ]), &[ Token::Seq { len: Some(4) }, Token::Bytes(b"foo"), Token::Bytes(b"bar"), Token::Bytes(b"baz"), Token::I64(42), Token::SeqEnd, ][..], ), ( Value::Map(vec![]), &[Token::Map { len: Some(0) }, Token::MapEnd][..], ), ( Value::Map(vec![( Key::Name(b"foo"[..].into()), Value::Owned(b"bar".to_vec()), )]), &[ Token::Map { len: Some(1) }, Token::String("foo"), Token::Bytes(b"bar"), Token::MapEnd, ][..], ), ] { assert_ser_tokens(obj, &tok[..]); assert_de_tokens(obj, &tok[..]); } } #[test] #[cfg(feature = "serde")] fn serde_number() { for (n, t) in &[ (Number::Dec(10), Token::I64(10)), (Number::Hex(0x10), Token::String("0x10")), (Number::Oct(0o10), Token::String("0o10")), ] { assert_ser_tokens(n, &[*t]); assert_de_tokens(n, &[*t]); } assert_de_tokens(&Number::Dec(10), &[Token::U64(10)]); } #[test] #[cfg(feature = "serde")] fn serde_event_id() { let obj = EventID { timestamp: 1615225617302, sequence: 25836, }; let tok = Token::String("1615225617.302:25836"); assert_ser_tokens(&obj, &[tok]); assert_de_tokens(&obj, &[tok]); } #[test] #[cfg(feature = "serde")] fn serde_message_type() { for (m, t) in &[ (MessageType::SYSCALL, Token::String("SYSCALL")), (MessageType(9999), Token::String("UNKNOWN[9999]")), ] { assert_ser_tokens(m, &[*t]); assert_de_tokens(m, &[*t]); } } #[test] #[cfg(feature = "serde")] fn serde_message() { let msg = parse(include_bytes!("testdata/line-eoe.txt"), false).unwrap(); assert_ser_tokens(&msg.body, &[Token::Map { len: Some(0) }, Token::MapEnd]); let msg = parse(include_bytes!("testdata/line-execve.txt"), false).unwrap(); assert_ser_tokens( &msg.body, &[ Token::Map { len: Some(2) }, Token::String("argc"), Token::I64(0), Token::String("a0"), Token::Bytes(b"whoami"), Token::MapEnd, ], ); } #[test] fn parse_uringop() { let msg = parse(include_bytes!("testdata/line-uringop.txt"), false).unwrap(); println!("{msg:?}"); let v = msg .body .get("uring_op") .unwrap_or_else(|| panic!("{}: uring_op not found", msg.id)); assert_eq!(*v, Value::Number(Number::Dec(18))); } #[test] fn parse_bpf() { let msg = parse(include_bytes!("testdata/line-bpf.txt"), false).unwrap(); println!("{msg:?}"); let v = msg .body .get("prog-id") .unwrap_or_else(|| panic!("{}: prog-id not found", msg.id)); assert_eq!(*v, Value::Number(Number::Dec(75))); } #[test] fn special() { Parser { enriched: false, split_msg: false, } .parse(&include_bytes!("testdata/line-daemon-start.txt")[..]) .unwrap_or_else(|e| panic!("{e}")); } linux-audit-parser-0.2.7/src/testdata/line-acct-lock.txt000064400000000000000000000003431046102023000213150ustar 00000000000000type=ACCT_LOCK msg=audit(1725000411.409:1065): pid=2352 uid=0 auid=4294967295 ses=4294967295 msg='op=locked-password id=1000 exe="/usr/bin/passwd" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" ID="ec2-user" linux-audit-parser-0.2.7/src/testdata/line-add-group.txt000064400000000000000000000003371046102023000213420ustar 00000000000000type=ADD_GROUP msg=audit(1724970920.775:6704): pid=15618 uid=0 auid=4294967295 ses=4294967295 msg='op=add-group acct="gitlab-runner" exe="/usr/sbin/useradd" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" linux-audit-parser-0.2.7/src/testdata/line-add-user.txt000064400000000000000000000003431046102023000211610ustar 00000000000000type=ADD_USER msg=audit(1724970920.775:6705): pid=15618 uid=0 auid=4294967295 ses=4294967295 msg='op=add-user id=1501 exe="/usr/sbin/useradd" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" ID="unknown(1501)" linux-audit-parser-0.2.7/src/testdata/line-anom-abend-2.txt000064400000000000000000000003501046102023000216130ustar 00000000000000type=ANOM_ABEND msg=audit(1703677054.334:4223663): auid=4294967295 uid=0 gid=0 ses=4294967295 subj==/usr/bin/man//&man_groff (enforce) pid=109919 comm="preconv" exe="/usr/bin/preconv" sig=31 res=1AUID="unset" UID="root" GID="root" linux-audit-parser-0.2.7/src/testdata/line-anom-abend.txt000064400000000000000000000003051046102023000214540ustar 00000000000000type=ANOM_ABEND msg=audit(1633653915.934:123): auid=4294967295 uid=1000 gid=1000 ses=4294967295 pid=1000 comm="ftptls" reason="memory violation" sig=6AUID="unset" UID="some-user" GID="some-group" linux-audit-parser-0.2.7/src/testdata/line-avc-denied.txt000064400000000000000000000003331046102023000214530ustar 00000000000000type=AVC msg=audit(1631798689.083:65686): avc: denied { setuid } for pid=15381 comm="laurel" capability=7 scontext=system_u:system_r:auditd_t:s0 tcontext=system_u:system_r:auditd_t:s0 tclass=capability permissive=1 linux-audit-parser-0.2.7/src/testdata/line-avc-granted.txt000064400000000000000000000003031046102023000216440ustar 00000000000000type=AVC msg=audit(1631870323.500:7098): avc: granted { setsecparam } for pid=11209 comm="tuned" scontext=system_u:system_r:tuned_t:s0 tcontext=system_u:object_r:security_t:s0 tclass=security linux-audit-parser-0.2.7/src/testdata/line-bpf.txt000064400000000000000000000001001046102023000202130ustar 00000000000000type=BPF msg=audit(1737533267.765:12263987): prog-id=75 op=LOAD linux-audit-parser-0.2.7/src/testdata/line-broken-avc-info.txt000064400000000000000000000003401046102023000224320ustar 00000000000000type=AVC msg=audit(1634728455.294:53732): apparmor="STATUS" operation="profile_replace" info="same as current profile, skipping" profile="unconfined" name="snap-update-ns.amazon-ssm-agent" pid=3981295 comm="apparmor_parser" linux-audit-parser-0.2.7/src/testdata/line-broken-subj1.txt000064400000000000000000000005361046102023000217630ustar 00000000000000type=SYSCALL msg=audit(1634628127.584:166): arch=c000003e syscall=59 success=yes exit=0 a0=55b26d44a6a0 a1=55b26d44a878 a2=55b26d44a8e8 a3=7faeccab5850 items=2 ppid=659 pid=661 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="dhclient" exe="/sbin/dhclient" subj=/{,usr/}sbin/dhclient key=(null) linux-audit-parser-0.2.7/src/testdata/line-broken-subj2.txt000064400000000000000000000005271046102023000217640ustar 00000000000000type=SYSCALL msg=audit(1634623555.431:13835339): arch=c000003e syscall=49 success=yes exit=0 a0=15 a1=55c5e046e264 a2=1c a3=7ffc8fab77ec items=0 ppid=1899774 pid=1899780 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="ntpd" exe="/usr/sbin/ntpd" subj==/usr/sbin/ntpd (enforce) key=(null) linux-audit-parser-0.2.7/src/testdata/line-chgrp-id.txt000064400000000000000000000003671046102023000211600ustar 00000000000000type=CHGRP_ID msg=audit(1723948962.207:28220): pid=11159 uid=0 auid=4294967295 ses=4294967295 msg='op=changing new_gid=4550 id=0 exe="/usr/bin/newgrp" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" NEW_GID="somegroup" ID="root" linux-audit-parser-0.2.7/src/testdata/line-cred-acq.txt000064400000000000000000000003641046102023000211370ustar 00000000000000type=CRED_ACQ msg=audit(1725004861.996:747421): pid=26752 uid=0 auid=4294967295 ses=4294967295 msg='op=PAM:setcred grantors=pam_env,pam_unix acct="root" exe="/usr/sbin/crond" hostname=? addr=? terminal=cron res=success'UID="root" AUID="unset" linux-audit-parser-0.2.7/src/testdata/line-cred-disp.txt000064400000000000000000000003411046102023000213250ustar 00000000000000type=CRED_DISP msg=audit(1725004605.819:105665): pid=14548 uid=0 auid=4294967295 ses=4294967295 msg='op=PAM:setcred grantors=pam_env,pam_unix acct="root" exe="/usr/bin/sudo" hostname=? addr=? terminal=/dev/pts/0 res=success' linux-audit-parser-0.2.7/src/testdata/line-cred-refr.txt000064400000000000000000000003411046102023000213240ustar 00000000000000type=CRED_REFR msg=audit(1725004605.807:105662): pid=14548 uid=0 auid=4294967295 ses=4294967295 msg='op=PAM:setcred grantors=pam_env,pam_unix acct="root" exe="/usr/bin/sudo" hostname=? addr=? terminal=/dev/pts/0 res=success' linux-audit-parser-0.2.7/src/testdata/line-crypto-key-user.txt000064400000000000000000000005051046102023000225370ustar 00000000000000type=CRYPTO_KEY_USER msg=audit(1724970643.372:107): pid=2751 uid=0 auid=4294967295 ses=4294967295 msg='op=destroy kind=server fp=SHA256:02:11:7b:31:4b:de:6c:32:85:19:91:e2:22:6d:8e:c6:73:8c:8f:72:2f:c3:36:bd:55:65:23:de:85:a6:ce:13 direction=? spid=2751 suid=0 exe="/usr/sbin/sshd" hostname=? addr=? terminal=? res=success' linux-audit-parser-0.2.7/src/testdata/line-crypto-param-change-user.txt000064400000000000000000000004011046102023000242650ustar 00000000000000type=CRYPTO_PARAM_CHANGE_USER msg=audit(1720042308.296:1823003): pid=2640 uid=0 auid=4294967295 ses=4294967295 msg='op=prng_seed kind=server bytes=6 source=/dev/urandom exe="/usr/sbin/sshd" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" linux-audit-parser-0.2.7/src/testdata/line-crypto-session.txt000064400000000000000000000005421046102023000224570ustar 00000000000000type=CRYPTO_SESSION msg=audit(1724778544.061:594047): pid=21702 uid=0 auid=4294967295 ses=4294967295 msg='op=start direction=from-client cipher=chacha20-poly1305@openssh.com ksize=512 mac= pfs=ecdh-sha2-nistp521 spid=21704 suid=74 rport=46984 laddr=10.10.10.10 lport=22 exe="/usr/sbin/sshd" hostname=? addr=10.11.11.11 terminal=? res=success' linux-audit-parser-0.2.7/src/testdata/line-daemon-end-2.txt000064400000000000000000000005161046102023000216250ustar 00000000000000type=DAEMON_END msg=audit(1724970459.096:8216): op=terminate auid=0 pid=12333 subj=24970459.090:670): pid=12323 uid=0 auid=4294967295 ses=4294967295 msg='op=PAM:session_open grantors=pam_keyinit,pam_keyinit,pam_limits,pam_systemd,pam_unix acct="root" exe="/usr/bin/sudo" hostname=? addr=? terminal=/dev/pts/0 res=success' res=success linux-audit-parser-0.2.7/src/testdata/line-daemon-end.txt000064400000000000000000000001541046102023000214640ustar 00000000000000type=DAEMON_END msg=audit(1640080836.094:7063): op=terminate auid=0 pid=27347 subj= res=successAUID="root" linux-audit-parser-0.2.7/src/testdata/line-daemon-start.txt000064400000000000000000000003301046102023000220470ustar 00000000000000type=DAEMON_START msg=audit(1738069334.056:5999): op=start ver=2.8.1 format=enriched kernel=4.12.14-122.231-default auid=4294967295 pid=32004 uid=0 ses=4294967295 subj=unconfined res=successAUID="unset" UID="root" linux-audit-parser-0.2.7/src/testdata/line-del-group.txt000064400000000000000000000003561046102023000213570ustar 00000000000000type=DEL_GROUP msg=audit(1724970804.251:2683): pid=13860 uid=0 auid=4294967295 ses=4294967295 msg='op=delete-group grp="nfsnobody" acct="nfsnobody" exe="/usr/sbin/userdel" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" linux-audit-parser-0.2.7/src/testdata/line-del-user.txt000064400000000000000000000003431046102023000211750ustar 00000000000000type=DEL_USER msg=audit(1724970804.251:2682): pid=13860 uid=0 auid=4294967295 ses=4294967295 msg='op=delete-user id=65534 exe="/usr/sbin/userdel" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" ID="nfsnobody" linux-audit-parser-0.2.7/src/testdata/line-eoe.txt000064400000000000000000000000521046102023000202220ustar 00000000000000type=EOE msg=audit(1615225617.302:25836): linux-audit-parser-0.2.7/src/testdata/line-execve.txt000064400000000000000000000001001046102023000207230ustar 00000000000000type=EXECVE msg=audit(1614788539.386:13232): argc=0 a0="whoami" linux-audit-parser-0.2.7/src/testdata/line-grp-mgmt.txt000064400000000000000000000003441046102023000212100ustar 00000000000000type=GRP_MGMT msg=audit(1724970841.047:3517): pid=14406 uid=0 auid=4294967295 ses=4294967295 msg='op=add-shadow-group id=990 exe="/usr/sbin/groupadd" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" ID="docker" linux-audit-parser-0.2.7/src/testdata/line-mac-policy-load.txt000064400000000000000000000001631046102023000224270ustar 00000000000000type=MAC_POLICY_LOAD msg=audit(1670142818.140:74058301): policy loaded auid=4294967295 ses=4294967295AUID="unset" linux-audit-parser-0.2.7/src/testdata/line-netfilter.txt000064400000000000000000000002201046102023000214430ustar 00000000000000type=NETFILTER_CFG msg=audit(1643035021.052:428): table=?:0;?:0 family=0 entries=2 op=nft_register_gen pid=1027 subj=unconfined comm="ebtables" linux-audit-parser-0.2.7/src/testdata/line-netlabel.txt000064400000000000000000000001521046102023000212410ustar 00000000000000type=MAC_UNLBL_ALLOW msg=audit(1631783567.248:3): netlabel: auid=0 ses=0 unlbl_accept=1 old=0AUID="root" linux-audit-parser-0.2.7/src/testdata/line-path-enriched.txt000064400000000000000000000003761046102023000221760ustar 00000000000000type=PATH msg=audit(1615113648.978:15219): item=1 name="/lib64/ld-linux-x86-64.so.2" inode=262146 dev=ca:03 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0OUID="root" OGID="root" linux-audit-parser-0.2.7/src/testdata/line-path.txt000064400000000000000000000003441046102023000204120ustar 00000000000000node=work type=PATH msg=audit(1614788539.386:13232): item=0 name="/usr/bin/whoami" inode=261214 dev=ca:03 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0 linux-audit-parser-0.2.7/src/testdata/line-sockaddr-unix-2.txt000064400000000000000000000005331046102023000223700ustar 00000000000000type=SOCKADDR msg=audit(1703653288.035:118019478): saddr=01002F746D702F7B39653263623038372D393734342D343137622D383435662D3035636136636534353763317D0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000SADDR={ fam=local path=/tmp/{9e2cb087-9744-417b-845f-05ca6ce457c1} } linux-audit-parser-0.2.7/src/testdata/line-sockaddr-unix.txt000064400000000000000000000003271046102023000222320ustar 00000000000000type=SOCKADDR msg=audit(1670486666.214:1232): saddr=01002F746D702F2E7B46443244443844342D463641412D344437342D413645312D4145464142313833444545427DSADDR={ fam=local path=/tmp/.{FD2DD8D4-F6AA-4D74-A6E1-AEFAB183DEEB} } linux-audit-parser-0.2.7/src/testdata/line-sockaddr-unknown-1.txt000064400000000000000000000001511046102023000230770ustar 00000000000000type=SOCKADDR msg=audit(1670427457.195:550): saddr=00000000000000000000000000000000SADDR=unknown family linux-audit-parser-0.2.7/src/testdata/line-sockaddr-unknown-2.txt000064400000000000000000000001601046102023000231000ustar 00000000000000type=SOCKADDR msg=audit(1709205350.768:2195413): saddr=00000000000000000000000000000000SADDR=unknown family(0) linux-audit-parser-0.2.7/src/testdata/line-sockaddr-unknown-3.txt000064400000000000000000000001571046102023000231070ustar 00000000000000type=SOCKADDR msg=audit(1709205499.986:983151): saddr=00000000000000000000000000000000SADDR=unknown-family(0) linux-audit-parser-0.2.7/src/testdata/line-software-update.txt000064400000000000000000000005341046102023000225710ustar 00000000000000type=SOFTWARE_UPDATE msg=audit(1724947731.641:5836352): pid=3312968 uid=0 auid=4294967295 ses=4294967295 subj=kernel msg='op=install sw="yum-utils-4.0.21-25.el8.noarch" sw_type=rpm key_enforce=0 gpg_res=1 root_dir="/" comm="yum" exe="/usr/libexec/platform-python3.6" hostname=8da7ccbba3a1 addr=? terminal=pts/0 res=success'UID="root" AUID="unset" linux-audit-parser-0.2.7/src/testdata/line-syscall.txt000064400000000000000000000007021046102023000211260ustar 00000000000000type=SYSCALL msg=audit(1615114232.375:15558): arch=c000003e syscall=59 success=yes exit=0 a0=63b29337fd18 a1=63b293387d58 a2=63b293375640 a3=fffffffffffff000 items=2 ppid=10883 pid=10884 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm="whoami" exe="/usr/bin/whoami" key=(null)ARCH=x86_64 SYSCALL=execve AUID="user" UID="root" GID="root" EUID="root" SUID="root" FSUID="root" EGID="root" SGID="root" FSGID="root" linux-audit-parser-0.2.7/src/testdata/line-tty.txt000064400000000000000000000003071046102023000202750ustar 00000000000000type=TTY msg=audit(1702500511.459:370948): tty pid=260398 uid=0 auid=4294967295 ses=4294967295 major=136 minor=1 comm="bash" data=72706D202D7161207C2067726570207379736C6F670DUID="root" AUID="unset" linux-audit-parser-0.2.7/src/testdata/line-unknown.txt000064400000000000000000000001121046102023000211460ustar 00000000000000type=UNKNOWN[1334] msg=audit(1626883065.201:216697): prog-id=45 op=UNLOAD linux-audit-parser-0.2.7/src/testdata/line-uringop.txt000064400000000000000000000003031046102023000211340ustar 00000000000000type=URINGOP msg=audit(1737533617.373:12266329): uring_op=18 success=yes exit=0 items=0 ppid=140504 pid=3178806 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 subj=unconfined key=(null) linux-audit-parser-0.2.7/src/testdata/line-user-acct.txt000064400000000000000000000003471046102023000213470ustar 00000000000000type=USER_ACCT msg=audit(1615113648.981:15220): pid=9460 uid=1000 auid=1000 ses=1 msg='op=PAM:accounting grantors=pam_permit acct="user" exe="/usr/bin/sudo" hostname=? addr=? terminal=/dev/pts/1 res=success'UID="user" AUID="user" linux-audit-parser-0.2.7/src/testdata/line-user-auth-2.txt000064400000000000000000000004061046102023000215310ustar 00000000000000type=USER_AUTH msg=audit(1670330949.860:161339): pid=5519 uid=0 auid=4294967295 ses=4294967295 msg='op=PAM:authentication grantors=? acct="system-property('xsl:vendor')/>" exe="/usr/bin/python3.8" hostname=? addr=? terminal=? res=failed'UID="root" AUID="unset" linux-audit-parser-0.2.7/src/testdata/line-user-auth.txt000064400000000000000000000004431046102023000213730ustar 00000000000000type=USER_AUTH msg=audit(1670424651.175:10465161): pid=1932610 uid=0 auid=4294967295 ses=4294967295 subj=/usr/sbin/cupsd (enforce) msg='op=PAM:authentication grantors=pam_permit acct="user" exe="/usr/sbin/cupsd" hostname=localhost addr=::1 terminal=cups res=success'UID="root" AUID="unset" linux-audit-parser-0.2.7/src/testdata/line-user-avc-1.txt000064400000000000000000000005171046102023000213430ustar 00000000000000type=USER_AVC msg=audit(1725140575.109:3128): pid=854 uid=81 auid=4294967295 ses=4294967295 subj=system_u:system_r:system_dbusd_t:s0-s0:c0.c1023 msg='avc: received policyload notice (seqno=2) exe=2F7573722F62696E2F646275732D6461656D6F6E202864656C6574656429 sauid=81 hostname=? addr=? terminal=?'UID="dbus" AUID="unset" SAUID="dbus" linux-audit-parser-0.2.7/src/testdata/line-user-avc-2.txt000064400000000000000000000004571046102023000213470ustar 00000000000000type=USER_AVC msg=audit(1725042134.780:89813047): pid=1183 uid=81 auid=4294967295 ses=4294967295 subj=system_u:system_r:system_dbusd_t:s0-s0:c0.c1023 msg='avc: received policyload notice (seqno=96) exe="/usr/bin/dbus-daemon" sauid=81 hostname=? addr=? terminal=?'UID="dbus" AUID="unset" SAUID="dbus" linux-audit-parser-0.2.7/src/testdata/line-user-chauthtok.txt000064400000000000000000000003671046102023000224310ustar 00000000000000type=USER_CHAUTHTOK msg=audit(1725004607.540:730609): pid=393655 uid=0 auid=4294967295 ses=4294967295 subj=unconfined msg='op=display aging info id=0 exe="/usr/bin/chage" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" ID="root" linux-audit-parser-0.2.7/src/testdata/line-user-end.txt000064400000000000000000000004151046102023000211770ustar 00000000000000type=USER_END msg=audit(1725004605.819:105664): pid=14548 uid=0 auid=4294967295 ses=4294967295 msg='op=PAM:session_close grantors=pam_keyinit,pam_keyinit,pam_limits,pam_systemd,pam_unix acct="root" exe="/usr/bin/sudo" hostname=? addr=? terminal=/dev/pts/0 res=success' linux-audit-parser-0.2.7/src/testdata/line-user-err.txt000064400000000000000000000003741046102023000212250ustar 00000000000000type=USER_ERR msg=audit(1724985521.124:28189957): pid=164518 uid=0 auid=4294967295 ses=4294967295 msg='op=PAM:bad_ident grantors=? acct="?" exe="/usr/sbin/sshd" hostname=10.105.231.56 addr=10.105.231.56 terminal=ssh res=failed'UID="root" AUID="unset" linux-audit-parser-0.2.7/src/testdata/line-user-login.txt000064400000000000000000000003561046102023000215450ustar 00000000000000type=USER_LOGIN msg=audit(1725003193.327:117286): pid=1712 uid=0 auid=1000 ses=764 msg='op=login id=1000 exe="/usr/sbin/sshd" hostname=other-host.invalid addr=10.10.10.10 terminal=ssh res=success'UID="root" AUID="ec2-user" ID="ec2-user" linux-audit-parser-0.2.7/src/testdata/line-user-logout.txt000064400000000000000000000003571046102023000217470ustar 00000000000000type=USER_LOGOUT msg=audit(1725003193.331:117292): pid=1712 uid=0 auid=1000 ses=764 msg='op=login id=1000 exe="/usr/sbin/sshd" hostname=other-host.invalid addr=10.10.10.10 terminal=ssh res=success'UID="root" AUID="ec2-user" ID="ec2-user" linux-audit-parser-0.2.7/src/testdata/line-user-mgmt.txt000064400000000000000000000003231046102023000213730ustar 00000000000000type=USER_MGMT msg=audit(1725003240.604:28251307): pid=18970 uid=0 auid=4294967295 ses=4294967295 msg='op=change-age acct="" exe="/usr/bin/chage" hostname=? addr=? terminal=? res=failed'UID="root" AUID="unset" linux-audit-parser-0.2.7/src/testdata/line-user-role-change.txt000064400000000000000000000006161046102023000226200ustar 00000000000000type=USER_ROLE_CHANGE msg=audit(1725003303.447:87595829): pid=3224193 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='op=pam_selinux default-context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 selected-context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" linux-audit-parser-0.2.7/src/testdata/line-user-selinux-err.txt000064400000000000000000000003661046102023000227130ustar 00000000000000type=USER_SELINUX_ERR msg=audit(1720378109.983:2630177): pid=1572772 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='avc: netlink recvfrom: error 9 exe="/usr/lib/systemd/systemd" sauid=0 hostname=? addr=? terminal=?' linux-audit-parser-0.2.7/src/testdata/line-user-start.txt000064400000000000000000000004161046102023000215670ustar 00000000000000type=USER_START msg=audit(1725004605.811:105663): pid=14548 uid=0 auid=4294967295 ses=4294967295 msg='op=PAM:session_open grantors=pam_keyinit,pam_keyinit,pam_limits,pam_systemd,pam_unix acct="root" exe="/usr/bin/sudo" hostname=? addr=? terminal=/dev/pts/0 res=success' linux-audit-parser-0.2.7/src/testdata/line-usys-config.txt000064400000000000000000000003221046102023000217200ustar 00000000000000type=USYS_CONFIG msg=audit(1724337072.493:467): pid=1876 uid=0 auid=4294967295 ses=4294967295 msg='op=change-system-time exe="/usr/sbin/hwclock" hostname=? addr=? terminal=? res=failed'UID="root" AUID="unset" linux-audit-parser-0.2.7/src/value.rs000064400000000000000000000351171046102023000156470ustar 00000000000000use std::convert::{Into, TryFrom}; use std::fmt::{self, Debug, Display}; use std::iter::Iterator; use std::str::{self, FromStr}; use std::string::*; #[cfg(feature = "serde")] use serde::{ de::{self, MapAccess, SeqAccess, Visitor}, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer, }; use crate::*; /// Quotes types in [`Value`] strings #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Quote { None, Single, Double, Braces, } #[derive(Clone, PartialEq)] /// [`Value`]s parsed as hexadecimal, decimal, or octal numbers pub enum Number { Hex(u64), Dec(i64), Oct(u64), } impl Debug for Number { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Num:<{self}>") } } impl Display for Number { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Number::Hex(n) => write!(f, "0x{n:x}"), Number::Oct(n) => write!(f, "0o{n:o}"), Number::Dec(n) => write!(f, "{n}"), } } } impl FromStr for Number { type Err = std::num::ParseIntError; fn from_str(s: &str) -> Result { if let Some(s) = s.strip_prefix("0x") { Ok(Number::Hex(u64::from_str_radix(s, 16)?)) } else if let Some(s) = s.strip_prefix("0o") { Ok(Number::Oct(u64::from_str_radix(s, 8)?)) } else { Ok(Number::Dec(i64::from_str(s)?)) } } } #[cfg(feature = "serde")] impl Serialize for Number { #[inline(always)] fn serialize(&self, s: S) -> Result { match self { Number::Dec(n) => s.serialize_i64(*n), _ => s.collect_str(&self), } } } #[cfg(feature = "serde")] impl<'de> Deserialize<'de> for Number { fn deserialize>(d: D) -> Result { d.deserialize_any(NumberVisitor) } } #[cfg(feature = "serde")] struct NumberVisitor; #[cfg(feature = "serde")] impl Visitor<'_> for NumberVisitor { type Value = Number; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a string or an integer") } fn visit_i64(self, value: i64) -> Result { Ok(Number::Dec(value)) } fn visit_u64(self, value: u64) -> Result { Ok(Number::Dec(value as _)) } fn visit_str(self, value: &str) -> Result { Number::from_str(value).map_err(E::custom) } } /// Representation of the value part of key/value pairs in [`Body`] #[derive(Clone, PartialEq)] pub enum Value<'a> { /// Empty value. Empty, /// A byte string. Str(&'a [u8], Quote), /// Parsed number. Number(Number), /// A list of byte strings. List(Vec>), /// A byte string that is not stored within the [`Body`]. Used for /// decoded hex-strings. Owned(Vec), /// An internal key/value map. Used when [`Parser::split_msg`] is set. Map(Vec<(Key, Value<'a>)>), /// Non-contiguous byte string. Not produced by the parser. Segments(Vec<&'a [u8]>), StringifiedList(Vec>), /// Elements removed from ARGV lists. Not produced by the parser. Skipped((usize, usize)), /// A literal string. Not produced by the parser. Literal(&'static str), } impl Default for Value<'_> { fn default() -> Self { Self::Empty } } impl Value<'_> { pub fn str_len(&self) -> usize { match self { Value::Str(r, _) => r.len(), Value::Segments(vr) => vr.iter().map(|r| r.len()).sum(), _ => 0, } } } impl TryFrom> for Vec { type Error = &'static str; fn try_from(v: Value) -> Result { match v { Value::Str(r, Quote::Braces) => { let mut s = Vec::with_capacity(r.len() + 2); s.push(b'{'); s.extend(Vec::from(r)); s.push(b'}'); Ok(s) } Value::Str(r, _) => Ok(Vec::from(r)), Value::Empty => Ok("".into()), Value::Segments(ranges) => { let l = ranges.iter().map(|r| r.len()).sum(); let mut sb = Vec::with_capacity(l); for r in ranges { sb.extend(Vec::from(r)); } Ok(sb) } Value::Number(_) => Err("Won't convert number to string"), Value::List(_) | Value::StringifiedList(_) => Err("Can't convert list to scalar"), Value::Map(_) => Err("Can't convert map to scalar"), Value::Skipped(_) => Err("Can't convert skipped to scalar"), Value::Literal(s) => Ok(s.to_string().into()), Value::Owned(v) => Ok(v), } } } impl TryFrom> for Vec> { type Error = &'static str; fn try_from(value: Value) -> Result { match value { Value::List(values) | Value::StringifiedList(values) => { let mut rv = Vec::with_capacity(values.len()); for v in values { let s = Vec::try_from(v)?; rv.push(s); } Ok(rv) } _ => Err("not a list"), } } } impl Debug for Value<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Value::Str(r, _q) => write!(f, "Str:<{}>", &String::from_utf8_lossy(r)), Value::Empty => write!(f, "Empty"), Value::Segments(segs) => { write!(f, "Segments<")?; for (n, r) in segs.iter().enumerate() { if n > 0 { write!(f, ", ")?; } write!(f, "{}", String::from_utf8_lossy(r))?; } write!(f, ">") } Value::List(vs) => { write!(f, "List:<")?; for (n, v) in vs.iter().enumerate() { if n > 0 { write!(f, ", ")?; } match v { Value::Str(r, _) => { write!(f, "{}", String::from_utf8_lossy(r))?; } Value::Segments(rs) => { for r in rs { write!(f, "{}", String::from_utf8_lossy(r))?; } } Value::Number(n) => write!(f, "{n:?}")?, Value::Skipped((elems, bytes)) => { write!(f, "Skip")?; } Value::Empty => panic!("list can't contain empty value"), Value::List(_) | Value::StringifiedList(_) => { panic!("list can't contain list") } Value::Map(_) => panic!("list can't contain map"), Value::Literal(v) => write!(f, "{v:?}")?, Value::Owned(v) => write!(f, "{}", String::from_utf8_lossy(v))?, } } write!(f, ">") } Value::StringifiedList(vs) => { write!(f, "StringifiedList:<")?; for (n, v) in vs.iter().enumerate() { if n > 0 { write!(f, " ")?; } match v { Value::Str(r, _) => { write!(f, "{}", String::from_utf8_lossy(r))?; } Value::Segments(rs) => { for r in rs { write!(f, "{}", String::from_utf8_lossy(r))?; } } Value::Number(n) => write!(f, "{n:?}")?, Value::Skipped((elems, bytes)) => { write!(f, "Skip")?; } Value::Empty => panic!("list can't contain empty value"), Value::List(_) | Value::StringifiedList(_) => { panic!("list can't contain list") } Value::Map(_) => panic!("List can't contain mapr"), Value::Literal(v) => write!(f, "{v}")?, Value::Owned(v) => write!(f, "{}", String::from_utf8_lossy(v))?, } } write!(f, ">") } Value::Map(vs) => { write!(f, "Map:<")?; for (n, (k, v)) in vs.iter().enumerate() { if n > 0 { write!(f, " ")?; } write!(f, "{k:?}={v:?}")?; } write!(f, ">") } Value::Number(n) => write!(f, "{n:?}"), Value::Skipped((elems, bytes)) => write!(f, "Skip"), Value::Literal(s) => write!(f, "{s:?}"), Value::Owned(v) => write!(f, "{}", String::from_utf8_lossy(v)), } } } #[cfg(feature = "serde")] impl Serialize for Value<'_> { #[inline(always)] fn serialize(&self, s: S) -> Result { match self { Value::Empty => s.serialize_unit(), Value::Str(r, Quote::Braces) => { let mut buf = Vec::with_capacity(r.len() + 2); buf.push(b'{'); buf.extend(*r); buf.push(b'}'); s.serialize_bytes(&buf) } Value::Str(r, _) => s.serialize_bytes(r), Value::Segments(segs) => { let l = segs.iter().map(|r| r.len()).sum(); let mut buf = Vec::with_capacity(l); for seg in segs { buf.extend(*seg); } s.serialize_bytes(&buf) } Value::List(vs) => s.collect_seq(vs.iter()), Value::StringifiedList(vs) => { let mut buf: Vec = Vec::with_capacity(vs.len()); let mut first = true; for v in vs { if first { first = false; } else { buf.push(b' '); } if let Value::Skipped((args, bytes)) = v { buf.extend(format!("<<< Skipped: args={args}, bytes={bytes} >>>").bytes()); } else { buf.extend(v.clone().try_into().unwrap_or_else(|_| vec![b'x'])); } } s.serialize_bytes(&buf) } Value::Number(n) => n.serialize(s), Value::Map(vs) => s.collect_map(vs.iter().cloned()), Value::Skipped((args, bytes)) => { let mut map = s.serialize_map(Some(2))?; map.serialize_entry("skipped_args", args)?; map.serialize_entry("skipped_bytes", bytes)?; map.end() } Value::Literal(v) => s.collect_str(v), Value::Owned(v) => s.serialize_bytes(v), } } } #[cfg(feature = "serde")] struct ValueVisitor; #[cfg(feature = "serde")] impl<'de> Visitor<'de> for ValueVisitor { type Value = Value<'de>; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a string, integer, sequence, map, or null value") } fn visit_none(self) -> Result { Ok(Value::Empty) } fn visit_unit(self) -> Result { Ok(Value::Empty) } fn visit_i64(self, value: i64) -> Result { Ok(Value::Number(Number::Dec(value))) } fn visit_u64(self, value: u64) -> Result { Ok(Value::Number(Number::Dec(value as _))) } fn visit_str(self, value: &str) -> Result { if let Ok(n) = Number::from_str(value) { Ok(Value::Number(n)) } else { Ok(Value::from(value.to_string())) } } fn visit_bytes(self, value: &[u8]) -> Result { Ok(Value::from(value.to_vec())) } fn visit_seq>(self, mut seq: A) -> Result { let mut v = vec![]; while let Some(elem) = seq.next_element()? { v.push(elem); } Ok(Value::List(v)) } fn visit_map>(self, mut map: A) -> Result { let mut kv = vec![]; while let Some((k, v)) = map.next_entry::()? { kv.push((k, v)); } Ok(Value::Map(kv)) } } #[cfg(feature = "serde")] impl<'de> Deserialize<'de> for Value<'de> { #[inline(always)] fn deserialize>(d: D) -> Result { d.deserialize_any(ValueVisitor) } } impl PartialEq for Value<'_> { fn eq(&self, other: &str) -> bool { self == other.as_bytes() } } impl PartialEq<[u8]> for Value<'_> { fn eq(&self, other: &[u8]) -> bool { match self { Value::Empty => other.is_empty(), Value::Str(r, _) => r == &other, Value::Segments(segs) => { let l = segs.iter().map(|s| s.len()).sum(); let mut buf: Vec = Vec::with_capacity(l); for s in segs { buf.extend(*s); } buf == other } Value::Literal(s) => s.as_bytes() == other, Value::Owned(v) => v == other, Value::List(_) | Value::StringifiedList(_) | Value::Map(_) | Value::Skipped(_) | Value::Number(_) => false, } } } impl<'a> From<&'a [u8]> for Value<'a> { fn from(value: &'a [u8]) -> Self { Value::Str(value, Quote::None) } } impl<'a> From<&'a str> for Value<'a> { fn from(value: &'a str) -> Self { Self::from(value.as_bytes()) } } impl From> for Value<'_> { fn from(value: Vec) -> Self { Value::Owned(value) } } impl From for Value<'_> { fn from(value: String) -> Self { Self::from(Vec::from(value)) } } impl From for Value<'_> { fn from(value: i64) -> Self { Value::Number(Number::Dec(value)) } }