mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-22 16:07:39 +00:00
Move sepolicy parsing error message into Rust
This commit is contained in:
parent
865fca71a5
commit
dd9d43be96
@ -168,7 +168,6 @@ LOCAL_SRC_FILES := \
|
|||||||
sepolicy/api.cpp \
|
sepolicy/api.cpp \
|
||||||
sepolicy/sepolicy.cpp \
|
sepolicy/sepolicy.cpp \
|
||||||
sepolicy/policydb.cpp \
|
sepolicy/policydb.cpp \
|
||||||
sepolicy/statement.cpp \
|
|
||||||
sepolicy/policy-rs.cpp
|
sepolicy/policy-rs.cpp
|
||||||
include $(BUILD_STATIC_LIBRARY)
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
use std::fmt::Arguments;
|
||||||
|
use std::io::Write;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::{io, slice, str};
|
use std::{fmt, io, slice, str};
|
||||||
|
|
||||||
use argh::EarlyExit;
|
use argh::EarlyExit;
|
||||||
use libc::c_char;
|
use libc::c_char;
|
||||||
@ -117,3 +119,16 @@ impl<T> EarlyExitExt<T> for Result<T, EarlyExit> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct FmtAdaptor<'a, T>(pub &'a mut T)
|
||||||
|
where
|
||||||
|
T: Write;
|
||||||
|
|
||||||
|
impl<T: Write> fmt::Write for FmtAdaptor<'_, T> {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
self.0.write_all(s.as_bytes()).map_err(|_| fmt::Error)
|
||||||
|
}
|
||||||
|
fn write_fmt(&mut self, args: Arguments<'_>) -> fmt::Result {
|
||||||
|
self.0.write_fmt(args).map_err(|_| fmt::Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -143,7 +143,7 @@ void sepolicy::type(Str type, StrVec attrs) {
|
|||||||
|
|
||||||
void sepolicy::attribute(Str name) {
|
void sepolicy::attribute(Str name) {
|
||||||
expand(name, [this](auto ...args) {
|
expand(name, [this](auto ...args) {
|
||||||
print_rule("name", args...);
|
print_rule("attribute", args...);
|
||||||
impl->add_type(args..., TYPE_ATTRIB);
|
impl->add_type(args..., TYPE_ATTRIB);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#![feature(format_args_nl)]
|
#![feature(format_args_nl)]
|
||||||
|
#![feature(try_blocks)]
|
||||||
|
|
||||||
use io::Cursor;
|
use io::Cursor;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
@ -8,7 +9,8 @@ use std::pin::Pin;
|
|||||||
|
|
||||||
pub use base;
|
pub use base;
|
||||||
use base::libc::{O_CLOEXEC, O_RDONLY};
|
use base::libc::{O_CLOEXEC, O_RDONLY};
|
||||||
use base::{error, BufReadExt, FsPath, LoggedResult, Utf8CStr};
|
use base::{BufReadExt, FsPath, LoggedResult, Utf8CStr};
|
||||||
|
use statement::{parse_statement, print_statement_help};
|
||||||
|
|
||||||
use crate::ffi::sepolicy;
|
use crate::ffi::sepolicy;
|
||||||
|
|
||||||
@ -88,6 +90,7 @@ mod ffi {
|
|||||||
fn parse_statement(sepol: Pin<&mut sepolicy>, statement: Utf8CStrRef);
|
fn parse_statement(sepol: Pin<&mut sepolicy>, statement: Utf8CStrRef);
|
||||||
fn magisk_rules(sepol: Pin<&mut sepolicy>);
|
fn magisk_rules(sepol: Pin<&mut sepolicy>);
|
||||||
fn xperm_to_string(perm: &Xperm) -> String;
|
fn xperm_to_string(perm: &Xperm) -> String;
|
||||||
|
fn print_statement_help();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +98,6 @@ trait SepolicyExt {
|
|||||||
fn load_rules(self: Pin<&mut Self>, rules: &[u8]);
|
fn load_rules(self: Pin<&mut Self>, rules: &[u8]);
|
||||||
fn load_rule_file(self: Pin<&mut Self>, filename: &Utf8CStr);
|
fn load_rule_file(self: Pin<&mut Self>, filename: &Utf8CStr);
|
||||||
fn load_rules_from_reader<T: BufRead>(self: Pin<&mut Self>, reader: &mut T);
|
fn load_rules_from_reader<T: BufRead>(self: Pin<&mut Self>, reader: &mut T);
|
||||||
fn parse_statement(self: Pin<&mut Self>, statement: &str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SepolicyExt for sepolicy {
|
impl SepolicyExt for sepolicy {
|
||||||
@ -105,13 +107,12 @@ impl SepolicyExt for sepolicy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn load_rule_file(self: Pin<&mut sepolicy>, filename: &Utf8CStr) {
|
fn load_rule_file(self: Pin<&mut sepolicy>, filename: &Utf8CStr) {
|
||||||
fn inner(sepol: Pin<&mut sepolicy>, filename: &Utf8CStr) -> LoggedResult<()> {
|
let result: LoggedResult<()> = try {
|
||||||
let file = FsPath::from(filename).open(O_RDONLY | O_CLOEXEC)?;
|
let file = FsPath::from(filename).open(O_RDONLY | O_CLOEXEC)?;
|
||||||
let mut reader = BufReader::new(file);
|
let mut reader = BufReader::new(file);
|
||||||
sepol.load_rules_from_reader(&mut reader);
|
self.load_rules_from_reader(&mut reader);
|
||||||
Ok(())
|
};
|
||||||
}
|
result.ok();
|
||||||
inner(self, filename).ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_rules_from_reader<T: BufRead>(mut self: Pin<&mut sepolicy>, reader: &mut T) {
|
fn load_rules_from_reader<T: BufRead>(mut self: Pin<&mut sepolicy>, reader: &mut T) {
|
||||||
@ -120,16 +121,10 @@ impl SepolicyExt for sepolicy {
|
|||||||
if line.is_empty() {
|
if line.is_empty() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
self.as_mut().parse_statement(line);
|
parse_statement(self.as_mut(), line);
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_statement(self: Pin<&mut Self>, statement: &str) {
|
|
||||||
if statement::parse_statement(self, statement).is_err() {
|
|
||||||
error!("sepolicy rule syntax error: {statement}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_rule_file(sepol: Pin<&mut sepolicy>, filename: &Utf8CStr) {
|
fn load_rule_file(sepol: Pin<&mut sepolicy>, filename: &Utf8CStr) {
|
||||||
@ -140,10 +135,6 @@ fn load_rules(sepol: Pin<&mut sepolicy>, rules: &[u8]) {
|
|||||||
sepol.load_rules(rules);
|
sepol.load_rules(rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_statement(sepol: Pin<&mut sepolicy>, statement: &Utf8CStr) {
|
|
||||||
sepol.parse_statement(statement.as_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
trait SepolicyMagisk {
|
trait SepolicyMagisk {
|
||||||
fn magisk_rules(self: Pin<&mut Self>);
|
fn magisk_rules(self: Pin<&mut Self>);
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,8 @@ int main(int argc, char *argv[]) {
|
|||||||
rule_files.emplace_back(argv[i + 1]);
|
rule_files.emplace_back(argv[i + 1]);
|
||||||
++i;
|
++i;
|
||||||
} else if (option == "help"sv) {
|
} else if (option == "help"sv) {
|
||||||
statement_help();
|
rust::print_statement_help();
|
||||||
|
exit(0);
|
||||||
} else {
|
} else {
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
}
|
}
|
||||||
|
@ -40,5 +40,3 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define impl reinterpret_cast<sepol_impl *>(this)
|
#define impl reinterpret_cast<sepol_impl *>(this)
|
||||||
|
|
||||||
void statement_help();
|
|
||||||
|
@ -715,7 +715,7 @@ void sepol_impl::print_type(FILE *fp, type_datum_t *type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ebitmap_get_bit(&db->permissive_map, type->s.value)) {
|
if (ebitmap_get_bit(&db->permissive_map, type->s.value)) {
|
||||||
fprintf(stdout, "permissive %s\n", name);
|
fprintf(fp, "permissive %s\n", name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,102 +0,0 @@
|
|||||||
#include <cstring>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <base.hpp>
|
|
||||||
|
|
||||||
#include "policy.hpp"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
static const char *type_msg_1 =
|
|
||||||
R"EOF("allow *source_type *target_type *class *perm_set"
|
|
||||||
"deny *source_type *target_type *class *perm_set"
|
|
||||||
"auditallow *source_type *target_type *class *perm_set"
|
|
||||||
"dontaudit *source_type *target_type *class *perm_set"
|
|
||||||
)EOF";
|
|
||||||
|
|
||||||
static const char *type_msg_2 =
|
|
||||||
R"EOF("allowxperm *source_type *target_type *class operation xperm_set"
|
|
||||||
"auditallowxperm *source_type *target_type *class operation xperm_set"
|
|
||||||
"dontauditxperm *source_type *target_type *class operation xperm_set"
|
|
||||||
- The only supported operation 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.
|
|
||||||
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 *
|
|
||||||
)EOF";
|
|
||||||
|
|
||||||
static const char *type_msg_3 =
|
|
||||||
R"EOF("permissive *type"
|
|
||||||
"enforce *type"
|
|
||||||
)EOF";
|
|
||||||
|
|
||||||
static const char *type_msg_4 =
|
|
||||||
R"EOF("typeattribute ^type ^attribute"
|
|
||||||
)EOF";
|
|
||||||
|
|
||||||
static const char *type_msg_5 =
|
|
||||||
R"EOF("type type_name ^(attribute)"
|
|
||||||
- Argument 'attribute' is optional, default to 'domain'
|
|
||||||
)EOF";
|
|
||||||
|
|
||||||
static const char *type_msg_6 =
|
|
||||||
R"EOF("attribute attribute_name"
|
|
||||||
)EOF";
|
|
||||||
|
|
||||||
static const char *type_msg_7 =
|
|
||||||
R"EOF("type_transition source_type target_type class default_type (object_name)"
|
|
||||||
- Argument 'object_name' is optional
|
|
||||||
)EOF";
|
|
||||||
|
|
||||||
static const char *type_msg_8 =
|
|
||||||
R"EOF("type_change source_type target_type class default_type"
|
|
||||||
"type_member source_type target_type class default_type"
|
|
||||||
)EOF";
|
|
||||||
|
|
||||||
static const char *type_msg_9 =
|
|
||||||
R"EOF("genfscon fs_name partial_path fs_context"
|
|
||||||
)EOF";
|
|
||||||
|
|
||||||
void statement_help() {
|
|
||||||
fprintf(stderr,
|
|
||||||
R"EOF(One policy statement should be treated as one 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 }
|
|
||||||
|
|
||||||
Supported policy statements:
|
|
||||||
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
)EOF", type_msg_1, type_msg_2, type_msg_3, type_msg_4,
|
|
||||||
type_msg_5, type_msg_6, type_msg_7, type_msg_8, type_msg_9);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
@ -1,6 +1,8 @@
|
|||||||
|
use std::fmt::{Display, Formatter, Write};
|
||||||
|
use std::io::stderr;
|
||||||
use std::{iter::Peekable, pin::Pin, vec::IntoIter};
|
use std::{iter::Peekable, pin::Pin, vec::IntoIter};
|
||||||
|
|
||||||
use base::{LoggedError, LoggedResult};
|
use base::{error, warn, FmtAdaptor};
|
||||||
|
|
||||||
use crate::ffi::Xperm;
|
use crate::ffi::Xperm;
|
||||||
use crate::sepolicy;
|
use crate::sepolicy;
|
||||||
@ -34,14 +36,29 @@ pub enum Token<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Tokens<'a> = Peekable<IntoIter<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 {
|
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() {
|
match tokens.next() {
|
||||||
Some(Token::ID(name)) => Ok(name),
|
Some(Token::ID(name)) => Ok(name),
|
||||||
_ => throw!(),
|
_ => 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 };
|
// names ::= names(mut v) CM ID(n) { v.push(n); v };
|
||||||
// term ::= ID(n) { vec![n] }
|
// term ::= ID(n) { vec![n] }
|
||||||
// term ::= LB names(n) RB { 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() {
|
match tokens.next() {
|
||||||
Some(Token::ID(name)) => Ok(vec![name]),
|
Some(Token::ID(name)) => Ok(vec![name]),
|
||||||
Some(Token::LB) => {
|
Some(Token::LB) => {
|
||||||
@ -77,7 +94,7 @@ fn parse_term<'a>(tokens: &mut Tokens<'a>) -> LoggedResult<Vec<&'a str>> {
|
|||||||
|
|
||||||
// terms ::= ST { vec![] }
|
// terms ::= ST { vec![] }
|
||||||
// terms ::= term(n) { n }
|
// 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() {
|
match tokens.peek() {
|
||||||
Some(Token::ST) => {
|
Some(Token::ST) => {
|
||||||
tokens.next();
|
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) { Xperm{low, high: low, reset: false} };
|
||||||
// xperm ::= HX(low) HP HX(high) { Xperm{low, high, 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() {
|
let low = match tokens.next() {
|
||||||
Some(Token::HX(low)) => low,
|
Some(Token::HX(low)) => low,
|
||||||
_ => throw!(),
|
_ => throw!(),
|
||||||
@ -118,7 +135,7 @@ fn parse_xperm(tokens: &mut Tokens) -> LoggedResult<Xperm> {
|
|||||||
//
|
//
|
||||||
// xperm_list ::= xperm(p) { vec![p] }
|
// xperm_list ::= xperm(p) { vec![p] }
|
||||||
// xperm_list ::= xperm_list(mut l) xperm(p) { l.push(p); l }
|
// 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 mut xperms = Vec::new();
|
||||||
let reset = match tokens.peek() {
|
let reset = match tokens.peek() {
|
||||||
Some(Token::TL) => {
|
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 ::= 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 ::= 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); };
|
// 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() {
|
let action = match tokens.next() {
|
||||||
Some(token) => token,
|
Some(token) => token,
|
||||||
_ => throw!(),
|
_ => Err(ParseError::General)?,
|
||||||
};
|
};
|
||||||
match action {
|
match action {
|
||||||
Token::AL | Token::DN | Token::AA | Token::DA => {
|
Token::AL | Token::DN | Token::AA | Token::DA => {
|
||||||
let s = parse_terms(tokens)?;
|
let result: SimpleResult<()> = try {
|
||||||
let t = parse_terms(tokens)?;
|
let s = parse_terms(tokens)?;
|
||||||
let c = parse_terms(tokens)?;
|
let t = parse_terms(tokens)?;
|
||||||
let p = parse_terms(tokens)?;
|
let c = parse_terms(tokens)?;
|
||||||
match action {
|
let p = parse_terms(tokens)?;
|
||||||
Token::AL => sepolicy.allow(s, t, c, p),
|
match action {
|
||||||
Token::DN => sepolicy.deny(s, t, c, p),
|
Token::AL => sepolicy.allow(s, t, c, p),
|
||||||
Token::AA => sepolicy.auditallow(s, t, c, p),
|
Token::DN => sepolicy.deny(s, t, c, p),
|
||||||
Token::DA => sepolicy.dontaudit(s, t, c, p),
|
Token::AA => sepolicy.auditallow(s, t, c, p),
|
||||||
_ => unreachable!(),
|
Token::DA => sepolicy.dontaudit(s, t, c, p),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if result.is_err() {
|
||||||
|
Err(ParseError::AvtabAv(action))?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Token::AX | Token::AY | Token::DX => {
|
Token::AX | Token::AY | Token::DX => {
|
||||||
let s = parse_terms(tokens)?;
|
let result: SimpleResult<()> = try {
|
||||||
let t = parse_terms(tokens)?;
|
let s = parse_terms(tokens)?;
|
||||||
let c = parse_terms(tokens)?;
|
let t = parse_terms(tokens)?;
|
||||||
let p = if matches!(tokens.next(), Some(Token::IO)) {
|
let c = parse_terms(tokens)?;
|
||||||
parse_xperms(tokens)?
|
let p = if matches!(tokens.next(), Some(Token::IO)) {
|
||||||
} else {
|
parse_xperms(tokens)?
|
||||||
throw!()
|
} 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 {
|
if result.is_err() {
|
||||||
Token::AX => sepolicy.allowxperm(s, t, c, p),
|
Err(ParseError::AvtabXperms(action))?
|
||||||
Token::AY => sepolicy.auditallowxperm(s, t, c, p),
|
|
||||||
Token::DX => sepolicy.dontauditxperm(s, t, c, p),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Token::PM | Token::EF => {
|
Token::PM | Token::EF => {
|
||||||
let t = parse_terms(tokens)?;
|
let result: SimpleResult<()> = try {
|
||||||
match action {
|
let t = parse_terms(tokens)?;
|
||||||
Token::PM => sepolicy.permissive(t),
|
match action {
|
||||||
Token::EF => sepolicy.enforce(t),
|
Token::PM => sepolicy.permissive(t),
|
||||||
_ => unreachable!(),
|
Token::EF => sepolicy.enforce(t),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if result.is_err() {
|
||||||
|
Err(ParseError::TypeState(action))?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Token::TA => {
|
Token::TA => {
|
||||||
let t = parse_term(tokens)?;
|
let result: SimpleResult<()> = try {
|
||||||
let a = parse_term(tokens)?;
|
let t = parse_term(tokens)?;
|
||||||
sepolicy.typeattribute(t, a)
|
let a = parse_term(tokens)?;
|
||||||
|
sepolicy.typeattribute(t, a)
|
||||||
|
};
|
||||||
|
if result.is_err() {
|
||||||
|
Err(ParseError::TypeAttr)?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Token::TY => {
|
Token::TY => {
|
||||||
let t = parse_id(tokens)?;
|
let result: SimpleResult<()> = try {
|
||||||
let a = if tokens.peek().is_none() {
|
let t = parse_id(tokens)?;
|
||||||
vec![]
|
let a = if tokens.peek().is_none() {
|
||||||
} else {
|
vec![]
|
||||||
parse_term(tokens)?
|
} else {
|
||||||
|
parse_term(tokens)?
|
||||||
|
};
|
||||||
|
sepolicy.type_(t, a)
|
||||||
};
|
};
|
||||||
sepolicy.type_(t, a)
|
if result.is_err() {
|
||||||
|
Err(ParseError::NewType)?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Token::AT => {
|
Token::AT => {
|
||||||
let t = parse_id(tokens)?;
|
let result: SimpleResult<()> = try {
|
||||||
sepolicy.attribute(t)
|
let t = parse_id(tokens)?;
|
||||||
|
sepolicy.attribute(t)
|
||||||
|
};
|
||||||
|
if result.is_err() {
|
||||||
|
Err(ParseError::NewAttr)?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Token::TT | Token::TC | Token::TM => {
|
Token::TC | Token::TM => {
|
||||||
let s = parse_id(tokens)?;
|
let result: SimpleResult<()> = try {
|
||||||
let t = parse_id(tokens)?;
|
let s = parse_id(tokens)?;
|
||||||
let c = parse_id(tokens)?;
|
let t = parse_id(tokens)?;
|
||||||
let d = parse_id(tokens)?;
|
let c = parse_id(tokens)?;
|
||||||
match action {
|
let d = parse_id(tokens)?;
|
||||||
Token::TT => {
|
match action {
|
||||||
let o = if tokens.peek().is_none() {
|
Token::TC => sepolicy.type_change(s, t, c, d),
|
||||||
""
|
Token::TM => sepolicy.type_member(s, t, c, d),
|
||||||
} else {
|
_ => unreachable!(),
|
||||||
parse_id(tokens)?
|
|
||||||
};
|
|
||||||
sepolicy.type_transition(s, t, c, d, o)
|
|
||||||
}
|
}
|
||||||
Token::TC => sepolicy.type_change(s, t, c, d),
|
};
|
||||||
Token::TM => sepolicy.type_member(s, t, c, d),
|
if result.is_err() {
|
||||||
_ => unreachable!(),
|
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 => {
|
Token::GF => {
|
||||||
let s = parse_id(tokens)?;
|
let result: SimpleResult<()> = try {
|
||||||
let t = parse_id(tokens)?;
|
let s = parse_id(tokens)?;
|
||||||
let c = parse_id(tokens)?;
|
let t = parse_id(tokens)?;
|
||||||
sepolicy.genfscon(s, t, c)
|
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() {
|
if tokens.peek().is_none() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
throw!()
|
Err(ParseError::General)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,7 +411,156 @@ fn tokenize_statement(statement: &str) -> Vec<Token> {
|
|||||||
tokens
|
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();
|
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!();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user