9 Commits

Author SHA1 Message Date
kpcyrd
798bd56e75 Release v0.17.1 2020-02-22 19:38:23 +01:00
kpcyrd
5359d1cb95 Merge pull request #157 from kpcyrd/sandbox
seccomp: whitelist ppoll instead of poll on aarch64
2020-02-22 19:34:17 +01:00
kpcyrd
ff7fe3936b Refactor ipc parent/child code 2020-02-22 18:28:33 +01:00
kpcyrd
d1d221f81e Improve child process logging 2020-02-22 17:46:36 +01:00
kpcyrd
e2acdf53a4 Downgrade from broken x509-parser release
https://github.com/rusticata/x509-parser/pull/27
2020-02-22 17:12:09 +01:00
kpcyrd
749d7efab5 Add sn0int run --dump-sandbox-init-msg for debugging 2020-02-22 16:52:36 +01:00
kpcyrd
d96a967fa9 Fix integration test 2020-02-22 16:04:35 +01:00
kpcyrd
3dddd0b041 Add exit and quit commands 2020-02-22 15:19:46 +01:00
kpcyrd
33f02bb832 seccomp: whitelist ppoll instead of poll on aarch64 2020-02-22 15:17:17 +01:00
22 changed files with 415 additions and 332 deletions

36
Cargo.lock generated
View File

@@ -620,12 +620,12 @@ dependencies = [
[[package]]
name = "der-parser"
version = "3.0.4"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nom 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rusticata-macros 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"rusticata-macros 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -2703,10 +2703,10 @@ dependencies = [
[[package]]
name = "rusticata-macros"
version = "2.0.4"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nom 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -3045,7 +3045,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "sn0int"
version = "0.17.0"
version = "0.17.1"
dependencies = [
"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3093,7 +3093,7 @@ dependencies = [
"shellwords 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"sloppy-rfc4880 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"sn0int-common 0.10.0",
"sn0int-std 0.17.0",
"sn0int-std 0.17.1",
"structopt 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"strum 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
"strum_macros 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3148,7 +3148,7 @@ dependencies = [
[[package]]
name = "sn0int-std"
version = "0.17.0"
version = "0.17.1"
dependencies = [
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3158,7 +3158,7 @@ dependencies = [
"chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
"chrootable-https 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ct-logs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"der-parser 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"der-parser 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3185,7 +3185,7 @@ dependencies = [
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"webpki 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
"webpki-roots 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
"x509-parser 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"x509-parser 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -4143,14 +4143,14 @@ dependencies = [
[[package]]
name = "x509-parser"
version = "0.6.1"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"der-parser 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"nom 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"der-parser 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rusticata-macros 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"rusticata-macros 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -4247,7 +4247,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum data-encoding-macro 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1995a2e32b5306e4f49cf55ffcf4365a4dab2c7ca3a97163a4f099ec2fee3453"
"checksum data-encoding-macro-internal 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "dd41a6f6a292f3d5be0cd1562bd6007652279d6afe3246f2722749e927338f7a"
"checksum deflate 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)" = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4"
"checksum der-parser 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f51f64dcdf1cdc550d21d73dc959726c7dbeeab4a01481d08084a7736956464e"
"checksum der-parser 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2240eda75e81eeac4d7e2899f17a8469d877c5e7f694c7801720bb26a019b0"
"checksum derive_more 0.99.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a806e96c59a76a5ba6e18735b6cf833344671e61e7863f2edb5c518ea2cac95c"
"checksum devise 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "74e04ba2d03c5fa0d954c061fc8c9c288badadffc272ebb87679a89846de3ed3"
"checksum devise_codegen 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "066ceb7928ca93a9bedc6d0e612a8a0424048b0ab1f75971b203d01420c055d7"
@@ -4470,7 +4470,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum rust-argon2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017"
"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum rusticata-macros 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "93cca5a839ae468017da70a202f294e3deeda9e7d98f21b83fc5d23e5163b9df"
"checksum rusticata-macros 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fe8c976e50876cb3423699b190e799ff4c8a1a4f47dc33f4036dd056c077b7bb"
"checksum rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e"
"checksum rustversion 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6"
"checksum rustyline 5.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a23cb19702a8d6afb6edb3c842386e680d4883760e0df74e6848e23c2a87a635"
@@ -4613,7 +4613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9"
"checksum winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
"checksum x509-parser 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05acbe8d07330e6c70e2ef78572dfd68137b033915cf58e3b26f0a4f2fe161f6"
"checksum x509-parser 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f229e801140da8500deb67fcae308657b39577d04b80a57c9479964c54c094d"
"checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5"
"checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d"
"checksum yansi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d60c3b48c9cdec42fb06b3b84b5b087405e1fa1c644a1af3930e4dfafe93de48"

View File

@@ -1,6 +1,6 @@
[package]
name = "sn0int"
version = "0.17.0"
version = "0.17.1"
description = "Semi-automatic OSINT framework and package manager"
authors = ["kpcyrd <git@rxv.cc>"]
license = "GPL-3.0"
@@ -22,7 +22,7 @@ sqlite-bundled = ["libsqlite3-sys/bundled"]
[dependencies]
sn0int-common = { version="0.10.0", path="sn0int-common" }
sn0int-std = { version="0.17.0", path="sn0int-std" }
sn0int-std = { version="0.17.1", path="sn0int-std" }
rustyline = "6.0"
log = "0.4"
env_logger = "0.7"

View File

@@ -63,6 +63,7 @@ def main(tempdir, binary):
print('[*] testing db for subdomains')
subdomains = sn0int_select(tempdir, binary, ['subdomains'])
assert {x['value'] for x in subdomains} == {
'example.com',
'www.example.com',
'm.example.com',
'dev.example.com',
@@ -91,6 +92,8 @@ def main(tempdir, binary):
print('[*] testing db for urls')
urls = sn0int_select(tempdir, binary, ['urls'])
assert {(x['value'], x['status']) for x in urls} == {
('http://example.com/', 200),
('https://example.com/', 200),
('http://www.example.com/', 200),
('https://www.example.com/', 200),
}

View File

@@ -1,6 +1,6 @@
[package]
name = "sn0int-std"
version = "0.17.0"
version = "0.17.1"
description = "sn0int - stdlib"
authors = ["kpcyrd <git@rxv.cc>"]
repository = "https://github.com/kpcyrd/sn0int"
@@ -32,8 +32,9 @@ url = "2.0"
tungstenite = { version = "0.10.1", default-features = false }
kuchiki = "0.8.0"
maxminddb = "0.13"
x509-parser = "0.6.0"
der-parser = "3.0"
# x509-parser 0.6.1 is broken
x509-parser = "0.5.1"
der-parser = "2.0"
publicsuffix = { version="1.5", default-features=false }
xml-rs = "0.8"
geo = "0.12"

View File

View File

@@ -9,7 +9,6 @@ pub mod geo;
pub mod geoip;
pub mod gfx;
pub mod html;
pub mod ipc;
pub mod json;
pub mod lazy;
pub mod psl;

View File

@@ -124,6 +124,9 @@ pub struct Run {
/// Narrow down targeted entities
#[structopt(short="t", long="target")]
pub target: Option<String>,
/// Dump the sandbox init message to stdout instead of running a child process
#[structopt(long="dump-sandbox-init-msg")]
pub dump_sandbox_init_msg: bool,
}
#[derive(Debug, StructOpt)]

View File

@@ -1,10 +1,12 @@
use crate::errors::*;
use chrootable_https::dns::Resolver;
use crate::args;
use crate::blobs::{Blob, BlobStorage};
use crate::cmd::Cmd;
use crate::db::{ttl, Filter};
use crate::engine::Module;
use crate::ipc::common::StartCommand;
use crate::models::*;
use crate::shell::Shell;
use crate::keyring::KeyRing;
@@ -113,16 +115,10 @@ fn prepare_keyring(keyring: &mut KeyRing, module: &Module, params: &Params) -> R
Ok(())
}
pub fn execute(rl: &mut Shell, params: Params, options: HashMap<String, String>) -> Result<()> {
let module = rl.module()
.map(|m| m.to_owned())
.ok_or_else(|| format_err!("No module selected"))?;
prepare_keyring(rl.keyring_mut(), &module, &params)?;
fn get_args(rl: &mut Shell, module: &Module) -> Result<Vec<(serde_json::Value, Option<String>, Vec<Blob>)>> {
let filter = rl.scoped_targets();
let args = match module.source() {
match module.source() {
Some(Source::Domains) => prepare_args::<Domain>(rl, &filter, None),
Some(Source::Subdomains) => prepare_args::<Subdomain>(rl, &filter, None),
Some(Source::IpAddrs) => prepare_args::<IpAddr>(rl, &filter, None),
@@ -152,7 +148,44 @@ pub fn execute(rl: &mut Shell, params: Params, options: HashMap<String, String>)
}
},
None => Ok(vec![(serde_json::Value::Null, None, vec![])]),
}?;
}
}
pub fn dump_sandbox_init_msg(rl: &mut Shell, params: Params, options: HashMap<String, String>) -> Result<()> {
let module = rl.module()
.map(|m| m.to_owned())
.ok_or_else(|| format_err!("No module selected"))?;
prepare_keyring(rl.keyring_mut(), &module, &params)?;
let keyring = rl.keyring().request_keys(&module);
let dns_config = Resolver::from_system()?;
let proxy = rl.config().network.proxy.clone();
let args = get_args(rl, &module)?;
for (arg, _pretty_arg, blobs) in args {
let start_cmd = StartCommand::new(params.verbose,
keyring.clone(),
dns_config.clone(),
proxy.clone(),
options.clone(),
module.clone(),
arg,
blobs);
let out = serde_json::to_string(&start_cmd)?;
println!("{}", out);
}
Ok(())
}
pub fn execute(rl: &mut Shell, params: Params, options: HashMap<String, String>) -> Result<()> {
let module = rl.module()
.map(|m| m.to_owned())
.ok_or_else(|| format_err!("No module selected"))?;
prepare_keyring(rl.keyring_mut(), &module, &params)?;
let args = get_args(rl, &module)?;
rl.signal_register().catch_ctrl();
let errors = worker::spawn(rl, &module, args, &params, rl.config().network.proxy.clone(), options);

View File

@@ -1,7 +1,7 @@
use crate::errors::*;
use crate::db::Family;
use crate::engine::{Environment, Reporter};
use crate::engine::{Environment, IpcChild};
use crate::geoip::{MaxmindReader, GeoIP, AsnDB};
use crate::hlua::{self, AnyLuaValue};
use crate::keyring::KeyRingEntry;
@@ -196,7 +196,7 @@ pub trait State {
// #[derive(Debug)]
pub struct LuaState {
error: Mutex<Option<Error>>,
logger: Arc<Mutex<Box<dyn Reporter>>>,
logger: Arc<Mutex<Box<dyn IpcChild>>>,
socket_sessions: Mutex<HashMap<String, Arc<Mutex<Socket>>>>,
ws_sessions: Mutex<HashMap<String, Arc<Mutex<WebSocket>>>>,
blobs: Mutex<HashMap<String, Arc<Blob>>>,
@@ -426,7 +426,7 @@ pub struct Script {
code: String,
}
pub fn ctx<'a>(env: Environment, logger: Arc<Mutex<Box<dyn Reporter>>>) -> (hlua::Lua<'a>, Arc<LuaState>) {
pub fn ctx<'a>(env: Environment, logger: Arc<Mutex<Box<dyn IpcChild>>>) -> (hlua::Lua<'a>, Arc<LuaState>) {
debug!("Creating lua context");
let mut lua = hlua::Lua::new();
lua.open_string();
@@ -592,7 +592,7 @@ impl Script {
}
pub fn run(&self, env: Environment,
tx: Arc<Mutex<Box<dyn Reporter>>>,
tx: Arc<Mutex<Box<dyn IpcChild>>>,
arg: AnyLuaValue,
) -> Result<()> {
let (mut lua, state) = ctx(env, tx);
@@ -623,7 +623,7 @@ impl Script {
#[cfg(test)]
pub fn test(&self) -> Result<()> {
use crate::engine::DummyReporter;
use crate::ipc::child::DummyIpcChild;
use crate::geoip::Maxmind;
use crate::paths;
let keyring = Vec::new();
@@ -652,6 +652,6 @@ a.prod.fastly.net
geoip,
asn,
};
self.run(env, DummyReporter::new(), AnyLuaValue::LuaNil)
self.run(env, DummyIpcChild::new(), AnyLuaValue::LuaNil)
}
}

View File

@@ -1,252 +0,0 @@
use crate::errors::*;
use chrootable_https::dns::Resolver;
use crate::blobs::Blob;
use crate::engine::{Environment, Module, Reporter};
use crate::geoip::MaxmindReader;
use crate::keyring::KeyRingEntry;
use crate::psl::PslReader;
use serde_json;
use crate::worker::{Event, Event2, LogEvent, ExitEvent, EventSender, EventWithCallback};
use std::collections::HashMap;
use std::env;
use std::ffi::OsString;
use std::io::prelude::*;
use std::io::{self, BufReader, BufRead, stdin, Stdin, Stdout};
use std::net::SocketAddr;
use std::sync::{mpsc, Arc, Mutex};
use std::process::{Command, Child, Stdio, ChildStdin, ChildStdout};
#[derive(Debug, Serialize, Deserialize)]
pub struct StartCommand {
verbose: u64,
keyring: Vec<KeyRingEntry>,
dns_config: Resolver,
proxy: Option<SocketAddr>,
options: HashMap<String, String>,
module: Module,
arg: serde_json::Value,
blobs: Vec<Blob>,
}
impl StartCommand {
pub fn new(verbose: u64,
keyring: Vec<KeyRingEntry>,
dns_config: Resolver,
proxy: Option<SocketAddr>,
options: HashMap<String, String>,
module: Module,
arg: serde_json::Value,
blobs: Vec<Blob>,
) -> StartCommand {
StartCommand {
verbose,
keyring,
dns_config,
proxy,
options,
module,
arg,
blobs,
}
}
}
pub struct Supervisor {
child: Child,
stdin: ChildStdin,
stdout: BufReader<ChildStdout>,
}
impl Supervisor {
pub fn setup(module: &Module) -> Result<Supervisor> {
let exe = match env::current_exe() {
Ok(exe) => exe.into_os_string(),
_ => OsString::from("sn0int"),
};
let mut child = Command::new(exe)
.arg("sandbox")
.arg(&module.canonical())
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.context("Failed to spawn child process")?;
let stdin = child.stdin.take().expect("Failed to take child stdin");
let stdout = child.stdout.take().expect("Failed to take child stdout");
let stdout = BufReader::new(stdout);
Ok(Supervisor {
child,
stdin,
stdout,
})
}
pub fn send_start(&mut self, start: &StartCommand) -> Result<()> {
let start = serde_json::to_value(&start)?;
self.send(&start)?;
Ok(())
}
pub fn send(&mut self, value: &serde_json::Value) -> Result<()> {
let mut value = serde_json::to_string(value)?;
value.push('\n');
self.stdin.write_all(value.as_bytes())?;
debug!("Supervisor sent: {:?}", value);
Ok(())
}
pub fn send_struct<T: serde::Serialize>(&mut self, value: T, tx: &EventSender) {
let value = serde_json::to_value(value).expect("Failed to serialize reply");
if let Err(_) = self.send(&value) {
tx.send(Event2::Log(LogEvent::Error("Failed to send to child".into())));
}
}
pub fn recv(&mut self) -> Result<Event> {
let mut line = String::new();
let len = self.stdout.read_line(&mut line)?;
let event = serde_json::from_str(&line[..len])?;
debug!("Supervisor received: {:?}", event);
Ok(event)
}
pub fn wait(&mut self) -> Result<()> {
let exit = self.child.wait()
.context("Failed to wait for child")?;
if exit.success() {
Ok(())
} else {
bail!("Child signaled error")
}
}
pub fn send_event_callback<T: EventWithCallback>(&mut self, event: T, tx: &EventSender)
where <T as EventWithCallback>::Payload: serde::Serialize
{
let (tx2, rx2) = mpsc::channel();
tx.send(event.with_callback(tx2));
let reply = rx2.recv().unwrap();
self.send_struct(reply, tx);
}
}
#[derive(Debug)]
pub struct StdioReporter {
stdin: Stdin,
stdout: Stdout,
}
impl StdioReporter {
pub fn setup() -> StdioReporter {
let stdin = io::stdin();
let stdout = io::stdout();
StdioReporter {
stdin,
stdout,
}
}
pub fn recv_start(&mut self) -> Result<StartCommand> {
let value = self.recv()?;
let event = serde_json::from_value(value)?;
Ok(event)
}
}
impl Reporter for StdioReporter {
fn send(&mut self, event: &Event) -> Result<()> {
let mut event = serde_json::to_string(&event)?;
event.push('\n');
self.stdout.write_all(event.as_bytes())?;
debug!("Reporter sent: {:?}", event);
Ok(())
}
fn recv(&mut self) -> Result<serde_json::Value> {
let mut line = String::new();
let len = self.stdin.read_line(&mut line)?;
let event = serde_json::from_str(&line[..len])?;
debug!("Reporter received: {:?}", event);
Ok(event)
}
}
pub fn spawn_module(module: Module,
tx: &EventSender,
arg: serde_json::Value,
keyring: Vec<KeyRingEntry>,
verbose: u64,
has_stdin: bool,
proxy: Option<SocketAddr>,
options: HashMap<String, String>,
blobs: Vec<Blob>,
) -> Result<ExitEvent> {
let dns_config = Resolver::from_system()?;
let mut reader = if has_stdin {
Some(BufReader::new(stdin()))
} else {
None
};
let mut supervisor = Supervisor::setup(&module)?;
supervisor.send_start(&StartCommand::new(verbose, keyring, dns_config, proxy, options, module, arg, blobs))?;
let exit = loop {
match supervisor.recv()? {
Event::Log(event) => tx.send(Event2::Log(event)),
Event::Database(object) => supervisor.send_event_callback(object, &tx),
Event::Stdio(object) => object.apply(&mut supervisor, tx, &mut reader),
Event::Ratelimit(req) => supervisor.send_event_callback(req, &tx),
Event::Blob(blob) => supervisor.send_event_callback(blob, &tx),
Event::Exit(event) => {
if let ExitEvent::Err(err) = &event {
tx.send(Event2::Log(LogEvent::Error(err.clone())));
}
break event;
},
}
};
supervisor.wait()?;
Ok(exit)
}
pub fn run_worker(geoip: Option<MaxmindReader>, asn: Option<MaxmindReader>, psl: PslReader) -> Result<()> {
let mut reporter = StdioReporter::setup();
let start = reporter.recv_start()?;
let environment = Environment {
verbose: start.verbose,
keyring: start.keyring,
dns_config: start.dns_config,
proxy: start.proxy,
options: start.options,
blobs: start.blobs,
psl,
geoip,
asn,
};
let mtx: Arc<Mutex<Box<dyn Reporter>>> = Arc::new(Mutex::new(Box::new(reporter)));
let result = start.module.run(environment,
mtx.clone(),
start.arg.into());
let mut reporter = Arc::try_unwrap(mtx).expect("Failed to consume Arc")
.into_inner().expect("Failed to consume Mutex");
let event = result.into();
reporter.send(&Event::Exit(event))?;
Ok(())
}

View File

@@ -5,7 +5,6 @@ use crate::config::Config;
use crate::geoip::MaxmindReader;
use crate::json::LuaJsonValue;
use crate::keyring::KeyRingEntry;
use serde_json;
use std::fs;
use std::fmt::Debug;
use std::path::PathBuf;
@@ -13,6 +12,7 @@ use std::collections::HashMap;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use crate::engine::ctx::Script;
use crate::ipc::child::IpcChild;
use sn0int_common::ModuleID;
use sn0int_common::metadata::{Metadata, Source};
use chrootable_https::dns::Resolver;
@@ -21,10 +21,9 @@ use crate::paths;
use std::cmp::Ordering;
use std::path::Path;
use crate::term;
use crate::worker::{self, Event};
use crate::worker;
pub mod ctx;
pub mod isolation;
pub use sn0int_std::engine::structs;
@@ -299,9 +298,9 @@ impl Module {
self.private_module
}
pub fn run(&self, env: Environment, reporter: Arc<Mutex<Box<dyn Reporter>>>, arg: LuaJsonValue) -> Result<()> {
pub fn run(&self, env: Environment, ipc_child: Arc<Mutex<Box<dyn IpcChild>>>, arg: LuaJsonValue) -> Result<()> {
debug!("Executing lua script {}", self.canonical());
self.script.run(env, reporter, arg.into())
self.script.run(env, ipc_child, arg.into())
}
#[inline]
@@ -320,28 +319,3 @@ impl Module {
}
}
}
pub trait Reporter: Debug {
fn send(&mut self, event: &Event) -> Result<()>;
fn recv(&mut self) -> Result<serde_json::Value>;
}
#[derive(Debug)]
pub struct DummyReporter;
impl DummyReporter {
pub fn new() -> Arc<Mutex<Box<dyn Reporter>>> {
Arc::new(Mutex::new(Box::new(DummyReporter)))
}
}
impl Reporter for DummyReporter {
fn send(&mut self, _event: &Event) -> Result<()> {
Ok(())
}
fn recv(&mut self) -> Result<serde_json::Value> {
unimplemented!("DummyReporter::recv doesn't exist")
}
}

110
src/ipc/child.rs Normal file
View File

@@ -0,0 +1,110 @@
use crate::errors::*;
use crate::ipc::common::*;
use crate::engine::Environment;
use crate::geoip::MaxmindReader;
use crate::psl::PslReader;
use serde_json;
use crate::worker::Event;
use std::fmt::Debug;
use std::io::prelude::*;
use std::io::{self, Stdin, Stdout};
use std::sync::{Arc, Mutex};
pub trait IpcChild: Debug {
fn send(&mut self, event: &Event) -> Result<()>;
fn recv(&mut self) -> Result<serde_json::Value>;
}
#[derive(Debug)]
pub struct StdioIpcChild {
stdin: Stdin,
stdout: Stdout,
}
impl StdioIpcChild {
pub fn setup() -> StdioIpcChild {
let stdin = io::stdin();
let stdout = io::stdout();
StdioIpcChild {
stdin,
stdout,
}
}
pub fn recv_start(&mut self) -> Result<StartCommand> {
let value = self.recv()?;
let event = serde_json::from_value(value)?;
Ok(event)
}
}
impl IpcChild for StdioIpcChild {
fn send(&mut self, event: &Event) -> Result<()> {
let mut event = serde_json::to_string(&event)?;
event.push('\n');
debug!("IpcChild sends: {:?}", event);
self.stdout.write_all(event.as_bytes())?;
Ok(())
}
fn recv(&mut self) -> Result<serde_json::Value> {
let mut line = String::new();
let len = self.stdin.read_line(&mut line)?;
let event = serde_json::from_str(&line[..len])?;
debug!("IpcChild received: {:?}", event);
Ok(event)
}
}
#[derive(Debug)]
pub struct DummyIpcChild;
impl DummyIpcChild {
pub fn new() -> Arc<Mutex<Box<dyn IpcChild>>> {
Arc::new(Mutex::new(Box::new(DummyIpcChild)))
}
}
impl IpcChild for DummyIpcChild {
fn send(&mut self, _event: &Event) -> Result<()> {
Ok(())
}
fn recv(&mut self) -> Result<serde_json::Value> {
unimplemented!("DummyIpcChild::recv doesn't exist")
}
}
pub fn run(geoip: Option<MaxmindReader>, asn: Option<MaxmindReader>, psl: PslReader) -> Result<()> {
let mut ipc_child = StdioIpcChild::setup();
let start = ipc_child.recv_start()?;
let environment = Environment {
verbose: start.verbose,
keyring: start.keyring,
dns_config: start.dns_config,
proxy: start.proxy,
options: start.options,
blobs: start.blobs,
psl,
geoip,
asn,
};
let mtx: Arc<Mutex<Box<dyn IpcChild>>> = Arc::new(Mutex::new(Box::new(ipc_child)));
let result = start.module.run(environment,
mtx.clone(),
start.arg.into());
let mut ipc_child = Arc::try_unwrap(mtx).expect("Failed to consume Arc")
.into_inner().expect("Failed to consume Mutex");
let event = result.into();
ipc_child.send(&Event::Exit(event))?;
Ok(())
}

44
src/ipc/common.rs Normal file
View File

@@ -0,0 +1,44 @@
use chrootable_https::dns::Resolver;
use crate::blobs::Blob;
use crate::engine::Module;
use crate::keyring::KeyRingEntry;
use serde_json;
use std::collections::HashMap;
use std::net::SocketAddr;
#[derive(Debug, Serialize, Deserialize)]
pub struct StartCommand {
pub verbose: u64,
pub keyring: Vec<KeyRingEntry>,
pub dns_config: Resolver,
pub proxy: Option<SocketAddr>,
pub options: HashMap<String, String>,
pub module: Module,
pub arg: serde_json::Value,
pub blobs: Vec<Blob>,
}
impl StartCommand {
pub fn new(verbose: u64,
keyring: Vec<KeyRingEntry>,
dns_config: Resolver,
proxy: Option<SocketAddr>,
options: HashMap<String, String>,
module: Module,
arg: serde_json::Value,
blobs: Vec<Blob>,
) -> StartCommand {
StartCommand {
verbose,
keyring,
dns_config,
proxy,
options,
module,
arg,
blobs,
}
}
}

3
src/ipc/mod.rs Normal file
View File

@@ -0,0 +1,3 @@
pub mod child;
pub mod common;
pub mod parent;

144
src/ipc/parent.rs Normal file
View File

@@ -0,0 +1,144 @@
use crate::errors::*;
use crate::ipc::common::*;
use chrootable_https::dns::Resolver;
use crate::blobs::Blob;
use crate::engine::Module;
use crate::keyring::KeyRingEntry;
use serde_json;
use crate::worker::{Event, Event2, LogEvent, ExitEvent, EventSender, EventWithCallback};
use std::collections::HashMap;
use std::env;
use std::ffi::OsString;
use std::io::prelude::*;
use std::io::{BufReader, BufRead, stdin};
use std::net::SocketAddr;
use std::sync::mpsc;
use std::process::{Command, Child, Stdio, ChildStdin, ChildStdout};
pub struct IpcParent {
child: Child,
stdin: ChildStdin,
stdout: BufReader<ChildStdout>,
}
impl IpcParent {
pub fn setup(module: &Module) -> Result<IpcParent> {
let exe = match env::current_exe() {
Ok(exe) => exe.into_os_string(),
_ => OsString::from("sn0int"),
};
let mut child = Command::new(exe)
.arg("sandbox")
.arg(&module.canonical())
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.context("Failed to spawn child process")?;
let stdin = child.stdin.take().expect("Failed to take child stdin");
let stdout = child.stdout.take().expect("Failed to take child stdout");
let stdout = BufReader::new(stdout);
Ok(IpcParent {
child,
stdin,
stdout,
})
}
pub fn send_start(&mut self, start: &StartCommand) -> Result<()> {
let start = serde_json::to_value(&start)?;
self.send(&start)?;
Ok(())
}
pub fn send(&mut self, value: &serde_json::Value) -> Result<()> {
let mut value = serde_json::to_string(value)?;
value.push('\n');
self.stdin.write_all(value.as_bytes())?;
debug!("IpcParent sent: {:?}", value);
Ok(())
}
pub fn send_struct<T: serde::Serialize>(&mut self, value: T, tx: &EventSender) {
let value = serde_json::to_value(value).expect("Failed to serialize reply");
if let Err(_) = self.send(&value) {
tx.send(Event2::Log(LogEvent::Error("Failed to send to child".into())));
}
}
pub fn recv(&mut self) -> Result<Event> {
let mut line = String::new();
let len = self.stdout.read_line(&mut line)?;
let event = serde_json::from_str(&line[..len])?;
debug!("IpcParent received: {:?}", event);
Ok(event)
}
pub fn wait(&mut self) -> Result<()> {
let exit = self.child.wait()
.context("Failed to wait for child")?;
if exit.success() {
Ok(())
} else {
bail!("Child signaled error")
}
}
pub fn send_event_callback<T: EventWithCallback>(&mut self, event: T, tx: &EventSender)
where <T as EventWithCallback>::Payload: serde::Serialize
{
let (tx2, rx2) = mpsc::channel();
tx.send(event.with_callback(tx2));
let reply = rx2.recv().unwrap();
self.send_struct(reply, tx);
}
}
pub fn run(module: Module,
tx: &EventSender,
arg: serde_json::Value,
keyring: Vec<KeyRingEntry>,
verbose: u64,
has_stdin: bool,
proxy: Option<SocketAddr>,
options: HashMap<String, String>,
blobs: Vec<Blob>,
) -> Result<ExitEvent> {
let dns_config = Resolver::from_system()?;
let mut reader = if has_stdin {
Some(BufReader::new(stdin()))
} else {
None
};
let mut ipc_parent = IpcParent::setup(&module)?;
ipc_parent.send_start(&StartCommand::new(verbose, keyring, dns_config, proxy, options, module, arg, blobs))?;
let exit = loop {
match ipc_parent.recv()? {
Event::Log(event) => tx.send(Event2::Log(event)),
Event::Database(object) => ipc_parent.send_event_callback(object, &tx),
Event::Stdio(object) => object.apply(&mut ipc_parent, tx, &mut reader),
Event::Ratelimit(req) => ipc_parent.send_event_callback(req, &tx),
Event::Blob(blob) => ipc_parent.send_event_callback(blob, &tx),
Event::Exit(event) => {
if let ExitEvent::Err(err) = &event {
tx.send(Event2::Log(LogEvent::Error(err.clone())));
}
break event;
},
}
};
ipc_parent.wait()?;
Ok(exit)
}

View File

@@ -27,6 +27,7 @@ pub use sn0int_std::geoip;
use sn0int_std::gfx;
use sn0int_std::html;
use sn0int_std::json;
pub mod ipc;
pub mod keyring;
use sn0int_std::lazy;
pub mod migrations;

View File

@@ -5,8 +5,9 @@ use sn0int::cmd::{self, LiteCmd};
use sn0int::config::Config;
use sn0int::db;
use sn0int::errors::*;
use sn0int::engine::{self, Module};
use sn0int::engine::Module;
use sn0int::geoip::{GeoIP, AsnDB, Maxmind};
use sn0int::ipc;
use sn0int::options::Opt;
use sn0int::paths;
use sn0int::psl::PslReader;
@@ -46,7 +47,11 @@ fn run_run(gargs: &Args, args: &args::Run, config: &Config) -> Result<()> {
rl.set_target(Some(target));
}
cmd::run_cmd::execute(&mut rl, args.into(), Opt::collect(&args.options))
if args.dump_sandbox_init_msg {
cmd::run_cmd::dump_sandbox_init_msg(&mut rl, args.into(), Opt::collect(&args.options))
} else {
cmd::run_cmd::execute(&mut rl, args.into(), Opt::collect(&args.options))
}
}
fn run_sandbox() -> Result<()> {
@@ -57,7 +62,7 @@ fn run_sandbox() -> Result<()> {
sandbox::init()
.context("Failed to init sandbox")?;
engine::isolation::run_worker(geoip, asn, psl)
ipc::child::run(geoip, asn, psl)
}
fn run_cmd<T: cmd::Cmd>(gargs: &Args, args: T, config: &Config) -> Result<()> {

View File

@@ -1,6 +1,7 @@
use crate::config::Config;
use crate::errors::*;
use crate::engine::{ctx, Environment, DummyReporter};
use crate::engine::{ctx, Environment};
use crate::ipc::child::DummyIpcChild;
use crate::engine::ctx::{State, LuaState};
use crate::geoip::{Maxmind, AsnDB, GeoIP};
use crate::hlua::{Lua, AnyLuaValue};
@@ -90,7 +91,7 @@ pub fn run(config: &Config) -> Result<()> {
let asn = AsnDB::try_open_reader(&cache_dir)?;
let env = Environment {
verbose: 0, // this doesn't do anything since we use a dummy reporter
verbose: 0, // this doesn't do anything since we use a dummy ipc child
keyring,
dns_config,
proxy,
@@ -101,7 +102,7 @@ pub fn run(config: &Config) -> Result<()> {
asn,
};
let tx = DummyReporter::new();
let tx = DummyIpcChild::new();
let (lua, state) = ctx::ctx(env, tx);
let mut repl = Repl::new(lua, state);

View File

@@ -17,7 +17,10 @@ pub fn init() -> Result<()> {
ctx.allow_syscall(Syscall::fcntl64)?;
ctx.allow_syscall(Syscall::uname)?;
ctx.allow_syscall(Syscall::close)?;
#[cfg(not(target_arch = "aarch64"))]
ctx.allow_syscall(Syscall::poll)?;
#[cfg(target_arch = "aarch64")]
ctx.allow_syscall(Syscall::ppoll)?;
ctx.allow_syscall(Syscall::epoll_create1)?;
ctx.allow_syscall(Syscall::pipe2)?;
ctx.allow_syscall(Syscall::epoll_ctl)?;

View File

@@ -49,6 +49,9 @@ pub enum Command {
Quickstart,
Workspace,
Exit,
Quit,
Interrupt,
}
@@ -61,6 +64,7 @@ impl Command {
Command::Autoscope => "autoscope",
Command::Back => "back",
Command::Delete => "delete",
Command::Exit => "exit",
Command::Help => "help",
Command::Keyring => "keyring",
Command::Mod => "mod",
@@ -73,6 +77,7 @@ impl Command {
Command::Target => "target",
Command::Use => "use",
Command::Quickstart => "quickstart",
Command::Quit => "quit",
Command::Workspace => "workspace",
Command::Interrupt => unreachable!(),
}
@@ -118,6 +123,7 @@ impl FromStr for Command {
"autoscope" => Ok(Command::Autoscope),
"back" => Ok(Command::Back),
"delete" => Ok(Command::Delete),
"exit" => Ok(Command::Exit),
"help" => Ok(Command::Help),
"keyring" => Ok(Command::Keyring),
"mod" => Ok(Command::Mod),
@@ -130,6 +136,7 @@ impl FromStr for Command {
"target" => Ok(Command::Target),
"use" => Ok(Command::Use),
"quickstart" => Ok(Command::Quickstart),
"quit" => Ok(Command::Quit),
"workspace" => Ok(Command::Workspace),
x => bail!("unknown command: {:?}, try \"help\"", x),
}
@@ -465,6 +472,9 @@ pub fn run_once(rl: &mut Shell) -> Result<bool> {
Some((Command::Use, args)) => use_cmd::run(rl, &args)?,
Some((Command::Quickstart, args)) => quickstart_cmd::run(rl, &args)?,
Some((Command::Workspace, args)) => cmd::<workspace_cmd::Args>(rl, &args)?,
Some((Command::Exit, _)) => return Ok(true),
Some((Command::Quit, _)) => return Ok(true),
Some((Command::Interrupt, _)) => return Ok(true),
None => (),
}

View File

@@ -5,8 +5,9 @@ use crate::channel;
use crate::cmd::run_cmd::Params;
use crate::db::{Database, DbChange, Family};
use crate::db::ttl::Ttl;
use crate::engine::{self, Module};
use crate::engine::isolation::Supervisor;
use crate::engine::Module;
use crate::ipc;
use crate::ipc::parent::IpcParent;
use crate::models::*;
use serde_json;
use crate::ratelimits::{Ratelimiter, RatelimitResponse};
@@ -340,13 +341,13 @@ impl StdioEvent {
}
}
pub fn apply(self, supervisor: &mut Supervisor, tx: &EventSender, reader: &mut Option<BufReader<Stdin>>) {
pub fn apply(self, ipc_parent: &mut IpcParent, tx: &EventSender, reader: &mut Option<BufReader<Stdin>>) {
let reply = match self {
StdioEvent::Readline => Self::read_line(reader),
StdioEvent::ToEnd => Self::read_to_end(reader),
};
let reply = reply.map_err(|e| e.to_string());
supervisor.send_struct(reply, tx);
ipc_parent.send_struct(reply, tx);
}
}
@@ -412,7 +413,7 @@ pub fn spawn(rl: &mut Shell, module: &Module, args: Vec<(serde_json::Value, Opti
}
tx.send(Event2::Start);
let event = match engine::isolation::spawn_module(module, &tx, arg, keyring, verbose, has_stdin, proxy, options, blobs) {
let event = match ipc::parent::run(module, &tx, arg, keyring, verbose, has_stdin, proxy, options, blobs) {
Ok(exit) => exit,
Err(err) => ExitEvent::SetupFailed(err.to_string()),
};