1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-22 13:34:15 +03:00

Add lp Rust bindings

Signed-off-by: David Mulder <dmulder@samba.org>
Reviewed-by: Alexander Bokovoy <ab@samba.org>
This commit is contained in:
David Mulder 2024-07-30 09:43:46 -06:00
parent 61a5dc11a7
commit f0cbe4d5a2
8 changed files with 320 additions and 0 deletions

View File

@ -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>

View File

@ -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>

View 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" }

View File

@ -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"

View File

@ -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()
);
}

View File

@ -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 {}

View 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;

View 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();
}