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::ValidateError;
6use super::parser::ParseStrategy;
7use super::{
8    array_type, array_type_validate_deref_both, Array, Counted, Parse, ParseError, Validate,
9    ValidateArray,
10};
11
12use std::fmt::Debug;
13use zerocopy::{little_endian as le, FromBytes, Immutable, KnownLayout, Unaligned};
14
15pub(super) const SELINUX_MAGIC: u32 = 0xf97cff8c;
16
17pub(super) const POLICYDB_STRING_MAX_LENGTH: u32 = 32;
18pub(super) const POLICYDB_SIGNATURE: &[u8] = b"SE Linux";
19
20pub(super) const POLICYDB_VERSION_MIN: u32 = 30;
21pub(super) const POLICYDB_VERSION_MAX: u32 = 33;
22
23pub(super) const CONFIG_MLS_FLAG: u32 = 1;
24pub(super) const CONFIG_HANDLE_UNKNOWN_REJECT_FLAG: u32 = 1 << 1;
25pub(super) const CONFIG_HANDLE_UNKNOWN_ALLOW_FLAG: u32 = 1 << 2;
26pub(super) const CONFIG_HANDLE_UNKNOWN_MASK: u32 =
27    CONFIG_HANDLE_UNKNOWN_REJECT_FLAG | CONFIG_HANDLE_UNKNOWN_ALLOW_FLAG;
28
29#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
30#[repr(C, packed)]
31pub(super) struct Magic(le::U32);
32
33impl Validate for Magic {
34    type Error = ValidateError;
35
36    fn validate(&self) -> Result<(), Self::Error> {
37        let found_magic = self.0.get();
38        if found_magic != SELINUX_MAGIC {
39            Err(ValidateError::InvalidMagic { found_magic })
40        } else {
41            Ok(())
42        }
43    }
44}
45
46array_type!(Signature, PS, PS::Output<SignatureMetadata>, PS::Slice<u8>);
47
48array_type_validate_deref_both!(Signature);
49
50impl<PS: ParseStrategy> ValidateArray<SignatureMetadata, u8> for Signature<PS> {
51    type Error = ValidateError;
52
53    fn validate_array<'a>(
54        _metadata: &'a SignatureMetadata,
55        data: &'a [u8],
56    ) -> Result<(), Self::Error> {
57        if data != POLICYDB_SIGNATURE {
58            Err(ValidateError::InvalidSignature { found_signature: data.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) -> 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) -> 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/// TODO: Eliminate `dead_code` guard.
115#[allow(dead_code)]
116#[derive(Debug)]
117pub(super) struct Config<PS: ParseStrategy> {
118    handle_unknown: HandleUnknown,
119    config: PS::Output<le::U32>,
120}
121
122impl<PS: ParseStrategy> Config<PS> {
123    pub fn handle_unknown(&self) -> HandleUnknown {
124        self.handle_unknown
125    }
126}
127
128impl<PS: ParseStrategy> Parse<PS> for Config<PS> {
129    type Error = ParseError;
130
131    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
132        let num_bytes = bytes.len();
133        let (config, tail) = PS::parse::<le::U32>(bytes).ok_or(ParseError::MissingData {
134            type_name: "Config",
135            type_size: std::mem::size_of::<le::U32>(),
136            num_bytes,
137        })?;
138
139        let found_config = PS::deref(&config).get();
140        if found_config & CONFIG_MLS_FLAG == 0 {
141            return Err(ParseError::ConfigMissingMlsFlag { found_config });
142        }
143        let handle_unknown = try_handle_unknown_fom_config(found_config)?;
144
145        Ok((Self { handle_unknown, config }, tail))
146    }
147}
148
149impl<PS: ParseStrategy> Validate for Config<PS> {
150    type Error = anyhow::Error;
151
152    /// All validation for [`Config`] is necessary to parse it correctly. No additional validation
153    /// required.
154    fn validate(&self) -> Result<(), Self::Error> {
155        Ok(())
156    }
157}
158
159#[derive(Copy, Clone, Debug, PartialEq)]
160pub enum HandleUnknown {
161    Deny,
162    Reject,
163    Allow,
164}
165
166fn try_handle_unknown_fom_config(config: u32) -> Result<HandleUnknown, ParseError> {
167    match config & CONFIG_HANDLE_UNKNOWN_MASK {
168        CONFIG_HANDLE_UNKNOWN_ALLOW_FLAG => Ok(HandleUnknown::Allow),
169        CONFIG_HANDLE_UNKNOWN_REJECT_FLAG => Ok(HandleUnknown::Reject),
170        0 => Ok(HandleUnknown::Deny),
171        _ => Err(ParseError::InvalidHandleUnknownConfigurationBits {
172            masked_bits: (config & CONFIG_HANDLE_UNKNOWN_MASK),
173        }),
174    }
175}
176
177#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
178#[repr(C, packed)]
179pub(super) struct Counts {
180    symbols_count: le::U32,
181    object_context_count: le::U32,
182}
183
184impl Validate for Counts {
185    type Error = anyhow::Error;
186
187    /// [`Counts`] have no internal consistency requirements.
188    fn validate(&self) -> Result<(), Self::Error> {
189        Ok(())
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196
197    use crate::policy::parser::{ByRef, ByValue};
198    use crate::policy::testing::{as_parse_error, as_validate_error};
199
200    macro_rules! validate_test {
201        ($parse_output:ident, $data:expr, $result:tt, $check_impl:block) => {{
202            let data = $data;
203            fn check_by_ref<'a>(
204                $result: Result<
205                    (),
206                    <$parse_output<ByRef<&'a [u8]>> as crate::policy::Validate>::Error,
207                >,
208            ) {
209                $check_impl;
210            }
211
212            fn check_by_value(
213                $result: Result<
214                    (),
215                    <$parse_output<ByValue<Vec<u8>>> as crate::policy::Validate>::Error,
216                >,
217            ) {
218                $check_impl
219            }
220
221            let by_ref = ByRef::new(data.as_slice());
222            let (by_ref_parsed, _) =
223                $parse_output::parse(by_ref).expect("successful parse for validate test");
224            let by_ref_result = by_ref_parsed.validate();
225            check_by_ref(by_ref_result);
226            let (by_value_parsed, _) = $parse_output::<ByValue<Vec<u8>>>::parse(ByValue::new(data))
227                .expect("successful parse for validate test");
228            let by_value_result = by_value_parsed.validate();
229            check_by_value(by_value_result);
230        }};
231    }
232
233    // TODO: Run this test over `validate()`.
234    #[test]
235    fn no_magic() {
236        let mut bytes = [SELINUX_MAGIC.to_le_bytes().as_slice()].concat();
237        // One byte short of magic.
238        bytes.pop();
239        let bytes = bytes;
240        assert_eq!(None, ByRef::parse::<Magic>(ByRef::new(bytes.as_slice())),);
241        assert_eq!(None, ByValue::parse::<Magic>(ByValue::new(bytes)),);
242    }
243
244    #[test]
245    fn invalid_magic() {
246        let mut bytes = [SELINUX_MAGIC.to_le_bytes().as_slice()].concat();
247        // Invalid first byte of magic.
248        bytes[0] = bytes[0] + 1;
249        let bytes = bytes;
250        let expected_invalid_magic =
251            u32::from_le_bytes(bytes.clone().as_slice().try_into().unwrap());
252
253        let (magic, tail) = ByRef::parse::<Magic>(ByRef::new(bytes.as_slice())).expect("magic");
254        assert_eq!(0, tail.len());
255        assert_eq!(
256            Err(ValidateError::InvalidMagic { found_magic: expected_invalid_magic }),
257            magic.validate()
258        );
259
260        let (magic, tail) = ByValue::parse::<Magic>(ByValue::new(bytes)).expect("magic");
261        assert_eq!(0, tail.len());
262        assert_eq!(
263            Err(ValidateError::InvalidMagic { found_magic: expected_invalid_magic }),
264            magic.validate()
265        );
266    }
267
268    #[test]
269    fn invalid_signature_length() {
270        const INVALID_SIGNATURE_LENGTH: u32 = POLICYDB_STRING_MAX_LENGTH + 1;
271        let bytes: Vec<u8> = [
272            INVALID_SIGNATURE_LENGTH.to_le_bytes().as_slice(),
273            [42u8; INVALID_SIGNATURE_LENGTH as usize].as_slice(),
274        ]
275        .concat();
276
277        validate_test!(Signature, bytes, result, {
278            assert_eq!(
279                Some(ValidateError::InvalidSignatureLength {
280                    found_length: INVALID_SIGNATURE_LENGTH
281                }),
282                result.err().map(as_validate_error),
283            );
284        });
285    }
286
287    #[test]
288    fn missing_signature() {
289        let bytes = [(1 as u32).to_le_bytes().as_slice()].concat();
290        match Signature::parse(ByRef::new(bytes.as_slice())).err().map(as_parse_error) {
291            Some(ParseError::MissingSliceData {
292                type_name: "u8",
293                type_size: 1,
294                num_items: 1,
295                num_bytes: 0,
296            }) => {}
297            parse_err => {
298                assert!(false, "Expected Some(MissingSliceData...), but got {:?}", parse_err);
299            }
300        }
301    }
302
303    #[test]
304    fn invalid_signature() {
305        // Invalid signature "TE Linux" is not "SE Linux".
306        const INVALID_SIGNATURE: &[u8] = b"TE Linux";
307
308        let bytes =
309            [(INVALID_SIGNATURE.len() as u32).to_le_bytes().as_slice(), INVALID_SIGNATURE].concat();
310
311        validate_test!(Signature, bytes, result, {
312            assert_eq!(
313                Some(ValidateError::InvalidSignature {
314                    found_signature: INVALID_SIGNATURE.to_owned()
315                }),
316                result.err().map(as_validate_error),
317            );
318        });
319    }
320
321    #[test]
322    fn invalid_policy_version() {
323        let bytes = [(POLICYDB_VERSION_MIN - 1).to_le_bytes().as_slice()].concat();
324        let (policy_version, tail) =
325            ByRef::parse::<PolicyVersion>(ByRef::new(bytes.as_slice())).expect("magic");
326        assert_eq!(0, tail.len());
327        assert_eq!(
328            Err(ValidateError::InvalidPolicyVersion {
329                found_policy_version: POLICYDB_VERSION_MIN - 1
330            }),
331            policy_version.validate()
332        );
333
334        let (policy_version, tail) =
335            ByValue::parse::<PolicyVersion>(ByValue::new(bytes)).expect("magic");
336        assert_eq!(0, tail.len());
337        assert_eq!(
338            Err(ValidateError::InvalidPolicyVersion {
339                found_policy_version: POLICYDB_VERSION_MIN - 1
340            }),
341            policy_version.validate()
342        );
343
344        let bytes = [(POLICYDB_VERSION_MAX + 1).to_le_bytes().as_slice()].concat();
345        let (policy_version, tail) =
346            ByRef::parse::<PolicyVersion>(ByRef::new(bytes.as_slice())).expect("magic");
347        assert_eq!(0, tail.len());
348        assert_eq!(
349            Err(ValidateError::InvalidPolicyVersion {
350                found_policy_version: POLICYDB_VERSION_MAX + 1
351            }),
352            policy_version.validate()
353        );
354
355        let (policy_version, tail) =
356            ByValue::parse::<PolicyVersion>(ByValue::new(bytes)).expect("magic");
357        assert_eq!(0, tail.len());
358        assert_eq!(
359            Err(ValidateError::InvalidPolicyVersion {
360                found_policy_version: POLICYDB_VERSION_MAX + 1
361            }),
362            policy_version.validate()
363        );
364    }
365
366    #[test]
367    fn config_missing_mls_flag() {
368        let bytes = [(!CONFIG_MLS_FLAG).to_le_bytes().as_slice()].concat();
369        match Config::parse(ByRef::new(bytes.as_slice())).err() {
370            Some(ParseError::ConfigMissingMlsFlag { .. }) => {}
371            parse_err => {
372                assert!(false, "Expected Some(ConfigMissingMlsFlag...), but got {:?}", parse_err);
373            }
374        }
375    }
376
377    #[test]
378    fn invalid_handle_unknown() {
379        let bytes = [(CONFIG_MLS_FLAG
380            | CONFIG_HANDLE_UNKNOWN_ALLOW_FLAG
381            | CONFIG_HANDLE_UNKNOWN_REJECT_FLAG)
382            .to_le_bytes()
383            .as_slice()]
384        .concat();
385        assert_eq!(
386            Some(ParseError::InvalidHandleUnknownConfigurationBits {
387                masked_bits: CONFIG_HANDLE_UNKNOWN_ALLOW_FLAG | CONFIG_HANDLE_UNKNOWN_REJECT_FLAG
388            }),
389            Config::parse(ByRef::new(bytes.as_slice())).err()
390        );
391    }
392}