netlink-packet-audit-0.5.1/.cargo_vcs_info.json0000644000000001360000000000100150540ustar { "git": { "sha1": "d75719be5f3d31ceb02950ce3857f984e3dda0d4" }, "path_in_vcs": "" }netlink-packet-audit-0.5.1/.github/workflows/clippy-rustfmt.yml000064400000000000000000000011021046102023000227600ustar 00000000000000name: Rustfmt and clippy check on: pull_request: types: [opened, synchronize, reopened] push: branches: - main jobs: rustfmt_clippy: strategy: fail-fast: true name: Rustfmt and clippy check runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install Rust Nightly run: | rustup override set nightly rustup update nightly rustup component add rustfmt clippy - name: rustfmt run: cargo fmt --all -- --check - name: clippy run: cargo clippy netlink-packet-audit-0.5.1/.github/workflows/license.yml000064400000000000000000000005201046102023000214030ustar 00000000000000name: license on: pull_request: types: [opened, synchronize, reopened] push: branches: - main jobs: check-license: name: Check License runs-on: ubuntu-latest timeout-minutes: 3 steps: - uses: actions/checkout@v3 - name: Check License Header uses: apache/skywalking-eyes@v0.3.0 netlink-packet-audit-0.5.1/.github/workflows/main.yml000064400000000000000000000013731046102023000207140ustar 00000000000000name: CI on: pull_request: types: [opened, synchronize, reopened] push: branches: - main jobs: ci: name: CI (stable) runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install Rust Stable run: | rustup override set stable rustup update stable rustup component add llvm-tools-preview - name: Install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov - name: Test and Generate code coverage run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: files: lcov.info fail_ci_if_error: false netlink-packet-audit-0.5.1/.gitignore000064400000000000000000000000411046102023000156270ustar 00000000000000Cargo.lock target vendor/ *.swp netlink-packet-audit-0.5.1/.licenserc.yaml000064400000000000000000000003711046102023000165560ustar 00000000000000header: license: content: | SPDX-License-Identifier: MIT paths-ignore: - 'target' - '**/*.toml' - '**/*.lock' - '**/*.yml' - '**/*.md' - 'CHANGELOG' - 'LICENSE-MIT' - '.gitignore' comment: on-failure netlink-packet-audit-0.5.1/.rustfmt.toml000064400000000000000000000001141046102023000163170ustar 00000000000000max_width = 80 wrap_comments = true reorder_imports = true edition = "2021" netlink-packet-audit-0.5.1/CHANGELOG000064400000000000000000000006361046102023000150630ustar 00000000000000# Changelog ## [0.5.1] - 2023-07-10 ### Breaking changes - N/A ### New features - N/A ### Bug fixes - Use latest rust-netlink crates. (d183103) ## [0.5.0] - 2023-01-28 ### Breaking changes - All public struct and enum are marked as `non_exhaustive`. Please check https://doc.rust-lang.org/reference/attributes/type_system.html for more detail. (5660d73) ### New features - N/A ### Bug fixes - N/A netlink-packet-audit-0.5.1/Cargo.toml0000644000000023310000000000100130510ustar # 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 = "2018" name = "netlink-packet-audit" version = "0.5.1" authors = ["Corentin Henry "] description = "netlink packet types" homepage = "https://github.com/rust-netlink/netlink-packet-audit" readme = "README.md" keywords = [ "netlink", "linux", ] license = "MIT" repository = "https://github.com/rust-netlink/netlink-packet-audit" [dependencies.anyhow] version = "1.0.31" [dependencies.byteorder] version = "1.3.2" [dependencies.bytes] version = "1.0" [dependencies.log] version = "0.4.8" [dependencies.netlink-packet-core] version = "0.7.0" [dependencies.netlink-packet-utils] version = "0.5.2" [dependencies.netlink-proto] version = "0.11" default-features = false [dev-dependencies.lazy_static] version = "1.4.0" netlink-packet-audit-0.5.1/Cargo.toml.orig000064400000000000000000000011531046102023000165330ustar 00000000000000[package] authors = ["Corentin Henry "] name = "netlink-packet-audit" version = "0.5.1" edition = "2018" homepage = "https://github.com/rust-netlink/netlink-packet-audit" keywords = ["netlink", "linux"] license = "MIT" readme = "README.md" repository = "https://github.com/rust-netlink/netlink-packet-audit" description = "netlink packet types" [dependencies] anyhow = "1.0.31" bytes = "1.0" byteorder = "1.3.2" log = "0.4.8" netlink-packet-core = "0.7.0" netlink-packet-utils = "0.5.2" netlink-proto = { default-features = false, version = "0.11" } [dev-dependencies] lazy_static = "1.4.0" netlink-packet-audit-0.5.1/LICENSE-MIT000064400000000000000000000027731046102023000153110ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Distributions of all or part of the Software intended to be used by the recipients as they would use the unmodified Software, containing modifications that substantially alter, remove, or disable functionality of the Software, outside of the documented configuration mechanisms provided by the Software, shall be modified such that the Original Author's bug reporting email addresses and urls are either replaced with the contact information of the parties responsible for the changes, or removed entirely. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. netlink-packet-audit-0.5.1/README.md000064400000000000000000000003111046102023000151160ustar 00000000000000# Rust audit netlink protocol The `netlink-packet-audit` crate provides netlink messages parsing for [audit][audit_man] protocol. [audit_man]: https://man7.org/linux/man-pages/man3/audit_open.3.html netlink-packet-audit-0.5.1/src/buffer.rs000064400000000000000000000062621046102023000162600ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ traits::{Parseable, ParseableParametrized}, DecodeError, }; use crate::{ constants::*, rules::{RuleBuffer, RuleMessage}, AuditMessage, StatusMessage, StatusMessageBuffer, }; #[non_exhaustive] pub struct AuditBuffer { buffer: T, } impl> AuditBuffer { pub fn new(buffer: T) -> AuditBuffer { AuditBuffer { buffer } } pub fn length(&self) -> usize { self.buffer.as_ref().len() } pub fn new_checked(buffer: T) -> Result, DecodeError> { Ok(Self::new(buffer)) } } impl<'a, T: AsRef<[u8]> + ?Sized> AuditBuffer<&'a T> { pub fn inner(&self) -> &'a [u8] { self.buffer.as_ref() } } impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> AuditBuffer<&'a mut T> { pub fn inner_mut(&mut self) -> &mut [u8] { self.buffer.as_mut() } } impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized, u16> for AuditMessage { fn parse_with_param( buf: &AuditBuffer<&'a T>, message_type: u16, ) -> Result { use self::AuditMessage::*; let message = match message_type { AUDIT_GET if buf.length() == 0 => GetStatus(None), AUDIT_GET => { let err = "failed to parse AUDIT_GET message"; let buf = StatusMessageBuffer::new(buf.inner()); GetStatus(Some(StatusMessage::parse(&buf).context(err)?)) } AUDIT_SET => { let err = "failed to parse AUDIT_SET message"; let buf = StatusMessageBuffer::new(buf.inner()); SetStatus(StatusMessage::parse(&buf).context(err)?) } AUDIT_ADD_RULE => { let err = "failed to parse AUDIT_ADD_RULE message"; let buf = RuleBuffer::new_checked(buf.inner()).context(err)?; AddRule(RuleMessage::parse(&buf).context(err)?) } AUDIT_DEL_RULE => { let err = "failed to parse AUDIT_DEL_RULE message"; let buf = RuleBuffer::new_checked(buf.inner()).context(err)?; DelRule(RuleMessage::parse(&buf).context(err)?) } AUDIT_LIST_RULES if buf.length() == 0 => ListRules(None), AUDIT_LIST_RULES => { let err = "failed to parse AUDIT_LIST_RULES message"; let buf = RuleBuffer::new_checked(buf.inner()).context(err)?; ListRules(Some(RuleMessage::parse(&buf).context(err)?)) } i if (AUDIT_EVENT_MESSAGE_MIN..AUDIT_EVENT_MESSAGE_MAX) .contains(&i) => { let data = String::from_utf8(buf.inner().to_vec()).context( "failed to parse audit event data as a valid string", )?; Event((i, data)) } i => { let data = String::from_utf8(buf.inner().to_vec()).context( "failed to parse audit event data as a valid string", )?; Other((i, data)) } }; Ok(message) } } netlink-packet-audit-0.5.1/src/codec.rs000064400000000000000000000125671046102023000160710ustar 00000000000000// SPDX-License-Identifier: MIT use std::{fmt::Debug, io}; use bytes::BytesMut; use netlink_packet_core::{ NetlinkBuffer, NetlinkDeserializable, NetlinkMessage, NetlinkSerializable, }; pub(crate) use netlink_proto::{NetlinkCodec, NetlinkMessageCodec}; /// audit specific implementation of [`NetlinkMessageCodec`] due to the /// protocol violations in messages generated by kernal audit. /// /// Among the known bugs in kernel audit messages: /// - `nlmsg_len` sometimes contains the padding too (it shouldn't) /// - `nlmsg_len` sometimes doesn't contain the header (it really should) /// /// See also: /// - https://blog.des.no/2020/08/netlink-auditing-and-counting-bytes/ /// - https://github.com/torvalds/linux/blob/b5013d084e03e82ceeab4db8ae8ceeaebe76b0eb/kernel/audit.c#L2386 /// - https://github.com/mozilla/libaudit-go/issues/24 /// - https://github.com/linux-audit/audit-userspace/issues/78 #[non_exhaustive] pub struct NetlinkAuditCodec { // we don't need an instance of this, just the type _private: (), } impl NetlinkMessageCodec for NetlinkAuditCodec { fn decode(src: &mut BytesMut) -> io::Result>> where T: NetlinkDeserializable + Debug, { debug!("NetlinkAuditCodec: decoding next message"); loop { // If there's nothing to read, return Ok(None) if src.is_empty() { trace!("buffer is empty"); return Ok(None); } // This is a bit hacky because we don't want to keep `src` // borrowed, since we need to mutate it later. let src_len = src.len(); let len = match NetlinkBuffer::new_checked(src.as_mut()) { Ok(mut buf) => { if (src_len as isize - buf.length() as isize) <= 16 { // The audit messages are sometimes truncated, // because the length specified in the header, // does not take the header itself into // account. To workaround this, we tweak the // length. We've noticed two occurences of // truncated packets: // // - the length of the header is not included (see also: // https://github.com/mozilla/libaudit-go/issues/24) // - some rule message have some padding for alignment (see // https://github.com/linux-audit/audit-userspace/issues/78) which is not // taken into account in the buffer length. // // How do we know that's the right length? Due to an // implementation detail and to // the fact that netlink is a datagram protocol. // // - our implementation of Stream always calls the codec // with at most 1 message in the buffer, so we know // the extra bytes do not belong to another message. // - because netlink is a datagram protocol, we receive // entire messages, so we know that if those extra // bytes do not belong to another message, they belong // to this one. warn!("found what looks like a truncated audit packet"); // also write correct length to buffer so parsing does // not fail: warn!( "setting packet length to {} instead of {}", src_len, buf.length() ); buf.set_length(src_len as u32); src_len } else { buf.length() as usize } } Err(e) => { // We either received a truncated packet, or the // packet if malformed (invalid length field). In // both case, we can't decode the datagram, and we // cannot find the start of the next one (if // any). The only solution is to clear the buffer // and potentially lose some datagrams. error!( "failed to decode datagram, clearing buffer: {:?}: {:#x?}.", e, src.as_ref() ); src.clear(); return Ok(None); } }; let bytes = src.split_to(len); let parsed = NetlinkMessage::::deserialize(&bytes); match parsed { Ok(packet) => { trace!("<<< {:?}", packet); return Ok(Some(packet)); } Err(e) => { error!("failed to decode packet {:#x?}: {}", &bytes, e); // continue looping, there may be more datagrams in the // buffer } } } } fn encode(msg: NetlinkMessage, buf: &mut BytesMut) -> io::Result<()> where T: Debug + NetlinkSerializable, { NetlinkCodec::encode(msg, buf) } } netlink-packet-audit-0.5.1/src/constants.rs000064400000000000000000000355031046102023000170230ustar 00000000000000// SPDX-License-Identifier: MIT // ========================================== // 1000 - 1099 are for commanding the audit system // ========================================== /// Get status pub const AUDIT_GET: u16 = 1000; /// Set status (enable/disable/auditd) pub const AUDIT_SET: u16 = 1001; /// List syscall rules -- deprecated pub const AUDIT_LIST: u16 = 1002; /// Add syscall rule -- deprecated pub const AUDIT_ADD: u16 = 1003; /// Delete syscall rule -- deprecated pub const AUDIT_DEL: u16 = 1004; /// Message from userspace -- deprecated pub const AUDIT_USER: u16 = 1005; /// Define the login id and information pub const AUDIT_LOGIN: u16 = 1006; /// Insert file/dir watch entry pub const AUDIT_WATCH_INS: u16 = 1007; /// Remove file/dir watch entry pub const AUDIT_WATCH_REM: u16 = 1008; /// List all file/dir watches pub const AUDIT_WATCH_LIST: u16 = 1009; /// Get info about sender of signal to auditd pub const AUDIT_SIGNAL_INFO: u16 = 1010; /// Add syscall filtering rule pub const AUDIT_ADD_RULE: u16 = 1011; /// Delete syscall filtering rule pub const AUDIT_DEL_RULE: u16 = 1012; /// List syscall filtering rules pub const AUDIT_LIST_RULES: u16 = 1013; /// Trim junk from watched tree pub const AUDIT_TRIM: u16 = 1014; /// Append to watched tree pub const AUDIT_MAKE_EQUIV: u16 = 1015; /// Get TTY auditing status pub const AUDIT_TTY_GET: u16 = 1016; /// Set TTY auditing status pub const AUDIT_TTY_SET: u16 = 1017; /// Turn an audit feature on or off pub const AUDIT_SET_FEATURE: u16 = 1018; /// Get which features are enabled pub const AUDIT_GET_FEATURE: u16 = 1019; // ========================================== // 1100 - 1199 user space trusted application messages // ========================================== /// Userspace messages mostly uninteresting to kernel pub const AUDIT_FIRST_USER_MSG: u16 = 1100; /// We filter this differently pub const AUDIT_USER_AVC: u16 = 1107; /// Non-ICANON TTY input meaning pub const AUDIT_USER_TTY: u16 = 1124; pub const AUDIT_LAST_USER_MSG: u16 = 1199; /// More user space messages; pub const AUDIT_FIRST_USER_MSG2: u16 = 2100; pub const AUDIT_LAST_USER_MSG2: u16 = 2999; // ========================================== // 1200 - 1299 messages internal to the audit daemon // ========================================== /// Daemon startup record pub const AUDIT_DAEMON_START: u16 = 1200; /// Daemon normal stop record pub const AUDIT_DAEMON_END: u16 = 1201; /// Daemon error stop record pub const AUDIT_DAEMON_ABORT: u16 = 1202; /// Daemon config change pub const AUDIT_DAEMON_CONFIG: u16 = 1203; // ========================================== // 1300 - 1399 audit event messages // ========================================== pub const AUDIT_EVENT_MESSAGE_MIN: u16 = 1300; pub const AUDIT_EVENT_MESSAGE_MAX: u16 = 1399; /// Syscall event pub const AUDIT_SYSCALL: u16 = 1300; /// Filename path information pub const AUDIT_PATH: u16 = 1302; /// IPC record pub const AUDIT_IPC: u16 = 1303; /// sys_socketcall arguments pub const AUDIT_SOCKETCALL: u16 = 1304; /// Audit system configuration change pub const AUDIT_CONFIG_CHANGE: u16 = 1305; /// sockaddr copied as syscall arg pub const AUDIT_SOCKADDR: u16 = 1306; /// Current working directory pub const AUDIT_CWD: u16 = 1307; /// execve arguments pub const AUDIT_EXECVE: u16 = 1309; /// IPC new permissions record type pub const AUDIT_IPC_SET_PERM: u16 = 1311; /// POSIX MQ open record type pub const AUDIT_MQ_OPEN: u16 = 1312; /// POSIX MQ send/receive record type pub const AUDIT_MQ_SENDRECV: u16 = 1313; /// POSIX MQ notify record type pub const AUDIT_MQ_NOTIFY: u16 = 1314; /// POSIX MQ get/set attribute record type pub const AUDIT_MQ_GETSETATTR: u16 = 1315; /// For use by 3rd party modules pub const AUDIT_KERNEL_OTHER: u16 = 1316; /// audit record for pipe/socketpair pub const AUDIT_FD_PAIR: u16 = 1317; /// ptrace target pub const AUDIT_OBJ_PID: u16 = 1318; /// Input on an administrative TTY pub const AUDIT_TTY: u16 = 1319; /// End of multi-record event pub const AUDIT_EOE: u16 = 1320; /// Information about fcaps increasing perms pub const AUDIT_BPRM_FCAPS: u16 = 1321; /// Record showing argument to sys_capset pub const AUDIT_CAPSET: u16 = 1322; /// Record showing descriptor and flags in mmap pub const AUDIT_MMAP: u16 = 1323; /// Packets traversing netfilter chains pub const AUDIT_NETFILTER_PKT: u16 = 1324; /// Netfilter chain modifications pub const AUDIT_NETFILTER_CFG: u16 = 1325; /// Secure Computing event pub const AUDIT_SECCOMP: u16 = 1326; /// Proctitle emit event pub const AUDIT_PROCTITLE: u16 = 1327; /// audit log listing feature changes pub const AUDIT_FEATURE_CHANGE: u16 = 1328; /// Replace auditd if this packet unanswerd pub const AUDIT_REPLACE: u16 = 1329; /// Kernel Module events pub const AUDIT_KERN_MODULE: u16 = 1330; /// Fanotify access decision pub const AUDIT_FANOTIFY: u16 = 1331; // ========================================== // 1400 - 1499 SE Linux use // ========================================== /// SE Linux avc denial or grant pub const AUDIT_AVC: u16 = 1400; /// Internal SE Linux Errors pub const AUDIT_SELINUX_ERR: u16 = 1401; /// dentry, vfsmount pair from avc pub const AUDIT_AVC_PATH: u16 = 1402; /// Policy file load pub const AUDIT_MAC_POLICY_LOAD: u16 = 1403; /// Changed enforcing,permissive,off pub const AUDIT_MAC_STATUS: u16 = 1404; /// Changes to booleans pub const AUDIT_MAC_CONFIG_CHANGE: u16 = 1405; /// NetLabel: allow unlabeled traffic pub const AUDIT_MAC_UNLBL_ALLOW: u16 = 1406; /// NetLabel: add CIPSOv4 DOI entry pub const AUDIT_MAC_CIPSOV4_ADD: u16 = 1407; /// NetLabel: del CIPSOv4 DOI entry pub const AUDIT_MAC_CIPSOV4_DEL: u16 = 1408; /// NetLabel: add LSM domain mapping pub const AUDIT_MAC_MAP_ADD: u16 = 1409; /// NetLabel: del LSM domain mapping pub const AUDIT_MAC_MAP_DEL: u16 = 1410; /// Not used pub const AUDIT_MAC_IPSEC_ADDSA: u16 = 1411; /// Not used pub const AUDIT_MAC_IPSEC_DELSA: u16 = 1412; /// Not used pub const AUDIT_MAC_IPSEC_ADDSPD: u16 = 1413; /// Not used pub const AUDIT_MAC_IPSEC_DELSPD: u16 = 1414; /// Audit an IPSec event pub const AUDIT_MAC_IPSEC_EVENT: u16 = 1415; /// NetLabel: add a static label pub const AUDIT_MAC_UNLBL_STCADD: u16 = 1416; /// NetLabel: del a static label pub const AUDIT_MAC_UNLBL_STCDEL: u16 = 1417; /// NetLabel: add CALIPSO DOI entry pub const AUDIT_MAC_CALIPSO_ADD: u16 = 1418; /// NetLabel: del CALIPSO DOI entry pub const AUDIT_MAC_CALIPSO_DEL: u16 = 1419; // ========================================== // 1700 - 1799 kernel anomaly records // ========================================== pub const AUDIT_FIRST_KERN_ANOM_MSG: u16 = 1700; pub const AUDIT_LAST_KERN_ANOM_MSG: u16 = 1799; /// Device changed promiscuous mode pub const AUDIT_ANOM_PROMISCUOUS: u16 = 1700; /// Process ended abnormally pub const AUDIT_ANOM_ABEND: u16 = 1701; /// Suspicious use of file links pub const AUDIT_ANOM_LINK: u16 = 1702; // ========================================== // 1800 - 1899 kernel integrity events // ========================================== /// Data integrity verification pub const AUDIT_INTEGRITY_DATA: u16 = 1800; /// Metadata integrity verification pub const AUDIT_INTEGRITY_METADATA: u16 = 1801; /// Integrity enable status pub const AUDIT_INTEGRITY_STATUS: u16 = 1802; /// Integrity HASH type pub const AUDIT_INTEGRITY_HASH: u16 = 1803; /// PCR invalidation msgs pub const AUDIT_INTEGRITY_PCR: u16 = 1804; /// policy rule pub const AUDIT_INTEGRITY_RULE: u16 = 1805; // 2000 is for otherwise unclassified kernel audit messages (legacy) pub const AUDIT_KERNEL: u16 = 2000; // rule flags /// Apply rule to user-generated messages pub const AUDIT_FILTER_USER: u32 = 0; /// Apply rule at task creation (not syscall) pub const AUDIT_FILTER_TASK: u32 = 1; /// Apply rule at syscall entry pub const AUDIT_FILTER_ENTRY: u32 = 2; /// Apply rule to file system watches pub const AUDIT_FILTER_WATCH: u32 = 3; /// Apply rule at syscall exit pub const AUDIT_FILTER_EXIT: u32 = 4; /// Apply rule at audit_log_start pub const AUDIT_FILTER_TYPE: u32 = 5; pub const AUDIT_FILTER_FS: u32 = 6; /// Mask to get actual filter pub const AUDIT_NR_FILTERS: u32 = 7; pub const AUDIT_FILTER_PREPEND: u32 = 16; /// Filter is unset pub const AUDIT_FILTER_UNSET: u32 = 128; // Rule actions /// Do not build context if rule matches pub const AUDIT_NEVER: u32 = 0; /// Build context if rule matches pub const AUDIT_POSSIBLE: u32 = 1; /// Generate audit record if rule matches pub const AUDIT_ALWAYS: u32 = 2; pub const AUDIT_MAX_FIELDS: usize = 64; pub const AUDIT_MAX_KEY_LEN: usize = 256; pub const AUDIT_BITMASK_SIZE: usize = 64; pub const AUDIT_SYSCALL_CLASSES: u32 = 16; pub const AUDIT_CLASS_DIR_WRITE: u32 = 0; pub const AUDIT_CLASS_DIR_WRITE_32: u32 = 1; pub const AUDIT_CLASS_CHATTR: u32 = 2; pub const AUDIT_CLASS_CHATTR_32: u32 = 3; pub const AUDIT_CLASS_READ: u32 = 4; pub const AUDIT_CLASS_READ_32: u32 = 5; pub const AUDIT_CLASS_WRITE: u32 = 6; pub const AUDIT_CLASS_WRITE_32: u32 = 7; pub const AUDIT_CLASS_SIGNAL: u32 = 8; pub const AUDIT_CLASS_SIGNAL_32: u32 = 9; pub const AUDIT_UNUSED_BITS: u32 = 134216704; // Field Comparing Constants pub const AUDIT_COMPARE_UID_TO_OBJ_UID: u32 = 1; pub const AUDIT_COMPARE_GID_TO_OBJ_GID: u32 = 2; pub const AUDIT_COMPARE_EUID_TO_OBJ_UID: u32 = 3; pub const AUDIT_COMPARE_EGID_TO_OBJ_GID: u32 = 4; pub const AUDIT_COMPARE_AUID_TO_OBJ_UID: u32 = 5; pub const AUDIT_COMPARE_SUID_TO_OBJ_UID: u32 = 6; pub const AUDIT_COMPARE_SGID_TO_OBJ_GID: u32 = 7; pub const AUDIT_COMPARE_FSUID_TO_OBJ_UID: u32 = 8; pub const AUDIT_COMPARE_FSGID_TO_OBJ_GID: u32 = 9; pub const AUDIT_COMPARE_UID_TO_AUID: u32 = 10; pub const AUDIT_COMPARE_UID_TO_EUID: u32 = 11; pub const AUDIT_COMPARE_UID_TO_FSUID: u32 = 12; pub const AUDIT_COMPARE_UID_TO_SUID: u32 = 13; pub const AUDIT_COMPARE_AUID_TO_FSUID: u32 = 14; pub const AUDIT_COMPARE_AUID_TO_SUID: u32 = 15; pub const AUDIT_COMPARE_AUID_TO_EUID: u32 = 16; pub const AUDIT_COMPARE_EUID_TO_SUID: u32 = 17; pub const AUDIT_COMPARE_EUID_TO_FSUID: u32 = 18; pub const AUDIT_COMPARE_SUID_TO_FSUID: u32 = 19; pub const AUDIT_COMPARE_GID_TO_EGID: u32 = 20; pub const AUDIT_COMPARE_GID_TO_FSGID: u32 = 21; pub const AUDIT_COMPARE_GID_TO_SGID: u32 = 22; pub const AUDIT_COMPARE_EGID_TO_FSGID: u32 = 23; pub const AUDIT_COMPARE_EGID_TO_SGID: u32 = 24; pub const AUDIT_COMPARE_SGID_TO_FSGID: u32 = 25; pub const AUDIT_MAX_FIELD_COMPARE: u32 = 25; // ======================================================================= // rule fields // ======================================================================= pub const AUDIT_PID: u32 = 0; pub const AUDIT_UID: u32 = 1; pub const AUDIT_EUID: u32 = 2; pub const AUDIT_SUID: u32 = 3; pub const AUDIT_FSUID: u32 = 4; pub const AUDIT_GID: u32 = 5; pub const AUDIT_EGID: u32 = 6; pub const AUDIT_SGID: u32 = 7; pub const AUDIT_FSGID: u32 = 8; pub const AUDIT_LOGINUID: u32 = 9; pub const AUDIT_PERS: u32 = 10; pub const AUDIT_ARCH: u32 = 11; pub const AUDIT_MSGTYPE: u32 = 12; pub const AUDIT_SUBJ_USER: u32 = 13; pub const AUDIT_SUBJ_ROLE: u32 = 14; pub const AUDIT_SUBJ_TYPE: u32 = 15; pub const AUDIT_SUBJ_SEN: u32 = 16; pub const AUDIT_SUBJ_CLR: u32 = 17; pub const AUDIT_PPID: u32 = 18; pub const AUDIT_OBJ_USER: u32 = 19; pub const AUDIT_OBJ_ROLE: u32 = 20; pub const AUDIT_OBJ_TYPE: u32 = 21; pub const AUDIT_OBJ_LEV_LOW: u32 = 22; pub const AUDIT_OBJ_LEV_HIGH: u32 = 23; pub const AUDIT_LOGINUID_SET: u32 = 24; pub const AUDIT_SESSIONID: u32 = 25; pub const AUDIT_FSTYPE: u32 = 26; pub const AUDIT_DEVMAJOR: u32 = 100; pub const AUDIT_DEVMINOR: u32 = 101; pub const AUDIT_INODE: u32 = 102; pub const AUDIT_EXIT: u32 = 103; pub const AUDIT_SUCCESS: u32 = 104; pub const AUDIT_WATCH: u32 = 105; pub const AUDIT_PERM: u32 = 106; pub const AUDIT_DIR: u32 = 107; pub const AUDIT_FILETYPE: u32 = 108; pub const AUDIT_OBJ_UID: u32 = 109; pub const AUDIT_OBJ_GID: u32 = 110; pub const AUDIT_FIELD_COMPARE: u32 = 111; pub const AUDIT_EXE: u32 = 112; pub const AUDIT_ARG0: u32 = 200; pub const AUDIT_ARG1: u32 = 201; pub const AUDIT_ARG2: u32 = 202; pub const AUDIT_ARG3: u32 = 203; pub const AUDIT_FILTERKEY: u32 = 210; pub const AUDIT_BIT_MASK: u32 = 0x0800_0000; pub const AUDIT_LESS_THAN: u32 = 0x1000_0000; pub const AUDIT_GREATER_THAN: u32 = 0x2000_0000; pub const AUDIT_NOT_EQUAL: u32 = 0x3000_0000; pub const AUDIT_EQUAL: u32 = 0x4000_0000; pub const AUDIT_BIT_TEST: u32 = AUDIT_BIT_MASK | AUDIT_EQUAL; pub const AUDIT_LESS_THAN_OR_EQUAL: u32 = AUDIT_LESS_THAN | AUDIT_EQUAL; pub const AUDIT_GREATER_THAN_OR_EQUAL: u32 = AUDIT_GREATER_THAN | AUDIT_EQUAL; pub const AUDIT_OPERATORS: u32 = AUDIT_EQUAL | AUDIT_NOT_EQUAL | AUDIT_BIT_MASK; // ============================================ // failure to log actions // ============================================ pub const AUDIT_FAIL_SILENT: u32 = 0; pub const AUDIT_FAIL_PRINTK: u32 = 1; pub const AUDIT_FAIL_PANIC: u32 = 2; pub const AUDIT_PERM_EXEC: u32 = 1; pub const AUDIT_PERM_WRITE: u32 = 2; pub const AUDIT_PERM_READ: u32 = 4; pub const AUDIT_PERM_ATTR: u32 = 8; pub const AUDIT_MESSAGE_TEXT_MAX: u32 = 8560; pub const AUDIT_FEATURE_VERSION: u32 = 1; pub const AUDIT_FEATURE_ONLY_UNSET_LOGINUID: u32 = 0; pub const AUDIT_FEATURE_LOGINUID_IMMUTABLE: u32 = 1; pub const AUDIT_LAST_FEATURE: u32 = 1; /// Unused multicast group for audit pub const AUDIT_NLGRP_NONE: u32 = 0; /// Multicast group to listen for audit events pub const AUDIT_NLGRP_READLOG: u32 = 1; pub const __AUDIT_ARCH_CONVENTION_MASK: u32 = 0x3000_0000; pub const __AUDIT_ARCH_CONVENTION_MIPS64_N32: u32 = 0x2000_0000; pub const __AUDIT_ARCH_64BIT: u32 = 0x0800_0000; pub const __AUDIT_ARCH_LE: u32 = 0x4000_0000; pub const AUDIT_ARCH_AARCH64: u32 = 0xC000_00B7; pub const AUDIT_ARCH_ALPHA: u32 = 0xC000_9026; pub const AUDIT_ARCH_ARM: u32 = 0x4000_0028; pub const AUDIT_ARCH_ARMEB: u32 = 0x28; pub const AUDIT_ARCH_CRIS: u32 = 0x4000_004C; pub const AUDIT_ARCH_FRV: u32 = 0x5441; pub const AUDIT_ARCH_I386: u32 = 0x4000_0003; pub const AUDIT_ARCH_IA64: u32 = 0xC000_0032; pub const AUDIT_ARCH_M32R: u32 = 0x58; pub const AUDIT_ARCH_M68K: u32 = 0x04; pub const AUDIT_ARCH_MICROBLAZE: u32 = 0xBD; pub const AUDIT_ARCH_MIPS: u32 = 0x08; pub const AUDIT_ARCH_MIPSEL: u32 = 0x4000_0008; pub const AUDIT_ARCH_MIPS64: u32 = 0x8000_0008; pub const AUDIT_ARCH_MIPS64N32: u32 = 0xA000_0008; pub const AUDIT_ARCH_MIPSEL64: u32 = 0xC000_0008; pub const AUDIT_ARCH_MIPSEL64N32: u32 = 0xE000_0008; pub const AUDIT_ARCH_OPENRISC: u32 = 92; pub const AUDIT_ARCH_PARISC: u32 = 15; pub const AUDIT_ARCH_PARISC64: u32 = 0x8000_000F; pub const AUDIT_ARCH_PPC: u32 = 20; pub const AUDIT_ARCH_PPC64: u32 = 0x8000_0015; pub const AUDIT_ARCH_PPC64LE: u32 = 0xC000_0015; pub const AUDIT_ARCH_S390: u32 = 22; pub const AUDIT_ARCH_S390X: u32 = 0x8000_0016; pub const AUDIT_ARCH_SH: u32 = 42; pub const AUDIT_ARCH_SHEL: u32 = 0x4000_002A; pub const AUDIT_ARCH_SH64: u32 = 0x8000_002A; pub const AUDIT_ARCH_SHEL64: u32 = 0xC000_002A; pub const AUDIT_ARCH_SPARC: u32 = 2; pub const AUDIT_ARCH_SPARC64: u32 = 0x8000_002B; pub const AUDIT_ARCH_TILEGX: u32 = 0xC000_00BF; pub const AUDIT_ARCH_TILEGX32: u32 = 0x4000_00BF; pub const AUDIT_ARCH_TILEPRO: u32 = 0x4000_00BC; pub const AUDIT_ARCH_X86_64: u32 = 0xC000_003E; netlink-packet-audit-0.5.1/src/lib.rs000064400000000000000000000007601046102023000155520ustar 00000000000000// SPDX-License-Identifier: MIT #[macro_use] extern crate log; use core::ops::Range; /// Represent a multi-bytes field with a fixed size in a packet pub(crate) type Field = Range; mod codec; pub use codec::NetlinkAuditCodec; pub mod status; pub use self::status::*; pub mod rules; pub use self::rules::*; mod message; pub use self::message::*; mod buffer; pub use self::buffer::*; pub mod constants; pub use self::constants::*; #[cfg(test)] #[macro_use] extern crate lazy_static; netlink-packet-audit-0.5.1/src/message.rs000064400000000000000000000111621046102023000164260ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_core::{ NetlinkDeserializable, NetlinkHeader, NetlinkPayload, NetlinkSerializable, }; use netlink_packet_utils::{ traits::{Emitable, ParseableParametrized}, DecodeError, }; use crate::{constants::*, rules::RuleMessage, AuditBuffer, StatusMessage}; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum AuditMessage { GetStatus(Option), SetStatus(StatusMessage), AddRule(RuleMessage), DelRule(RuleMessage), ListRules(Option), /// Event message (message types 1300 through 1399). This includes the /// following message types (this list is non-exhaustive, and not /// really kept up to date): `AUDIT_SYSCALL`, `AUDIT_PATH`, /// `AUDIT_IPC`, `AUDIT_SOCKETCALL`, `AUDIT_CONFIG_CHANGE`, /// `AUDIT_SOCKADDR`, `AUDIT_CWD`, `AUDIT_EXECVE`, /// `AUDIT_IPC_SET_PERM`, `AUDIT_MQ_OPEN`, `AUDIT_MQ_SENDRECV`, /// `AUDIT_MQ_NOTIFY`, `AUDIT_MQ_GETSETATTR`, `AUDIT_KERNEL_OTHER`, /// `AUDIT_FD_PAIR`, `AUDIT_OBJ_PID`, `AUDIT_TTY`, `AUDIT_EOE`, /// `AUDIT_BPRM_FCAPS`, `AUDIT_CAPSET`, `AUDIT_MMAP`, /// `AUDIT_NETFILTER_PKT`, `AUDIT_NETFILTER_CFG`, `AUDIT_SECCOMP`, /// `AUDIT_PROCTITLE`, `AUDIT_FEATURE_CHANGE`, `AUDIT_REPLACE`, /// `AUDIT_KERN_MODULE`, `AUDIT_FANOTIFY`. /// /// The first element of the tuple is the message type, and the second is /// the event data. Event((u16, String)), /// All the other events are parsed as such as they can be parsed also. Other((u16, String)), } impl AuditMessage { pub fn is_event(&self) -> bool { matches!(self, AuditMessage::Event(_)) } pub fn is_get_status(&self) -> bool { matches!(self, AuditMessage::GetStatus(_)) } pub fn is_set_status(&self) -> bool { matches!(self, AuditMessage::GetStatus(_)) } pub fn is_add_rule(&self) -> bool { matches!(self, AuditMessage::AddRule(_)) } pub fn is_del_rule(&self) -> bool { matches!(self, AuditMessage::DelRule(_)) } pub fn is_list_rules(&self) -> bool { matches!(self, AuditMessage::ListRules(_)) } pub fn message_type(&self) -> u16 { use self::AuditMessage::*; match self { GetStatus(_) => AUDIT_GET, SetStatus(_) => AUDIT_SET, ListRules(_) => AUDIT_LIST_RULES, AddRule(_) => AUDIT_ADD_RULE, DelRule(_) => AUDIT_DEL_RULE, Event((message_type, _)) => *message_type, Other((message_type, _)) => *message_type, } } } impl Emitable for AuditMessage { fn buffer_len(&self) -> usize { use self::AuditMessage::*; match self { GetStatus(Some(ref msg)) => msg.buffer_len(), SetStatus(ref msg) => msg.buffer_len(), AddRule(ref msg) => msg.buffer_len(), DelRule(ref msg) => msg.buffer_len(), ListRules(Some(ref msg)) => msg.buffer_len(), GetStatus(None) | ListRules(None) => 0, Event((_, ref data)) => data.len(), Other((_, ref data)) => data.len(), } } fn emit(&self, buffer: &mut [u8]) { use self::AuditMessage::*; match self { GetStatus(Some(ref msg)) => msg.emit(buffer), SetStatus(ref msg) => msg.emit(buffer), AddRule(ref msg) => msg.emit(buffer), DelRule(ref msg) => msg.emit(buffer), ListRules(Some(ref msg)) => msg.emit(buffer), ListRules(None) | GetStatus(None) => {} Event((_, ref data)) => buffer.copy_from_slice(data.as_bytes()), Other((_, ref data)) => buffer.copy_from_slice(data.as_bytes()), } } } impl NetlinkSerializable for AuditMessage { fn message_type(&self) -> u16 { self.message_type() } fn buffer_len(&self) -> usize { ::buffer_len(self) } fn serialize(&self, buffer: &mut [u8]) { self.emit(buffer) } } impl NetlinkDeserializable for AuditMessage { type Error = DecodeError; fn deserialize( header: &NetlinkHeader, payload: &[u8], ) -> Result { match AuditBuffer::new_checked(payload) { Err(e) => Err(e), Ok(buffer) => match AuditMessage::parse_with_param( &buffer, header.message_type, ) { Err(e) => Err(e), Ok(message) => Ok(message), }, } } } impl From for NetlinkPayload { fn from(message: AuditMessage) -> Self { NetlinkPayload::InnerMessage(message) } } netlink-packet-audit-0.5.1/src/rules/action.rs000064400000000000000000000014121046102023000174060ustar 00000000000000// SPDX-License-Identifier: MIT use crate::constants::*; #[derive(Copy, Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum RuleAction { Never, Possible, Always, Unknown(u32), } impl From for RuleAction { fn from(value: u32) -> Self { use self::RuleAction::*; match value { AUDIT_NEVER => Never, AUDIT_POSSIBLE => Possible, AUDIT_ALWAYS => Always, _ => Unknown(value), } } } impl From for u32 { fn from(value: RuleAction) -> Self { use self::RuleAction::*; match value { Never => AUDIT_NEVER, Possible => AUDIT_POSSIBLE, Always => AUDIT_ALWAYS, Unknown(value) => value, } } } netlink-packet-audit-0.5.1/src/rules/buffer.rs000064400000000000000000000223041046102023000174050ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{traits::Parseable, DecodeError}; use crate::{constants::*, rules::*, Field}; // FIXME: when const fn are stable, use them, instead of defining a macro // const fn u32_array(start: usize, len: usize) -> Field { // start..(start + 4 * len) // } macro_rules! u32_array { ($start:expr, $len:expr) => { $start..($start + 4 * $len) }; } const FLAGS: Field = 0..4; const ACTION: Field = 4..8; const FIELD_COUNT: Field = 8..12; const SYSCALLS: Field = u32_array!(FIELD_COUNT.end, AUDIT_BITMASK_SIZE); const FIELDS: Field = u32_array!(SYSCALLS.end, AUDIT_MAX_FIELDS); const VALUES: Field = u32_array!(FIELDS.end, AUDIT_MAX_FIELDS); const FIELD_FLAGS: Field = u32_array!(VALUES.end, AUDIT_MAX_FIELDS); const BUFLEN: Field = FIELD_FLAGS.end..FIELD_FLAGS.end + 4; pub(crate) const RULE_BUF_MIN_LEN: usize = BUFLEN.end; #[allow(non_snake_case)] fn BUF(len: usize) -> Field { BUFLEN.end..(BUFLEN.end + len) } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct RuleBuffer { buffer: T, } impl> RuleBuffer { pub fn new(buffer: T) -> RuleBuffer { RuleBuffer { buffer } } pub fn new_checked(buffer: T) -> Result { let packet = Self::new(buffer); packet.check_len()?; Ok(packet) } pub(crate) fn check_len(&self) -> Result<(), DecodeError> { let len = self.buffer.as_ref().len(); if len < BUFLEN.end { Err(format!( "buffer size is {}, whereas a rule buffer is at least {} long", len, BUFLEN.end ) .into()) } else if len < BUFLEN.end + self.buflen() as usize { Err(format!( "buffer length is {}, but it should be {} (header) + {} \ (length field)", len, BUFLEN.end, self.buflen() ) .into()) } else { Ok(()) } } pub fn flags(&self) -> u32 { NativeEndian::read_u32(&self.buffer.as_ref()[FLAGS]) } pub fn action(&self) -> u32 { NativeEndian::read_u32(&self.buffer.as_ref()[ACTION]) } pub fn field_count(&self) -> u32 { NativeEndian::read_u32(&self.buffer.as_ref()[FIELD_COUNT]) } pub fn buflen(&self) -> u32 { NativeEndian::read_u32(&self.buffer.as_ref()[BUFLEN]) } } impl<'a, T: AsRef<[u8]> + ?Sized> RuleBuffer<&'a T> { pub fn syscalls(&self) -> &'a [u8] { &self.buffer.as_ref()[SYSCALLS] } pub fn fields(&self) -> &'a [u8] { &self.buffer.as_ref()[FIELDS] } pub fn values(&self) -> &'a [u8] { &self.buffer.as_ref()[VALUES] } pub fn field_flags(&self) -> &'a [u8] { &self.buffer.as_ref()[FIELD_FLAGS] } pub fn buf(&self) -> &'a [u8] { let field = BUF(self.buflen() as usize); &self.buffer.as_ref()[field.start..field.end] } } impl + AsMut<[u8]>> RuleBuffer { pub fn set_flags(&mut self, value: u32) { NativeEndian::write_u32(&mut self.buffer.as_mut()[FLAGS], value) } pub fn set_action(&mut self, value: u32) { NativeEndian::write_u32(&mut self.buffer.as_mut()[ACTION], value) } pub fn set_field_count(&mut self, value: u32) { NativeEndian::write_u32(&mut self.buffer.as_mut()[FIELD_COUNT], value) } pub fn set_buflen(&mut self, value: u32) { NativeEndian::write_u32(&mut self.buffer.as_mut()[BUFLEN], value) } pub fn syscalls_mut(&mut self) -> &mut [u8] { &mut self.buffer.as_mut()[SYSCALLS] } pub fn fields_mut(&mut self) -> &mut [u8] { &mut self.buffer.as_mut()[FIELDS] } pub fn set_field(&mut self, position: usize, value: u32) { let offset = FIELDS.start + (position * 4); assert!(position <= FIELDS.end - 4); NativeEndian::write_u32( &mut self.buffer.as_mut()[offset..offset + 4], value, ) } pub fn values_mut(&mut self) -> &mut [u8] { &mut self.buffer.as_mut()[VALUES] } pub fn set_value(&mut self, position: usize, value: u32) { let offset = VALUES.start + (position * 4); assert!(position <= VALUES.end - 4); NativeEndian::write_u32( &mut self.buffer.as_mut()[offset..offset + 4], value, ) } pub fn field_flags_mut(&mut self) -> &mut [u8] { &mut self.buffer.as_mut()[FIELD_FLAGS] } pub fn set_field_flags(&mut self, position: usize, value: u32) { let offset = FIELD_FLAGS.start + (position * 4); assert!(position <= FIELD_FLAGS.end - 4); NativeEndian::write_u32( &mut self.buffer.as_mut()[offset..offset + 4], value, ) } pub fn buf_mut(&mut self) -> &mut [u8] { let field = BUF(self.buflen() as usize); &mut self.buffer.as_mut()[field.start..field.end] } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for RuleMessage { fn parse(buf: &RuleBuffer<&'a T>) -> Result { use self::RuleField::*; buf.check_len().context("invalid rule message buffer")?; let mut rule = RuleMessage::new(); rule.flags = buf.flags().into(); rule.action = buf.action().into(); rule.syscalls = RuleSyscalls::from_slice(buf.syscalls())?; let mut offset = 0; let fields = buf.fields().chunks(4).map(NativeEndian::read_u32); let values = buf.values().chunks(4).map(NativeEndian::read_u32); let field_flags = buf .field_flags() .chunks(4) .map(|chunk| RuleFieldFlags::from(NativeEndian::read_u32(chunk))); for (field, value, flags) in fields .zip(values.zip(field_flags)) .map(|(field, (value, flags))| (field, value, flags)) .take(buf.field_count() as usize) { let field = match field { AUDIT_PID => Pid(value), AUDIT_UID => Uid(value), AUDIT_EUID => Euid(value), AUDIT_SUID => Suid(value), AUDIT_FSUID => Fsuid(value), AUDIT_GID => Gid(value), AUDIT_EGID => Egid(value), AUDIT_SGID => Sgid(value), AUDIT_FSGID => Fsgid(value), AUDIT_LOGINUID => Loginuid(value), AUDIT_PERS => Pers(value), AUDIT_ARCH => Arch(value), AUDIT_MSGTYPE => Msgtype(value), AUDIT_PPID => Ppid(value), AUDIT_LOGINUID_SET => LoginuidSet(value), AUDIT_SESSIONID => Sessionid(value), AUDIT_FSTYPE => Fstype(value), AUDIT_DEVMAJOR => Devmajor(value), AUDIT_DEVMINOR => Devminor(value), AUDIT_INODE => Inode(value), AUDIT_EXIT => Exit(value), AUDIT_SUCCESS => Success(value), AUDIT_PERM => Perm(value), AUDIT_FILETYPE => Filetype(value), AUDIT_OBJ_UID => ObjUid(value), AUDIT_OBJ_GID => ObjGid(value), AUDIT_FIELD_COMPARE => FieldCompare(value), AUDIT_EXE => Exe(value), AUDIT_ARG0 => Arg0(value), AUDIT_ARG1 => Arg1(value), AUDIT_ARG2 => Arg2(value), AUDIT_ARG3 => Arg3(value), _ => { // For all the other fields, the value is a string let str_end = offset + value as usize; if str_end > buf.buf().len() { return Err(format!( "failed to decode field. type={field} \ (value should be a string?)" ) .into()); } let s: String = String::from_utf8_lossy(&buf.buf()[offset..str_end]) .into(); offset = str_end; match field { AUDIT_WATCH => Watch(s), AUDIT_DIR => Dir(s), AUDIT_FILTERKEY => Filterkey(s), AUDIT_SUBJ_USER => SubjUser(s), AUDIT_SUBJ_ROLE => SubjRole(s), AUDIT_SUBJ_TYPE => SubjType(s), AUDIT_SUBJ_SEN => SubjSen(s), AUDIT_SUBJ_CLR => SubjClr(s), AUDIT_OBJ_USER => ObjUser(s), AUDIT_OBJ_ROLE => ObjRole(s), AUDIT_OBJ_TYPE => ObjType(s), AUDIT_OBJ_LEV_LOW => ObjLevLow(s), AUDIT_OBJ_LEV_HIGH => ObjLevHigh(s), _ => { return Err(format!( "failed to decode field (unknown type) \ type={field}, value={s}" ) .into()); } } } }; rule.fields.push((field, flags)); } Ok(rule) } } netlink-packet-audit-0.5.1/src/rules/field.rs000064400000000000000000000044421046102023000172220ustar 00000000000000// SPDX-License-Identifier: MIT use crate::constants::*; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum RuleField { Pid(u32), Uid(u32), Euid(u32), Suid(u32), Fsuid(u32), Gid(u32), Egid(u32), Sgid(u32), Fsgid(u32), Loginuid(u32), Pers(u32), Arch(u32), Msgtype(u32), Ppid(u32), LoginuidSet(u32), Sessionid(u32), Fstype(u32), Devmajor(u32), Devminor(u32), Inode(u32), Exit(u32), Success(u32), Perm(u32), Filetype(u32), ObjUid(u32), ObjGid(u32), FieldCompare(u32), Exe(u32), Arg0(u32), Arg1(u32), Arg2(u32), Arg3(u32), Watch(String), Dir(String), Filterkey(String), SubjUser(String), SubjRole(String), SubjType(String), SubjSen(String), SubjClr(String), ObjUser(String), ObjRole(String), ObjType(String), ObjLevLow(String), ObjLevHigh(String), } #[derive(Copy, Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum RuleFieldFlags { BitMask, BitTest, LessThan, GreaterThan, NotEqual, Equal, LessThanOrEqual, GreaterThanOrEqual, None, Unknown(u32), } impl From for RuleFieldFlags { fn from(value: u32) -> Self { use self::RuleFieldFlags::*; match value { AUDIT_BIT_MASK => BitMask, AUDIT_BIT_TEST => BitTest, AUDIT_LESS_THAN => LessThan, AUDIT_GREATER_THAN => GreaterThan, AUDIT_NOT_EQUAL => NotEqual, AUDIT_EQUAL => Equal, AUDIT_LESS_THAN_OR_EQUAL => LessThanOrEqual, AUDIT_GREATER_THAN_OR_EQUAL => GreaterThanOrEqual, 0 => None, _ => Unknown(value), } } } impl From for u32 { fn from(value: RuleFieldFlags) -> Self { use self::RuleFieldFlags::*; match value { BitMask => AUDIT_BIT_MASK, BitTest => AUDIT_BIT_TEST, LessThan => AUDIT_LESS_THAN, GreaterThan => AUDIT_GREATER_THAN, NotEqual => AUDIT_NOT_EQUAL, Equal => AUDIT_EQUAL, LessThanOrEqual => AUDIT_LESS_THAN_OR_EQUAL, GreaterThanOrEqual => AUDIT_GREATER_THAN_OR_EQUAL, None => 0, Unknown(value) => value, } } } netlink-packet-audit-0.5.1/src/rules/flags.rs000064400000000000000000000030441046102023000172300ustar 00000000000000// SPDX-License-Identifier: MIT use crate::constants::*; #[derive(Copy, Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum RuleFlags { FilterUser, FilterTask, FilterEntry, FilterWatch, FilterExit, FilterType, FilterFs, NrFilters, FilterPrepend, Unset, Unknown(u32), } impl From for RuleFlags { fn from(value: u32) -> Self { use self::RuleFlags::*; match value { AUDIT_FILTER_USER => FilterUser, AUDIT_FILTER_TASK => FilterTask, AUDIT_FILTER_ENTRY => FilterEntry, AUDIT_FILTER_WATCH => FilterWatch, AUDIT_FILTER_EXIT => FilterExit, AUDIT_FILTER_TYPE => FilterType, AUDIT_FILTER_FS => FilterFs, AUDIT_NR_FILTERS => NrFilters, AUDIT_FILTER_PREPEND => FilterPrepend, AUDIT_FILTER_UNSET => Unset, _ => Unknown(value), } } } impl From for u32 { fn from(value: RuleFlags) -> Self { use self::RuleFlags::*; match value { FilterUser => AUDIT_FILTER_USER, FilterTask => AUDIT_FILTER_TASK, FilterEntry => AUDIT_FILTER_ENTRY, FilterWatch => AUDIT_FILTER_WATCH, FilterExit => AUDIT_FILTER_EXIT, FilterType => AUDIT_FILTER_TYPE, FilterFs => AUDIT_FILTER_FS, NrFilters => AUDIT_NR_FILTERS, FilterPrepend => AUDIT_FILTER_PREPEND, Unset => AUDIT_FILTER_UNSET, Unknown(value) => value, } } } netlink-packet-audit-0.5.1/src/rules/mod.rs000064400000000000000000000004311046102023000167100ustar 00000000000000// SPDX-License-Identifier: MIT mod action; pub use self::action::*; mod field; pub use self::field::*; mod flags; pub use self::flags::*; mod syscalls; pub use self::syscalls::*; mod buffer; pub use self::buffer::*; mod rule; pub use self::rule::*; #[cfg(test)] mod tests; netlink-packet-audit-0.5.1/src/rules/rule.rs000064400000000000000000000247051046102023000171120ustar 00000000000000// SPDX-License-Identifier: MIT use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::traits::Emitable; use crate::{ constants::*, rules::{ RuleAction, RuleBuffer, RuleField, RuleFieldFlags, RuleFlags, RuleSyscalls, RULE_BUF_MIN_LEN, }, }; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct RuleMessage { pub flags: RuleFlags, pub action: RuleAction, pub fields: Vec<(RuleField, RuleFieldFlags)>, pub syscalls: RuleSyscalls, } impl Default for RuleMessage { fn default() -> Self { RuleMessage::new() } } impl RuleMessage { pub fn new() -> Self { RuleMessage { flags: RuleFlags::from(0), action: RuleAction::from(0), fields: Vec::with_capacity(AUDIT_MAX_FIELDS), syscalls: RuleSyscalls::new_zeroed(), } } #[rustfmt::skip] fn compute_string_values_length(&self) -> usize { use self::RuleField::*; let mut len = 0; for (field, _) in self.fields.iter() { match field { Watch(ref s) | Dir(ref s) | Filterkey(ref s) | SubjUser(ref s) | SubjRole(ref s) | SubjType(ref s) | SubjSen(ref s) | SubjClr(ref s) | ObjUser(ref s) | ObjRole(ref s) | ObjType(ref s) | ObjLevLow(ref s) | ObjLevHigh(ref s) => len += s.len(), _ => {} } } len } } fn set_str_field( rule_buffer: &mut RuleBuffer, position: usize, buflen: &mut usize, s: &str, ) where T: AsRef<[u8]> + AsMut<[u8]>, { // append the string to the strings buffer rule_buffer.buf_mut()[*buflen..*buflen + s.len()] .copy_from_slice(s.as_bytes()); // set the field's value to the string length rule_buffer.set_value(position, s.len() as u32); *buflen += s.len(); } impl Emitable for RuleMessage { fn buffer_len(&self) -> usize { RULE_BUF_MIN_LEN + self.compute_string_values_length() } fn emit(&self, buffer: &mut [u8]) { use self::RuleField::*; let mut rule_buffer = RuleBuffer::new(buffer); rule_buffer.set_flags(self.flags.into()); rule_buffer.set_action(self.action.into()); rule_buffer.set_field_count(self.fields.len() as u32); { let syscalls = rule_buffer.syscalls_mut(); for (i, word) in self.syscalls.0.iter().enumerate() { NativeEndian::write_u32(&mut syscalls[i * 4..i * 4 + 4], *word); } } rule_buffer.set_buflen(self.compute_string_values_length() as u32); let mut buflen = 0; for (i, (field, flags)) in self.fields.iter().enumerate() { rule_buffer.set_field_flags(i, (*flags).into()); match field { Watch(ref s) => { rule_buffer.set_field(i, AUDIT_WATCH); set_str_field(&mut rule_buffer, i, &mut buflen, s); } Dir(ref s) => { rule_buffer.set_field(i, AUDIT_DIR); set_str_field(&mut rule_buffer, i, &mut buflen, s); } Filterkey(ref s) => { rule_buffer.set_field(i, AUDIT_FILTERKEY); set_str_field(&mut rule_buffer, i, &mut buflen, s); } SubjUser(ref s) => { rule_buffer.set_field(i, AUDIT_SUBJ_USER); set_str_field(&mut rule_buffer, i, &mut buflen, s); } SubjRole(ref s) => { rule_buffer.set_field(i, AUDIT_SUBJ_ROLE); set_str_field(&mut rule_buffer, i, &mut buflen, s); } SubjType(ref s) => { rule_buffer.set_field(i, AUDIT_SUBJ_TYPE); set_str_field(&mut rule_buffer, i, &mut buflen, s); } SubjSen(ref s) => { rule_buffer.set_field(i, AUDIT_SUBJ_SEN); set_str_field(&mut rule_buffer, i, &mut buflen, s); } SubjClr(ref s) => { rule_buffer.set_field(i, AUDIT_SUBJ_CLR); set_str_field(&mut rule_buffer, i, &mut buflen, s); } ObjUser(ref s) => { rule_buffer.set_field(i, AUDIT_OBJ_USER); set_str_field(&mut rule_buffer, i, &mut buflen, s); } ObjRole(ref s) => { rule_buffer.set_field(i, AUDIT_OBJ_ROLE); set_str_field(&mut rule_buffer, i, &mut buflen, s); } ObjType(ref s) => { rule_buffer.set_field(i, AUDIT_OBJ_TYPE); set_str_field(&mut rule_buffer, i, &mut buflen, s); } ObjLevLow(ref s) => { rule_buffer.set_field(i, AUDIT_OBJ_LEV_LOW); set_str_field(&mut rule_buffer, i, &mut buflen, s); } ObjLevHigh(ref s) => { rule_buffer.set_field(i, AUDIT_OBJ_LEV_HIGH); set_str_field(&mut rule_buffer, i, &mut buflen, s); } Pid(val) => { rule_buffer.set_field(i, AUDIT_PID); rule_buffer.set_value(i, *val); } Uid(val) => { rule_buffer.set_field(i, AUDIT_UID); rule_buffer.set_value(i, *val); } Euid(val) => { rule_buffer.set_field(i, AUDIT_EUID); rule_buffer.set_value(i, *val); } Suid(val) => { rule_buffer.set_field(i, AUDIT_SUID); rule_buffer.set_value(i, *val); } Fsuid(val) => { rule_buffer.set_field(i, AUDIT_FSUID); rule_buffer.set_value(i, *val); } Gid(val) => { rule_buffer.set_field(i, AUDIT_GID); rule_buffer.set_value(i, *val); } Egid(val) => { rule_buffer.set_field(i, AUDIT_EGID); rule_buffer.set_value(i, *val); } Sgid(val) => { rule_buffer.set_field(i, AUDIT_SGID); rule_buffer.set_value(i, *val); } Fsgid(val) => { rule_buffer.set_field(i, AUDIT_FSGID); rule_buffer.set_value(i, *val); } Loginuid(val) => { rule_buffer.set_field(i, AUDIT_LOGINUID); rule_buffer.set_value(i, *val); } Pers(val) => { rule_buffer.set_field(i, AUDIT_PERS); rule_buffer.set_value(i, *val); } Arch(val) => { rule_buffer.set_field(i, AUDIT_ARCH); rule_buffer.set_value(i, *val); } Msgtype(val) => { rule_buffer.set_field(i, AUDIT_MSGTYPE); rule_buffer.set_value(i, *val); } Ppid(val) => { rule_buffer.set_field(i, AUDIT_PPID); rule_buffer.set_value(i, *val); } LoginuidSet(val) => { rule_buffer.set_field(i, AUDIT_LOGINUID_SET); rule_buffer.set_value(i, *val); } Sessionid(val) => { rule_buffer.set_field(i, AUDIT_SESSIONID); rule_buffer.set_value(i, *val); } Fstype(val) => { rule_buffer.set_field(i, AUDIT_FSTYPE); rule_buffer.set_value(i, *val); } Devmajor(val) => { rule_buffer.set_field(i, AUDIT_DEVMAJOR); rule_buffer.set_value(i, *val); } Devminor(val) => { rule_buffer.set_field(i, AUDIT_DEVMINOR); rule_buffer.set_value(i, *val); } Inode(val) => { rule_buffer.set_field(i, AUDIT_INODE); rule_buffer.set_value(i, *val); } Exit(val) => { rule_buffer.set_field(i, AUDIT_EXIT); rule_buffer.set_value(i, *val); } Success(val) => { rule_buffer.set_field(i, AUDIT_SUCCESS); rule_buffer.set_value(i, *val); } Perm(val) => { rule_buffer.set_field(i, AUDIT_PERM); rule_buffer.set_value(i, *val); } Filetype(val) => { rule_buffer.set_field(i, AUDIT_FILETYPE); rule_buffer.set_value(i, *val); } ObjUid(val) => { rule_buffer.set_field(i, AUDIT_OBJ_UID); rule_buffer.set_value(i, *val); } ObjGid(val) => { rule_buffer.set_field(i, AUDIT_OBJ_GID); rule_buffer.set_value(i, *val); } FieldCompare(val) => { rule_buffer.set_field(i, AUDIT_FIELD_COMPARE); rule_buffer.set_value(i, *val); } Exe(val) => { rule_buffer.set_field(i, AUDIT_EXE); rule_buffer.set_value(i, *val); } Arg0(val) => { rule_buffer.set_field(i, AUDIT_ARG0); rule_buffer.set_value(i, *val); } Arg1(val) => { rule_buffer.set_field(i, AUDIT_ARG1); rule_buffer.set_value(i, *val); } Arg2(val) => { rule_buffer.set_field(i, AUDIT_ARG2); rule_buffer.set_value(i, *val); } Arg3(val) => { rule_buffer.set_field(i, AUDIT_ARG3); rule_buffer.set_value(i, *val); } } } } } netlink-packet-audit-0.5.1/src/rules/syscalls.rs000064400000000000000000000141371046102023000177760ustar 00000000000000// SPDX-License-Identifier: MIT use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::DecodeError; use crate::constants::*; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct RuleSyscalls(pub(crate) Vec); const BITMASK_BYTE_LEN: usize = AUDIT_BITMASK_SIZE * 4; const BITMASK_BIT_LEN: u32 = AUDIT_BITMASK_SIZE as u32 * 32; // FIXME: I'm not 100% sure this implementation is correct wrt to endianness. impl RuleSyscalls { // FIXME: this should be a TryFrom when it stabilized... pub fn from_slice(slice: &[u8]) -> Result { if slice.len() != BITMASK_BYTE_LEN { return Err(DecodeError::from(format!( "invalid bitmask size: expected {} bytes got {}", BITMASK_BYTE_LEN, slice.len() ))); } let mut mask = RuleSyscalls::new_zeroed(); let mut word = 0; while word < AUDIT_BITMASK_SIZE { mask.0[word] = NativeEndian::read_u32(&slice[word * 4..word * 4 + 4]); word += 1; } Ok(mask) } pub fn new_zeroed() -> Self { RuleSyscalls(vec![0; AUDIT_BITMASK_SIZE]) } pub fn new_maxed() -> Self { RuleSyscalls(vec![0xffff_ffff; AUDIT_BITMASK_SIZE]) } /// Unset all the bits pub fn unset_all(&mut self) -> &mut Self { self.0 = vec![0; AUDIT_BITMASK_SIZE]; self } /// Return `true` if all the syscalls are set, `false` otherwise pub fn is_all(&self) -> bool { for i in 0..AUDIT_BITMASK_SIZE { if self.0[i] != 0xffff_ffff { return false; } } true } /// Set all the bits pub fn set_all(&mut self) -> &mut Self { self.0 = vec![0xffff_ffff; AUDIT_BITMASK_SIZE]; self } /// Unset the bit corresponding to the given syscall pub fn unset(&mut self, syscall: u32) -> &mut Self { let (word, mask) = Self::syscall_coordinates(syscall); self.0[word] &= !mask; self } /// Set the bit corresponding to the given syscall pub fn set(&mut self, syscall: u32) -> &mut Self { let (word, mask) = Self::syscall_coordinates(syscall); self.0[word] |= mask; self } /// Check if the bit corresponding to the given syscall is set pub fn has(&self, syscall: u32) -> bool { let (word, mask) = Self::syscall_coordinates(syscall); self.0[word] & mask == mask } fn syscall_coordinates(syscall: u32) -> (usize, u32) { let word_index = syscall / 32; let mask = 0x0000_0001 << (syscall - word_index * 32); (word_index as usize, mask) } } // FIXME: There is a LOT of copy paste for those iterator implementations... // This feels wrong but I could not figure out how to avoid it :( #[non_exhaustive] pub struct RuleSyscallsIter { index: u32, syscalls: T, } impl IntoIterator for RuleSyscalls { type Item = u32; type IntoIter = RuleSyscallsIter; fn into_iter(self) -> Self::IntoIter { RuleSyscallsIter { index: 0, syscalls: self, } } } impl Iterator for RuleSyscallsIter { type Item = u32; fn next(&mut self) -> Option { while self.index < BITMASK_BIT_LEN { let index = self.index; self.index += 1; if self.syscalls.has(index) { return Some(index); } } None } } impl<'a> IntoIterator for &'a RuleSyscalls { type Item = u32; type IntoIter = RuleSyscallsIter<&'a RuleSyscalls>; fn into_iter(self) -> Self::IntoIter { RuleSyscallsIter { index: 0, syscalls: self, } } } impl<'a> Iterator for RuleSyscallsIter<&'a RuleSyscalls> { type Item = u32; fn next(&mut self) -> Option { while self.index < BITMASK_BIT_LEN { let index = self.index; self.index += 1; if self.syscalls.has(index) { return Some(index); } } None } } impl<'a> IntoIterator for &'a mut RuleSyscalls { type Item = u32; type IntoIter = RuleSyscallsIter<&'a mut RuleSyscalls>; fn into_iter(self) -> Self::IntoIter { RuleSyscallsIter { index: 0, syscalls: self, } } } impl<'a> Iterator for RuleSyscallsIter<&'a mut RuleSyscalls> { type Item = u32; fn next(&mut self) -> Option { while self.index < BITMASK_BIT_LEN { let index = self.index; self.index += 1; if self.syscalls.has(index) { return Some(index); } } None } } #[cfg(test)] mod test { use super::*; #[test] fn test_from_slice() { let s: Vec = vec![0xff; BITMASK_BYTE_LEN]; let syscalls = RuleSyscalls::from_slice(&s[..]).unwrap(); assert_eq!(syscalls.0, vec![0xffff_ffff; AUDIT_BITMASK_SIZE]); let s: Vec = vec![0; BITMASK_BYTE_LEN]; let syscalls = RuleSyscalls::from_slice(&s[..]).unwrap(); assert_eq!(syscalls.0, vec![0; AUDIT_BITMASK_SIZE]); } #[test] fn test_iter() { let s: Vec = vec![0xff; BITMASK_BYTE_LEN]; let syscalls = RuleSyscalls::from_slice(&s[..]).unwrap(); let mut iter = syscalls.into_iter(); for i in 0..BITMASK_BIT_LEN { assert_eq!(i, iter.next().unwrap()); } assert!(iter.next().is_none()); let s: Vec = vec![0; BITMASK_BYTE_LEN]; let syscalls = RuleSyscalls::from_slice(&s[..]).unwrap(); let mut iter = syscalls.into_iter(); assert!(iter.next().is_none()); } #[test] fn test_set_unset() { let mut syscalls = RuleSyscalls::new_zeroed(); for i in 0..BITMASK_BIT_LEN { syscalls.set(i); } assert_eq!(syscalls.0, vec![0xffff_ffff; AUDIT_BITMASK_SIZE]); for i in 0..BITMASK_BIT_LEN { syscalls.unset(BITMASK_BIT_LEN - 1 - i); } assert_eq!(syscalls.0, vec![0; AUDIT_BITMASK_SIZE]); } } netlink-packet-audit-0.5.1/src/rules/tests.rs000064400000000000000000000561651046102023000173120ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_utils::traits::{Emitable, Parseable}; use crate::rules::{ RuleAction, RuleBuffer, RuleField, RuleFieldFlags, RuleFlags, RuleMessage, RuleSyscalls, }; const AUDIT_ARCH_X86_64: u32 = 0xC000_003E; #[test] fn parse_rule_1() { let buf = RuleBuffer::new_checked(&M1_BYTES[..]).unwrap(); let parsed = RuleMessage::parse(&buf).unwrap(); assert_eq!(parsed, *M1); } #[test] fn emit_rule_1() { assert_eq!(M1.buffer_len(), M1_BYTES.len()); let mut buf = vec![0; M1_BYTES.len()]; M1.emit(&mut buf[..]); assert_eq!(&buf[..], &M1_BYTES[..]); } #[test] fn parse_rule_2() { let buf = RuleBuffer::new_checked(&M2_BYTES[..]).unwrap(); let parsed = RuleMessage::parse(&buf).unwrap(); assert_eq!(parsed, *M2); } #[test] fn emit_rule_2() { assert_eq!(M2.buffer_len(), M2_BYTES.len()); let mut buf = vec![0; M2_BYTES.len()]; M2.emit(&mut buf[..]); assert_eq!(&buf[..], &M2_BYTES[..]); } #[test] fn parse_rule_3() { let buf = RuleBuffer::new_checked(&M3_BYTES[..]).unwrap(); let parsed = RuleMessage::parse(&buf).unwrap(); assert_eq!(parsed, *M3); } #[test] fn emit_rule_3() { assert_eq!(M3.buffer_len(), M3_BYTES.len()); let mut buf = vec![0; M3_BYTES.len()]; M3.emit(&mut buf[..]); assert_eq!(&buf[..], &M3_BYTES[..]); } lazy_static! { // -w /etc/passwd -p rwxa static ref M1_BYTES: Vec = vec![ 0x04, 0x00, 0x00, 0x00, // flags 0x02, 0x00, 0x00, 0x00, // actions 0x02, 0x00, 0x00, 0x00, // field count // syscalls (64 u32) 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // fields (AUDIT_MAX_FIELD=64, but only the first 8 bytes are being used here) // // 0x69 = AUDIT_WATCH => the value is a path, so the field value is the path length, and the string itself is at the end of the buffer. 0x69, 0x00, 0x00, 0x00, // 0x6a = AUDIT_PERM 0x6a, 0x00, 0x00, 0x00, // Unused 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // values (AUDIT_MAX_FIELD=64, but only the first 8 bytes are being used here) 0x0b, 0x00, 0x00, 0x00, // 11 = length of the string 0x0f, 0x00, 0x00, 0x00, // 15 (not sure how it's interpreted) // Unused 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // fieldflags 0x00, 0x00, 0x00, 0x40, // equal 0x00, 0x00, 0x00, 0x40, // equal // Unused 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, // total buf length = 11 // buf /etc/passwd 0x2f, 0x65, 0x74, 0x63, 0x2f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x64]; static ref M1: RuleMessage = RuleMessage { flags: RuleFlags::FilterExit, action: RuleAction::Always, fields: vec![ ( RuleField::Watch("/etc/passwd".into()), RuleFieldFlags::Equal, ), (RuleField::Perm(15), RuleFieldFlags::Equal), ], syscalls: RuleSyscalls::new_maxed(), }; // -w /etc/passwd -p rwxa -k mykey static ref M2_BYTES: Vec = vec![ 0x04, 0x00, 0x00, 0x00, // flags 0x02, 0x00, 0x00, 0x00, // actions 0x03, 0x00, 0x00, 0x00, // fields count // syscalls 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // fields 0x69, 0x00, 0x00, 0x00, // AUDIT_WATCH 0x6a, 0x00, 0x00, 0x00, // AUDIT_PERM 0xd2, 0x00, 0x00, 0x00, // AUDIT_FILTERKEY // unused 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // values 0x0b, 0x00, 0x00, 0x00, // length of the AUDIT_WATCH value (11) 0x0f, 0x00, 0x00, 0x00, // value of the AUDIT_PERM field (15) 0x05, 0x00, 0x00, 0x00, // length of the AUDIT_FILTERKEY field (5) // unused 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // fieldflags 0x00, 0x00, 0x00, 0x40, // equal 0x00, 0x00, 0x00, 0x40, // equal 0x00, 0x00, 0x00, 0x40, // equal // unused 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // string buffer 0x10, 0x00, 0x00, 0x00, // buffer length = 16 0x2f, 0x65, 0x74, 0x63, 0x2f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x64, // value of AUDIT_WATCH ("/etc/passwd") 0x6d, 0x79, 0x6b, 0x65, 0x79 // value of AUDIT_FILTERKEY ("mykey") ]; static ref M2: RuleMessage = RuleMessage { flags: RuleFlags::FilterExit, action: RuleAction::Always, fields: vec![ (RuleField::Watch("/etc/passwd".into()), RuleFieldFlags::Equal), (RuleField::Perm(15), RuleFieldFlags::Equal), (RuleField::Filterkey("mykey".into()), RuleFieldFlags::Equal), ], syscalls: RuleSyscalls::new_maxed(), }; // -a always,exit -F arch=b64 -S personality -F key=bypass static ref M3_BYTES: Vec = vec![ 0x04, 0x00, 0x00, 0x00, // flags 0x02, 0x00, 0x00, 0x00, // actions 0x02, 0x00, 0x00, 0x00, // field count // syscalls (execve) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // fields 0x0b, 0x00, 0x00, 0x00, // AUDIT_ARCH 0xd2, 0x00, 0x00, 0x00, // AUDIT_FILTERKEY // unused 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // values 0x3e, 0x00, 0x00, 0xc0, // AUDIT_ARCH_X86_64 0x06, 0x00, 0x00, 0x00, // length of the string = 6 // unused 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // fieldflags 0x00, 0x00, 0x00, 0x40, // equal 0x00, 0x00, 0x00, 0x40, // equal // unused 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, // buffer totoal length (6) 0x62, 0x79, 0x70, 0x61, 0x73, 0x73 // bypass ]; static ref M3: RuleMessage = { let mut syscalls = RuleSyscalls::new_zeroed(); syscalls.set(135); RuleMessage { flags: RuleFlags::FilterExit, action: RuleAction::Always, fields: vec![ (RuleField::Arch(AUDIT_ARCH_X86_64), RuleFieldFlags::Equal), (RuleField::Filterkey("bypass".into()), RuleFieldFlags::Equal), ], syscalls, } }; } netlink-packet-audit-0.5.1/src/status.rs000064400000000000000000000136041046102023000163300ustar 00000000000000// SPDX-License-Identifier: MIT use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ traits::{Emitable, Parseable}, DecodeError, }; use crate::Field; const MASK: Field = 0..4; const ENABLED: Field = 4..8; const FAILURE: Field = 8..12; const PID: Field = 12..16; const RATE_LIMITING: Field = 16..20; const BACKLOG_LIMIT: Field = 20..24; const LOST: Field = 24..28; const BACKLOG: Field = 28..32; const FEATURE_BITMAP: Field = 32..36; const BACKLOG_WAIT_TIME: Field = 36..40; pub const STATUS_MESSAGE_LEN: usize = BACKLOG_WAIT_TIME.end; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct StatusMessage { /// Bit mask for valid entries pub mask: u32, pub enabled: u32, /// Failure-to-log action pub failure: u32, /// PID of auditd process pub pid: u32, /// Message rate limit (per second) pub rate_limiting: u32, /// Waiting messages limit pub backlog_limit: u32, /// Messages lost pub lost: u32, /// Messages waiting in queue pub backlog: u32, /// bitmap of kernel audit features pub feature_bitmap: u32, /// Message queue wait timeout pub backlog_wait_time: u32, } impl StatusMessage { pub fn new() -> Self { Default::default() } } #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub struct StatusMessageBuffer { buffer: T, } impl> StatusMessageBuffer { pub fn new(buffer: T) -> StatusMessageBuffer { StatusMessageBuffer { buffer } } pub fn new_checked( buffer: T, ) -> Result, DecodeError> { let buf = Self::new(buffer); buf.check_buffer_length()?; Ok(buf) } fn check_buffer_length(&self) -> Result<(), DecodeError> { let len = self.buffer.as_ref().len(); if len < STATUS_MESSAGE_LEN { return Err(format!( "invalid StatusMessageBuffer buffer: length is {len} \ instead of {STATUS_MESSAGE_LEN}" ) .into()); } Ok(()) } pub fn into_inner(self) -> T { self.buffer } pub fn mask(&self) -> u32 { NativeEndian::read_u32(&self.buffer.as_ref()[MASK]) } pub fn enabled(&self) -> u32 { NativeEndian::read_u32(&self.buffer.as_ref()[ENABLED]) } pub fn failure(&self) -> u32 { NativeEndian::read_u32(&self.buffer.as_ref()[FAILURE]) } pub fn pid(&self) -> u32 { NativeEndian::read_u32(&self.buffer.as_ref()[PID]) } pub fn rate_limiting(&self) -> u32 { NativeEndian::read_u32(&self.buffer.as_ref()[RATE_LIMITING]) } pub fn backlog_limit(&self) -> u32 { NativeEndian::read_u32(&self.buffer.as_ref()[BACKLOG_LIMIT]) } pub fn lost(&self) -> u32 { NativeEndian::read_u32(&self.buffer.as_ref()[LOST]) } pub fn backlog(&self) -> u32 { NativeEndian::read_u32(&self.buffer.as_ref()[BACKLOG]) } pub fn feature_bitmap(&self) -> u32 { NativeEndian::read_u32(&self.buffer.as_ref()[FEATURE_BITMAP]) } pub fn backlog_wait_time(&self) -> u32 { NativeEndian::read_u32(&self.buffer.as_ref()[BACKLOG_WAIT_TIME]) } } impl + AsMut<[u8]>> StatusMessageBuffer { pub fn set_mask(&mut self, value: u32) { NativeEndian::write_u32(&mut self.buffer.as_mut()[MASK], value) } pub fn set_enabled(&mut self, value: u32) { NativeEndian::write_u32(&mut self.buffer.as_mut()[ENABLED], value) } pub fn set_failure(&mut self, value: u32) { NativeEndian::write_u32(&mut self.buffer.as_mut()[FAILURE], value) } pub fn set_pid(&mut self, value: u32) { NativeEndian::write_u32(&mut self.buffer.as_mut()[PID], value) } pub fn set_rate_limiting(&mut self, value: u32) { NativeEndian::write_u32(&mut self.buffer.as_mut()[RATE_LIMITING], value) } pub fn set_backlog_limit(&mut self, value: u32) { NativeEndian::write_u32(&mut self.buffer.as_mut()[BACKLOG_LIMIT], value) } pub fn set_lost(&mut self, value: u32) { NativeEndian::write_u32(&mut self.buffer.as_mut()[LOST], value) } pub fn set_backlog(&mut self, value: u32) { NativeEndian::write_u32(&mut self.buffer.as_mut()[BACKLOG], value) } pub fn set_feature_bitmap(&mut self, value: u32) { NativeEndian::write_u32( &mut self.buffer.as_mut()[FEATURE_BITMAP], value, ) } pub fn set_backlog_wait_time(&mut self, value: u32) { NativeEndian::write_u32( &mut self.buffer.as_mut()[BACKLOG_WAIT_TIME], value, ) } } impl> Parseable> for StatusMessage { fn parse(buf: &StatusMessageBuffer) -> Result { buf.check_buffer_length()?; Ok(StatusMessage { mask: buf.mask(), enabled: buf.enabled(), failure: buf.failure(), pid: buf.pid(), rate_limiting: buf.rate_limiting(), backlog_limit: buf.backlog_limit(), lost: buf.lost(), backlog: buf.backlog(), feature_bitmap: buf.feature_bitmap(), backlog_wait_time: buf.backlog_wait_time(), }) } } impl Emitable for StatusMessage { fn buffer_len(&self) -> usize { STATUS_MESSAGE_LEN } fn emit(&self, buffer: &mut [u8]) { let mut buffer = StatusMessageBuffer::new(buffer); buffer.set_mask(self.mask); buffer.set_enabled(self.enabled); buffer.set_failure(self.failure); buffer.set_pid(self.pid); buffer.set_rate_limiting(self.rate_limiting); buffer.set_backlog_limit(self.backlog_limit); buffer.set_lost(self.lost); buffer.set_backlog(self.backlog); buffer.set_feature_bitmap(self.feature_bitmap); buffer.set_backlog_wait_time(self.backlog_wait_time); } }