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