Move sepolicy parsing error message into Rust

This commit is contained in:
topjohnwu
2024-03-21 14:07:28 -07:00
parent 865fca71a5
commit dd9d43be96
9 changed files with 315 additions and 198 deletions

View File

@@ -1,6 +1,8 @@
use std::fmt::{Display, Formatter, Write};
use std::io::stderr;
use std::{iter::Peekable, pin::Pin, vec::IntoIter};
use base::{LoggedError, LoggedResult};
use base::{error, warn, FmtAdaptor};
use crate::ffi::Xperm;
use crate::sepolicy;
@@ -34,14 +36,29 @@ pub enum Token<'a> {
}
type Tokens<'a> = Peekable<IntoIter<Token<'a>>>;
type SimpleResult<T> = Result<T, ()>;
type ParseResult<'a> = Result<(), ParseError<'a>>;
enum ParseError<'a> {
General,
AvtabAv(Token<'a>),
AvtabXperms(Token<'a>),
AvtabType(Token<'a>),
TypeState(Token<'a>),
TypeAttr,
TypeTrans,
NewType,
NewAttr,
GenfsCon,
}
macro_rules! throw {
() => {
Err(LoggedError::default())?
Err(())?
};
}
fn parse_id<'a>(tokens: &mut Tokens<'a>) -> LoggedResult<&'a str> {
fn parse_id<'a>(tokens: &mut Tokens<'a>) -> SimpleResult<&'a str> {
match tokens.next() {
Some(Token::ID(name)) => Ok(name),
_ => throw!(),
@@ -52,7 +69,7 @@ fn parse_id<'a>(tokens: &mut Tokens<'a>) -> LoggedResult<&'a str> {
// names ::= names(mut v) CM ID(n) { v.push(n); v };
// term ::= ID(n) { vec![n] }
// term ::= LB names(n) RB { n };
fn parse_term<'a>(tokens: &mut Tokens<'a>) -> LoggedResult<Vec<&'a str>> {
fn parse_term<'a>(tokens: &mut Tokens<'a>) -> SimpleResult<Vec<&'a str>> {
match tokens.next() {
Some(Token::ID(name)) => Ok(vec![name]),
Some(Token::LB) => {
@@ -77,7 +94,7 @@ fn parse_term<'a>(tokens: &mut Tokens<'a>) -> LoggedResult<Vec<&'a str>> {
// terms ::= ST { vec![] }
// terms ::= term(n) { n }
fn parse_terms<'a>(tokens: &mut Tokens<'a>) -> LoggedResult<Vec<&'a str>> {
fn parse_terms<'a>(tokens: &mut Tokens<'a>) -> SimpleResult<Vec<&'a str>> {
match tokens.peek() {
Some(Token::ST) => {
tokens.next();
@@ -89,7 +106,7 @@ fn parse_terms<'a>(tokens: &mut Tokens<'a>) -> LoggedResult<Vec<&'a str>> {
// xperm ::= HX(low) { Xperm{low, high: low, reset: false} };
// xperm ::= HX(low) HP HX(high) { Xperm{low, high, reset: false} };
fn parse_xperm(tokens: &mut Tokens) -> LoggedResult<Xperm> {
fn parse_xperm(tokens: &mut Tokens) -> SimpleResult<Xperm> {
let low = match tokens.next() {
Some(Token::HX(low)) => low,
_ => throw!(),
@@ -118,7 +135,7 @@ fn parse_xperm(tokens: &mut Tokens) -> LoggedResult<Xperm> {
//
// xperm_list ::= xperm(p) { vec![p] }
// xperm_list ::= xperm_list(mut l) xperm(p) { l.push(p); l }
fn parse_xperms(tokens: &mut Tokens) -> LoggedResult<Vec<Xperm>> {
fn parse_xperms(tokens: &mut Tokens) -> SimpleResult<Vec<Xperm>> {
let mut xperms = Vec::new();
let reset = match tokens.peek() {
Some(Token::TL) => {
@@ -188,98 +205,147 @@ fn parse_xperms(tokens: &mut Tokens) -> LoggedResult<Vec<Xperm>> {
// statement ::= TC ID(s) ID(t) ID(c) ID(d) { extra.as_mut().type_change(s, t, c, d); };
// statement ::= TM ID(s) ID(t) ID(c) ID(d) { extra.as_mut().type_member(s, t, c, d);};
// statement ::= GF ID(s) ID(t) ID(c) { extra.as_mut().genfscon(s, t, c); };
fn exec_statement(sepolicy: Pin<&mut sepolicy>, tokens: &mut Tokens) -> LoggedResult<()> {
fn exec_statement<'a>(sepolicy: Pin<&mut sepolicy>, tokens: &'a mut Tokens) -> ParseResult<'a> {
let action = match tokens.next() {
Some(token) => token,
_ => throw!(),
_ => Err(ParseError::General)?,
};
match action {
Token::AL | Token::DN | Token::AA | Token::DA => {
let s = parse_terms(tokens)?;
let t = parse_terms(tokens)?;
let c = parse_terms(tokens)?;
let p = parse_terms(tokens)?;
match action {
Token::AL => sepolicy.allow(s, t, c, p),
Token::DN => sepolicy.deny(s, t, c, p),
Token::AA => sepolicy.auditallow(s, t, c, p),
Token::DA => sepolicy.dontaudit(s, t, c, p),
_ => unreachable!(),
let result: SimpleResult<()> = try {
let s = parse_terms(tokens)?;
let t = parse_terms(tokens)?;
let c = parse_terms(tokens)?;
let p = parse_terms(tokens)?;
match action {
Token::AL => sepolicy.allow(s, t, c, p),
Token::DN => sepolicy.deny(s, t, c, p),
Token::AA => sepolicy.auditallow(s, t, c, p),
Token::DA => sepolicy.dontaudit(s, t, c, p),
_ => unreachable!(),
}
};
if result.is_err() {
Err(ParseError::AvtabAv(action))?
}
}
Token::AX | Token::AY | Token::DX => {
let s = parse_terms(tokens)?;
let t = parse_terms(tokens)?;
let c = parse_terms(tokens)?;
let p = if matches!(tokens.next(), Some(Token::IO)) {
parse_xperms(tokens)?
} else {
throw!()
let result: SimpleResult<()> = try {
let s = parse_terms(tokens)?;
let t = parse_terms(tokens)?;
let c = parse_terms(tokens)?;
let p = if matches!(tokens.next(), Some(Token::IO)) {
parse_xperms(tokens)?
} else {
throw!()
};
match action {
Token::AX => sepolicy.allowxperm(s, t, c, p),
Token::AY => sepolicy.auditallowxperm(s, t, c, p),
Token::DX => sepolicy.dontauditxperm(s, t, c, p),
_ => unreachable!(),
}
};
match action {
Token::AX => sepolicy.allowxperm(s, t, c, p),
Token::AY => sepolicy.auditallowxperm(s, t, c, p),
Token::DX => sepolicy.dontauditxperm(s, t, c, p),
_ => unreachable!(),
if result.is_err() {
Err(ParseError::AvtabXperms(action))?
}
}
Token::PM | Token::EF => {
let t = parse_terms(tokens)?;
match action {
Token::PM => sepolicy.permissive(t),
Token::EF => sepolicy.enforce(t),
_ => unreachable!(),
let result: SimpleResult<()> = try {
let t = parse_terms(tokens)?;
match action {
Token::PM => sepolicy.permissive(t),
Token::EF => sepolicy.enforce(t),
_ => unreachable!(),
}
};
if result.is_err() {
Err(ParseError::TypeState(action))?
}
}
Token::TA => {
let t = parse_term(tokens)?;
let a = parse_term(tokens)?;
sepolicy.typeattribute(t, a)
let result: SimpleResult<()> = try {
let t = parse_term(tokens)?;
let a = parse_term(tokens)?;
sepolicy.typeattribute(t, a)
};
if result.is_err() {
Err(ParseError::TypeAttr)?
}
}
Token::TY => {
let t = parse_id(tokens)?;
let a = if tokens.peek().is_none() {
vec![]
} else {
parse_term(tokens)?
let result: SimpleResult<()> = try {
let t = parse_id(tokens)?;
let a = if tokens.peek().is_none() {
vec![]
} else {
parse_term(tokens)?
};
sepolicy.type_(t, a)
};
sepolicy.type_(t, a)
if result.is_err() {
Err(ParseError::NewType)?
}
}
Token::AT => {
let t = parse_id(tokens)?;
sepolicy.attribute(t)
let result: SimpleResult<()> = try {
let t = parse_id(tokens)?;
sepolicy.attribute(t)
};
if result.is_err() {
Err(ParseError::NewAttr)?
}
}
Token::TT | Token::TC | Token::TM => {
let s = parse_id(tokens)?;
let t = parse_id(tokens)?;
let c = parse_id(tokens)?;
let d = parse_id(tokens)?;
match action {
Token::TT => {
let o = if tokens.peek().is_none() {
""
} else {
parse_id(tokens)?
};
sepolicy.type_transition(s, t, c, d, o)
Token::TC | Token::TM => {
let result: SimpleResult<()> = try {
let s = parse_id(tokens)?;
let t = parse_id(tokens)?;
let c = parse_id(tokens)?;
let d = parse_id(tokens)?;
match action {
Token::TC => sepolicy.type_change(s, t, c, d),
Token::TM => sepolicy.type_member(s, t, c, d),
_ => unreachable!(),
}
Token::TC => sepolicy.type_change(s, t, c, d),
Token::TM => sepolicy.type_member(s, t, c, d),
_ => unreachable!(),
};
if result.is_err() {
Err(ParseError::AvtabType(action))?
}
}
Token::TT => {
let result: SimpleResult<()> = try {
let s = parse_id(tokens)?;
let t = parse_id(tokens)?;
let c = parse_id(tokens)?;
let d = parse_id(tokens)?;
let o = if tokens.peek().is_none() {
""
} else {
parse_id(tokens)?
};
sepolicy.type_transition(s, t, c, d, o);
};
if result.is_err() {
Err(ParseError::TypeTrans)?
}
}
Token::GF => {
let s = parse_id(tokens)?;
let t = parse_id(tokens)?;
let c = parse_id(tokens)?;
sepolicy.genfscon(s, t, c)
let result: SimpleResult<()> = try {
let s = parse_id(tokens)?;
let t = parse_id(tokens)?;
let c = parse_id(tokens)?;
sepolicy.genfscon(s, t, c)
};
if result.is_err() {
Err(ParseError::GenfsCon)?
}
}
_ => throw!(),
_ => Err(ParseError::General)?,
}
if tokens.peek().is_none() {
Ok(())
} else {
throw!()
Err(ParseError::General)
}
}
@@ -345,7 +411,156 @@ fn tokenize_statement(statement: &str) -> Vec<Token> {
tokens
}
pub fn parse_statement(sepolicy: Pin<&mut sepolicy>, statement: &str) -> LoggedResult<()> {
pub fn parse_statement(sepolicy: Pin<&mut sepolicy>, statement: &str) {
let mut tokens = tokenize_statement(statement).into_iter().peekable();
exec_statement(sepolicy, &mut tokens)
let result = exec_statement(sepolicy, &mut tokens);
if let Err(e) = result {
warn!("Syntax error in: \"{}\"", statement);
error!("Hint: {}", e);
}
}
// Token to string
impl Display for Token<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Token::AL => f.write_str("allow"),
Token::DN => f.write_str("deny"),
Token::AA => f.write_str("auditallow"),
Token::DA => f.write_str("dontaudit"),
Token::AX => f.write_str("allowxperm"),
Token::AY => f.write_str("auditallowxperm"),
Token::DX => f.write_str("dontauditxperm"),
Token::PM => f.write_str("permissive"),
Token::EF => f.write_str("enforce"),
Token::TA => f.write_str("typeattribute"),
Token::TY => f.write_str("type"),
Token::AT => f.write_str("attribute"),
Token::TT => f.write_str("type_transition"),
Token::TC => f.write_str("type_change"),
Token::TM => f.write_str("type_member"),
Token::GF => f.write_str("genfscon"),
Token::IO => f.write_str("ioctl"),
Token::LB => f.write_char('{'),
Token::RB => f.write_char('}'),
Token::CM => f.write_char(','),
Token::ST => f.write_char('*'),
Token::TL => f.write_char('~'),
Token::HP => f.write_char('-'),
Token::HX(n) => f.write_fmt(format_args!("{:06X}", n)),
Token::ID(s) => f.write_str(s),
}
}
}
impl Display for ParseError<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ParseError::General => format_statement_help(f),
ParseError::AvtabAv(action) => {
write!(f, "{action} *source_type *target_type *class *perm_set")
}
ParseError::AvtabXperms(action) => {
write!(
f,
"{action} *source_type *target_type *class operation xperm_set"
)
}
ParseError::AvtabType(action) => {
write!(f, "{action} source_type target_type class default_type")
}
ParseError::TypeState(action) => {
write!(f, "{action} *type")
}
ParseError::TypeAttr => f.write_str("typeattribute ^type ^attribute"),
ParseError::TypeTrans => f.write_str(
"type_transition source_type target_type class default_type (object_name)",
),
ParseError::NewType => f.write_str("type type_name ^(attribute)"),
ParseError::NewAttr => f.write_str("attribute attribute_name"),
ParseError::GenfsCon => f.write_str("genfscon fs_name partial_path fs_context"),
}
}
}
fn format_statement_help(f: &mut dyn Write) -> std::fmt::Result {
write!(
f,
r#"** Policy statements:
One policy statement should be treated as a single parameter;
this means each policy statement should be enclosed in quotes.
Multiple policy statements can be provided in a single command.
Statements has a format of "<rule_name> [args...]".
Arguments labeled with (^) can accept one or more entries.
Multiple entries consist of a space separated list enclosed in braces ({{}}).
Arguments labeled with (*) are the same as (^), but additionally
support the match-all operator (*).
Example: "allow {{ s1 s2 }} {{ t1 t2 }} class *"
Will be expanded to:
allow s1 t1 class {{ all-permissions-of-class }}
allow s1 t2 class {{ all-permissions-of-class }}
allow s2 t1 class {{ all-permissions-of-class }}
allow s2 t2 class {{ all-permissions-of-class }}
** Extended permissions:
The only supported operation for extended permissions right now is 'ioctl'.
xperm_set is one or multiple hexadecimal numeric values ranging from 0x0000 to 0xFFFF.
Multiple values consist of a space separated list enclosed in braces ({{}}).
Use the complement operator (~) to specify all permissions except those explicitly listed.
Use the range operator (-) to specify all permissions within the low high range.
Use the match all operator (*) to match all ioctl commands (0x0000-0xFFFF).
The special value 0 is used to clear all rules.
Some examples:
allowxperm source target class ioctl 0x8910
allowxperm source target class ioctl {{ 0x8910-0x8926 0x892A-0x8935 }}
allowxperm source target class ioctl ~{{ 0x8910 0x892A }}
allowxperm source target class ioctl *
** Supported policy statements:
{}
{}
{}
{}
{}
{}
{}
{}
{}
{}
{}
{}
{}
{}
{}
{}
"#,
ParseError::AvtabAv(Token::AL),
ParseError::AvtabAv(Token::DN),
ParseError::AvtabAv(Token::AA),
ParseError::AvtabAv(Token::DA),
ParseError::AvtabXperms(Token::AX),
ParseError::AvtabXperms(Token::AY),
ParseError::AvtabXperms(Token::DX),
ParseError::TypeState(Token::PM),
ParseError::TypeState(Token::EF),
ParseError::TypeAttr,
ParseError::NewType,
ParseError::NewAttr,
ParseError::TypeTrans,
ParseError::AvtabType(Token::TC),
ParseError::AvtabType(Token::TM),
ParseError::GenfsCon
)
}
pub fn print_statement_help() {
format_statement_help(&mut FmtAdaptor(&mut stderr())).ok();
eprintln!();
}