2022-08-13 17:04:22 +02:00
|
|
|
// SPDX-FileCopyrightText: 2022 Matteo Settenvini <matteo.settenvini@montecristosoftware.eu>
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
|
|
|
mod common;
|
|
|
|
|
2022-08-14 23:03:59 +02:00
|
|
|
use {
|
2022-08-24 14:46:14 +02:00
|
|
|
crate::common::{Eai, Restriction, Restrictions},
|
2022-08-15 22:55:35 +02:00
|
|
|
anyhow::Result,
|
2022-08-14 23:03:59 +02:00
|
|
|
libc::{freeaddrinfo, gai_strerror, getaddrinfo},
|
2022-08-18 13:59:11 +02:00
|
|
|
nix::unistd::getuid,
|
|
|
|
once_cell::sync::Lazy,
|
|
|
|
std::collections::HashMap,
|
|
|
|
std::net::{IpAddr, Ipv4Addr, Ipv6Addr},
|
2022-08-14 23:03:59 +02:00
|
|
|
};
|
2022-08-13 17:04:22 +02:00
|
|
|
|
2022-08-24 14:46:14 +02:00
|
|
|
static CLOUDFLARE_PARENTALCONTROL_ADDRS: Lazy<Restrictions> = Lazy::new(|| {
|
2022-08-18 13:59:11 +02:00
|
|
|
vec![
|
2022-08-24 14:46:14 +02:00
|
|
|
Restriction {
|
|
|
|
ip: IpAddr::V4(Ipv4Addr::new(1, 1, 1, 3)),
|
2022-09-05 23:15:29 +02:00
|
|
|
hostname: "family.cloudflare-dns.com".into(),
|
2022-08-24 14:46:14 +02:00
|
|
|
},
|
|
|
|
Restriction {
|
|
|
|
ip: IpAddr::V4(Ipv4Addr::new(1, 0, 0, 3)),
|
2022-09-05 23:15:29 +02:00
|
|
|
hostname: "family.cloudflare-dns.com".into(),
|
2022-08-24 14:46:14 +02:00
|
|
|
},
|
|
|
|
Restriction {
|
2022-09-05 23:15:29 +02:00
|
|
|
ip: IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1113)),
|
|
|
|
hostname: "family.cloudflare-dns.com".into(),
|
2022-08-24 14:46:14 +02:00
|
|
|
},
|
|
|
|
Restriction {
|
2022-09-05 23:15:29 +02:00
|
|
|
ip: IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1003)),
|
|
|
|
hostname: "family.cloudflare-dns.com".into(),
|
2022-08-24 14:46:14 +02:00
|
|
|
},
|
2022-08-18 13:59:11 +02:00
|
|
|
]
|
|
|
|
});
|
|
|
|
|
2022-09-05 23:15:29 +02:00
|
|
|
static NO_RESTRICTIONS: Lazy<Restrictions> = Lazy::new(|| vec![]);
|
|
|
|
|
2022-08-21 14:47:37 +02:00
|
|
|
fork_test! {
|
|
|
|
#[test]
|
|
|
|
fn nss_module_is_loaded() -> Result<()> {
|
|
|
|
common::setup()?;
|
2022-09-05 23:15:29 +02:00
|
|
|
tokio::runtime::Runtime::new().unwrap().block_on(async {
|
|
|
|
let _dbus = common::mock_dbus(HashMap::from([(getuid(), vec![NO_RESTRICTIONS.clone()])])).await?;
|
|
|
|
common::resolve_with_module(libc::AF_INET, "gnome.org")?;
|
|
|
|
Ok(())
|
|
|
|
})
|
2022-08-21 14:47:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn application_dns_is_nxdomain() -> Result<()> {
|
|
|
|
common::setup()?;
|
|
|
|
tokio::runtime::Runtime::new().unwrap().block_on(async {
|
2022-08-25 01:00:18 +02:00
|
|
|
let _dbus = common::mock_dbus(HashMap::from([(
|
2022-08-21 14:47:37 +02:00
|
|
|
getuid(),
|
|
|
|
vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()],
|
2022-08-25 01:00:18 +02:00
|
|
|
)])).await?;
|
2022-08-21 14:47:37 +02:00
|
|
|
|
|
|
|
let hostname = std::ffi::CString::new("use-application-dns.net").unwrap();
|
|
|
|
unsafe {
|
|
|
|
let mut addr = std::ptr::null_mut();
|
|
|
|
let getaddrinfo_status = getaddrinfo(
|
|
|
|
hostname.as_ptr(),
|
|
|
|
std::ptr::null(),
|
|
|
|
std::ptr::null(),
|
|
|
|
&mut addr,
|
|
|
|
);
|
|
|
|
|
|
|
|
let error = std::ffi::CStr::from_ptr(gai_strerror(getaddrinfo_status));
|
|
|
|
assert_eq!(
|
|
|
|
getaddrinfo_status,
|
|
|
|
Eai::NoName.0,
|
|
|
|
"Should have gotten no hostname (NXDOMAIN), instead got {}",
|
|
|
|
error.to_str().unwrap()
|
|
|
|
);
|
|
|
|
freeaddrinfo(addr);
|
|
|
|
};
|
|
|
|
|
2022-08-25 01:00:18 +02:00
|
|
|
Ok(())
|
2022-08-21 14:47:37 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-08-23 11:07:17 +02:00
|
|
|
#[test]
|
|
|
|
fn getaddrinfo_resolution() -> Result<()> {
|
|
|
|
common::setup()?;
|
2022-08-24 14:46:14 +02:00
|
|
|
|
|
|
|
const HOSTNAME: &str = "gnome.org";
|
|
|
|
|
2022-08-23 11:07:17 +02:00
|
|
|
tokio::runtime::Runtime::new().unwrap().block_on(async {
|
2022-08-25 01:00:18 +02:00
|
|
|
let _dbus = common::mock_dbus(HashMap::from([(
|
2022-08-23 11:07:17 +02:00
|
|
|
getuid(),
|
|
|
|
vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()],
|
2022-08-25 01:00:18 +02:00
|
|
|
)])).await?;
|
2022-08-23 11:07:17 +02:00
|
|
|
|
|
|
|
unsafe {
|
|
|
|
let mut addr = std::ptr::null_mut();
|
|
|
|
let getaddrinfo_status = getaddrinfo(
|
2022-08-24 14:46:14 +02:00
|
|
|
std::ffi::CString::new(HOSTNAME)?.as_ptr(),
|
2022-08-23 11:07:17 +02:00
|
|
|
std::ptr::null(),
|
|
|
|
std::ptr::null(),
|
|
|
|
&mut addr,
|
|
|
|
);
|
|
|
|
|
|
|
|
let error = std::ffi::CStr::from_ptr(gai_strerror(getaddrinfo_status));
|
|
|
|
assert_eq!(
|
|
|
|
getaddrinfo_status,
|
|
|
|
Eai::Success.0,
|
|
|
|
"Should have gotten an hostname, instead got {}",
|
|
|
|
error.to_str().unwrap()
|
|
|
|
);
|
|
|
|
freeaddrinfo(addr);
|
|
|
|
};
|
|
|
|
|
2022-08-25 01:00:18 +02:00
|
|
|
Ok(())
|
2022-08-23 11:07:17 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-08-21 14:47:37 +02:00
|
|
|
#[test]
|
|
|
|
fn wikipedia_is_unrestricted() -> Result<()> {
|
|
|
|
common::setup()?;
|
|
|
|
|
2022-08-24 14:46:14 +02:00
|
|
|
const HOSTNAME: &str = "wikipedia.org";
|
2022-08-21 14:47:37 +02:00
|
|
|
|
2022-08-24 14:46:14 +02:00
|
|
|
tokio::runtime::Runtime::new().unwrap().block_on(async {
|
2022-09-12 00:38:01 +02:00
|
|
|
let _dbus = common::mock_dbus(HashMap::from([(
|
|
|
|
getuid(),
|
|
|
|
vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()],
|
|
|
|
)])).await?;
|
2022-08-24 14:46:14 +02:00
|
|
|
|
2022-09-12 00:38:01 +02:00
|
|
|
for family in [libc::AF_INET, libc::AF_INET6] {
|
2022-08-21 14:47:37 +02:00
|
|
|
let system_addr = common::resolve_with_system(family, HOSTNAME)?;
|
2022-09-05 23:15:29 +02:00
|
|
|
let our_addrs = common::resolve_with_module(family, HOSTNAME)?;
|
|
|
|
assert!(our_addrs.contains(&system_addr));
|
2022-08-24 14:46:14 +02:00
|
|
|
}
|
2022-08-22 23:12:34 +02:00
|
|
|
Ok(())
|
2022-08-21 14:47:37 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-08-24 14:46:14 +02:00
|
|
|
fn adultsite_is_restricted_ipv4() -> Result<()> {
|
2022-08-21 14:47:37 +02:00
|
|
|
common::setup()?;
|
|
|
|
|
2022-08-24 14:46:14 +02:00
|
|
|
const HOSTNAME: &str = "nudity.testcategory.com";
|
|
|
|
|
2022-08-21 14:47:37 +02:00
|
|
|
tokio::runtime::Runtime::new().unwrap().block_on(async {
|
2022-08-25 01:00:18 +02:00
|
|
|
let _dbus = common::mock_dbus(HashMap::from([(
|
2022-08-21 14:47:37 +02:00
|
|
|
getuid(),
|
|
|
|
vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()],
|
2022-08-25 01:00:18 +02:00
|
|
|
)])).await?;
|
2022-08-21 14:47:37 +02:00
|
|
|
|
|
|
|
let system_addr = common::resolve_with_system(libc::AF_INET, HOSTNAME)?;
|
2022-09-05 23:15:29 +02:00
|
|
|
let our_addrs = common::resolve_with_module(libc::AF_INET, HOSTNAME)?;
|
2022-09-12 00:38:01 +02:00
|
|
|
assert!(!our_addrs.contains(&system_addr), "Resolver answered with {:?}, should not contain {}", our_addrs, system_addr);
|
2022-09-05 23:15:29 +02:00
|
|
|
assert_eq!(our_addrs, [IpAddr::V4(Ipv4Addr::UNSPECIFIED)]);
|
2022-08-25 01:00:18 +02:00
|
|
|
Ok(())
|
2022-08-24 14:46:14 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn adultsite_is_restricted_ipv6() -> Result<()> {
|
|
|
|
common::setup()?;
|
|
|
|
|
|
|
|
const HOSTNAME: &str = "nudity.testcategory.com";
|
|
|
|
|
|
|
|
tokio::runtime::Runtime::new().unwrap().block_on(async {
|
2022-08-25 01:00:18 +02:00
|
|
|
let _dbus = common::mock_dbus(HashMap::from([(
|
2022-08-24 14:46:14 +02:00
|
|
|
getuid(),
|
|
|
|
vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()],
|
2022-08-25 01:00:18 +02:00
|
|
|
)])).await;
|
2022-08-24 14:46:14 +02:00
|
|
|
|
2022-08-21 14:47:37 +02:00
|
|
|
let system_addr = common::resolve_with_system(libc::AF_INET6, HOSTNAME)?;
|
2022-09-05 23:15:29 +02:00
|
|
|
let our_addrs = common::resolve_with_module(libc::AF_INET6, HOSTNAME)?;
|
2022-09-12 00:38:01 +02:00
|
|
|
assert!(!our_addrs.contains(&system_addr), "Resolver answered with {:?}, should not contain {}", our_addrs, system_addr);
|
2022-09-05 23:15:29 +02:00
|
|
|
assert_eq!(our_addrs, [IpAddr::V6(Ipv6Addr::UNSPECIFIED)]);
|
2022-08-25 01:00:18 +02:00
|
|
|
Ok(())
|
2022-08-21 14:47:37 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn privileged_user_bypasses_restrictions() -> Result<()> {
|
|
|
|
common::setup()?;
|
|
|
|
|
2022-09-05 23:15:29 +02:00
|
|
|
const HOSTNAME: &str = "malware.testcategory.com";
|
2022-08-21 14:47:37 +02:00
|
|
|
|
2022-08-24 14:46:14 +02:00
|
|
|
tokio::runtime::Runtime::new().unwrap().block_on(async {
|
2022-09-12 00:38:01 +02:00
|
|
|
let _dbus = common::mock_dbus(HashMap::from([(getuid(), vec![NO_RESTRICTIONS.clone()])])).await?;
|
2022-08-21 14:47:37 +02:00
|
|
|
for family in [libc::AF_INET, libc::AF_INET6] {
|
|
|
|
let system_addr = common::resolve_with_system(family, HOSTNAME)?;
|
2022-09-05 23:15:29 +02:00
|
|
|
let our_addrs = common::resolve_with_module(family, HOSTNAME)?;
|
|
|
|
assert!(our_addrs.contains(&system_addr));
|
2022-08-21 14:47:37 +02:00
|
|
|
}
|
2022-08-22 23:12:34 +02:00
|
|
|
Ok(())
|
2022-08-21 14:47:37 +02:00
|
|
|
})
|
|
|
|
}
|
2022-08-13 17:04:22 +02:00
|
|
|
}
|