mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-22 16:07:39 +00:00
Rewrite sepolicy statement parsing in Rust
This commit is contained in:
parent
ecc74d45d1
commit
4d2921e742
@ -167,7 +167,6 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
|
||||
LOCAL_SRC_FILES := \
|
||||
sepolicy/api.cpp \
|
||||
sepolicy/sepolicy.cpp \
|
||||
sepolicy/rules.cpp \
|
||||
sepolicy/policydb.cpp \
|
||||
sepolicy/statement.cpp \
|
||||
sepolicy/policy-rs.cpp
|
||||
|
@ -1,104 +1,27 @@
|
||||
#include <base.hpp>
|
||||
|
||||
#include "flags.h"
|
||||
#include "policy.hpp"
|
||||
|
||||
#if 0
|
||||
// Print out all rules going through public API for debugging
|
||||
template <typename ...Args>
|
||||
static void dprint(const char *action, Args ...args) {
|
||||
std::string s(action);
|
||||
for (int i = 0; i < sizeof...(args); ++i) s += " %s";
|
||||
s += "\n";
|
||||
LOGD(s.data(), as_str(args)...);
|
||||
}
|
||||
#else
|
||||
#define dprint(...)
|
||||
#endif
|
||||
|
||||
bool sepolicy::allow(const char *s, const char *t, const char *c, const char *p) {
|
||||
dprint(__FUNCTION__, s, t, c, p);
|
||||
return impl->add_rule(s, t, c, p, AVTAB_ALLOWED, false);
|
||||
}
|
||||
|
||||
bool sepolicy::deny(const char *s, const char *t, const char *c, const char *p) {
|
||||
dprint(__FUNCTION__, s, t, c, p);
|
||||
return impl->add_rule(s, t, c, p, AVTAB_ALLOWED, true);
|
||||
}
|
||||
|
||||
bool sepolicy::auditallow(const char *s, const char *t, const char *c, const char *p) {
|
||||
dprint(__FUNCTION__, s, t, c, p);
|
||||
return impl->add_rule(s, t, c, p, AVTAB_AUDITALLOW, false);
|
||||
}
|
||||
|
||||
bool sepolicy::dontaudit(const char *s, const char *t, const char *c, const char *p) {
|
||||
dprint(__FUNCTION__, s, t, c, p);
|
||||
return impl->add_rule(s, t, c, p, AVTAB_AUDITDENY, true);
|
||||
}
|
||||
|
||||
bool sepolicy::allowxperm(const char *s, const char *t, const char *c, const argument &xperm) {
|
||||
dprint(__FUNCTION__, s, t, c, "ioctl", xperm);
|
||||
return impl->add_xperm_rule(s, t, c, xperm, AVTAB_XPERMS_ALLOWED);
|
||||
}
|
||||
|
||||
bool sepolicy::auditallowxperm(const char *s, const char *t, const char *c, const argument &xperm) {
|
||||
dprint(__FUNCTION__, s, t, c, "ioctl", xperm);
|
||||
return impl->add_xperm_rule(s, t, c, xperm, AVTAB_XPERMS_AUDITALLOW);
|
||||
}
|
||||
|
||||
bool sepolicy::dontauditxperm(const char *s, const char *t, const char *c, const argument &xperm) {
|
||||
dprint(__FUNCTION__, s, t, c, "ioctl", xperm);
|
||||
return impl->add_xperm_rule(s, t, c, xperm, AVTAB_XPERMS_DONTAUDIT);
|
||||
}
|
||||
|
||||
bool sepolicy::type_change(const char *s, const char *t, const char *c, const char *d) {
|
||||
dprint(__FUNCTION__, s, t, c, d);
|
||||
return impl->add_type_rule(s, t, c, d, AVTAB_CHANGE);
|
||||
}
|
||||
|
||||
bool sepolicy::type_member(const char *s, const char *t, const char *c, const char *d) {
|
||||
dprint(__FUNCTION__, s, t, c, d);
|
||||
return impl->add_type_rule(s, t, c, d, AVTAB_MEMBER);
|
||||
}
|
||||
|
||||
bool sepolicy::type_transition(const char *s, const char *t, const char *c, const char *d, const char *o) {
|
||||
if (o) {
|
||||
dprint(__FUNCTION__, s, t, c, d, o);
|
||||
return impl->add_filename_trans(s, t, c, d, o);
|
||||
#if MAGISK_DEBUG
|
||||
template<typename Arg>
|
||||
auto as_str(Arg arg) {
|
||||
if constexpr (std::is_same_v<Arg, const char *> || std::is_same_v<Arg, char *>) {
|
||||
return arg == nullptr ? std::string("*") : std::string(arg);
|
||||
} else {
|
||||
dprint(__FUNCTION__, s, t, c, d);
|
||||
return impl->add_type_rule(s, t, c, d, AVTAB_TRANSITION);
|
||||
return std::to_string(arg);
|
||||
}
|
||||
}
|
||||
|
||||
bool sepolicy::permissive(const char *s) {
|
||||
dprint(__FUNCTION__, s);
|
||||
return impl->set_type_state(s, true);
|
||||
}
|
||||
|
||||
bool sepolicy::enforce(const char *s) {
|
||||
dprint(__FUNCTION__, s);
|
||||
return impl->set_type_state(s, false);
|
||||
}
|
||||
|
||||
bool sepolicy::type(const char *name, const char *attr) {
|
||||
dprint(__FUNCTION__, name, attr);
|
||||
return impl->add_type(name, TYPE_TYPE) && impl->add_typeattribute(name, attr);
|
||||
}
|
||||
|
||||
bool sepolicy::attribute(const char *name) {
|
||||
dprint(__FUNCTION__, name);
|
||||
return impl->add_type(name, TYPE_ATTRIB);
|
||||
}
|
||||
|
||||
bool sepolicy::typeattribute(const char *type, const char *attr) {
|
||||
dprint(__FUNCTION__, type, attr);
|
||||
return impl->add_typeattribute(type, attr);
|
||||
}
|
||||
|
||||
bool sepolicy::genfscon(const char *fs_name, const char *path, const char *ctx) {
|
||||
dprint(__FUNCTION__, fs_name, path, ctx);
|
||||
return impl->add_genfscon(fs_name, path, ctx);
|
||||
// Print out all rules going through public API for debugging
|
||||
template<typename ...Args>
|
||||
static void dprint(const char *action, Args ...args) {
|
||||
std::string s;
|
||||
s = (... + (" " + as_str(args)));
|
||||
LOGD("%s%s", action, s.data());
|
||||
}
|
||||
#else
|
||||
#define dprint(...) ((void) 0)
|
||||
#endif
|
||||
|
||||
bool sepolicy::exists(const char *type) {
|
||||
return hashtab_search(impl->db->p_types.table, type) != nullptr;
|
||||
@ -108,6 +31,177 @@ void sepolicy::load_rule_file(const char *file) {
|
||||
rust::load_rule_file(*this, file);
|
||||
}
|
||||
|
||||
void sepolicy::parse_statement(const char *data) {
|
||||
rust::parse_statement(*this, data);
|
||||
}
|
||||
|
||||
void sepolicy::magisk_rules() {
|
||||
rust::magisk_rules(*this);
|
||||
}
|
||||
|
||||
void sepolicy::load_rules(const std::string &rules) {
|
||||
rust::load_rules(*this, byte_view(rules, false));
|
||||
}
|
||||
|
||||
template<typename F, typename ...T>
|
||||
requires(std::invocable<F, T...>)
|
||||
inline void expand(F &&f, T &&...args) {
|
||||
f(std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
template<typename ...T>
|
||||
inline void expand(const rust::Vec<rust::Str> &vec, T &&...args) {
|
||||
for (auto i = vec.begin(); i != vec.end() || vec.empty(); ++i) {
|
||||
expand(std::forward<T>(args)..., vec.empty() ? nullptr : std::string(*i).data());
|
||||
if (vec.empty()) break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ...T>
|
||||
inline void expand(const rust::Vec<Xperm> &vec, T &&...args) {
|
||||
for (auto &p : vec) {
|
||||
expand(std::forward<T>(args)..., p.low, p.high, p.reset);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ...T>
|
||||
inline void expand(const rust::Str &s, T &&...args) {
|
||||
expand(std::forward<T>(args)..., std::string(s).data());
|
||||
}
|
||||
|
||||
void
|
||||
sepolicy::allow(rust::Vec<rust::Str> src, rust::Vec<rust::Str> tgt, rust::Vec<rust::Str> cls,
|
||||
rust::Vec<rust::Str> perm) {
|
||||
expand(src, tgt, cls, perm, [this](auto ...args) {
|
||||
dprint("allow", args...);
|
||||
impl->add_rule(args..., AVTAB_ALLOWED, false);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
sepolicy::deny(rust::Vec<rust::Str> src, rust::Vec<rust::Str> tgt, rust::Vec<rust::Str> cls,
|
||||
rust::Vec<rust::Str> perm) {
|
||||
expand(src, tgt, cls, perm, [this](auto ...args) {
|
||||
dprint("deny", args...);
|
||||
impl->add_rule(args..., AVTAB_ALLOWED, true);
|
||||
});
|
||||
}
|
||||
|
||||
void sepolicy::auditallow(rust::Vec<rust::Str> src, rust::Vec<rust::Str> tgt,
|
||||
rust::Vec<rust::Str> cls,
|
||||
rust::Vec<rust::Str> perm) {
|
||||
expand(src, tgt, cls, perm, [this](auto ...args) {
|
||||
dprint("auditallow", args...);
|
||||
impl->add_rule(args..., AVTAB_AUDITALLOW, false);
|
||||
});
|
||||
}
|
||||
|
||||
void sepolicy::dontaudit(rust::Vec<rust::Str> src, rust::Vec<rust::Str> tgt,
|
||||
rust::Vec<rust::Str> cls,
|
||||
rust::Vec<rust::Str> perm) {
|
||||
expand(src, tgt, cls, perm, [this](auto ...args) {
|
||||
dprint("dontaudit", args...);
|
||||
impl->add_rule(args..., AVTAB_AUDITDENY, true);
|
||||
});
|
||||
}
|
||||
|
||||
void sepolicy::permissive(rust::Vec<rust::Str> types) {
|
||||
expand(types, [this](auto ...args) {
|
||||
dprint("permissive", args...);
|
||||
impl->set_type_state(args..., true);
|
||||
});
|
||||
}
|
||||
|
||||
void sepolicy::enforce(rust::Vec<rust::Str> types) {
|
||||
expand(types, [this](auto ...args) {
|
||||
dprint("enforce", args...);
|
||||
impl->set_type_state(args..., false);
|
||||
});
|
||||
}
|
||||
|
||||
void sepolicy::typeattribute(rust::Vec<rust::Str> types, rust::Vec<rust::Str> attrs) {
|
||||
expand(types, attrs, [this](auto ...args) {
|
||||
dprint("typeattribute", args...);
|
||||
impl->add_typeattribute(args...);
|
||||
});
|
||||
}
|
||||
|
||||
void sepolicy::type(rust::Str type, rust::Vec<rust::Str> attrs) {
|
||||
expand(type, attrs, [this](auto name, auto attr) {
|
||||
dprint("type", name, attr);
|
||||
impl->add_type(name, TYPE_TYPE) && impl->add_typeattribute(name, attr);
|
||||
});
|
||||
}
|
||||
|
||||
void sepolicy::attribute(rust::Str name) {
|
||||
expand(name, [this](auto ...args) {
|
||||
dprint("name", args...);
|
||||
impl->add_type(args..., TYPE_ATTRIB);
|
||||
});
|
||||
}
|
||||
|
||||
void sepolicy::type_transition(rust::Str src, rust::Str tgt, rust::Str cls, rust::Str def,
|
||||
rust::Vec<rust::Str> obj) {
|
||||
auto obj_str = obj.empty() ? std::string() : std::string(obj[0]);
|
||||
auto o = obj.empty() ? nullptr : obj_str.data();
|
||||
expand(src, tgt, cls, def, [this, &o](auto ...args) {
|
||||
dprint("type_transition", args..., o);
|
||||
if (o) {
|
||||
impl->add_filename_trans(args..., o);
|
||||
} else {
|
||||
impl->add_type_rule(args..., AVTAB_TRANSITION);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void sepolicy::type_change(rust::Str src, rust::Str tgt, rust::Str cls, rust::Str def) {
|
||||
expand(src, tgt, cls, def, [this](auto ...args) {
|
||||
dprint("type_change", args...);
|
||||
impl->add_type_rule(args..., AVTAB_CHANGE);
|
||||
});
|
||||
}
|
||||
|
||||
void sepolicy::type_member(rust::Str src, rust::Str tgt, rust::Str cls, rust::Str def) {
|
||||
expand(src, tgt, cls, def, [this](auto ...args) {
|
||||
dprint("type_member", args...);
|
||||
impl->add_type_rule(args..., AVTAB_MEMBER);
|
||||
});
|
||||
}
|
||||
|
||||
void sepolicy::genfscon(rust::Str fs_name, rust::Str path, rust::Str ctx) {
|
||||
expand(fs_name, path, ctx, [this](auto ...args) {
|
||||
dprint("genfscon", args...);
|
||||
impl->add_genfscon(args...);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
sepolicy::allowxperm(rust::Vec<rust::Str> src, rust::Vec<rust::Str> tgt, rust::Vec<rust::Str> cls,
|
||||
rust::Vec<Xperm> xperm) {
|
||||
expand(src, tgt, cls, xperm, [this](auto ...args) {
|
||||
dprint("allowxperm", args...);
|
||||
impl->add_xperm_rule(args..., AVTAB_XPERMS_ALLOWED);
|
||||
});
|
||||
}
|
||||
|
||||
void sepolicy::auditallowxperm(rust::Vec<rust::Str> src, rust::Vec<rust::Str> tgt,
|
||||
rust::Vec<rust::Str> cls,
|
||||
rust::Vec<Xperm> xperm) {
|
||||
expand(src, tgt, cls, xperm, [this](auto ...args) {
|
||||
dprint("auditallowxperm", args...);
|
||||
impl->add_xperm_rule(args..., AVTAB_XPERMS_AUDITALLOW);
|
||||
});
|
||||
}
|
||||
|
||||
void sepolicy::dontauditxperm(rust::Vec<rust::Str> src, rust::Vec<rust::Str> tgt,
|
||||
rust::Vec<rust::Str> cls,
|
||||
rust::Vec<Xperm> xperm) {
|
||||
expand(src, tgt, cls, xperm, [this](auto ...args) {
|
||||
dprint("dontauditxperm", args...);
|
||||
impl->add_xperm_rule(args..., AVTAB_XPERMS_DONTAUDIT);
|
||||
});
|
||||
}
|
||||
|
||||
void sepolicy::strip_dontaudit() {
|
||||
impl->strip_dontaudit();
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ using token_list = std::vector<const char *>;
|
||||
using argument = std::pair<token_list, bool>;
|
||||
using argument_list = std::vector<argument>;
|
||||
|
||||
struct Xperm;
|
||||
|
||||
#define ALL nullptr
|
||||
|
||||
struct sepolicy {
|
||||
@ -34,46 +36,44 @@ struct sepolicy {
|
||||
static sepolicy *from_file(c_str file);
|
||||
static sepolicy *from_split();
|
||||
static sepolicy *compile_split();
|
||||
|
||||
// External APIs
|
||||
bool to_file(c_str file);
|
||||
void parse_statement(rust::Str stmt);
|
||||
void load_rules(const std::string &rules);
|
||||
void load_rule_file(c_str file);
|
||||
void print_rules();
|
||||
void parse_statement(c_str statement);
|
||||
|
||||
// Operation on types
|
||||
bool type(c_str name, c_str attr);
|
||||
bool attribute(c_str name);
|
||||
bool permissive(c_str type);
|
||||
bool enforce(c_str type);
|
||||
bool typeattribute(c_str type, c_str attr);
|
||||
void type(rust::Str type, rust::Vec<rust::Str> attrs);
|
||||
void attribute(rust::Str names);
|
||||
void permissive(rust::Vec<rust::Str> types);
|
||||
void enforce(rust::Vec<rust::Str> types);
|
||||
void typeattribute(rust::Vec<rust::Str> types, rust::Vec<rust::Str> attrs);
|
||||
bool exists(c_str type);
|
||||
|
||||
// Access vector rules
|
||||
bool allow(c_str src, c_str tgt, c_str cls, c_str perm);
|
||||
bool deny(c_str src, c_str tgt, c_str cls, c_str perm);
|
||||
bool auditallow(c_str src, c_str tgt, c_str cls, c_str perm);
|
||||
bool dontaudit(c_str src, c_str tgt, c_str cls, c_str perm);
|
||||
void allow(rust::Vec<rust::Str> src, rust::Vec<rust::Str> tgt, rust::Vec<rust::Str> cls, rust::Vec<rust::Str> perm);
|
||||
void deny(rust::Vec<rust::Str> src, rust::Vec<rust::Str> tgt, rust::Vec<rust::Str> cls, rust::Vec<rust::Str> perm);
|
||||
void auditallow(rust::Vec<rust::Str> src, rust::Vec<rust::Str> tgt, rust::Vec<rust::Str> cls, rust::Vec<rust::Str> perm);
|
||||
void dontaudit(rust::Vec<rust::Str> src, rust::Vec<rust::Str> tgt, rust::Vec<rust::Str> cls, rust::Vec<rust::Str> perm);
|
||||
|
||||
// Extended permissions access vector rules
|
||||
bool allowxperm(c_str src, c_str tgt, c_str cls, const argument &xperm);
|
||||
bool auditallowxperm(c_str src, c_str tgt, c_str cls, const argument &xperm);
|
||||
bool dontauditxperm(c_str src, c_str tgt, c_str cls, const argument &xperm);
|
||||
void allowxperm(rust::Vec<rust::Str> src, rust::Vec<rust::Str> tgt, rust::Vec<rust::Str> cls, rust::Vec<Xperm> xperm);
|
||||
void auditallowxperm(rust::Vec<rust::Str> src, rust::Vec<rust::Str> tgt, rust::Vec<rust::Str> cls, rust::Vec<Xperm> xperm);
|
||||
void dontauditxperm(rust::Vec<rust::Str> src, rust::Vec<rust::Str> tgt, rust::Vec<rust::Str> cls, rust::Vec<Xperm> xperm);
|
||||
|
||||
// Type rules
|
||||
bool type_transition(c_str src, c_str tgt, c_str cls, c_str def, c_str obj = nullptr);
|
||||
bool type_change(c_str src, c_str tgt, c_str cls, c_str def);
|
||||
bool type_member(c_str src, c_str tgt, c_str cls, c_str def);
|
||||
void type_transition(rust::Str src, rust::Str tgt, rust::Str cls, rust::Str def, rust::Vec<rust::Str> obj);
|
||||
void type_change(rust::Str src, rust::Str tgt, rust::Str cls, rust::Str def);
|
||||
void type_member(rust::Str src, rust::Str tgt, rust::Str cls, rust::Str def);
|
||||
|
||||
// File system labeling
|
||||
bool genfscon(c_str fs_name, c_str path, c_str ctx);
|
||||
void genfscon(rust::Str fs_name, rust::Str path, rust::Str ctx);
|
||||
|
||||
// Magisk
|
||||
void magisk_rules();
|
||||
|
||||
// Deprecate
|
||||
bool create(c_str name) { return type(name, "domain"); }
|
||||
void strip_dontaudit();
|
||||
|
||||
protected:
|
||||
// Prevent anyone from accidentally creating an instance
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![feature(format_args_nl)]
|
||||
|
||||
use io::Cursor;
|
||||
use std::io;
|
||||
use std::io::{BufRead, BufReader};
|
||||
@ -5,12 +7,22 @@ use std::pin::Pin;
|
||||
|
||||
pub use base;
|
||||
use base::libc::{O_CLOEXEC, O_RDONLY};
|
||||
use base::{BufReadExt, FsPath, LoggedResult, Utf8CStr};
|
||||
use base::{error, BufReadExt, FsPath, LoggedResult, Utf8CStr};
|
||||
|
||||
use crate::ffi::sepolicy;
|
||||
|
||||
mod statement;
|
||||
|
||||
mod rules;
|
||||
|
||||
#[cxx::bridge]
|
||||
mod ffi {
|
||||
struct Xperm {
|
||||
low: u16,
|
||||
high: u16,
|
||||
reset: bool,
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
#[namespace = "rust"]
|
||||
#[cxx_name = "Utf8CStr"]
|
||||
@ -19,13 +31,69 @@ mod ffi {
|
||||
include!("include/sepolicy.hpp");
|
||||
|
||||
type sepolicy;
|
||||
fn parse_statement(self: Pin<&mut sepolicy>, stmt: &str);
|
||||
fn allow(self: Pin<&mut sepolicy>, s: Vec<&str>, t: Vec<&str>, c: Vec<&str>, p: Vec<&str>);
|
||||
fn deny(self: Pin<&mut sepolicy>, s: Vec<&str>, t: Vec<&str>, c: Vec<&str>, p: Vec<&str>);
|
||||
fn auditallow(
|
||||
self: Pin<&mut sepolicy>,
|
||||
s: Vec<&str>,
|
||||
t: Vec<&str>,
|
||||
c: Vec<&str>,
|
||||
p: Vec<&str>,
|
||||
);
|
||||
fn dontaudit(
|
||||
self: Pin<&mut sepolicy>,
|
||||
s: Vec<&str>,
|
||||
t: Vec<&str>,
|
||||
c: Vec<&str>,
|
||||
p: Vec<&str>,
|
||||
);
|
||||
fn allowxperm(
|
||||
self: Pin<&mut sepolicy>,
|
||||
s: Vec<&str>,
|
||||
t: Vec<&str>,
|
||||
c: Vec<&str>,
|
||||
p: Vec<Xperm>,
|
||||
);
|
||||
fn auditallowxperm(
|
||||
self: Pin<&mut sepolicy>,
|
||||
s: Vec<&str>,
|
||||
t: Vec<&str>,
|
||||
c: Vec<&str>,
|
||||
p: Vec<Xperm>,
|
||||
);
|
||||
fn dontauditxperm(
|
||||
self: Pin<&mut sepolicy>,
|
||||
s: Vec<&str>,
|
||||
t: Vec<&str>,
|
||||
c: Vec<&str>,
|
||||
p: Vec<Xperm>,
|
||||
);
|
||||
fn permissive(self: Pin<&mut sepolicy>, t: Vec<&str>);
|
||||
fn enforce(self: Pin<&mut sepolicy>, t: Vec<&str>);
|
||||
fn typeattribute(self: Pin<&mut sepolicy>, t: Vec<&str>, a: Vec<&str>);
|
||||
#[cxx_name = "type"]
|
||||
fn type_(self: Pin<&mut sepolicy>, t: &str, a: Vec<&str>);
|
||||
fn attribute(self: Pin<&mut sepolicy>, t: &str);
|
||||
fn type_transition(
|
||||
self: Pin<&mut sepolicy>,
|
||||
s: &str,
|
||||
t: &str,
|
||||
c: &str,
|
||||
d: &str,
|
||||
o: Vec<&str>,
|
||||
);
|
||||
fn type_change(self: Pin<&mut sepolicy>, s: &str, t: &str, c: &str, d: &str);
|
||||
fn type_member(self: Pin<&mut sepolicy>, s: &str, t: &str, c: &str, d: &str);
|
||||
fn genfscon(self: Pin<&mut sepolicy>, s: &str, t: &str, c: &str);
|
||||
fn strip_dontaudit(self: Pin<&mut sepolicy>);
|
||||
}
|
||||
|
||||
#[namespace = "rust"]
|
||||
extern "Rust" {
|
||||
fn load_rules(sepol: Pin<&mut sepolicy>, rules: &[u8]);
|
||||
fn load_rule_file(sepol: Pin<&mut sepolicy>, filename: Utf8CStrRef);
|
||||
fn parse_statement(sepol: Pin<&mut sepolicy>, statement: Utf8CStrRef);
|
||||
fn magisk_rules(sepol: Pin<&mut sepolicy>);
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,6 +101,11 @@ trait SepolicyExt {
|
||||
fn load_rules(self: Pin<&mut Self>, rules: &[u8]);
|
||||
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 parse_statement(self: Pin<&mut Self>, statement: &str);
|
||||
}
|
||||
|
||||
trait SepolicyMagisk {
|
||||
fn magisk_rules(self: Pin<&mut Self>);
|
||||
}
|
||||
|
||||
impl SepolicyExt for sepolicy {
|
||||
@ -54,10 +127,19 @@ impl SepolicyExt for sepolicy {
|
||||
fn load_rules_from_reader<T: BufRead>(mut self: Pin<&mut sepolicy>, reader: &mut T) {
|
||||
reader.foreach_lines(|line| {
|
||||
let line = line.trim();
|
||||
if line.is_empty() {
|
||||
return true;
|
||||
}
|
||||
self.as_mut().parse_statement(line);
|
||||
true
|
||||
});
|
||||
}
|
||||
|
||||
fn parse_statement(self: Pin<&mut Self>, statement: &str) {
|
||||
if let Err(_) = statement::parse_statement(self, statement) {
|
||||
error!("sepolicy rule syntax error: {statement}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_rule_file(sepol: Pin<&mut sepolicy>, filename: &Utf8CStr) {
|
||||
@ -67,3 +149,11 @@ pub fn load_rule_file(sepol: Pin<&mut sepolicy>, filename: &Utf8CStr) {
|
||||
pub fn load_rules(sepol: Pin<&mut sepolicy>, rules: &[u8]) {
|
||||
sepol.load_rules(rules);
|
||||
}
|
||||
|
||||
pub fn parse_statement(sepol: Pin<&mut sepolicy>, statement: &Utf8CStr) {
|
||||
sepol.parse_statement(statement.as_str());
|
||||
}
|
||||
|
||||
pub fn magisk_rules(sepol: Pin<&mut sepolicy>) {
|
||||
sepol.magisk_rules();
|
||||
}
|
||||
|
@ -20,8 +20,8 @@ struct sepol_impl : public sepolicy {
|
||||
|
||||
bool add_rule(const char *s, const char *t, const char *c, const char *p, int effect, bool invert);
|
||||
void add_rule(type_datum_t *src, type_datum_t *tgt, class_datum_t *cls, perm_datum_t *perm, int effect, bool invert);
|
||||
void add_xperm_rule(type_datum_t *src, type_datum_t *tgt, class_datum_t *cls, const argument &xperm, int effect);
|
||||
bool add_xperm_rule(const char *s, const char *t, const char *c, const argument &xperm, int effect);
|
||||
void add_xperm_rule(type_datum_t *src, type_datum_t *tgt, class_datum_t *cls, uint16_t low, uint16_t high, bool reset, int effect);
|
||||
bool add_xperm_rule(const char *s, const char *t, const char *c, uint16_t low, uint16_t high, bool reset, int effect);
|
||||
bool add_type_rule(const char *s, const char *t, const char *c, const char *d, int effect);
|
||||
bool add_filename_trans(const char *s, const char *t, const char *c, const char *d, const char *o);
|
||||
bool add_genfscon(const char *fs_name, const char *path, const char *context);
|
||||
|
@ -1,136 +0,0 @@
|
||||
#include <consts.hpp>
|
||||
#include <base.hpp>
|
||||
|
||||
#include "policy.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void sepolicy::magisk_rules() {
|
||||
// Temp suppress warnings
|
||||
set_log_level_state(LogLevel::Warn, false);
|
||||
|
||||
// Prevent anything to change sepolicy except ourselves
|
||||
deny(ALL, "kernel", "security", "load_policy");
|
||||
|
||||
type(SEPOL_PROC_DOMAIN, "domain");
|
||||
permissive(SEPOL_PROC_DOMAIN); /* Just in case something is missing */
|
||||
typeattribute(SEPOL_PROC_DOMAIN, "mlstrustedsubject");
|
||||
typeattribute(SEPOL_PROC_DOMAIN, "netdomain");
|
||||
typeattribute(SEPOL_PROC_DOMAIN, "bluetoothdomain");
|
||||
type(SEPOL_FILE_TYPE, "file_type");
|
||||
typeattribute(SEPOL_FILE_TYPE, "mlstrustedobject");
|
||||
type(SEPOL_LOG_TYPE, "file_type");
|
||||
typeattribute(SEPOL_LOG_TYPE, "mlstrustedobject");
|
||||
|
||||
// Make our root domain unconstrained
|
||||
allow(SEPOL_PROC_DOMAIN, ALL, ALL, ALL);
|
||||
// Allow us to do any ioctl
|
||||
if (impl->db->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) {
|
||||
argument all;
|
||||
all.first.push_back(nullptr);
|
||||
allowxperm(SEPOL_PROC_DOMAIN, ALL, "blk_file", all);
|
||||
allowxperm(SEPOL_PROC_DOMAIN, ALL, "fifo_file", all);
|
||||
allowxperm(SEPOL_PROC_DOMAIN, ALL, "chr_file", all);
|
||||
}
|
||||
|
||||
// Create unconstrained file type
|
||||
allow(ALL, SEPOL_FILE_TYPE, "file", ALL);
|
||||
allow(ALL, SEPOL_FILE_TYPE, "dir", ALL);
|
||||
allow(ALL, SEPOL_FILE_TYPE, "fifo_file", ALL);
|
||||
allow(ALL, SEPOL_FILE_TYPE, "chr_file", ALL);
|
||||
allow(ALL, SEPOL_FILE_TYPE, "lnk_file", ALL);
|
||||
allow(ALL, SEPOL_FILE_TYPE, "sock_file", ALL);
|
||||
|
||||
// Only allow zygote to open log pipe
|
||||
allow("zygote", SEPOL_LOG_TYPE, "fifo_file", "open");
|
||||
allow("zygote", SEPOL_LOG_TYPE, "fifo_file", "read");
|
||||
// Allow all processes to output logs
|
||||
allow("domain", SEPOL_LOG_TYPE, "fifo_file", "write");
|
||||
|
||||
// Allow these processes to access MagiskSU and output logs
|
||||
const char *clients[] {
|
||||
"zygote", "shell", "system_app", "platform_app",
|
||||
"priv_app", "untrusted_app", "untrusted_app_all"
|
||||
};
|
||||
for (auto type: clients) {
|
||||
if (!exists(type))
|
||||
continue;
|
||||
allow(type, SEPOL_PROC_DOMAIN, "unix_stream_socket", "connectto");
|
||||
allow(type, SEPOL_PROC_DOMAIN, "unix_stream_socket", "getopt");
|
||||
}
|
||||
|
||||
// Let everyone access tmpfs files (for SAR sbin overlay)
|
||||
allow(ALL, "tmpfs", "file", ALL);
|
||||
|
||||
// Allow magiskinit daemon to handle mock selinuxfs
|
||||
allow("kernel", "tmpfs", "fifo_file", "write");
|
||||
|
||||
// For relabelling files
|
||||
allow("rootfs", "labeledfs", "filesystem", "associate");
|
||||
allow(SEPOL_FILE_TYPE, "pipefs", "filesystem", "associate");
|
||||
allow(SEPOL_FILE_TYPE, "devpts", "filesystem", "associate");
|
||||
|
||||
// Let init transit to SEPOL_PROC_DOMAIN
|
||||
allow("kernel", "kernel", "process", "setcurrent");
|
||||
allow("kernel", SEPOL_PROC_DOMAIN, "process", "dyntransition");
|
||||
|
||||
// Let init run stuffs
|
||||
allow("kernel", SEPOL_PROC_DOMAIN, "fd", "use");
|
||||
allow("init", SEPOL_PROC_DOMAIN, "process", ALL);
|
||||
|
||||
// suRights
|
||||
allow("servicemanager", SEPOL_PROC_DOMAIN, "dir", "search");
|
||||
allow("servicemanager", SEPOL_PROC_DOMAIN, "dir", "read");
|
||||
allow("servicemanager", SEPOL_PROC_DOMAIN, "file", "open");
|
||||
allow("servicemanager", SEPOL_PROC_DOMAIN, "file", "read");
|
||||
allow("servicemanager", SEPOL_PROC_DOMAIN, "process", "getattr");
|
||||
allow(ALL, SEPOL_PROC_DOMAIN, "process", "sigchld");
|
||||
|
||||
// allowLog
|
||||
allow("logd", SEPOL_PROC_DOMAIN, "dir", "search");
|
||||
allow("logd", SEPOL_PROC_DOMAIN, "file", "read");
|
||||
allow("logd", SEPOL_PROC_DOMAIN, "file", "open");
|
||||
allow("logd", SEPOL_PROC_DOMAIN, "file", "getattr");
|
||||
|
||||
// dumpsys
|
||||
allow(ALL, SEPOL_PROC_DOMAIN, "fd", "use");
|
||||
allow(ALL, SEPOL_PROC_DOMAIN, "fifo_file", "write");
|
||||
allow(ALL, SEPOL_PROC_DOMAIN, "fifo_file", "read");
|
||||
allow(ALL, SEPOL_PROC_DOMAIN, "fifo_file", "open");
|
||||
allow(ALL, SEPOL_PROC_DOMAIN, "fifo_file", "getattr");
|
||||
|
||||
// bootctl
|
||||
allow("hwservicemanager", SEPOL_PROC_DOMAIN, "dir", "search");
|
||||
allow("hwservicemanager", SEPOL_PROC_DOMAIN, "file", "read");
|
||||
allow("hwservicemanager", SEPOL_PROC_DOMAIN, "file", "open");
|
||||
allow("hwservicemanager", SEPOL_PROC_DOMAIN, "process", "getattr");
|
||||
|
||||
// For mounting loop devices, mirrors, tmpfs
|
||||
allow("kernel", ALL, "file", "read");
|
||||
allow("kernel", ALL, "file", "write");
|
||||
|
||||
// Allow all binder transactions
|
||||
allow(ALL, SEPOL_PROC_DOMAIN, "binder", ALL);
|
||||
|
||||
// For changing file context
|
||||
allow("rootfs", "tmpfs", "filesystem", "associate");
|
||||
|
||||
// Zygisk rules
|
||||
allow("zygote", "zygote", "process", "execmem");
|
||||
allow("zygote", "fs_type", "filesystem", "unmount");
|
||||
allow("system_server", "system_server", "process", "execmem");
|
||||
|
||||
// Shut llkd up
|
||||
dontaudit("llkd", SEPOL_PROC_DOMAIN, "process", "ptrace");
|
||||
|
||||
// Keep /data/adb/* context
|
||||
deny("init", "adb_data_file", "dir", "search");
|
||||
deny("vendor_init", "adb_data_file", "dir", "search");
|
||||
|
||||
#if 0
|
||||
// Remove all dontaudit in debug mode
|
||||
impl->strip_dontaudit();
|
||||
#endif
|
||||
|
||||
set_log_level_state(LogLevel::Warn, true);
|
||||
}
|
145
native/src/sepolicy/rules.rs
Normal file
145
native/src/sepolicy/rules.rs
Normal file
@ -0,0 +1,145 @@
|
||||
use crate::{ffi::Xperm, sepolicy, SepolicyMagisk};
|
||||
use base::{set_log_level_state, LogLevel};
|
||||
use std::pin::Pin;
|
||||
|
||||
macro_rules! rules {
|
||||
(@args all) => {
|
||||
vec![]
|
||||
};
|
||||
(@args xall) => {
|
||||
vec![Xperm { low: 0x0000, high: 0xFFFF, reset: false }]
|
||||
};
|
||||
(@args [proc]) => {
|
||||
vec!["magisk"]
|
||||
};
|
||||
(@args [file]) => {
|
||||
vec!["magisk_file"]
|
||||
};
|
||||
(@args [log]) => {
|
||||
vec!["magisk_log_file"]
|
||||
};
|
||||
(@args proc) => {
|
||||
"magisk"
|
||||
};
|
||||
(@args file) => {
|
||||
"magisk_file"
|
||||
};
|
||||
(@args log) => {
|
||||
"magisk_log_file"
|
||||
};
|
||||
(@args [$($arg:tt)*]) => {
|
||||
vec![$($arg)*]
|
||||
};
|
||||
(@args $arg:expr) => {
|
||||
$arg
|
||||
};
|
||||
(@stmt $self:ident) => {};
|
||||
(@stmt $self:ident $action:ident($($args:tt),*); $($res:tt)*) => {
|
||||
$self.as_mut().$action($(rules!(@args $args)),*);
|
||||
rules!{@stmt $self $($res)* }
|
||||
};
|
||||
(use $self:ident; $($res:tt)*) => {{
|
||||
rules!{@stmt $self $($res)* }
|
||||
}};
|
||||
}
|
||||
|
||||
impl SepolicyMagisk for sepolicy {
|
||||
fn magisk_rules(mut self: Pin<&mut Self>) {
|
||||
// Temp suppress warnings
|
||||
set_log_level_state(LogLevel::Warn, false);
|
||||
rules! {
|
||||
use self;
|
||||
allow(all, ["kernel"], ["security"], ["load_policy"]);
|
||||
type_(proc, ["domain"]);
|
||||
permissive([proc]);
|
||||
typeattribute([proc], ["mlstrustedsubject", "netdomain", "bluetoothdomain"]);
|
||||
type_(file, ["file_type"]);
|
||||
typeattribute([file], ["mlstrustedobject"]);
|
||||
type_(log, ["file_type"]);
|
||||
typeattribute([log], ["mlstrustedobject"]);
|
||||
|
||||
// Make our root domain unconstrained
|
||||
allow([proc], all, all, all);
|
||||
|
||||
// Allow us to do any ioctl
|
||||
allowxperm(["magisk"], all, ["blk_file", "fifo_file", "chr_file"], xall);
|
||||
|
||||
// Create unconstrained file type
|
||||
allow(all, [file], ["file", "dir", "fifo_file", "chr_file", "lnk_file", "sock_file"], all);
|
||||
|
||||
// Only allow zygote to open log pipe
|
||||
allow(["zygote"], [log], ["fifo_file"], ["open", "read"]);
|
||||
|
||||
// Allow all processes to output logs
|
||||
|
||||
allow(["domain"], [log], ["fifo_file"], ["write"]);
|
||||
|
||||
// Allow these processes to access MagiskSU and output logs
|
||||
allow(["zygote", "shell", "system_app", "priv_app", "untrusted_app", "untrusted_app_all"],
|
||||
[proc], ["unix_stream_socket"], ["connectto", "getopt"]);
|
||||
|
||||
// Let everyone access tmpfs files (for SAR sbin overlay)
|
||||
allow(all, ["tmpfs"], ["file"], all);
|
||||
|
||||
// Allow magiskinit daemon to handle mock selinuxfs
|
||||
allow(["kernel"], ["tmpfs"], ["fifo_file"], ["write"]);
|
||||
|
||||
// For relabelling files
|
||||
allow(["rootfs"], ["labeledfs"], ["filesystem"], ["associate"]);
|
||||
allow([file], ["pipefs", "devpts"], ["filesystem"], ["associate"]);
|
||||
|
||||
// Let init transit to SEPOL_PROC_DOMAIN
|
||||
allow(["kernel"], ["kernel"], ["process"], ["setcurrent"]);
|
||||
allow(["kernel"], [proc], ["process"], ["dyntransition"]);
|
||||
|
||||
// Let init run stuffs
|
||||
allow(["kernel"], [proc], ["fd"], ["use"]);
|
||||
allow(["init"], [proc], ["process"], all);
|
||||
|
||||
// suRights
|
||||
allow(["servicemanager"], [proc], ["dir"], ["search", "read"]);
|
||||
allow(["servicemanager"], [proc], ["file"], ["open", "read"]);
|
||||
allow(["servicemanager"], [proc], ["process"], ["getattr"]);
|
||||
allow(all, [proc], ["process"], ["sigchld"]);
|
||||
|
||||
// allowLog
|
||||
allow(["logd"], [proc], ["dir"], ["search"]);
|
||||
allow(["logd"], [proc], ["file"], ["read", "open", "getattr"]);
|
||||
|
||||
// dumpsys
|
||||
allow(all, [proc], ["fd"], ["use"]);
|
||||
allow(all, [proc], ["fifo_file"], ["write", "read", "open", "getattr"]);
|
||||
|
||||
// bootctl
|
||||
allow(["hwservicemanager"], [proc], ["dir"], ["search"]);
|
||||
allow(["hwservicemanager"], [proc], ["file"], ["read", "open"]);
|
||||
allow(["hwservicemanager"], [proc], ["process"], ["getattr"]);
|
||||
|
||||
// For mounting loop devices, mirrors, tmpfs
|
||||
allow(["kernel"], all, ["file"], ["read", "write"]);
|
||||
|
||||
// Allow all binder transactions
|
||||
allow(all, [proc], ["binder"], all);
|
||||
|
||||
// For changing file context
|
||||
allow(["rootfs"], ["tmpfs"], ["filesystem"], ["associate"]);
|
||||
|
||||
// Zygisk rules
|
||||
allow(["zygote"], ["zygote"], ["process"], ["execmem"]);
|
||||
allow(["zygote"], ["fs_type"], ["filesystem"], ["unmount"]);
|
||||
allow(["system_server"], ["system_server"], ["process"], ["execmem"]);
|
||||
|
||||
// Shut llkd up
|
||||
dontaudit(["llkd"], [proc], ["process"], ["ptrace"]);
|
||||
|
||||
// Keep /data/adb/* context
|
||||
deny(["init"], ["adb_data_file"], ["dir"], ["search"]);
|
||||
deny(["vendor_init"], ["adb_data_file"], ["dir"], ["search"]);
|
||||
}
|
||||
// Remove all dontaudit in debug mode
|
||||
#[cfg(debug_assertions)]
|
||||
self.as_mut().strip_dontaudit();
|
||||
|
||||
set_log_level_state(LogLevel::Warn, true);
|
||||
}
|
||||
}
|
@ -270,18 +270,18 @@ bool sepol_impl::add_rule(const char *s, const char *t, const char *c, const cha
|
||||
#define ioctl_driver(x) (x>>8 & 0xFF)
|
||||
#define ioctl_func(x) (x & 0xFF)
|
||||
|
||||
void sepol_impl::add_xperm_rule(type_datum_t *src, type_datum_t *tgt, class_datum_t *cls, const argument &xperm, int effect) {
|
||||
void sepol_impl::add_xperm_rule(type_datum_t *src, type_datum_t *tgt, class_datum_t *cls, uint16_t low, uint16_t high, bool reset, int effect) {
|
||||
if (src == nullptr) {
|
||||
for_each_attr(db->p_types.table, [&](type_datum_t *type) {
|
||||
add_xperm_rule(type, tgt, cls, xperm, effect);
|
||||
add_xperm_rule(type, tgt, cls, low, high, reset, effect);
|
||||
});
|
||||
} else if (tgt == nullptr) {
|
||||
for_each_attr(db->p_types.table, [&](type_datum_t *type) {
|
||||
add_xperm_rule(src, type, cls, xperm, effect);
|
||||
add_xperm_rule(src, type, cls, low, high, reset, effect);
|
||||
});
|
||||
} else if (cls == nullptr) {
|
||||
hashtab_for_each(db->p_classes.table, [&](hashtab_ptr_t node) {
|
||||
add_xperm_rule(src, tgt, auto_cast(node->datum), xperm, effect);
|
||||
add_xperm_rule(src, tgt, auto_cast(node->datum), low, high, reset, effect);
|
||||
});
|
||||
} else {
|
||||
avtab_key_t key;
|
||||
@ -304,35 +304,6 @@ void sepol_impl::add_xperm_rule(type_datum_t *src, type_datum_t *tgt, class_datu
|
||||
node = avtab_search_node_next(node, key.specified);
|
||||
}
|
||||
|
||||
bool reset = xperm.second;
|
||||
vector<pair<uint16_t, uint16_t>> ranges;
|
||||
|
||||
for (const char *tok : xperm.first) {
|
||||
uint16_t low = 0;
|
||||
uint16_t high = 0;
|
||||
if (tok == nullptr) {
|
||||
low = 0x0000;
|
||||
high = 0xFF00;
|
||||
reset = true;
|
||||
} else if (strchr(tok, '-')) {
|
||||
if (sscanf(tok, "%hx-%hx", &low, &high) != 2) {
|
||||
// Invalid token, skip
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (sscanf(tok, "%hx", &low) != 1) {
|
||||
// Invalid token, skip
|
||||
continue;
|
||||
}
|
||||
high = low;
|
||||
}
|
||||
if (high == 0) {
|
||||
reset = true;
|
||||
} else {
|
||||
ranges.emplace_back(make_pair(low, high));
|
||||
}
|
||||
}
|
||||
|
||||
if (reset) {
|
||||
for (int i = 0; i <= 0xFF; ++i) {
|
||||
if (node_list[i]) {
|
||||
@ -361,25 +332,23 @@ void sepol_impl::add_xperm_rule(type_datum_t *src, type_datum_t *tgt, class_datu
|
||||
return node;
|
||||
};
|
||||
|
||||
if (!xperm.second) {
|
||||
for (auto [low, high] : ranges) {
|
||||
if (ioctl_driver(low) != ioctl_driver(high)) {
|
||||
if (driver_node == nullptr) {
|
||||
driver_node = new_driver_node();
|
||||
}
|
||||
for (int i = ioctl_driver(low); i <= ioctl_driver(high); ++i) {
|
||||
xperm_set(i, driver_node->datum.xperms->perms);
|
||||
}
|
||||
} else {
|
||||
uint8_t driver = ioctl_driver(low);
|
||||
auto node = node_list[driver];
|
||||
if (node == nullptr) {
|
||||
node = new_func_node(driver);
|
||||
node_list[driver] = node;
|
||||
}
|
||||
for (int i = ioctl_func(low); i <= ioctl_func(high); ++i) {
|
||||
xperm_set(i, node->datum.xperms->perms);
|
||||
}
|
||||
if (!reset) {
|
||||
if (ioctl_driver(low) != ioctl_driver(high)) {
|
||||
if (driver_node == nullptr) {
|
||||
driver_node = new_driver_node();
|
||||
}
|
||||
for (int i = ioctl_driver(low); i <= ioctl_driver(high); ++i) {
|
||||
xperm_set(i, driver_node->datum.xperms->perms);
|
||||
}
|
||||
} else {
|
||||
uint8_t driver = ioctl_driver(low);
|
||||
auto node = node_list[driver];
|
||||
if (node == nullptr) {
|
||||
node = new_func_node(driver);
|
||||
node_list[driver] = node;
|
||||
}
|
||||
for (int i = ioctl_func(low); i <= ioctl_func(high); ++i) {
|
||||
xperm_set(i, node->datum.xperms->perms);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -389,31 +358,29 @@ void sepol_impl::add_xperm_rule(type_datum_t *src, type_datum_t *tgt, class_datu
|
||||
// Fill the driver perms
|
||||
memset(driver_node->datum.xperms->perms, ~0, sizeof(avtab_extended_perms_t::perms));
|
||||
|
||||
for (auto [low, high] : ranges) {
|
||||
if (ioctl_driver(low) != ioctl_driver(high)) {
|
||||
for (int i = ioctl_driver(low); i <= ioctl_driver(high); ++i) {
|
||||
xperm_clear(i, driver_node->datum.xperms->perms);
|
||||
}
|
||||
} else {
|
||||
uint8_t driver = ioctl_driver(low);
|
||||
auto node = node_list[driver];
|
||||
if (node == nullptr) {
|
||||
node = new_func_node(driver);
|
||||
// Fill the func perms
|
||||
memset(node->datum.xperms->perms, ~0, sizeof(avtab_extended_perms_t::perms));
|
||||
node_list[driver] = node;
|
||||
}
|
||||
xperm_clear(driver, driver_node->datum.xperms->perms);
|
||||
for (int i = ioctl_func(low); i <= ioctl_func(high); ++i) {
|
||||
xperm_clear(i, node->datum.xperms->perms);
|
||||
}
|
||||
if (ioctl_driver(low) != ioctl_driver(high)) {
|
||||
for (int i = ioctl_driver(low); i <= ioctl_driver(high); ++i) {
|
||||
xperm_clear(i, driver_node->datum.xperms->perms);
|
||||
}
|
||||
} else {
|
||||
uint8_t driver = ioctl_driver(low);
|
||||
auto node = node_list[driver];
|
||||
if (node == nullptr) {
|
||||
node = new_func_node(driver);
|
||||
// Fill the func perms
|
||||
memset(node->datum.xperms->perms, ~0, sizeof(avtab_extended_perms_t::perms));
|
||||
node_list[driver] = node;
|
||||
}
|
||||
xperm_clear(driver, driver_node->datum.xperms->perms);
|
||||
for (int i = ioctl_func(low); i <= ioctl_func(high); ++i) {
|
||||
xperm_clear(i, node->datum.xperms->perms);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool sepol_impl::add_xperm_rule(const char *s, const char *t, const char *c, const argument &xperm, int effect) {
|
||||
bool sepol_impl::add_xperm_rule(const char *s, const char *t, const char *c, uint16_t low, uint16_t high, bool reset, int effect) {
|
||||
type_datum_t *src = nullptr, *tgt = nullptr;
|
||||
class_datum_t *cls = nullptr;
|
||||
|
||||
@ -441,7 +408,7 @@ bool sepol_impl::add_xperm_rule(const char *s, const char *t, const char *c, con
|
||||
}
|
||||
}
|
||||
|
||||
add_xperm_rule(src, tgt, cls, xperm, effect);
|
||||
add_xperm_rule(src, tgt, cls, low, high, reset, effect);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -100,471 +100,3 @@ Supported policy statements:
|
||||
type_msg_5, type_msg_6, type_msg_7, type_msg_8, type_msg_9);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Return value:
|
||||
// 0: success
|
||||
// -1: unclosed bracket
|
||||
// -2: nested brackets
|
||||
// -3: double complement
|
||||
static int tokenize_string(char *stmt, argument_list &args) {
|
||||
char *cur = stmt;
|
||||
bool complement = false;
|
||||
|
||||
auto add_new_arg = [&] (char *str) {
|
||||
argument arg;
|
||||
for (char *tok; (tok = strtok_r(nullptr, " ", &str)) != nullptr;) {
|
||||
if (tok == "*"sv) {
|
||||
// If any of the tokens is "*", the result is a single nullptr
|
||||
arg.first.clear();
|
||||
arg.first.push_back(nullptr);
|
||||
break;
|
||||
} else {
|
||||
arg.first.push_back(tok);
|
||||
}
|
||||
}
|
||||
arg.second = complement;
|
||||
complement = false;
|
||||
args.push_back(std::move(arg));
|
||||
};
|
||||
|
||||
char *tok = strtok_r(nullptr, " ", &cur);
|
||||
while (tok) {
|
||||
if (tok[0] == '~') {
|
||||
if (complement) {
|
||||
// Double complement is not supported
|
||||
return -3;
|
||||
}
|
||||
complement = true;
|
||||
if (tok[1] == '\0') {
|
||||
// If '~' is followed by a space, find the next token
|
||||
tok = strtok_r(nullptr, " ", &cur);
|
||||
} else {
|
||||
// Reparse the rest of the string
|
||||
++tok;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find brackets
|
||||
char *begin = strchr(tok, '{');
|
||||
char *end = strchr(tok, '}');
|
||||
|
||||
if (begin == nullptr && end) {
|
||||
// Bracket not closed, syntax error
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (begin) {
|
||||
if (end && (begin > end)) {
|
||||
// Bracket not closed, syntax error
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Restore the string so we can properly find the closing bracket
|
||||
if (cur)
|
||||
cur[-1] = ' ';
|
||||
|
||||
if (end == nullptr) {
|
||||
// Find again
|
||||
end = strchr(begin + 1, '}');
|
||||
if (end == nullptr) {
|
||||
// Bracket not closed, syntax error
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Close bracket and start the next parsing iteration after that
|
||||
*end = '\0';
|
||||
cur = end + 1;
|
||||
|
||||
if (strchr(begin + 1, '{')) {
|
||||
// We don't support nested brackets
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (begin != tok) {
|
||||
// There is an argument before the opening bracket
|
||||
*begin = '\0';
|
||||
add_new_arg(tok);
|
||||
}
|
||||
|
||||
// Parse the arguments enclosed in the brackets
|
||||
add_new_arg(begin + 1);
|
||||
} else {
|
||||
add_new_arg(tok);
|
||||
}
|
||||
|
||||
tok = strtok_r(nullptr, " ", &cur);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check all args listed have size = 1 (no multiple entries)
|
||||
template <int ...indices>
|
||||
static bool enforce_single(const argument_list &args) {
|
||||
initializer_list<int> list{indices...};
|
||||
for (int i : list)
|
||||
if (args[i].first.size() != 1)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check all args listed are not null (no match all operator)
|
||||
template <int ...indices>
|
||||
static bool enforce_non_null(const argument_list &args) {
|
||||
initializer_list<int> list{indices...};
|
||||
for (int i : list)
|
||||
if (args[i].first.size() == 1 && args[i].first[0] == nullptr)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <int size>
|
||||
static bool enforce_size(const argument_list &args) {
|
||||
return args.size() == size;
|
||||
}
|
||||
|
||||
// Check all args are not complements except those listed
|
||||
template <int ...except>
|
||||
static bool check_complements(const argument_list &args) {
|
||||
initializer_list<int> list{except...};
|
||||
for (int i = 0; i < args.size(); ++i) {
|
||||
bool disallow = true;
|
||||
for (int e : list) {
|
||||
if (i == e) {
|
||||
disallow = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (disallow && args[i].second)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Tokenize and check argument count
|
||||
template <int size>
|
||||
static bool tokenize_and_check(char *stmt, argument_list &args) {
|
||||
return tokenize_string(stmt, args) == 0 &&
|
||||
enforce_size<size>(args) &&
|
||||
check_complements<>(args);
|
||||
}
|
||||
|
||||
#define sprint(...) off += ssprintf(buf + off, sizeof(buf) - off, __VA_ARGS__)
|
||||
|
||||
const char *as_str(const argument &arg) {
|
||||
size_t off = 0;
|
||||
static char buf[4096];
|
||||
|
||||
if (arg.second) {
|
||||
sprint("~");
|
||||
}
|
||||
sprint("{ ");
|
||||
for (const char *tok : arg.first) {
|
||||
if (tok == nullptr) {
|
||||
sprint("0x0000-0xFF00 ");
|
||||
} else {
|
||||
sprint("%s ", tok);
|
||||
}
|
||||
}
|
||||
sprint("}");
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
const char *as_str(const char *arg) { return arg ?: "*"; }
|
||||
|
||||
template <typename Func, typename ...Args>
|
||||
static void run_and_check(const Func &fn, const char *action, Args ...args) {
|
||||
if (!fn(args...)) {
|
||||
string s = "Error in: %s";
|
||||
for (int i = 0; i < sizeof...(args); ++i) s += " %s";
|
||||
s += "\n";
|
||||
LOGW(s.data(), action, as_str(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
#define run_fn(...) run_and_check(fn, action, __VA_ARGS__)
|
||||
|
||||
// Pattern 1: allow *{ source } *{ target } *{ class } *{ permission }
|
||||
template <typename Func>
|
||||
static bool parse_pattern_1(const Func &fn, const char *action, char *stmt) {
|
||||
argument_list args;
|
||||
if (!tokenize_and_check<4>(stmt, args))
|
||||
return false;
|
||||
for (auto src : args[0].first)
|
||||
for (auto tgt : args[1].first)
|
||||
for (auto cls : args[2].first)
|
||||
for (auto perm : args[3].first)
|
||||
run_fn(src, tgt, cls, perm);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pattern 2: allowxperm *{ source } *{ target } *{ class } ioctl xperm_set
|
||||
template <typename Func>
|
||||
static bool parse_pattern_2(const Func &fn, const char *action, char *stmt) {
|
||||
argument_list args;
|
||||
if (tokenize_string(stmt, args) != 0 ||
|
||||
!enforce_size<5>(args) ||
|
||||
!check_complements<4>(args) ||
|
||||
!enforce_single<3>(args) ||
|
||||
!enforce_non_null<3>(args) ||
|
||||
args[3].first[0] != "ioctl"sv)
|
||||
return false;
|
||||
for (auto src : args[0].first)
|
||||
for (auto tgt : args[1].first)
|
||||
for (auto cls : args[2].first)
|
||||
run_fn(src, tgt, cls, args[4]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pattern 3: permissive *{ type }
|
||||
template <typename Func>
|
||||
static bool parse_pattern_3(const Func &fn, const char *action, char *stmt) {
|
||||
argument_list args;
|
||||
if (!tokenize_and_check<1>(stmt, args))
|
||||
return false;
|
||||
for (auto type : args[0].first)
|
||||
run_fn(type);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pattern 4: typeattribute { type } { attribute }
|
||||
template <typename Func>
|
||||
static bool parse_pattern_4(const Func &fn, const char *action, char *stmt) {
|
||||
argument_list args;
|
||||
if (!tokenize_and_check<2>(stmt, args) || !enforce_non_null<0, 1>(args))
|
||||
return false;
|
||||
for (auto type : args[0].first)
|
||||
for (auto attr : args[1].first)
|
||||
run_fn(type, attr);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pattern 5: type name { attribute }
|
||||
template <typename Func>
|
||||
static bool parse_pattern_5(const Func &fn, const char *action, char *stmt) {
|
||||
argument_list args;
|
||||
string tmp_str;
|
||||
if (tokenize_string(stmt, args) != 0 || !enforce_single<0>(args))
|
||||
return false;
|
||||
if (args.size() == 1) {
|
||||
args.emplace_back(make_pair(initializer_list<const char*>{ "domain" }, false));
|
||||
}
|
||||
if (!enforce_size<2>(args) || !enforce_non_null<1>(args) || !check_complements<>(args))
|
||||
return false;
|
||||
for (auto attr : args[1].first)
|
||||
run_fn(args[0].first[0], attr);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pattern 6: attribute name
|
||||
template <typename Func>
|
||||
static bool parse_pattern_6(const Func &fn, const char *action, char *stmt) {
|
||||
argument_list args;
|
||||
if (!tokenize_and_check<1>(stmt, args) || !enforce_single<0>(args))
|
||||
return false;
|
||||
run_fn(args[0].first[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pattern 7: type_transition source target class default (filename)
|
||||
template <typename Func>
|
||||
static bool parse_pattern_7(const Func &fn, const char *action, char *stmt) {
|
||||
argument_list args;
|
||||
if (tokenize_string(stmt, args) != 0)
|
||||
return false;
|
||||
if (args.size() == 4)
|
||||
args.emplace_back(make_pair(initializer_list<const char*>{ nullptr }, false));
|
||||
if (!enforce_size<5>(args) ||
|
||||
!enforce_non_null<0, 1, 2, 3>(args) ||
|
||||
!enforce_single<0, 1, 2, 3, 4>(args) ||
|
||||
!check_complements<>(args))
|
||||
return false;
|
||||
run_fn(args[0].first[0], args[1].first[0], args[2].first[0],
|
||||
args[3].first[0], args[4].first[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pattern 8: type_change source target class default
|
||||
template <typename Func>
|
||||
static bool parse_pattern_8(const Func &fn, const char *action, char *stmt) {
|
||||
argument_list args;
|
||||
if (!tokenize_and_check<4>(stmt, args) || !enforce_single<0, 1, 2, 3>(args))
|
||||
return false;
|
||||
run_fn(args[0].first[0], args[1].first[0], args[2].first[0], args[3].first[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pattern 9: genfscon name path context
|
||||
template <typename Func>
|
||||
static bool parse_pattern_9(const Func &fn, const char *action, char *stmt) {
|
||||
argument_list args;
|
||||
if (!tokenize_and_check<3>(stmt, args) || !enforce_single<0, 1, 2>(args))
|
||||
return false;
|
||||
run_fn(args[0].first[0], args[1].first[0], args[2].first[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
#define add_action_func(name, type, fn) \
|
||||
else if (strcmp(name, action) == 0) { \
|
||||
auto __fn = [&](auto && ...args){ return (fn)(args...); }; \
|
||||
if (!parse_pattern_##type(__fn, name, remain)) \
|
||||
LOGW("Syntax error in '%.*s'\n\n%s\n", (int) stmt.length(), stmt.data(), type_msg_##type); \
|
||||
}
|
||||
|
||||
#define add_action(act, type) add_action_func(#act, type, act)
|
||||
|
||||
void sepolicy::parse_statement(rust::Str stmt) {
|
||||
if (stmt.empty()) return;
|
||||
// strtok modify strings, create a copy
|
||||
string cpy(stmt.data(), stmt.length());
|
||||
|
||||
char *remain;
|
||||
char *action = strtok_r(cpy.data(), " ", &remain);
|
||||
if (remain == nullptr) {
|
||||
LOGW("Syntax error in '%.*s'\n\n", (int) stmt.length(), stmt.data());
|
||||
return;
|
||||
}
|
||||
|
||||
if (0) {}
|
||||
add_action(allow, 1)
|
||||
add_action(deny, 1)
|
||||
add_action(auditallow, 1)
|
||||
add_action(dontaudit, 1)
|
||||
add_action(allowxperm, 2)
|
||||
add_action(auditallowxperm, 2)
|
||||
add_action(dontauditxperm, 2)
|
||||
add_action(permissive, 3)
|
||||
add_action(enforce, 3)
|
||||
add_action(typeattribute, 4)
|
||||
add_action(type, 5)
|
||||
add_action(attribute, 6)
|
||||
add_action(type_transition, 7)
|
||||
add_action(type_change, 8)
|
||||
add_action(type_member, 8)
|
||||
add_action(genfscon, 9)
|
||||
|
||||
// Backwards compatible syntax
|
||||
add_action(create, 3)
|
||||
add_action_func("attradd", 4, typeattribute)
|
||||
add_action_func("name_transition", 7, type_transition)
|
||||
|
||||
else { LOGW("Unknown action: '%s'\n\n", action); }
|
||||
}
|
||||
|
||||
// Parsing is hard, the following is a test suite to ensure correctness
|
||||
|
||||
static void test_parse_stmt(const char *stmt, const char *expect, int code = 0) {
|
||||
auto print_error = [](int code) {
|
||||
switch (code) {
|
||||
case -1:
|
||||
fprintf(stderr, "unclosed bracket\n");
|
||||
break;
|
||||
case -2:
|
||||
fprintf(stderr, "nested brackets\n");
|
||||
break;
|
||||
case -3:
|
||||
fprintf(stderr, "double complement\n");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
char cpy[4096];
|
||||
strncpy(cpy, stmt, sizeof(cpy));
|
||||
argument_list args;
|
||||
int result = tokenize_string(cpy, args);
|
||||
if (result != 0) {
|
||||
if (expect != nullptr) {
|
||||
fprintf(stderr, "Parsing: '%s' with unexpected error: ", stmt);
|
||||
print_error(result);
|
||||
exit(1);
|
||||
} else {
|
||||
if (code != result) {
|
||||
fprintf(stderr, "Parsing: '%s'\n", stmt);
|
||||
fprintf(stderr, "Expect error: ");
|
||||
print_error(code);
|
||||
fprintf(stderr, "Result error: ");
|
||||
print_error(result);
|
||||
exit(1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
char buf[4096];
|
||||
size_t off = 0;
|
||||
for (const auto &arg : args) {
|
||||
sprint("(%d,[", arg.second);
|
||||
bool first = true;
|
||||
for (const char *tok : arg.first) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
sprint(",");
|
||||
}
|
||||
sprint("'%s'", tok ?: "(null)");
|
||||
}
|
||||
sprint("])\n");
|
||||
}
|
||||
if (strncmp(buf, expect, sizeof(buf)) != 0) {
|
||||
fprintf(stderr, "Parsing: '%s'\n", stmt);
|
||||
fprintf(stderr, "Expect:\n%s", expect);
|
||||
fprintf(stderr, "-------------------\n");
|
||||
fprintf(stderr, "Result:\n%s", buf);
|
||||
fprintf(stderr, "-------------------\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void assert_msg(bool b, const char *msg) {
|
||||
if (!b) {
|
||||
fprintf(stderr, "Assertion failed: %s\n", msg);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
void test_parse_statements() {
|
||||
// Test parsing correctness
|
||||
test_parse_stmt("a b c",
|
||||
"(0,['a'])\n(0,['b'])\n(0,['c'])\n");
|
||||
test_parse_stmt(" a b {c}",
|
||||
"(0,['a'])\n(0,['b'])\n(0,['c'])\n");
|
||||
test_parse_stmt("a b{c }",
|
||||
"(0,['a'])\n(0,['b'])\n(0,['c'])\n");
|
||||
test_parse_stmt("a b{c}d",
|
||||
"(0,['a'])\n(0,['b'])\n(0,['c'])\n(0,['d'])\n");
|
||||
test_parse_stmt("a b { c d }",
|
||||
"(0,['a'])\n(0,['b'])\n(0,['c','d'])\n");
|
||||
test_parse_stmt("a {b} ~c",
|
||||
"(0,['a'])\n(0,['b'])\n(1,['c'])\n");
|
||||
test_parse_stmt("a b ~ { c d }",
|
||||
"(0,['a'])\n(0,['b'])\n(1,['c','d'])\n");
|
||||
test_parse_stmt("a b *",
|
||||
"(0,['a'])\n(0,['b'])\n(0,['(null)'])\n");
|
||||
test_parse_stmt("a b { c * }",
|
||||
"(0,['a'])\n(0,['b'])\n(0,['(null)'])\n");
|
||||
|
||||
// Invalid syntax tests
|
||||
test_parse_stmt("a b { c", nullptr, -1);
|
||||
test_parse_stmt("a {b}}c}", nullptr, -1);
|
||||
test_parse_stmt("a }b{c}", nullptr, -1);
|
||||
test_parse_stmt("{a} b } {c}", nullptr, -1);
|
||||
test_parse_stmt("a { b } { c", nullptr, -1);
|
||||
test_parse_stmt("a b {{ c }}", nullptr, -2);
|
||||
test_parse_stmt("a b ~ ~c", nullptr, -3);
|
||||
test_parse_stmt("a b ~~c", nullptr, -3);
|
||||
|
||||
// Test enforcement functions
|
||||
string s = "a * { b c } { d e } ~f *";
|
||||
argument_list args;
|
||||
assert_msg(tokenize_string(s.data(), args) == 0, "parse failure");
|
||||
assert_msg(enforce_size<6>(args), "size != 6");
|
||||
assert_msg(enforce_non_null<0, 2, 3, 4>(args), "non-null enforcement failed");
|
||||
assert_msg(!enforce_non_null<0, 1, 2, 3, 4>(args), "non-null enforcement should fail");
|
||||
assert_msg(enforce_single<0, 1, 4, 5>(args), "single enforcement check failed");
|
||||
assert_msg(!enforce_single<0, 1, 2>(args), "single enforcement check should fail");
|
||||
assert_msg(check_complements<4>(args), "check complements failed");
|
||||
assert_msg(!check_complements<>(args), "check complements should fail");
|
||||
}
|
||||
|
349
native/src/sepolicy/statement.rs
Normal file
349
native/src/sepolicy/statement.rs
Normal file
@ -0,0 +1,349 @@
|
||||
use crate::ffi::Xperm;
|
||||
use crate::sepolicy;
|
||||
use base::{LoggedError, LoggedResult};
|
||||
use std::{iter::Peekable, pin::Pin, vec::IntoIter};
|
||||
|
||||
pub enum Token<'a> {
|
||||
AL,
|
||||
DN,
|
||||
AA,
|
||||
DA,
|
||||
AX,
|
||||
AY,
|
||||
DX,
|
||||
PM,
|
||||
EF,
|
||||
TA,
|
||||
TY,
|
||||
AT,
|
||||
TT,
|
||||
TC,
|
||||
TM,
|
||||
GF,
|
||||
IO,
|
||||
LB,
|
||||
RB,
|
||||
CM,
|
||||
ST,
|
||||
TL,
|
||||
HP,
|
||||
HX(u16),
|
||||
ID(&'a str),
|
||||
}
|
||||
|
||||
type Tokens<'a> = Peekable<IntoIter<Token<'a>>>;
|
||||
|
||||
macro_rules! throw {
|
||||
() => {
|
||||
Err(LoggedError::default())?
|
||||
};
|
||||
}
|
||||
|
||||
fn parse_id<'a>(tokens: &mut Tokens<'a>) -> LoggedResult<&'a str> {
|
||||
match tokens.next() {
|
||||
Some(Token::ID(name)) => Ok(name),
|
||||
_ => throw!(),
|
||||
}
|
||||
}
|
||||
|
||||
// names ::= ID(n) { vec![n] };
|
||||
// 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>> {
|
||||
match tokens.next() {
|
||||
Some(Token::ID(name)) => return Ok(vec![name]),
|
||||
Some(Token::LB) => {
|
||||
let mut names = Vec::new();
|
||||
loop {
|
||||
names.push(parse_id(tokens)?);
|
||||
match tokens.peek() {
|
||||
Some(Token::CM) => {
|
||||
tokens.next();
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
if matches!(tokens.next(), Some(Token::RB)) {
|
||||
return Ok(names);
|
||||
}
|
||||
throw!()
|
||||
}
|
||||
_ => throw!(),
|
||||
}
|
||||
}
|
||||
|
||||
// terms ::= ST { vec![] }
|
||||
// terms ::= term(n) { n }
|
||||
fn parse_terms<'a>(tokens: &mut Tokens<'a>) -> LoggedResult<Vec<&'a str>> {
|
||||
match tokens.peek() {
|
||||
Some(Token::ST) => {
|
||||
tokens.next();
|
||||
Ok(Vec::new())
|
||||
}
|
||||
_ => parse_term(tokens),
|
||||
}
|
||||
}
|
||||
|
||||
// xperm ::= HX(low) { Xperm{low, high: 0, reset: false} };
|
||||
// xperm ::= HX(low) HP HX(high) { Xperm{low, high, reset: false} };
|
||||
fn parse_xperm<'a>(tokens: &mut Tokens<'a>) -> LoggedResult<Xperm> {
|
||||
let low = match tokens.next() {
|
||||
Some(Token::HX(low)) => low,
|
||||
_ => throw!(),
|
||||
};
|
||||
let high = match tokens.peek() {
|
||||
Some(Token::HP) => {
|
||||
tokens.next();
|
||||
match tokens.next() {
|
||||
Some(Token::HX(high)) => high,
|
||||
_ => throw!(),
|
||||
}
|
||||
}
|
||||
_ => 0,
|
||||
};
|
||||
Ok(Xperm {
|
||||
low,
|
||||
high,
|
||||
reset: false,
|
||||
})
|
||||
}
|
||||
|
||||
// xperms ::= HX(low) { if low > 0 { vec![Xperm{low, high: 0, reset: false}] } else { vec![Xperm{low: 0x0000, high: 0xFFFF, reset: true}] }};
|
||||
// xperms ::= LB xperm_list(l) RB { l };
|
||||
// xperms ::= TL LB xperm_list(mut l) RB { l.iter_mut().for_each(|x| { x.reset = true; }); l };
|
||||
// xperms ::= ST { vec![Xperm{low: 0x0000, high: 0xFFFF, reset: false}] };
|
||||
//
|
||||
// xperm_list ::= xperm(p) { vec![p] }
|
||||
// xperm_list ::= xperm_list(mut l) xperm(p) { l.push(p); l }
|
||||
fn parse_xperms<'a>(tokens: &mut Tokens<'a>) -> LoggedResult<Vec<Xperm>> {
|
||||
let mut xperms = Vec::new();
|
||||
let reset = match tokens.peek() {
|
||||
Some(Token::TL) => {
|
||||
tokens.next();
|
||||
if !matches!(tokens.peek(), Some(Token::LB)) {
|
||||
throw!();
|
||||
}
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
match tokens.next() {
|
||||
Some(Token::LB) => {
|
||||
// parse xperm_list
|
||||
loop {
|
||||
let mut xperm = parse_xperm(tokens)?;
|
||||
xperm.reset = reset;
|
||||
xperms.push(xperm);
|
||||
if matches!(tokens.peek(), Some(Token::RB)) {
|
||||
tokens.next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(Token::ST) => {
|
||||
xperms.push(Xperm {
|
||||
low: 0x0000,
|
||||
high: 0xFFFF,
|
||||
reset,
|
||||
});
|
||||
}
|
||||
Some(Token::HX(low)) => {
|
||||
if low > 0 {
|
||||
xperms.push(Xperm {
|
||||
low,
|
||||
high: 0,
|
||||
reset,
|
||||
});
|
||||
} else {
|
||||
xperms.push(Xperm {
|
||||
low: 0x0000,
|
||||
high: 0xFFFF,
|
||||
reset,
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => throw!(),
|
||||
}
|
||||
Ok(xperms)
|
||||
}
|
||||
|
||||
// statement ::= AL sterm(s) sterm(t) sterm(c) sterm(p) { extra.as_mut().allow(s, t, c, p); };
|
||||
// statement ::= DN sterm(s) sterm(t) sterm(c) sterm(p) { extra.as_mut().deny(s, t, c, p); };
|
||||
// statement ::= AA sterm(s) sterm(t) sterm(c) sterm(p) { extra.as_mut().auditallow(s, t, c, p); };
|
||||
// statement ::= DA sterm(s) sterm(t) sterm(c) sterm(p) { extra.as_mut().dontaudit(s, t, c, p); };
|
||||
// statement ::= AX sterm(s) sterm(t) sterm(c) IO xperms(p) { extra.as_mut().allowxperm(s, t, c, p); };
|
||||
// statement ::= AY sterm(s) sterm(t) sterm(c) IO xperms(p) { extra.as_mut().auditallowxperm(s, t, c, p); };
|
||||
// statement ::= DX sterm(s) sterm(t) sterm(c) IO xperms(p) { extra.as_mut().dontauditxperm(s, t, c, p); };
|
||||
// statement ::= PM sterm(t) { extra.as_mut().permissive(t); };
|
||||
// statement ::= EF sterm(t) { extra.as_mut().enforce(t); };
|
||||
// statement ::= TA term(t) term(a) { extra.as_mut().typeattribute(t, a); };
|
||||
// statement ::= TY ID(t) { extra.as_mut().type_(t, vec![]);};
|
||||
// statement ::= TY ID(t) term(a) { extra.as_mut().type_(t, a);};
|
||||
// statement ::= AT ID(t) { extra.as_mut().attribute(t); };
|
||||
// statement ::= TT ID(s) ID(t) ID(c) ID(d) { extra.as_mut().type_transition(s, t, c, d, vec![]); };
|
||||
// statement ::= TT ID(s) ID(t) ID(c) ID(d) ID(o) { extra.as_mut().type_transition(s, t, c, d, vec![o]); };
|
||||
// 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<'a>(sepolicy: Pin<&mut sepolicy>, tokens: &mut Tokens<'a>) -> LoggedResult<()> {
|
||||
let action = match tokens.next() {
|
||||
Some(token) => token,
|
||||
_ => throw!(),
|
||||
};
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
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!()
|
||||
};
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
Token::PM | Token::EF => {
|
||||
let t = parse_terms(tokens)?;
|
||||
match action {
|
||||
Token::PM => sepolicy.permissive(t),
|
||||
Token::EF => sepolicy.enforce(t),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
Token::TA => {
|
||||
let t = parse_term(tokens)?;
|
||||
let a = parse_term(tokens)?;
|
||||
sepolicy.typeattribute(t, a)
|
||||
}
|
||||
Token::TY => {
|
||||
let t = parse_id(tokens)?;
|
||||
let a = if matches!(tokens.peek(), None) {
|
||||
vec![]
|
||||
} else {
|
||||
parse_term(tokens)?
|
||||
};
|
||||
sepolicy.type_(t, a)
|
||||
}
|
||||
Token::AT => {
|
||||
let t = parse_id(tokens)?;
|
||||
sepolicy.attribute(t)
|
||||
}
|
||||
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 matches!(tokens.peek(), None) {
|
||||
vec![]
|
||||
} else {
|
||||
vec![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),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
Token::GF => {
|
||||
let s = parse_id(tokens)?;
|
||||
let t = parse_id(tokens)?;
|
||||
let c = parse_id(tokens)?;
|
||||
sepolicy.genfscon(s, t, c)
|
||||
}
|
||||
_ => throw!(),
|
||||
}
|
||||
if matches!(tokens.peek(), None) {
|
||||
Ok(())
|
||||
} else {
|
||||
throw!()
|
||||
}
|
||||
}
|
||||
|
||||
fn tokenize_statement<'a>(statement: &'a str) -> Vec<Token<'a>> {
|
||||
let mut tokens = Vec::new();
|
||||
for s in statement.trim().split_whitespace() {
|
||||
let mut res = Some(s);
|
||||
while let Some(s) = res {
|
||||
match s {
|
||||
"allow" => tokens.push(Token::AL),
|
||||
"deny" => tokens.push(Token::DN),
|
||||
"auditallow" => tokens.push(Token::AA),
|
||||
"dontaudit" => tokens.push(Token::DA),
|
||||
"allowxperm" => tokens.push(Token::AX),
|
||||
"auditallowxperm" => tokens.push(Token::AY),
|
||||
"dontauditxperm" => tokens.push(Token::DX),
|
||||
"permissive" => tokens.push(Token::PM),
|
||||
"enforce" => tokens.push(Token::EF),
|
||||
"typeattribute" => tokens.push(Token::TA),
|
||||
"type" => tokens.push(Token::TY),
|
||||
"attribute" => tokens.push(Token::AT),
|
||||
"type_transition" => tokens.push(Token::TT),
|
||||
"type_change" => tokens.push(Token::TC),
|
||||
"type_member" => tokens.push(Token::TM),
|
||||
"genfscon" => tokens.push(Token::GF),
|
||||
"ioctl" => tokens.push(Token::IO),
|
||||
"" => {}
|
||||
_ => {
|
||||
if let Some(s) = s.strip_prefix("{") {
|
||||
tokens.push(Token::LB);
|
||||
res = Some(s);
|
||||
continue;
|
||||
} else if let Some(s) = s.strip_prefix("}") {
|
||||
tokens.push(Token::RB);
|
||||
res = Some(s);
|
||||
continue;
|
||||
} else if let Some(s) = s.strip_prefix(",") {
|
||||
tokens.push(Token::CM);
|
||||
res = Some(s);
|
||||
continue;
|
||||
} else if let Some(s) = s.strip_prefix("*") {
|
||||
tokens.push(Token::ST);
|
||||
res = Some(s);
|
||||
continue;
|
||||
} else if let Some(s) = s.strip_prefix("~") {
|
||||
res = Some(s);
|
||||
tokens.push(Token::TL);
|
||||
continue;
|
||||
} else if let Some(s) = s.strip_prefix("-") {
|
||||
res = Some(s);
|
||||
tokens.push(Token::HP);
|
||||
continue;
|
||||
} else if let Some(s) = s.strip_prefix("0x") {
|
||||
tokens.push(Token::HX(s.parse().unwrap_or(0)));
|
||||
} else {
|
||||
tokens.push(Token::ID(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
tokens
|
||||
}
|
||||
|
||||
pub fn parse_statement<'a>(sepolicy: Pin<&mut sepolicy>, statement: &'a str) -> LoggedResult<()> {
|
||||
let mut tokens = tokenize_statement(statement).into_iter().peekable();
|
||||
exec_statement(sepolicy, &mut tokens)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user