Parse rule files with Rust

This commit is contained in:
topjohnwu 2023-05-24 19:11:56 -07:00
parent 5a94ef9106
commit 18d0cedbe2
16 changed files with 176 additions and 93 deletions

View File

@ -176,7 +176,8 @@ LOCAL_SRC_FILES := \
sepolicy/sepolicy.cpp \ sepolicy/sepolicy.cpp \
sepolicy/rules.cpp \ sepolicy/rules.cpp \
sepolicy/policydb.cpp \ sepolicy/policydb.cpp \
sepolicy/statement.cpp sepolicy/statement.cpp \
sepolicy/policy-rs.cpp
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)
include src/Android-rs.mk include src/Android-rs.mk

46
native/src/Cargo.lock generated
View File

@ -83,7 +83,7 @@ dependencies = [
"codespan-reporting", "codespan-reporting",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.15", "syn 2.0.16",
] ]
[[package]] [[package]]
@ -96,7 +96,7 @@ version = "1.0.94"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.15", "syn 2.0.16",
] ]
[[package]] [[package]]
@ -168,9 +168,9 @@ dependencies = [
[[package]] [[package]]
name = "io-lifetimes" name = "io-lifetimes"
version = "1.0.10" version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi",
"libc", "libc",
@ -179,15 +179,15 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.142" version = "0.2.144"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.3.6" version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b64f40e5e03e0d54f03845c8197d0291253cdbedfb1cb46b13c2c117554a9f4c" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]] [[package]]
name = "log" name = "log"
@ -228,13 +228,17 @@ dependencies = [
"base", "base",
"cxx", "cxx",
"cxx-gen", "cxx-gen",
"magiskpolicy",
] ]
[[package]] [[package]]
name = "magiskpolicy" name = "magiskpolicy"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"anyhow",
"base", "base",
"cxx",
"cxx-gen",
] ]
[[package]] [[package]]
@ -331,9 +335,9 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.26" version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -349,9 +353,9 @@ dependencies = [
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.8.1" version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" checksum = "d1a59b5d8e97dee33696bf13c5ba8ab85341c002922fba050069326b9c498974"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -360,15 +364,15 @@ dependencies = [
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.7.1" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.37.18" version = "0.37.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bbfc1d1c7c40c01715f47d71444744a81669ca84e8b63e25a55e169b1f86433" checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"errno", "errno",
@ -391,9 +395,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.15" version = "2.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -439,14 +443,14 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.15", "syn 2.0.16",
] ]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.8" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"

View File

@ -2,19 +2,23 @@
exclude = ["external"] exclude = ["external"]
members = ["base", "boot", "core", "init", "sepolicy"] members = ["base", "boot", "core", "init", "sepolicy"]
[workspace.dependencies]
cxx = { path = "external/cxx-rs" }
cxx-gen = { path = "external/cxx-rs/gen/lib" }
libc = "0.2"
cfg-if = "1.0"
anyhow = "1.0"
num-traits = "0.2"
num-derive = "0.3"
[profile.dev] [profile.dev]
opt-level = "z" opt-level = "z"
lto = true lto = true
codegen-units = 1 codegen-units = 1
panic = "abort" panic = "abort"
strip = true
[profile.release] [profile.release]
opt-level = "z" opt-level = "z"
lto = true lto = true
codegen-units = 1 codegen-units = 1
panic = "abort" panic = "abort"
strip = true
[patch.crates-io]
cxx = { path = "external/cxx-rs" }

View File

@ -7,9 +7,9 @@ edition = "2021"
path = "lib.rs" path = "lib.rs"
[build-dependencies] [build-dependencies]
cxx-gen = { path = "../external/cxx-rs/gen/lib" } cxx-gen = { workspace = true }
[dependencies] [dependencies]
cxx = { path = "../external/cxx-rs" } cxx = { workspace = true }
libc = "0.2" libc = { workspace = true }
cfg-if = "1.0" cfg-if = { workspace = true }

View File

@ -1,14 +1,12 @@
use std::cmp::min; use std::cmp::min;
use std::ffi::CStr; use std::ffi::CStr;
use std::fs::File;
use std::io; use std::io;
use std::io::{BufRead, Write}; use std::io::{BufRead, Write};
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd}; use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd};
use std::path::Path;
use libc::{c_char, c_uint, mode_t, EEXIST, ENOENT, O_CLOEXEC, O_PATH}; use libc::{c_char, c_uint, mode_t, EEXIST, ENOENT, O_CLOEXEC, O_PATH};
use crate::{bfmt_cstr, errno, xopen}; use crate::{bfmt_cstr, errno, error, xopen};
pub mod unsafe_impl { pub mod unsafe_impl {
use std::ffi::CStr; use std::ffi::CStr;
@ -137,9 +135,43 @@ pub extern "C" fn mkdirs(path: *const c_char, mode: mode_t) -> i32 {
} }
} }
pub fn read_lines<P: AsRef<Path>>(path: P) -> io::Result<io::Lines<io::BufReader<File>>> { pub trait BufReadExt {
let file = File::open(path)?; fn foreach_lines<F: FnMut(&mut String) -> bool>(&mut self, f: F);
Ok(io::BufReader::new(file).lines()) fn foreach_props<F: FnMut(&str, &str) -> bool>(&mut self, f: F);
}
impl<T: BufRead> BufReadExt for T {
fn foreach_lines<F: FnMut(&mut String) -> bool>(&mut self, mut f: F) {
let mut buf = String::new();
loop {
match self.read_line(&mut buf) {
Ok(0) => break,
Ok(_) => {
if !f(&mut buf) {
break;
}
}
Err(e) => {
error!("{}", e);
break;
}
};
buf.clear();
}
}
fn foreach_props<F: FnMut(&str, &str) -> bool>(&mut self, mut f: F) {
self.foreach_lines(|line| {
let line = line.trim();
if line.starts_with('#') {
return true;
}
if let Some((key, value)) = line.split_once('=') {
return f(key, value);
}
return true;
});
}
} }
pub trait WriteExt { pub trait WriteExt {

View File

@ -9,7 +9,7 @@ path = "lib.rs"
[build-dependencies] [build-dependencies]
protobuf-codegen = "3.2.0" protobuf-codegen = "3.2.0"
cxx-gen = { path = "../external/cxx-rs/gen/lib" } cxx-gen = { workspace = true }
[dependencies] [dependencies]
base = { path = "../base" } base = { path = "../base" }

View File

@ -8,10 +8,10 @@ crate-type = ["staticlib"]
path = "lib.rs" path = "lib.rs"
[build-dependencies] [build-dependencies]
cxx-gen = { path = "../external/cxx-rs/gen/lib" } cxx-gen = { workspace = true }
[dependencies] [dependencies]
base = { path = "../base" } base = { path = "../base" }
cxx = { path = "../external/cxx-rs" } cxx = { workspace = true }
num-traits = "0.2" num-traits = { workspace = true }
num-derive = "0.3" num-derive = { workspace = true }

View File

@ -8,8 +8,9 @@ crate-type = ["staticlib"]
path = "lib.rs" path = "lib.rs"
[build-dependencies] [build-dependencies]
cxx-gen = { path = "../external/cxx-rs/gen/lib" } cxx-gen = { workspace = true }
[dependencies] [dependencies]
base = { path = "../base" } base = { path = "../base" }
cxx = { path = "../external/cxx-rs" } magiskpolicy = { path = "../sepolicy" }
cxx = { workspace = true }

View File

@ -1,4 +1,6 @@
pub use logging::*; pub use logging::*;
// Has to be pub so all symbols in that crate is included
pub use magiskpolicy;
mod logging; mod logging;

View File

@ -7,5 +7,10 @@ edition = "2021"
crate-type = ["staticlib", "rlib"] crate-type = ["staticlib", "rlib"]
path = "lib.rs" path = "lib.rs"
[build-dependencies]
cxx-gen = { workspace = true }
[dependencies] [dependencies]
base = { path = "../base" } base = { path = "../base" }
cxx = { workspace = true }
anyhow = { workspace = true }

View File

@ -103,3 +103,13 @@ bool sepolicy::genfscon(const char *fs_name, const char *path, const char *ctx)
bool sepolicy::exists(const char *type) { bool sepolicy::exists(const char *type) {
return hashtab_search(impl->db->p_types.table, type) != nullptr; return hashtab_search(impl->db->p_types.table, type) != nullptr;
} }
void sepolicy::load_rule_file(const char *file) {
rust::load_rule_file(*this, rust::Slice(
reinterpret_cast<const uint8_t *>(file), strlen(file)));
}
void sepolicy::load_rules(const std::string &rules) {
rust::load_rules(*this, rust::Slice(
reinterpret_cast<const uint8_t *>(rules.data()), rules.length()));
}

View File

@ -0,0 +1,8 @@
use crate::gen::gen_cxx_binding;
#[path = "../include/gen.rs"]
mod gen;
fn main() {
gen_cxx_binding("policy-rs");
}

View File

@ -1,9 +1,10 @@
#pragma once #pragma once
#include <stdlib.h> #include <stdlib.h>
#include <selinux.hpp>
#include <string> #include <string>
#include <selinux.hpp>
#define ALL nullptr #define ALL nullptr
struct sepolicy { struct sepolicy {
@ -17,7 +18,8 @@ struct sepolicy {
// External APIs // External APIs
bool to_file(c_str file); bool to_file(c_str file);
void parse_statement(c_str stmt); void parse_statement(c_str stmt, int len);
void parse_statement(c_str stmt) { parse_statement(stmt, strlen(stmt)); }
void load_rules(const std::string &rules); void load_rules(const std::string &rules);
void load_rule_file(c_str file); void load_rule_file(c_str file);

View File

@ -1 +1,53 @@
use io::Cursor;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::pin::Pin;
use std::{io, str};
pub use base; pub use base;
use base::*;
use crate::ffi::sepolicy;
#[cxx::bridge]
mod ffi {
unsafe extern "C++" {
include!("include/sepolicy.hpp");
type sepolicy;
unsafe fn parse_statement(self: Pin<&mut sepolicy>, stmt: *const c_char, len: i32);
}
#[namespace = "rust"]
extern "Rust" {
fn load_rules(sepol: Pin<&mut sepolicy>, rules: &[u8]);
fn load_rule_file(sepol: Pin<&mut sepolicy>, filename: &[u8]);
}
}
fn load_rules_from_reader<T: BufRead>(mut sepol: Pin<&mut sepolicy>, reader: &mut T) {
reader.foreach_lines(|line| {
let bytes = line.trim().as_bytes();
unsafe {
sepol
.as_mut()
.parse_statement(bytes.as_ptr().cast(), bytes.len() as i32);
}
true
});
}
pub fn load_rule_file(sepol: Pin<&mut sepolicy>, filename: &[u8]) {
fn inner(sepol: Pin<&mut sepolicy>, filename: &[u8]) -> anyhow::Result<()> {
let filename = str::from_utf8(filename)?;
let mut reader = BufReader::new(File::open(filename)?);
load_rules_from_reader(sepol, &mut reader);
Ok(())
}
inner(sepol, filename).ok_or_log();
}
pub fn load_rules(sepol: Pin<&mut sepolicy>, rules: &[u8]) {
let mut cursor = Cursor::new(rules);
load_rules_from_reader(sepol, &mut cursor);
}

View File

@ -5,6 +5,8 @@
#include <sepol/policydb/policydb.h> #include <sepol/policydb/policydb.h>
#include <sepolicy.hpp> #include <sepolicy.hpp>
#include "policy-rs.hpp"
struct sepol_impl : public sepolicy { struct sepol_impl : public sepolicy {
avtab_ptr_t get_avtab_node(avtab_key_t *key, avtab_extended_perms_t *xperms); avtab_ptr_t get_avtab_node(avtab_key_t *key, avtab_extended_perms_t *xperms);
bool add_rule(const char *s, const char *t, const char *c, const char *p, int effect, bool invert); bool add_rule(const char *s, const char *t, const char *c, const char *p, int effect, bool invert);

View File

@ -267,21 +267,21 @@ static bool parse_pattern_9(const Func &fn, const char *action, char *stmt) {
#define add_action_func(name, type, fn) \ #define add_action_func(name, type, fn) \
else if (strcmp(name, action) == 0) { \ else if (strcmp(name, action) == 0) { \
auto __fn = [=](auto && ...args){ return (fn)(args...); };\ auto __fn = [&](auto && ...args){ return (fn)(args...); }; \
if (!parse_pattern_##type(__fn, name, remain)) \ if (!parse_pattern_##type(__fn, name, remain)) \
LOGW("Syntax error in '%s'\n\n%s\n", stmt, type_msg_##type); \ LOGW("Syntax error in '%.*s'\n\n%s\n", len, stmt, type_msg_##type); \
} }
#define add_action(act, type) add_action_func(#act, type, act) #define add_action(act, type) add_action_func(#act, type, act)
void sepolicy::parse_statement(const char *stmt) { void sepolicy::parse_statement(const char *stmt, int len) {
// strtok modify strings, create a copy // strtok modify strings, create a copy
string cpy(stmt); string cpy(stmt, len);
char *remain; char *remain;
char *action = strtok_r(cpy.data(), " ", &remain); char *action = strtok_r(cpy.data(), " ", &remain);
if (remain == nullptr) { if (remain == nullptr) {
LOGW("Syntax error in '%s'\n\n", stmt); LOGW("Syntax error in '%.*s'\n\n", len, stmt);
return; return;
} }
@ -310,43 +310,3 @@ void sepolicy::parse_statement(const char *stmt) {
else { LOGW("Unknown action: '%s'\n\n", action); } else { LOGW("Unknown action: '%s'\n\n", action); }
} }
void sepolicy::load_rule_file(const char *file) {
file_readline(true, file, [&](string_view line) -> bool {
if (line.empty() || line[0] == '#')
return true;
parse_statement(line.data());
return true;
});
}
void sepolicy::load_rules(const string &rules) {
struct cookie {
const string &s;
size_t pos;
};
cookie c{rules, 0};
FILE *fp = funopen(&c, /* read */ [](void *v, char *buf, int sz) -> int {
auto c = reinterpret_cast<cookie*>(v);
if (c->pos == c->s.length())
return 0;
size_t end = std::min(c->pos + sz, c->s.length());
int len = end - c->pos;
memcpy(buf, c->s.data() + c->pos, len);
c->pos = end;
return len;
}, /* write */ [](auto, auto, auto) -> int {
return 0;
}, /* seek */ [](auto, auto, auto) -> fpos_t {
return 0;
}, /* close */ [](auto) -> int {
return 0;
});
file_readline(true, fp, [&](string_view line) -> bool {
if (line.empty() || line[0] == '#')
return true;
parse_statement(line.data());
return true;
});
}