]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
Add lp Rust bindings
authorDavid Mulder <dmulder@samba.org>
Tue, 30 Jul 2024 15:43:46 +0000 (09:43 -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>
docs-xml/smbdotconf/himmelblaud/himmelblaudhelloenabled.xml [new file with mode: 0644]
docs-xml/smbdotconf/himmelblaud/himmelblaudsfafallback.xml [new file with mode: 0644]
himmelblaud/Cargo.toml
himmelblaud/param/Cargo.toml [new file with mode: 0644]
himmelblaud/param/build.rs [new file with mode: 0644]
himmelblaud/param/src/lib.rs [new file with mode: 0644]
lib/param/loadparm.c
source3/param/loadparm.c

diff --git a/docs-xml/smbdotconf/himmelblaud/himmelblaudhelloenabled.xml b/docs-xml/smbdotconf/himmelblaud/himmelblaudhelloenabled.xml
new file mode 100644 (file)
index 0000000..b148362
--- /dev/null
@@ -0,0 +1,18 @@
+<samba:parameter name="himmelblaud hello enabled"
+                 context="G"
+                 type="boolean"
+                 xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+
+       <para>This parameter controls Hello enrollment and authentication to
+       Azure Entra ID. By default, it is disabled to prevent security risks,
+       such as on hosts exposing the SSH port. Administrators should enable
+       this setting only when Hello enrollment is appropriate for their
+       environment.
+
+</para>
+</description>
+
+<value type="default">no</value>
+<value type="example">yes</value>
+</samba:parameter>
diff --git a/docs-xml/smbdotconf/himmelblaud/himmelblaudsfafallback.xml b/docs-xml/smbdotconf/himmelblaud/himmelblaudsfafallback.xml
new file mode 100644 (file)
index 0000000..1b30d0a
--- /dev/null
@@ -0,0 +1,17 @@
+<samba:parameter name="himmelblaud sfa fallback"
+                 context="G"
+                 type="boolean"
+                 xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+
+       <para>This parameter is designed to control whether Himmelblaud should fallback to
+       Single Factor Authentication (SFA) if Multi-Factor Authentication (MFA) isn't
+       available. This normally is possible during a short window during which MFA
+       enrollment is available to a new user.
+
+</para>
+</description>
+
+<value type="default">no</value>
+<value type="example">yes</value>
+</samba:parameter>
index 69197cd5ff37be115c92f31baed257fa550d7f35..bdf487d30daa065cd87166c6a0e2a3cf56240aef 100644 (file)
@@ -17,9 +17,11 @@ dbg = { workspace = true }
 [workspace]
 members = [
   "chelps", "dbg", "ntstatus_gen",
+  "param",
 ]
 
 [workspace.dependencies]
+param = { path = "param" }
 dbg = { path = "dbg" }
 chelps = { path = "chelps" }
 ntstatus_gen = { path = "ntstatus_gen" }
diff --git a/himmelblaud/param/Cargo.toml b/himmelblaud/param/Cargo.toml
new file mode 100644 (file)
index 0000000..e90e500
--- /dev/null
@@ -0,0 +1,15 @@
+[package]
+name = "param"
+edition.workspace = true
+license.workspace = true
+homepage.workspace = true
+version.workspace = true
+
+[dependencies]
+chelps = { workspace = true }
+dbg.workspace = true
+ntstatus_gen.workspace = true
+paste = "1.0.15"
+
+[build-dependencies]
+bindgen = "0.69.4"
diff --git a/himmelblaud/param/build.rs b/himmelblaud/param/build.rs
new file mode 100644 (file)
index 0000000..d2fa9df
--- /dev/null
@@ -0,0 +1,48 @@
+use std::env;
+use std::path::PathBuf;
+
+fn main() {
+    let bindings = bindgen::Builder::default()
+        .blocklist_function("qgcvt")
+        .blocklist_function("qgcvt_r")
+        .blocklist_function("qfcvt")
+        .blocklist_function("qfcvt_r")
+        .blocklist_function("qecvt")
+        .blocklist_function("qecvt_r")
+        .blocklist_function("strtold")
+        .clang_arg("-Dbool=int")
+        .clang_arg("-Doffset_t=loff_t")
+        .clang_arg("-I../../bin/default")
+        .clang_arg("-includestdint.h")
+        .header("../../lib/param/param.h")
+        .header("../../lib/param/loadparm.h")
+        .header("../../source3/param/loadparm.h")
+        .header("../../bin/default/lib/param/param_functions.h")
+        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
+        .generate()
+        .expect("Unable to generate bindings");
+    println!(
+        "cargo:rerun-if-changed=../../bin/default/lib/param/param_functions.h"
+    );
+
+    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
+    bindings
+        .write_to_file(out_path.join("bindings.rs"))
+        .expect("Couldn't write bindings!");
+
+    let mut src_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
+    src_dir.push("../../bin/default/source3");
+    println!(
+        "cargo:rustc-link-search=native={}",
+        src_dir.to_str().unwrap()
+    );
+    println!("cargo:rustc-link-lib=smbconf");
+
+    let mut src_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
+    src_dir.push("../../bin/default/lib/param");
+    println!("cargo:rustc-link-lib=samba-hostconfig-private-samba");
+    println!(
+        "cargo:rustc-link-search=native={}",
+        src_dir.to_str().unwrap()
+    );
+}
diff --git a/himmelblaud/param/src/lib.rs b/himmelblaud/param/src/lib.rs
new file mode 100644 (file)
index 0000000..7b4bcaf
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   Parameter loading functions
+
+   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 chelps::{string_free, wrap_c_char, wrap_string};
+use dbg::{DBG_ERR, DBG_INFO, DBG_WARNING};
+use ntstatus_gen::NT_STATUS_UNSUCCESSFUL;
+use std::error::Error;
+use std::ffi::c_void;
+use std::sync::{Arc, Mutex};
+
+mod ffi {
+    #![allow(non_upper_case_globals)]
+    #![allow(non_camel_case_types)]
+    #![allow(non_snake_case)]
+    #![allow(dead_code)]
+    #![allow(clippy::upper_case_acronyms)]
+    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
+}
+
+pub struct LoadParm {
+    lp: Arc<Mutex<*mut ffi::loadparm_context>>,
+}
+
+macro_rules! lpcfg_str {
+    ($var:ident) => {
+        paste::item! {
+            pub fn $var (&self) -> Result<Option<String>, Box<dyn Error + '_>> {
+                let lp = self.lp.lock()?;
+                let val = unsafe { ffi::[< lpcfg_ $var >] (*lp) } ;
+                unsafe { Ok(wrap_c_char(val)) }
+            }
+        }
+    };
+}
+
+macro_rules! lpcfg_i32 {
+    ($var:ident) => {
+        paste::item! {
+            pub fn $var (&self) -> Result<i32, Box<dyn Error + '_>> {
+                let lp = self.lp.lock()?;
+                unsafe { Ok(ffi::[< lpcfg_ $var >] (*lp)) }
+            }
+        }
+    };
+}
+
+macro_rules! lpcfg_bool {
+    ($var:ident) => {
+        paste::item! {
+            pub fn $var (&self) -> Result<bool, Box<dyn Error + '_>> {
+                let lp = self.lp.lock()?;
+                unsafe { Ok(ffi::[< lpcfg_ $var >] (*lp) != 0) }
+            }
+        }
+    };
+}
+
+impl LoadParm {
+    pub fn new(configfile: Option<&str>) -> Result<Self, Box<dyn Error + '_>> {
+        let lp = unsafe {
+            match configfile {
+                Some(configfile) => {
+                    let configfile_cstr = wrap_string(configfile);
+                    let lp = ffi::loadparm_init_global(0);
+                    if ffi::lpcfg_load(lp, configfile_cstr) != 1 {
+                        return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
+                    }
+                    string_free(configfile_cstr);
+                    lp
+                }
+                None => ffi::loadparm_init_global(1),
+            }
+        };
+        Ok(LoadParm {
+            lp: Arc::new(Mutex::new(lp)),
+        })
+    }
+
+    pub fn private_path(
+        &self,
+        name: &str,
+    ) -> Result<Option<String>, Box<dyn Error + '_>> {
+        let lp = self.lp.lock()?;
+        let path = unsafe {
+            let name_cstr = wrap_string(name);
+            let path =
+                ffi::lpcfg_private_path(*lp as *mut c_void, *lp, name_cstr);
+            string_free(name_cstr);
+            path
+        };
+        unsafe { Ok(wrap_c_char(path)) }
+    }
+
+    pub fn logfile(&self) -> Result<Option<String>, Box<dyn Error + '_>> {
+        let lp = self.lp.lock()?;
+        let logfile = unsafe {
+            let lp_sub = ffi::lpcfg_noop_substitution();
+            ffi::lpcfg_logfile(*lp, lp_sub, *lp as *mut c_void)
+        };
+        unsafe { Ok(wrap_c_char(logfile)) }
+    }
+
+    pub fn idmap_range(
+        &self,
+        domain_name: &str,
+    ) -> Result<(u32, u32), Box<dyn Error + '_>> {
+        if let Ok(Some(backend)) = self.idmap_backend(domain_name) {
+            if backend != "upn" {
+                DBG_ERR!("Backend '{}' is not supported for Entra ID", backend);
+                return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
+            }
+        } else {
+            DBG_WARNING!(
+                "No idmap backend configured for domain '{}'",
+                domain_name
+            );
+            DBG_INFO!("Falling back to default idmap configuration");
+            return self.idmap_default_range();
+        }
+        let mut low: u32 = 0;
+        let mut high: u32 = 0;
+        let res = unsafe {
+            let domain_name_cstr = wrap_string(domain_name);
+            let res =
+                ffi::lp_idmap_range(domain_name_cstr, &mut low, &mut high);
+            string_free(domain_name_cstr);
+            res
+        };
+        if res == 0 {
+            return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
+        }
+        Ok((low, high))
+    }
+
+    pub fn idmap_backend(
+        &self,
+        domain_name: &str,
+    ) -> Result<Option<String>, Box<dyn Error + '_>> {
+        let backend = unsafe {
+            let domain_name_cstr = wrap_string(domain_name);
+            let backend = ffi::lp_idmap_backend(domain_name_cstr);
+            string_free(domain_name_cstr);
+            backend
+        };
+        unsafe { Ok(wrap_c_char(backend)) }
+    }
+
+    pub fn idmap_default_range(
+        &self,
+    ) -> Result<(u32, u32), Box<dyn Error + '_>> {
+        if let Ok(Some(backend)) = self.idmap_default_backend() {
+            if backend != "upn" {
+                DBG_ERR!(
+                    "Default backend '{}' is not supported for Entra ID",
+                    backend
+                );
+                return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
+            }
+        } else {
+            DBG_ERR!("No default idmap backend configured.");
+            return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
+        }
+        let mut low: u32 = 0;
+        let mut high: u32 = 0;
+        let res = unsafe { ffi::lp_idmap_default_range(&mut low, &mut high) };
+        if res == 0 {
+            return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
+        }
+        Ok((low, high))
+    }
+
+    pub fn idmap_default_backend(
+        &self,
+    ) -> Result<Option<String>, Box<dyn Error + '_>> {
+        let backend = unsafe { ffi::lp_idmap_default_backend() };
+        unsafe { Ok(wrap_c_char(backend)) }
+    }
+
+    lpcfg_str!(realm);
+    lpcfg_str!(winbindd_socket_directory);
+    lpcfg_i32!(winbind_request_timeout);
+    lpcfg_bool!(himmelblaud_sfa_fallback);
+    lpcfg_bool!(himmelblaud_hello_enabled);
+    lpcfg_str!(cache_directory);
+    lpcfg_str!(template_homedir);
+    lpcfg_str!(template_shell);
+}
+
+unsafe impl Send for LoadParm {}
+unsafe impl Sync for LoadParm {}
index 047c59d3c6b5a8d9abe548ec26f1f697c03d8272..01c8df0ed70aef4cb17492c40a9ab099f1a54f31 100644 (file)
@@ -3165,6 +3165,14 @@ struct loadparm_context *loadparm_init(TALLOC_CTX *mem_ctx)
                                  "acl claims evaluation",
                                  "AD DC only");
 
+       /* Set the default Himmelblaud globals */
+       lpcfg_do_global_parameter(lp_ctx,
+                                 "himmelblaud hello enabled",
+                                 "false");
+       lpcfg_do_global_parameter(lp_ctx,
+                                 "himmelblaud sfa fallback",
+                                 "false");
+
        for (i = 0; parm_table[i].label; i++) {
                if (!(lp_ctx->flags[i] & FLAG_CMDLINE)) {
                        lp_ctx->flags[i] |= FLAG_DEFAULT;
index 557567d927f9af06749e18f3d89000aff77344bd..ae23d22f40192f8a3b1412bbe89a25433fc66afc 100644 (file)
@@ -1004,6 +1004,10 @@ void loadparm_s3_init_globals(struct loadparm_context *lp_ctx,
 
        Globals.acl_claims_evaluation = ACL_CLAIMS_EVALUATION_AD_DC_ONLY;
 
+       /* Set the default Himmelblaud globals */
+       Globals.himmelblaud_hello_enabled = false;
+       Globals.himmelblaud_sfa_fallback = false;
+
        /* Now put back the settings that were set with lp_set_cmdline() */
        apply_lp_set_cmdline();
 }