fapolicy_rules/subject.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, SubjPart};
12
13/// # Subject
14/// The subject is the process that is performing actions on system resources.
15/// The fields in the rule that describe the subject are written in a `name=value` format.
16/// There can be one or more subject fields. Each field is and'ed with others to decide if a rule triggers.
17#[derive(Clone, Debug, PartialEq)]
18pub struct Subject {
19 pub parts: Vec<Part>,
20}
21
22impl Subject {
23 pub fn new(parts: Vec<Part>) -> Self {
24 Subject { 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![SubjPart::All])
33 }
34
35 pub fn exe(&self) -> Option<String> {
36 match self.parts.iter().find(|p| matches!(p, Part::Exe(_))) {
37 Some(Part::Exe(path)) => Some(path.clone()),
38 _ => None,
39 }
40 }
41
42 pub fn from_exe(path: &str) -> Subject {
43 Subject::new(vec![SubjPart::Exe(path.into())])
44 }
45}
46
47/// # Subject Field
48/// Composed with logical AND to create the Subject of the rule
49///
50/// ## execdirs
51/// The `execdirs` option will match against the following list of directories:
52/// - `/usr/`
53/// - `/bin/`
54/// - `/sbin/`
55/// - `/lib/`
56/// - `/lib64/`
57/// - `/usr/libexec/`
58///
59/// ## systemdirs
60/// The `systemdirs` option will match against the same list as `execdirs` but also includes `/etc/`.
61///
62/// ### Currently unsupported Subject Fields
63/// - `auid`: This is the login uid that the audit system assigns users when they log in to the system. Daemons have a value of -1.
64/// - `sessionid`: This is the numeric session id that the audit system assigns to users when they log in. Daemons have a value of -1.
65/// - `pid`: This is the numeric process id that a program has.
66/// - `dir`: If you wish to match a directory, then use this by giving the full path to the directory.
67/// - Its recommended to end with the `/` to ensure it matches a directory.
68/// - There are 3 keywords that `dir` supports:
69/// - `execdirs`
70/// - `systemdirs`
71/// - `untrusted`
72/// - `ftype`: This option takes the mime type of a file as an argument. If you wish to check the mime type of a file while writing rules, run the following command:
73/// - `file --mime-type /path-to-file`
74/// - `device`: This option will match against the device that the executable resides on. To use it, start with `/dev/` and add the target device name.
75/// - `pattern`: There are various ways that an attacker may try to execute code that may reveal itself in the pattern of file accesses made during program startup. This rule can take one of several options depending on which access patterns is wished to be blocked. Fapolicyd is able to detect these different access patterns and provide the access decision as soon as it identifies the pattern. The pattern type can be any of:
76/// - `normal`: This matches against any ELF program that is dynamically linked.
77/// - `ld_so`: This matches against access patterns that indicate that the program is being started directly by the runtime linker.
78/// - `ld_preload`: This matches against access patterns that indicate that the program is being started with either `LD_PRELOAD` or `LD_AUDIT` present in the environment. Note that even without this rule, you have protection against `LD_PRELOAD` of unknown binaries when the rules are written such that trust is used to determine if a library should be opened. In that case, the preloaded library would be denied but the application will still execute. This rule makes it so that even trusted libraries can be denied and the application will not execute.
79/// - `static`: This matches against ELF files that are statically linked.
80///
81#[derive(Clone, Debug, PartialEq)]
82pub enum Part {
83 /// This matches against any subject. When used, this must be the only subject in the rule.
84 All,
85 /// This is the shortened command name. When an interpreter starts a program, it usually renames the program to the script rather than the interpreter.
86 Comm(String),
87 /// This is the user id that the program is running under.
88 Uid(u32),
89 /// This is the group id that the program is running under.
90 Gid(u32),
91 /// This is the numeric process id that a program has.
92 Pid(u32),
93 /// This is the full path to the executable. Globbing is not supported. You may also use the special keyword \fBuntrusted\fP to match on the subject not being listed in the rpm database.
94 Exe(String),
95 Pattern(String),
96 /// This is a boolean describing whether it is required for the subject 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. When trust is used on the subject, it could be a daemon. If that daemon gets updated on disk, the trustdb will be updated to the new SHA256 hash. If the integrity setting is not none, the running daemon is not likely to be trusted unless it gets restarted. The default rules are not written in a way that this would happen. But this needs to be highlighted as it may not be obvious when writing a new rule.
97 Trust(bool),
98}
99
100impl From<Part> for Subject {
101 fn from(p: Part) -> Self {
102 Subject { parts: vec![p] }
103 }
104}
105
106impl Display for Subject {
107 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
108 let s: String = self
109 .parts
110 .iter()
111 .map(|p| format!("{}", p))
112 .collect::<Vec<String>>()
113 .join(" ");
114 f.write_fmt(format_args!("{}", s))
115 }
116}
117
118impl Display for Part {
119 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
120 match self {
121 Part::All => f.write_str("all"),
122 Part::Comm(cmd) => f.write_fmt(format_args!("comm={}", cmd)),
123 Part::Uid(id) => f.write_fmt(format_args!("uid={}", id)),
124 Part::Gid(id) => f.write_fmt(format_args!("gid={}", id)),
125 Part::Pid(id) => f.write_fmt(format_args!("pid={}", id)),
126 Part::Exe(id) => f.write_fmt(format_args!("exe={}", id)),
127 Part::Pattern(id) => f.write_fmt(format_args!("pattern={}", id)),
128 Part::Trust(b) => f.write_fmt(format_args!("trust={}", bool_to_c(*b))),
129 }
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use std::str::FromStr;
137
138 #[test]
139 fn display() {
140 assert_eq!(format!("{}", Part::All), "all");
141 assert_eq!(format!("{}", Part::Comm("dnf".into())), "comm=dnf");
142 assert_eq!(format!("{}", Part::Uid(42)), "uid=42");
143 assert_eq!(format!("{}", Part::Gid(42)), "gid=42");
144 assert_eq!(format!("{}", Part::Trust(false)), "trust=0");
145 assert_eq!(format!("{}", Part::Trust(true)), "trust=1");
146 }
147
148 #[test]
149 fn fromstr() -> Result<(), String> {
150 assert_eq!(Subject::from(Part::All), Subject::from_str("all")?);
151 assert_eq!(
152 Subject::from(Part::Comm("dnf".into())),
153 Subject::from_str("comm=dnf")?
154 );
155 assert_eq!(Subject::from(Part::Uid(42)), Subject::from_str("uid=42")?);
156 assert_eq!(Subject::from(Part::Gid(42)), Subject::from_str("gid=42")?);
157 assert_eq!(
158 Subject::from(Part::Trust(true)),
159 Subject::from_str("trust=1")?
160 );
161 assert_eq!(
162 Subject::from(Part::Trust(false)),
163 Subject::from_str("trust=0")?
164 );
165 Ok(())
166 }
167}