1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// SPDX-License-Identifier: MIT

use std::process;

use futures::{
    future::{self, Either},
    stream::{Stream, StreamExt, TryStream},
    FutureExt,
};
use netlink_proto::{sys::SocketAddr, ConnectionHandle};

use crate::packet::{
    rules::RuleMessage,
    AuditMessage,
    NetlinkMessage,
    NetlinkPayload,
    StatusMessage,
    NLM_F_ACK,
    NLM_F_CREATE,
    NLM_F_DUMP,
    NLM_F_EXCL,
    NLM_F_NONREC,
    NLM_F_REQUEST,
};

// ==========================================
// mask values
// ==========================================
pub const AUDIT_STATUS_ENABLED: u32 = 1;
pub const AUDIT_STATUS_FAILURE: u32 = 2;
pub const AUDIT_STATUS_PID: u32 = 4;
pub const AUDIT_STATUS_RATE_LIMIT: u32 = 8;
pub const AUDIT_STATUS_BACKLOG_LIMIT: u32 = 16;
pub const AUDIT_STATUS_BACKLOG_WAIT_TIME: u32 = 32;
pub const AUDIT_STATUS_LOST: u32 = 64;
pub const AUDIT_FEATURE_BITMAP_BACKLOG_LIMIT: u32 = 1;
pub const AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME: u32 = 2;
pub const AUDIT_FEATURE_BITMAP_EXECUTABLE_PATH: u32 = 4;
pub const AUDIT_FEATURE_BITMAP_EXCLUDE_EXTEND: u32 = 8;
pub const AUDIT_FEATURE_BITMAP_SESSIONID_FILTER: u32 = 16;
pub const AUDIT_FEATURE_BITMAP_LOST_RESET: u32 = 32;
pub const AUDIT_FEATURE_BITMAP_FILTER_FS: u32 = 64;
pub const AUDIT_FEATURE_BITMAP_ALL: u32 = 127;
pub const AUDIT_VERSION_LATEST: u32 = 127;
pub const AUDIT_VERSION_BACKLOG_LIMIT: u32 = 1;
pub const AUDIT_VERSION_BACKLOG_WAIT_TIME: u32 = 2;

use crate::Error;

/// A handle to the netlink connection, used to send and receive netlink messsage
#[derive(Clone, Debug)]
pub struct Handle(ConnectionHandle<AuditMessage>);

impl Handle {
    pub(crate) fn new(conn: ConnectionHandle<AuditMessage>) -> Self {
        Handle(conn)
    }

    /// Send a netlink message, and get the reponse as a stream of messages.
    pub fn request(
        &mut self,
        message: NetlinkMessage<AuditMessage>,
    ) -> Result<impl Stream<Item = NetlinkMessage<AuditMessage>>, Error> {
        self.0
            .request(message, SocketAddr::new(0, 0))
            .map_err(|_| Error::RequestFailed)
    }

    /// Send a netlink message that expects an acknowledgement. The returned future resolved when
    /// that ACK is received. If anything else is received, the future resolves into an error.
    async fn acked_request(&mut self, message: NetlinkMessage<AuditMessage>) -> Result<(), Error> {
        let mut response = self.request(message)?;
        if let Some(message) = response.next().await {
            let (header, payload) = message.into_parts();
            // NetlinkError and AuditMessage are forwarded to the
            // handle. Ack is signaled by the stream finishing.
            if let NetlinkPayload::Error(err_msg) = payload {
                Err(Error::NetlinkError(err_msg))
            } else {
                Err(Error::UnexpectedMessage(NetlinkMessage::new(
                    header, payload,
                )))
            }
        } else {
            Ok(())
        }
    }

    /// Add the given rule
    pub async fn add_rule(&mut self, rule: RuleMessage) -> Result<(), Error> {
        let mut req = NetlinkMessage::from(AuditMessage::AddRule(rule));
        req.header.flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE;
        self.acked_request(req).await
    }

    /// Deletes a given rule
    pub async fn del_rule(&mut self, rule: RuleMessage) -> Result<(), Error> {
        let mut req = NetlinkMessage::from(AuditMessage::DelRule(rule));
        req.header.flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_NONREC;
        self.acked_request(req).await
    }

    /// List the current rules
    pub fn list_rules(&mut self) -> impl TryStream<Ok = RuleMessage, Error = Error> {
        let mut req = NetlinkMessage::from(AuditMessage::ListRules(None));
        req.header.flags = NLM_F_REQUEST | NLM_F_DUMP;

        match self.request(req) {
            Ok(response) => Either::Left(response.map(move |msg| {
                let (header, payload) = msg.into_parts();
                match payload {
                    NetlinkPayload::InnerMessage(AuditMessage::ListRules(Some(rule_msg))) => {
                        Ok(rule_msg)
                    }
                    NetlinkPayload::Error(err_msg) => Err(Error::NetlinkError(err_msg)),
                    _ => Err(Error::UnexpectedMessage(NetlinkMessage::new(
                        header, payload,
                    ))),
                }
            })),
            Err(e) => Either::Right(future::err::<RuleMessage, Error>(e).into_stream()),
        }
    }

    /// Enable receiving audit events
    pub async fn enable_events(&mut self) -> Result<(), Error> {
        let mut status = StatusMessage::new();
        status.enabled = 1;
        status.pid = process::id();
        status.mask = AUDIT_STATUS_ENABLED | AUDIT_STATUS_PID;
        let mut req = NetlinkMessage::from(AuditMessage::SetStatus(status));
        req.header.flags = NLM_F_REQUEST | NLM_F_ACK;
        self.acked_request(req).await
    }

    /// Get current audit status
    pub async fn get_status(&mut self) -> Result<StatusMessage, Error> {
        let mut req = NetlinkMessage::from(AuditMessage::GetStatus(None));
        req.header.flags = NLM_F_REQUEST | NLM_F_DUMP;
        let mut request = self.request(req)?;

        let response = request.next().await.ok_or(Error::RequestFailed)?;

        match response.into_parts() {
            (_, NetlinkPayload::InnerMessage(AuditMessage::GetStatus(Some(status)))) => Ok(status),
            (header, payload) => Err(Error::UnexpectedMessage(NetlinkMessage::new(
                header, payload,
            ))),
        }
    }
}