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
//! [`EventSu`]

use std::ffi::OsStr;

use endpoint_sec_sys::{es_event_su_t, es_string_token_t};
use libc::uid_t;

/// A `su` policy decision event.
#[doc(alias = "es_event_su_t")]
pub struct EventSu<'a> {
    /// The raw event.
    pub(crate) raw: &'a es_event_su_t,
}

impl<'a> EventSu<'a> {
    /// True iff su was successful.
    #[inline(always)]
    pub fn success(&self) -> bool {
        self.raw.success
    }

    /// If `success` is `false`, a failure message is contained in this field.
    #[inline(always)]
    pub fn failure_message(&self) -> Option<&'a OsStr> {
        match self.success() {
            false => None,
            // Safety: checked for `success` value, lifetime matches that of event
            true => Some(unsafe { self.raw.failure_message.as_os_str() }),
        }
    }

    /// The uid of the user who initiated the su.
    #[inline(always)]
    pub fn from_uid(&self) -> uid_t {
        self.raw.from_uid
    }

    /// The name of the user who initiated the su.
    #[inline(always)]
    pub fn from_username(&self) -> &'a OsStr {
        // Safety: lifetime matches that of message
        unsafe { self.raw.from_username.as_os_str() }
    }

    /// True iff su was successful, Describes whether or not the to_uid is interpretable
    #[inline(always)]
    pub fn has_to_uid(&self) -> bool {
        self.raw.has_to_uid
    }

    /// If success, the user ID that is going to be substituted
    #[inline(always)]
    pub fn to_uid(&self) -> Option<uid_t> {
        // Safety: checked for success and `has_to_uid`
        #[allow(clippy::unnecessary_lazy_evaluations)]
        (self.success() && self.has_to_uid()).then(|| unsafe { self.raw.to_uid.uid })
    }

    /// If success, the user name that is going to be substituted
    #[inline(always)]
    pub fn to_username(&self) -> Option<&'a OsStr> {
        match self.success() {
            false => None,
            // Safety: checked for success, lifetime matches that of event
            true => unsafe { Some(self.raw.to_username.as_os_str()) },
        }
    }

    /// If success, the shell that is going to be executed
    #[inline(always)]
    pub fn shell(&self) -> Option<&'a OsStr> {
        match self.success() {
            false => None,
            // Safety: checked for success, lifetime matches that of event
            true => unsafe { Some(self.raw.shell.as_os_str()) },
        }
    }

    /// Argument count
    #[inline(always)]
    pub fn arg_count(&self) -> usize {
        self.raw.argc
    }

    /// Environment count
    #[inline(always)]
    pub fn env_count(&self) -> usize {
        self.raw.env_count
    }

    /// If success, the arguments are passed into to the shell
    #[inline(always)]
    pub fn args<'e>(&'e self) -> Option<SuArgs<'e, 'a>> {
        match self.success() {
            false => None,
            true => Some(SuArgs::new(self)),
        }
    }

    /// If success, list of environment variables that is going to be substituted
    #[inline(always)]
    pub fn envs<'e>(&'e self) -> Option<SuEnvs<'e, 'a>> {
        match self.success() {
            false => None,
            true => Some(SuEnvs::new(self)),
        }
    }
}

// Safety: safe to send across threads: does not contain any interior mutability nor depend on current thread state
unsafe impl Send for EventSu<'_> {}

impl_debug_eq_hash_with_functions!(EventSu<'a>; success, failure_message, from_uid, from_username, to_uid, to_username, shell, arg_count, env_count);

/// Read the `idx` arg of `raw`
///
/// # Safety
///
/// Must be called with a valid event for which `idx` is in range `0..raw.argc`
unsafe fn read_nth_arg(raw: &es_event_su_t, idx: usize) -> es_string_token_t {
    std::ptr::read(raw.argv.add(idx))
}

/// Read the `idx` env of `raw`
///
/// # Safety
///
/// Must be called with a valid event for which `idx` is in range `0..raw.env_count`
unsafe fn read_nth_env(raw: &es_event_su_t, idx: usize) -> es_string_token_t {
    std::ptr::read(raw.env.add(idx))
}

make_event_data_iterator!(
    EventSu;
    /// Iterator over the arguments of an [`EventSu`]
    SuArgs with arg_count (usize);
    &'raw OsStr;
    read_nth_arg,
    super::as_os_str,
);

make_event_data_iterator!(
    EventSu;
    /// Iterator over the environment of an [`EventSu`]
    SuEnvs with env_count (usize);
    &'raw OsStr;
    read_nth_env,
    super::as_os_str,
);