Skip to main content

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