]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
Add the Azure Entra Id Daemon
authorDavid Mulder <dmulder@samba.org>
Wed, 10 Jul 2024 20:04:28 +0000 (14:04 -0600)
committerDavid Mulder <dmulder@samba.org>
Wed, 23 Oct 2024 14:21:33 +0000 (14:21 +0000)
Signed-off-by: David Mulder <dmulder@samba.org>
Reviewed-by: Alexander Bokovoy <ab@samba.org>
himmelblaud/Cargo.toml
himmelblaud/build.rs [new file with mode: 0644]
himmelblaud/sock/Cargo.toml [new file with mode: 0644]
himmelblaud/sock/src/lib.rs [new file with mode: 0644]
himmelblaud/sock/src/proto.rs [new file with mode: 0644]
himmelblaud/src/constants.rs [new file with mode: 0644]
himmelblaud/src/himmelblaud.rs [new file with mode: 0644]
himmelblaud/src/main.rs
himmelblaud/wscript_build [new file with mode: 0644]
wscript_build

index bce2de8fe08c12990b758c1ed1c94224c2181efe..0ba4178a70e1553984f0c7f5bf269e1a191d3d47 100644 (file)
@@ -12,21 +12,36 @@ homepage.workspace = true
 version.workspace = true
 
 [dependencies]
+libhimmelblau = "0.2.7"
+ntstatus_gen.workspace = true
+param = { workspace = true }
+sock = { workspace = true }
 tdb = { workspace = true }
 dbg = { workspace = true }
+chelps.workspace = true
+clap = "4.5.9"
+kanidm-hsm-crypto = "0.2.0"
+serde_json = "1.0.120"
+tokio = { version = "1.38.1", features = [ "rt", "macros" ] }
+tokio-util = { version = "0.7.11", features = ["codec"] }
+bytes = "1.6.1"
+futures = "0.3.30"
+serde = "1.0.204"
+idmap = { workspace = true }
 libc = "0.2.155"
 
 [workspace]
 members = [
   "chelps", "dbg", "idmap",
   "ntstatus_gen",
-  "param", "tdb",
+  "param", "sock", "tdb",
 ]
 
 [workspace.dependencies]
 param = { path = "param" }
 dbg = { path = "dbg" }
 chelps = { path = "chelps" }
+sock = { path = "sock" }
 ntstatus_gen = { path = "ntstatus_gen" }
 tdb = { path = "tdb" }
 idmap = { path = "idmap" }
diff --git a/himmelblaud/build.rs b/himmelblaud/build.rs
new file mode 100644 (file)
index 0000000..1b50150
--- /dev/null
@@ -0,0 +1,18 @@
+use std::env;
+
+fn main() {
+    // Re-export the Target OS, so that Himmelblaud has access to this at
+    // runtime.
+    if &env::var("CARGO_CFG_TARGET_OS").unwrap() != "none" {
+        println!(
+            "cargo:rustc-env=TARGET_OS={}",
+            &env::var("CARGO_CFG_TARGET_OS").unwrap()
+        );
+    } else {
+        println!(
+            "cargo:rustc-env=TARGET_OS={}",
+            &env::var("CARGO_CFG_TARGET_FAMILY").unwrap()
+        );
+    }
+    println!("cargo:rerun-if-changed-env=TARGET");
+}
diff --git a/himmelblaud/sock/Cargo.toml b/himmelblaud/sock/Cargo.toml
new file mode 100644 (file)
index 0000000..675e520
--- /dev/null
@@ -0,0 +1,12 @@
+[package]
+name = "sock"
+edition.workspace = true
+license.workspace = true
+homepage.workspace = true
+version.workspace = true
+
+[dependencies]
+libc = "0.2.155"
+libnss = "0.8.0"
+serde = { version = "1.0.204", features = ["derive"] }
+serde_json = "1.0.120"
diff --git a/himmelblaud/sock/src/lib.rs b/himmelblaud/sock/src/lib.rs
new file mode 100644 (file)
index 0000000..e52ad39
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   Unix socket communication for the Himmelblau daemon
+
+   Copyright (C) David Mulder 2024
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+mod proto;
+pub use proto::*;
+
+use serde_json::{from_slice as json_from_slice, to_vec as json_to_vec};
+use std::error::Error;
+use std::io::{Read, Write};
+use std::os::unix::net::UnixStream;
+use std::time::Duration;
+
+pub struct ClientStream {
+    stream: UnixStream,
+}
+
+impl ClientStream {
+    pub fn new(path: &str) -> Result<Self, Box<dyn Error>> {
+        Ok(ClientStream {
+            stream: UnixStream::connect(path)?,
+        })
+    }
+
+    pub fn send(
+        &mut self,
+        req: &Request,
+        timeout: u64,
+    ) -> Result<Response, Box<dyn Error>> {
+        let timeout = Duration::from_secs(timeout);
+        self.stream.set_read_timeout(Some(timeout))?;
+        self.stream.set_write_timeout(Some(timeout))?;
+        let req_bytes = json_to_vec(req)?;
+        self.stream.write_all(&req_bytes)?;
+        let mut buf = Vec::new();
+        self.stream.read_to_end(&mut buf)?;
+        let resp: Response = json_from_slice(&buf)?;
+        Ok(resp)
+    }
+}
diff --git a/himmelblaud/sock/src/proto.rs b/himmelblaud/sock/src/proto.rs
new file mode 100644 (file)
index 0000000..ad40c50
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   Unix socket communication for the Himmelblau daemon
+
+   Copyright (C) David Mulder 2024
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+use libc::uid_t;
+use libnss::group::Group as NssGroup;
+use libnss::passwd::Passwd as NssPasswd;
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct Passwd {
+    pub name: String,
+    pub passwd: String,
+    pub uid: u32,
+    pub gid: u32,
+    pub gecos: String,
+    pub dir: String,
+    pub shell: String,
+}
+
+impl From<Passwd> for NssPasswd {
+    fn from(val: Passwd) -> Self {
+        NssPasswd {
+            name: val.name,
+            passwd: val.passwd,
+            uid: val.uid,
+            gid: val.gid,
+            gecos: val.gecos,
+            dir: val.dir,
+            shell: val.shell,
+        }
+    }
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct Group {
+    pub name: String,
+    pub passwd: String,
+    pub gid: u32,
+    pub members: Vec<String>,
+}
+
+impl From<Group> for NssGroup {
+    fn from(val: Group) -> Self {
+        NssGroup {
+            name: val.name,
+            passwd: val.passwd,
+            gid: val.gid,
+            members: val.members,
+        }
+    }
+}
+
+#[derive(Serialize, Deserialize)]
+pub enum PamAuthRequest {
+    Password { cred: String },
+    MFACode { cred: String },
+    MFAPoll { poll_attempt: u32 },
+    SetupPin { pin: String },
+    Pin { pin: String },
+}
+
+#[derive(Serialize, Deserialize)]
+pub enum Request {
+    NssAccounts,
+    NssAccountByUid(uid_t),
+    NssAccountByName(String),
+    NssGroups,
+    NssGroupByGid(uid_t),
+    NssGroupByName(String),
+    PamAuthenticateInit(String),
+    PamAuthenticateStep(PamAuthRequest),
+    PamAccountAllowed(String),
+    PamAccountBeginSession(String),
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub enum PamAuthResponse {
+    Unknown,
+    Success,
+    Denied,
+    Password,
+    MFACode { msg: String },
+    MFAPoll { msg: String, polling_interval: u32 },
+    SetupPin { msg: String },
+    Pin,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub enum Response {
+    NssAccounts(Vec<Passwd>),
+    NssAccount(Option<Passwd>),
+    NssGroups(Vec<Group>),
+    NssGroup(Option<Group>),
+    PamStatus(Option<bool>),
+    PamAuthStepResponse(PamAuthResponse),
+    Success,
+    Error,
+}
diff --git a/himmelblaud/src/constants.rs b/himmelblaud/src/constants.rs
new file mode 100644 (file)
index 0000000..e2a3647
--- /dev/null
@@ -0,0 +1 @@
+pub const DEFAULT_ODC_PROVIDER: &str = "odc.officeapps.live.com";
diff --git a/himmelblaud/src/himmelblaud.rs b/himmelblaud/src/himmelblaud.rs
new file mode 100644 (file)
index 0000000..b3ea0d4
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   Himmelblau daemon
+
+   Copyright (C) David Mulder 2024
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+use crate::cache::{GroupCache, PrivateCache, UserCache};
+use bytes::{BufMut, BytesMut};
+use dbg::{DBG_DEBUG, DBG_ERR};
+use futures::{SinkExt, StreamExt};
+use himmelblau::graph::Graph;
+use himmelblau::BrokerClientApplication;
+use idmap::Idmap;
+use kanidm_hsm_crypto::{BoxedDynTpm, MachineKey};
+use param::LoadParm;
+use sock::{Request, Response};
+use std::error::Error;
+use std::io;
+use std::io::{Error as IoError, ErrorKind};
+use std::sync::Arc;
+use tokio::net::UnixStream;
+use tokio::sync::Mutex;
+use tokio_util::codec::{Decoder, Encoder, Framed};
+
+pub(crate) struct Resolver {
+    realm: String,
+    tenant_id: String,
+    lp: LoadParm,
+    idmap: Idmap,
+    graph: Graph,
+    pcache: PrivateCache,
+    user_cache: UserCache,
+    group_cache: GroupCache,
+    hsm: Mutex<BoxedDynTpm>,
+    machine_key: MachineKey,
+    client: Arc<Mutex<BrokerClientApplication>>,
+}
+
+impl Resolver {
+    pub(crate) fn new(
+        realm: &str,
+        tenant_id: &str,
+        lp: LoadParm,
+        idmap: Idmap,
+        graph: Graph,
+        pcache: PrivateCache,
+        user_cache: UserCache,
+        group_cache: GroupCache,
+        hsm: BoxedDynTpm,
+        machine_key: MachineKey,
+        client: BrokerClientApplication,
+    ) -> Self {
+        Resolver {
+            realm: realm.to_string(),
+            tenant_id: tenant_id.to_string(),
+            lp,
+            idmap,
+            graph,
+            pcache,
+            user_cache,
+            group_cache,
+            hsm: Mutex::new(hsm),
+            machine_key,
+            client: Arc::new(Mutex::new(client)),
+        }
+    }
+}
+
+struct ClientCodec;
+
+impl Decoder for ClientCodec {
+    type Error = io::Error;
+    type Item = Request;
+
+    fn decode(
+        &mut self,
+        src: &mut BytesMut,
+    ) -> Result<Option<Self::Item>, Self::Error> {
+        match serde_json::from_slice::<Request>(src) {
+            Ok(msg) => {
+                src.clear();
+                Ok(Some(msg))
+            }
+            _ => Ok(None),
+        }
+    }
+}
+
+impl Encoder<Response> for ClientCodec {
+    type Error = io::Error;
+
+    fn encode(
+        &mut self,
+        msg: Response,
+        dst: &mut BytesMut,
+    ) -> Result<(), Self::Error> {
+        DBG_DEBUG!("Attempting to send response -> {:?} ...", msg);
+        let data = serde_json::to_vec(&msg).map_err(|e| {
+            DBG_ERR!("socket encoding error -> {:?}", e);
+            io::Error::new(ErrorKind::Other, "JSON encode error")
+        })?;
+        dst.put(data.as_slice());
+        Ok(())
+    }
+}
+
+impl ClientCodec {
+    fn new() -> Self {
+        ClientCodec
+    }
+}
+
+pub(crate) async fn handle_client(
+    stream: UnixStream,
+    resolver: Arc<Mutex<Resolver>>,
+) -> Result<(), Box<dyn Error>> {
+    DBG_DEBUG!("Accepted connection");
+
+    let Ok(_ucred) = stream.peer_cred() else {
+        return Err(Box::new(IoError::new(
+            ErrorKind::Other,
+            "Unable to verify peer credentials.",
+        )));
+    };
+
+    let mut reqs = Framed::new(stream, ClientCodec::new());
+
+    while let Some(Ok(req)) = reqs.next().await {
+        let resp = match req {
+            _ => todo!(),
+        };
+        reqs.send(resp).await?;
+        reqs.flush().await?;
+        DBG_DEBUG!("flushed response!");
+    }
+
+    DBG_DEBUG!("Disconnecting client ...");
+    Ok(())
+}
index f328e4d9d04c31d0d70d16d21a07d1613be9d577..74997fc019432496d12be0d22f5c24fd1bd56e75 100644 (file)
@@ -1 +1,382 @@
-fn main() {}
+/*
+   Unix SMB/CIFS implementation.
+
+   Himmelblau daemon
+
+   Copyright (C) David Mulder 2024
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+use clap::{Arg, ArgAction, Command};
+use dbg::*;
+use himmelblau::graph::Graph;
+use himmelblau::BrokerClientApplication;
+use idmap::Idmap;
+use kanidm_hsm_crypto::soft::SoftTpm;
+use kanidm_hsm_crypto::{BoxedDynTpm, Tpm};
+use param::LoadParm;
+use std::path::{Path, PathBuf};
+use std::process::ExitCode;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::Arc;
+use tokio::net::UnixListener;
+use tokio::signal::unix::{signal, SignalKind};
+use tokio::sync::Mutex;
+
+mod constants;
+use constants::DEFAULT_ODC_PROVIDER;
+mod cache;
+mod himmelblaud;
+use cache::{GroupCache, PrivateCache, UserCache};
+
+#[tokio::main(flavor = "current_thread")]
+async fn main() -> ExitCode {
+    let clap_args = Command::new("himmelblaud")
+        .version(env!("CARGO_PKG_VERSION"))
+        .about("Samba Himmelblau Authentication Daemon")
+        .arg(
+            Arg::new("debuglevel")
+                .help("Set debug level")
+                .short('d')
+                .long("debuglevel")
+                .value_parser(
+                    clap::value_parser!(u16).range(0..(MAX_DEBUG_LEVEL as i64)),
+                )
+                .action(ArgAction::Set),
+        )
+        .arg(
+            Arg::new("debug-stdout")
+                .help("Send debug output to standard output")
+                .long("debug-stdout")
+                .action(ArgAction::SetTrue),
+        )
+        .arg(
+            Arg::new("configfile")
+                .help("Use alternative configuration file")
+                .short('s')
+                .long("configfile")
+                .action(ArgAction::Set),
+        )
+        .get_matches();
+
+    let stop_now = Arc::new(AtomicBool::new(false));
+    let terminate_now = Arc::clone(&stop_now);
+    let quit_now = Arc::clone(&stop_now);
+    let interrupt_now = Arc::clone(&stop_now);
+
+    async {
+        // Set the command line debug level
+        if let Some(debuglevel) = clap_args.get_one::<u16>("debuglevel") {
+            debuglevel_set!(*debuglevel);
+        }
+
+        // Initialize the LoadParm from the command line specified config file
+        let lp = match LoadParm::new(
+            clap_args
+                .get_one::<String>("configfile")
+                .map(|x| x.as_str()),
+        ) {
+            Ok(lp) => lp,
+            Err(e) => {
+                eprintln!("Failed loading smb.conf: {:?}", e);
+                return ExitCode::FAILURE;
+            }
+        };
+
+        // Check that the realm is configured. This the bare minimum for
+        // himmelblaud, since a device join can happen at authentication time,
+        // but we need to know the permitted enrollment domain.
+        let realm = match lp.realm() {
+            Ok(Some(realm)) => realm,
+            _ => {
+                eprintln!(
+                    "The realm MUST be set in the \
+                    smb.conf to start himmelblaud"
+                );
+                return ExitCode::FAILURE;
+            }
+        };
+
+        // Setup logging, either to the configured logfile, or to stdout, depending
+        // on what is specified on the command line.
+        match lp.logfile() {
+            Ok(Some(logfile)) => debug_set_logfile(&logfile),
+            _ => {
+                eprintln!("Failed to determine logfile name");
+                return ExitCode::FAILURE;
+            }
+        }
+        match clap_args.get_flag("debug-stdout") {
+            true => setup_logging(env!("CARGO_PKG_NAME"), DEBUG_STDOUT),
+            false => setup_logging(env!("CARGO_PKG_NAME"), DEBUG_FILE),
+        }
+
+        // Determine the unix socket path
+        let sock_dir_str = match lp.winbindd_socket_directory() {
+            Ok(Some(sock_dir)) => sock_dir,
+            _ => return ExitCode::FAILURE,
+        };
+        let sock_dir = Path::new(&sock_dir_str);
+        let mut sock_path = PathBuf::from(sock_dir);
+        sock_path.push("hb_pipe");
+        let sock_path = match sock_path.to_str() {
+            Some(sock_path) => sock_path,
+            None => return ExitCode::FAILURE,
+        };
+
+        // Initialize the Himmelblau cache
+        let private_cache_path = match lp.private_path("himmelblau.tdb") {
+            Ok(Some(private_cache_path)) => private_cache_path,
+            _ => {
+                DBG_ERR!("Failed to determine private cache path");
+                return ExitCode::FAILURE;
+            }
+        };
+        let mut pcache = match PrivateCache::new(&private_cache_path) {
+            Ok(cache) => cache,
+            Err(e) => {
+                DBG_ERR!(
+                    "Failed to open the himmelblau private cache: {:?}",
+                    e
+                );
+                return ExitCode::FAILURE;
+            }
+        };
+
+        let cache_dir = match lp.cache_directory() {
+            Ok(Some(cache_dir)) => cache_dir,
+            _ => {
+                DBG_ERR!("Failed to determine cache directory");
+                return ExitCode::FAILURE;
+            }
+        };
+
+        let user_cache_path = Path::new(&cache_dir)
+            .join("himmelblau_users.tdb")
+            .display()
+            .to_string();
+        let user_cache = match UserCache::new(&user_cache_path) {
+            Ok(cache) => cache,
+            Err(e) => {
+                DBG_ERR!("Failed to open the himmelblau user cache: {:?}", e);
+                return ExitCode::FAILURE;
+            }
+        };
+
+        let group_cache_path = Path::new(&cache_dir)
+            .join("himmelblau_groups.tdb")
+            .display()
+            .to_string();
+        let group_cache = match GroupCache::new(&group_cache_path) {
+            Ok(cache) => cache,
+            Err(e) => {
+                DBG_ERR!("Failed to open the himmelblau group cache: {:?}", e);
+                return ExitCode::FAILURE;
+            }
+        };
+
+        // Check for and create the hsm pin if required.
+        let auth_value = match pcache.hsm_pin_fetch_or_create() {
+            Ok(auth_value) => auth_value,
+            Err(e) => {
+                DBG_ERR!("{:?}", e);
+                return ExitCode::FAILURE;
+            }
+        };
+
+        // Setup the HSM and its machine key
+        let mut hsm: BoxedDynTpm = BoxedDynTpm::new(SoftTpm::new());
+
+        let loadable_machine_key = match pcache
+            .loadable_machine_key_fetch_or_create(&mut hsm, &auth_value)
+        {
+            Ok(lmk) => lmk,
+            Err(e) => {
+                DBG_ERR!("{:?}", e);
+                return ExitCode::FAILURE;
+            }
+        };
+
+        let res = hsm.machine_key_load(&auth_value, &loadable_machine_key);
+        let machine_key = match res {
+            Ok(machine_key) => machine_key,
+            Err(e) => {
+                DBG_ERR!("Unable to load machine root key: {:?}", e);
+                DBG_INFO!("This can occur if you have changed your HSM pin.");
+                DBG_INFO!(
+                    "To proceed, run `tdbtool erase {}`",
+                    private_cache_path
+                );
+                DBG_INFO!("The host will forget domain enrollments.");
+                return ExitCode::FAILURE;
+            }
+        };
+
+        // Get the transport key for a joined domain
+        let loadable_transport_key =
+            pcache.loadable_transport_key_fetch(&realm);
+
+        // Get the certificate key for a joined domain
+        let loadable_cert_key = pcache.loadable_cert_key_fetch(&realm);
+
+        // Contact the odc provider to get the authority host and tenant id
+        let graph = match Graph::new(DEFAULT_ODC_PROVIDER, &realm).await {
+            Ok(graph) => graph,
+            Err(e) => {
+                DBG_ERR!("Failed initializing the graph: {:?}", e);
+                return ExitCode::FAILURE;
+            }
+        };
+        let authority_host = graph.authority_host();
+        let tenant_id = graph.tenant_id();
+        let authority = format!("https://{}/{}", authority_host, tenant_id);
+
+        let client = match BrokerClientApplication::new(
+            Some(&authority),
+            loadable_transport_key,
+            loadable_cert_key,
+        ) {
+            Ok(client) => client,
+            Err(e) => {
+                DBG_ERR!("Failed initializing the broker: {:?}", e);
+                return ExitCode::FAILURE;
+            }
+        };
+
+        let mut idmap = match Idmap::new() {
+            Ok(idmap) => idmap,
+            Err(e) => {
+                DBG_ERR!("Failed initializing the idmapper: {:?}", e);
+                return ExitCode::FAILURE;
+            }
+        };
+        // Configure the idmap range
+        let (low, high) = match lp.idmap_range(&realm) {
+            Ok(res) => res,
+            Err(e) => {
+                DBG_ERR!("Failed fetching idmap range: {:?}", e);
+                return ExitCode::FAILURE;
+            }
+        };
+        if let Err(e) = idmap.add_gen_domain(&realm, &tenant_id, (low, high)) {
+            DBG_ERR!("Failed adding the domain idmap range: {:?}", e);
+            return ExitCode::FAILURE;
+        }
+
+        let resolver = Arc::new(Mutex::new(himmelblaud::Resolver::new(
+            &realm,
+            &tenant_id,
+            lp,
+            idmap,
+            graph,
+            pcache,
+            user_cache,
+            group_cache,
+            hsm,
+            machine_key,
+            client,
+        )));
+
+        // Listen for incomming requests from PAM and NSS
+        let listener = match UnixListener::bind(sock_path) {
+            Ok(listener) => listener,
+            Err(e) => {
+                DBG_ERR!("Failed setting up the socket listener: {:?}", e);
+                return ExitCode::FAILURE;
+            }
+        };
+
+        let server = tokio::spawn(async move {
+            while !stop_now.load(Ordering::Relaxed) {
+                let resolver_ref = resolver.clone();
+                match listener.accept().await {
+                    Ok((socket, _addr)) => {
+                        tokio::spawn(async move {
+                            if let Err(e) = himmelblaud::handle_client(
+                                socket,
+                                resolver_ref.clone(),
+                            )
+                            .await
+                            {
+                                DBG_ERR!(
+                                    "handle_client error occurred: {:?}",
+                                    e
+                                );
+                            }
+                        });
+                    }
+                    Err(e) => {
+                        DBG_ERR!("Error while handling connection: {:?}", e);
+                    }
+                }
+            }
+        });
+
+        let terminate_task = tokio::spawn(async move {
+            match signal(SignalKind::terminate()) {
+                Ok(mut stream) => {
+                    stream.recv().await;
+                    terminate_now.store(true, Ordering::Relaxed);
+                }
+                Err(e) => {
+                    DBG_ERR!("Failed registering terminate signal: {}", e);
+                }
+            };
+        });
+
+        let quit_task = tokio::spawn(async move {
+            match signal(SignalKind::quit()) {
+                Ok(mut stream) => {
+                    stream.recv().await;
+                    quit_now.store(true, Ordering::Relaxed);
+                }
+                Err(e) => {
+                    DBG_ERR!("Failed registering quit signal: {}", e);
+                }
+            };
+        });
+
+        let interrupt_task = tokio::spawn(async move {
+            match signal(SignalKind::interrupt()) {
+                Ok(mut stream) => {
+                    stream.recv().await;
+                    interrupt_now.store(true, Ordering::Relaxed);
+                }
+                Err(e) => {
+                    DBG_ERR!("Failed registering interrupt signal: {}", e);
+                }
+            };
+        });
+
+        DBG_INFO!("Server started ...");
+
+        tokio::select! {
+            _ = server => {
+                DBG_DEBUG!("Main listener task is terminating");
+            },
+            _ = terminate_task => {
+                DBG_DEBUG!("Received signal to terminate");
+            },
+            _ = quit_task => {
+                DBG_DEBUG!("Received signal to quit");
+            },
+            _ = interrupt_task => {
+                DBG_DEBUG!("Received signal to interrupt");
+            }
+        }
+
+        ExitCode::SUCCESS
+    }
+    .await
+}
diff --git a/himmelblaud/wscript_build b/himmelblaud/wscript_build
new file mode 100644 (file)
index 0000000..f302ab1
--- /dev/null
@@ -0,0 +1,4 @@
+#!/usr/bin/env python
+
+bld.SAMBA_RUST_BINARY('himmelblaud',
+                      source='src/main.rs param/src/lib.rs chelps/src/lib.rs dbg/src/lib.rs ntstatus_gen/src/lib.rs sock/src/lib.rs tdb/src/lib.rs version/src/lib.rs')
index 3ee2cc5243e2507727f43add1225b8670161e8e8..d2b10a45134ce55955807f741a69f1a5fc5488e0 100644 (file)
@@ -155,6 +155,7 @@ bld.RECURSE('dfs_server')
 bld.RECURSE('file_server')
 bld.RECURSE('lib/krb5_wrap')
 bld.RECURSE('packaging')
+bld.RECURSE('himmelblaud')
 
 bld.RECURSE('testsuite/headers')