selinux/policy/
metadata.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use super::error::{ParseError, ValidateError};
6use super::parser::PolicyCursor;
7use super::{
8    Array, Counted, Parse, PolicyValidationContext, Validate, ValidateArray, array_type,
9    array_type_validate_deref_both,
10};
11
12use zerocopy::{FromBytes, Immutable, KnownLayout, Unaligned, little_endian as le};
13
14pub(super) const SELINUX_MAGIC: u32 = 0xf97cff8c;
15
16pub(super) const POLICYDB_STRING_MAX_LENGTH: u32 = 32;
17pub(super) const POLICYDB_SIGNATURE: &[u8] = b"SE Linux";
18
19pub(super) const POLICYDB_VERSION_MIN: u32 = 30;
20pub const POLICYDB_VERSION_MAX: u32 = 33;
21
22pub(super) const CONFIG_MLS_FLAG: u32 = 1;
23pub(super) const CONFIG_HANDLE_UNKNOWN_REJECT_FLAG: u32 = 1 << 1;
24pub(super) const CONFIG_HANDLE_UNKNOWN_ALLOW_FLAG: u32 = 1 << 2;
25pub(super) const CONFIG_HANDLE_UNKNOWN_MASK: u32 =
26    CONFIG_HANDLE_UNKNOWN_REJECT_FLAG | CONFIG_HANDLE_UNKNOWN_ALLOW_FLAG;
27
28#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
29#[repr(C, packed)]
30pub(super) struct Magic(le::U32);
31
32impl Validate for Magic {
33    type Error = ValidateError;
34
35    fn validate(&self, _context: &mut PolicyValidationContext) -> Result<(), Self::Error> {
36        let found_magic = self.0.get();
37        if found_magic != SELINUX_MAGIC {
38            Err(ValidateError::InvalidMagic { found_magic })
39        } else {
40            Ok(())
41        }
42    }
43}
44
45array_type!(Signature, SignatureMetadata, Vec<u8>);
46
47array_type_validate_deref_both!(Signature);
48
49impl ValidateArray<SignatureMetadata, u8> for Signature {
50    type Error = ValidateError;
51
52    fn validate_array(
53        _context: &mut PolicyValidationContext,
54        _metadata: &SignatureMetadata,
55        items: &[u8],
56    ) -> Result<(), Self::Error> {
57        if items != POLICYDB_SIGNATURE {
58            Err(ValidateError::InvalidSignature { found_signature: items.to_owned() })
59        } else {
60            Ok(())
61        }
62    }
63}
64
65#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
66#[repr(C, packed)]
67pub(super) struct SignatureMetadata(le::U32);
68
69impl Validate for SignatureMetadata {
70    type Error = ValidateError;
71
72    /// [`SignatureMetadata`] has no constraints.
73    fn validate(&self, _context: &mut PolicyValidationContext) -> Result<(), Self::Error> {
74        let found_length = self.0.get();
75        if found_length > POLICYDB_STRING_MAX_LENGTH {
76            Err(ValidateError::InvalidSignatureLength { found_length })
77        } else {
78            Ok(())
79        }
80    }
81}
82
83impl Counted for SignatureMetadata {
84    fn count(&self) -> u32 {
85        self.0.get()
86    }
87}
88
89#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
90#[repr(C, packed)]
91pub(super) struct PolicyVersion(le::U32);
92
93impl PolicyVersion {
94    pub fn policy_version(&self) -> u32 {
95        self.0.get()
96    }
97}
98
99impl Validate for PolicyVersion {
100    type Error = ValidateError;
101
102    fn validate(&self, _context: &mut PolicyValidationContext) -> Result<(), Self::Error> {
103        let found_policy_version = self.0.get();
104        if found_policy_version < POLICYDB_VERSION_MIN
105            || found_policy_version > POLICYDB_VERSION_MAX
106        {
107            Err(ValidateError::InvalidPolicyVersion { found_policy_version })
108        } else {
109            Ok(())
110        }
111    }
112}
113
114#[derive(Debug)]
115pub(super) struct Config {
116    handle_unknown: HandleUnknown,
117
118    #[allow(dead_code)]
119    config: le::U32,
120}
121
122impl Config {
123    pub fn handle_unknown(&self) -> HandleUnknown {
124        self.handle_unknown
125    }
126}
127
128impl Parse for Config {
129    type Error = ParseError;
130
131    fn parse(bytes: PolicyCursor) -> Result<(Self, PolicyCursor), Self::Error> {
132        let (config, tail) = PolicyCursor::parse::<le::U32>(bytes)?;
133
134        let found_config = config.get();
135        if found_config & CONFIG_MLS_FLAG == 0 {
136            return Err(ParseError::ConfigMissingMlsFlag { found_config });
137        }
138        let handle_unknown = try_handle_unknown_fom_config(found_config)?;
139
140        Ok((Self { handle_unknown, config }, tail))
141    }
142}
143
144impl Validate for Config {
145    type Error = anyhow::Error;
146
147    /// All validation for [`Config`] is necessary to parse it correctly. No additional validation
148    /// required.
149    fn validate(&self, _context: &mut PolicyValidationContext) -> Result<(), Self::Error> {
150        Ok(())
151    }
152}
153
154#[derive(Copy, Clone, Debug, PartialEq)]
155pub enum HandleUnknown {
156    Deny,
157    Reject,
158    Allow,
159}
160
161fn try_handle_unknown_fom_config(config: u32) -> Result<HandleUnknown, ParseError> {
162    match config & CONFIG_HANDLE_UNKNOWN_MASK {
163        CONFIG_HANDLE_UNKNOWN_ALLOW_FLAG => Ok(HandleUnknown::Allow),
164        CONFIG_HANDLE_UNKNOWN_REJECT_FLAG => Ok(HandleUnknown::Reject),
165        0 => Ok(HandleUnknown::Deny),
166        _ => Err(ParseError::InvalidHandleUnknownConfigurationBits {
167            masked_bits: (config & CONFIG_HANDLE_UNKNOWN_MASK),
168        }),
169    }
170}
171
172#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
173#[repr(C, packed)]
174pub(super) struct Counts {
175    symbols_count: le::U32,
176    object_context_count: le::U32,
177}
178
179impl Validate for Counts {
180    type Error = anyhow::Error;
181
182    /// [`Counts`] have no internal consistency requirements.
183    fn validate(&self, _context: &mut PolicyValidationContext) -> Result<(), Self::Error> {
184        Ok(())
185    }
186}
187
188#[cfg(test)]
189mod tests {
190    use super::super::parser::PolicyCursor;
191    use super::super::testing::{as_parse_error, as_validate_error};
192    use super::super::{PolicyValidationContext, Validate};
193    use super::*;
194
195    use std::sync::Arc;
196
197    macro_rules! validate_test {
198        ($parse_output:ident, $data:expr, $result:tt, $check_impl:block) => {{
199            let data = Arc::new($data);
200            let mut context = PolicyValidationContext { data: data.clone() };
201            fn check_by_value($result: Result<(), <$parse_output as Validate>::Error>) {
202                $check_impl
203            }
204
205            let (by_value_parsed, _tail) = $parse_output::parse(PolicyCursor::new(data.clone()))
206                .expect("successful parse for validate test");
207            let by_value_result = by_value_parsed.validate(&mut context);
208            check_by_value(by_value_result);
209        }};
210    }
211
212    // TODO: Run this test over `validate()`.
213    #[test]
214    fn no_magic() {
215        let mut bytes = [SELINUX_MAGIC.to_le_bytes().as_slice()].concat();
216        // One byte short of magic.
217        bytes.pop();
218        let data = Arc::new(bytes);
219        assert_eq!(
220            Err(ParseError::MissingData {
221                type_name: "selinux_lib_test::policy::metadata::Magic",
222                type_size: 4,
223                num_bytes: 3
224            }),
225            PolicyCursor::parse::<Magic>(PolicyCursor::new(data)),
226        );
227    }
228
229    #[test]
230    fn invalid_magic() {
231        let mut bytes = [SELINUX_MAGIC.to_le_bytes().as_slice()].concat();
232        // Invalid first byte of magic.
233        bytes[0] = bytes[0] + 1;
234        let bytes = bytes;
235        let expected_invalid_magic =
236            u32::from_le_bytes(bytes.clone().as_slice().try_into().unwrap());
237
238        let data = Arc::new(bytes);
239        let mut context = PolicyValidationContext { data: data.clone() };
240        let (magic, tail) =
241            PolicyCursor::parse::<Magic>(PolicyCursor::new(data.clone())).expect("magic");
242        assert_eq!(data.len(), tail.offset() as usize);
243        assert_eq!(
244            Err(ValidateError::InvalidMagic { found_magic: expected_invalid_magic }),
245            magic.validate(&mut context)
246        );
247    }
248
249    #[test]
250    fn invalid_signature_length() {
251        const INVALID_SIGNATURE_LENGTH: u32 = POLICYDB_STRING_MAX_LENGTH + 1;
252        let bytes: Vec<u8> = [
253            INVALID_SIGNATURE_LENGTH.to_le_bytes().as_slice(),
254            [42u8; INVALID_SIGNATURE_LENGTH as usize].as_slice(),
255        ]
256        .concat();
257
258        validate_test!(Signature, bytes, result, {
259            assert_eq!(
260                Some(ValidateError::InvalidSignatureLength {
261                    found_length: INVALID_SIGNATURE_LENGTH
262                }),
263                result.err().map(as_validate_error),
264            );
265        });
266    }
267
268    #[test]
269    fn missing_signature() {
270        let bytes = [(1 as u32).to_le_bytes().as_slice()].concat();
271        match Signature::parse(PolicyCursor::new(Arc::new(bytes))).err().map(as_parse_error) {
272            Some(ParseError::MissingData { type_name: "u8", type_size: 1, num_bytes: 0 }) => {}
273            parse_err => {
274                assert!(false, "Expected Some(MissingData...), but got {:?}", parse_err);
275            }
276        }
277    }
278
279    #[test]
280    fn invalid_signature() {
281        // Invalid signature "TE Linux" is not "SE Linux".
282        const INVALID_SIGNATURE: &[u8] = b"TE Linux";
283
284        let bytes =
285            [(INVALID_SIGNATURE.len() as u32).to_le_bytes().as_slice(), INVALID_SIGNATURE].concat();
286
287        validate_test!(Signature, bytes, result, {
288            assert_eq!(
289                Some(ValidateError::InvalidSignature {
290                    found_signature: INVALID_SIGNATURE.to_owned()
291                }),
292                result.err().map(as_validate_error),
293            );
294        });
295    }
296
297    #[test]
298    fn invalid_policy_version() {
299        let bytes = [(POLICYDB_VERSION_MIN - 1).to_le_bytes().as_slice()].concat();
300        let data = Arc::new(bytes);
301        let mut context = PolicyValidationContext { data: data.clone() };
302        let (policy_version, tail) =
303            PolicyCursor::parse::<PolicyVersion>(PolicyCursor::new(data.clone())).expect("magic");
304        assert_eq!(data.len(), tail.offset() as usize);
305        assert_eq!(
306            Err(ValidateError::InvalidPolicyVersion {
307                found_policy_version: POLICYDB_VERSION_MIN - 1
308            }),
309            policy_version.validate(&mut context)
310        );
311
312        let bytes = [(POLICYDB_VERSION_MAX + 1).to_le_bytes().as_slice()].concat();
313        let data = Arc::new(bytes);
314        let mut context = PolicyValidationContext { data: data.clone() };
315        let (policy_version, tail) =
316            PolicyCursor::parse::<PolicyVersion>(PolicyCursor::new(data.clone())).expect("magic");
317        assert_eq!(data.len(), tail.offset() as usize);
318        assert_eq!(
319            Err(ValidateError::InvalidPolicyVersion {
320                found_policy_version: POLICYDB_VERSION_MAX + 1
321            }),
322            policy_version.validate(&mut context)
323        );
324    }
325
326    #[test]
327    fn config_missing_mls_flag() {
328        let bytes = [(!CONFIG_MLS_FLAG).to_le_bytes().as_slice()].concat();
329        match Config::parse(PolicyCursor::new(Arc::new(bytes))).err() {
330            Some(ParseError::ConfigMissingMlsFlag { .. }) => {}
331            parse_err => {
332                assert!(false, "Expected Some(ConfigMissingMlsFlag...), but got {:?}", parse_err);
333            }
334        }
335    }
336
337    #[test]
338    fn invalid_handle_unknown() {
339        let bytes = [(CONFIG_MLS_FLAG
340            | CONFIG_HANDLE_UNKNOWN_ALLOW_FLAG
341            | CONFIG_HANDLE_UNKNOWN_REJECT_FLAG)
342            .to_le_bytes()
343            .as_slice()]
344        .concat();
345        assert_eq!(
346            Some(ParseError::InvalidHandleUnknownConfigurationBits {
347                masked_bits: CONFIG_HANDLE_UNKNOWN_ALLOW_FLAG | CONFIG_HANDLE_UNKNOWN_REJECT_FLAG
348            }),
349            Config::parse(PolicyCursor::new(Arc::new(bytes))).err()
350        );
351    }
352}