Skip to main content

selinux/policy/
security_context.rs

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