Skip to main content

selinux/policy/
security_context.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::index::PolicyIndex;
6use super::new::{CategorySetBuilder, Context, IdSpan, MlsLevel, MlsRange};
7use super::parser::PolicyCursor;
8use super::{CategoryId, Parse, PolicyValidationContext, RoleId, TypeId, UserId, Validate};
9use crate::NullessByteStr;
10use crate::new_policy::NewPolicy;
11use crate::new_policy::traits::{HasName, HasPolicyId};
12
13use bstr::BString;
14
15use thiserror::Error;
16
17/// Security context, a variable-length string associated with each SELinux object in the
18/// system. Contains mandatory `user:role:type` components and an optional
19/// [:range] component.
20///
21/// Security contexts are configured by userspace atop Starnix, and mapped to
22/// [`SecurityId`]s for internal use in Starnix.
23#[derive(Clone, Debug, Eq, PartialEq)]
24pub struct SecurityContext {
25    inner: Context,
26}
27
28impl SecurityContext {
29    /// Returns a new instance with the specified field values.
30    /// Fields are not validated against the policy until explicitly via `validate()`,
31    /// or implicitly via insertion into a [`SidTable`].
32    pub(super) fn new(
33        user: UserId,
34        role: RoleId,
35        type_: TypeId,
36        low_level: MlsLevel,
37        high_level: Option<MlsLevel>,
38    ) -> Self {
39        let inner = Context::new(user, role, type_, MlsRange::new(low_level, high_level));
40        Self { inner }
41    }
42
43    pub(super) fn new_from_policy_context(context: &super::arrays::Context) -> SecurityContext {
44        let low = context.low_level().clone();
45        let high = context.high_level().clone();
46        let mls_range = MlsRange::new(low, high);
47        let inner =
48            Context::new(context.user_id(), context.role_id(), context.type_id(), mls_range);
49        SecurityContext { inner }
50    }
51
52    /// Returns the user component of the security context.
53    pub fn user(&self) -> UserId {
54        self.inner.user_id()
55    }
56
57    /// Returns the role component of the security context.
58    pub fn role(&self) -> RoleId {
59        self.inner.role_id()
60    }
61
62    /// Returns the type component of the security context.
63    pub fn type_(&self) -> TypeId {
64        self.inner.type_id()
65    }
66
67    /// Returns the [lowest] security level of the context.
68    pub fn low_level(&self) -> &MlsLevel {
69        self.inner.low_level()
70    }
71
72    /// Returns the highest security level, if it allows a range.
73    pub fn high_level(&self) -> Option<&MlsLevel> {
74        self.inner.high_level().as_ref()
75    }
76
77    /// Returns the high level if distinct from the low level, or
78    /// else returns the low level.
79    pub fn effective_high_level(&self) -> &MlsLevel {
80        self.high_level().unwrap_or_else(|| self.low_level())
81    }
82
83    /// Returns [`SecurityContext`] parsed from `security_context`, against the supplied
84    /// `policy`. The returned structure is guaranteed to be valid for this `policy`.
85    ///
86    /// Security Contexts in Multi-Level Security (MLS) and Multi-Category Security (MCS)
87    /// policies take the form:
88    ///   context := <user>:<role>:<type>:<levels>
89    /// such that they always include user, role, type, and a range of
90    /// security levels.
91    ///
92    /// The security levels part consists of a "low" value and optional "high"
93    /// value, defining the range.  In MCS policies each level may optionally be
94    /// associated with a set of categories:
95    /// categories:
96    ///   levels := <level>[-<level>]
97    ///   level := <sensitivity>[:<category_spec>[,<category_spec>]*]
98    ///
99    /// Entries in the optional list of categories may specify individual
100    /// categories, or ranges (from low to high):
101    ///   category_spec := <category>[.<category>]
102    ///
103    /// e.g. "u:r:t:s0" has a single (low) sensitivity.
104    /// e.g. "u:r:t:s0-s1" has a sensitivity range.
105    /// e.g. "u:r:t:s0:c1,c2,c3" has a single sensitivity, with three categories.
106    /// e.g. "u:r:t:s0:c1-s1:c1,c2,c3" has a sensitivity range, with categories
107    ///      associated with both low and high ends.
108    ///
109    /// Returns an error if the [`security_context`] is not a syntactically valid
110    /// Security Context string, or the fields are not valid under the current policy.
111    pub(super) fn from_string(
112        policy_index: &PolicyIndex,
113        security_context: NullessByteStr<'_>,
114    ) -> Result<Self, SecurityContextError> {
115        let as_str = std::str::from_utf8(security_context.as_bytes())
116            .map_err(|_| SecurityContextError::InvalidSyntax)?;
117
118        // Parse the user, role, type and security level parts, to validate syntax.
119        let mut items = as_str.splitn(4, ":");
120        let user = items.next().ok_or(SecurityContextError::InvalidSyntax)?;
121        let role = items.next().ok_or(SecurityContextError::InvalidSyntax)?;
122        let type_ = items.next().ok_or(SecurityContextError::InvalidSyntax)?;
123
124        // `next()` holds the remainder of the string, if any.
125        let mut levels = items.next().ok_or(SecurityContextError::InvalidSyntax)?.split("-");
126        let low_level = levels.next().ok_or(SecurityContextError::InvalidSyntax)?;
127        if low_level.is_empty() {
128            return Err(SecurityContextError::InvalidSyntax);
129        }
130        let high_level = levels.next();
131        if let Some(high_level) = high_level {
132            if high_level.is_empty() {
133                return Err(SecurityContextError::InvalidSyntax);
134            }
135        }
136        if levels.next() != None {
137            return Err(SecurityContextError::InvalidSyntax);
138        }
139
140        // Resolve the user, role, type and security levels to identifiers.
141        let user = policy_index
142            .users()
143            .get_by_name(user.as_bytes())
144            .ok_or_else(|| SecurityContextError::UnknownUser { name: user.into() })?
145            .id();
146        let role = policy_index
147            .roles()
148            .get_by_name(role.as_bytes())
149            .ok_or_else(|| SecurityContextError::UnknownRole { name: role.into() })?
150            .id();
151        let type_ = policy_index
152            .types()
153            .get_by_name(type_.as_bytes())
154            .ok_or_else(|| SecurityContextError::UnknownType { name: type_.into() })?
155            .id();
156
157        let low_level = MlsLevel::from_string(policy_index, low_level)?;
158        let high_level = high_level.map(|x| MlsLevel::from_string(policy_index, x)).transpose()?;
159
160        Ok(Self::new(user, role, type_, low_level, high_level))
161    }
162
163    /// Returns this [`SecurityContext`] serialized to a byte string.
164    pub(super) fn to_string(&self, policy_index: &PolicyIndex) -> Vec<u8> {
165        let mut levels = self.low_level().to_string(policy_index);
166        if let Some(high_level) = self.high_level() {
167            levels.push(b'-');
168            levels.extend(high_level.to_string(policy_index));
169        }
170        let type_ = policy_index.types().get_by_id(self.type_()).unwrap();
171        let parts: [&[u8]; 4] = [
172            policy_index.users().get_by_id(self.user()).unwrap().name(),
173            policy_index.roles().get_by_id(self.role()).unwrap().name(),
174            type_.name(),
175            levels.as_slice(),
176        ];
177        parts.join(b":".as_ref())
178    }
179
180    /// Validates that this [`SecurityContext`]'s fields are consistent with policy constraints
181    /// (e.g. that the role is valid for the user).
182    pub(super) fn validate(&self, policy_index: &PolicyIndex) -> Result<(), SecurityContextError> {
183        let user = policy_index.users().get_by_id(self.user()).unwrap();
184
185        // Validation of the user/role/type relationships is skipped for the special "object_r"
186        // role, which is applied by default to non-process/socket-like resources.
187        if self.role() != policy_index.object_role() {
188            // Validate that the selected role is valid for this user.
189            if !user.roles().contains(self.role()) {
190                return Err(SecurityContextError::InvalidRoleForUser {
191                    role: policy_index.roles().get_by_id(self.role()).unwrap().name().into(),
192                    user: user.name().into(),
193                });
194            }
195
196            // Validate that the selected type is valid for this role.
197            let role = policy_index.roles().get_by_id(self.role()).unwrap();
198            if !role.types().contains(self.type_()) {
199                return Err(SecurityContextError::InvalidTypeForRole {
200                    type_: policy_index.types().get_by_id(self.type_()).unwrap().name().into(),
201                    role: role.name().into(),
202                });
203            }
204        }
205
206        // Check that the security context's MLS range is valid for the user (steps 1, 2,
207        // and 3 below).
208        let valid_low = user.mls_range().low();
209        let valid_high = user.mls_range().high().as_ref().unwrap_or(valid_low);
210
211        // 1. Check that the security context's low level is in the valid range for the user.
212        if !(self.low_level().dominates(valid_low) && valid_high.dominates(self.low_level())) {
213            return Err(SecurityContextError::InvalidLevelForUser {
214                level: self.low_level().to_string(policy_index).into(),
215                user: user.name().into(),
216            });
217        }
218        if let Some(high_level) = self.high_level() {
219            // 2. Check that the security context's high level is in the valid range for the user.
220            if !(valid_high.dominates(high_level) && high_level.dominates(valid_low)) {
221                return Err(SecurityContextError::InvalidLevelForUser {
222                    level: high_level.to_string(policy_index).into(),
223                    user: user.name().into(),
224                });
225            }
226
227            // 3. Check that the security context's levels are internally consistent: i.e.,
228            //    that the high level dominates the low level.
229            if !high_level.dominates(self.low_level()) {
230                return Err(SecurityContextError::InvalidSecurityRange {
231                    low: self.low_level().to_string(policy_index).into(),
232                    high: high_level.to_string(policy_index).into(),
233                });
234            }
235        }
236        Ok(())
237    }
238}
239
240impl MlsLevel {
241    /// Parses [`MlsLevel`] from the supplied string slice.
242    pub(super) fn from_string(
243        policy_index: &PolicyIndex,
244        level: &str,
245    ) -> Result<Self, SecurityContextError> {
246        if level.is_empty() {
247            return Err(SecurityContextError::InvalidSyntax);
248        }
249
250        // Parse the parts before looking up values, to catch invalid syntax.
251        let mut items = level.split(":");
252        let sensitivity = items.next().ok_or(SecurityContextError::InvalidSyntax)?;
253        let categories_item = items.next();
254        if items.next() != None {
255            return Err(SecurityContextError::InvalidSyntax);
256        }
257
258        // Lookup the sensitivity, and associated categories/ranges, if any.
259        let sensitivity = policy_index
260            .sensitivities()
261            .get_by_name(sensitivity.as_bytes())
262            .ok_or_else(|| SecurityContextError::UnknownSensitivity { name: sensitivity.into() })?
263            .id();
264
265        let mut categories = CategorySetBuilder::new();
266        if let Some(categories_str) = categories_item {
267            for entry in categories_str.split(",") {
268                if let Some((low_str, high_str)) = entry.split_once(".") {
269                    let low = Self::category_id_by_name(policy_index, low_str)?;
270                    let high = Self::category_id_by_name(policy_index, high_str)?;
271                    if high <= low {
272                        return Err(SecurityContextError::InvalidSyntax);
273                    }
274                    categories.insert_range(low, high);
275                } else {
276                    let id = Self::category_id_by_name(policy_index, entry)?;
277                    categories.insert(id);
278                };
279            }
280        }
281
282        Ok(Self::new(sensitivity, categories.build()))
283    }
284
285    fn category_id_by_name(
286        policy_index: &PolicyIndex,
287        name: &str,
288    ) -> Result<CategoryId, SecurityContextError> {
289        Ok(policy_index
290            .categories()
291            .get_by_name(name.as_bytes())
292            .ok_or_else(|| SecurityContextError::UnknownCategory { name: name.into() })?
293            .id())
294    }
295
296    pub fn category_spans(&self) -> impl Iterator<Item = CategorySpan> + '_ {
297        self.categories().spans()
298    }
299
300    pub fn to_string(&self, policy: &NewPolicy) -> Vec<u8> {
301        let sensitivity = policy.sensitivities().get_by_id(self.sensitivity()).unwrap().name();
302        let categories = self
303            .category_spans()
304            .map(|x| x.to_string(policy))
305            .collect::<Vec<Vec<u8>>>()
306            .join(b",".as_ref());
307
308        if categories.is_empty() {
309            sensitivity.to_vec()
310        } else {
311            [sensitivity, categories.as_slice()].join(b":".as_ref())
312        }
313    }
314}
315
316/// Describes an entry in a category specification, which may be a single category
317/// (in which case `low` = `high`) or a span of consecutive categories. The bounds
318/// are included in the span.
319pub type CategorySpan = IdSpan<CategoryId>;
320
321impl IdSpan<CategoryId> {
322    /// Returns `Vec<u8>` describing the category, or category range.
323    fn to_string(&self, policy: &NewPolicy) -> Vec<u8> {
324        match self.low() == self.high() {
325            true => policy.categories().get_by_id(self.low()).unwrap().name().into(),
326            false => [
327                policy.categories().get_by_id(self.low()).unwrap().name(),
328                policy.categories().get_by_id(self.high()).unwrap().name(),
329            ]
330            .join(b".".as_ref()),
331        }
332    }
333}
334
335/// Temporary adapter implementing legacy policy Parse trait by delegating to new_policy trait during migration.
336impl Parse for MlsLevel {
337    type Error = anyhow::Error;
338
339    fn parse<'a>(cursor: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
340        let offset = cursor.offset() as usize;
341        let slice = &cursor.data().as_ref()[offset..];
342        let mut new_cursor = crate::new_policy::parser::PolicyCursor::new(slice);
343        let level = <Self as crate::new_policy::traits::Parse>::parse(&mut new_cursor)
344            .map_err(|e| anyhow::anyhow!("Parse error: {:?}", e))?;
345        let bytes_parsed = new_cursor.offset();
346        let new_offset = cursor.offset() + bytes_parsed as u32;
347        Ok((level, PolicyCursor::new_at(cursor.data(), new_offset)))
348    }
349}
350
351/// Temporary adapter implementing legacy policy Validate trait by delegating to new_policy trait during migration.
352impl Validate for MlsLevel {
353    type Error = anyhow::Error;
354
355    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
356        crate::new_policy::traits::Validate::validate(self, &context.new_policy).map_err(Into::into)
357    }
358}
359
360/// Temporary adapter implementing legacy policy Parse trait by delegating to new_policy trait during migration.
361impl Parse for MlsRange {
362    type Error = anyhow::Error;
363
364    fn parse<'a>(cursor: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
365        let offset = cursor.offset() as usize;
366        let slice = &cursor.data().as_ref()[offset..];
367        let mut new_cursor = crate::new_policy::parser::PolicyCursor::new(slice);
368        let range = <Self as crate::new_policy::traits::Parse>::parse(&mut new_cursor)
369            .map_err(|e| anyhow::anyhow!("Parse error: {:?}", e))?;
370        let bytes_parsed = new_cursor.offset();
371        let new_offset = cursor.offset() + bytes_parsed as u32;
372        Ok((range, PolicyCursor::new_at(cursor.data(), new_offset)))
373    }
374}
375
376/// Temporary adapter implementing legacy policy Validate trait by delegating to new_policy trait during migration.
377impl Validate for MlsRange {
378    type Error = anyhow::Error;
379
380    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
381        crate::new_policy::traits::Validate::validate(self, &context.new_policy).map_err(Into::into)
382    }
383}
384
385/// Errors that may be returned when attempting to parse or validate a security context.
386#[derive(Clone, Debug, Error, Eq, PartialEq)]
387pub enum SecurityContextError {
388    #[error("security context syntax is invalid")]
389    InvalidSyntax,
390    #[error("sensitivity {name:?} not defined by policy")]
391    UnknownSensitivity { name: BString },
392    #[error("category {name:?} not defined by policy")]
393    UnknownCategory { name: BString },
394    #[error("user {name:?} not defined by policy")]
395    UnknownUser { name: BString },
396    #[error("role {name:?} not defined by policy")]
397    UnknownRole { name: BString },
398    #[error("type {name:?} not defined by policy")]
399    UnknownType { name: BString },
400    #[error("role {role:?} not valid for {user:?}")]
401    InvalidRoleForUser { role: BString, user: BString },
402    #[error("type {type_:?} not valid for {role:?}")]
403    InvalidTypeForRole { role: BString, type_: BString },
404    #[error("security level {level:?} not valid for {user:?}")]
405    InvalidLevelForUser { level: BString, user: BString },
406    #[error("high security level {high:?} lower than low level {low:?}")]
407    InvalidSecurityRange { low: BString, high: BString },
408}
409
410#[cfg(test)]
411mod tests {
412    use super::super::new::CategorySet;
413    use super::super::{Policy, PolicyId, SensitivityId, parse_policy_by_value};
414    use super::*;
415    use std::cmp::Ordering;
416
417    fn test_policy() -> Policy {
418        const TEST_POLICY: &[u8] =
419            include_bytes!("../../testdata/micro_policies/security_context_tests_policy");
420        parse_policy_by_value(TEST_POLICY.to_vec()).unwrap().validate().unwrap()
421    }
422
423    // CategoryItem helper for tests.
424    #[derive(Debug, Eq, PartialEq)]
425    struct CategoryItem {
426        low: String,
427        high: String,
428    }
429
430    fn user_name(policy: &Policy, id: UserId) -> &str {
431        std::str::from_utf8(policy.users().get_by_id(id).unwrap().name()).unwrap()
432    }
433
434    fn role_name(policy: &Policy, id: RoleId) -> &str {
435        std::str::from_utf8(policy.roles().get_by_id(id).unwrap().name()).unwrap()
436    }
437
438    fn type_name(policy: &Policy, id: TypeId) -> &str {
439        std::str::from_utf8(policy.types().get_by_id(id).unwrap().name()).unwrap()
440    }
441
442    fn sensitivity_name(policy: &Policy, id: SensitivityId) -> &str {
443        std::str::from_utf8(policy.sensitivities().get_by_id(id).unwrap().name()).unwrap()
444    }
445
446    fn category_name(policy: &Policy, id: CategoryId) -> &str {
447        std::str::from_utf8(policy.categories().get_by_id(id).unwrap().name()).unwrap()
448    }
449
450    fn category_span(policy: &Policy, category: &CategorySpan) -> CategoryItem {
451        CategoryItem {
452            low: category_name(policy, category.low()).into(),
453            high: category_name(policy, category.high()).into(),
454        }
455    }
456
457    fn category_spans(
458        policy: &Policy,
459        iter: impl Iterator<Item = CategorySpan>,
460    ) -> Vec<CategoryItem> {
461        iter.map(|x| category_span(policy, &x)).collect()
462    }
463
464    // Creates a category range for testing.
465    fn cat(low: u32, high: u32) -> CategorySpan {
466        CategorySpan::new(
467            CategoryId::from_u32(low).expect("category ids are nonzero"),
468            CategoryId::from_u32(high).expect("category ids are nonzero"),
469        )
470    }
471
472    // Compares two sets of categories for testing.
473    fn compare(lhs: &[CategorySpan], rhs: &[CategorySpan]) -> Option<Ordering> {
474        let lhs_set = CategorySet::from_ids(lhs.iter().flat_map(|span| {
475            (span.low().as_u32()..=span.high().as_u32()).map(|i| CategoryId::from_u32(i).unwrap())
476        }));
477        let rhs_set = CategorySet::from_ids(rhs.iter().flat_map(|span| {
478            (span.low().as_u32()..=span.high().as_u32()).map(|i| CategoryId::from_u32(i).unwrap())
479        }));
480        lhs_set.compare(&rhs_set)
481    }
482
483    #[test]
484    fn category_compare() {
485        let cat_1 = cat(1, 1);
486        let cat_2 = cat(1, 3);
487        let cat_3 = cat(2, 3);
488        assert_eq!(compare(&[cat_1.clone()], &[cat_1.clone()]), Some(Ordering::Equal));
489        assert_eq!(compare(&[cat_1.clone()], &[cat_2.clone()]), Some(Ordering::Less));
490        assert_eq!(compare(&[cat_1.clone()], &[cat_3.clone()]), None);
491        assert_eq!(compare(&[cat_2.clone()], &[cat_1.clone()]), Some(Ordering::Greater));
492        assert_eq!(compare(&[cat_2.clone()], &[cat_3.clone()]), Some(Ordering::Greater));
493    }
494
495    #[test]
496    fn categories_compare_empty_iter() {
497        let cats_0 = &[];
498        let cats_1 = &[cat(1, 1)];
499        assert_eq!(compare(cats_0, cats_0), Some(Ordering::Equal));
500        assert_eq!(compare(cats_0, cats_1), Some(Ordering::Less));
501        assert_eq!(compare(cats_1, cats_0), Some(Ordering::Greater));
502    }
503
504    #[test]
505    fn categories_compare_same_length() {
506        let cats_1 = &[cat(1, 1), cat(3, 3)];
507        let cats_2 = &[cat(1, 1), cat(4, 4)];
508        let cats_3 = &[cat(1, 2), cat(4, 4)];
509        let cats_4 = &[cat(1, 2), cat(4, 5)];
510
511        assert_eq!(compare(cats_1, cats_1), Some(Ordering::Equal));
512        assert_eq!(compare(cats_1, cats_2), None);
513        assert_eq!(compare(cats_1, cats_3), None);
514        assert_eq!(compare(cats_1, cats_4), None);
515
516        assert_eq!(compare(cats_2, cats_1), None);
517        assert_eq!(compare(cats_2, cats_2), Some(Ordering::Equal));
518        assert_eq!(compare(cats_2, cats_3), Some(Ordering::Less));
519        assert_eq!(compare(cats_2, cats_4), Some(Ordering::Less));
520
521        assert_eq!(compare(cats_3, cats_1), None);
522        assert_eq!(compare(cats_3, cats_2), Some(Ordering::Greater));
523        assert_eq!(compare(cats_3, cats_3), Some(Ordering::Equal));
524        assert_eq!(compare(cats_3, cats_4), Some(Ordering::Less));
525
526        assert_eq!(compare(cats_4, cats_1), None);
527        assert_eq!(compare(cats_4, cats_2), Some(Ordering::Greater));
528        assert_eq!(compare(cats_4, cats_3), Some(Ordering::Greater));
529        assert_eq!(compare(cats_4, cats_4), Some(Ordering::Equal));
530    }
531
532    #[test]
533    fn categories_compare_different_lengths() {
534        let cats_1 = &[cat(1, 1)];
535        let cats_2 = &[cat(1, 4)];
536        let cats_3 = &[cat(1, 1), cat(4, 4)];
537        let cats_4 = &[cat(1, 2), cat(4, 5), cat(7, 7)];
538
539        assert_eq!(compare(cats_1, cats_3), Some(Ordering::Less));
540        assert_eq!(compare(cats_1, cats_4), Some(Ordering::Less));
541
542        assert_eq!(compare(cats_2, cats_3), Some(Ordering::Greater));
543        assert_eq!(compare(cats_2, cats_4), None);
544
545        assert_eq!(compare(cats_3, cats_1), Some(Ordering::Greater));
546        assert_eq!(compare(cats_3, cats_2), Some(Ordering::Less));
547        assert_eq!(compare(cats_3, cats_4), Some(Ordering::Less));
548
549        assert_eq!(compare(cats_4, cats_1), Some(Ordering::Greater));
550        assert_eq!(compare(cats_4, cats_2), None);
551        assert_eq!(compare(cats_4, cats_3), Some(Ordering::Greater));
552    }
553
554    #[test]
555    // Test cases where one interval appears before or after all intervals of the
556    // other set, or in a gap between intervals of the other set.
557    fn categories_compare_with_gaps() {
558        let cats_1 = &[cat(1, 2), cat(4, 5)];
559        let cats_2 = &[cat(4, 5)];
560        let cats_3 = &[cat(2, 5), cat(10, 11)];
561        let cats_4 = &[cat(2, 5), cat(7, 8), cat(10, 11)];
562
563        assert_eq!(compare(cats_1, cats_2), Some(Ordering::Greater));
564        assert_eq!(compare(cats_1, cats_3), None);
565        assert_eq!(compare(cats_1, cats_4), None);
566
567        assert_eq!(compare(cats_2, cats_1), Some(Ordering::Less));
568        assert_eq!(compare(cats_2, cats_3), Some(Ordering::Less));
569        assert_eq!(compare(cats_2, cats_4), Some(Ordering::Less));
570
571        assert_eq!(compare(cats_3, cats_1), None);
572        assert_eq!(compare(cats_3, cats_2), Some(Ordering::Greater));
573        assert_eq!(compare(cats_3, cats_4), Some(Ordering::Less));
574
575        assert_eq!(compare(cats_4, cats_1), None);
576        assert_eq!(compare(cats_4, cats_2), Some(Ordering::Greater));
577        assert_eq!(compare(cats_4, cats_3), Some(Ordering::Greater));
578    }
579
580    #[test]
581    fn parse_security_context_single_sensitivity() {
582        let policy = test_policy();
583        let security_context = policy
584            .parse_security_context(b"user0:object_r:type0:s0".into())
585            .expect("creating security context should succeed");
586        assert_eq!(user_name(&policy, security_context.user()), "user0");
587        assert_eq!(role_name(&policy, security_context.role()), "object_r");
588        assert_eq!(type_name(&policy, security_context.type_()), "type0");
589        assert_eq!(sensitivity_name(&policy, security_context.low_level().sensitivity()), "s0");
590        assert!(category_spans(&policy, security_context.low_level().category_spans()).is_empty());
591        assert_eq!(security_context.high_level(), None);
592    }
593
594    #[test]
595    fn parse_security_context_with_sensitivity_range() {
596        let policy = test_policy();
597        let security_context = policy
598            .parse_security_context(b"user0:object_r:type0:s0-s1".into())
599            .expect("creating security context should succeed");
600        assert_eq!(user_name(&policy, security_context.user()), "user0");
601        assert_eq!(role_name(&policy, security_context.role()), "object_r");
602        assert_eq!(type_name(&policy, security_context.type_()), "type0");
603        assert_eq!(sensitivity_name(&policy, security_context.low_level().sensitivity()), "s0");
604        assert!(category_spans(&policy, security_context.low_level().category_spans()).is_empty());
605        let high_level = security_context.high_level().unwrap();
606        assert_eq!(sensitivity_name(&policy, high_level.sensitivity()), "s1");
607        assert!(category_spans(&policy, high_level.category_spans()).is_empty());
608    }
609
610    #[test]
611    fn parse_security_context_with_single_sensitivity_and_categories_interval() {
612        let policy = test_policy();
613        let security_context = policy
614            .parse_security_context(b"user0:object_r:type0:s1:c0.c4".into())
615            .expect("creating security context should succeed");
616        assert_eq!(user_name(&policy, security_context.user()), "user0");
617        assert_eq!(role_name(&policy, security_context.role()), "object_r");
618        assert_eq!(type_name(&policy, security_context.type_()), "type0");
619        assert_eq!(sensitivity_name(&policy, security_context.low_level().sensitivity()), "s1");
620        assert_eq!(
621            category_spans(&policy, security_context.low_level().category_spans()),
622            [CategoryItem { low: "c0".to_string(), high: "c4".to_string() }]
623        );
624        assert_eq!(security_context.high_level(), None);
625    }
626
627    #[test]
628    fn parse_security_context_and_normalize_categories() {
629        let policy = &test_policy();
630        let normalize = {
631            |security_context: &str| -> String {
632                String::from_utf8(
633                    policy.serialize_security_context(
634                        &policy
635                            .parse_security_context(security_context.into())
636                            .expect("creating security context should succeed"),
637                    ),
638                )
639                .unwrap()
640            }
641        };
642        // Overlapping category ranges are merged.
643        assert_eq!(normalize("user0:object_r:type0:s1:c0.c1,c1"), "user0:object_r:type0:s1:c0.c1");
644        assert_eq!(
645            normalize("user0:object_r:type0:s1:c0.c2,c1.c2"),
646            "user0:object_r:type0:s1:c0.c2"
647        );
648        assert_eq!(
649            normalize("user0:object_r:type0:s1:c0.c2,c1.c3"),
650            "user0:object_r:type0:s1:c0.c3"
651        );
652        // Adjacent category ranges are merged.
653        assert_eq!(normalize("user0:object_r:type0:s1:c0.c1,c2"), "user0:object_r:type0:s1:c0.c2");
654        // Category ranges are ordered by first element.
655        assert_eq!(
656            normalize("user0:object_r:type0:s1:c2.c3,c0"),
657            "user0:object_r:type0:s1:c0,c2.c3"
658        );
659    }
660
661    #[test]
662    fn parse_security_context_with_sensitivity_range_and_category_interval() {
663        let policy = test_policy();
664        let security_context = policy
665            .parse_security_context(b"user0:object_r:type0:s0-s1:c0.c4".into())
666            .expect("creating security context should succeed");
667        assert_eq!(user_name(&policy, security_context.user()), "user0");
668        assert_eq!(role_name(&policy, security_context.role()), "object_r");
669        assert_eq!(type_name(&policy, security_context.type_()), "type0");
670        assert_eq!(sensitivity_name(&policy, security_context.low_level().sensitivity()), "s0");
671        assert!(category_spans(&policy, security_context.low_level().category_spans()).is_empty());
672        let high_level = security_context.high_level().unwrap();
673        assert_eq!(sensitivity_name(&policy, high_level.sensitivity()), "s1");
674        assert_eq!(
675            category_spans(&policy, high_level.category_spans()),
676            [CategoryItem { low: "c0".to_string(), high: "c4".to_string() }]
677        );
678    }
679
680    #[test]
681    fn parse_security_context_with_sensitivity_range_with_categories() {
682        let policy = test_policy();
683        let security_context = policy
684            .parse_security_context(b"user0:object_r:type0:s0:c0-s1:c0.c4".into())
685            .expect("creating security context should succeed");
686        assert_eq!(user_name(&policy, security_context.user()), "user0");
687        assert_eq!(role_name(&policy, security_context.role()), "object_r");
688        assert_eq!(type_name(&policy, security_context.type_()), "type0");
689        assert_eq!(sensitivity_name(&policy, security_context.low_level().sensitivity()), "s0");
690        assert_eq!(
691            category_spans(&policy, security_context.low_level().category_spans()),
692            [CategoryItem { low: "c0".to_string(), high: "c0".to_string() }]
693        );
694
695        let high_level = security_context.high_level().unwrap();
696        assert_eq!(sensitivity_name(&policy, high_level.sensitivity()), "s1");
697        assert_eq!(
698            category_spans(&policy, high_level.category_spans()),
699            [CategoryItem { low: "c0".to_string(), high: "c4".to_string() }]
700        );
701    }
702
703    #[test]
704    fn parse_security_context_with_single_sensitivity_and_category_list() {
705        let policy = test_policy();
706        let security_context = policy
707            .parse_security_context(b"user0:object_r:type0:s1:c0,c4".into())
708            .expect("creating security context should succeed");
709        assert_eq!(user_name(&policy, security_context.user()), "user0");
710        assert_eq!(role_name(&policy, security_context.role()), "object_r");
711        assert_eq!(type_name(&policy, security_context.type_()), "type0");
712        assert_eq!(sensitivity_name(&policy, security_context.low_level().sensitivity()), "s1");
713        assert_eq!(
714            category_spans(&policy, security_context.low_level().category_spans()),
715            [
716                CategoryItem { low: "c0".to_string(), high: "c0".to_string() },
717                CategoryItem { low: "c4".to_string(), high: "c4".to_string() }
718            ]
719        );
720        assert_eq!(security_context.high_level(), None);
721    }
722
723    #[test]
724    fn parse_security_context_with_single_sensitivity_and_category_list_and_range() {
725        let policy = test_policy();
726        let security_context = policy
727            .parse_security_context(b"user0:object_r:type0:s1:c0,c3.c4".into())
728            .expect("creating security context should succeed");
729        assert_eq!(user_name(&policy, security_context.user()), "user0");
730        assert_eq!(role_name(&policy, security_context.role()), "object_r");
731        assert_eq!(type_name(&policy, security_context.type_()), "type0");
732        assert_eq!(sensitivity_name(&policy, security_context.low_level().sensitivity()), "s1");
733        assert_eq!(
734            category_spans(&policy, security_context.low_level().category_spans()),
735            [
736                CategoryItem { low: "c0".to_string(), high: "c0".to_string() },
737                CategoryItem { low: "c3".to_string(), high: "c4".to_string() }
738            ]
739        );
740        assert_eq!(security_context.high_level(), None);
741    }
742
743    #[test]
744    fn parse_invalid_syntax() {
745        let policy = test_policy();
746        for invalid_label in [
747            "user0",
748            "user0:object_r",
749            "user0:object_r:type0",
750            "user0:object_r:type0:s0-",
751            "user0:object_r:type0:s0:s0:s0",
752            "user0:object_r:type0:s0:c0.c0", // Category upper bound is equal to lower bound.
753            "user0:object_r:type0:s0:c1.c0", // Category upper bound is less than lower bound.
754        ] {
755            assert_eq!(
756                policy.parse_security_context(invalid_label.as_bytes().into()),
757                Err(SecurityContextError::InvalidSyntax),
758                "validating {:?}",
759                invalid_label
760            );
761        }
762    }
763
764    #[test]
765    fn parse_invalid_sensitivity() {
766        let policy = test_policy();
767        for invalid_label in ["user0:object_r:type0:s_invalid", "user0:object_r:type0:s0-s_invalid"]
768        {
769            assert_eq!(
770                policy.parse_security_context(invalid_label.as_bytes().into()),
771                Err(SecurityContextError::UnknownSensitivity { name: "s_invalid".into() }),
772                "validating {:?}",
773                invalid_label
774            );
775        }
776    }
777
778    #[test]
779    fn parse_invalid_category() {
780        let policy = test_policy();
781        for invalid_label in
782            ["user0:object_r:type0:s1:c_invalid", "user0:object_r:type0:s1:c0.c_invalid"]
783        {
784            assert_eq!(
785                policy.parse_security_context(invalid_label.as_bytes().into()),
786                Err(SecurityContextError::UnknownCategory { name: "c_invalid".into() }),
787                "validating {:?}",
788                invalid_label
789            );
790        }
791    }
792
793    #[test]
794    fn invalid_security_context_fields() {
795        let policy = test_policy();
796
797        // Fails validation because the security context's high level does not dominate its
798        // low level: the low level has categories that the high level does not.
799        let context = policy
800            .parse_security_context(b"user0:object_r:type0:s1:c0,c3.c4-s1".into())
801            .expect("successfully parsed");
802        assert_eq!(
803            policy.validate_security_context(&context),
804            Err(SecurityContextError::InvalidSecurityRange {
805                low: "s1:c0,c3.c4".into(),
806                high: "s1".into()
807            })
808        );
809
810        // Fails validation because the security context's high level does not dominate its
811        // low level: the category sets of the high level and low level are not comparable.
812        let context = policy
813            .parse_security_context(b"user0:object_r:type0:s1:c0-s1:c1".into())
814            .expect("successfully parsed");
815        assert_eq!(
816            policy.validate_security_context(&context),
817            Err(SecurityContextError::InvalidSecurityRange {
818                low: "s1:c0".into(),
819                high: "s1:c1".into()
820            })
821        );
822
823        // Fails validation because the security context's high level does not dominate its
824        // low level: the sensitivity of the high level is lower than that of the low level.
825        let context = policy
826            .parse_security_context(b"user0:object_r:type0:s1:c0-s0:c0.c1".into())
827            .expect("successfully parsed");
828        assert_eq!(
829            policy.validate_security_context(&context),
830            Err(SecurityContextError::InvalidSecurityRange {
831                low: "s1:c0".into(),
832                high: "s0:c0.c1".into()
833            })
834        );
835
836        // Fails validation because the policy's high level does not dominate the
837        // security context's high level: the security context's high level has categories
838        // that the policy's high level does not.
839        let context = policy
840            .parse_security_context(b"user1:subject_r:type0:s1-s1:c3".into())
841            .expect("successfully parsed");
842        assert_eq!(
843            policy.validate_security_context(&context),
844            Err(SecurityContextError::InvalidLevelForUser {
845                level: "s1:c3".into(),
846                user: "user1".into(),
847            })
848        );
849
850        // Fails validation because the security context's low level does not dominate
851        // the policy's low level: the security context's low level has a lower sensitivity
852        // than the policy's low level.
853        let context = policy
854            .parse_security_context(b"user1:object_r:type0:s0".into())
855            .expect("successfully parsed");
856        assert_eq!(
857            policy.validate_security_context(&context),
858            Err(SecurityContextError::InvalidLevelForUser {
859                level: "s0".into(),
860                user: "user1".into(),
861            })
862        );
863
864        // Fails validation because the sensitivity is not valid for the user.
865        let context = policy
866            .parse_security_context(b"user1:object_r:type0:s0".into())
867            .expect("successfully parsed");
868        assert!(policy.validate_security_context(&context).is_err());
869
870        // Fails validation because the role is not valid for the user.
871        let context = policy
872            .parse_security_context(b"user0:subject_r:type0:s0".into())
873            .expect("successfully parsed");
874        assert!(policy.validate_security_context(&context).is_err());
875
876        // Fails validation because the type is not valid for the role.
877        let context = policy
878            .parse_security_context(b"user1:subject_r:non_subject_t:s1".into())
879            .expect("successfully parsed");
880        assert!(policy.validate_security_context(&context).is_err());
881
882        // Passes validation even though the role is not explicitly allowed for the user,
883        // because it is the special "object_r" role, used when labelling resources.
884        let context = policy
885            .parse_security_context(b"user1:object_r:type0:s1".into())
886            .expect("successfully parsed");
887        assert!(policy.validate_security_context(&context).is_ok());
888    }
889
890    #[test]
891    fn format_security_contexts() {
892        let policy = test_policy();
893        for label in [
894            "user0:object_r:type0:s0",
895            "user0:object_r:type0:s0-s1",
896            "user0:object_r:type0:s1:c0.c4",
897            "user0:object_r:type0:s0-s1:c0.c4",
898            "user0:object_r:type0:s1:c0,c3",
899            "user0:object_r:type0:s0-s1:c0,c2,c4",
900            "user0:object_r:type0:s1:c0,c3.c4-s1:c0,c2.c4",
901        ] {
902            let security_context =
903                policy.parse_security_context(label.as_bytes().into()).expect("should succeed");
904            assert_eq!(policy.serialize_security_context(&security_context), label.as_bytes());
905        }
906    }
907}