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::{CategoryId, ParsedPolicy, RoleId, SensitivityId, TypeId, UserId};
10
11use crate::NullessByteStr;
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(crate) 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_by_name(type_)
170            .ok_or_else(|| SecurityContextError::UnknownType { name: type_.into() })?
171            .id();
172
173        let low_level = SecurityLevel::parse(policy_index, low_level)?;
174        let high_level = high_level.map(|x| SecurityLevel::parse(policy_index, x)).transpose()?;
175
176        Ok(Self::new(user, role, type_, low_level, high_level))
177    }
178
179    /// Returns this Security Context serialized to a byte string.
180    pub(super) fn serialize(&self, policy_index: &PolicyIndex) -> Vec<u8> {
181        let mut levels = self.low_level.serialize(policy_index.parsed_policy());
182        if let Some(high_level) = &self.high_level {
183            levels.push(b'-');
184            levels.extend(high_level.serialize(policy_index.parsed_policy()));
185        }
186        let parts: [&[u8]; 4] = [
187            policy_index.parsed_policy().user(self.user).name_bytes(),
188            policy_index.parsed_policy().role(self.role).name_bytes(),
189            policy_index.parsed_policy().type_(self.type_).name_bytes(),
190            levels.as_slice(),
191        ];
192        parts.join(b":".as_ref())
193    }
194
195    /// Validates that this `SecurityContext`'s fields are consistent with policy constraints
196    /// (e.g. that the role is valid for the user).
197    pub(super) fn validate(&self, policy_index: &PolicyIndex) -> Result<(), SecurityContextError> {
198        let user = policy_index.parsed_policy().user(self.user);
199
200        // Validation of the user/role/type relationships is skipped for the special "object_r"
201        // role, which is applied by default to non-process/socket-like resources.
202        if self.role != policy_index.object_role() {
203            // Validate that the selected role is valid for this user.
204            //
205            // TODO(b/335399404): Identifiers are 1-based, while the roles bitmap is 0-based.
206            if !user.roles().is_set(self.role.0.get() - 1) {
207                return Err(SecurityContextError::InvalidRoleForUser {
208                    role: policy_index.parsed_policy().role(self.role).name_bytes().into(),
209                    user: user.name_bytes().into(),
210                });
211            }
212
213            // Validate that the selected type is valid for this role.
214            let role = policy_index.parsed_policy().role(self.role);
215            // TODO(b/335399404): Identifiers are 1-based, while the roles bitmap is 0-based.
216            if !role.types().is_set(self.type_.0.get() - 1) {
217                return Err(SecurityContextError::InvalidTypeForRole {
218                    type_: policy_index.parsed_policy().type_(self.type_).name_bytes().into(),
219                    role: role.name_bytes().into(),
220                });
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(level: &MlsLevel) -> 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(policy_index: &PolicyIndex, level: &str) -> Result<Self, SecurityContextError> {
282        if level.is_empty() {
283            return Err(SecurityContextError::InvalidSyntax);
284        }
285
286        // Parse the parts before looking up values, to catch invalid syntax.
287        let mut items = level.split(":");
288        let sensitivity = items.next().ok_or(SecurityContextError::InvalidSyntax)?;
289        let categories_item = items.next();
290        if items.next() != None {
291            return Err(SecurityContextError::InvalidSyntax);
292        }
293
294        // Lookup the sensitivity, and associated categories/ranges, if any.
295        let sensitivity = policy_index
296            .parsed_policy()
297            .sensitivity_by_name(sensitivity)
298            .ok_or_else(|| SecurityContextError::UnknownSensitivity { name: sensitivity.into() })?
299            .id();
300        let mut categories = Vec::new();
301        if let Some(categories_str) = categories_item {
302            for entry in categories_str.split(",") {
303                let category = if let Some((low, high)) = entry.split_once(".") {
304                    let low = Self::category_id_by_name(policy_index, low)?;
305                    let high = Self::category_id_by_name(policy_index, high)?;
306                    if high <= low {
307                        return Err(SecurityContextError::InvalidSyntax);
308                    }
309                    CategorySpan::new(low, high)
310                } else {
311                    let id = Self::category_id_by_name(policy_index, entry)?;
312                    CategorySpan::new(id, id)
313                };
314                categories.push(category);
315            }
316        }
317        if categories.is_empty() {
318            return Ok(Self { sensitivity, categories });
319        }
320        // Represent the set of category IDs in the following normalized form:
321        // - Consecutive IDs are coalesced into spans.
322        // - The list of spans is sorted by ID.
323        //
324        // 1. Sort by lower bound, then upper bound.
325        categories.sort_by(|x, y| (x.low, x.high).cmp(&(y.low, y.high)));
326        // 2. Merge overlapping and adjacent ranges.
327        let categories = categories.into_iter();
328        let normalized =
329            categories.fold(vec![], |mut normalized: Vec<CategorySpan>, current: CategorySpan| {
330                if let Some(last) = normalized.last_mut() {
331                    if current.low <= last.high
332                        || (u32::from(current.low.0) - u32::from(last.high.0) == 1)
333                    {
334                        *last = CategorySpan::new(last.low, current.high)
335                    } else {
336                        normalized.push(current);
337                    }
338                    return normalized;
339                }
340                normalized.push(current);
341                normalized
342            });
343
344        Ok(Self { sensitivity, categories: normalized })
345    }
346
347    fn category_id_by_name(
348        policy_index: &PolicyIndex,
349        name: &str,
350    ) -> Result<CategoryId, SecurityContextError> {
351        Ok(policy_index
352            .parsed_policy()
353            .category_by_name(name)
354            .ok_or_else(|| SecurityContextError::UnknownCategory { name: name.into() })?
355            .id())
356    }
357}
358
359/// Models a security level consisting of a single sensitivity ID and some number of
360/// category IDs.
361pub trait Level<'a, T: Into<CategorySpan> + Clone, IterT: 'a + Iterator<Item = T>> {
362    /// Returns the sensitivity of this security level.
363    fn sensitivity(&self) -> SensitivityId;
364
365    /// Returns an iterator over categories of this security level.
366    fn category_spans(&'a self) -> CategoryIterator<T, IterT>;
367
368    /// Returns a byte string describing the security level sensitivity and
369    /// categories.
370    fn serialize(&'a self, parsed_policy: &ParsedPolicy) -> Vec<u8> {
371        let sensitivity = parsed_policy.sensitivity(self.sensitivity()).name_bytes();
372        let categories = self
373            .category_spans()
374            .map(|x| x.serialize(parsed_policy))
375            .collect::<Vec<Vec<u8>>>()
376            .join(b",".as_ref());
377
378        if categories.is_empty() {
379            sensitivity.to_vec()
380        } else {
381            [sensitivity, categories.as_slice()].join(b":".as_ref())
382        }
383    }
384
385    /// Implements the "dominance" partial ordering of security levels.
386    fn compare<U: Into<CategorySpan> + Clone, IterU: 'a + Iterator<Item = U>>(
387        &'a self,
388        other: &'a (impl Level<'a, U, IterU> + 'a),
389    ) -> Option<Ordering> {
390        let s_order = self.sensitivity().cmp(&other.sensitivity());
391        let c_order = self.category_spans().compare(&other.category_spans())?;
392        if s_order == c_order {
393            return Some(s_order);
394        } else if c_order == Ordering::Equal {
395            return Some(s_order);
396        } else if s_order == Ordering::Equal {
397            return Some(c_order);
398        }
399        // In the remaining cases `s_order` and `c_order` are strictly opposed,
400        // so the security levels are not comparable.
401        None
402    }
403
404    /// Returns `true` if `self` dominates `other`.
405    fn dominates<U: Into<CategorySpan> + Clone, IterU: 'a + Iterator<Item = U>>(
406        &'a self,
407        other: &'a (impl Level<'a, U, IterU> + 'a),
408    ) -> bool {
409        match self.compare(other) {
410            Some(Ordering::Equal) | Some(Ordering::Greater) => true,
411            _ => false,
412        }
413    }
414}
415
416impl<'a> Level<'a, &'a CategorySpan, Iter<'a, CategorySpan>> for SecurityLevel {
417    fn sensitivity(&self) -> SensitivityId {
418        self.sensitivity
419    }
420    fn category_spans(&'a self) -> CategoryIterator<&'a CategorySpan, Iter<'a, CategorySpan>> {
421        CategoryIterator::<&'a CategorySpan, Iter<'a, CategorySpan>>::new(self.categories.iter())
422    }
423}
424
425/// An iterator over a list of spans of category IDs.
426pub struct CategoryIterator<T: Into<CategorySpan>, IterT: Iterator<Item = T>>(RefCell<IterT>);
427
428impl<T: Into<CategorySpan> + Clone, IterT: Iterator<Item = T>> CategoryIterator<T, IterT> {
429    pub fn new(iter: IterT) -> Self {
430        Self(RefCell::new(iter))
431    }
432
433    fn next(&self) -> Option<CategorySpan> {
434        self.0.borrow_mut().next().map(|x| x.into())
435    }
436
437    fn compare<'a, U: Into<CategorySpan> + Clone, IterU: 'a + Iterator<Item = U>>(
438        &'a self,
439        other: &'a CategoryIterator<U, IterU>,
440    ) -> Option<Ordering> {
441        let mut self_contains_other = true;
442        let mut other_contains_self = true;
443
444        let mut self_now = self.next();
445        let mut other_now = other.next();
446
447        while let (Some(self_span), Some(other_span)) = (self_now.clone(), other_now.clone()) {
448            if self_span.high < other_span.low {
449                other_contains_self = false;
450            } else if other_span.high < self_span.low {
451                self_contains_other = false;
452            } else {
453                match self_span.compare(&other_span) {
454                    None => {
455                        return None;
456                    }
457                    Some(Ordering::Less) => {
458                        self_contains_other = false;
459                    }
460                    Some(Ordering::Greater) => {
461                        other_contains_self = false;
462                    }
463                    Some(Ordering::Equal) => {}
464                }
465                if !self_contains_other && !other_contains_self {
466                    return None;
467                }
468            }
469            if self_span.high <= other_span.high {
470                self_now = self.next();
471            }
472            if other_span.high <= self_span.high {
473                other_now = other.next();
474            }
475        }
476        if self_now.is_some() {
477            other_contains_self = false;
478        } else if other_now.is_some() {
479            self_contains_other = false;
480        }
481        match (self_contains_other, other_contains_self) {
482            (true, true) => Some(Ordering::Equal),
483            (true, false) => Some(Ordering::Greater),
484            (false, true) => Some(Ordering::Less),
485            (false, false) => None,
486        }
487    }
488}
489
490impl<T: Into<CategorySpan>, IterT: Iterator<Item = T>> Iterator for CategoryIterator<T, IterT> {
491    type Item = CategorySpan;
492
493    fn next(&mut self) -> Option<Self::Item> {
494        self.0.borrow_mut().next().map(|x| x.into())
495    }
496}
497
498/// Describes an entry in a category specification, which may be a single category
499/// (in which case `low` = `high`) or a span of consecutive categories. The bounds
500/// are included in the span.
501#[derive(Clone, Debug, Eq, PartialEq)]
502pub struct CategorySpan {
503    low: CategoryId,
504    high: CategoryId,
505}
506
507impl CategorySpan {
508    pub(super) fn new(low: CategoryId, high: CategoryId) -> Self {
509        Self { low, high }
510    }
511
512    /// Returns a byte string describing the category, or category range.
513    fn serialize(&self, parsed_policy: &ParsedPolicy) -> Vec<u8> {
514        match self.low == self.high {
515            true => parsed_policy.category(self.low).name_bytes().into(),
516            false => [
517                parsed_policy.category(self.low).name_bytes(),
518                parsed_policy.category(self.high).name_bytes(),
519            ]
520            .join(b".".as_ref()),
521        }
522    }
523
524    // Implements the set-containment partial ordering.
525    fn compare(&self, other: &Self) -> Option<Ordering> {
526        match (self.low.cmp(&other.low), self.high.cmp(&other.high)) {
527            (Ordering::Equal, Ordering::Equal) => Some(Ordering::Equal),
528            (Ordering::Equal, Ordering::Greater)
529            | (Ordering::Less, Ordering::Equal)
530            | (Ordering::Less, Ordering::Greater) => Some(Ordering::Greater),
531            (Ordering::Equal, Ordering::Less)
532            | (Ordering::Greater, Ordering::Equal)
533            | (Ordering::Greater, Ordering::Less) => Some(Ordering::Less),
534            _ => None,
535        }
536    }
537}
538
539impl From<ExtensibleBitmapSpan> for CategorySpan {
540    fn from(value: ExtensibleBitmapSpan) -> CategorySpan {
541        CategorySpan {
542            low: CategoryId(NonZeroU32::new(value.low + 1).unwrap()),
543            high: CategoryId(NonZeroU32::new(value.high + 1).unwrap()),
544        }
545    }
546}
547
548impl From<&CategorySpan> for CategorySpan {
549    fn from(value: &CategorySpan) -> Self {
550        value.clone()
551    }
552}
553
554/// Errors that may be returned when attempting to parse or validate a security context.
555#[derive(Clone, Debug, Error, Eq, PartialEq)]
556pub enum SecurityContextError {
557    #[error("security context syntax is invalid")]
558    InvalidSyntax,
559    #[error("sensitivity {name:?} not defined by policy")]
560    UnknownSensitivity { name: BString },
561    #[error("category {name:?} not defined by policy")]
562    UnknownCategory { name: BString },
563    #[error("user {name:?} not defined by policy")]
564    UnknownUser { name: BString },
565    #[error("role {name:?} not defined by policy")]
566    UnknownRole { name: BString },
567    #[error("type {name:?} not defined by policy")]
568    UnknownType { name: BString },
569    #[error("role {role:?} not valid for {user:?}")]
570    InvalidRoleForUser { role: BString, user: BString },
571    #[error("type {type_:?} not valid for {role:?}")]
572    InvalidTypeForRole { role: BString, type_: BString },
573    #[error("security level {level:?} not valid for {user:?}")]
574    InvalidLevelForUser { level: BString, user: BString },
575    #[error("high security level {high:?} lower than low level {low:?}")]
576    InvalidSecurityRange { low: BString, high: BString },
577}
578
579#[cfg(test)]
580mod tests {
581    use super::super::{Policy, parse_policy_by_value};
582    use super::*;
583
584    use std::num::NonZeroU32;
585
586    type TestPolicy = Policy;
587    fn test_policy() -> TestPolicy {
588        const TEST_POLICY: &[u8] =
589            include_bytes!("../../testdata/micro_policies/security_context_tests_policy.pp");
590        parse_policy_by_value(TEST_POLICY.to_vec()).unwrap().validate().unwrap()
591    }
592
593    // Represents a `CategorySpan`.
594    #[derive(Debug, Eq, PartialEq)]
595    struct CategoryItem<'a> {
596        low: &'a str,
597        high: &'a str,
598    }
599
600    fn user_name(policy: &TestPolicy, id: UserId) -> &str {
601        std::str::from_utf8(policy.0.parsed_policy().user(id).name_bytes()).unwrap()
602    }
603
604    fn role_name(policy: &TestPolicy, id: RoleId) -> &str {
605        std::str::from_utf8(policy.0.parsed_policy().role(id).name_bytes()).unwrap()
606    }
607
608    fn type_name(policy: &TestPolicy, id: TypeId) -> &str {
609        std::str::from_utf8(policy.0.parsed_policy().type_(id).name_bytes()).unwrap()
610    }
611
612    fn sensitivity_name(policy: &TestPolicy, id: SensitivityId) -> &str {
613        std::str::from_utf8(policy.0.parsed_policy().sensitivity(id).name_bytes()).unwrap()
614    }
615
616    fn category_name(policy: &TestPolicy, id: CategoryId) -> &str {
617        std::str::from_utf8(policy.0.parsed_policy().category(id).name_bytes()).unwrap()
618    }
619
620    fn category_span<'a>(policy: &'a TestPolicy, category: &CategorySpan) -> CategoryItem<'a> {
621        CategoryItem {
622            low: category_name(policy, category.low),
623            high: category_name(policy, category.high),
624        }
625    }
626
627    fn category_spans<'a>(
628        policy: &'a TestPolicy,
629        categories: &Vec<CategorySpan>,
630    ) -> Vec<CategoryItem<'a>> {
631        categories.iter().map(|x| category_span(policy, x)).collect()
632    }
633
634    // A test helper that creates a category span from a pair of positive integers.
635    fn cat(low: u32, high: u32) -> CategorySpan {
636        CategorySpan {
637            low: CategoryId(NonZeroU32::new(low).expect("category ids are nonzero")),
638            high: CategoryId(NonZeroU32::new(high).expect("category ids are nonzero")),
639        }
640    }
641
642    // A test helper that compares two sets of catetories.
643    fn compare(lhs: &[CategorySpan], rhs: &[CategorySpan]) -> Option<Ordering> {
644        CategoryIterator::new(lhs.iter()).compare(&CategoryIterator::new(rhs.iter()))
645    }
646
647    #[test]
648    fn category_compare() {
649        let cat_1 = cat(1, 1);
650        let cat_2 = cat(1, 3);
651        let cat_3 = cat(2, 3);
652        assert_eq!(cat_1.compare(&cat_1), Some(Ordering::Equal));
653        assert_eq!(cat_1.compare(&cat_2), Some(Ordering::Less));
654        assert_eq!(cat_1.compare(&cat_3), None);
655        assert_eq!(cat_2.compare(&cat_1), Some(Ordering::Greater));
656        assert_eq!(cat_2.compare(&cat_3), Some(Ordering::Greater));
657    }
658
659    #[test]
660    fn categories_compare_empty_iter() {
661        let cats_0 = &[];
662        let cats_1 = &[cat(1, 1)];
663        assert_eq!(compare(cats_0, cats_0), Some(Ordering::Equal));
664        assert_eq!(compare(cats_0, cats_1), Some(Ordering::Less));
665        assert_eq!(compare(cats_1, cats_0), Some(Ordering::Greater));
666    }
667
668    #[test]
669    fn categories_compare_same_length() {
670        let cats_1 = &[cat(1, 1), cat(3, 3)];
671        let cats_2 = &[cat(1, 1), cat(4, 4)];
672        let cats_3 = &[cat(1, 2), cat(4, 4)];
673        let cats_4 = &[cat(1, 2), cat(4, 5)];
674
675        assert_eq!(compare(cats_1, cats_1), Some(Ordering::Equal));
676        assert_eq!(compare(cats_1, cats_2), None);
677        assert_eq!(compare(cats_1, cats_3), None);
678        assert_eq!(compare(cats_1, cats_4), None);
679
680        assert_eq!(compare(cats_2, cats_1), None);
681        assert_eq!(compare(cats_2, cats_2), Some(Ordering::Equal));
682        assert_eq!(compare(cats_2, cats_3), Some(Ordering::Less));
683        assert_eq!(compare(cats_2, cats_4), Some(Ordering::Less));
684
685        assert_eq!(compare(cats_3, cats_1), None);
686        assert_eq!(compare(cats_3, cats_2), Some(Ordering::Greater));
687        assert_eq!(compare(cats_3, cats_3), Some(Ordering::Equal));
688        assert_eq!(compare(cats_3, cats_4), Some(Ordering::Less));
689
690        assert_eq!(compare(cats_4, cats_1), None);
691        assert_eq!(compare(cats_4, cats_2), Some(Ordering::Greater));
692        assert_eq!(compare(cats_4, cats_3), Some(Ordering::Greater));
693        assert_eq!(compare(cats_4, cats_4), Some(Ordering::Equal));
694    }
695
696    #[test]
697    fn categories_compare_different_lengths() {
698        let cats_1 = &[cat(1, 1)];
699        let cats_2 = &[cat(1, 4)];
700        let cats_3 = &[cat(1, 1), cat(4, 4)];
701        let cats_4 = &[cat(1, 2), cat(4, 5), cat(7, 7)];
702
703        assert_eq!(compare(cats_1, cats_3), Some(Ordering::Less));
704        assert_eq!(compare(cats_1, cats_4), Some(Ordering::Less));
705
706        assert_eq!(compare(cats_2, cats_3), Some(Ordering::Greater));
707        assert_eq!(compare(cats_2, cats_4), None);
708
709        assert_eq!(compare(cats_3, cats_1), Some(Ordering::Greater));
710        assert_eq!(compare(cats_3, cats_2), Some(Ordering::Less));
711        assert_eq!(compare(cats_3, cats_4), Some(Ordering::Less));
712
713        assert_eq!(compare(cats_4, cats_1), Some(Ordering::Greater));
714        assert_eq!(compare(cats_4, cats_2), None);
715        assert_eq!(compare(cats_4, cats_3), Some(Ordering::Greater));
716    }
717
718    #[test]
719    // Test cases where one interval appears before or after all intervals of the
720    // other set, or in a gap between intervals of the other set.
721    fn categories_compare_with_gaps() {
722        let cats_1 = &[cat(1, 2), cat(4, 5)];
723        let cats_2 = &[cat(4, 5)];
724        let cats_3 = &[cat(2, 5), cat(10, 11)];
725        let cats_4 = &[cat(2, 5), cat(7, 8), cat(10, 11)];
726
727        assert_eq!(compare(cats_1, cats_2), Some(Ordering::Greater));
728        assert_eq!(compare(cats_1, cats_3), None);
729        assert_eq!(compare(cats_1, cats_4), None);
730
731        assert_eq!(compare(cats_2, cats_1), Some(Ordering::Less));
732        assert_eq!(compare(cats_2, cats_3), Some(Ordering::Less));
733        assert_eq!(compare(cats_2, cats_4), Some(Ordering::Less));
734
735        assert_eq!(compare(cats_3, cats_1), None);
736        assert_eq!(compare(cats_3, cats_2), Some(Ordering::Greater));
737        assert_eq!(compare(cats_3, cats_4), Some(Ordering::Less));
738
739        assert_eq!(compare(cats_4, cats_1), None);
740        assert_eq!(compare(cats_4, cats_2), Some(Ordering::Greater));
741        assert_eq!(compare(cats_4, cats_3), Some(Ordering::Greater));
742    }
743
744    #[test]
745    fn parse_security_context_single_sensitivity() {
746        let policy = test_policy();
747        let security_context = policy
748            .parse_security_context(b"user0:object_r:type0:s0".into())
749            .expect("creating security context should succeed");
750        assert_eq!(user_name(&policy, security_context.user), "user0");
751        assert_eq!(role_name(&policy, security_context.role), "object_r");
752        assert_eq!(type_name(&policy, security_context.type_), "type0");
753        assert_eq!(sensitivity_name(&policy, security_context.low_level.sensitivity), "s0");
754        assert_eq!(security_context.low_level.categories, Vec::new());
755        assert_eq!(security_context.high_level, None);
756    }
757
758    #[test]
759    fn parse_security_context_with_sensitivity_range() {
760        let policy = test_policy();
761        let security_context = policy
762            .parse_security_context(b"user0:object_r:type0:s0-s1".into())
763            .expect("creating security context should succeed");
764        assert_eq!(user_name(&policy, security_context.user), "user0");
765        assert_eq!(role_name(&policy, security_context.role), "object_r");
766        assert_eq!(type_name(&policy, security_context.type_), "type0");
767        assert_eq!(sensitivity_name(&policy, security_context.low_level.sensitivity), "s0");
768        assert_eq!(security_context.low_level.categories, Vec::new());
769        let high_level = security_context.high_level.as_ref().unwrap();
770        assert_eq!(sensitivity_name(&policy, high_level.sensitivity), "s1");
771        assert_eq!(high_level.categories, Vec::new());
772    }
773
774    #[test]
775    fn parse_security_context_with_single_sensitivity_and_categories_interval() {
776        let policy = test_policy();
777        let security_context = policy
778            .parse_security_context(b"user0:object_r:type0:s1:c0.c4".into())
779            .expect("creating security context should succeed");
780        assert_eq!(user_name(&policy, security_context.user), "user0");
781        assert_eq!(role_name(&policy, security_context.role), "object_r");
782        assert_eq!(type_name(&policy, security_context.type_), "type0");
783        assert_eq!(sensitivity_name(&policy, security_context.low_level.sensitivity), "s1");
784        assert_eq!(
785            category_spans(&policy, &security_context.low_level.categories),
786            [CategoryItem { low: "c0", high: "c4" }]
787        );
788        assert_eq!(security_context.high_level, None);
789    }
790
791    #[test]
792    fn parse_security_context_and_normalize_categories() {
793        let policy = &test_policy();
794        let normalize = {
795            |security_context: &str| -> String {
796                String::from_utf8(
797                    policy.serialize_security_context(
798                        &policy
799                            .parse_security_context(security_context.into())
800                            .expect("creating security context should succeed"),
801                    ),
802                )
803                .unwrap()
804            }
805        };
806        // Overlapping category ranges are merged.
807        assert_eq!(normalize("user0:object_r:type0:s1:c0.c1,c1"), "user0:object_r:type0:s1:c0.c1");
808        assert_eq!(
809            normalize("user0:object_r:type0:s1:c0.c2,c1.c2"),
810            "user0:object_r:type0:s1:c0.c2"
811        );
812        assert_eq!(
813            normalize("user0:object_r:type0:s1:c0.c2,c1.c3"),
814            "user0:object_r:type0:s1:c0.c3"
815        );
816        // Adjacent category ranges are merged.
817        assert_eq!(normalize("user0:object_r:type0:s1:c0.c1,c2"), "user0:object_r:type0:s1:c0.c2");
818        // Category ranges are ordered by first element.
819        assert_eq!(
820            normalize("user0:object_r:type0:s1:c2.c3,c0"),
821            "user0:object_r:type0:s1:c0,c2.c3"
822        );
823    }
824
825    #[test]
826    fn parse_security_context_with_sensitivity_range_and_category_interval() {
827        let policy = test_policy();
828        let security_context = policy
829            .parse_security_context(b"user0:object_r:type0:s0-s1:c0.c4".into())
830            .expect("creating security context should succeed");
831        assert_eq!(user_name(&policy, security_context.user), "user0");
832        assert_eq!(role_name(&policy, security_context.role), "object_r");
833        assert_eq!(type_name(&policy, security_context.type_), "type0");
834        assert_eq!(sensitivity_name(&policy, security_context.low_level.sensitivity), "s0");
835        assert_eq!(security_context.low_level.categories, Vec::new());
836        let high_level = security_context.high_level.as_ref().unwrap();
837        assert_eq!(sensitivity_name(&policy, high_level.sensitivity), "s1");
838        assert_eq!(
839            category_spans(&policy, &high_level.categories),
840            [CategoryItem { low: "c0", high: "c4" }]
841        );
842    }
843
844    #[test]
845    fn parse_security_context_with_sensitivity_range_with_categories() {
846        let policy = test_policy();
847        let security_context = policy
848            .parse_security_context(b"user0:object_r:type0:s0:c0-s1:c0.c4".into())
849            .expect("creating security context should succeed");
850        assert_eq!(user_name(&policy, security_context.user), "user0");
851        assert_eq!(role_name(&policy, security_context.role), "object_r");
852        assert_eq!(type_name(&policy, security_context.type_), "type0");
853        assert_eq!(sensitivity_name(&policy, security_context.low_level.sensitivity), "s0");
854        assert_eq!(
855            category_spans(&policy, &security_context.low_level.categories),
856            [CategoryItem { low: "c0", high: "c0" }]
857        );
858
859        let high_level = security_context.high_level.as_ref().unwrap();
860        assert_eq!(sensitivity_name(&policy, high_level.sensitivity), "s1");
861        assert_eq!(
862            category_spans(&policy, &high_level.categories),
863            [CategoryItem { low: "c0", high: "c4" }]
864        );
865    }
866
867    #[test]
868    fn parse_security_context_with_single_sensitivity_and_category_list() {
869        let policy = test_policy();
870        let security_context = policy
871            .parse_security_context(b"user0:object_r:type0:s1:c0,c4".into())
872            .expect("creating security context should succeed");
873        assert_eq!(user_name(&policy, security_context.user), "user0");
874        assert_eq!(role_name(&policy, security_context.role), "object_r");
875        assert_eq!(type_name(&policy, security_context.type_), "type0");
876        assert_eq!(sensitivity_name(&policy, security_context.low_level.sensitivity), "s1");
877        assert_eq!(
878            category_spans(&policy, &security_context.low_level.categories),
879            [CategoryItem { low: "c0", high: "c0" }, CategoryItem { low: "c4", high: "c4" }]
880        );
881        assert_eq!(security_context.high_level, None);
882    }
883
884    #[test]
885    fn parse_security_context_with_single_sensitivity_and_category_list_and_range() {
886        let policy = test_policy();
887        let security_context = policy
888            .parse_security_context(b"user0:object_r:type0:s1:c0,c3.c4".into())
889            .expect("creating security context should succeed");
890        assert_eq!(user_name(&policy, security_context.user), "user0");
891        assert_eq!(role_name(&policy, security_context.role), "object_r");
892        assert_eq!(type_name(&policy, security_context.type_), "type0");
893        assert_eq!(sensitivity_name(&policy, security_context.low_level.sensitivity), "s1");
894        assert_eq!(
895            category_spans(&policy, &security_context.low_level.categories),
896            [CategoryItem { low: "c0", high: "c0" }, CategoryItem { low: "c3", high: "c4" }]
897        );
898        assert_eq!(security_context.high_level, None);
899    }
900
901    #[test]
902    fn parse_invalid_syntax() {
903        let policy = test_policy();
904        for invalid_label in [
905            "user0",
906            "user0:object_r",
907            "user0:object_r:type0",
908            "user0:object_r:type0:s0-",
909            "user0:object_r:type0:s0:s0:s0",
910            "user0:object_r:type0:s0:c0.c0", // Category upper bound is equal to lower bound.
911            "user0:object_r:type0:s0:c1.c0", // Category upper bound is less than lower bound.
912        ] {
913            assert_eq!(
914                policy.parse_security_context(invalid_label.as_bytes().into()),
915                Err(SecurityContextError::InvalidSyntax),
916                "validating {:?}",
917                invalid_label
918            );
919        }
920    }
921
922    #[test]
923    fn parse_invalid_sensitivity() {
924        let policy = test_policy();
925        for invalid_label in ["user0:object_r:type0:s_invalid", "user0:object_r:type0:s0-s_invalid"]
926        {
927            assert_eq!(
928                policy.parse_security_context(invalid_label.as_bytes().into()),
929                Err(SecurityContextError::UnknownSensitivity { name: "s_invalid".into() }),
930                "validating {:?}",
931                invalid_label
932            );
933        }
934    }
935
936    #[test]
937    fn parse_invalid_category() {
938        let policy = test_policy();
939        for invalid_label in
940            ["user0:object_r:type0:s1:c_invalid", "user0:object_r:type0:s1:c0.c_invalid"]
941        {
942            assert_eq!(
943                policy.parse_security_context(invalid_label.as_bytes().into()),
944                Err(SecurityContextError::UnknownCategory { name: "c_invalid".into() }),
945                "validating {:?}",
946                invalid_label
947            );
948        }
949    }
950
951    #[test]
952    fn invalid_security_context_fields() {
953        let policy = test_policy();
954
955        // Fails validation because the security context's high level does not dominate its
956        // low level: the low level has categories that the high level does not.
957        let context = policy
958            .parse_security_context(b"user0:object_r:type0:s1:c0,c3.c4-s1".into())
959            .expect("successfully parsed");
960        assert_eq!(
961            policy.validate_security_context(&context),
962            Err(SecurityContextError::InvalidSecurityRange {
963                low: "s1:c0,c3.c4".into(),
964                high: "s1".into()
965            })
966        );
967
968        // Fails validation because the security context's high level does not dominate its
969        // low level: the category sets of the high level and low level are not comparable.
970        let context = policy
971            .parse_security_context(b"user0:object_r:type0:s1:c0-s1:c1".into())
972            .expect("successfully parsed");
973        assert_eq!(
974            policy.validate_security_context(&context),
975            Err(SecurityContextError::InvalidSecurityRange {
976                low: "s1:c0".into(),
977                high: "s1:c1".into()
978            })
979        );
980
981        // Fails validation because the security context's high level does not dominate its
982        // low level: the sensitivity of the high level is lower than that of the low level.
983        let context = policy
984            .parse_security_context(b"user0:object_r:type0:s1:c0-s0:c0.c1".into())
985            .expect("successfully parsed");
986        assert_eq!(
987            policy.validate_security_context(&context),
988            Err(SecurityContextError::InvalidSecurityRange {
989                low: "s1:c0".into(),
990                high: "s0:c0.c1".into()
991            })
992        );
993
994        // Fails validation because the policy's high level does not dominate the
995        // security context's high level: the security context's high level has categories
996        // that the policy's high level does not.
997        let context = policy
998            .parse_security_context(b"user1:subject_r:type0:s1-s1:c3".into())
999            .expect("successfully parsed");
1000        assert_eq!(
1001            policy.validate_security_context(&context),
1002            Err(SecurityContextError::InvalidLevelForUser {
1003                level: "s1:c3".into(),
1004                user: "user1".into(),
1005            })
1006        );
1007
1008        // Fails validation because the security context's low level does not dominate
1009        // the policy's low level: the security context's low level has a lower sensitivity
1010        // than the policy's low level.
1011        let context = policy
1012            .parse_security_context(b"user1:object_r:type0:s0".into())
1013            .expect("successfully parsed");
1014        assert_eq!(
1015            policy.validate_security_context(&context),
1016            Err(SecurityContextError::InvalidLevelForUser {
1017                level: "s0".into(),
1018                user: "user1".into(),
1019            })
1020        );
1021
1022        // Fails validation because the sensitivity is not valid for the user.
1023        let context = policy
1024            .parse_security_context(b"user1:object_r:type0:s0".into())
1025            .expect("successfully parsed");
1026        assert!(policy.validate_security_context(&context).is_err());
1027
1028        // Fails validation because the role is not valid for the user.
1029        let context = policy
1030            .parse_security_context(b"user0:subject_r:type0:s0".into())
1031            .expect("successfully parsed");
1032        assert!(policy.validate_security_context(&context).is_err());
1033
1034        // Fails validation because the type is not valid for the role.
1035        let context = policy
1036            .parse_security_context(b"user1:subject_r:non_subject_t:s1".into())
1037            .expect("successfully parsed");
1038        assert!(policy.validate_security_context(&context).is_err());
1039
1040        // Passes validation even though the role is not explicitly allowed for the user,
1041        // because it is the special "object_r" role, used when labelling resources.
1042        let context = policy
1043            .parse_security_context(b"user1:object_r:type0:s1".into())
1044            .expect("successfully parsed");
1045        assert!(policy.validate_security_context(&context).is_ok());
1046    }
1047
1048    #[test]
1049    fn format_security_contexts() {
1050        let policy = test_policy();
1051        for label in [
1052            "user0:object_r:type0:s0",
1053            "user0:object_r:type0:s0-s1",
1054            "user0:object_r:type0:s1:c0.c4",
1055            "user0:object_r:type0:s0-s1:c0.c4",
1056            "user0:object_r:type0:s1:c0,c3",
1057            "user0:object_r:type0:s0-s1:c0,c2,c4",
1058            "user0:object_r:type0:s1:c0,c3.c4-s1:c0,c2.c4",
1059        ] {
1060            let security_context =
1061                policy.parse_security_context(label.as_bytes().into()).expect("should succeed");
1062            assert_eq!(policy.serialize_security_context(&security_context), label.as_bytes());
1063        }
1064    }
1065}