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/rules.cpp \
sepolicy/policydb.cpp \
sepolicy/statement.cpp
sepolicy/statement.cpp \
sepolicy/policy-rs.cpp
include $(BUILD_STATIC_LIBRARY)
include src/Android-rs.mk

46
native/src/Cargo.lock generated
View File

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

View File

@ -2,19 +2,23 @@
exclude = ["external"]
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]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
strip = true
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
strip = true
[patch.crates-io]
cxx = { path = "external/cxx-rs" }

View File

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

View File

@ -1,14 +1,12 @@
use std::cmp::min;
use std::ffi::CStr;
use std::fs::File;
use std::io;
use std::io::{BufRead, Write};
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 crate::{bfmt_cstr, errno, xopen};
use crate::{bfmt_cstr, errno, error, xopen};
pub mod unsafe_impl {
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>>> {
let file = File::open(path)?;
Ok(io::BufReader::new(file).lines())
pub trait BufReadExt {
fn foreach_lines<F: FnMut(&mut String) -> bool>(&mut self, f: F);
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 {

View File

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

View File

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

View File

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

View File

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

View File

@ -7,5 +7,10 @@ edition = "2021"
crate-type = ["staticlib", "rlib"]
path = "lib.rs"
[build-dependencies]
cxx-gen = { workspace = true }
[dependencies]
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) {
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
#include <stdlib.h>
#include <selinux.hpp>
#include <string>
#include <selinux.hpp>
#define ALL nullptr
struct sepolicy {
@ -17,7 +18,8 @@ struct sepolicy {
// External APIs
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_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;
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 <sepolicy.hpp>
#include "policy-rs.hpp"
struct sepol_impl : public sepolicy {
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);

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) \
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)) \
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)
void sepolicy::parse_statement(const char *stmt) {
void sepolicy::parse_statement(const char *stmt, int len) {
// strtok modify strings, create a copy
string cpy(stmt);
string cpy(stmt, len);
char *remain;
char *action = strtok_r(cpy.data(), " ", &remain);
if (remain == nullptr) {
LOGW("Syntax error in '%s'\n\n", stmt);
LOGW("Syntax error in '%.*s'\n\n", len, stmt);
return;
}
@ -310,43 +310,3 @@ void sepolicy::parse_statement(const char *stmt) {
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;
});
}