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