selinux/policy/
security_context.rs

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