fapolicy_rules/parser/
rule.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 nom::character::complete::space0;
10use nom::character::complete::space1;
11use nom::sequence::terminated;
12
13use crate::parser::parse::{end_of_rule, subject_object_parts, StrTrace, TraceResult};
14use crate::parser::{decision, permission};
15use crate::Rule;
16
17pub fn parse(i: StrTrace) -> TraceResult<Rule> {
18    match nom::combinator::complete(nom::sequence::tuple((
19        terminated(decision::parse, space1),
20        terminated(permission::parse, space0),
21        subject_object_parts,
22        end_of_rule,
23    )))(i)
24    {
25        Ok((remaining_input, (dec, perm, so, _))) => Ok((
26            remaining_input,
27            Rule {
28                subj: so.subject,
29                perm,
30                obj: so.object,
31                dec,
32            },
33        )),
34        Err(e) => Err(e),
35    }
36}
37
38pub fn parse_with_error_message(i: StrTrace) -> Result<Rule, String> {
39    match parse(i) {
40        Ok((_, r)) => Ok(r),
41        Err(nom::Err::Error(e)) => Err(e.to_string()),
42        _ => Err("Unrecognized error".to_string()),
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use crate::{Decision, ObjPart, Object, Permission, Rvalue, SubjPart, Subject};
49
50    use super::*;
51
52    // todo;; need better error propagation, and then better negative tests
53    #[test]
54    fn bad_rules() {
55        parse("deny_audit perm=open all : foo".into())
56            .err()
57            .unwrap();
58        parse("deny_audit perm=open all trust=foo : all".into())
59            .err()
60            .unwrap();
61
62        parse("deny_audit perm=open all : all trust=1 foo".into())
63            .err()
64            .unwrap();
65
66        parse("deny_audit perm=open all : all trust=foo".into())
67            .err()
68            .unwrap();
69    }
70
71    #[test]
72    fn parse_rule() {
73        let (rem, r) = parse("deny_audit perm=any pattern=ld_so : all".into())
74            .ok()
75            .unwrap();
76        assert_eq!(Decision::DenyAudit, r.dec);
77        assert_eq!(Permission::Any, r.perm);
78        assert_eq!(Subject::from(SubjPart::Pattern("ld_so".into())), r.subj);
79        assert_eq!(Object::from(ObjPart::All), r.obj);
80        assert!(rem.is_empty());
81
82        let (rem, r) = parse("deny_audit perm=any all : all".into()).ok().unwrap();
83        assert_eq!(Decision::DenyAudit, r.dec);
84        assert_eq!(Permission::Any, r.perm);
85        assert_eq!(Subject::from(SubjPart::All), r.subj);
86        assert_eq!(Object::from(ObjPart::All), r.obj);
87        assert!(rem.is_empty());
88
89        let (rem, r) = parse("deny_audit perm=open all : device=/dev/cdrom".into())
90            .ok()
91            .unwrap();
92        assert_eq!(Decision::DenyAudit, r.dec);
93        assert_eq!(Permission::Open, r.perm);
94        assert_eq!(Subject::from(SubjPart::All), r.subj);
95        assert_eq!(Object::from(ObjPart::Device("/dev/cdrom".into())), r.obj);
96        assert!(rem.is_empty());
97
98        let (rem, r) = parse("deny_audit perm=open exe=/usr/bin/ssh : dir=/opt".into())
99            .ok()
100            .unwrap();
101        assert_eq!(Decision::DenyAudit, r.dec);
102        assert_eq!(Permission::Open, r.perm);
103        assert_eq!(Subject::from(SubjPart::Exe("/usr/bin/ssh".into())), r.subj);
104        assert_eq!(Object::from(ObjPart::Dir("/opt".into())), r.obj);
105        assert!(rem.is_empty());
106
107        let (rem, r) = parse("deny_audit perm=any all : ftype=application/x-bad-elf".into())
108            .ok()
109            .unwrap();
110        assert_eq!(Decision::DenyAudit, r.dec);
111        assert_eq!(Permission::Any, r.perm);
112        assert_eq!(Subject::from(SubjPart::All), r.subj);
113        assert_eq!(
114            Object::from(ObjPart::FileType(Rvalue::Literal(
115                "application/x-bad-elf".into()
116            ))),
117            r.obj
118        );
119        assert!(rem.is_empty());
120    }
121}