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, RoleId, TypeId, UserId};
8use crate::NullessByteStr;
9use crate::new_policy::NewPolicy;
10use crate::new_policy::traits::{HasName, HasPolicyId, PolicyId as _};
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(super) 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(super) 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_index: &PolicyIndex,
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_index
119            .users()
120            .get_by_name(user.as_bytes())
121            .ok_or_else(|| SecurityContextError::UnknownUser { name: user.into() })?
122            .id();
123        let role = policy_index
124            .roles()
125            .get_by_name(role.as_bytes())
126            .ok_or_else(|| SecurityContextError::UnknownRole { name: role.into() })?
127            .id();
128        let type_ = policy_index
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_index, low_level)?;
135        let high_level = high_level.map(|x| MlsLevel::from_string(policy_index, 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_index: &PolicyIndex) -> Vec<u8> {
142        let mut levels = self.low_level().to_string(policy_index);
143        if let Some(high_level) = self.high_level() {
144            levels.push(b'-');
145            levels.extend(high_level.to_string(policy_index));
146        }
147        let type_ = policy_index.types().get_by_id(self.type_()).unwrap();
148        let parts: [&[u8]; 4] = [
149            policy_index.users().get_by_id(self.user()).unwrap().name(),
150            policy_index.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_index: &PolicyIndex) -> Result<(), SecurityContextError> {
160        let user = policy_index.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_index).into(),
169                    high: high_level.to_string(policy_index).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_index.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_index.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_index.roles().get_by_id(self.role()).unwrap();
190        if !role.types().contains(self.type_()) {
191            return Err(SecurityContextError::InvalidTypeForRole {
192                type_: policy_index.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_index).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_index).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_index: &PolicyIndex,
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_index
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_index, low_str)?;
253                    let high = Self::category_id_by_name(policy_index, 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_index, 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_index: &PolicyIndex,
270        name: &str,
271    ) -> Result<CategoryId, SecurityContextError> {
272        Ok(policy_index
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::new::CategorySet;
348    use super::super::{Policy, PolicyId, SensitivityId, parse_policy_by_value};
349    use super::*;
350    use std::cmp::Ordering;
351
352    fn test_policy() -> Policy {
353        const TEST_POLICY: &[u8] =
354            include_bytes!("../../testdata/micro_policies/security_context_tests_policy");
355        parse_policy_by_value(TEST_POLICY.to_vec()).unwrap().validate().unwrap()
356    }
357
358    // CategoryItem helper for tests.
359    #[derive(Debug, Eq, PartialEq)]
360    struct CategoryItem {
361        low: String,
362        high: String,
363    }
364
365    fn user_name(policy: &Policy, id: UserId) -> &str {
366        std::str::from_utf8(policy.users().get_by_id(id).unwrap().name()).unwrap()
367    }
368
369    fn role_name(policy: &Policy, id: RoleId) -> &str {
370        std::str::from_utf8(policy.roles().get_by_id(id).unwrap().name()).unwrap()
371    }
372
373    fn type_name(policy: &Policy, id: TypeId) -> &str {
374        std::str::from_utf8(policy.types().get_by_id(id).unwrap().name()).unwrap()
375    }
376
377    fn sensitivity_name(policy: &Policy, id: SensitivityId) -> &str {
378        std::str::from_utf8(policy.sensitivities().get_by_id(id).unwrap().name()).unwrap()
379    }
380
381    fn category_name(policy: &Policy, id: CategoryId) -> &str {
382        std::str::from_utf8(policy.categories().get_by_id(id).unwrap().name()).unwrap()
383    }
384
385    fn category_span(policy: &Policy, category: &CategorySpan) -> CategoryItem {
386        CategoryItem {
387            low: category_name(policy, category.low()).into(),
388            high: category_name(policy, category.high()).into(),
389        }
390    }
391
392    fn category_spans(
393        policy: &Policy,
394        iter: impl Iterator<Item = CategorySpan>,
395    ) -> Vec<CategoryItem> {
396        iter.map(|x| category_span(policy, &x)).collect()
397    }
398
399    // Creates a category range for testing.
400    fn cat(low: u32, high: u32) -> CategorySpan {
401        CategorySpan::new(
402            CategoryId::from_u32(low).expect("category ids are nonzero"),
403            CategoryId::from_u32(high).expect("category ids are nonzero"),
404        )
405    }
406
407    // Compares two sets of categories for testing.
408    fn compare(lhs: &[CategorySpan], rhs: &[CategorySpan]) -> Option<Ordering> {
409        let lhs_set = CategorySet::from_ids(lhs.iter().flat_map(|span| {
410            (span.low().as_u32()..=span.high().as_u32()).map(|i| CategoryId::from_u32(i).unwrap())
411        }));
412        let rhs_set = CategorySet::from_ids(rhs.iter().flat_map(|span| {
413            (span.low().as_u32()..=span.high().as_u32()).map(|i| CategoryId::from_u32(i).unwrap())
414        }));
415        lhs_set.compare(&rhs_set)
416    }
417
418    #[test]
419    fn category_compare() {
420        let cat_1 = cat(1, 1);
421        let cat_2 = cat(1, 3);
422        let cat_3 = cat(2, 3);
423        assert_eq!(compare(&[cat_1.clone()], &[cat_1.clone()]), Some(Ordering::Equal));
424        assert_eq!(compare(&[cat_1.clone()], &[cat_2.clone()]), Some(Ordering::Less));
425        assert_eq!(compare(&[cat_1.clone()], &[cat_3.clone()]), None);
426        assert_eq!(compare(&[cat_2.clone()], &[cat_1.clone()]), Some(Ordering::Greater));
427        assert_eq!(compare(&[cat_2.clone()], &[cat_3.clone()]), Some(Ordering::Greater));
428    }
429
430    #[test]
431    fn categories_compare_empty_iter() {
432        let cats_0 = &[];
433        let cats_1 = &[cat(1, 1)];
434        assert_eq!(compare(cats_0, cats_0), Some(Ordering::Equal));
435        assert_eq!(compare(cats_0, cats_1), Some(Ordering::Less));
436        assert_eq!(compare(cats_1, cats_0), Some(Ordering::Greater));
437    }
438
439    #[test]
440    fn categories_compare_same_length() {
441        let cats_1 = &[cat(1, 1), cat(3, 3)];
442        let cats_2 = &[cat(1, 1), cat(4, 4)];
443        let cats_3 = &[cat(1, 2), cat(4, 4)];
444        let cats_4 = &[cat(1, 2), cat(4, 5)];
445
446        assert_eq!(compare(cats_1, cats_1), Some(Ordering::Equal));
447        assert_eq!(compare(cats_1, cats_2), None);
448        assert_eq!(compare(cats_1, cats_3), None);
449        assert_eq!(compare(cats_1, cats_4), None);
450
451        assert_eq!(compare(cats_2, cats_1), None);
452        assert_eq!(compare(cats_2, cats_2), Some(Ordering::Equal));
453        assert_eq!(compare(cats_2, cats_3), Some(Ordering::Less));
454        assert_eq!(compare(cats_2, cats_4), Some(Ordering::Less));
455
456        assert_eq!(compare(cats_3, cats_1), None);
457        assert_eq!(compare(cats_3, cats_2), Some(Ordering::Greater));
458        assert_eq!(compare(cats_3, cats_3), Some(Ordering::Equal));
459        assert_eq!(compare(cats_3, cats_4), Some(Ordering::Less));
460
461        assert_eq!(compare(cats_4, cats_1), None);
462        assert_eq!(compare(cats_4, cats_2), Some(Ordering::Greater));
463        assert_eq!(compare(cats_4, cats_3), Some(Ordering::Greater));
464        assert_eq!(compare(cats_4, cats_4), Some(Ordering::Equal));
465    }
466
467    #[test]
468    fn categories_compare_different_lengths() {
469        let cats_1 = &[cat(1, 1)];
470        let cats_2 = &[cat(1, 4)];
471        let cats_3 = &[cat(1, 1), cat(4, 4)];
472        let cats_4 = &[cat(1, 2), cat(4, 5), cat(7, 7)];
473
474        assert_eq!(compare(cats_1, cats_3), Some(Ordering::Less));
475        assert_eq!(compare(cats_1, cats_4), Some(Ordering::Less));
476
477        assert_eq!(compare(cats_2, cats_3), Some(Ordering::Greater));
478        assert_eq!(compare(cats_2, cats_4), None);
479
480        assert_eq!(compare(cats_3, cats_1), Some(Ordering::Greater));
481        assert_eq!(compare(cats_3, cats_2), Some(Ordering::Less));
482        assert_eq!(compare(cats_3, cats_4), Some(Ordering::Less));
483
484        assert_eq!(compare(cats_4, cats_1), Some(Ordering::Greater));
485        assert_eq!(compare(cats_4, cats_2), None);
486        assert_eq!(compare(cats_4, cats_3), Some(Ordering::Greater));
487    }
488
489    #[test]
490    // Test cases where one interval appears before or after all intervals of the
491    // other set, or in a gap between intervals of the other set.
492    fn categories_compare_with_gaps() {
493        let cats_1 = &[cat(1, 2), cat(4, 5)];
494        let cats_2 = &[cat(4, 5)];
495        let cats_3 = &[cat(2, 5), cat(10, 11)];
496        let cats_4 = &[cat(2, 5), cat(7, 8), cat(10, 11)];
497
498        assert_eq!(compare(cats_1, cats_2), Some(Ordering::Greater));
499        assert_eq!(compare(cats_1, cats_3), None);
500        assert_eq!(compare(cats_1, cats_4), None);
501
502        assert_eq!(compare(cats_2, cats_1), Some(Ordering::Less));
503        assert_eq!(compare(cats_2, cats_3), Some(Ordering::Less));
504        assert_eq!(compare(cats_2, cats_4), Some(Ordering::Less));
505
506        assert_eq!(compare(cats_3, cats_1), None);
507        assert_eq!(compare(cats_3, cats_2), Some(Ordering::Greater));
508        assert_eq!(compare(cats_3, cats_4), Some(Ordering::Less));
509
510        assert_eq!(compare(cats_4, cats_1), None);
511        assert_eq!(compare(cats_4, cats_2), Some(Ordering::Greater));
512        assert_eq!(compare(cats_4, cats_3), Some(Ordering::Greater));
513    }
514
515    #[test]
516    fn parse_security_context_single_sensitivity() {
517        let policy = test_policy();
518        let security_context = policy
519            .parse_security_context(b"user0:object_r:type0:s0".into())
520            .expect("creating security context should succeed");
521        assert_eq!(user_name(&policy, security_context.user()), "user0");
522        assert_eq!(role_name(&policy, security_context.role()), "object_r");
523        assert_eq!(type_name(&policy, security_context.type_()), "type0");
524        assert_eq!(sensitivity_name(&policy, security_context.low_level().sensitivity()), "s0");
525        assert!(category_spans(&policy, security_context.low_level().category_spans()).is_empty());
526        assert_eq!(security_context.high_level(), None);
527    }
528
529    #[test]
530    fn parse_security_context_with_sensitivity_range() {
531        let policy = test_policy();
532        let security_context = policy
533            .parse_security_context(b"user0:object_r:type0:s0-s1".into())
534            .expect("creating security context should succeed");
535        assert_eq!(user_name(&policy, security_context.user()), "user0");
536        assert_eq!(role_name(&policy, security_context.role()), "object_r");
537        assert_eq!(type_name(&policy, security_context.type_()), "type0");
538        assert_eq!(sensitivity_name(&policy, security_context.low_level().sensitivity()), "s0");
539        assert!(category_spans(&policy, security_context.low_level().category_spans()).is_empty());
540        let high_level = security_context.high_level().unwrap();
541        assert_eq!(sensitivity_name(&policy, high_level.sensitivity()), "s1");
542        assert!(category_spans(&policy, high_level.category_spans()).is_empty());
543    }
544
545    #[test]
546    fn parse_security_context_with_single_sensitivity_and_categories_interval() {
547        let policy = test_policy();
548        let security_context = policy
549            .parse_security_context(b"user0:object_r:type0:s1:c0.c4".into())
550            .expect("creating security context should succeed");
551        assert_eq!(user_name(&policy, security_context.user()), "user0");
552        assert_eq!(role_name(&policy, security_context.role()), "object_r");
553        assert_eq!(type_name(&policy, security_context.type_()), "type0");
554        assert_eq!(sensitivity_name(&policy, security_context.low_level().sensitivity()), "s1");
555        assert_eq!(
556            category_spans(&policy, security_context.low_level().category_spans()),
557            [CategoryItem { low: "c0".to_string(), high: "c4".to_string() }]
558        );
559        assert_eq!(security_context.high_level(), None);
560    }
561
562    #[test]
563    fn parse_security_context_and_normalize_categories() {
564        let policy = &test_policy();
565        let normalize = {
566            |security_context: &str| -> String {
567                String::from_utf8(
568                    policy.serialize_security_context(
569                        &policy
570                            .parse_security_context(security_context.into())
571                            .expect("creating security context should succeed"),
572                    ),
573                )
574                .unwrap()
575            }
576        };
577        // Overlapping category ranges are merged.
578        assert_eq!(normalize("user0:object_r:type0:s1:c0.c1,c1"), "user0:object_r:type0:s1:c0,c1");
579        assert_eq!(
580            normalize("user0:object_r:type0:s1:c0.c2,c1.c2"),
581            "user0:object_r:type0:s1:c0.c2"
582        );
583        assert_eq!(
584            normalize("user0:object_r:type0:s1:c0.c2,c1.c3"),
585            "user0:object_r:type0:s1:c0.c3"
586        );
587        // Adjacent category ranges are merged.
588        assert_eq!(normalize("user0:object_r:type0:s1:c0.c1,c2"), "user0:object_r:type0:s1:c0.c2");
589        // Category ranges are ordered by first element.
590        assert_eq!(
591            normalize("user0:object_r:type0:s1:c2.c3,c0"),
592            "user0:object_r:type0:s1:c0,c2,c3"
593        );
594    }
595
596    #[test]
597    fn parse_security_context_with_sensitivity_range_and_category_interval() {
598        let policy = test_policy();
599        let security_context = policy
600            .parse_security_context(b"user0:object_r:type0:s0-s1:c0.c4".into())
601            .expect("creating security context should succeed");
602        assert_eq!(user_name(&policy, security_context.user()), "user0");
603        assert_eq!(role_name(&policy, security_context.role()), "object_r");
604        assert_eq!(type_name(&policy, security_context.type_()), "type0");
605        assert_eq!(sensitivity_name(&policy, security_context.low_level().sensitivity()), "s0");
606        assert!(category_spans(&policy, security_context.low_level().category_spans()).is_empty());
607        let high_level = security_context.high_level().unwrap();
608        assert_eq!(sensitivity_name(&policy, high_level.sensitivity()), "s1");
609        assert_eq!(
610            category_spans(&policy, high_level.category_spans()),
611            [CategoryItem { low: "c0".to_string(), high: "c4".to_string() }]
612        );
613    }
614
615    #[test]
616    fn parse_security_context_with_sensitivity_range_with_categories() {
617        let policy = test_policy();
618        let security_context = policy
619            .parse_security_context(b"user0:object_r:type0:s0:c0-s1:c0.c4".into())
620            .expect("creating security context should succeed");
621        assert_eq!(user_name(&policy, security_context.user()), "user0");
622        assert_eq!(role_name(&policy, security_context.role()), "object_r");
623        assert_eq!(type_name(&policy, security_context.type_()), "type0");
624        assert_eq!(sensitivity_name(&policy, security_context.low_level().sensitivity()), "s0");
625        assert_eq!(
626            category_spans(&policy, security_context.low_level().category_spans()),
627            [CategoryItem { low: "c0".to_string(), high: "c0".to_string() }]
628        );
629
630        let high_level = security_context.high_level().unwrap();
631        assert_eq!(sensitivity_name(&policy, high_level.sensitivity()), "s1");
632        assert_eq!(
633            category_spans(&policy, high_level.category_spans()),
634            [CategoryItem { low: "c0".to_string(), high: "c4".to_string() }]
635        );
636    }
637
638    #[test]
639    fn parse_security_context_with_single_sensitivity_and_category_list() {
640        let policy = test_policy();
641        let security_context = policy
642            .parse_security_context(b"user0:object_r:type0:s1:c0,c4".into())
643            .expect("creating security context should succeed");
644        assert_eq!(user_name(&policy, security_context.user()), "user0");
645        assert_eq!(role_name(&policy, security_context.role()), "object_r");
646        assert_eq!(type_name(&policy, security_context.type_()), "type0");
647        assert_eq!(sensitivity_name(&policy, security_context.low_level().sensitivity()), "s1");
648        assert_eq!(
649            category_spans(&policy, security_context.low_level().category_spans()),
650            [
651                CategoryItem { low: "c0".to_string(), high: "c0".to_string() },
652                CategoryItem { low: "c4".to_string(), high: "c4".to_string() }
653            ]
654        );
655        assert_eq!(security_context.high_level(), None);
656    }
657
658    #[test]
659    fn parse_security_context_with_single_sensitivity_and_category_list_and_range() {
660        let policy = test_policy();
661        let security_context = policy
662            .parse_security_context(b"user0:object_r:type0:s1:c0,c3.c4".into())
663            .expect("creating security context should succeed");
664        assert_eq!(user_name(&policy, security_context.user()), "user0");
665        assert_eq!(role_name(&policy, security_context.role()), "object_r");
666        assert_eq!(type_name(&policy, security_context.type_()), "type0");
667        assert_eq!(sensitivity_name(&policy, security_context.low_level().sensitivity()), "s1");
668        assert_eq!(
669            category_spans(&policy, security_context.low_level().category_spans()),
670            [
671                CategoryItem { low: "c0".to_string(), high: "c0".to_string() },
672                CategoryItem { low: "c3".to_string(), high: "c4".to_string() }
673            ]
674        );
675        assert_eq!(security_context.high_level(), None);
676    }
677
678    #[test]
679    fn parse_invalid_syntax() {
680        let policy = test_policy();
681        for invalid_label in [
682            "user0",
683            "user0:object_r",
684            "user0:object_r:type0",
685            "user0:object_r:type0:s0-",
686            "user0:object_r:type0:s0:s0:s0",
687            "user0:object_r:type0:s0:c0.c0", // Category upper bound is equal to lower bound.
688            "user0:object_r:type0:s0:c1.c0", // Category upper bound is less than lower bound.
689        ] {
690            assert_eq!(
691                policy.parse_security_context(invalid_label.as_bytes().into()),
692                Err(SecurityContextError::InvalidSyntax),
693                "validating {:?}",
694                invalid_label
695            );
696        }
697    }
698
699    #[test]
700    fn parse_invalid_sensitivity() {
701        let policy = test_policy();
702        for invalid_label in ["user0:object_r:type0:s_invalid", "user0:object_r:type0:s0-s_invalid"]
703        {
704            assert_eq!(
705                policy.parse_security_context(invalid_label.as_bytes().into()),
706                Err(SecurityContextError::UnknownSensitivity { name: "s_invalid".into() }),
707                "validating {:?}",
708                invalid_label
709            );
710        }
711    }
712
713    #[test]
714    fn parse_invalid_category() {
715        let policy = test_policy();
716        for invalid_label in
717            ["user0:object_r:type0:s1:c_invalid", "user0:object_r:type0:s1:c0.c_invalid"]
718        {
719            assert_eq!(
720                policy.parse_security_context(invalid_label.as_bytes().into()),
721                Err(SecurityContextError::UnknownCategory { name: "c_invalid".into() }),
722                "validating {:?}",
723                invalid_label
724            );
725        }
726    }
727
728    #[test]
729    fn invalid_security_context_fields() {
730        let policy = test_policy();
731
732        // Fails validation because the security context's high level does not dominate its
733        // low level: the low level has categories that the high level does not.
734        let context = policy
735            .parse_security_context(b"user0:object_r:type0:s1:c0,c3.c4-s1".into())
736            .expect("successfully parsed");
737        assert_eq!(
738            policy.validate_security_context(&context),
739            Err(SecurityContextError::InvalidSecurityRange {
740                low: "s1:c0,c3,c4".into(),
741                high: "s1".into()
742            })
743        );
744
745        // Fails validation because the security context's high level does not dominate its
746        // low level: the category sets of the high level and low level are not comparable.
747        let context = policy
748            .parse_security_context(b"user0:object_r:type0:s1:c0-s1:c1".into())
749            .expect("successfully parsed");
750        assert_eq!(
751            policy.validate_security_context(&context),
752            Err(SecurityContextError::InvalidSecurityRange {
753                low: "s1:c0".into(),
754                high: "s1:c1".into()
755            })
756        );
757
758        // Fails validation because the security context's high level does not dominate its
759        // low level: the sensitivity of the high level is lower than that of the low level.
760        let context = policy
761            .parse_security_context(b"user0:object_r:type0:s1:c0-s0:c0.c1".into())
762            .expect("successfully parsed");
763        assert_eq!(
764            policy.validate_security_context(&context),
765            Err(SecurityContextError::InvalidSecurityRange {
766                low: "s1:c0".into(),
767                high: "s0:c0,c1".into()
768            })
769        );
770
771        // Fails validation because the policy's high level does not dominate the
772        // security context's high level: the security context's high level has categories
773        // that the policy's high level does not.
774        let context = policy
775            .parse_security_context(b"user1:subject_r:type0:s1-s1:c3".into())
776            .expect("successfully parsed");
777        assert_eq!(
778            policy.validate_security_context(&context),
779            Err(SecurityContextError::InvalidLevelForUser {
780                level: "s1:c3".into(),
781                user: "user1".into(),
782            })
783        );
784
785        // Fails validation because the security context's low level does not dominate
786        // the policy's low level: the security context's low level has a lower sensitivity
787        // than the policy's low level.
788        let context = policy
789            .parse_security_context(b"user1:subject_r:type0:s0".into())
790            .expect("successfully parsed");
791        assert_eq!(
792            policy.validate_security_context(&context),
793            Err(SecurityContextError::InvalidLevelForUser {
794                level: "s0".into(),
795                user: "user1".into(),
796            })
797        );
798
799        // Passes validation even though the level is outside the user's range, because the
800        // special "object_r" role is exempt from the user MLS range check.
801        let context = policy
802            .parse_security_context(b"user1:object_r:type0:s0".into())
803            .expect("successfully parsed");
804        assert!(policy.validate_security_context(&context).is_ok());
805
806        // Fails validation because the role is not valid for the user.
807        let context = policy
808            .parse_security_context(b"user0:subject_r:type0:s0".into())
809            .expect("successfully parsed");
810        assert!(policy.validate_security_context(&context).is_err());
811
812        // Fails validation because the type is not valid for the role.
813        let context = policy
814            .parse_security_context(b"user1:subject_r:non_subject_t:s1".into())
815            .expect("successfully parsed");
816        assert!(policy.validate_security_context(&context).is_err());
817
818        // Passes validation even though the role is not explicitly allowed for the user,
819        // because it is the special "object_r" role, used when labelling resources.
820        let context = policy
821            .parse_security_context(b"user1:object_r:type0:s1".into())
822            .expect("successfully parsed");
823        assert!(policy.validate_security_context(&context).is_ok());
824    }
825
826    #[test]
827    fn format_security_contexts() {
828        let policy = test_policy();
829        for label in [
830            "user0:object_r:type0:s0",
831            "user0:object_r:type0:s0-s1",
832            "user0:object_r:type0:s1:c0.c4",
833            "user0:object_r:type0:s0-s1:c0.c4",
834            "user0:object_r:type0:s1:c0,c3",
835            "user0:object_r:type0:s0-s1:c0,c2,c4",
836            "user0:object_r:type0:s1:c0,c3,c4-s1:c0,c2.c4",
837        ] {
838            let security_context =
839                policy.parse_security_context(label.as_bytes().into()).expect("should succeed");
840            assert_eq!(policy.serialize_security_context(&security_context), label.as_bytes());
841        }
842    }
843}