ksud: sepolicy support

This commit is contained in:
tiann
2023-01-31 18:47:15 +08:00
parent 2a88cca50c
commit 3413f4a4fe
6 changed files with 773 additions and 4 deletions

View File

@@ -316,6 +316,17 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "derive-new"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.10.6" version = "0.10.6"
@@ -593,6 +604,7 @@ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
"const_format", "const_format",
"derive-new",
"encoding", "encoding",
"env_logger", "env_logger",
"extattr", "extattr",
@@ -602,6 +614,7 @@ dependencies = [
"jwalk", "jwalk",
"libc", "libc",
"log", "log",
"nom",
"regex", "regex",
"retry", "retry",
"serde", "serde",

View File

@@ -27,6 +27,8 @@ android-properties = { version = "0.2.2", features = ["bionic-deprecated"] }
extattr = "1.0.0" extattr = "1.0.0"
jwalk = "0.8.1" jwalk = "0.8.1"
is_executable = "1.0.1" is_executable = "1.0.1"
nom = "7"
derive-new = "0.5"
[profile.release] [profile.release]
strip = true strip = true

View File

@@ -35,7 +35,10 @@ enum Commands {
Install, Install,
/// SELinux policy Patch tool /// SELinux policy Patch tool
Sepolicy, Sepolicy {
#[command(subcommand)]
command: Sepolicy,
},
/// For developers /// For developers
Debug { Debug {
@@ -68,6 +71,21 @@ enum Debug {
Test, Test,
} }
#[derive(clap::Subcommand, Debug)]
enum Sepolicy {
/// Patch sepolicy
Patch {
/// sepolicy statements
sepolicy: String,
},
/// Apply sepolicy from file
Apply {
/// sepolicy file path
file: String,
},
}
#[derive(clap::Subcommand, Debug)] #[derive(clap::Subcommand, Debug)]
enum Module { enum Module {
/// Install module <ZIP> /// Install module <ZIP>
@@ -118,7 +136,10 @@ pub fn run() -> Result<()> {
} }
} }
Commands::Install => event::install(), Commands::Install => event::install(),
Commands::Sepolicy => todo!(), Commands::Sepolicy { command } => match command {
Sepolicy::Patch { sepolicy } => crate::sepolicy::live_patch(&sepolicy),
Sepolicy::Apply { file } => crate::sepolicy::apply_file(&file),
},
Commands::Services => event::on_services(), Commands::Services => event::on_services(),
Commands::Debug { command } => match command { Commands::Debug { command } => match command {
@@ -133,7 +154,7 @@ pub fn run() -> Result<()> {
Ok(()) Ok(())
} }
Debug::Su => crate::ksu::grant_root(), Debug::Su => crate::ksu::grant_root(),
Debug::Test => todo!(), Debug::Test => todo!()
}, },
}; };

View File

@@ -3,7 +3,7 @@
use anyhow::{ensure, Result}; use anyhow::{ensure, Result};
use std::os::unix::process::CommandExt; use std::os::unix::process::CommandExt;
const KERNEL_SU_OPTION: u32 = 0xDEADBEEF; pub const KERNEL_SU_OPTION: u32 = 0xDEADBEEF;
const CMD_GRANT_ROOT: u64 = 0; const CMD_GRANT_ROOT: u64 = 0;
// const CMD_BECOME_MANAGER: u64 = 1; // const CMD_BECOME_MANAGER: u64 = 1;
@@ -13,6 +13,7 @@ const CMD_GET_VERSION: u64 = 2;
// const CMD_GET_ALLOW_LIST: u64 = 5; // const CMD_GET_ALLOW_LIST: u64 = 5;
// const CMD_GET_DENY_LIST: u64 = 6; // const CMD_GET_DENY_LIST: u64 = 6;
const CMD_REPORT_EVENT: u64 = 7; const CMD_REPORT_EVENT: u64 = 7;
pub const CMD_SET_SEPOLICY: u64 = 8;
const EVENT_POST_FS_DATA: u64 = 1; const EVENT_POST_FS_DATA: u64 = 1;
const EVENT_BOOT_COMPLETED: u64 = 2; const EVENT_BOOT_COMPLETED: u64 = 2;

View File

@@ -7,6 +7,7 @@ mod ksu;
mod module; mod module;
mod restorecon; mod restorecon;
mod utils; mod utils;
mod sepolicy;
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
cli::run() cli::run()

View File

@@ -0,0 +1,731 @@
use anyhow::Result;
use derive_new::new;
use nom::{
branch::alt,
bytes::complete::{tag, take_while, take_while1, take_while_m_n},
character::{
complete::{space0, space1},
is_alphanumeric,
},
combinator::map,
sequence::Tuple,
IResult, Parser,
};
use std::vec;
type SeObject<'a> = Vec<&'a str>;
fn is_sepolicy_char(c: char) -> bool {
is_alphanumeric(c as u8) || c == '_' || c == '-'
}
fn parse_single_word(input: &str) -> IResult<&str, &str> {
take_while1(is_sepolicy_char).parse(input)
}
fn parse_bracket_objs(input: &str) -> IResult<&str, SeObject> {
let (input, (_, words, _)) = (
tag("{"),
take_while_m_n(1, 100, |c: char| is_sepolicy_char(c) || c.is_whitespace()),
tag("}"),
)
.parse(input)?;
Ok((input, words.split_whitespace().collect()))
}
fn parse_single_obj(input: &str) -> IResult<&str, SeObject> {
let (input, word) = take_while1(is_sepolicy_char).parse(input)?;
Ok((input, vec![word]))
}
fn parse_star(input: &str) -> IResult<&str, SeObject> {
let (input, _) = tag("*").parse(input)?;
Ok((input, vec!["*"]))
}
// 1. a single sepolicy word
// 2. { obj1 obj2 obj3 ...}
// 3. *
fn parse_seobj(input: &str) -> IResult<&str, SeObject> {
let (input, strs) = alt((parse_single_obj, parse_bracket_objs, parse_star)).parse(input)?;
Ok((input, strs))
}
fn parse_seobj_no_star(input: &str) -> IResult<&str, SeObject> {
let (input, strs) = alt((parse_single_obj, parse_bracket_objs)).parse(input)?;
Ok((input, strs))
}
trait SeObjectParser<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self>
where
Self: Sized;
}
#[derive(Debug, PartialEq, Eq, new)]
struct NormalPerm<'a> {
op: &'a str,
source: SeObject<'a>,
target: SeObject<'a>,
class: SeObject<'a>,
perm: SeObject<'a>,
}
#[derive(Debug, PartialEq, Eq, new)]
struct XPerm<'a> {
op: &'a str,
source: SeObject<'a>,
target: SeObject<'a>,
class: SeObject<'a>,
operation: &'a str,
perm_set: &'a str,
}
#[derive(Debug, PartialEq, Eq, new)]
struct TypeState<'a> {
op: &'a str,
stype: SeObject<'a>,
}
#[derive(Debug, PartialEq, Eq, new)]
struct TypeAttr<'a> {
stype: SeObject<'a>,
sattr: SeObject<'a>,
}
#[derive(Debug, PartialEq, Eq, new)]
struct Type<'a> {
name: &'a str,
attrs: SeObject<'a>,
}
#[derive(Debug, PartialEq, Eq, new)]
struct Attr<'a> {
name: &'a str,
}
#[derive(Debug, PartialEq, Eq, new)]
struct TypeTransition<'a> {
source: &'a str,
target: &'a str,
class: &'a str,
default_type: &'a str,
object_name: Option<&'a str>,
}
#[derive(Debug, PartialEq, Eq, new)]
struct TypeChange<'a> {
op: &'a str,
source: &'a str,
target: &'a str,
class: &'a str,
default_type: &'a str,
}
#[derive(Debug, PartialEq, Eq, new)]
struct GenFsCon<'a> {
fs_name: &'a str,
partial_path: &'a str,
fs_context: &'a str,
}
#[derive(Debug)]
enum PolicyStatement<'a> {
// "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"
NormalPerm(NormalPerm<'a>),
// "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"
XPerm(XPerm<'a>),
// "permissive ^type"
// "enforce ^type"
TypeState(TypeState<'a>),
// "type type_name ^(attribute)"
Type(Type<'a>),
// "typeattribute ^type ^attribute"
TypeAttr(TypeAttr<'a>),
// "attribute ^attribute"
Attr(Attr<'a>),
// "type_transition source_type target_type class default_type (object_name)"
TypeTransition(TypeTransition<'a>),
// "type_change source_type target_type class default_type"
// "type_member source_type target_type class default_type"
TypeChange(TypeChange<'a>),
// "genfscon fs_name partial_path fs_context"
GenFsCon(GenFsCon<'a>),
}
impl<'a> SeObjectParser<'a> for NormalPerm<'a> {
fn parse(input: &'a str) -> IResult<&str, Self> {
let (input, op) = alt((
tag("allow"),
tag("deny"),
tag("auditallow"),
tag("dontaudit"),
))(input)?;
let (input, _) = space0(input)?;
let (input, source) = parse_seobj(input)?;
let (input, _) = space0(input)?;
let (input, target) = parse_seobj(input)?;
let (input, _) = space0(input)?;
let (input, class) = parse_seobj(input)?;
let (input, _) = space0(input)?;
let (input, perm) = parse_seobj(input)?;
Ok((input, NormalPerm::new(op, source, target, class, perm)))
}
}
impl<'a> SeObjectParser<'a> for XPerm<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (input, op) = alt((
tag("allowxperm"),
tag("auditallowxperm"),
tag("dontauditxperm"),
))(input)?;
let (input, _) = space0(input)?;
let (input, source) = parse_seobj(input)?;
let (input, _) = space0(input)?;
let (input, target) = parse_seobj(input)?;
let (input, _) = space0(input)?;
let (input, class) = parse_seobj(input)?;
let (input, _) = space0(input)?;
let (input, operation) = parse_single_word(input)?;
let (input, _) = space0(input)?;
let (input, perm_set) = parse_single_word(input)?;
Ok((
input,
XPerm::new(op, source, target, class, operation, perm_set),
))
}
}
impl<'a> SeObjectParser<'a> for TypeState<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (input, op) = alt((tag("permissive"), tag("enforce")))(input)?;
let (input, _) = space1(input)?;
let (input, stype) = parse_seobj_no_star(input)?;
Ok((input, TypeState::new(op, stype)))
}
}
impl<'a> SeObjectParser<'a> for Type<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (input, _) = tag("type")(input)?;
let (input, _) = space1(input)?;
let (input, name) = parse_single_word(input)?;
if input.is_empty() {
return Ok((input, Type::new(name, vec!["domain"]))); // default to domain
}
let (input, _) = space1(input)?;
let (input, attrs) = parse_seobj_no_star(input)?;
Ok((input, Type::new(name, attrs)))
}
}
impl<'a> SeObjectParser<'a> for TypeAttr<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (input, _) = alt((tag("typeattribute"), tag("attradd")))(input)?;
let (input, _) = space1(input)?;
let (input, stype) = parse_seobj_no_star(input)?;
let (input, _) = space1(input)?;
let (input, attr) = parse_seobj_no_star(input)?;
Ok((input, TypeAttr::new(stype, attr)))
}
}
impl<'a> SeObjectParser<'a> for Attr<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (input, _) = tag("attribute")(input)?;
let (input, _) = space1(input)?;
let (input, attr) = parse_single_word(input)?;
Ok((input, Attr::new(attr)))
}
}
impl<'a> SeObjectParser<'a> for TypeTransition<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (input, _) = alt((tag("type_transition"), tag("name_transition")))(input)?;
let (input, _) = space1(input)?;
let (input, source) = parse_single_word(input)?;
let (input, _) = space1(input)?;
let (input, target) = parse_single_word(input)?;
let (input, _) = space1(input)?;
let (input, class) = parse_single_word(input)?;
let (input, _) = space1(input)?;
let (input, default) = parse_single_word(input)?;
if input.is_empty() {
return Ok((
input,
TypeTransition::new(source, target, class, default, None),
));
}
let (input, _) = space1(input)?;
let (input, object) = parse_single_word(input)?;
Ok((
input,
TypeTransition::new(source, target, class, default, Some(object)),
))
}
}
impl<'a> SeObjectParser<'a> for TypeChange<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (input, op) = alt((tag("type_change"), tag("type_member")))(input)?;
let (input, _) = space1(input)?;
let (input, source) = parse_single_word(input)?;
let (input, _) = space1(input)?;
let (input, target) = parse_single_word(input)?;
let (input, _) = space1(input)?;
let (input, class) = parse_single_word(input)?;
let (input, _) = space1(input)?;
let (input, default) = parse_single_word(input)?;
Ok((input, TypeChange::new(op, source, target, class, default)))
}
}
impl<'a> SeObjectParser<'a> for GenFsCon<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self>
where
Self: Sized,
{
let (input, _) = tag("genfscon")(input)?;
let (input, _) = space1(input)?;
let (input, fs) = parse_single_word(input)?;
let (input, _) = space1(input)?;
let (input, path) = parse_single_word(input)?;
let (input, _) = space1(input)?;
let (input, context) = parse_single_word(input)?;
Ok((input, GenFsCon::new(fs, path, context)))
}
}
impl<'a> PolicyStatement<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (input, _) = space0(input)?;
let (input, statement) = alt((
map(NormalPerm::parse, PolicyStatement::NormalPerm),
map(XPerm::parse, PolicyStatement::XPerm),
map(TypeState::parse, PolicyStatement::TypeState),
map(Type::parse, PolicyStatement::Type),
map(TypeAttr::parse, PolicyStatement::TypeAttr),
map(Attr::parse, PolicyStatement::Attr),
map(TypeTransition::parse, PolicyStatement::TypeTransition),
map(TypeChange::parse, PolicyStatement::TypeChange),
map(GenFsCon::parse, PolicyStatement::GenFsCon),
))(input)?;
let (input, _) = space0(input)?;
let (input, _) = take_while(|c| c == ';')(input)?;
let (input, _) = space0(input)?;
Ok((input, statement))
}
}
fn parse_sepolicy<'a, 'b>(input: &'b str) -> Result<Vec<PolicyStatement<'a>>>
where
'b: 'a,
{
let mut statements = vec![];
for line in input.split(['\n', ';']) {
if let Ok((_, statement)) = PolicyStatement::parse(line.trim()) {
statements.push(statement);
}
}
Ok(statements)
}
const SEPOLICY_MAX_LEN: usize = 128;
const CMD_NORMAL_PERM: u32 = 1;
const CMD_XPERM: u32 = 2;
const CMD_TYPE_STATE: u32 = 3;
const CMD_TYPE: u32 = 4;
const CMD_TYPE_ATTR: u32 = 5;
const CMD_ATTR: u32 = 6;
const CMD_TYPE_TRANSITION: u32 = 7;
const CMD_TYPE_CHANGE: u32 = 8;
const CMD_GENFSCON: u32 = 9;
#[derive(Debug)]
enum PolicyObject {
All, // for "*", stand for all objects, and is NULL in ffi
One([u8; SEPOLICY_MAX_LEN]),
None,
}
impl Default for PolicyObject {
fn default() -> Self {
PolicyObject::None
}
}
impl TryFrom<&str> for PolicyObject {
type Error = anyhow::Error;
fn try_from(s: &str) -> Result<Self> {
anyhow::ensure!(s.len() <= SEPOLICY_MAX_LEN, "policy object too long");
if s == "*" {
return Ok(PolicyObject::All);
}
let mut buf = [0u8; SEPOLICY_MAX_LEN];
buf[..s.len()].copy_from_slice(s.as_bytes());
Ok(PolicyObject::One(buf))
}
}
/// atomic statement, such as: allow domain1 domain2:file1 read;
/// normal statement would be expand to atomic statement, for example:
/// allow domain1 domain2:file1 { read write }; would be expand to two atomic statement
/// allow domain1 domain2:file1 read;allow domain1 domain2:file1 write;
#[derive(Debug, new)]
struct AtomicStatement {
cmd: u32,
subcmd: u32,
sepol1: PolicyObject,
sepol2: PolicyObject,
sepol3: PolicyObject,
sepol4: PolicyObject,
sepol5: PolicyObject,
sepol6: PolicyObject,
sepol7: PolicyObject,
}
impl<'a> TryFrom<&'a NormalPerm<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a NormalPerm<'a>) -> Result<Self> {
let mut result = vec![];
let subcmd = match perm.op {
"allow" => 1,
"deny" => 2,
"auditallow" => 3,
"dontaudit" => 4,
_ => 0,
};
for &s in &perm.source {
for &t in &perm.target {
for &c in &perm.class {
for &p in &perm.perm {
result.push(AtomicStatement {
cmd: CMD_NORMAL_PERM,
subcmd,
sepol1: s.try_into()?,
sepol2: t.try_into()?,
sepol3: c.try_into()?,
sepol4: p.try_into()?,
sepol5: PolicyObject::None,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
});
}
}
}
}
Ok(result)
}
}
impl<'a> TryFrom<&'a XPerm<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a XPerm<'a>) -> Result<Self> {
let mut result = vec![];
let subcmd = match perm.op {
"allowxperm" => 1,
"auditallowxperm" => 2,
"dontauditxperm" => 3,
_ => 0,
};
for &s in &perm.source {
for &t in &perm.target {
for &c in &perm.class {
result.push(AtomicStatement {
cmd: CMD_XPERM,
subcmd,
sepol1: s.try_into()?,
sepol2: t.try_into()?,
sepol3: c.try_into()?,
sepol4: perm.operation.try_into()?,
sepol5: perm.perm_set.try_into()?,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
});
}
}
}
Ok(result)
}
}
impl<'a> TryFrom<&'a TypeState<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a TypeState<'a>) -> Result<Self> {
let mut result = vec![];
let subcmd = match perm.op {
"permissive" => 1,
"enforcing" => 2,
_ => 0,
};
for &t in &perm.stype {
result.push(AtomicStatement {
cmd: CMD_TYPE_STATE,
subcmd,
sepol1: t.try_into()?,
sepol2: PolicyObject::None,
sepol3: PolicyObject::None,
sepol4: PolicyObject::None,
sepol5: PolicyObject::None,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
});
}
Ok(result)
}
}
impl<'a> TryFrom<&'a Type<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a Type<'a>) -> Result<Self> {
let mut result = vec![];
for &attr in &perm.attrs {
result.push(AtomicStatement {
cmd: CMD_TYPE,
subcmd: 0,
sepol1: perm.name.try_into()?,
sepol2: attr.try_into()?,
sepol3: PolicyObject::None,
sepol4: PolicyObject::None,
sepol5: PolicyObject::None,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
});
}
Ok(result)
}
}
impl<'a> TryFrom<&'a TypeAttr<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a TypeAttr<'a>) -> Result<Self> {
let mut result = vec![];
for &t in &perm.stype {
for &attr in &perm.sattr {
result.push(AtomicStatement {
cmd: CMD_TYPE_ATTR,
subcmd: 0,
sepol1: t.try_into()?,
sepol2: attr.try_into()?,
sepol3: PolicyObject::None,
sepol4: PolicyObject::None,
sepol5: PolicyObject::None,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
});
}
}
Ok(result)
}
}
impl<'a> TryFrom<&'a Attr<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a Attr<'a>) -> Result<Self> {
let mut result = vec![];
result.push(AtomicStatement {
cmd: CMD_ATTR,
subcmd: 0,
sepol1: perm.name.try_into()?,
sepol2: PolicyObject::None,
sepol3: PolicyObject::None,
sepol4: PolicyObject::None,
sepol5: PolicyObject::None,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
});
Ok(result)
}
}
impl<'a> TryFrom<&'a TypeTransition<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a TypeTransition<'a>) -> Result<Self> {
let mut result = vec![];
let obj = match perm.object_name {
Some(obj) => obj.try_into()?,
None => PolicyObject::None,
};
result.push(AtomicStatement {
cmd: CMD_TYPE_TRANSITION,
subcmd: 0,
sepol1: perm.source.try_into()?,
sepol2: perm.target.try_into()?,
sepol3: perm.class.try_into()?,
sepol4: perm.default_type.try_into()?,
sepol5: obj,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
});
Ok(result)
}
}
impl<'a> TryFrom<&'a TypeChange<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a TypeChange<'a>) -> Result<Self> {
let mut result = vec![];
let subcmd = match perm.op {
"type_change" => 1,
"type_member" => 2,
_ => 0,
};
result.push(AtomicStatement {
cmd: CMD_TYPE_CHANGE,
subcmd,
sepol1: perm.source.try_into()?,
sepol2: perm.target.try_into()?,
sepol3: perm.class.try_into()?,
sepol4: perm.default_type.try_into()?,
sepol5: PolicyObject::None,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
});
Ok(result)
}
}
impl<'a> TryFrom<&'a GenFsCon<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a GenFsCon<'a>) -> Result<Self> {
let result = vec![AtomicStatement {
cmd: CMD_GENFSCON,
subcmd: 0,
sepol1: perm.fs_name.try_into()?,
sepol2: perm.partial_path.try_into()?,
sepol3: perm.fs_context.try_into()?,
sepol4: PolicyObject::None,
sepol5: PolicyObject::None,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
}];
Ok(result)
}
}
impl<'a> TryFrom<&'a PolicyStatement<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(value: &'a PolicyStatement) -> Result<Self> {
match value {
PolicyStatement::NormalPerm(perm) => perm.try_into(),
PolicyStatement::XPerm(perm) => perm.try_into(),
PolicyStatement::TypeState(perm) => perm.try_into(),
PolicyStatement::Type(perm) => perm.try_into(),
PolicyStatement::TypeAttr(perm) => perm.try_into(),
PolicyStatement::Attr(perm) => perm.try_into(),
PolicyStatement::TypeTransition(perm) => perm.try_into(),
PolicyStatement::TypeChange(perm) => perm.try_into(),
PolicyStatement::GenFsCon(perm) => perm.try_into(),
}
}
}
////////////////////////////////////////////////////////////////
/// for C FFI to call kernel interface
///////////////////////////////////////////////////////////////
#[derive(Debug)]
#[repr(C)]
struct FfiPolicy {
cmd: u32,
subcmd: u32,
sepol1: *const libc::c_char,
sepol2: *const libc::c_char,
sepol3: *const libc::c_char,
sepol4: *const libc::c_char,
sepol5: *const libc::c_char,
sepol6: *const libc::c_char,
sepol7: *const libc::c_char,
}
fn to_c_ptr(pol: &PolicyObject) -> *const libc::c_char {
match pol {
PolicyObject::None => std::ptr::null(),
PolicyObject::All => std::ptr::null(),
PolicyObject::One(s) => s.as_ptr(),
}
}
impl From<AtomicStatement> for FfiPolicy {
fn from(policy: AtomicStatement) -> FfiPolicy {
FfiPolicy {
cmd: policy.cmd,
subcmd: policy.subcmd,
sepol1: to_c_ptr(&policy.sepol1),
sepol2: to_c_ptr(&policy.sepol2),
sepol3: to_c_ptr(&policy.sepol3),
sepol4: to_c_ptr(&policy.sepol4),
sepol5: to_c_ptr(&policy.sepol5),
sepol6: to_c_ptr(&policy.sepol6),
sepol7: to_c_ptr(&policy.sepol7),
}
}
}
fn apply_one_rule<'a>(statement: &'a PolicyStatement<'a>) -> Result<()> {
let policies: Vec<AtomicStatement> = statement.try_into()?;
for policy in policies {
let mut result: u32 = 0;
let cpolicy = FfiPolicy::from(policy);
unsafe {
libc::prctl(
crate::ksu::KERNEL_SU_OPTION as i32,
crate::ksu::CMD_SET_SEPOLICY,
0,
&cpolicy as *const _ as *const libc::c_void,
&mut result as *mut _ as *mut libc::c_void,
);
}
if result != crate::ksu::KERNEL_SU_OPTION {
log::warn!("apply rule failed: {result}");
}
}
Ok(())
}
pub fn live_patch(policy: &str) -> Result<()> {
let result = parse_sepolicy(policy.trim())?;
for statement in result {
println!("{statement:?}");
apply_one_rule(&statement)?;
}
Ok(())
}
pub fn apply_file(path: &str) -> Result<()> {
let input = std::fs::read_to_string(path)?;
live_patch(&input)
}