From ff188833d2361dbae17c14547c8551cde81636c5 Mon Sep 17 00:00:00 2001 From: David Mulder Date: Mon, 7 Oct 2024 07:41:36 -0600 Subject: [PATCH] Remove the sssd build dependencies I had already extracted the sssd idmap code for Samba. Merge that back into Himmelblau to remove all the SSSD build dependencies. Signed-off-by: David Mulder --- .github/workflows/build.yml | 6 +- .github/workflows/test.yml | 6 +- .gitmodules | 4 - src/common/src/idprovider/himmelblau.rs | 8 +- src/idmap/.gitignore | 10 - src/idmap/Cargo.toml | 6 +- src/idmap/autogen.sh | 3 - src/idmap/build.rs | 36 +- src/idmap/configure.ac | 17 - src/idmap/src/lib.rs | 165 +- src/idmap/src/murmurhash3.c | 113 ++ src/idmap/src/murmurhash3.h | 21 + src/idmap/src/sss_idmap.c | 1909 +++++++++++++++++++++++ src/idmap/src/sss_idmap.h | 1100 +++++++++++++ src/idmap/src/sss_idmap_conv.c | 570 +++++++ src/idmap/src/sss_idmap_private.h | 84 + src/idmap/src/util.h | 46 + src/idmap/sssd | 1 - src/idmap/wrapper.h | 1 - 19 files changed, 3945 insertions(+), 161 deletions(-) delete mode 100644 .gitmodules delete mode 100644 src/idmap/.gitignore delete mode 100755 src/idmap/autogen.sh delete mode 100644 src/idmap/configure.ac create mode 100644 src/idmap/src/murmurhash3.c create mode 100644 src/idmap/src/murmurhash3.h create mode 100644 src/idmap/src/sss_idmap.c create mode 100644 src/idmap/src/sss_idmap.h create mode 100644 src/idmap/src/sss_idmap_conv.c create mode 100644 src/idmap/src/sss_idmap_private.h create mode 100644 src/idmap/src/util.h delete mode 160000 src/idmap/sssd delete mode 100644 src/idmap/wrapper.h diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eed26a64..9303a077 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,16 +40,14 @@ jobs: tpm-udev \ libtss2-dev \ libcap-dev \ - libtalloc-dev \ - libtevent-dev \ - libldb-dev \ libdhash-dev \ libkrb5-dev \ libpcre2-dev \ libclang-13-dev \ autoconf \ gettext \ - libdbus-1-dev + libdbus-1-dev \ + libutf8proc-dev - name: "Fetch submodules" run: git submodule init && git submodule update diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bd11b417..94b801b3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,16 +40,14 @@ jobs: tpm-udev \ libtss2-dev \ libcap-dev \ - libtalloc-dev \ - libtevent-dev \ - libldb-dev \ libdhash-dev \ libkrb5-dev \ libpcre2-dev \ libclang-13-dev \ autoconf \ gettext \ - libdbus-1-dev + libdbus-1-dev \ + libutf8proc-dev - name: "Fetch submodules" run: git submodule init && git submodule update diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 9f90db01..00000000 --- a/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ -[submodule "src/idmap/sssd"] - path = src/idmap/sssd - url = https://github.com/dmulder/sssd.git - branch = sss_idmap_4_idp diff --git a/src/common/src/idprovider/himmelblau.rs b/src/common/src/idprovider/himmelblau.rs index be63cefe..535441a8 100644 --- a/src/common/src/idprovider/himmelblau.rs +++ b/src/common/src/idprovider/himmelblau.rs @@ -18,7 +18,7 @@ use himmelblau::auth::{ use himmelblau::discovery::EnrollAttrs; use himmelblau::error::{ErrorResponse, MsalError, AUTH_PENDING, DEVICE_AUTH_FAIL, REQUIRES_MFA}; use himmelblau::graph::{DirectoryObject, Graph}; -use idmap::SssIdmap; +use idmap::Idmap; use kanidm_hsm_crypto::{LoadableIdentityKey, LoadableMsOapxbcRsaKey, PinValue, SealedData, Tpm}; use reqwest; use serde::{Deserialize, Serialize}; @@ -85,7 +85,7 @@ impl HimmelblauMultiProvider { Ok(config) => Arc::new(RwLock::new(config)), Err(e) => return Err(anyhow!("{}", e)), }; - let idmap = match SssIdmap::new() { + let idmap = match Idmap::new() { Ok(idmap) => Arc::new(RwLock::new(idmap)), Err(e) => return Err(anyhow!("{:?}", e)), }; @@ -390,7 +390,7 @@ pub struct HimmelblauProvider { authority_host: String, graph: Graph, refresh_cache: RefreshCache, - idmap: Arc>, + idmap: Arc>, } impl HimmelblauProvider { @@ -401,7 +401,7 @@ impl HimmelblauProvider { domain: &str, authority_host: &str, graph: Graph, - idmap: &Arc>, + idmap: &Arc>, ) -> Result { Ok(HimmelblauProvider { client: RwLock::new(client), diff --git a/src/idmap/.gitignore b/src/idmap/.gitignore deleted file mode 100644 index de93b03c..00000000 --- a/src/idmap/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -config.h -aclocal.m4 -autom4te.cache/ -config.h.in -config.h.in~ -config.log -config.status -configure -configure.ac~ -configure~ diff --git a/src/idmap/Cargo.toml b/src/idmap/Cargo.toml index 669459a9..c40a3ab5 100644 --- a/src/idmap/Cargo.toml +++ b/src/idmap/Cargo.toml @@ -14,9 +14,9 @@ name = "idmap" path = "src/lib.rs" [dependencies] -libc = "0.2.153" -tracing = { workspace = true } -uuid = { workspace = true } +libc.workspace = true +tracing.workspace = true +uuid.workspace = true [build-dependencies] cc = "1.0.97" diff --git a/src/idmap/autogen.sh b/src/idmap/autogen.sh deleted file mode 100755 index 905970e6..00000000 --- a/src/idmap/autogen.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -autoreconf -if diff --git a/src/idmap/build.rs b/src/idmap/build.rs index 44bebb90..f67d61fb 100644 --- a/src/idmap/build.rs +++ b/src/idmap/build.rs @@ -1,36 +1,11 @@ use std::env; -use std::io::{self, Write}; -use std::path::Path; use std::path::PathBuf; -use std::process::Command; fn main() { - let autoreconf = Command::new("./autogen.sh") - .output() - .expect("Failed to configure sss_idmap"); - if !autoreconf.status.success() { - io::stdout().write_all(&autoreconf.stdout).unwrap(); - io::stderr().write_all(&autoreconf.stderr).unwrap(); - panic!("Failed to configure sss_idmap"); - } - io::stdout().write_all(&autoreconf.stdout).unwrap(); - let configure = Command::new("./configure") - .output() - .expect("Failed to configure sss_idmap"); - if !configure.status.success() { - io::stdout().write_all(&configure.stdout).unwrap(); - io::stderr().write_all(&configure.stderr).unwrap(); - panic!("Failed to configure sss_idmap"); - } - io::stdout().write_all(&configure.stdout).unwrap(); - cc::Build::new() - .file("sssd/src/lib/idmap/sss_idmap.c") - .file("sssd/src/lib/idmap/sss_idmap_conv.c") - .file("sssd/src/util/murmurhash3.c") - .include(Path::new("/usr/include/samba-4.0")) - .include(Path::new("sssd/src")) - .include(Path::new("./")) // for config.h + .file("src/sss_idmap.c") + .file("src/sss_idmap_conv.c") + .file("src/murmurhash3.c") .warnings(false) .compile("sss_idmap"); @@ -42,8 +17,7 @@ fn main() { .blocklist_function("qecvt") .blocklist_function("qecvt_r") .blocklist_function("strtold") - .clang_arg("-I/usr/include/samba-4.0") - .header("wrapper.h") + .header("src/sss_idmap.h") .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .generate() .expect("Unable to generate bindings"); @@ -52,4 +26,6 @@ fn main() { bindings .write_to_file(out_path.join("bindings.rs")) .expect("Couldn't write bindings!"); + println!("cargo:rustc-link-lib=utf8proc"); + println!("cargo:rustc-env=LD_LIBRARY_PATH=../../bin/shared/private/"); } diff --git a/src/idmap/configure.ac b/src/idmap/configure.ac deleted file mode 100644 index 8c54c9ef..00000000 --- a/src/idmap/configure.ac +++ /dev/null @@ -1,17 +0,0 @@ -AC_PREREQ([2.69]) - -m4_include([sssd/version.m4]) -AC_INIT([libsss_idmap],[VERSION_NUMBER],[sssd-devel@lists.fedorahosted.org]) - -AC_CONFIG_HEADERS([config.h]) - -AC_DEFINE_UNQUOTED( - [SSS_ATTRIBUTE_FALLTHROUGH], - [$sss_cv_attribute_fallthrough_val], - [__attribute__((fallthrough)) if supported]) - -m4_include([sssd/src/external/sizes.m4]) -m4_include([sssd/src/build_macros.m4]) -m4_include([sssd/src/external/libpcre.m4]) - -AC_OUTPUT diff --git a/src/idmap/src/lib.rs b/src/idmap/src/lib.rs index 83a713cd..2a90e1a2 100644 --- a/src/idmap/src/lib.rs +++ b/src/idmap/src/lib.rs @@ -1,3 +1,23 @@ +/* + Himmelblaud + + ID-mapping library + + 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 . +*/ #![deny(warnings)] #![warn(unused_extern_crates)] #![deny(clippy::todo)] @@ -11,8 +31,9 @@ #![deny(clippy::trivially_copy_pass_by_ref)] use std::collections::HashMap; use std::ffi::CString; +use std::fmt; use std::ptr; -use std::sync::RwLock; +use std::sync::{Arc, Mutex}; use uuid::Uuid; #[macro_use] @@ -26,52 +47,42 @@ mod ffi { include!(concat!(env!("OUT_DIR"), "/bindings.rs")); } -#[derive(Debug)] -#[allow(non_camel_case_types)] -pub enum IdmapError { - IDMAP_SUCCESS, - IDMAP_NOT_IMPLEMENTED, - IDMAP_ERROR, - IDMAP_OUT_OF_MEMORY, - IDMAP_NO_DOMAIN, - IDMAP_CONTEXT_INVALID, - IDMAP_SID_INVALID, - IDMAP_SID_UNKNOWN, - IDMAP_NO_RANGE, - IDMAP_BUILTIN_SID, - IDMAP_OUT_OF_SLICES, - IDMAP_COLLISION, - IDMAP_EXTERNAL, - IDMAP_NAME_UNKNOWN, - IDMAP_NO_REVERSE, - IDMAP_ERR_LAST, +#[derive(PartialEq, Eq)] +pub struct IdmapError(u32); + +pub const IDMAP_SUCCESS: IdmapError = IdmapError(ffi::idmap_error_code_IDMAP_SUCCESS); +pub const IDMAP_NOT_IMPLEMENTED: IdmapError = + IdmapError(ffi::idmap_error_code_IDMAP_NOT_IMPLEMENTED); +pub const IDMAP_ERROR: IdmapError = IdmapError(ffi::idmap_error_code_IDMAP_ERROR); +pub const IDMAP_OUT_OF_MEMORY: IdmapError = IdmapError(ffi::idmap_error_code_IDMAP_OUT_OF_MEMORY); +pub const IDMAP_NO_DOMAIN: IdmapError = IdmapError(ffi::idmap_error_code_IDMAP_NO_DOMAIN); +pub const IDMAP_CONTEXT_INVALID: IdmapError = + IdmapError(ffi::idmap_error_code_IDMAP_CONTEXT_INVALID); +pub const IDMAP_SID_INVALID: IdmapError = IdmapError(ffi::idmap_error_code_IDMAP_SID_INVALID); +pub const IDMAP_SID_UNKNOWN: IdmapError = IdmapError(ffi::idmap_error_code_IDMAP_SID_UNKNOWN); +pub const IDMAP_NO_RANGE: IdmapError = IdmapError(ffi::idmap_error_code_IDMAP_NO_RANGE); +pub const IDMAP_BUILTIN_SID: IdmapError = IdmapError(ffi::idmap_error_code_IDMAP_BUILTIN_SID); +pub const IDMAP_OUT_OF_SLICES: IdmapError = IdmapError(ffi::idmap_error_code_IDMAP_OUT_OF_SLICES); +pub const IDMAP_COLLISION: IdmapError = IdmapError(ffi::idmap_error_code_IDMAP_COLLISION); +pub const IDMAP_EXTERNAL: IdmapError = IdmapError(ffi::idmap_error_code_IDMAP_EXTERNAL); +pub const IDMAP_NAME_UNKNOWN: IdmapError = IdmapError(ffi::idmap_error_code_IDMAP_NAME_UNKNOWN); +pub const IDMAP_NO_REVERSE: IdmapError = IdmapError(ffi::idmap_error_code_IDMAP_NO_REVERSE); +pub const IDMAP_ERR_LAST: IdmapError = IdmapError(ffi::idmap_error_code_IDMAP_ERR_LAST); + +impl fmt::Display for IdmapError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "IdmapError({:#x})", self.0) + } } -fn map_err(e: ffi::idmap_error_code) -> IdmapError { - match e { - ffi::idmap_error_code_IDMAP_SUCCESS => IdmapError::IDMAP_SUCCESS, - ffi::idmap_error_code_IDMAP_NOT_IMPLEMENTED => IdmapError::IDMAP_NOT_IMPLEMENTED, - ffi::idmap_error_code_IDMAP_ERROR => IdmapError::IDMAP_ERROR, - ffi::idmap_error_code_IDMAP_OUT_OF_MEMORY => IdmapError::IDMAP_OUT_OF_MEMORY, - ffi::idmap_error_code_IDMAP_NO_DOMAIN => IdmapError::IDMAP_NO_DOMAIN, - ffi::idmap_error_code_IDMAP_CONTEXT_INVALID => IdmapError::IDMAP_CONTEXT_INVALID, - ffi::idmap_error_code_IDMAP_SID_INVALID => IdmapError::IDMAP_SID_INVALID, - ffi::idmap_error_code_IDMAP_SID_UNKNOWN => IdmapError::IDMAP_SID_UNKNOWN, - ffi::idmap_error_code_IDMAP_NO_RANGE => IdmapError::IDMAP_NO_RANGE, - ffi::idmap_error_code_IDMAP_BUILTIN_SID => IdmapError::IDMAP_BUILTIN_SID, - ffi::idmap_error_code_IDMAP_OUT_OF_SLICES => IdmapError::IDMAP_OUT_OF_SLICES, - ffi::idmap_error_code_IDMAP_COLLISION => IdmapError::IDMAP_COLLISION, - ffi::idmap_error_code_IDMAP_EXTERNAL => IdmapError::IDMAP_EXTERNAL, - ffi::idmap_error_code_IDMAP_NAME_UNKNOWN => IdmapError::IDMAP_NAME_UNKNOWN, - ffi::idmap_error_code_IDMAP_NO_REVERSE => IdmapError::IDMAP_NO_REVERSE, - ffi::idmap_error_code_IDMAP_ERR_LAST => IdmapError::IDMAP_ERR_LAST, - _ => { - error!("Unknown error code '{}'", e); - IdmapError::IDMAP_ERROR - } +impl fmt::Debug for IdmapError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "IdmapError({:#x})", self.0) } } +impl std::error::Error for IdmapError {} + #[allow(dead_code)] struct AadSid { sid_rev_num: u8, @@ -100,18 +111,18 @@ fn object_id_to_sid(object_id: &Uuid) -> Result { sid.sub_auths[1] = u32::from_be_bytes( bytes_array[0..4] .try_into() - .map_err(|_| IdmapError::IDMAP_SID_INVALID)?, + .map_err(|_| IDMAP_SID_INVALID)?, ); sid.sub_auths[2] = u32::from_be_bytes(s_bytes_array); sid.sub_auths[3] = u32::from_le_bytes( bytes_array[8..12] .try_into() - .map_err(|_| IdmapError::IDMAP_SID_INVALID)?, + .map_err(|_| IDMAP_SID_INVALID)?, ); sid.sub_auths[4] = u32::from_le_bytes( bytes_array[12..] .try_into() - .map_err(|_| IdmapError::IDMAP_SID_INVALID)?, + .map_err(|_| IDMAP_SID_INVALID)?, ); Ok(sid) @@ -119,28 +130,23 @@ fn object_id_to_sid(object_id: &Uuid) -> Result { fn rid_from_sid(sid: &AadSid) -> Result { Ok(sid.sub_auths - [usize::try_from(sid.num_auths).map_err(|_| IdmapError::IDMAP_SID_INVALID)? - 1]) + [usize::try_from(sid.num_auths).map_err(|_| IDMAP_SID_INVALID)? - 1]) } pub const DEFAULT_IDMAP_RANGE: (u32, u32) = (200000, 2000200000); -// The ctx is behind a read/write lock to make it 'safer' to Send/Sync. -// Granted, dereferencing a raw pointer is still inherently unsafe. -pub struct SssIdmap { - ctx: RwLock<*mut ffi::sss_idmap_ctx>, +pub struct Idmap { + ctx: Arc>, ranges: HashMap, } -unsafe impl Send for SssIdmap {} -unsafe impl Sync for SssIdmap {} - -impl SssIdmap { - pub fn new() -> Result { +impl Idmap { + pub fn new() -> Result { let mut ctx = ptr::null_mut(); unsafe { - match map_err(ffi::sss_idmap_init(None, ptr::null_mut(), None, &mut ctx)) { - IdmapError::IDMAP_SUCCESS => Ok(SssIdmap { - ctx: RwLock::new(ctx), + match IdmapError(ffi::sss_idmap_init(None, ptr::null_mut(), None, &mut ctx)) { + IDMAP_SUCCESS => Ok(Idmap { + ctx: Arc::new(Mutex::new(ctx)), ranges: HashMap::new(), }), e => Err(e), @@ -154,21 +160,19 @@ impl SssIdmap { tenant_id: &str, range: (u32, u32), ) -> Result<(), IdmapError> { - let ctx = self.ctx.write().map_err(|e| { + let ctx = self.ctx.lock().map_err(|e| { error!("Failed obtaining write lock on sss_idmap_ctx: {}", e); - IdmapError::IDMAP_ERROR + IDMAP_ERROR })?; - let domain_name_cstr = - CString::new(domain_name).map_err(|_| IdmapError::IDMAP_OUT_OF_MEMORY)?; - let tenant_id_cstr = - CString::new(tenant_id).map_err(|_| IdmapError::IDMAP_OUT_OF_MEMORY)?; + let domain_name_cstr = CString::new(domain_name).map_err(|_| IDMAP_OUT_OF_MEMORY)?; + let tenant_id_cstr = CString::new(tenant_id).map_err(|_| IDMAP_OUT_OF_MEMORY)?; let mut idmap_range = ffi::sss_idmap_range { min: range.0, max: range.1, }; self.ranges.insert(tenant_id.to_string(), range); unsafe { - match map_err(ffi::sss_idmap_add_gen_domain_ex( + match IdmapError(ffi::sss_idmap_add_gen_domain_ex( *ctx, domain_name_cstr.as_ptr(), tenant_id_cstr.as_ptr(), @@ -180,30 +184,28 @@ impl SssIdmap { 0, false, )) { - IdmapError::IDMAP_SUCCESS => Ok(()), + IDMAP_SUCCESS => Ok(()), e => Err(e), } } } pub fn gen_to_unix(&self, tenant_id: &str, input: &str) -> Result { - let ctx = self.ctx.write().map_err(|e| { + let ctx = self.ctx.lock().map_err(|e| { error!("Failed obtaining write lock on sss_idmap_ctx: {}", e); - IdmapError::IDMAP_ERROR + IDMAP_ERROR })?; - let tenant_id_cstr = - CString::new(tenant_id).map_err(|_| IdmapError::IDMAP_OUT_OF_MEMORY)?; - let input_cstr = - CString::new(input.to_lowercase()).map_err(|_| IdmapError::IDMAP_OUT_OF_MEMORY)?; + let tenant_id_cstr = CString::new(tenant_id).map_err(|_| IDMAP_OUT_OF_MEMORY)?; + let input_cstr = CString::new(input.to_lowercase()).map_err(|_| IDMAP_OUT_OF_MEMORY)?; unsafe { let mut id: u32 = 0; - match map_err(ffi::sss_idmap_gen_to_unix( + match IdmapError(ffi::sss_idmap_gen_to_unix( *ctx, tenant_id_cstr.as_ptr(), input_cstr.as_ptr(), &mut id, )) { - IdmapError::IDMAP_SUCCESS => Ok(id), + IDMAP_SUCCESS => Ok(id), e => Err(e), } } @@ -218,16 +220,16 @@ impl SssIdmap { let rid = rid_from_sid(&sid)?; let idmap_range = match self.ranges.get(tenant_id) { Some(idmap_range) => idmap_range, - None => return Err(IdmapError::IDMAP_NO_RANGE), + None => return Err(IDMAP_NO_RANGE), }; let uid_count = idmap_range.1 - idmap_range.0; Ok((rid % uid_count) + idmap_range.0) } } -impl Drop for SssIdmap { +impl Drop for Idmap { fn drop(&mut self) { - match self.ctx.write() { + match self.ctx.lock() { Ok(ctx) => unsafe { let _ = ffi::sss_idmap_free(*ctx); }, @@ -241,9 +243,12 @@ impl Drop for SssIdmap { } } +unsafe impl Send for Idmap {} +unsafe impl Sync for Idmap {} + #[cfg(test)] mod tests { - use crate::{SssIdmap, DEFAULT_IDMAP_RANGE}; + use crate::{Idmap, DEFAULT_IDMAP_RANGE}; use std::collections::HashMap; use uuid::Uuid; @@ -251,7 +256,7 @@ mod tests { fn sssd_idmapping() { let domain = "contoso.onmicrosoft.com"; let tenant_id = "d7af6c1b-0497-40fe-9d17-07e6b0f8332e"; - let mut idmap = SssIdmap::new().expect("SssIdmap initialization failed"); + let mut idmap = Idmap::new().expect("Idmap initialization failed"); idmap .add_gen_domain(domain, tenant_id, DEFAULT_IDMAP_RANGE) @@ -277,7 +282,7 @@ mod tests { fn legacy_idmapping() { let domain = "contoso.onmicrosoft.com"; let tenant_id = "d7af6c1b-0497-40fe-9d17-07e6b0f8332e"; - let mut idmap = SssIdmap::new().expect("SssIdmap initialization failed"); + let mut idmap = Idmap::new().expect("Idmap initialization failed"); // Test using the legacy default idmap range idmap diff --git a/src/idmap/src/murmurhash3.c b/src/idmap/src/murmurhash3.c new file mode 100644 index 00000000..50a58272 --- /dev/null +++ b/src/idmap/src/murmurhash3.c @@ -0,0 +1,113 @@ +/* This file is based on the public domain MurmurHash3 from Austin Appleby: + * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp + * + * We use only the 32 bit variant because the 2 produce different result while + * we need to produce the same result regardless of the architecture as + * clients can be both 64 or 32 bit at the same time. + */ + +#include +#include +#include + +#include "murmurhash3.h" +#include "util.h" + +static uint32_t rotl(uint32_t x, int8_t r) +{ + return (x << r) | (x >> (32 - r)); +} + +/* slower than original but is endian neutral and handles platforms that + * do only aligned reads */ +__attribute__((always_inline)) +static inline uint32_t getblock(const uint8_t *p, int i) +{ + uint32_t r; + size_t size = sizeof(uint32_t); + + memcpy(&r, &p[i * size], size); + + return le32toh(r); +} + +/* + * Finalization mix - force all bits of a hash block to avalanche + */ + +__attribute__((always_inline)) +static inline uint32_t fmix(uint32_t h) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + + +uint32_t murmurhash3(const char *key, int len, uint32_t seed) +{ + const uint8_t *blocks; + const uint8_t *tail; + int nblocks; + uint32_t h1; + uint32_t k1; + uint32_t c1; + uint32_t c2; + int i; + + blocks = (const uint8_t *)key; + nblocks = len / 4; + h1 = seed; + c1 = 0xcc9e2d51; + c2 = 0x1b873593; + + /* body */ + + for (i = 0; i < nblocks; i++) { + + k1 = getblock(blocks, i); + + k1 *= c1; + k1 = rotl(k1, 15); + k1 *= c2; + + h1 ^= k1; + h1 = rotl(h1, 13); + h1 = h1 * 5 + 0xe6546b64; + } + + /* tail */ + + tail = (const uint8_t *)key + nblocks * 4; + + k1 = 0; + + switch (len & 3) { + case 3: + k1 ^= tail[2] << 16; + SSS_ATTRIBUTE_FALLTHROUGH; + case 2: + k1 ^= tail[1] << 8; + SSS_ATTRIBUTE_FALLTHROUGH; + case 1: + k1 ^= tail[0]; + k1 *= c1; + k1 = rotl(k1, 15); + k1 *= c2; + h1 ^= k1; + break; + default: + break; + } + + /* finalization */ + + h1 ^= len; + h1 = fmix(h1); + + return h1; +} diff --git a/src/idmap/src/murmurhash3.h b/src/idmap/src/murmurhash3.h new file mode 100644 index 00000000..27671831 --- /dev/null +++ b/src/idmap/src/murmurhash3.h @@ -0,0 +1,21 @@ +/* This file is based on the public domain MurmurHash3 from Austin Appleby: + * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp + * + * We use only the 32 bit variant because the 2 produce different result while + * we need to produce the same result regardless of the architecture as + * clients can be both 64 or 32 bit at the same time. + */ + +#ifndef _SHARED_MURMURHASH3_H_ +#define _SHARED_MURMURHASH3_H_ + +/* CAUTION: + * This file is also used in sss_client (pam, nss). Therefore it have to be + * minimalist and cannot include DEBUG macros or header file util.h. + */ + +#include + +uint32_t murmurhash3(const char *key, int len, uint32_t seed); + +#endif /* _SHARED_MURMURHASH3_H_ */ diff --git a/src/idmap/src/sss_idmap.c b/src/idmap/src/sss_idmap.c new file mode 100644 index 00000000..4da1d0df --- /dev/null +++ b/src/idmap/src/sss_idmap.c @@ -0,0 +1,1909 @@ +/* + SSSD + + ID-mapping library + + Authors: + Sumit Bose + + Copyright (C) 2012 Red Hat + + 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 . +*/ + +#include +#include +#include +#include +#include + +#include "sss_idmap.h" +#include "sss_idmap_private.h" +#include "murmurhash3.h" + +#define SID_FMT "%s-%"PRIu32 +#define SID_STR_MAX_LEN 1024 + +/* Hold all parameters for unix<->sid mapping relevant for + * given slice. */ +struct idmap_range_params { + uint32_t min_id; + uint32_t max_id; + char *range_id; + + uint32_t first_rid; + struct idmap_range_params *next; +}; + +struct idmap_domain_info { + char *name; + char *sid; + struct idmap_range_params range_params; + struct idmap_domain_info *next; + bool external_mapping; + + struct idmap_range_params *helpers; + bool auto_add_ranges; + bool helpers_owner; + + idmap_offset_func *offset_func; + idmap_rev_offset_func *rev_offset_func; + void *offset_func_pvt; + + idmap_store_cb cb; + void *pvt; +}; + +static void *default_alloc(size_t size, void *pvt) +{ + return malloc(size); +} + +static void default_free(void *ptr, void *pvt) +{ + free(ptr); +} + +static char *idmap_strdup(struct sss_idmap_ctx *ctx, const char *str) +{ + char *new = NULL; + size_t len; + + CHECK_IDMAP_CTX(ctx, NULL); + + len = strlen(str) + 1; + + new = ctx->alloc_func(len, ctx->alloc_pvt); + if (new == NULL) { + return NULL; + } + + memcpy(new, str, len); + + return new; +} + +static bool ranges_eq(const struct idmap_range_params *a, + const struct idmap_range_params *b) +{ + if (a == NULL || b == NULL) { + return false; + } + + if (a->first_rid == b->first_rid + && a->min_id == b->min_id + && a->max_id == b->max_id) { + return true; + } + + return false; +} + +static enum idmap_error_code +construct_range(struct sss_idmap_ctx *ctx, + const struct idmap_range_params *src, + char *id, + struct idmap_range_params **_dst) +{ + struct idmap_range_params *dst; + + if (src == NULL || id == NULL || _dst == NULL) { + return IDMAP_ERROR; + } + + dst = ctx->alloc_func(sizeof(struct idmap_range_params), ctx->alloc_pvt); + if (dst == NULL) { + return IDMAP_OUT_OF_MEMORY; + } + + dst->min_id = src->min_id; + dst->max_id = src->max_id; + dst->first_rid = src->first_rid; + dst->next = NULL; + dst->range_id = id; + + *_dst = dst; + return IDMAP_SUCCESS; +} + +static bool id_is_in_range(uint32_t id, + struct idmap_range_params *rp, + uint32_t *rid) +{ + if (id == 0 || rp == NULL) { + return false; + } + + if (id >= rp->min_id && id <= rp->max_id) { + if (rid != NULL) { + *rid = rp->first_rid + (id - rp->min_id); + } + + return true; + } + + return false; +} + +const char *idmap_error_string(enum idmap_error_code err) +{ + switch (err) { + case IDMAP_SUCCESS: + return "IDMAP operation successful"; + break; + case IDMAP_NOT_IMPLEMENTED: + return "IDMAP Function is not yet implemented"; + break; + case IDMAP_ERROR: + return "IDMAP general error"; + break; + case IDMAP_OUT_OF_MEMORY: + return "IDMAP operation ran out of memory"; + break; + case IDMAP_NO_DOMAIN: + return "IDMAP domain not found"; + break; + case IDMAP_CONTEXT_INVALID: + return "IDMAP context is invalid"; + break; + case IDMAP_SID_INVALID: + return "IDMAP SID is invalid"; + break; + case IDMAP_SID_UNKNOWN: + return "IDMAP SID not found"; + break; + case IDMAP_NO_RANGE: + return "IDMAP range not found"; + break; + case IDMAP_BUILTIN_SID: + return "IDMAP SID from BUILTIN domain"; + break; + case IDMAP_OUT_OF_SLICES: + return "IDMAP not more free slices"; + break; + case IDMAP_COLLISION: + return "IDMAP new range collides with existing one"; + break; + case IDMAP_EXTERNAL: + return "IDMAP ID managed externally"; + break; + case IDMAP_NAME_UNKNOWN: + return "IDMAP domain with the given name not found"; + break; + case IDMAP_NO_REVERSE: + return "IDMAP cannot revert id to original source"; + break; + case IDMAP_UTF8_ERROR: + return "IDMAP failed to modify UTF8 string"; + break; + default: + return "IDMAP unknown error code"; + } +} + +bool is_domain_sid(const char *sid) +{ + const char *p; + long long a; + char *endptr; + size_t c; + + if (sid == NULL || strncmp(sid, DOM_SID_PREFIX, DOM_SID_PREFIX_LEN) != 0) { + return false; + } + + p = sid + DOM_SID_PREFIX_LEN; + c = 0; + + do { + errno = 0; + a = strtoull(p, &endptr, 10); + if (errno != 0 || a > UINT32_MAX) { + return false; + } + + if (*endptr == '-') { + p = endptr + 1; + } else if (*endptr != '\0') { + return false; + } + c++; + } while(c < 3 && *endptr != '\0'); + + if (c != 3 || *endptr != '\0') { + return false; + } + + return true; +} + +enum idmap_error_code sss_idmap_init(idmap_alloc_func *alloc_func, + void *alloc_pvt, + idmap_free_func *free_func, + struct sss_idmap_ctx **_ctx) +{ + struct sss_idmap_ctx *ctx; + + if (alloc_func == NULL) { + alloc_func = default_alloc; + } + + ctx = alloc_func(sizeof(struct sss_idmap_ctx), alloc_pvt); + if (ctx == NULL) { + return IDMAP_OUT_OF_MEMORY; + } + memset(ctx, 0, sizeof(struct sss_idmap_ctx)); + + ctx->alloc_func = alloc_func; + ctx->alloc_pvt = alloc_pvt; + ctx->free_func = (free_func == NULL) ? default_free : free_func; + + /* Set default values. */ + ctx->idmap_opts.autorid_mode = SSS_IDMAP_DEFAULT_AUTORID; + ctx->idmap_opts.idmap_lower = SSS_IDMAP_DEFAULT_LOWER; + ctx->idmap_opts.idmap_upper = SSS_IDMAP_DEFAULT_UPPER; + ctx->idmap_opts.rangesize = SSS_IDMAP_DEFAULT_RANGESIZE; + ctx->idmap_opts.extra_slice_init = SSS_IDMAP_DEFAULT_EXTRA_SLICE_INIT; + + *_ctx = ctx; + + return IDMAP_SUCCESS; +} + +static void free_helpers(struct sss_idmap_ctx *ctx, + struct idmap_range_params *helpers, + bool helpers_owner) +{ + struct idmap_range_params *it = helpers; + struct idmap_range_params *tmp; + + if (helpers_owner == false) { + return; + } + + while (it != NULL) { + tmp = it->next; + + ctx->free_func(it->range_id, ctx->alloc_pvt); + ctx->free_func(it, ctx->alloc_pvt); + + it = tmp; + } +} + +static struct idmap_range_params* +get_helper_by_id(struct idmap_range_params *helpers, const char *id) +{ + struct idmap_range_params *it; + + for (it = helpers; it != NULL; it = it->next) { + if (strcmp(it->range_id, id) == 0) { + return it; + } + } + + return NULL; +} + +static void sss_idmap_free_domain(struct sss_idmap_ctx *ctx, + struct idmap_domain_info *dom) +{ + if (ctx == NULL || dom == NULL) { + return; + } + + ctx->free_func(dom->range_params.range_id, ctx->alloc_pvt); + + free_helpers(ctx, dom->helpers, dom->helpers_owner); + + ctx->free_func(dom->name, ctx->alloc_pvt); + ctx->free_func(dom->sid, ctx->alloc_pvt); + ctx->free_func(dom, ctx->alloc_pvt); +} + +enum idmap_error_code sss_idmap_free(struct sss_idmap_ctx *ctx) +{ + struct idmap_domain_info *dom; + struct idmap_domain_info *next; + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + next = ctx->idmap_domain_info; + while (next) { + dom = next; + next = dom->next; + sss_idmap_free_domain(ctx, dom); + } + + ctx->free_func(ctx, ctx->alloc_pvt); + + return IDMAP_SUCCESS; +} + +static enum idmap_error_code sss_idmap_free_ptr(struct sss_idmap_ctx *ctx, + void *ptr) +{ + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + if (ptr != NULL) { + ctx->free_func(ptr, ctx->alloc_pvt); + } + + return IDMAP_SUCCESS; +} + +enum idmap_error_code sss_idmap_free_sid(struct sss_idmap_ctx *ctx, + char *sid) +{ + return sss_idmap_free_ptr(ctx, sid); +} + +enum idmap_error_code sss_idmap_free_dom_sid(struct sss_idmap_ctx *ctx, + struct sss_dom_sid *dom_sid) +{ + return sss_idmap_free_ptr(ctx, dom_sid); +} + +enum idmap_error_code sss_idmap_free_smb_sid(struct sss_idmap_ctx *ctx, + struct dom_sid *smb_sid) +{ + return sss_idmap_free_ptr(ctx, smb_sid); +} + +enum idmap_error_code sss_idmap_free_bin_sid(struct sss_idmap_ctx *ctx, + uint8_t *bin_sid) +{ + return sss_idmap_free_ptr(ctx, bin_sid); +} + +static bool check_overlap(struct idmap_range_params *range, + id_t min, id_t max) +{ + return ((range->min_id <= min && range->max_id >= max) + || (range->min_id >= min && range->min_id <= max) + || (range->max_id >= min && range->max_id <= max)); +} + +static bool check_dom_overlap(struct idmap_range_params *prim_range, + /* struct idmap_range_params *sec_ranges, */ + id_t min, + id_t max) +{ + return check_overlap(prim_range, min, max); +} + +enum idmap_error_code sss_idmap_calculate_range(struct sss_idmap_ctx *ctx, + const char *range_id, + id_t *slice_num, + struct sss_idmap_range *_range) +{ + id_t max_slices; + id_t orig_slice; + id_t new_slice = 0; + id_t min; + id_t max; + id_t idmap_lower; + id_t idmap_upper; + id_t rangesize; + bool autorid_mode; + uint32_t hash_val; + struct idmap_domain_info *dom; + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + idmap_lower = ctx->idmap_opts.idmap_lower; + idmap_upper = ctx->idmap_opts.idmap_upper; + rangesize = ctx->idmap_opts.rangesize; + autorid_mode = ctx->idmap_opts.autorid_mode; + + max_slices = (idmap_upper - idmap_lower) / rangesize; + + if (slice_num && *slice_num != -1) { + /* The slice is being set explicitly. + * This may happen at system startup when we're loading + * previously-determined slices. In the future, we may also + * permit configuration to select the slice for a domain + * explicitly. + */ + new_slice = *slice_num; + min = (rangesize * new_slice) + idmap_lower; + max = min + rangesize - 1; + for (dom = ctx->idmap_domain_info; dom != NULL; dom = dom->next) { + if (check_dom_overlap(&dom->range_params,min, max)) { + /* This range overlaps one already registered + * Fail, because the slice was manually configured + */ + return IDMAP_COLLISION; + } + } + } else { + /* If slice is -1, we're being asked to pick a new slice */ + + if (autorid_mode) { + /* In autorid compatibility mode, always start at 0 and find the + * first free value. + */ + orig_slice = 0; + } else { + /* Hash the range identifier string */ + hash_val = murmurhash3(range_id, strlen(range_id), 0xdeadbeef); + + /* Now get take the modulus of the hash val and the max_slices + * to determine its optimal position in the range. + */ + new_slice = hash_val % max_slices; + orig_slice = new_slice; + } + + min = (rangesize * new_slice) + idmap_lower; + max = min + rangesize - 1; + /* Verify that this slice is not already in use */ + do { + for (dom = ctx->idmap_domain_info; dom != NULL; dom = dom->next) { + + if (check_dom_overlap(&dom->range_params, + min, max)) { + /* This range overlaps one already registered + * We'll try the next available slot + */ + new_slice++; + if (new_slice >= max_slices) { + /* loop around to the beginning if necessary */ + new_slice = 0; + } + + min = (rangesize * new_slice) + idmap_lower; + max = min + rangesize - 1; + break; + } + } + + /* Keep trying until dom is NULL (meaning we got to the end + * without matching) or we have run out of slices and gotten + * back to the first one we tried. + */ + } while (dom && new_slice != orig_slice); + + if (dom) { + /* We looped all the way through and found no empty slots */ + return IDMAP_OUT_OF_SLICES; + } + } + + _range->min = (rangesize * new_slice) + idmap_lower; + _range->max = _range->min + rangesize - 1; + + if (slice_num) { + *slice_num = new_slice; + } + + return IDMAP_SUCCESS; +} + +enum idmap_error_code sss_idmap_check_collision_ex(const char *o_name, + const char *o_sid, + struct sss_idmap_range *o_range, + uint32_t o_first_rid, + const char *o_range_id, + bool o_external_mapping, + const char *n_name, + const char *n_sid, + struct sss_idmap_range *n_range, + uint32_t n_first_rid, + const char *n_range_id, + bool n_external_mapping) +{ + bool names_equal; + bool sids_equal; + + /* TODO: if both ranges have the same ID check if an update is + * needed. */ + + /* Check if ID ranges overlap. + * ID ranges with external mapping may overlap. */ + if ((!n_external_mapping && !o_external_mapping) + && ((n_range->min >= o_range->min + && n_range->min <= o_range->max) + || (n_range->max >= o_range->min + && n_range->max <= o_range->max))) { + return IDMAP_COLLISION; + } + + names_equal = (strcasecmp(n_name, o_name) == 0); + sids_equal = ((n_sid == NULL && o_sid == NULL) + || (n_sid != NULL && o_sid != NULL + && strcasecmp(n_sid, o_sid) == 0)); + + /* check if domain name and SID are consistent */ + if ((names_equal && !sids_equal) || (!names_equal && sids_equal)) { + return IDMAP_COLLISION; + } + + /* check if external_mapping is consistent */ + if (names_equal && sids_equal + && n_external_mapping != o_external_mapping) { + return IDMAP_COLLISION; + } + + /* check if RID ranges overlap */ + if (names_equal && sids_equal + && n_external_mapping == false + && n_first_rid >= o_first_rid + && n_first_rid <= o_first_rid + (o_range->max - o_range->min)) { + return IDMAP_COLLISION; + } + + return IDMAP_SUCCESS; +} + +enum idmap_error_code sss_idmap_check_collision(struct sss_idmap_ctx *ctx, + char *n_name, char *n_sid, + struct sss_idmap_range *n_range, + uint32_t n_first_rid, + char *n_range_id, + bool n_external_mapping) +{ + struct idmap_domain_info *dom; + enum idmap_error_code err; + struct sss_idmap_range range; + + for (dom = ctx->idmap_domain_info; dom != NULL; dom = dom->next) { + + range.min = dom->range_params.min_id; + range.max = dom->range_params.max_id; + + err = sss_idmap_check_collision_ex(dom->name, dom->sid, + &range, + dom->range_params.first_rid, + dom->range_params.range_id, + dom->external_mapping, + n_name, n_sid, n_range, n_first_rid, + n_range_id, n_external_mapping); + if (err != IDMAP_SUCCESS) { + return err; + } + } + return IDMAP_SUCCESS; +} + +static enum +idmap_error_code dom_check_collision(struct idmap_domain_info *dom_list, + struct idmap_domain_info *new_dom) +{ + struct idmap_domain_info *dom; + enum idmap_error_code err; + struct sss_idmap_range range; + struct sss_idmap_range new_dom_range = { new_dom->range_params.min_id, + new_dom->range_params.max_id }; + + for (dom = dom_list; dom != NULL; dom = dom->next) { + range.min = dom->range_params.min_id; + range.max = dom->range_params.max_id; + + err = sss_idmap_check_collision_ex(dom->name, dom->sid, + &range, + dom->range_params.first_rid, + dom->range_params.range_id, + dom->external_mapping, + new_dom->name, new_dom->sid, + &new_dom_range, + new_dom->range_params.first_rid, + new_dom->range_params.range_id, + new_dom->external_mapping); + if (err != IDMAP_SUCCESS) { + return err; + } + } + return IDMAP_SUCCESS; +} + +static char* +generate_sec_slice_name(struct sss_idmap_ctx *ctx, + const char *domain_sid, uint32_t rid) +{ + const char *SEC_SLICE_NAME_FMT = "%s-%"PRIu32; + char *slice_name; + int len, len2; + + len = snprintf(NULL, 0, SEC_SLICE_NAME_FMT, domain_sid, rid); + if (len <= 0) { + return NULL; + } + + slice_name = ctx->alloc_func(len + 1, ctx->alloc_pvt); + if (slice_name == NULL) { + return NULL; + } + + len2 = snprintf(slice_name, len + 1, SEC_SLICE_NAME_FMT, domain_sid, + rid); + if (len != len2) { + ctx->free_func(slice_name, ctx->alloc_pvt); + return NULL; + } + + return slice_name; +} + +static enum idmap_error_code +generate_slice(struct sss_idmap_ctx *ctx, char *slice_name, uint32_t first_rid, + struct idmap_range_params **_slice) +{ + struct idmap_range_params *slice; + struct sss_idmap_range tmp_range; + enum idmap_error_code err; + + slice = ctx->alloc_func(sizeof(struct idmap_range_params), ctx->alloc_pvt); + if (slice == NULL) { + return IDMAP_OUT_OF_MEMORY; + } + + slice->next = NULL; + + err = sss_idmap_calculate_range(ctx, slice_name, NULL, &tmp_range); + if (err != IDMAP_SUCCESS) { + ctx->free_func(slice, ctx->alloc_pvt); + return err; + } + + slice->min_id = tmp_range.min; + slice->max_id = tmp_range.max; + slice->range_id = slice_name; + slice->first_rid = first_rid; + + *_slice = slice; + return IDMAP_SUCCESS; +} + +static enum idmap_error_code +get_helpers(struct sss_idmap_ctx *ctx, + const char *domain_sid, + uint32_t first_rid, + struct idmap_range_params **_sec_slices) +{ + struct idmap_range_params *prev = NULL; + struct idmap_range_params *sec_slices = NULL; + static enum idmap_error_code err; + struct idmap_range_params *slice; + char *secondary_name; + + for (int i = 0; i < ctx->idmap_opts.extra_slice_init; i++) { + secondary_name = generate_sec_slice_name(ctx, domain_sid, first_rid); + if (secondary_name == NULL) { + err = IDMAP_OUT_OF_MEMORY; + goto fail; + } + + err = generate_slice(ctx, secondary_name, first_rid, &slice); + if (err != IDMAP_SUCCESS) { + goto fail; + } + + first_rid += ctx->idmap_opts.rangesize; + + if (prev != NULL) { + prev->next = slice; + } + + if (sec_slices == NULL) { + sec_slices = slice; + } + + prev = slice; + } + + *_sec_slices = sec_slices; + return IDMAP_SUCCESS; + +fail: + ctx->free_func(secondary_name, ctx->alloc_pvt); + + /* Free already generated helpers. */ + free_helpers(ctx, sec_slices, true); + + return err; +} + +enum idmap_error_code sss_idmap_add_gen_domain_ex(struct sss_idmap_ctx *ctx, + const char *domain_name, + const char *domain_id, + struct sss_idmap_range *range, + const char *range_id, + idmap_offset_func *offset_func, + idmap_rev_offset_func *rev_offset_func, + void *offset_func_pvt, + uint32_t shift, + bool external_mapping) +{ + struct idmap_domain_info *dom = NULL; + enum idmap_error_code err; + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + if (domain_name == NULL || domain_id == NULL) { + return IDMAP_NO_DOMAIN; + } + + if (range == NULL) { + return IDMAP_NO_RANGE; + } + + dom = ctx->alloc_func(sizeof(struct idmap_domain_info), ctx->alloc_pvt); + if (dom == NULL) { + return IDMAP_OUT_OF_MEMORY; + } + memset(dom, 0, sizeof(struct idmap_domain_info)); + + dom->name = idmap_strdup(ctx, domain_name); + if (dom->name == NULL) { + err = IDMAP_OUT_OF_MEMORY; + goto fail; + } + + dom->sid = idmap_strdup(ctx, domain_id); + if (dom->sid == NULL) { + err = IDMAP_OUT_OF_MEMORY; + goto fail; + } + + dom->range_params.min_id = range->min; + dom->range_params.max_id = range->max; + + if (range_id != NULL) { + dom->range_params.range_id = idmap_strdup(ctx, range_id); + if (dom->range_params.range_id == NULL) { + err = IDMAP_OUT_OF_MEMORY; + goto fail; + } + } + + dom->range_params.first_rid = shift; + dom->external_mapping = external_mapping; + + dom->offset_func = offset_func; + dom->rev_offset_func = rev_offset_func; + dom->offset_func_pvt = offset_func_pvt; + + err = dom_check_collision(ctx->idmap_domain_info, dom); + if (err != IDMAP_SUCCESS) { + goto fail; + } + + dom->next = ctx->idmap_domain_info; + ctx->idmap_domain_info = dom; + + return IDMAP_SUCCESS; + +fail: + sss_idmap_free_domain(ctx, dom); + + return err; +} + +enum idmap_error_code sss_idmap_add_domain_ex(struct sss_idmap_ctx *ctx, + const char *domain_name, + const char *domain_sid, + struct sss_idmap_range *range, + const char *range_id, + uint32_t rid, + bool external_mapping) +{ + struct idmap_domain_info *dom = NULL; + enum idmap_error_code err; + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + if (domain_name == NULL) { + return IDMAP_NO_DOMAIN; + } + + if (range == NULL) { + return IDMAP_NO_RANGE; + } + + /* For algorithmic mapping a valid domain SID is required, for external + * mapping it may be NULL, but if set it should be valid. */ + if ((!external_mapping && !is_domain_sid(domain_sid)) + || (external_mapping + && domain_sid != NULL + && !is_domain_sid(domain_sid))) { + return IDMAP_SID_INVALID; + } + + dom = ctx->alloc_func(sizeof(struct idmap_domain_info), ctx->alloc_pvt); + if (dom == NULL) { + return IDMAP_OUT_OF_MEMORY; + } + memset(dom, 0, sizeof(struct idmap_domain_info)); + + dom->name = idmap_strdup(ctx, domain_name); + if (dom->name == NULL) { + err = IDMAP_OUT_OF_MEMORY; + goto fail; + } + + if (domain_sid != NULL) { + dom->sid = idmap_strdup(ctx, domain_sid); + if (dom->sid == NULL) { + err = IDMAP_OUT_OF_MEMORY; + goto fail; + } + } + + dom->range_params.min_id = range->min; + dom->range_params.max_id = range->max; + + if (range_id != NULL) { + dom->range_params.range_id = idmap_strdup(ctx, range_id); + if (dom->range_params.range_id == NULL) { + err = IDMAP_OUT_OF_MEMORY; + goto fail; + } + } + + dom->range_params.first_rid = rid; + dom->external_mapping = external_mapping; + + err = dom_check_collision(ctx->idmap_domain_info, dom); + if (err != IDMAP_SUCCESS) { + goto fail; + } + + dom->next = ctx->idmap_domain_info; + ctx->idmap_domain_info = dom; + + return IDMAP_SUCCESS; + +fail: + sss_idmap_free_domain(ctx, dom); + + return err; +} + +enum idmap_error_code +sss_idmap_add_auto_domain_ex(struct sss_idmap_ctx *ctx, + const char *domain_name, + const char *domain_sid, + struct sss_idmap_range *range, + const char *range_id, + uint32_t rid, + bool external_mapping, + idmap_store_cb cb, + void *pvt) +{ + enum idmap_error_code err; + + err = sss_idmap_add_domain_ex(ctx, domain_name, domain_sid, range, + range_id, rid, external_mapping); + if (err != IDMAP_SUCCESS) { + return err; + } + + if (external_mapping) { + /* There's no point in generating secondary ranges if external_mapping + is enabled. */ + ctx->idmap_domain_info->auto_add_ranges = false; + return IDMAP_SUCCESS; + } + + if ((range->max - range->min + 1) != ctx->idmap_opts.rangesize) { + /* Range of primary slice is not equal to the value of + ldap_idmap_range_size option. */ + return IDMAP_ERROR; + } + + /* No additional secondary ranges should be added if no sec ranges are + predeclared. */ + if (ctx->idmap_opts.extra_slice_init == 0) { + ctx->idmap_domain_info->auto_add_ranges = false; + return IDMAP_SUCCESS; + } + + /* Add size of primary slice for first_rid of secondary slices. */ + rid += ctx->idmap_opts.rangesize; + err = get_helpers(ctx, domain_sid, rid, + &ctx->idmap_domain_info->helpers); + if (err == IDMAP_SUCCESS) { + ctx->idmap_domain_info->auto_add_ranges = true; + ctx->idmap_domain_info->helpers_owner = true; + } else { + /* Running out of slices for secondary mapping is a non-fatal + * problem. */ + if (err == IDMAP_OUT_OF_SLICES) { + err = IDMAP_SUCCESS; + } + ctx->idmap_domain_info->auto_add_ranges = false; + } + + ctx->idmap_domain_info->cb = cb; + ctx->idmap_domain_info->pvt = pvt; + + return err; +} + +enum idmap_error_code sss_idmap_add_domain(struct sss_idmap_ctx *ctx, + const char *domain_name, + const char *domain_sid, + struct sss_idmap_range *range) +{ + return sss_idmap_add_domain_ex(ctx, domain_name, domain_sid, range, NULL, + 0, false); +} + +static bool sss_idmap_sid_is_builtin(const char *sid) +{ + if (strncmp(sid, "S-1-5-32-", 9) == 0) { + return true; + } + + return false; +} + +static bool parse_rid(const char *sid, size_t dom_prefix_len, long long *_rid) +{ + long long rid; + char *endptr; + + errno = 0; + /* Use suffix of sid - part after domain and following '-' */ + rid = strtoull(sid + dom_prefix_len + 1, &endptr, 10); + if (errno != 0 || rid > UINT32_MAX || *endptr != '\0') { + return false; + } + + *_rid = rid; + return true; +} + +static bool is_from_dom(const char *domain_id, const char *id) +{ + if (domain_id == NULL) { + return false; + } + + return strcmp(domain_id, id) == 0; +} + +static bool is_sid_from_dom(const char *dom_sid, const char *sid, + size_t *_dom_sid_len) +{ + size_t dom_sid_len; + + if (dom_sid == NULL) { + return false; + } + + dom_sid_len = strlen(dom_sid); + *_dom_sid_len = dom_sid_len; + + if (strlen(sid) < dom_sid_len || sid[dom_sid_len] != '-') { + return false; + } + + return strncmp(sid, dom_sid, dom_sid_len) == 0; +} + +static bool comp_id(struct idmap_range_params *range_params, long long rid, + uint32_t *_id) +{ + uint32_t id; + + if (rid >= range_params->first_rid + && ((UINT32_MAX - range_params->min_id) > + (rid - range_params->first_rid))) { + id = range_params->min_id + (rid - range_params->first_rid); + if (id <= range_params->max_id) { + *_id = id; + return true; + } + } + return false; +} + +static enum idmap_error_code +get_range(struct sss_idmap_ctx *ctx, + struct idmap_range_params *helpers, + const char *dom_sid, + long long rid, + struct idmap_range_params **_range) +{ + char *secondary_name = NULL; + enum idmap_error_code err; + int first_rid; + struct idmap_range_params *range; + struct idmap_range_params *helper; + + first_rid = (rid / ctx->idmap_opts.rangesize) * ctx->idmap_opts.rangesize; + + secondary_name = generate_sec_slice_name(ctx, dom_sid, first_rid); + if (secondary_name == NULL) { + err = IDMAP_OUT_OF_MEMORY; + goto error; + } + + helper = get_helper_by_id(helpers, secondary_name); + if (helper != NULL) { + /* Utilize helper's range. */ + err = construct_range(ctx, helper, secondary_name, &range); + } else { + /* Have to generate a whole new range. */ + err = generate_slice(ctx, secondary_name, first_rid, &range); + } + + if (err != IDMAP_SUCCESS) { + goto error; + } + + *_range = range; + return IDMAP_SUCCESS; + +error: + ctx->free_func(secondary_name, ctx->alloc_pvt); + return err; +} + +static enum idmap_error_code +spawn_dom(struct sss_idmap_ctx *ctx, + struct idmap_domain_info *parent, + struct idmap_range_params *range) +{ + struct sss_idmap_range tmp; + static enum idmap_error_code err; + struct idmap_domain_info *it; + + tmp.min = range->min_id; + tmp.max = range->max_id; + + err = sss_idmap_add_domain_ex(ctx, + parent->name, + parent->sid, + &tmp, range->range_id, + range->first_rid, false); + if (err != IDMAP_SUCCESS) { + return err; + } + + it = ctx->idmap_domain_info; + while (it != NULL) { + /* Find the newly added domain. */ + if (ranges_eq(&it->range_params, range)) { + + /* Share helpers. */ + it->helpers = parent->helpers; + it->auto_add_ranges = parent->auto_add_ranges; + + /* Share call back for storing domains */ + it->cb = parent->cb; + it->pvt = parent->pvt; + break; + } + + it = it->next; + } + + if (it == NULL) { + /* Failed to find just added domain. */ + return IDMAP_ERROR; + } + + /* Store mapping for newly created domain. */ + if (it->cb != NULL) { + err = it->cb(it->name, + it->sid, + it->range_params.range_id, + it->range_params.min_id, + it->range_params.max_id, + it->range_params.first_rid, + it->pvt); + if (err != IDMAP_SUCCESS) { + return err; + } + } + + return IDMAP_SUCCESS; +} + +static enum idmap_error_code +add_dom_for_sid(struct sss_idmap_ctx *ctx, + struct idmap_domain_info *matched_dom, + const char *sid, + uint32_t *_id) +{ + enum idmap_error_code err; + long long rid; + struct idmap_range_params *range = NULL; + + if (parse_rid(sid, strlen(matched_dom->sid), &rid) == false) { + err = IDMAP_SID_INVALID; + goto done; + } + + err = get_range(ctx, matched_dom->helpers, matched_dom->sid, rid, &range); + if (err != IDMAP_SUCCESS) { + goto done; + } + + err = spawn_dom(ctx, matched_dom, range); + if (err != IDMAP_SUCCESS) { + goto done; + } + + if (!comp_id(range, rid, _id)) { + err = IDMAP_ERROR; + goto done; + } + + err = IDMAP_SUCCESS; + +done: + if (range != NULL) { + ctx->free_func(range->range_id, ctx->alloc_pvt); + } + ctx->free_func(range, ctx->alloc_pvt); + return err; +} + +enum idmap_error_code offset_identity(void *pvt, uint32_t range_size, + const char *input, long long *offset) +{ + long long out; + char *endptr; + + if (input == NULL || offset == NULL) { + return IDMAP_ERROR; + } + + errno = 0; + out = strtoull(input, &endptr, 10); + if (errno != 0 || out >= range_size || *endptr != '\0' + || endptr == input) { + return IDMAP_ERROR; + } + + *offset = out; + + return IDMAP_SUCCESS; +} + +enum idmap_error_code rev_offset_identity(struct sss_idmap_ctx *ctx, void *pvt, + uint32_t id, char **_out) +{ + char *out; + int len; + int ret; + + len = snprintf(NULL, 0, "%"PRIu32, id); + if (len <= 0 || len > SID_STR_MAX_LEN) { + return IDMAP_ERROR; + } + + out = ctx->alloc_func(len + 1, ctx->alloc_pvt); + if (out == NULL) { + return IDMAP_OUT_OF_MEMORY; + } + + ret = snprintf(out, len + 1, "%"PRIu32, id); + if (ret != len) { + ctx->free_func(out, ctx->alloc_pvt); + return IDMAP_ERROR; + } + + *_out = out; + return IDMAP_SUCCESS; +} + +static char *normalize_casefold(const char *input, bool normalize, + bool casefold) +{ + if (casefold) { + return (char *) utf8proc_NFKC_Casefold((const utf8proc_uint8_t *) input); + } + + if (normalize) { + return (char *) utf8proc_NFKC((const utf8proc_uint8_t *) input); + } + + return NULL; +} + +struct offset_murmurhash3_data offset_murmurhash3_data_default = + { .seed = 0xdeadbeef, + .normalize = true, + .casefold = false }; + +enum idmap_error_code offset_murmurhash3(void *pvt, uint32_t range_size, + const char *input, long long *offset) +{ + struct offset_murmurhash3_data *offset_murmurhash3_data; + long long out; + char *tmp = NULL; + const char *val; + + if (input == NULL || offset == NULL) { + return IDMAP_ERROR; + } + + if (pvt != NULL) { + offset_murmurhash3_data = (struct offset_murmurhash3_data *) pvt; + } else { + offset_murmurhash3_data = &offset_murmurhash3_data_default; + } + + if (offset_murmurhash3_data->normalize || offset_murmurhash3_data->casefold) { + tmp = normalize_casefold(input, offset_murmurhash3_data->normalize, + offset_murmurhash3_data->casefold); + if (tmp == NULL) { + return IDMAP_UTF8_ERROR; + } + } + + val = (tmp == NULL) ? input : tmp; + + out = murmurhash3(val, strlen(val), offset_murmurhash3_data->seed); + free(tmp); + + out %= range_size; + + *offset = out; + + return IDMAP_SUCCESS; +} + +enum idmap_error_code sss_idmap_gen_to_unix(struct sss_idmap_ctx *ctx, + const char *domain_id, + const char *input, + uint32_t *_id) +{ + struct idmap_domain_info *idmap_domain_info; + struct idmap_domain_info *matched_dom = NULL; + long long offset; + uint32_t range_size; + enum idmap_error_code err; + idmap_offset_func *offset_func = offset_murmurhash3; + void *offset_func_pvt = NULL; + + if (domain_id == NULL || input == NULL || _id == NULL) { + return IDMAP_ERROR; + } + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + idmap_domain_info = ctx->idmap_domain_info; + + if (idmap_domain_info->offset_func != NULL) { + offset_func = idmap_domain_info->offset_func; + if (idmap_domain_info->offset_func_pvt != NULL) { + offset_func_pvt = idmap_domain_info->offset_func_pvt; + } + } + + /* Try primary slices */ + while (idmap_domain_info != NULL) { + + if (is_from_dom(idmap_domain_info->sid, domain_id)) { + + if (idmap_domain_info->external_mapping == true) { + return IDMAP_EXTERNAL; + } + + range_size = 1 + (idmap_domain_info->range_params.max_id - idmap_domain_info->range_params.min_id); + err = offset_func(offset_func_pvt, range_size, input, &offset); + if (err != IDMAP_SUCCESS) { + return err; + } + + if (offset >= range_size) { + return IDMAP_ERROR; + } + + if (comp_id(&idmap_domain_info->range_params, offset, _id)) { + return IDMAP_SUCCESS; + } + + matched_dom = idmap_domain_info; + } + + idmap_domain_info = idmap_domain_info->next; + } + + return matched_dom ? IDMAP_NO_RANGE : IDMAP_NO_DOMAIN; +} + +enum idmap_error_code sss_idmap_unix_to_gen(struct sss_idmap_ctx *ctx, + uint32_t id, + char **_out) +{ + struct idmap_domain_info *idmap_domain_info; + uint32_t offset; + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + idmap_domain_info = ctx->idmap_domain_info; + + while (idmap_domain_info != NULL) { + if (id_is_in_range(id, &idmap_domain_info->range_params, &offset)) { + + if (idmap_domain_info->external_mapping == true + || idmap_domain_info->sid == NULL) { + return IDMAP_EXTERNAL; + } + + if (idmap_domain_info->rev_offset_func == NULL) { + return IDMAP_NO_REVERSE; + } + + return idmap_domain_info->rev_offset_func(ctx, + idmap_domain_info->offset_func_pvt, + offset, _out); + } + + idmap_domain_info = idmap_domain_info->next; + } + + return IDMAP_NO_DOMAIN; +} + +enum idmap_error_code sss_idmap_sid_to_unix(struct sss_idmap_ctx *ctx, + const char *sid, + uint32_t *_id) +{ + struct idmap_domain_info *idmap_domain_info; + struct idmap_domain_info *matched_dom = NULL; + size_t dom_len; + long long rid; + + if (sid == NULL || _id == NULL) { + return IDMAP_ERROR; + } + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + idmap_domain_info = ctx->idmap_domain_info; + + if (sss_idmap_sid_is_builtin(sid)) { + return IDMAP_BUILTIN_SID; + } + + /* Try primary slices */ + while (idmap_domain_info != NULL) { + + if (is_sid_from_dom(idmap_domain_info->sid, sid, &dom_len)) { + + if (idmap_domain_info->external_mapping == true) { + return IDMAP_EXTERNAL; + } + + if (parse_rid(sid, dom_len, &rid) == false) { + return IDMAP_SID_INVALID; + } + + if (comp_id(&idmap_domain_info->range_params, rid, _id)) { + return IDMAP_SUCCESS; + } + + matched_dom = idmap_domain_info; + } + + idmap_domain_info = idmap_domain_info->next; + } + + if (matched_dom != NULL && matched_dom->auto_add_ranges) { + return add_dom_for_sid(ctx, matched_dom, sid, _id); + } + + return matched_dom ? IDMAP_NO_RANGE : IDMAP_NO_DOMAIN; +} + +enum idmap_error_code sss_idmap_check_sid_unix(struct sss_idmap_ctx *ctx, + const char *sid, + uint32_t id) +{ + struct idmap_domain_info *idmap_domain_info; + size_t dom_len; + bool no_range = false; + + if (sid == NULL) { + return IDMAP_ERROR; + } + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + if (ctx->idmap_domain_info == NULL) { + return IDMAP_NO_DOMAIN; + } + + idmap_domain_info = ctx->idmap_domain_info; + + if (sss_idmap_sid_is_builtin(sid)) { + return IDMAP_BUILTIN_SID; + } + + while (idmap_domain_info != NULL) { + if (idmap_domain_info->sid != NULL) { + dom_len = strlen(idmap_domain_info->sid); + if (strlen(sid) > dom_len && sid[dom_len] == '-' + && strncmp(sid, idmap_domain_info->sid, dom_len) == 0) { + + if (id >= idmap_domain_info->range_params.min_id + && id <= idmap_domain_info->range_params.max_id) { + return IDMAP_SUCCESS; + } + + no_range = true; + } + } + + idmap_domain_info = idmap_domain_info->next; + } + + return no_range ? IDMAP_NO_RANGE : IDMAP_SID_UNKNOWN; +} + +static enum idmap_error_code generate_sid(struct sss_idmap_ctx *ctx, + const char *dom_sid, + uint32_t rid, + char **_sid) +{ + char *sid; + int len; + int ret; + + len = snprintf(NULL, 0, SID_FMT, dom_sid, rid); + if (len <= 0 || len > SID_STR_MAX_LEN) { + return IDMAP_ERROR; + } + + sid = ctx->alloc_func(len + 1, ctx->alloc_pvt); + if (sid == NULL) { + return IDMAP_OUT_OF_MEMORY; + } + + ret = snprintf(sid, len + 1, SID_FMT, dom_sid, rid); + if (ret != len) { + ctx->free_func(sid, ctx->alloc_pvt); + return IDMAP_ERROR; + } + + *_sid = sid; + return IDMAP_SUCCESS; +} + +enum idmap_error_code sss_idmap_unix_to_sid(struct sss_idmap_ctx *ctx, + uint32_t id, + char **_sid) +{ + struct idmap_domain_info *idmap_domain_info; + uint32_t rid; + enum idmap_error_code err; + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + idmap_domain_info = ctx->idmap_domain_info; + + while (idmap_domain_info != NULL) { + if (id_is_in_range(id, &idmap_domain_info->range_params, &rid)) { + + if (idmap_domain_info->external_mapping == true + || idmap_domain_info->sid == NULL) { + return IDMAP_EXTERNAL; + } + + return generate_sid(ctx, idmap_domain_info->sid, rid, _sid); + } + + idmap_domain_info = idmap_domain_info->next; + } + + /* Check secondary ranges. */ + idmap_domain_info = ctx->idmap_domain_info; + while (idmap_domain_info != NULL) { + + for (struct idmap_range_params *it = idmap_domain_info->helpers; + it != NULL; + it = it->next) { + + if (idmap_domain_info->helpers_owner == false) { + /* Checking helpers on owner is sufficient. */ + continue; + } + + if (id_is_in_range(id, it, &rid)) { + + if (idmap_domain_info->external_mapping == true + || idmap_domain_info->sid == NULL) { + return IDMAP_EXTERNAL; + } + + err = spawn_dom(ctx, idmap_domain_info, it); + if (err != IDMAP_SUCCESS) { + return err; + } + + return generate_sid(ctx, idmap_domain_info->sid, rid, _sid); + } + } + + idmap_domain_info = idmap_domain_info->next; + } + + return IDMAP_NO_DOMAIN; +} + +enum idmap_error_code sss_idmap_dom_sid_to_unix(struct sss_idmap_ctx *ctx, + struct sss_dom_sid *dom_sid, + uint32_t *id) +{ + enum idmap_error_code err; + char *sid; + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + err = sss_idmap_dom_sid_to_sid(ctx, dom_sid, &sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + err = sss_idmap_sid_to_unix(ctx, sid, id); + +done: + ctx->free_func(sid, ctx->alloc_pvt); + + return err; +} + +enum idmap_error_code sss_idmap_bin_sid_to_unix(struct sss_idmap_ctx *ctx, + uint8_t *bin_sid, + size_t length, + uint32_t *id) +{ + enum idmap_error_code err; + char *sid; + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + err = sss_idmap_bin_sid_to_sid(ctx, bin_sid, length, &sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + err = sss_idmap_sid_to_unix(ctx, sid, id); + +done: + ctx->free_func(sid, ctx->alloc_pvt); + + return err; +} + +enum idmap_error_code sss_idmap_smb_sid_to_unix(struct sss_idmap_ctx *ctx, + struct dom_sid *smb_sid, + uint32_t *id) +{ + enum idmap_error_code err; + char *sid; + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + err = sss_idmap_smb_sid_to_sid(ctx, smb_sid, &sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + err = sss_idmap_sid_to_unix(ctx, sid, id); + +done: + ctx->free_func(sid, ctx->alloc_pvt); + + return err; +} + +enum idmap_error_code sss_idmap_check_dom_sid_to_unix(struct sss_idmap_ctx *ctx, + struct sss_dom_sid *dom_sid, + uint32_t id) +{ + enum idmap_error_code err; + char *sid; + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + err = sss_idmap_dom_sid_to_sid(ctx, dom_sid, &sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + err = sss_idmap_check_sid_unix(ctx, sid, id); + +done: + ctx->free_func(sid, ctx->alloc_pvt); + + return err; +} + +enum idmap_error_code sss_idmap_check_bin_sid_unix(struct sss_idmap_ctx *ctx, + uint8_t *bin_sid, + size_t length, + uint32_t id) +{ + enum idmap_error_code err; + char *sid; + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + err = sss_idmap_bin_sid_to_sid(ctx, bin_sid, length, &sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + err = sss_idmap_check_sid_unix(ctx, sid, id); + +done: + ctx->free_func(sid, ctx->alloc_pvt); + + return err; +} + +enum idmap_error_code sss_idmap_check_smb_sid_unix(struct sss_idmap_ctx *ctx, + struct dom_sid *smb_sid, + uint32_t id) +{ + enum idmap_error_code err; + char *sid; + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + err = sss_idmap_smb_sid_to_sid(ctx, smb_sid, &sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + err = sss_idmap_check_sid_unix(ctx, sid, id); + +done: + ctx->free_func(sid, ctx->alloc_pvt); + + return err; +} +enum idmap_error_code sss_idmap_unix_to_dom_sid(struct sss_idmap_ctx *ctx, + uint32_t id, + struct sss_dom_sid **_dom_sid) +{ + enum idmap_error_code err; + char *sid = NULL; + struct sss_dom_sid *dom_sid = NULL; + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + err = sss_idmap_unix_to_sid(ctx, id, &sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + err = sss_idmap_sid_to_dom_sid(ctx, sid, &dom_sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + *_dom_sid = dom_sid; + err = IDMAP_SUCCESS; + +done: + ctx->free_func(sid, ctx->alloc_pvt); + if (err != IDMAP_SUCCESS) { + ctx->free_func(dom_sid, ctx->alloc_pvt); + } + + return err; +} + +enum idmap_error_code sss_idmap_unix_to_bin_sid(struct sss_idmap_ctx *ctx, + uint32_t id, + uint8_t **_bin_sid, + size_t *_length) +{ + enum idmap_error_code err; + char *sid = NULL; + uint8_t *bin_sid = NULL; + size_t length; + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + err = sss_idmap_unix_to_sid(ctx, id, &sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + err = sss_idmap_sid_to_bin_sid(ctx, sid, &bin_sid, &length); + if (err != IDMAP_SUCCESS) { + goto done; + } + + *_bin_sid = bin_sid; + *_length = length; + err = IDMAP_SUCCESS; + +done: + ctx->free_func(sid, ctx->alloc_pvt); + if (err != IDMAP_SUCCESS) { + ctx->free_func(bin_sid, ctx->alloc_pvt); + } + + return err; + +} + +enum idmap_error_code +sss_idmap_ctx_set_autorid(struct sss_idmap_ctx *ctx, bool use_autorid) +{ + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + ctx->idmap_opts.autorid_mode = use_autorid; + return IDMAP_SUCCESS; +} + +enum idmap_error_code +sss_idmap_ctx_set_lower(struct sss_idmap_ctx *ctx, id_t lower) +{ + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + ctx->idmap_opts.idmap_lower = lower; + return IDMAP_SUCCESS; +} + +enum idmap_error_code +sss_idmap_ctx_set_upper(struct sss_idmap_ctx *ctx, id_t upper) +{ + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + ctx->idmap_opts.idmap_upper = upper; + return IDMAP_SUCCESS; +} + +enum idmap_error_code +sss_idmap_ctx_set_rangesize(struct sss_idmap_ctx *ctx, id_t rangesize) +{ + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + ctx->idmap_opts.rangesize = rangesize; + return IDMAP_SUCCESS; +} + +enum idmap_error_code +sss_idmap_ctx_set_extra_slice_init(struct sss_idmap_ctx *ctx, + int extra_slice_init) +{ + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + ctx->idmap_opts.extra_slice_init = extra_slice_init; + return IDMAP_SUCCESS; +} + +enum idmap_error_code +sss_idmap_ctx_get_autorid(struct sss_idmap_ctx *ctx, bool *_autorid) +{ + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + *_autorid = ctx->idmap_opts.autorid_mode; + return IDMAP_SUCCESS; +} + +enum idmap_error_code +sss_idmap_ctx_get_lower(struct sss_idmap_ctx *ctx, id_t *_lower) +{ + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + *_lower = ctx->idmap_opts.idmap_lower; + return IDMAP_SUCCESS; +} + +enum idmap_error_code +sss_idmap_ctx_get_upper(struct sss_idmap_ctx *ctx, id_t *_upper) +{ + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + *_upper = ctx->idmap_opts.idmap_upper; + return IDMAP_SUCCESS; +} + +enum idmap_error_code +sss_idmap_ctx_get_rangesize(struct sss_idmap_ctx *ctx, id_t *_rangesize) +{ + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + *_rangesize = ctx->idmap_opts.rangesize; + return IDMAP_SUCCESS; +} + +enum idmap_error_code +sss_idmap_domain_has_algorithmic_mapping(struct sss_idmap_ctx *ctx, + const char *dom_sid, + bool *has_algorithmic_mapping) +{ + struct idmap_domain_info *idmap_domain_info; + size_t len; + size_t dom_sid_len; + + if (dom_sid == NULL) { + return IDMAP_SID_INVALID; + } + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + if (ctx->idmap_domain_info == NULL) { + return IDMAP_NO_DOMAIN; + } + + idmap_domain_info = ctx->idmap_domain_info; + + while (idmap_domain_info != NULL) { + if (idmap_domain_info->sid != NULL) { + len = strlen(idmap_domain_info->sid); + dom_sid_len = strlen(dom_sid); + if (((dom_sid_len > len && dom_sid[len] == '-') + || dom_sid_len == len) + && strncmp(dom_sid, idmap_domain_info->sid, len) == 0) { + + *has_algorithmic_mapping = !idmap_domain_info->external_mapping; + return IDMAP_SUCCESS; + + } + } + + idmap_domain_info = idmap_domain_info->next; + } + + return IDMAP_SID_UNKNOWN; +} + +enum idmap_error_code +sss_idmap_domain_by_name_has_algorithmic_mapping(struct sss_idmap_ctx *ctx, + const char *dom_name, + bool *has_algorithmic_mapping) +{ + struct idmap_domain_info *idmap_domain_info; + + if (dom_name == NULL) { + return IDMAP_ERROR; + } + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + if (ctx->idmap_domain_info == NULL) { + return IDMAP_NO_DOMAIN; + } + + idmap_domain_info = ctx->idmap_domain_info; + + while (idmap_domain_info != NULL) { + if (idmap_domain_info->name != NULL + && strcmp(dom_name, idmap_domain_info->name) == 0) { + + *has_algorithmic_mapping = !idmap_domain_info->external_mapping; + return IDMAP_SUCCESS; + } + + idmap_domain_info = idmap_domain_info->next; + } + + return IDMAP_NAME_UNKNOWN; +} diff --git a/src/idmap/src/sss_idmap.h b/src/idmap/src/sss_idmap.h new file mode 100644 index 00000000..a94c07f3 --- /dev/null +++ b/src/idmap/src/sss_idmap.h @@ -0,0 +1,1100 @@ +/* + SSSD + + ID-mapping library + + Authors: + Sumit Bose + + Copyright (C) 2012 Red Hat + + 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 . +*/ + +#ifndef SSS_IDMAP_H_ +#define SSS_IDMAP_H_ + +#include +#include +#include +#include + +#define DOM_SID_PREFIX "S-1-5-21-" +#define DOM_SID_PREFIX_LEN (sizeof(DOM_SID_PREFIX) - 1) + +/** + * @defgroup sss_idmap Map Unix UIDs and GIDs to SIDs and back + * Libsss_idmap provides a mechanism to translate a SID to a UNIX UID or GID + * or the other way round. + * @{ + */ + +/** + * Error codes used by libsss_idmap + */ +enum idmap_error_code { + /** Success */ + IDMAP_SUCCESS = 0, + + /** Function is not yet implemented */ + IDMAP_NOT_IMPLEMENTED, + + /** General error */ + IDMAP_ERROR, + + /** Ran out of memory during processing */ + IDMAP_OUT_OF_MEMORY, + + /** No domain added */ + IDMAP_NO_DOMAIN, + + /** The provided idmap context is invalid */ + IDMAP_CONTEXT_INVALID, + + /** The provided SID is invalid */ + IDMAP_SID_INVALID, + + /** The provided SID was not found */ + IDMAP_SID_UNKNOWN, + + /** The provided UID or GID could not be mapped */ + IDMAP_NO_RANGE, + + /** The provided SID is a built-in one */ + IDMAP_BUILTIN_SID, + + /** No more free slices */ + IDMAP_OUT_OF_SLICES, + + /** New domain collides with existing one */ + IDMAP_COLLISION, + + /** External source should be consulted for idmapping */ + IDMAP_EXTERNAL, + + /** The provided name was not found */ + IDMAP_NAME_UNKNOWN, + + /** It is not possible to convert an id into the original value the id was + * derived from */ + IDMAP_NO_REVERSE, + + /** Error during UTF8 operation like normalization or casefolding */ + IDMAP_UTF8_ERROR, + + /** Sentinel to indicate the end of the error code list, not returned by + * any call */ + IDMAP_ERR_LAST +}; + +/** + * Typedef for memory allocation functions + */ +typedef void *(idmap_alloc_func)(size_t size, void *pvt); +typedef void (idmap_free_func)(void *ptr, void *pvt); + +/** + * Typedef for storing mappings of dynamically created domains + */ +typedef enum idmap_error_code (*idmap_store_cb)(const char *dom_name, + const char *dom_sid, + const char *range_id, + uint32_t min_id, + uint32_t max_id, + uint32_t first_rid, + void *pvt); + +/** + * Structure for id ranges + * FIXME: this struct might change when it is clear how ranges are handled on + * the server side + */ +struct sss_idmap_range { + uint32_t min; + uint32_t max; +}; + +/** + * Opaque type for SIDs + */ +struct sss_dom_sid; + +/** + * Opaque type for the idmap context + */ +struct sss_idmap_ctx; + +/** + * Placeholder for Samba's struct dom_sid. Consumers of libsss_idmap should + * include an appropriate Samba header file to define struct dom_sid. We use + * it here to avoid a hard dependency on Samba devel packages. + */ +struct dom_sid; + +/** + * @brief Initialize idmap context + * + * @param[in] alloc_func Function to allocate memory for the context, if + * NULL malloc() id used + * @param[in] alloc_pvt Private data for allocation routine + * @param[in] free_func Function to free the memory the context, if + * NULL free() id used + * @param[out] ctx idmap context + * + * @return + * - #IDMAP_OUT_OF_MEMORY: Insufficient memory to create the context + */ +enum idmap_error_code sss_idmap_init(idmap_alloc_func *alloc_func, + void *alloc_pvt, + idmap_free_func *free_func, + struct sss_idmap_ctx **ctx); + +/** + * @brief Set/unset autorid compatibility mode + * + * @param[in] ctx idmap context + * @param[in] use_autorid If true, autorid compatibility mode will be used + */ +enum idmap_error_code +sss_idmap_ctx_set_autorid(struct sss_idmap_ctx *ctx, bool use_autorid); + +/** + * @brief Set the lower bound of the range of POSIX IDs + * + * @param[in] ctx idmap context + * @param[in] lower lower bound of the range + */ +enum idmap_error_code +sss_idmap_ctx_set_lower(struct sss_idmap_ctx *ctx, id_t lower); + +/** + * @brief Set the upper bound of the range of POSIX IDs + * + * @param[in] ctx idmap context + * @param[in] upper upper bound of the range + */ +enum idmap_error_code +sss_idmap_ctx_set_upper(struct sss_idmap_ctx *ctx, id_t upper); + +/** + * @brief Set the range size of POSIX IDs available for single domain + * + * @param[in] ctx idmap context + * @param[in] rangesize range size of IDs + */ +enum idmap_error_code +sss_idmap_ctx_set_rangesize(struct sss_idmap_ctx *ctx, id_t rangesize); + +/** + * @brief Set the number of secondary slices available for domain + * + * @param[in] ctx idmap context + * @param[in] extra_slice_init number of secondary slices to be generated + * at startup + */ +enum idmap_error_code +sss_idmap_ctx_set_extra_slice_init(struct sss_idmap_ctx *ctx, + int extra_slice_init); + +/** + * @brief Check if autorid compatibility mode is set + * + * @param[in] ctx idmap context + * @param[out] _autorid true if autorid is used + */ +enum idmap_error_code +sss_idmap_ctx_get_autorid(struct sss_idmap_ctx *ctx, bool *_autorid); + +/** + * @brief Get the lower bound of the range of POSIX IDs + * + * @param[in] ctx idmap context + * @param[out] _lower returned lower bound + */ +enum idmap_error_code +sss_idmap_ctx_get_lower(struct sss_idmap_ctx *ctx, id_t *_lower); + +/** + * @brief Get the upper bound of the range of POSIX IDs + * + * @param[in] ctx idmap context + * @param[out] _upper returned upper bound + */ +enum idmap_error_code +sss_idmap_ctx_get_upper(struct sss_idmap_ctx *ctx, id_t *_upper); + +/** + * @brief Get the range size of POSIX IDs available for single domain + * + * @param[in] ctx idmap context + * @param[out] rangesize returned range size + */ +enum idmap_error_code +sss_idmap_ctx_get_rangesize(struct sss_idmap_ctx *ctx, id_t *rangesize); + +/** + * @brief Calculate new range of available POSIX IDs + * + * @param[in] ctx Idmap context + * @param[in] dom_sid Zero-terminated string representation of the domain + * SID (S-1-15-.....) + * @param[in,out] slice_num Slice number to be used. Set this pointer to NULL or + * the addressed value to -1 to calculate slice number + * automatically. The calculated value will be + * returned in this parameter. + * @param[out] range Structure containing upper and lower bound of the + * range of POSIX IDs + * + * @return + * - #IDMAP_OUT_OF_SLICES: Cannot calculate new range because all slices are + * used. + */ +enum idmap_error_code sss_idmap_calculate_range(struct sss_idmap_ctx *ctx, + const char *dom_sid, + id_t *slice_num, + struct sss_idmap_range *range); + +/** + * @brief Add a domain to the idmap context + * + * @param[in] ctx Idmap context + * @param[in] domain_name Zero-terminated string with the domain name + * @param[in] domain_sid Zero-terminated string representation of the domain + * SID (S-1-15-.....) + * @param[in] range TBD Some information about the id ranges of this + * domain + * + * @return + * - #IDMAP_OUT_OF_MEMORY: Insufficient memory to store the data in the idmap + * context + * - #IDMAP_SID_INVALID: Invalid SID provided + * - #IDMAP_NO_DOMAIN: No domain domain name given + * - #IDMAP_COLLISION: New domain collides with existing one + */ +enum idmap_error_code sss_idmap_add_domain(struct sss_idmap_ctx *ctx, + const char *domain_name, + const char *domain_sid, + struct sss_idmap_range *range); + +/** + * @brief Add a domain with the first mappable RID to the idmap context + * + * @param[in] ctx Idmap context + * @param[in] domain_name Zero-terminated string with the domain name + * @param[in] domain_sid Zero-terminated string representation of the domain + * SID (S-1-15-.....) + * @param[in] range TBD Some information about the id ranges of this + * domain + * @param[in] range_id optional unique identifier of a range, it is needed + * to allow updates at runtime + * @param[in] rid The RID that should be mapped to the first ID of the + * given range. + * @param[in] external_mapping If set to true the ID will not be mapped + * algorithmically, but the *_to_unix and *_unix_to_* + * calls will return IDMAP_EXTERNAL to instruct the + * caller to check external sources. For a single + * domain all ranges must be of the same type. It is + * not possible to mix algorithmic and external + * mapping. + * + * @return + * - #IDMAP_OUT_OF_MEMORY: Insufficient memory to store the data in the idmap + * context + * - #IDMAP_SID_INVALID: Invalid SID provided + * - #IDMAP_NO_DOMAIN: No domain domain name given + * - #IDMAP_COLLISION: New domain collides with existing one + */ +enum idmap_error_code sss_idmap_add_domain_ex(struct sss_idmap_ctx *ctx, + const char *domain_name, + const char *domain_sid, + struct sss_idmap_range *range, + const char *range_id, + uint32_t rid, + bool external_mapping); + +/** + * @brief Add a domain with the first mappable RID to the idmap context and + * generate automatically secondary slices + * + * @param[in] ctx Idmap context + * @param[in] domain_name Zero-terminated string with the domain name + * @param[in] domain_sid Zero-terminated string representation of the domain + * SID (S-1-15-.....) + * @param[in] range TBD Some information about the id ranges of this + * domain + * @param[in] range_id optional unique identifier of a range, it is needed + * to allow updates at runtime + * @param[in] rid The RID that should be mapped to the first ID of the + * given range. + * @param[in] external_mapping If set to true the ID will not be mapped + * algorithmically, but the *_to_unix and *_unix_to_* + * calls will return IDMAP_EXTERNAL to instruct the + * caller to check external sources. For a single + * domain all ranges must be of the same type. It is + * not possible to mix algorithmic and external + * mapping. + * @param[in] cb The callback for storing mapping of dynamically + * created domains. + * @param[in] pvt Private data for callback cb. + * + * @return + * - #IDMAP_OUT_OF_MEMORY: Insufficient memory to store the data in the idmap + * context + * - #IDMAP_SID_INVALID: Invalid SID provided + * - #IDMAP_NO_DOMAIN: No domain domain name given + * - #IDMAP_COLLISION: New domain collides with existing one + */ +enum idmap_error_code +sss_idmap_add_auto_domain_ex(struct sss_idmap_ctx *ctx, + const char *domain_name, + const char *domain_sid, + struct sss_idmap_range *range, + const char *range_id, + uint32_t rid, + bool external_mapping, + idmap_store_cb cb, + void *pvt); + +/** + * @brief Check if a new range would collide with any existing one + * + * @param[in] ctx Idmap context + * @param[in] n_name Zero-terminated string with the domain name the new + * range should belong to + * @param[in] n_sid Zero-terminated string representation of the domain + * SID (S-1-15-.....) the new range should belong to + * @param[in] n_range The new id range + * @param[in] n_range_id unique identifier of the new range, it is needed + * to allow updates at runtime, may be NULL + * @param[in] n_first_rid The RID that should be mapped to the first ID of the + * new range. + * @param[in] n_external_mapping Mapping type of the new range + * + * @return + * - #IDMAP_COLLISION: New range collides with existing one + */ +enum idmap_error_code sss_idmap_check_collision(struct sss_idmap_ctx *ctx, + char *n_name, char *n_sid, + struct sss_idmap_range *n_range, + uint32_t n_first_rid, + char *n_range_id, + bool n_external_mapping); + +/** + * @brief Check if two ranges would collide + * + * @param[in] o_name Zero-terminated string with the domain name the + * first range should belong to + * @param[in] o_sid Zero-terminated string representation of the domain + * SID (S-1-15-.....) the first range should belong to + * @param[in] o_range The first id range + * @param[in] o_range_id unique identifier of the first range, it is needed + * to allow updates at runtime, may be NULL + * @param[in] o_first_rid The RID that should be mapped to the first ID of the + * first range. + * @param[in] o_external_mapping Mapping type of the first range + * @param[in] n_name Zero-terminated string with the domain name the + * second range should belong to + * @param[in] n_sid Zero-terminated string representation of the domain + * SID (S-1-15-.....) the second range should belong to + * @param[in] n_range The second id range + * @param[in] n_range_id unique identifier of the second range, it is needed + * to allow updates at runtime, may be NULL + * @param[in] n_first_rid The RID that should be mapped to the first ID of the + * second range. + * @param[in] n_external_mapping Mapping type of the second range + * + * @return + * - #IDMAP_COLLISION: New range collides with existing one + */ +enum idmap_error_code sss_idmap_check_collision_ex(const char *o_name, + const char *o_sid, + struct sss_idmap_range *o_range, + uint32_t o_first_rid, + const char *o_range_id, + bool o_external_mapping, + const char *n_name, + const char *n_sid, + struct sss_idmap_range *n_range, + uint32_t n_first_rid, + const char *n_range_id, + bool n_external_mapping); + +/** + * @brief Translate SID to a unix UID or GID + * + * @param[in] ctx Idmap context + * @param[in] sid Zero-terminated string representation of the SID + * @param[out] id Returned unix UID or GID + * + * @return + * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context + * - #IDMAP_SID_INVALID: Invalid SID provided + * - #IDMAP_SID_UNKNOWN: SID cannot be found in the domains added to the + * idmap context + * - #IDMAP_EXTERNAL: external source is authoritative for mapping + */ +enum idmap_error_code sss_idmap_sid_to_unix(struct sss_idmap_ctx *ctx, + const char *sid, + uint32_t *id); + +/** + * @brief Translate a SID structure to a unix UID or GID + * + * @param[in] ctx Idmap context + * @param[in] dom_sid SID structure + * @param[out] id Returned unix UID or GID + * + * @return + * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context + * - #IDMAP_SID_INVALID: Invalid SID provided + * - #IDMAP_SID_UNKNOWN: SID cannot be found in the domains added to the + * idmap context + * - #IDMAP_EXTERNAL: external source is authoritative for mapping + */ +enum idmap_error_code sss_idmap_dom_sid_to_unix(struct sss_idmap_ctx *ctx, + struct sss_dom_sid *dom_sid, + uint32_t *id); + +/** + * @brief Translate a binary SID to a unix UID or GID + * + * @param[in] ctx Idmap context + * @param[in] bin_sid Array with the binary SID + * @param[in] length Size of the array containing the binary SID + * @param[out] id Returned unix UID or GID + * + * @return + * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context + * - #IDMAP_SID_INVALID: Invalid SID provided + * - #IDMAP_SID_UNKNOWN: SID cannot be found in the domains added to the + * idmap context + * - #IDMAP_EXTERNAL: external source is authoritative for mapping + */ +enum idmap_error_code sss_idmap_bin_sid_to_unix(struct sss_idmap_ctx *ctx, + uint8_t *bin_sid, + size_t length, + uint32_t *id); + +/** + * @brief Translate a Samba dom_sid structure to a unix UID or GID + * + * @param[in] ctx Idmap context + * @param[in] smb_sid Samba dom_sid structure + * @param[out] id Returned unix UID or GID + * + * @return + * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context + * - #IDMAP_SID_INVALID: Invalid SID provided + * - #IDMAP_SID_UNKNOWN: SID cannot be found in the domains added to the + * idmap context + * - #IDMAP_EXTERNAL: external source is authoritative for mapping + */ +enum idmap_error_code sss_idmap_smb_sid_to_unix(struct sss_idmap_ctx *ctx, + struct dom_sid *smb_sid, + uint32_t *id); + +/** + * @brief Check if a SID and a unix UID or GID belong to the same range + * + * @param[in] ctx Idmap context + * @param[in] sid Zero-terminated string representation of the SID + * @param[in] id Unix UID or GID + * + * @return + * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context + * - #IDMAP_SID_INVALID: Invalid SID provided + * - #IDMAP_SID_UNKNOWN: SID cannot be found in the domains added to the + * idmap context + * - #IDMAP_NO_RANGE No matching ID range found + */ +enum idmap_error_code sss_idmap_check_sid_unix(struct sss_idmap_ctx *ctx, + const char *sid, + uint32_t id); + +/** + * @brief Check if a SID structure and a unix UID or GID belong to the same range + * + * @param[in] ctx Idmap context + * @param[in] dom_sid SID structure + * @param[in] id Unix UID or GID + * + * @return + * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context + * - #IDMAP_SID_INVALID: Invalid SID provided + * - #IDMAP_SID_UNKNOWN: SID cannot be found in the domains added to the + * idmap context + * - #IDMAP_NO_RANGE No matching ID range found + */ +enum idmap_error_code sss_idmap_check_dom_sid_unix(struct sss_idmap_ctx *ctx, + struct sss_dom_sid *dom_sid, + uint32_t id); + +/** + * @brief Check if a binary SID and a unix UID or GID belong to the same range + * + * @param[in] ctx Idmap context + * @param[in] bin_sid Array with the binary SID + * @param[in] length Size of the array containing the binary SID + * @param[in] id Unix UID or GID + * + * @return + * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context + * - #IDMAP_SID_INVALID: Invalid SID provided + * - #IDMAP_SID_UNKNOWN: SID cannot be found in the domains added to the + * idmap context + * - #IDMAP_NO_RANGE No matching ID range found + */ +enum idmap_error_code sss_idmap_check_bin_sid_unix(struct sss_idmap_ctx *ctx, + uint8_t *bin_sid, + size_t length, + uint32_t id); + +/** + * @brief Check if a Samba dom_sid structure and a unix UID or GID belong to + * the same range + * + * @param[in] ctx Idmap context + * @param[in] smb_sid Samba dom_sid structure + * @param[in] id Unix UID or GID + * + * @return + * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context + * - #IDMAP_SID_INVALID: Invalid SID provided + * - #IDMAP_SID_UNKNOWN: SID cannot be found in the domains added to the + * idmap context + * - #IDMAP_NO_RANGE No matching ID range found + */ +enum idmap_error_code sss_idmap_check_smb_sid_unix(struct sss_idmap_ctx *ctx, + struct dom_sid *smb_sid, + uint32_t id); + +/** + * @brief Translate unix UID or GID to a SID + * + * @param[in] ctx Idmap context + * @param[in] id unix UID or GID + * @param[out] sid Zero-terminated string representation of the SID, must be + * freed if not needed anymore + * + * @return + * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context + * - #IDMAP_NO_RANGE: The provided ID cannot be found in the domains added + * to the idmap context + * - #IDMAP_EXTERNAL: external source is authoritative for mapping + */ +enum idmap_error_code sss_idmap_unix_to_sid(struct sss_idmap_ctx *ctx, + uint32_t id, + char **sid); + +/** + * @brief Translate unix UID or GID to a SID structure + * + * @param[in] ctx Idmap context + * @param[in] id unix UID or GID + * @param[out] dom_sid SID structure, must be freed if not needed anymore + * + * @return + * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context + * - #IDMAP_NO_RANGE: The provided ID cannot be found in the domains added + * to the idmap context + * - #IDMAP_EXTERNAL: external source is authoritative for mapping + */ +enum idmap_error_code sss_idmap_unix_to_dom_sid(struct sss_idmap_ctx *ctx, + uint32_t id, + struct sss_dom_sid **dom_sid); + +/** + * @brief Translate unix UID or GID to a binary SID + * + * @param[in] ctx Idmap context + * @param[in] id unix UID or GID + * @param[out] bin_sid Array with the binary SID, + * must be freed if not needed anymore + * @param[out] length size of the array containing the binary SID + * + * @return + * - #IDMAP_NO_DOMAIN: No domains are added to the idmap context + * - #IDMAP_NO_RANGE: The provided ID cannot be found in the domains added + * to the idmap context + * - #IDMAP_EXTERNAL: external source is authoritative for mapping + */ +enum idmap_error_code sss_idmap_unix_to_bin_sid(struct sss_idmap_ctx *ctx, + uint32_t id, + uint8_t **bin_sid, + size_t *length); + +/** + * @brief Free all the allocated memory of the idmap context + * + * @param[in] ctx Idmap context + * + * @return + * - #IDMAP_CONTEXT_INVALID: Provided context is invalid + */ +enum idmap_error_code sss_idmap_free(struct sss_idmap_ctx *ctx); + +/** + * @brief Free mapped SID. + * + * @param[in] ctx Idmap context + * @param[in] sid SID to be freed. + * + * @return + * - #IDMAP_CONTEXT_INVALID: Provided context is invalid + */ +enum idmap_error_code sss_idmap_free_sid(struct sss_idmap_ctx *ctx, + char *sid); + +/** + * @brief Free mapped domain SID. + * + * @param[in] ctx Idmap context + * @param[in] dom_sid Domain SID to be freed. + * + * @return + * - #IDMAP_CONTEXT_INVALID: Provided context is invalid + */ +enum idmap_error_code sss_idmap_free_dom_sid(struct sss_idmap_ctx *ctx, + struct sss_dom_sid *dom_sid); + +/** + * @brief Free mapped Samba SID. + * + * @param[in] ctx Idmap context + * @param[in] smb_sid Samba SID to be freed. + * + * @return + * - #IDMAP_CONTEXT_INVALID: Provided context is invalid + */ +enum idmap_error_code sss_idmap_free_smb_sid(struct sss_idmap_ctx *ctx, + struct dom_sid *smb_sid); + +/** + * @brief Free mapped binary SID. + * + * @param[in] ctx Idmap context + * @param[in] bin_sid Binary SID to be freed. + * + * @return + * - #IDMAP_CONTEXT_INVALID: Provided context is invalid + */ +enum idmap_error_code sss_idmap_free_bin_sid(struct sss_idmap_ctx *ctx, + uint8_t *bin_sid); + +/** + * @brief Translate error code to a string + * + * @param[in] err Idmap error code + * + * @return + * - Error description as a zero-terminated string + */ +const char *idmap_error_string(enum idmap_error_code err); + +/** + * @brief Check if given string can be used as domain SID + * + * @param[in] str String to check + * + * @return + * - true: String can be used as domain SID + * - false: String can not be used as domain SID + */ +bool is_domain_sid(const char *str); + +/** + * @brief Check if a domain is configured with algorithmic mapping + * + * @param[in] ctx Idmap context + * @param[in] dom_sid SID string, can be either a domain SID + * or an object SID + * @param[out] has_algorithmic_mapping Boolean value indicating if the given + * domain is configured for algorithmic + * mapping or not. + * + * @return + * - #IDMAP_SUCCESS: Domain for the given SID was found and + * has_algorithmic_mapping is set accordingly + * - #IDMAP_SID_INVALID: Provided SID is invalid + * - #IDMAP_CONTEXT_INVALID: Provided idmap context is invalid + * - #IDMAP_NO_DOMAIN: No domains are available in the idmap context + * - #IDMAP_SID_UNKNOWN: No domain with the given SID was found in the + * idmap context + */ +enum idmap_error_code +sss_idmap_domain_has_algorithmic_mapping(struct sss_idmap_ctx *ctx, + const char *dom_sid, + bool *has_algorithmic_mapping); + +/** + * @brief Check if a domain is configured with algorithmic mapping + * + * @param[in] ctx Idmap context + * @param[in] dom_name Name of the domain + * @param[out] has_algorithmic_mapping Boolean value indicating if the given + * domain is configured for algorithmic + * mapping or not. + * + * @return + * - #IDMAP_SUCCESS: Domain for the given name was found and + * has_algorithmic_mapping is set accordingly + * - #IDMAP_ERROR: Provided name is invalid + * - #IDMAP_CONTEXT_INVALID: Provided idmap context is invalid + * - #IDMAP_NO_DOMAIN: No domains are available in the idmap context + * - #IDMAP_NAME_UNKNOWN: No domain with the given name was found in the + * idmap context + */ +enum idmap_error_code +sss_idmap_domain_by_name_has_algorithmic_mapping(struct sss_idmap_ctx *ctx, + const char *dom_name, + bool *has_algorithmic_mapping); + +/** + * @brief Convert binary SID to SID structure + * + * @param[in] ctx Idmap context + * @param[in] bin_sid Array with the binary SID + * @param[in] length Size of the array containing the binary SID + * @param[out] dom_sid SID structure, + * must be freed if not needed anymore + * + * @return + * - #IDMAP_SID_INVALID: Given SID is invalid + * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result + */ +enum idmap_error_code sss_idmap_bin_sid_to_dom_sid(struct sss_idmap_ctx *ctx, + const uint8_t *bin_sid, + size_t length, + struct sss_dom_sid **dom_sid); + +/** + * @brief Convert binary SID to SID string + * + * @param[in] ctx Idmap context + * @param[in] bin_sid Array with the binary SID + * @param[in] length Size of the array containing the binary SID + * @param[out] sid Zero-terminated string representation of the SID, + * must be freed if not needed anymore + * + * @return + * - #IDMAP_SID_INVALID: Given SID is invalid + * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result + */ +enum idmap_error_code sss_idmap_bin_sid_to_sid(struct sss_idmap_ctx *ctx, + const uint8_t *bin_sid, + size_t length, + char **sid); + +/** + * @brief Convert SID structure to binary SID + * + * @param[in] ctx Idmap context + * @param[in] dom_sid SID structure + * @param[out] bin_sid Array with the binary SID, + * must be freed if not needed anymore + * @param[out] length Size of the array containing the binary SID + * + * @return + * - #IDMAP_SID_INVALID: Given SID is invalid + * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result + */ +enum idmap_error_code sss_idmap_dom_sid_to_bin_sid(struct sss_idmap_ctx *ctx, + struct sss_dom_sid *dom_sid, + uint8_t **bin_sid, + size_t *length); + +/** + * @brief Convert SID string to binary SID + * + * @param[in] ctx Idmap context + * @param[in] sid Zero-terminated string representation of the SID + * @param[out] bin_sid Array with the binary SID, + * must be freed if not needed anymore + * @param[out] length Size of the array containing the binary SID + * + * @return + * - #IDMAP_SID_INVALID: Given SID is invalid + * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result + */ +enum idmap_error_code sss_idmap_sid_to_bin_sid(struct sss_idmap_ctx *ctx, + const char *sid, + uint8_t **bin_sid, + size_t *length); + +/** + * @brief Convert SID structure to SID string + * + * @param[in] ctx Idmap context + * @param[in] dom_sid SID structure + * @param[out] sid Zero-terminated string representation of the SID, + * must be freed if not needed anymore + * + * @return + * - #IDMAP_SID_INVALID: Given SID is invalid + * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result + */ +enum idmap_error_code sss_idmap_dom_sid_to_sid(struct sss_idmap_ctx *ctx, + struct sss_dom_sid *dom_sid, + char **sid); + +/** + * @brief Convert SID string to SID structure + * + * @param[in] ctx Idmap context + * @param[in] sid Zero-terminated string representation of the SID + * @param[out] dom_sid SID structure, + * must be freed if not needed anymore + * + * @return + * - #IDMAP_SID_INVALID: Given SID is invalid + * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result + */ +enum idmap_error_code sss_idmap_sid_to_dom_sid(struct sss_idmap_ctx *ctx, + const char *sid, + struct sss_dom_sid **dom_sid); + +/** + * @brief Convert SID string to Samba dom_sid structure + * + * @param[in] ctx Idmap context + * @param[in] sid Zero-terminated string representation of the SID + * @param[out] smb_sid Samba dom_sid structure, + * must be freed if not needed anymore + * + * @return + * - #IDMAP_SID_INVALID: Given SID is invalid + * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result + */ +enum idmap_error_code sss_idmap_sid_to_smb_sid(struct sss_idmap_ctx *ctx, + const char *sid, + struct dom_sid **smb_sid); + +/** + * @brief Convert Samba dom_sid structure to SID string + * + * @param[in] ctx Idmap context + * @param[in] smb_sid Samba dom_sid structure + * @param[out] sid Zero-terminated string representation of the SID, + * must be freed if not needed anymore + * + * @return + * - #IDMAP_SID_INVALID: Given SID is invalid + * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result + */ +enum idmap_error_code sss_idmap_smb_sid_to_sid(struct sss_idmap_ctx *ctx, + struct dom_sid *smb_sid, + char **sid); + +/** + * @brief Convert SID structure to Samba dom_sid structure + * + * @param[in] ctx Idmap context + * @param[in] dom_sid SID structure + * @param[out] smb_sid Samba dom_sid structure, + * must be freed if not needed anymore + * + * @return + * - #IDMAP_SID_INVALID: Given SID is invalid + * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result + */ +enum idmap_error_code sss_idmap_dom_sid_to_smb_sid(struct sss_idmap_ctx *ctx, + struct sss_dom_sid *dom_sid, + struct dom_sid **smb_sid); + +/** + * @brief Convert Samba dom_sid structure to SID structure + * + * @param[in] ctx Idmap context + * @param[in] smb_sid Samba dom_sid structure + * @param[out] dom_sid SID structure, + * must be freed if not needed anymore + * + * @return + * - #IDMAP_SID_INVALID: Given SID is invalid + * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result + */ +enum idmap_error_code sss_idmap_smb_sid_to_dom_sid(struct sss_idmap_ctx *ctx, + struct dom_sid *smb_sid, + struct sss_dom_sid **dom_sid); + +/** + * @brief Convert binary SID to Samba dom_sid structure + * + * @param[in] ctx Idmap context + * @param[in] bin_sid Array with the binary SID + * @param[in] length Size of the array containing the binary SID + * @param[out] smb_sid Samba dom_sid structure, + * must be freed if not needed anymore + * + * @return + * - #IDMAP_SID_INVALID: Given SID is invalid + * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result + */ +enum idmap_error_code sss_idmap_bin_sid_to_smb_sid(struct sss_idmap_ctx *ctx, + const uint8_t *bin_sid, + size_t length, + struct dom_sid **smb_sid); + +/** + * @brief Convert Samba dom_sid structure to binary SID + * + * @param[in] ctx Idmap context + * @param[in] smb_sid Samba dom_sid structure + * @param[out] bin_sid Array with the binary SID, + * must be freed if not needed anymore + * @param[out] length Size of the array containing the binary SID + * + * @return + * - #IDMAP_SID_INVALID: Given SID is invalid + * - #IDMAP_OUT_OF_MEMORY: Failed to allocate memory for the result + */ +enum idmap_error_code sss_idmap_smb_sid_to_bin_sid(struct sss_idmap_ctx *ctx, + struct dom_sid *smb_sid, + uint8_t **bin_sid, + size_t *length); + +/** + * Typedef for functions to calculate an offset for id-mapping and, if + * possible, for the reverse operation. + */ +typedef enum idmap_error_code (idmap_offset_func)(void *pvt, + uint32_t range_size, + const char *input, + long long *offset); + +typedef enum idmap_error_code (idmap_rev_offset_func)(struct sss_idmap_ctx *ctx, + void *pvt, + uint32_t offset, + char **out); + +/** + * @brief Add a generic domain to the idmap context + * + * @param[in] ctx Idmap context + * @param[in] domain_name Zero-terminated string with the domain name + * @param[in] domain_id Zero-terminated string representation of a unique + * identifier of the domain, e.g. if available a domain + * UUID or the URI of domain specific service + * @param[in] range Id ranges struct with smallest and largest id of the + * range + * @param[in] range_id A name for the id range, currently not used, might + * become important when we allow multiple ranges for a + * single domain + * @param[in] offset_func Function to calculate an offset in a given range + * from some input given as string, if NULL + * offset_murmurhash3 will be used. + * @param[in] rev_offset_func Function to calculate the original input from a + * given offset, i.e. the reverse of offset_func, may + * be NULL + * @param[in] offset_func_pvt Private data for offset_func and + * rev_offset_func, may be NULL + * @param[in] shift Currently not used, might become important when we + * allow multiple ranges for a single domain + * @param[in] external_mapping Indicates that for this domain the mapping + * should not be done by libsss_idmap, the related + * calls will return IDMAP_EXTERNAL in this case. + * Nevertheless it might be important to add the domain + * to the idmap context so that libsss_idmap will not + * use the related ranges for mapping. + * + * @return + * - #IDMAP_OUT_OF_MEMORY: Insufficient memory to store the data in the idmap + * context + * - #IDMAP_SID_INVALID: Invalid SID provided + * - #IDMAP_NO_DOMAIN: No domain domain name given + * - #IDMAP_COLLISION: New domain collides with existing one + */ +enum idmap_error_code sss_idmap_add_gen_domain_ex(struct sss_idmap_ctx *ctx, + const char *domain_name, + const char *domain_id, + struct sss_idmap_range *range, + const char *range_id, + idmap_offset_func *offset_func, + idmap_rev_offset_func *rev_offset_func, + void *offset_func_pvt, + uint32_t shift, + bool external_mapping); + +/** + * @brief Calculate offset from string containing only numbers + */ +enum idmap_error_code offset_identity(void *pvt, uint32_t range_size, + const char *input, long long *offset); + +/** + * @brief Reverse of offset_identity, return a string containing only numbers + * representing the given offset + */ +enum idmap_error_code rev_offset_identity(struct sss_idmap_ctx *ctx, void *pvt, + uint32_t id, char **_out); + +/** + * @brief Calculate offset from string with the help of murmurhash3 + */ +enum idmap_error_code offset_murmurhash3(void *pvt, uint32_t range_size, + const char *input, long long *offset); + +/** + * Structure for private data for offset_murmurhash3. If not given 0xdeadbeef + * will be used as seed. UTF8 strings will be normalized by default but not + * casefolded. + */ +struct offset_murmurhash3_data { + uint32_t seed; + bool normalize; + bool casefold; +}; + +/** + * @brief Translate some input to a unix UID or GID + * + * @param[in] ctx Idmap context + * @param[in] domain_id Zero-terminated string with the domain ID of a known + * domain + * @param[in] input Zero-terminated string which should be translated into + * an offset to calculate the unix UID or GID + * @param[out] id Returned unix UID or GID + * + * @return + * - #IDMAP_NO_DOMAIN: No domain with domain_id found in ctx + * - #IDMAP_EXTERNAL: external source is authoritative for mapping + */ +enum idmap_error_code sss_idmap_gen_to_unix(struct sss_idmap_ctx *ctx, + const char *domain_id, + const char *input, + uint32_t *_id); + +/** + * @brief Translate some input to a unix UID or GID + * + * @param[in] ctx Idmap context + * @param[in] id UNIX UID or GID + * an offset to calculate the unix UID or GID + * @param[out] out Original value the UID or GID was derived from + * + * @return + * - #IDMAP_NO_DOMAIN: No domain with domain_id found in ctx + * - #IDMAP_EXTERNAL: external source is authoritative for mapping + * - #IDMAP_NO_REVERSE: the id cannot be reverted back to the original + * source + */ +enum idmap_error_code sss_idmap_unix_to_gen(struct sss_idmap_ctx *ctx, + uint32_t id, + char **out); + +/** + * @} + */ +#endif /* SSS_IDMAP_H_ */ diff --git a/src/idmap/src/sss_idmap_conv.c b/src/idmap/src/sss_idmap_conv.c new file mode 100644 index 00000000..bd3148d4 --- /dev/null +++ b/src/idmap/src/sss_idmap_conv.c @@ -0,0 +1,570 @@ +/* + SSSD + + ID-mapping library - conversion utilities + + Authors: + Sumit Bose + + Copyright (C) 2012 Red Hat + + 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 . +*/ + +#include +#include +#include +#include + +#include "sss_idmap.h" +#include "sss_idmap_private.h" +//#include "util/util.h" +//#include "util/sss_endian.h" +#include "util.h" + +#define SID_ID_AUTHS 6 +#define SID_SUB_AUTHS 15 +struct sss_dom_sid { + uint8_t sid_rev_num; + int8_t num_auths; /* [range(0,15)] */ + uint8_t id_auth[SID_ID_AUTHS]; /* highest order byte has index 0 */ + uint32_t sub_auths[SID_SUB_AUTHS]; /* host byte-order */ +}; + +enum idmap_error_code sss_idmap_bin_sid_to_dom_sid(struct sss_idmap_ctx *ctx, + const uint8_t *bin_sid, + size_t length, + struct sss_dom_sid **_dom_sid) +{ + enum idmap_error_code err; + struct sss_dom_sid *dom_sid; + size_t i = 0; + size_t p = 0; + uint32_t val; + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + if (length > sizeof(struct sss_dom_sid)) return IDMAP_SID_INVALID; + + dom_sid = ctx->alloc_func(sizeof(struct sss_dom_sid), ctx->alloc_pvt); + if (dom_sid == NULL) { + return IDMAP_OUT_OF_MEMORY; + } + memset(dom_sid, 0, sizeof(struct sss_dom_sid)); + + /* Safely copy in the SID revision number */ + dom_sid->sid_rev_num = (uint8_t) *(bin_sid + p); + p++; + + /* Safely copy in the number of sub auth values */ + dom_sid->num_auths = (uint8_t) *(bin_sid + p); + p++; + + /* Make sure we aren't being told to read more bin_sid + * than can fit in the structure + */ + if (dom_sid->num_auths > SID_SUB_AUTHS) { + err = IDMAP_SID_INVALID; + goto done; + } + + /* Safely copy in the id_auth values */ + for (i = 0; i < SID_ID_AUTHS; i++) { + dom_sid->id_auth[i] = (uint8_t) *(bin_sid + p); + p++; + } + + /* Safely copy in the sub_auths values */ + for (i = 0; i < dom_sid->num_auths; i++) { + /* SID sub auth values in Active Directory are stored little-endian, + * we store them in host order */ + SAFEALIGN_COPY_UINT32(&val, bin_sid + p, &p); + dom_sid->sub_auths[i] = le32toh(val); + } + + *_dom_sid = dom_sid; + err = IDMAP_SUCCESS; + +done: + if (err != IDMAP_SUCCESS) { + ctx->free_func(dom_sid, ctx->alloc_pvt); + } + return err; +} + +enum idmap_error_code sss_idmap_dom_sid_to_bin_sid(struct sss_idmap_ctx *ctx, + struct sss_dom_sid *dom_sid, + uint8_t **_bin_sid, + size_t *_length) +{ + enum idmap_error_code err; + uint8_t *bin_sid; + size_t length; + size_t i = 0; + size_t p = 0; + uint32_t val; + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + if (dom_sid->num_auths > SID_SUB_AUTHS) { + return IDMAP_SID_INVALID; + } + + length = 2 + SID_ID_AUTHS + dom_sid->num_auths * 4; + + bin_sid = ctx->alloc_func(length, ctx->alloc_pvt); + if (bin_sid == NULL) { + return IDMAP_OUT_OF_MEMORY; + } + + bin_sid[p] = dom_sid->sid_rev_num; + p++; + + bin_sid[p] = dom_sid->num_auths; + p++; + + for (i = 0; i < SID_ID_AUTHS; i++) { + bin_sid[p] = dom_sid->id_auth[i]; + p++; + } + + for (i = 0; i < dom_sid->num_auths; i++) { + if (p + sizeof(uint32_t) > length) { + err = IDMAP_SID_INVALID; + goto done; + } + val = htole32(dom_sid->sub_auths[i]); + SAFEALIGN_COPY_UINT32(bin_sid + p, &val, &p); + } + + *_bin_sid = bin_sid; + *_length = length; + + err = IDMAP_SUCCESS; +done: + if (err != IDMAP_SUCCESS) { + ctx->free_func(bin_sid, ctx->alloc_pvt); + } + return err; +} + +enum idmap_error_code sss_idmap_dom_sid_to_sid(struct sss_idmap_ctx *ctx, + struct sss_dom_sid *dom_sid, + char **_sid) +{ + enum idmap_error_code err; + char *sid_buf; + size_t sid_buf_len; + char *p; + int nc; + int8_t i; + uint32_t id_auth_val = 0; + + if (dom_sid->num_auths > SID_SUB_AUTHS) { + return IDMAP_SID_INVALID; + } + + sid_buf_len = 25 + dom_sid->num_auths * 11; + sid_buf = ctx->alloc_func(sid_buf_len, ctx->alloc_pvt); + if (sid_buf == NULL) { + return IDMAP_OUT_OF_MEMORY; + } + memset(sid_buf, 0, sid_buf_len); + + /* Only 32bits are used for the string representation */ + id_auth_val = (dom_sid->id_auth[2] << 24) + + (dom_sid->id_auth[3] << 16) + + (dom_sid->id_auth[4] << 8) + + (dom_sid->id_auth[5]); + + nc = snprintf(sid_buf, sid_buf_len, "S-%u-%lu", dom_sid->sid_rev_num, + (unsigned long) id_auth_val); + if (nc < 0 || nc >= sid_buf_len) { + err = IDMAP_SID_INVALID; + goto done; + } + + + /* Loop through the sub-auths, if any, prepending a hyphen + * for each one. + */ + p = sid_buf; + for (i = 0; i < dom_sid->num_auths ; i++) { + p += nc; + sid_buf_len -= nc; + + nc = snprintf(p, sid_buf_len, "-%lu", + (unsigned long) dom_sid->sub_auths[i]); + if (nc < 0 || nc >= sid_buf_len) { + err = IDMAP_SID_INVALID; + goto done; + } + } + + *_sid = sid_buf; + err = IDMAP_SUCCESS; + +done: + if (err != IDMAP_SUCCESS) { + ctx->free_func(sid_buf, ctx->alloc_pvt); + } + + return err; +} + +enum idmap_error_code sss_idmap_sid_to_dom_sid(struct sss_idmap_ctx *ctx, + const char *sid, + struct sss_dom_sid **_dom_sid) +{ + enum idmap_error_code err; + unsigned long ul; + char *r; + char *end; + struct sss_dom_sid *dom_sid; + + CHECK_IDMAP_CTX(ctx, IDMAP_CONTEXT_INVALID); + + if (sid == NULL || (sid[0] != 'S' && sid[0] != 's') || sid[1] != '-') { + return IDMAP_SID_INVALID; + } + + dom_sid = ctx->alloc_func(sizeof(struct sss_dom_sid), ctx->alloc_pvt); + if (dom_sid == NULL) { + return IDMAP_OUT_OF_MEMORY; + } + memset(dom_sid, 0, sizeof(struct sss_dom_sid)); + + + if (!isdigit(sid[2])) { + err = IDMAP_SID_INVALID; + goto done; + } + errno = 0; + ul = strtoul(sid + 2, &r, 10); + if (errno != 0 || r == NULL || *r != '-' || ul > UINT8_MAX) { + err = IDMAP_SID_INVALID; + goto done; + } + dom_sid->sid_rev_num = (uint8_t) ul; + r++; + + if (!isdigit(*r)) { + err = IDMAP_SID_INVALID; + goto done; + } + errno = 0; + ul = strtoul(r, &r, 10); + if (errno != 0 || r == NULL || ul > UINT32_MAX) { + err = IDMAP_SID_INVALID; + goto done; + } + + /* id_auth in the string should always be <2^32 in decimal */ + /* store values in the same order as the binary representation */ + dom_sid->id_auth[0] = 0; + dom_sid->id_auth[1] = 0; + dom_sid->id_auth[2] = (ul & 0xff000000) >> 24; + dom_sid->id_auth[3] = (ul & 0x00ff0000) >> 16; + dom_sid->id_auth[4] = (ul & 0x0000ff00) >> 8; + dom_sid->id_auth[5] = (ul & 0x000000ff); + + if (*r == '\0') { + /* no sub auths given */ + err = IDMAP_SUCCESS; + goto done; + } + + if (*r != '-') { + err = IDMAP_SID_INVALID; + goto done; + } + + do { + if (dom_sid->num_auths >= SID_SUB_AUTHS) { + err = IDMAP_SID_INVALID; + goto done; + } + + r++; + if (!isdigit(*r)) { + err = IDMAP_SID_INVALID; + goto done; + } + + errno = 0; + ul = strtoul(r, &end, 10); + if (errno != 0 || ul > UINT32_MAX || end == NULL || + (*end != '\0' && *end != '-')) { + err = IDMAP_SID_INVALID; + goto done; + } + + dom_sid->sub_auths[dom_sid->num_auths++] = ul; + + r = end; + } while (*r != '\0'); + + err = IDMAP_SUCCESS; + +done: + if (err != IDMAP_SUCCESS) { + ctx->free_func(dom_sid, ctx->alloc_pvt); + } else { + *_dom_sid = dom_sid; + } + + return err; +} + +enum idmap_error_code sss_idmap_sid_to_bin_sid(struct sss_idmap_ctx *ctx, + const char *sid, + uint8_t **_bin_sid, + size_t *_length) +{ + enum idmap_error_code err; + struct sss_dom_sid *dom_sid = NULL; + size_t length; + uint8_t *bin_sid = NULL; + + err = sss_idmap_sid_to_dom_sid(ctx, sid, &dom_sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + err = sss_idmap_dom_sid_to_bin_sid(ctx, dom_sid, &bin_sid, &length); + if (err != IDMAP_SUCCESS) { + goto done; + } + + *_length = length; + *_bin_sid = bin_sid; + err = IDMAP_SUCCESS; + +done: + ctx->free_func(dom_sid, ctx->alloc_pvt); + if (err != IDMAP_SUCCESS) { + ctx->free_func(bin_sid, ctx->alloc_pvt); + } + + return err; +} + +enum idmap_error_code sss_idmap_bin_sid_to_sid(struct sss_idmap_ctx *ctx, + const uint8_t *bin_sid, + size_t length, + char **_sid) +{ + enum idmap_error_code err; + struct sss_dom_sid *dom_sid = NULL; + char *sid = NULL; + + err = sss_idmap_bin_sid_to_dom_sid(ctx, bin_sid, length, &dom_sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + err = sss_idmap_dom_sid_to_sid(ctx, dom_sid, &sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + *_sid = sid; + err = IDMAP_SUCCESS; + +done: + ctx->free_func(dom_sid, ctx->alloc_pvt); + if (err != IDMAP_SUCCESS) { + ctx->free_func(sid, ctx->alloc_pvt); + } + + return err; +} + +enum idmap_error_code sss_idmap_sid_to_smb_sid(struct sss_idmap_ctx *ctx, + const char *sid, + struct dom_sid **_smb_sid) +{ + enum idmap_error_code err; + struct sss_dom_sid *dom_sid = NULL; + struct dom_sid *smb_sid = NULL; + + err = sss_idmap_sid_to_dom_sid(ctx, sid, &dom_sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + err = sss_idmap_dom_sid_to_smb_sid(ctx, dom_sid, &smb_sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + *_smb_sid = smb_sid; + err = IDMAP_SUCCESS; + +done: + ctx->free_func(dom_sid, ctx->alloc_pvt); + if (err != IDMAP_SUCCESS) { + ctx->free_func(smb_sid, ctx->alloc_pvt); + } + + return err; +} + +enum idmap_error_code sss_idmap_smb_sid_to_sid(struct sss_idmap_ctx *ctx, + struct dom_sid *smb_sid, + char **_sid) +{ + enum idmap_error_code err; + struct sss_dom_sid *dom_sid = NULL; + char *sid = NULL; + + err = sss_idmap_smb_sid_to_dom_sid(ctx, smb_sid, &dom_sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + err = sss_idmap_dom_sid_to_sid(ctx, dom_sid, &sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + *_sid = sid; + err = IDMAP_SUCCESS; + +done: + ctx->free_func(dom_sid, ctx->alloc_pvt); + if (err != IDMAP_SUCCESS) { + ctx->free_func(sid, ctx->alloc_pvt); + } + + return err; +} + +enum idmap_error_code sss_idmap_dom_sid_to_smb_sid(struct sss_idmap_ctx *ctx, + struct sss_dom_sid *dom_sid, + struct dom_sid **_smb_sid) +{ + struct dom_sid *smb_sid; + size_t c; + + smb_sid = ctx->alloc_func(sizeof(struct dom_sid), ctx->alloc_pvt); + if (smb_sid == NULL) { + return IDMAP_OUT_OF_MEMORY; + } + memset(smb_sid, 0, sizeof(struct dom_sid)); + + smb_sid->sid_rev_num = dom_sid->sid_rev_num; + smb_sid->num_auths = dom_sid->num_auths; + for (c = 0; c < SID_ID_AUTHS; c++) { + smb_sid->id_auth[c] = dom_sid->id_auth[c]; + } + for (c = 0; c < SID_SUB_AUTHS; c++) { + smb_sid->sub_auths[c] = dom_sid->sub_auths[c]; + } + + *_smb_sid = smb_sid; + + return IDMAP_SUCCESS; +} + +enum idmap_error_code sss_idmap_smb_sid_to_dom_sid(struct sss_idmap_ctx *ctx, + struct dom_sid *smb_sid, + struct sss_dom_sid **_dom_sid) +{ + struct sss_dom_sid *dom_sid; + size_t c; + + dom_sid = ctx->alloc_func(sizeof(struct sss_dom_sid), ctx->alloc_pvt); + if (dom_sid == NULL) { + return IDMAP_OUT_OF_MEMORY; + } + memset(dom_sid, 0, sizeof(struct sss_dom_sid)); + + dom_sid->sid_rev_num = smb_sid->sid_rev_num; + dom_sid->num_auths = smb_sid->num_auths; + for (c = 0; c < SID_ID_AUTHS; c++) { + dom_sid->id_auth[c] = smb_sid->id_auth[c]; + } + for (c = 0; c < SID_SUB_AUTHS; c++) { + dom_sid->sub_auths[c] = smb_sid->sub_auths[c]; + } + + *_dom_sid = dom_sid; + + return IDMAP_SUCCESS; +} + +enum idmap_error_code sss_idmap_bin_sid_to_smb_sid(struct sss_idmap_ctx *ctx, + const uint8_t *bin_sid, + size_t length, + struct dom_sid **_smb_sid) +{ + enum idmap_error_code err; + struct sss_dom_sid *dom_sid = NULL; + struct dom_sid *smb_sid = NULL; + + err = sss_idmap_bin_sid_to_dom_sid(ctx, bin_sid, length, &dom_sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + err = sss_idmap_dom_sid_to_smb_sid(ctx, dom_sid, &smb_sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + *_smb_sid = smb_sid; + err = IDMAP_SUCCESS; + +done: + ctx->free_func(dom_sid, ctx->alloc_pvt); + if (err != IDMAP_SUCCESS) { + ctx->free_func(smb_sid, ctx->alloc_pvt); + } + + return err; +} + +enum idmap_error_code sss_idmap_smb_sid_to_bin_sid(struct sss_idmap_ctx *ctx, + struct dom_sid *smb_sid, + uint8_t **_bin_sid, + size_t *_length) +{ + enum idmap_error_code err; + struct sss_dom_sid *dom_sid = NULL; + uint8_t *bin_sid = NULL; + size_t length; + + err = sss_idmap_smb_sid_to_dom_sid(ctx, smb_sid, &dom_sid); + if (err != IDMAP_SUCCESS) { + goto done; + } + + err = sss_idmap_dom_sid_to_bin_sid(ctx, dom_sid, &bin_sid, &length); + if (err != IDMAP_SUCCESS) { + goto done; + } + + *_bin_sid = bin_sid; + *_length = length; + err = IDMAP_SUCCESS; + +done: + ctx->free_func(dom_sid, ctx->alloc_pvt); + if (err != IDMAP_SUCCESS) { + ctx->free_func(bin_sid, ctx->alloc_pvt); + } + + return err; +} diff --git a/src/idmap/src/sss_idmap_private.h b/src/idmap/src/sss_idmap_private.h new file mode 100644 index 00000000..15300d11 --- /dev/null +++ b/src/idmap/src/sss_idmap_private.h @@ -0,0 +1,84 @@ +/* + SSSD + + ID-mapping library - private headers + + Authors: + Sumit Bose + + Copyright (C) 2012 Red Hat + + 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 . +*/ + +#ifndef SSS_IDMAP_PRIVATE_H_ +#define SSS_IDMAP_PRIVATE_H_ + +#define SSS_IDMAP_DEFAULT_LOWER 200000 +#define SSS_IDMAP_DEFAULT_UPPER 2000200000 +#define SSS_IDMAP_DEFAULT_RANGESIZE 200000 +#define SSS_IDMAP_DEFAULT_AUTORID false +#define SSS_IDMAP_DEFAULT_EXTRA_SLICE_INIT 10 + +#define CHECK_IDMAP_CTX(ctx, ret) do { \ + if (ctx == NULL || ctx->alloc_func == NULL || ctx->free_func == NULL) { \ + return ret; \ + } \ +} while(0) + +struct sss_idmap_opts { + /* true if autorid compatibility mode is used */ + bool autorid_mode; + + /* smallest available id (for all domains) */ + id_t idmap_lower; + + /* highest available id (for all domains) */ + id_t idmap_upper; + + /* number of available UIDs (for single domain) */ + id_t rangesize; + + /* maximal number of secondary slices */ + int extra_slice_init; +}; + +struct sss_idmap_ctx { + idmap_alloc_func *alloc_func; + void *alloc_pvt; + idmap_free_func *free_func; + struct sss_idmap_opts idmap_opts; + struct idmap_domain_info *idmap_domain_info; +}; + +/* This is a copy of the definition in the samba gen_ndr/security.h header + * file. We use it here to be able to offer conversions form struct dom_sid to + * string or binary representation since those are not made available by + * public samba libraries. + * + * If the definition ever changes on the samba side we have to adopt the + * change. But chances are very low that this will ever happen since e.g. this + * struct is also defined in public documentation from Microsoft. See e.g. + * section 2.4.2.3 of "[MS-DTYP]: Windows Data Types" + * http://msdn.microsoft.com/en-us/library/cc230364(v=prot.10) + */ + +struct dom_sid { + uint8_t sid_rev_num; + int8_t num_auths; + uint8_t id_auth[6]; + uint32_t sub_auths[15]; +}; + +#endif /* SSS_IDMAP_PRIVATE_H_ */ diff --git a/src/idmap/src/util.h b/src/idmap/src/util.h new file mode 100644 index 00000000..12d4cf96 --- /dev/null +++ b/src/idmap/src/util.h @@ -0,0 +1,46 @@ +/* + Himmelblaud + + ID-mapping library utils + + 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 . +*/ + +#ifndef _UTILS_H +#define _UTILS_H + +#include +#include + +static inline void +safealign_memcpy(void *dest, const void *src, size_t n, size_t *counter) +{ + memcpy(dest, src, n); + if (counter) { + *counter += n; + } +} + +#define SAFEALIGN_COPY_UINT32(dest, src, pctr) \ + safealign_memcpy(dest, src, sizeof(uint32_t), pctr) + +#if defined(__GNUC__) && __GNUC__ >= 7 + #define SSS_ATTRIBUTE_FALLTHROUGH __attribute__ ((fallthrough)) +#else + #define SSS_ATTRIBUTE_FALLTHROUGH +#endif + +#endif /* _UTILS_H */ diff --git a/src/idmap/sssd b/src/idmap/sssd deleted file mode 160000 index 85b17ac2..00000000 --- a/src/idmap/sssd +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 85b17ac21c1b72e57f7c2b5f017f16da8b4b77ae diff --git a/src/idmap/wrapper.h b/src/idmap/wrapper.h deleted file mode 100644 index 6be1d216..00000000 --- a/src/idmap/wrapper.h +++ /dev/null @@ -1 +0,0 @@ -#include "sssd/src/lib/idmap/sss_idmap.h"