Move to rust stable, introduce mock for dbus in tests

This commit is contained in:
Matteo Settenvini 2022-08-18 13:59:11 +02:00
parent 37ef5d4e65
commit 96663abaef
Signed by: matteo
GPG key ID: 8576CC1AD97D42DF
9 changed files with 234 additions and 55 deletions

View file

@ -6,20 +6,25 @@
#![allow(non_snake_case)]
#![allow(dead_code)]
use std::str::FromStr;
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
include!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/constants.rs"));
use {
anyhow::{anyhow, bail, ensure, Result},
futures_util::TryStreamExt,
libc::{freeaddrinfo, gai_strerror, getaddrinfo},
nix::sys::socket::{SockaddrLike as _, SockaddrStorage},
nix::unistd::Uid,
std::collections::HashMap,
std::env,
std::ffi::{CStr, CString},
std::net::{IpAddr, Ipv4Addr, Ipv6Addr},
std::path::PathBuf,
std::process::Command,
std::str::FromStr,
std::sync::Once,
tokio::task,
tokio::task::JoinHandle,
};
pub type Eai = EaiRetcode;
@ -106,3 +111,72 @@ pub fn resolve_system_and_us(hostname: &str) -> Result<(IpAddr, IpAddr)> {
Ok((ip_from_system, ip_from_us))
}
}
pub type Restrictions = Vec<IpAddr>;
pub fn mock_dbus(responses: HashMap<Uid, Vec<Restrictions>>) -> JoinHandle<Result<()>> {
async fn dbus_loop(mut responses: HashMap<Uid, Vec<Restrictions>>) -> Result<()> {
use zbus::MessageType::*;
let mut responses_size: usize = responses.values().map(|v| v.len()).sum();
for r in responses.values_mut() {
r.reverse(); // we pop responses from the back, so...
}
let connection = zbus::ConnectionBuilder::session()?.build().await?;
let mut stream = zbus::MessageStream::from(&connection);
while responses_size > 0 {
let msg = stream
.try_next()
.await?
.ok_or(anyhow!("Unparseable DBus message"))?;
if msg.header()?.message_type()? == MethodCall
&& msg
.interface()
.ok_or(anyhow!("Invoked method has no interface"))?
== DBUS_INTERFACE
&& msg
.member()
.ok_or(anyhow!("Invoked method has no member"))?
== DBUS_GET_RESTRICTIONS_METHOD
{
let user_id: u32 = msg.body()?;
match responses.get_mut(&Uid::from(user_id)) {
Some(answers) => match answers.pop() {
Some(a) => {
responses_size = responses_size - 1;
let ips: Vec<String> = a.into_iter().map(|ip| ip.to_string()).collect();
connection.reply(&msg, &ips).await
}
None => {
connection
.reply_error(
&msg,
"MockExhausted",
&format!("DBus mock is saturated for user with id {}", user_id),
)
.await
}
},
None => {
connection
.reply_error(
&msg,
"MockExhausted",
&format!(
"No mocked invocations available for user with id {}",
user_id
),
)
.await
}
}?;
}
}
Ok(())
}
task::spawn(async { dbus_loop(responses).await })
}

View file

@ -7,10 +7,25 @@ use {
crate::common::Eai,
anyhow::Result,
libc::{freeaddrinfo, gai_strerror, getaddrinfo},
std::net::{IpAddr, Ipv4Addr},
nix::unistd::getuid,
once_cell::sync::Lazy,
std::collections::HashMap,
std::net::{IpAddr, Ipv4Addr, Ipv6Addr},
std::time::Duration,
tokio::time::timeout,
};
static CLOUDFLARE_PARENTALCONTROL_ADDRS: Lazy<Vec<IpAddr>> = Lazy::new(|| {
vec![
IpAddr::V4(Ipv4Addr::new(1, 1, 1, 3)),
IpAddr::V4(Ipv4Addr::new(1, 0, 0, 3)),
IpAddr::V6(Ipv6Addr::new(2606, 4700, 4700, 0, 0, 0, 0, 1113)),
IpAddr::V6(Ipv6Addr::new(2606, 4700, 4700, 0, 0, 0, 0, 1003)),
]
});
#[test]
#[ignore]
fn nss_module_is_loaded() -> Result<()> {
common::setup()?;
@ -37,10 +52,15 @@ fn nss_module_is_loaded() -> Result<()> {
Ok(())
}
#[test]
fn application_dns_is_nxdomain() -> Result<()> {
common::setup()?;
#[tokio::test]
#[ignore]
async fn application_dns_is_nxdomain() -> Result<()> {
let dbus = common::mock_dbus(HashMap::from([(
getuid(),
vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()],
)]));
common::setup()?;
let hostname = std::ffi::CString::new("use-application-dns.net").unwrap();
unsafe {
let mut addr = std::ptr::null_mut();
@ -60,32 +80,43 @@ fn application_dns_is_nxdomain() -> Result<()> {
);
freeaddrinfo(addr);
};
Ok(())
timeout(Duration::from_secs(1), dbus).await??
}
#[test]
#[tokio::test]
#[ignore]
async fn wikipedia_is_unrestricted() -> Result<()> {
let dbus = common::mock_dbus(HashMap::from([(
getuid(),
vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()],
)]));
fn wikipedia_is_unrestricted() -> Result<()> {
let (system_addr, our_addr) = common::resolve_system_and_us("wikipedia.org")?;
assert_eq!(system_addr, our_addr);
Ok(())
timeout(Duration::from_secs(1), dbus).await??
}
#[test]
#[tokio::test]
#[ignore]
fn adultsite_is_restricted() -> Result<()> {
async fn adultsite_is_restricted() -> Result<()> {
let dbus = common::mock_dbus(HashMap::from([(
getuid(),
vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()],
)]));
let (system_addr, our_addr) = common::resolve_system_and_us("pornhub.com")?;
assert_ne!(system_addr, our_addr);
assert_eq!(our_addr, IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)));
Ok(())
timeout(Duration::from_secs(1), dbus).await??
}
#[test]
#[tokio::test]
#[ignore]
fn root_user_bypasses_restrictions() -> Result<()> {
// TODO fake root
async fn privileged_user_bypasses_restrictions() -> Result<()> {
let dbus = common::mock_dbus(HashMap::from([(getuid(), vec![ /* no restriction */])]));
let (system_addr, our_addr) = common::resolve_system_and_us("pornhub.com")?;
assert_eq!(system_addr, our_addr);
Ok(())
timeout(Duration::from_secs(1), dbus).await??
}