Add TLS support to resolver, implement DBus ifaces

This commit is contained in:
Matteo Settenvini 2022-08-24 14:46:14 +02:00
parent b5797b12f1
commit c52195dd8b
Signed by: matteo
GPG key ID: 8576CC1AD97D42DF
12 changed files with 308 additions and 221 deletions

View file

@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: 2022 Matteo Settenvini <matteo.settenvini@montecristosoftware.eu>
// SPDX-License-Identifier: GPL-3.0-or-later
use {
serde::{Deserialize, Serialize},
std::net::IpAddr,
zbus::{dbus_proxy, Result},
};
#[dbus_proxy(
default_service = "com.endlessm.ParentalControls",
interface = "com.endlessm.ParentalControls.Dns",
default_path = "/com/endlessm/ParentalControls/Dns",
gen_blocking = false
)]
trait MalcontentDns {
fn get_restrictions(&self, uid: u32) -> Result<Restrictions>;
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, zvariant::Type)]
pub struct Restriction {
pub ip: IpAddr,
pub hostname: String,
}
pub type Restrictions = Vec<Restriction>;

111
src/policy_checker/mod.rs Normal file
View file

@ -0,0 +1,111 @@
// SPDX-FileCopyrightText: 2022 Matteo Settenvini <matteo.settenvini@montecristosoftware.eu>
// SPDX-License-Identifier: GPL-3.0-or-later
mod dbus;
use {
self::dbus::*,
anyhow::Result,
nix::unistd::{getuid, Uid},
once_cell::sync::Lazy,
std::collections::HashMap,
std::net::{SocketAddr, TcpStream},
std::sync::{Arc, RwLock},
trust_dns_proto::rr::domain::Name as DomainName,
trust_dns_resolver::config as dns_config,
trust_dns_resolver::TokioAsyncResolver,
};
pub use self::dbus::{Restriction, Restrictions};
const DNS_UDP_PORT: u16 = 53;
const DNS_TLS_PORT: u16 = 853;
pub static POLICY_CHECKER: Lazy<PolicyChecker> = Lazy::new(|| PolicyChecker::new());
// TODO: accept notifications about config changes
pub struct PolicyChecker {
resolvers: RwLock<HashMap<Uid, Option<Arc<TokioAsyncResolver>>>>,
}
impl PolicyChecker {
pub fn new() -> Self {
Self {
resolvers: RwLock::new(HashMap::new()),
}
}
async fn restrictions<'a>(&'a self, user: Uid) -> Result<Restrictions> {
if user.is_root() {
return Ok(vec![]);
};
let connection = zbus::Connection::session().await?;
let proxy = MalcontentDnsProxy::new(&connection).await?;
Ok(proxy.get_restrictions(user.as_raw()).await?)
}
pub async fn resolver(&self, user: Option<Uid>) -> Result<Option<Arc<TokioAsyncResolver>>> {
let user = user.unwrap_or_else(|| getuid());
// Check if already cached
{
let ro_resolvers = self.resolvers.read().unwrap();
if let Some(resolver) = ro_resolvers.get(&user) {
return Ok(resolver.clone());
}
}
// Else, initialize and cache it
{
let mut rw_resolvers = self.resolvers.write().unwrap();
if let Some(resolver) = rw_resolvers.get(&user) {
return Ok(resolver.clone());
}
// try first to prime resolver with DoT implementation,
// fallback to unencrypted only if not available.
let restrictions = self.restrictions(user).await?;
let resolver = if !restrictions.is_empty() {
let resolver = TokioAsyncResolver::tokio(
resolver_config_for(restrictions),
dns_config::ResolverOpts::default(),
)?;
Some(Arc::new(resolver))
} else {
None
};
rw_resolvers.insert(user, resolver.clone());
Ok(resolver)
}
}
}
fn resolver_config_for(restrictions: Vec<Restriction>) -> dns_config::ResolverConfig {
use dns_config::NameServerConfigGroup as NsConfig;
let resolver_config_group =
restrictions
.into_iter()
.fold(NsConfig::new(), |mut config, restr| {
let new_config =
if TcpStream::connect(SocketAddr::new(restr.ip, DNS_TLS_PORT)).is_ok() {
NsConfig::from_ips_tls(&[restr.ip], DNS_TLS_PORT, restr.hostname, true)
} else {
NsConfig::from_ips_clear(&[restr.ip], DNS_UDP_PORT, true)
};
config.merge(new_config);
config
});
let basename = gethostname::gethostname()
.as_os_str()
.to_str()
.map(|hn| DomainName::from_labels(hn.split('.')).ok())
.flatten()
.map(|hn| hn.base_name());
dns_config::ResolverConfig::from_parts(basename, vec![], resolver_config_group)
}