fapolicy_rules/
object.rs

1/*
2 * Copyright Concurrent Technologies Corporation 2021
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 */
8
9use std::fmt::{Display, Formatter};
10
11use crate::{bool_to_c, ObjPart, Rvalue};
12
13/// # Object
14/// The object is the file that the subject is interacting with.
15/// The fields in the rule that describe the object are written in a `name=value` format.
16/// There can be one or more object fields. Each field is and'ed with others to decide if a rule triggers.
17#[derive(Clone, Debug, PartialEq)]
18pub struct Object {
19    pub parts: Vec<Part>,
20}
21
22impl Object {
23    pub fn new(parts: Vec<Part>) -> Self {
24        Object { parts }
25    }
26
27    pub fn is_all(&self) -> bool {
28        self.parts.contains(&Part::All)
29    }
30
31    pub fn all() -> Self {
32        Self::new(vec![ObjPart::All])
33    }
34
35    pub fn path(&self) -> Option<String> {
36        match self.parts.iter().find(|p| matches!(p, Part::Path(_))) {
37            Some(Part::Path(path)) => Some(path.clone()),
38            _ => None,
39        }
40    }
41
42    pub fn from_path(path: &str) -> Object {
43        Object::new(vec![ObjPart::Path(path.into())])
44    }
45}
46
47/// # Object Field
48/// Composed with logical AND to create the Object of the rule
49///
50/// ### Currently unsupported Object Fields
51///   - `sha256hash`: This option matches against the sha256 hash of the file being accessed.
52///     - The hash in the rules should be all lowercase letters and do NOT start with 0x.
53///     - Lowercase is the default output of sha256sum.
54///
55#[derive(Clone, Debug, PartialEq)]
56pub enum Part {
57    /// This matches against any subject. When used, this must be the only subject in the rule.
58    All,
59    /// This option will match against the device that the file being accessed resides on. To use it, start with `/dev/` and add the target device name.
60    Device(String),
61    /// If you wish to match on access to any file in a directory, then use this by giving the full path to the directory.
62    ///  - Its recommended to end with the `/` to ensure it matches a directory.
63    ///  - There are 3 keywords that `dir` supports:
64    ///    - `execdirs`
65    ///    - `systemdirs`
66    ///    - `untrusted`
67    ///
68    /// ### See the `dir` option under Subject for an explanation of these keywords.
69    Dir(String),
70    /// This option matches against the mime type of the file being accessed. See `ftype` under Subject for more information on determining the mime type.
71    FileType(Rvalue),
72    /// This is the full path to the file that will be accessed. Globbing is not supported. You may also use the special keyword `untrusted` to match on the subject not being listed in the rpm database.
73    Path(String),
74    /// This is a boolean describing whether it is required for the object to be in the trust database or not. A value of 1 means its required while 0 means its not. Trust checking is extended by the integrity setting in fapolicyd.conf.
75    Trust(bool),
76}
77
78impl From<Part> for Object {
79    fn from(p: Part) -> Self {
80        Object { parts: vec![p] }
81    }
82}
83
84impl Display for Object {
85    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
86        let s: String = self
87            .parts
88            .iter()
89            .map(|p| format!("{}", p))
90            .collect::<Vec<String>>()
91            .join(" ");
92        f.write_fmt(format_args!("{}", s))
93    }
94}
95
96impl Display for Part {
97    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
98        match self {
99            Part::All => f.write_str("all"),
100            Part::Device(p) => f.write_fmt(format_args!("device={}", p)),
101            Part::Dir(p) => f.write_fmt(format_args!("dir={}", p)),
102            Part::FileType(t) => f.write_fmt(format_args!("ftype={}", t)),
103            Part::Path(p) => f.write_fmt(format_args!("path={}", p)),
104            Part::Trust(b) => f.write_fmt(format_args!("trust={}", bool_to_c(*b))),
105        }
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use crate::file_type::Rvalue::Literal;
112
113    use super::*;
114
115    #[test]
116    fn display() {
117        assert_eq!(format!("{}", Part::All), "all");
118        assert_eq!(format!("{}", Part::Dir("/foo".into())), "dir=/foo");
119        assert_eq!(
120            format!("{}", Part::Device("/dev/cdrom".into())),
121            "device=/dev/cdrom"
122        );
123        assert_eq!(
124            format!(
125                "{}",
126                Part::FileType(Literal("application/x-sharedlib".into()))
127            ),
128            "ftype=application/x-sharedlib"
129        );
130    }
131
132    #[test]
133    fn display_trusted() {
134        assert_eq!(
135            format!("{}", Object::new(vec![Part::All, Part::Trust(true)])),
136            "all trust=1"
137        );
138        assert_eq!(
139            format!(
140                "{}",
141                Object::new(vec![Part::Dir("/foo".into()), Part::Trust(true)])
142            ),
143            "dir=/foo trust=1"
144        );
145        assert_eq!(
146            format!(
147                "{}",
148                Object::new(vec![Part::Device("/dev/cdrom".into()), Part::Trust(true)])
149            ),
150            "device=/dev/cdrom trust=1"
151        );
152        assert_eq!(
153            format!(
154                "{}",
155                Object::new(vec![
156                    Part::FileType(Literal("application/x-sharedlib".into())),
157                    Part::Trust(true)
158                ])
159            ),
160            "ftype=application/x-sharedlib trust=1"
161        );
162    }
163}